python / python/cpython

`mock.patch(..., autospec=True)` on a `functools.partialmethod` attribute produces a completely uncallable mock

Open
#153,581 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

stdlib type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:

Autospeccing a class attribute that is a functools.partialmethod (either via mock.patch.object(cls, name, autospec=True) or mock.create_autospec(cls, ...) combined with a direct
mock.patch.object on that one attribute) produces a unittest.mock.NonCallableMagicMock. Calling it then raises TypeError: 'NonCallableMagicMock' object is not callable, even though the real attribute is perfectly callable (instance.method(...) works fine on the un-mocked class).

import functools
from unittest import mock

class Client:
    def _send_request(self, method, url):
        return method, url

    _send_post_request = functools.partialmethod(_send_request, "POST")

with mock.patch.object(Client, "_send_post_request", autospec=True) as m:
    print(type(m))  # <class 'unittest.mock.NonCallableMagicMock'>
    client = Client()
    client._send_post_request("https://example.com")
    # TypeError: 'NonCallableMagicMock' object is not callable
Root cause:

_patch.get_original() (Lib/unittest/mock.py) reads the attribute via target.__dict__[name] first, deliberately bypassing the descriptor protocol (this is needed to support patching descriptors like @property correctly). For a functools.partialmethod attribute, this yields the
raw partialmethod instance rather than the bound-callable object that getattr(instance, name) would produce.

functools.partialmethod objects are not themselves callable (they have no __call__; only __get__, since they are non-data descriptors meant to be resolved through attribute access):

>>> import functools, inspect
>>> p = functools.partialmethod(lambda self, a: None, 1)
>>> callable(p)
False
>>> inspect.signature(p)
Traceback (most recent call last):
  ...
TypeError: functools.partialmethod(<function <lambda> at 0x...>, 1, ) is not a callable object
CPython versions tested on:

3.12, but tested on main as well.

Operating systems tested on:

Linux

Linked PRs
  • gh-153582

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The issue identifies Lib/unittest/mock.py and _patch.get_original(); start by running the partialmethod reproduction and tracing how autospec handles the raw descriptor. Done means patching the attribute with autospec produces a callable mock and the shown client call no longer raises TypeError, with regression coverage for the case.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
testing
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.