derwiki-adroll / mock

Automatically exported from code.google.com/p/mock
BSD 2-Clause "Simplified" License
0 stars 0 forks source link

Can't mock built-in or extension types like datetime (+ fix) #200

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?

@mock.patch("datetime.date.today")
def test_datetime_mocking(today):
    today.return_value = datetime.date(2013, 1, 31)
    print "today is ", datetime.date.today()

What is the expected output? What do you see instead?

I expect it to print the mocked date.

Traceback (most recent call last):
  File "/Users/jbb/.virtualenvs/fifteen5/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/Users/jbb/.virtualenvs/fifteen5/lib/python2.7/site-packages/mock.py", line 1226, in patched
    patching.__exit__(*exc_info)
  File "/Users/jbb/.virtualenvs/fifteen5/lib/python2.7/site-packages/mock.py", line 1392, in __exit__
    mysetattr(self.target, self.attribute, self.temp_original)
  File "/Users/jbb/.virtualenvs/fifteen5/lib/python2.7/site-packages/mock.py", line 119, in mysetattr
    return setattr(target, attribute, new_attr)
TypeError: can't set attributes of built-in/extension type 'datetime.date'

Please provide any additional information below.

I generalized the following solution for mock: 
http://stackoverflow.com/a/14214646

Basically replaced setattr() in __enter__ and __exit__ with this:

def _mock_setattr(target, attribute, new_attr):
    try:
        return setattr(target, attribute, new_attr)
    except TypeError:
        # built-in/extension type
        import ctypes as c

        class PyObject_HEAD(c.Structure):
            _fields_ = [
                ('HEAD', c.c_ubyte * (object.__basicsize__ -
                                      c.sizeof(c.c_void_p))),
                ('ob_type', c.c_void_p)
            ]

        _get_dict = c.pythonapi._PyObject_GetDictPtr
        _get_dict.restype = c.POINTER(c.py_object)
        _get_dict.argtypes = [c.py_object]
        d = _get_dict(target)[0]
        d[attribute] = new_attr

Ain't pretty but it works for me.  Probably Python version dependent or 
something but it would be great to enable this in mock as its a very common use 
case.

Original issue reported on code.google.com by hexspr...@gmail.com on 13 Feb 2013 at 5:40

GoogleCodeExporter commented 9 years ago
Yeah, I wasn't thinking about PyPy or Jython internals in this case.... 
still... wouldn't hurt to allow it for CPython with a warning message to the 
others?

I tested it with CPython 2.7 and 3.3

Original comment by hexspr...@gmail.com on 13 Feb 2013 at 6:00

Attachments:

GoogleCodeExporter commented 9 years ago
This is an ingenious solution, but it's messing with implementation details and 
there are no guarantees that this will be successful or that it won't segfault 
the interpreter. It's not something that can be in the standard library I'm 
afraid.

Original comment by fuzzyman on 18 Mar 2013 at 7:31