bpo-41877: Check for misspelled speccing arguments (GH-23737)

patch, patch.object and create_autospec silently ignore misspelled
arguments such as autospect, auto_spec and set_spec. This can lead
to tests failing to check what they are supposed to check.

This change adds a check causing a RuntimeError if the above
functions get any of the above misspellings as arguments. It also
adds a new argument, "unsafe", which can be set to True to disable
this check.

Also add "!r" to format specifiers in added error messages.
This commit is contained in:
vabr-g 2020-12-14 19:30:09 +01:00 committed by GitHub
parent 42c9f0fd0a
commit fdb9efce6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 8 deletions

View file

@ -38,6 +38,12 @@ class Something(object):
def smeth(a, b, c, d=None): pass
class Typos():
autospect = None
auto_spec = None
set_spec = None
def something(a): pass
@ -2175,6 +2181,52 @@ class MockTest(unittest.TestCase):
self.assertEqual(obj.obj_with_bool_func.__bool__.call_count, 0)
def test_misspelled_arguments(self):
class Foo():
one = 'one'
# patch, patch.object and create_autospec need to check for misspelled
# arguments explicitly and throw a RuntimError if found.
with self.assertRaises(RuntimeError):
with patch(f'{__name__}.Something.meth', autospect=True): pass
with self.assertRaises(RuntimeError):
with patch.object(Foo, 'one', autospect=True): pass
with self.assertRaises(RuntimeError):
with patch(f'{__name__}.Something.meth', auto_spec=True): pass
with self.assertRaises(RuntimeError):
with patch.object(Foo, 'one', auto_spec=True): pass
with self.assertRaises(RuntimeError):
with patch(f'{__name__}.Something.meth', set_spec=True): pass
with self.assertRaises(RuntimeError):
with patch.object(Foo, 'one', set_spec=True): pass
with self.assertRaises(RuntimeError):
m = create_autospec(Foo, set_spec=True)
# patch.multiple, on the other hand, should flag misspelled arguments
# through an AttributeError, when trying to find the keys from kwargs
# as attributes on the target.
with self.assertRaises(AttributeError):
with patch.multiple(
f'{__name__}.Something', meth=DEFAULT, autospect=True): pass
with self.assertRaises(AttributeError):
with patch.multiple(
f'{__name__}.Something', meth=DEFAULT, auto_spec=True): pass
with self.assertRaises(AttributeError):
with patch.multiple(
f'{__name__}.Something', meth=DEFAULT, set_spec=True): pass
with patch(f'{__name__}.Something.meth', unsafe=True, autospect=True):
pass
with patch.object(Foo, 'one', unsafe=True, autospect=True): pass
with patch(f'{__name__}.Something.meth', unsafe=True, auto_spec=True):
pass
with patch.object(Foo, 'one', unsafe=True, auto_spec=True): pass
with patch(f'{__name__}.Something.meth', unsafe=True, set_spec=True):
pass
with patch.object(Foo, 'one', unsafe=True, set_spec=True): pass
m = create_autospec(Foo, set_spec=True, unsafe=True)
with patch.multiple(
f'{__name__}.Typos', autospect=True, set_spec=True, auto_spec=True):
pass
if __name__ == '__main__':
unittest.main()