Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions Lib/test/test_unittest/testmock/testpatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os
import sys
from collections import OrderedDict
from types import LazyImportType, ModuleType

import unittest
import test
Expand Down Expand Up @@ -2101,5 +2102,39 @@ def test(_):
test()


class PatchLazyImportTest(unittest.TestCase):

def lazy_module(self):
# `lazy from` binds eagerly when the module it imports from is already
# imported, so publish the target only once the statement has run.
self.enterContext(uncache('lazy_patch_user', 'lazy_patch_target'))
user = ModuleType('lazy_patch_user')
exec('lazy from lazy_patch_target import function', user.__dict__)
sys.modules['lazy_patch_user'] = user
target = ModuleType('lazy_patch_target')
exec('def function(arg): pass', target.__dict__)
sys.modules['lazy_patch_target'] = target

self.assertIsInstance(user.__dict__['function'], LazyImportType)
return user

def test_autospec(self):
module = self.lazy_module()
with patch.object(module, 'function', autospec=True) as mock_function:
mock_function('arg')
with self.assertRaises(TypeError):
mock_function('arg', 'extra')

def test_spec(self):
module = self.lazy_module()
with patch.object(module, 'function', spec=True) as mock_function:
mock_function('arg')

def test_spec_set(self):
module = self.lazy_module()
with patch.object(module, 'function', spec_set=True) as mock_function:
mock_function('arg')


if __name__ == '__main__':
unittest.main()
6 changes: 5 additions & 1 deletion Lib/unittest/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import threading
from annotationlib import Format
from dataclasses import fields, is_dataclass
from types import CodeType, ModuleType, MethodType
from types import CodeType, LazyImportType, ModuleType, MethodType
from unittest.util import safe_repr
from functools import wraps, partial
from threading import RLock
Expand Down Expand Up @@ -1507,6 +1507,10 @@ def __enter__(self):

original, local = self.get_original()

if (isinstance(original, LazyImportType)
and (spec is True or spec_set is True or autospec is True)):
original = original.resolve()

if new is DEFAULT and autospec is None:
inherit = False
if spec is True:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix :func:`unittest.mock.patch` with ``autospec``, ``spec``, or ``spec_set``
when used with lazy-imported objects.
Loading