Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mock.create_autospec fails if an attribute is a partial function #76334

Closed
bclau mannequin opened this issue Nov 28, 2017 · 12 comments
Closed

mock.create_autospec fails if an attribute is a partial function #76334

bclau mannequin opened this issue Nov 28, 2017 · 12 comments
Labels
3.7 (EOL) end of life 3.8 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@bclau
Copy link
Mannequin

bclau mannequin commented Nov 28, 2017

BPO 32153
Nosy @cjw296, @berkerpeksag, @TonyFlury, @BCLAU, @tirkarthi
PRs
  • bpo-32153: Add unit test for create_autospec with partial function returned in getattr #10398
  • [3.7] bpo-32153: Add unit test for create_autospec with partial function returned in getattr (GH-10398) #10855
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2018-12-12.08:05:43.105>
    created_at = <Date 2017-11-28.10:23:15.019>
    labels = ['3.7', '3.8', 'type-bug', 'library']
    title = 'mock.create_autospec fails if an attribute is a partial function'
    updated_at = <Date 2018-12-12.08:05:43.104>
    user = 'https://github.com/bclau'

    bugs.python.org fields:

    activity = <Date 2018-12-12.08:05:43.104>
    actor = 'cjw296'
    assignee = 'none'
    closed = True
    closed_date = <Date 2018-12-12.08:05:43.105>
    closer = 'cjw296'
    components = ['Library (Lib)']
    creation = <Date 2017-11-28.10:23:15.019>
    creator = 'cbelu'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 32153
    keywords = ['patch']
    message_count = 12.0
    messages = ['307115', '325465', '325466', '325467', '325487', '327529', '327531', '329433', '330907', '330909', '331675', '331677']
    nosy_count = 5.0
    nosy_names = ['cjw296', 'berker.peksag', 'anthony-flury', 'cbelu', 'xtreak']
    pr_nums = ['10398', '10855']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue32153'
    versions = ['Python 3.7', 'Python 3.8']

    @bclau
    Copy link
    Mannequin Author

    bclau mannequin commented Nov 28, 2017

    If an object's attribute is a partial function, mock.create_autospec will fail while trying to copy the partial functions' details to the mocked function, as the partial function does not have the __name__ attribute.

    Example:

        import functools
    
        from unittest import mock
    
    
        class Proxy(object):
            def __init__(self, obj):
                self.obj
    
            def __getattr__(self, name):
                return functools.partial(self.__run_method, name)
    
            def __run_method(self, name, *args, **kwargs):
                return getattr(self.obj, name)(*args, **kwargs)
    
                
        a = mock.Mock()
        proxy = Proxy(a)
    
        mock.create_autospec(proxy)

    Output:

        Traceback (most recent call last):
          File "py3_mock_functools.py", line 20, in <module>
            mock.create_autospec(proxy)
          File "/usr/lib/python3.5/unittest/mock.py", line 2156, in create_autospec
            _check_signature(spec, mock, is_type, instance)
          File "/usr/lib/python3.5/unittest/mock.py", line 112, in _check_signature
            _copy_func_details(func, checksig)
          File "/usr/lib/python3.5/unittest/mock.py", line 117, in _copy_func_details
            funcopy.__name__ = func.__name__
        AttributeError: 'functools.partial' object has no attribute '__name__'

    @bclau bclau mannequin added 3.8 only security fixes 3.7 (EOL) end of life stdlib Python modules in the Lib dir and removed 3.8 only security fixes labels Nov 28, 2017
    @TonyFlury
    Copy link
    Mannequin

    TonyFlury mannequin commented Sep 16, 2018

    It seems to me that we have three alternatives :

    1. Refuse to create the mock object with a suitable Exception (rather than a crash
    2. Copy the object and simply ignore the missing dunder_name (so that funcopy dunder_name is not set
    3. Set funcopy dunder_name to a known string when the source dunder_name is missing

    It seems obvious to me that option 3 is correct - just a question of what funcopy dunder_name should be set to. I would imagine there is code that uses funcopy dunder_name in some way ?

    @berkerpeksag
    Copy link
    Member

    I think option 2 is what functools.update_wrapper() does and it may be better to make create_autospec() consistent with it.

    Perhaps we can replace _copy_func_details() with functools.update_wrapper():

        assigments = (
            '__name__', '__doc__', '__text_signature__',
            # And the rest of the attributes in _copy_func_details().
        )
        functools.update_wrapper(..., assigned=assignments, updated=())

    @berkerpeksag berkerpeksag added 3.8 only security fixes type-bug An unexpected behavior, bug, or error labels Sep 16, 2018
    @berkerpeksag
    Copy link
    Member

    Or we can special case functools.partial() in _copy_func_details() and use partial_object.func.__name__.

    (I'm not sure which solution is better, but we need to the same thing for __doc__ as well.)

    @TonyFlury
    Copy link
    Mannequin

    TonyFlury mannequin commented Sep 16, 2018

    Am not a big fan of special casing,

    I think the functools.update_wrapper is the way to go - will have a look later and produce a pull request with some test cases.

    @tirkarthi
    Copy link
    Member

    I think the original error has been fixed with bpo-28919 where the attribute errors are ignored while copying the functions as suggested by Anthony in solution 2. So can this issue be closed as outdated to reopen a new one for using update_wrapper as enhancement or the title can be changed to reflect the fact that autospec should now use update_wrapper instead of using _copy_func_details ? Correct me if I am wrong on the workflow to update the ticket.

    Also there doesn't seem to be any test for this that can possibly added.

    Current implementation :

    def _copy_func_details(func, funcopy):
        # we explicitly don't copy func.__dict__ into this copy as it would
        # expose original attributes that should be mocked
        for attribute in (
            '__name__', '__doc__', '__text_signature__',
            '__module__', '__defaults__', '__kwdefaults__',
        ):
            try:
                setattr(funcopy, attribute, getattr(func, attribute))
            except AttributeError:
                pass

    Thanks

    @berkerpeksag
    Copy link
    Member

    Yes, it would be great if we could convert the snippet in msg307115 to a test case before closing this issue. 3.6 still has this bug, but since we are pretty close to the end of its bugfix maintenance period, we can skip it.

    @tirkarthi
    Copy link
    Member

    @berker.peksag I have created a unit test PR and verified that the test fails in 3.6 and passes on master.

    # 3.6 branch

       cpython git:(25bd107399)   ./python.exe
    Python 3.6.7+ (remotes/upstream/3.6:25bd107399, Nov  8 2018, 00:50:43)
    [GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> ^D
       cpython git:(25bd107399)   ./python.exe -m unittest -v unittest.test.testmock.testhelpers.SpecSignatureTest.test_autospec_getattr_partial_function
    test_autospec_getattr_partial_function (unittest.test.testmock.testhelpers.SpecSignatureTest) ... ERROR

    ======================================================================
    ERROR: test_autospec_getattr_partial_function (unittest.test.testmock.testhelpers.SpecSignatureTest)
    ----------------------------------------------------------------------

    Traceback (most recent call last):
      File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/test/testmock/testhelpers.py", line 884, in test_autospec_getattr_partial_function
        autospec = create_autospec(proxy)
      File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 2182, in create_autospec
        _check_signature(spec, mock, is_type, instance)
      File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 102, in _check_signature
        _copy_func_details(func, checksig)
      File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 107, in _copy_func_details
        funcopy.__name__ = func.__name__
    AttributeError: 'functools.partial' object has no attribute '__name__'

    Ran 1 test in 0.007s

    FAILED (errors=1)

    # Master

       cpython git:(bpo32153) ./python.exe
    Python 3.8.0a0 (heads/bpo32153:3e9cd8d982, Nov  8 2018, 00:53:46)
    [Clang 7.0.2 (clang-700.1.81)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> ^D
       cpython git:(bpo32153) ./python.exe -m unittest -v unittest.test.testmock.testhelpers.SpecSignatureTest.test_autospec_getattr_partial_function
    test_autospec_getattr_partial_function (unittest.test.testmock.testhelpers.SpecSignatureTest) ... ok

    Ran 1 test in 0.005s

    OK

    @cjw296
    Copy link
    Contributor

    cjw296 commented Dec 3, 2018

    New changeset c667b09 by Chris Withers (Xtreak) in branch 'master':
    bpo-32153: Add unit test for create_autospec with partial function returned in getattr (bpo-10398)
    c667b09

    @cjw296
    Copy link
    Contributor

    cjw296 commented Dec 3, 2018

    New changeset 1ef06c6 by Chris Withers (Miss Islington (bot)) in branch '3.7':
    bpo-32153: Add unit test for create_autospec with partial function returned in getattr (GH-10398) (bpo-10855)
    1ef06c6

    @tirkarthi
    Copy link
    Member

    @cjw296 since the unit tests were added and the original report is fixed with 3.7 and above can this be closed?

    @cjw296
    Copy link
    Contributor

    cjw296 commented Dec 12, 2018

    Yep! Good catch :-)

    @cjw296 cjw296 closed this as completed Dec 12, 2018
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.7 (EOL) end of life 3.8 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    3 participants