[Python-Dev] How far to go with user-friendliness

Ron Adam ron3200 at gmail.com
Mon Jul 20 18:57:08 CEST 2015



On 07/20/2015 03:32 AM, Florian Bruhin wrote:
> * Ron Adam<ron3200 at gmail.com>  [2015-07-19 18:06:22 -0400]:
>> >
>> >
>> >On 07/19/2015 02:33 PM, Florian Bruhin wrote:
>>> > >* Ron Adam<ron3200 at gmail.com>   [2015-07-19 11:17:10 -0400]:
>>>>> > >>>I had to look at the source to figure out what this thread was really all
>>>>> > >>>about.
>> >
>> >And it seems I don't quite get it still, but I am trying.
> No worries - I'll try to clarify until things are clear :)

Thanks,  :-)

>>>>> > >>>Basically it looks to me the purpose of adding "assret" is to add an "alias
>>>>> > >>>check" for "unsafe" methods.  It doesn't actually add an "alias".  It allows
>>>>> > >>>a developer to use a valid alias to avoid conflicting with methods starting
>>>>> > >>>with assert that will still work with the mock module.
>>>>> > >>>
>>>>> > >>>The mock module says that when "unsafe" flag is set to True, it will not
>>>>> > >>>raise AssertionError for methods beginning with "assert" and "assret".  It
>>>>> > >>>doesn't specify what "unsafe" means, and why you would want to do that.
>>>>> > >>>
>>>>> > >>>So first do this...
>>>>> > >>>
>>>>> > >>>     * Document "unsafe" in mock module.
>> >
>> >I still think documenting the purpose of "unsafe", rather than just the
>> >effect it has is important.
>> >
>> > From the confusion in this thread, (including my own), it's clear the
>> >documentation does need some attention.
>> >
>> >
>> >There are only two places where it mentions "unsafe" in the docs...
>> >
>> >The class signature...
>> >
>> >"""
>> >class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT,
>> >wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)
>> >"""
>> >
>> >
>> >And in the section below that.
>> >
>> >"""
>> >unsafe: By default if any attribute starts with assert or assret will raise
>> >an AttributeError. Passing unsafe=True will allow access to these
>> >attributes.
>> >"""
>> >
>> >But that doesn't explain the purpose or why these are unsafe or why it
>> >should throw an AttributeError.   Or what to do with that AttributeError.

> It's "unsafe" because tests which:
>
> 1) are using the assert_* methods of a mock, and
> 2) where the programmer did a typo (assert_called() instead of
>     assert_called_with() for example)
>
> do silently pass.

And further down, you say...

> Compare it with the behavior of a normal object - if you call a method
> which doesn't exist, it raises AttributeError.
>
> This isn't possible with Mock objects, as they are designed to support
> *any*  call, and you can check the calls have happened after the fact.


And the docs say...

"""
spec: This can be either a list of strings or an existing object (a class 
or instance) that acts as the specification for the mock object. If you 
pass in an object then a list of strings is formed by calling dir on the 
object (excluding unsupported magic attributes and methods). Accessing any 
attribute not in this list will raise an AttributeError.
"""

So calling a method that doesn't exist on a mocked object will raise an 
AttributeError if it is given a spec.

But if you don't give it a spec, then a mispelling of *any* method will 
pass silently.  So it's not a problem limited to "assert" methods.

It seems the obvious and best solution is to always use a spec.



>> >It's not clear why getting an AttributeError for methods beginning with
>> >assert is needed, and how that exception is to be used.   (Should it Bubble
>> >up, or should it be caught and handled?)

> Note the discussion*isn't*  about the fact that assert-methods should
> raise AttributeError! The patch also does the same with "assret".
>
> At least if I understand things correctly, the discussion is whether
> *only*  "assert*" should be handled as a typo, or "assret*" too.

Both of these are new in 3.5.  And they are related to each other.  So yes, 
they do need to be looked at together in order to understand the problem 
being discussed.

> The exception should bubble up, as the whole point of it is to tell
> you you did a typo and your test is broken.

I think this is too simple of an explanation.  That could be true for any 
method or attribute call.

 >>> m = Mock(spec=["assert_me", "call_me"])
 >>> m.call_me()
<Mock name='mock.call_me()' id='140590283081488'>

 >>> m.all_me()
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File 
"/media/hda2/home/ra/Dev/python-dev/python3.5/cpython-master/Lib/unittest/mock.py", 
line 578, in __getattr__
     raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'all_me'

It does raise AttributeError's on missing methods if a spec is given.  So 
catching mispelled methods in tests is only a problem if you don't use a 
spec.  (And not limited to assert methods)


 >>> m.assert_me()
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File 
"/media/hda2/home/ra/Dev/python-dev/python3.5/cpython-master/Lib/unittest/mock.py", 
line 583, in __getattr__
     raise AttributeError(name)
AttributeError: assert_me


Why is AttributeError raised here?  Why are methods beginning with assert 
special?  (or "unsafe")

Cheers,
    Ron






















More information about the Python-Dev mailing list