classification
Title: PropertyMock refuses to raise AttributeErrror as a side effect
Type: behavior Stage: test needed
Components: Versions: Python 3.7, Python 3.6, Python 3.4, Python 3.5
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: michael.foord Nosy List: eric.snow, kushal.das, michael.foord, rbcollins, stevenk
Priority: normal Keywords: easy

Created on 2014-04-16 16:15 by michael.foord, last changed 2016-09-12 03:25 by rbcollins.

Messages (5)
msg216487 - (view) Author: Michael Foord (michael.foord) * (Python committer) Date: 2014-04-16 16:15
What steps will reproduce the problem?

>>> import mock
>>> a_mock = mock.MagicMock()
>>> no_attribute = mock.PropertyMock(side_effect=AttributeError)
>>> type(a_mock).property = no_attribute



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

I would expect the above to raise an AttributeError. Instead it returns a MagicMock instance.

>>> a_mock.property
<MagicMock name='mock.property' id='140165240345424'>

I would expect it to have the same effect as calling a PropertyMock with any other exception as a side effect:

>>> mock_value_error = mock.PropertyMock(side_effect=ValueError)
>>> type(a_mock).other_property = mock_value_error
>>> a_mock.other_property
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ahammel/bin/python/mock-1.0.1-py2.6.egg/mock.py", line 2365, in __get__
    return self()
  File "/home/ahammel/bin/python/mock-1.0.1-py2.6.egg/mock.py", line 955, in __call__
    return _mock_self._mock_call(*args, **kwargs)
  File "/home/ahammel/bin/python/mock-1.0.1-py2.6.egg/mock.py", line 1010, in _mock_call
    raise effect
ValueError



What version of the product are you using? On what operating system?

Using version mock-1.0.1-py2.6 on CentOS 6.4



Please provide any additional information below.

PropertyMock objects apparently won't raise sublcasses of AttributeError either:

>>> class MockAttributeError(AttributeError): pass
... 
>>> no_attr = mock.PropertyMock(side_effect=MockAttributeError)
>>> type(a_mock).property = no_attr
>>> a_mock.property
<MagicMock name='mock.property' id='140165240345424'>

Works fine for subclasses of other Exceptions:

>>> class MockKeyError(KeyError): pass
... 
>>> no_key = mock.PropertyMock(side_effect=MockKeyError)
>>> type(a_mock).property = no_key
>>> a_mock.property
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ahammel/bin/python/mock-1.0.1-py2.6.egg/mock.py", line 2365, in __get__
    return self()
  File "/home/ahammel/bin/python/mock-1.0.1-py2.6.egg/mock.py", line 955, in __call__
    return _mock_self._mock_call(*args, **kwargs)
  File "/home/ahammel/bin/python/mock-1.0.1-py2.6.egg/mock.py", line 1010, in _mock_call
    raise effect
__main__.MockKeyError
msg216503 - (view) Author: Eric Snow (eric.snow) * (Python committer) Date: 2014-04-16 17:03
Perhaps related to #1615?
msg275934 - (view) Author: Robert Collins (rbcollins) * (Python committer) Date: 2016-09-12 03:14
We're poking at this at the kiwipycon sprints
msg275937 - (view) Author: Steve Kowalik (stevenk) Date: 2016-09-12 03:23
This is in fact, working entirely as expected.

If we define a property that raises an AttributeError, then __getattr__() will be called, since the descriptor protocol says that an attribute error is a missing descriptor.

>>> class Foo(object):
...   def __getattr__(self, attr):
...     return 42
...   @property
...   def bacon(self):
...     print(1)
...     return int.lala
... 
>>> Foo().bacon
1
42

If we then follow a similar pattern using mock:

>>> from unittest import mock
>>> a_mock = mock.MagicMock()
>>> def foo():
...   print(1)
...   raise AttributeError()
... 
>>> no_attribute = mock.PropertyMock(side_effect=foo)
>>> type(a_mock).property = no_attribute
>>> a_mock.property
1
<MagicMock name='mock.property' id='139971099507232'>

You can see that the method is called, since we print one, but then what is going on is that MagicMock.__getattr__ is called, which has the behavior to return a new mock, like so:

>>> a_mock.b
<MagicMock name='mock.b' id='139971099646776'>
msg275938 - (view) Author: Robert Collins (rbcollins) * (Python committer) Date: 2016-09-12 03:25
I could imagine doing some complex things to let you get at the AttributeError in this case but *not* when a different descriptor was added to type(a_mock), but I think it would confusing overall. This might be worth a doc tweak to cover it?
History
Date User Action Args
2016-09-12 03:25:05rbcollinssetstage: needs patch -> test needed
messages: + msg275938
versions: + Python 3.6, Python 3.7
2016-09-12 03:23:03stevenksetnosy: + stevenk
messages: + msg275937
2016-09-12 03:14:55rbcollinssetnosy: + rbcollins
messages: + msg275934
2014-04-16 17:03:19eric.snowsetnosy: + eric.snow
messages: + msg216503
2014-04-16 16:15:17michael.foordcreate