File manager - Edit - /home/u478019808/domains/bestandroidphones.store/public_html/static/img/logo/third_party.tar
Back
mock/ChangeLog 0000644 00000000137 15025013454 0007247 0 ustar 00 This is a placeholder for our changelog symlink. Actual changes will appear here after build. mock/mock/tests/testcallable.py 0000644 00000010517 15025013454 0012604 0 ustar 00 # Copyright (C) 2007-2012 Michael Foord & the mock team # E-mail: fuzzyman AT voidspace DOT org DOT uk # http://www.voidspace.org.uk/python/mock/ import unittest2 as unittest from mock.tests.support import is_instance, X, SomeClass from mock import ( Mock, MagicMock, NonCallableMagicMock, NonCallableMock, patch, create_autospec, CallableMixin ) class TestCallable(unittest.TestCase): def assertNotCallable(self, mock): self.assertTrue(is_instance(mock, NonCallableMagicMock)) self.assertFalse(is_instance(mock, CallableMixin)) def test_non_callable(self): for mock in NonCallableMagicMock(), NonCallableMock(): self.assertRaises(TypeError, mock) self.assertFalse(hasattr(mock, '__call__')) self.assertIn(mock.__class__.__name__, repr(mock)) def test_heirarchy(self): self.assertTrue(issubclass(MagicMock, Mock)) self.assertTrue(issubclass(NonCallableMagicMock, NonCallableMock)) def test_attributes(self): one = NonCallableMock() self.assertTrue(issubclass(type(one.one), Mock)) two = NonCallableMagicMock() self.assertTrue(issubclass(type(two.two), MagicMock)) def test_subclasses(self): class MockSub(Mock): pass one = MockSub() self.assertTrue(issubclass(type(one.one), MockSub)) class MagicSub(MagicMock): pass two = MagicSub() self.assertTrue(issubclass(type(two.two), MagicSub)) def test_patch_spec(self): patcher = patch('%s.X' % __name__, spec=True) mock = patcher.start() self.addCleanup(patcher.stop) instance = mock() mock.assert_called_once_with() self.assertNotCallable(instance) self.assertRaises(TypeError, instance) def test_patch_spec_set(self): patcher = patch('%s.X' % __name__, spec_set=True) mock = patcher.start() self.addCleanup(patcher.stop) instance = mock() mock.assert_called_once_with() self.assertNotCallable(instance) self.assertRaises(TypeError, instance) def test_patch_spec_instance(self): patcher = patch('%s.X' % __name__, spec=X()) mock = patcher.start() self.addCleanup(patcher.stop) self.assertNotCallable(mock) self.assertRaises(TypeError, mock) def test_patch_spec_set_instance(self): patcher = patch('%s.X' % __name__, spec_set=X()) mock = patcher.start() self.addCleanup(patcher.stop) self.assertNotCallable(mock) self.assertRaises(TypeError, mock) def test_patch_spec_callable_class(self): class CallableX(X): def __call__(self): pass class Sub(CallableX): pass class Multi(SomeClass, Sub): pass class OldStyle: def __call__(self): pass class OldStyleSub(OldStyle): pass for arg in 'spec', 'spec_set': for Klass in CallableX, Sub, Multi, OldStyle, OldStyleSub: with patch('%s.X' % __name__, **{arg: Klass}) as mock: instance = mock() mock.assert_called_once_with() self.assertTrue(is_instance(instance, MagicMock)) # inherited spec self.assertRaises(AttributeError, getattr, instance, 'foobarbaz') result = instance() # instance is callable, result has no spec instance.assert_called_once_with() result(3, 2, 1) result.assert_called_once_with(3, 2, 1) result.foo(3, 2, 1) result.foo.assert_called_once_with(3, 2, 1) def test_create_autospec(self): mock = create_autospec(X) instance = mock() self.assertRaises(TypeError, instance) mock = create_autospec(X()) self.assertRaises(TypeError, mock) def test_create_autospec_instance(self): mock = create_autospec(SomeClass, instance=True) self.assertRaises(TypeError, mock) mock.wibble() mock.wibble.assert_called_once_with() self.assertRaises(TypeError, mock.wibble, 'some', 'args') if __name__ == "__main__": unittest.main() mock/mock/tests/testmagicmethods.py 0000644 00000037773 15025013454 0013526 0 ustar 00 # Copyright (C) 2007-2012 Michael Foord & the mock team # E-mail: fuzzyman AT voidspace DOT org DOT uk # http://www.voidspace.org.uk/python/mock/ from __future__ import division try: unicode except NameError: # Python 3 unicode = str long = int import inspect import sys import textwrap import six import unittest2 as unittest from mock import Mock, MagicMock from mock.mock import _magics class TestMockingMagicMethods(unittest.TestCase): def test_deleting_magic_methods(self): mock = Mock() self.assertFalse(hasattr(mock, '__getitem__')) mock.__getitem__ = Mock() self.assertTrue(hasattr(mock, '__getitem__')) del mock.__getitem__ self.assertFalse(hasattr(mock, '__getitem__')) def test_magicmock_del(self): mock = MagicMock() # before using getitem del mock.__getitem__ self.assertRaises(TypeError, lambda: mock['foo']) mock = MagicMock() # this time use it first mock['foo'] del mock.__getitem__ self.assertRaises(TypeError, lambda: mock['foo']) def test_magic_method_wrapping(self): mock = Mock() def f(self, name): return self, 'fish' mock.__getitem__ = f self.assertIsNot(mock.__getitem__, f) self.assertEqual(mock['foo'], (mock, 'fish')) self.assertEqual(mock.__getitem__('foo'), (mock, 'fish')) mock.__getitem__ = mock self.assertIs(mock.__getitem__, mock) def test_magic_methods_isolated_between_mocks(self): mock1 = Mock() mock2 = Mock() mock1.__iter__ = Mock(return_value=iter([])) self.assertEqual(list(mock1), []) self.assertRaises(TypeError, lambda: list(mock2)) def test_repr(self): mock = Mock() self.assertEqual(repr(mock), "<Mock id='%s'>" % id(mock)) mock.__repr__ = lambda s: 'foo' self.assertEqual(repr(mock), 'foo') def test_str(self): mock = Mock() self.assertEqual(str(mock), object.__str__(mock)) mock.__str__ = lambda s: 'foo' self.assertEqual(str(mock), 'foo') @unittest.skipIf(six.PY3, "no unicode in Python 3") def test_unicode(self): mock = Mock() self.assertEqual(unicode(mock), unicode(str(mock))) mock.__unicode__ = lambda s: unicode('foo') self.assertEqual(unicode(mock), unicode('foo')) def test_dict_methods(self): mock = Mock() self.assertRaises(TypeError, lambda: mock['foo']) def _del(): del mock['foo'] def _set(): mock['foo'] = 3 self.assertRaises(TypeError, _del) self.assertRaises(TypeError, _set) _dict = {} def getitem(s, name): return _dict[name] def setitem(s, name, value): _dict[name] = value def delitem(s, name): del _dict[name] mock.__setitem__ = setitem mock.__getitem__ = getitem mock.__delitem__ = delitem self.assertRaises(KeyError, lambda: mock['foo']) mock['foo'] = 'bar' self.assertEqual(_dict, {'foo': 'bar'}) self.assertEqual(mock['foo'], 'bar') del mock['foo'] self.assertEqual(_dict, {}) def test_numeric(self): original = mock = Mock() mock.value = 0 self.assertRaises(TypeError, lambda: mock + 3) def add(self, other): mock.value += other return self mock.__add__ = add self.assertEqual(mock + 3, mock) self.assertEqual(mock.value, 3) del mock.__add__ def iadd(mock): mock += 3 self.assertRaises(TypeError, iadd, mock) mock.__iadd__ = add mock += 6 self.assertEqual(mock, original) self.assertEqual(mock.value, 9) self.assertRaises(TypeError, lambda: 3 + mock) mock.__radd__ = add self.assertEqual(7 + mock, mock) self.assertEqual(mock.value, 16) def test_division(self): original = mock = Mock() mock.value = 32 self.assertRaises(TypeError, lambda: mock / 2) def truediv(self, other): mock.value /= other return self mock.__truediv__ = truediv self.assertEqual(mock / 2, mock) self.assertEqual(mock.value, 16) del mock.__truediv__ if six.PY3: def itruediv(mock): mock /= 4 self.assertRaises(TypeError, itruediv, mock) mock.__itruediv__ = truediv mock /= 8 self.assertEqual(mock, original) self.assertEqual(mock.value, 2) else: mock.value = 2 self.assertRaises(TypeError, lambda: 8 / mock) mock.__rtruediv__ = truediv self.assertEqual(0.5 / mock, mock) self.assertEqual(mock.value, 4) def test_hash(self): mock = Mock() # test delegation self.assertEqual(hash(mock), Mock.__hash__(mock)) def _hash(s): return 3 mock.__hash__ = _hash self.assertEqual(hash(mock), 3) def test_nonzero(self): m = Mock() self.assertTrue(bool(m)) nonzero = lambda s: False if six.PY2: m.__nonzero__ = nonzero else: m.__bool__ = nonzero self.assertFalse(bool(m)) def test_comparison(self): mock = Mock() def comp(s, o): return True mock.__lt__ = mock.__gt__ = mock.__le__ = mock.__ge__ = comp self. assertTrue(mock < 3) self. assertTrue(mock > 3) self. assertTrue(mock <= 3) self. assertTrue(mock >= 3) if six.PY2: # incomparable in Python 3 self.assertEqual(Mock() < 3, object() < 3) self.assertEqual(Mock() > 3, object() > 3) self.assertEqual(Mock() <= 3, object() <= 3) self.assertEqual(Mock() >= 3, object() >= 3) else: self.assertRaises(TypeError, lambda: MagicMock() < object()) self.assertRaises(TypeError, lambda: object() < MagicMock()) self.assertRaises(TypeError, lambda: MagicMock() < MagicMock()) self.assertRaises(TypeError, lambda: MagicMock() > object()) self.assertRaises(TypeError, lambda: object() > MagicMock()) self.assertRaises(TypeError, lambda: MagicMock() > MagicMock()) self.assertRaises(TypeError, lambda: MagicMock() <= object()) self.assertRaises(TypeError, lambda: object() <= MagicMock()) self.assertRaises(TypeError, lambda: MagicMock() <= MagicMock()) self.assertRaises(TypeError, lambda: MagicMock() >= object()) self.assertRaises(TypeError, lambda: object() >= MagicMock()) self.assertRaises(TypeError, lambda: MagicMock() >= MagicMock()) def test_equality(self): for mock in Mock(), MagicMock(): self.assertEqual(mock == mock, True) self.assertIsInstance(mock == mock, bool) self.assertEqual(mock != mock, False) self.assertIsInstance(mock != mock, bool) self.assertEqual(mock == object(), False) self.assertEqual(mock != object(), True) def eq(self, other): return other == 3 mock.__eq__ = eq self.assertTrue(mock == 3) self.assertFalse(mock == 4) def ne(self, other): return other == 3 mock.__ne__ = ne self.assertTrue(mock != 3) self.assertFalse(mock != 4) mock = MagicMock() mock.__eq__.return_value = True self.assertIsInstance(mock == 3, bool) self.assertEqual(mock == 3, True) mock.__ne__.return_value = False self.assertIsInstance(mock != 3, bool) self.assertEqual(mock != 3, False) def test_len_contains_iter(self): mock = Mock() self.assertRaises(TypeError, len, mock) self.assertRaises(TypeError, iter, mock) self.assertRaises(TypeError, lambda: 'foo' in mock) mock.__len__ = lambda s: 6 self.assertEqual(len(mock), 6) mock.__contains__ = lambda s, o: o == 3 self.assertIn(3, mock) self.assertNotIn(6, mock) mock.__iter__ = lambda s: iter('foobarbaz') self.assertEqual(list(mock), list('foobarbaz')) def test_magicmock(self): mock = MagicMock() mock.__iter__.return_value = iter([1, 2, 3]) self.assertEqual(list(mock), [1, 2, 3]) name = '__nonzero__' other = '__bool__' if six.PY3: name, other = other, name getattr(mock, name).return_value = False self.assertFalse(hasattr(mock, other)) self.assertFalse(bool(mock)) for entry in _magics: self.assertTrue(hasattr(mock, entry)) self.assertFalse(hasattr(mock, '__imaginery__')) def test_magic_mock_equality(self): mock = MagicMock() self.assertIsInstance(mock == object(), bool) self.assertIsInstance(mock != object(), bool) self.assertEqual(mock == object(), False) self.assertEqual(mock != object(), True) self.assertEqual(mock == mock, True) self.assertEqual(mock != mock, False) def test_magicmock_defaults(self): mock = MagicMock() self.assertEqual(int(mock), 1) self.assertEqual(complex(mock), 1j) self.assertEqual(float(mock), 1.0) self.assertEqual(long(mock), long(1)) self.assertNotIn(object(), mock) self.assertEqual(len(mock), 0) self.assertEqual(list(mock), []) self.assertEqual(hash(mock), object.__hash__(mock)) self.assertEqual(str(mock), object.__str__(mock)) self.assertEqual(unicode(mock), object.__str__(mock)) self.assertIsInstance(unicode(mock), unicode) self.assertTrue(bool(mock)) if six.PY2: self.assertEqual(oct(mock), '1') else: # in Python 3 oct and hex use __index__ # so these tests are for __index__ in py3k self.assertEqual(oct(mock), '0o1') self.assertEqual(hex(mock), '0x1') # how to test __sizeof__ ? @unittest.skipIf(six.PY3, "no __cmp__ in Python 3") def test_non_default_magic_methods(self): mock = MagicMock() self.assertRaises(AttributeError, lambda: mock.__cmp__) mock = Mock() mock.__cmp__ = lambda s, o: 0 self.assertEqual(mock, object()) def test_magic_methods_and_spec(self): class Iterable(object): def __iter__(self): pass mock = Mock(spec=Iterable) self.assertRaises(AttributeError, lambda: mock.__iter__) mock.__iter__ = Mock(return_value=iter([])) self.assertEqual(list(mock), []) class NonIterable(object): pass mock = Mock(spec=NonIterable) self.assertRaises(AttributeError, lambda: mock.__iter__) def set_int(): mock.__int__ = Mock(return_value=iter([])) self.assertRaises(AttributeError, set_int) mock = MagicMock(spec=Iterable) self.assertEqual(list(mock), []) self.assertRaises(AttributeError, set_int) def test_magic_methods_and_spec_set(self): class Iterable(object): def __iter__(self): pass mock = Mock(spec_set=Iterable) self.assertRaises(AttributeError, lambda: mock.__iter__) mock.__iter__ = Mock(return_value=iter([])) self.assertEqual(list(mock), []) class NonIterable(object): pass mock = Mock(spec_set=NonIterable) self.assertRaises(AttributeError, lambda: mock.__iter__) def set_int(): mock.__int__ = Mock(return_value=iter([])) self.assertRaises(AttributeError, set_int) mock = MagicMock(spec_set=Iterable) self.assertEqual(list(mock), []) self.assertRaises(AttributeError, set_int) def test_setting_unsupported_magic_method(self): mock = MagicMock() def set_setattr(): mock.__setattr__ = lambda self, name: None self.assertRaisesRegex(AttributeError, "Attempting to set unsupported magic method '__setattr__'.", set_setattr ) def test_attributes_and_return_value(self): mock = MagicMock() attr = mock.foo def _get_type(obj): # the type of every mock (or magicmock) is a custom subclass # so the real type is the second in the mro return type(obj).__mro__[1] self.assertEqual(_get_type(attr), MagicMock) returned = mock() self.assertEqual(_get_type(returned), MagicMock) def test_magic_methods_are_magic_mocks(self): mock = MagicMock() self.assertIsInstance(mock.__getitem__, MagicMock) mock[1][2].__getitem__.return_value = 3 self.assertEqual(mock[1][2][3], 3) def test_magic_method_reset_mock(self): mock = MagicMock() str(mock) self.assertTrue(mock.__str__.called) mock.reset_mock() self.assertFalse(mock.__str__.called) def test_dir(self): # overriding the default implementation for mock in Mock(), MagicMock(): def _dir(self): return ['foo'] mock.__dir__ = _dir self.assertEqual(dir(mock), ['foo']) @unittest.skipIf('PyPy' in sys.version, "This fails differently on pypy") def test_bound_methods(self): m = Mock() # XXXX should this be an expected failure instead? # this seems like it should work, but is hard to do without introducing # other api inconsistencies. Failure message could be better though. m.__iter__ = [3].__iter__ self.assertRaises(TypeError, iter, m) def test_magic_method_type(self): class Foo(MagicMock): pass foo = Foo() self.assertIsInstance(foo.__int__, Foo) def test_descriptor_from_class(self): m = MagicMock() type(m).__str__.return_value = 'foo' self.assertEqual(str(m), 'foo') def test_iterable_as_iter_return_value(self): m = MagicMock() m.__iter__.return_value = [1, 2, 3] self.assertEqual(list(m), [1, 2, 3]) self.assertEqual(list(m), [1, 2, 3]) m.__iter__.return_value = iter([4, 5, 6]) self.assertEqual(list(m), [4, 5, 6]) self.assertEqual(list(m), []) @unittest.skipIf(sys.version_info < (3, 5), "@ added in Python 3.5") def test_matmul(self): src = textwrap.dedent("""\ m = MagicMock() self.assertIsInstance(m @ 1, MagicMock) m.__matmul__.return_value = 42 m.__rmatmul__.return_value = 666 m.__imatmul__.return_value = 24 self.assertEqual(m @ 1, 42) self.assertEqual(1 @ m, 666) m @= 24 self.assertEqual(m, 24) """) exec(src) def test_divmod_and_rdivmod(self): m = MagicMock() self.assertIsInstance(divmod(5, m), MagicMock) m.__divmod__.return_value = (2, 1) self.assertEqual(divmod(m, 2), (2, 1)) m = MagicMock() foo = divmod(2, m) self.assertIsInstance(foo, MagicMock) foo_direct = m.__divmod__(2) self.assertIsInstance(foo_direct, MagicMock) bar = divmod(m, 2) self.assertIsInstance(bar, MagicMock) bar_direct = m.__rdivmod__(2) self.assertIsInstance(bar_direct, MagicMock) # http://bugs.python.org/issue23310 # Check if you can change behaviour of magic methds in MagicMock init def test_magic_in_initialization(self): m = MagicMock(**{'__str__.return_value': "12"}) self.assertEqual(str(m), "12") def test_changing_magic_set_in_initialization(self): m = MagicMock(**{'__str__.return_value': "12"}) m.__str__.return_value = "13" self.assertEqual(str(m), "13") m = MagicMock(**{'__str__.return_value': "12"}) m.configure_mock(**{'__str__.return_value': "14"}) self.assertEqual(str(m), "14") if __name__ == '__main__': unittest.main() mock/mock/tests/__main__.py 0000644 00000001157 15025013454 0011665 0 ustar 00 import os import unittest def load_tests(loader, standard_tests, pattern): # top level directory cached on loader instance this_dir = os.path.dirname(__file__) pattern = pattern or "test*.py" # We are inside unittest.test.testmock, so the top-level is three notches up top_level_dir = os.path.dirname(os.path.dirname(os.path.dirname(this_dir))) package_tests = loader.discover(start_dir=this_dir, pattern=pattern, top_level_dir=top_level_dir) standard_tests.addTests(package_tests) return standard_tests if __name__ == '__main__': unittest.main() mock/mock/tests/testpatch.py 0000644 00000156033 15025013454 0012150 0 ustar 00 # Copyright (C) 2007-2012 Michael Foord & the mock team # E-mail: fuzzyman AT voidspace DOT org DOT uk # http://www.voidspace.org.uk/python/mock/ import os import sys import six import unittest2 as unittest from mock.tests import support from mock.tests.support import SomeClass, is_instance, callable from mock import ( NonCallableMock, CallableMixin, patch, sentinel, MagicMock, Mock, NonCallableMagicMock, patch, DEFAULT, call ) from mock.mock import _patch, _get_target builtin_string = '__builtin__' if six.PY3: builtin_string = 'builtins' unicode = str PTModule = sys.modules[__name__] MODNAME = '%s.PTModule' % __name__ def _get_proxy(obj, get_only=True): class Proxy(object): def __getattr__(self, name): return getattr(obj, name) if not get_only: def __setattr__(self, name, value): setattr(obj, name, value) def __delattr__(self, name): delattr(obj, name) Proxy.__setattr__ = __setattr__ Proxy.__delattr__ = __delattr__ return Proxy() # for use in the test something = sentinel.Something something_else = sentinel.SomethingElse class Foo(object): def __init__(self, a): pass def f(self, a): pass def g(self): pass foo = 'bar' class Bar(object): def a(self): pass foo_name = '%s.Foo' % __name__ def function(a, b=Foo): pass class Container(object): def __init__(self): self.values = {} def __getitem__(self, name): return self.values[name] def __setitem__(self, name, value): self.values[name] = value def __delitem__(self, name): del self.values[name] def __iter__(self): return iter(self.values) class PatchTest(unittest.TestCase): def assertNotCallable(self, obj, magic=True): MockClass = NonCallableMagicMock if not magic: MockClass = NonCallableMock self.assertRaises(TypeError, obj) self.assertTrue(is_instance(obj, MockClass)) self.assertFalse(is_instance(obj, CallableMixin)) def test_single_patchobject(self): class Something(object): attribute = sentinel.Original @patch.object(Something, 'attribute', sentinel.Patched) def test(): self.assertEqual(Something.attribute, sentinel.Patched, "unpatched") test() self.assertEqual(Something.attribute, sentinel.Original, "patch not restored") def test_patchobject_with_none(self): class Something(object): attribute = sentinel.Original @patch.object(Something, 'attribute', None) def test(): self.assertIsNone(Something.attribute, "unpatched") test() self.assertEqual(Something.attribute, sentinel.Original, "patch not restored") def test_multiple_patchobject(self): class Something(object): attribute = sentinel.Original next_attribute = sentinel.Original2 @patch.object(Something, 'attribute', sentinel.Patched) @patch.object(Something, 'next_attribute', sentinel.Patched2) def test(): self.assertEqual(Something.attribute, sentinel.Patched, "unpatched") self.assertEqual(Something.next_attribute, sentinel.Patched2, "unpatched") test() self.assertEqual(Something.attribute, sentinel.Original, "patch not restored") self.assertEqual(Something.next_attribute, sentinel.Original2, "patch not restored") def test_object_lookup_is_quite_lazy(self): global something original = something @patch('%s.something' % __name__, sentinel.Something2) def test(): pass try: something = sentinel.replacement_value test() self.assertEqual(something, sentinel.replacement_value) finally: something = original def test_patch(self): @patch('%s.something' % __name__, sentinel.Something2) def test(): self.assertEqual(PTModule.something, sentinel.Something2, "unpatched") test() self.assertEqual(PTModule.something, sentinel.Something, "patch not restored") @patch('%s.something' % __name__, sentinel.Something2) @patch('%s.something_else' % __name__, sentinel.SomethingElse) def test(): self.assertEqual(PTModule.something, sentinel.Something2, "unpatched") self.assertEqual(PTModule.something_else, sentinel.SomethingElse, "unpatched") self.assertEqual(PTModule.something, sentinel.Something, "patch not restored") self.assertEqual(PTModule.something_else, sentinel.SomethingElse, "patch not restored") # Test the patching and restoring works a second time test() self.assertEqual(PTModule.something, sentinel.Something, "patch not restored") self.assertEqual(PTModule.something_else, sentinel.SomethingElse, "patch not restored") mock = Mock() mock.return_value = sentinel.Handle @patch('%s.open' % builtin_string, mock) def test(): self.assertEqual(open('filename', 'r'), sentinel.Handle, "open not patched") test() test() self.assertNotEqual(open, mock, "patch not restored") def test_patch_class_attribute(self): @patch('%s.SomeClass.class_attribute' % __name__, sentinel.ClassAttribute) def test(): self.assertEqual(PTModule.SomeClass.class_attribute, sentinel.ClassAttribute, "unpatched") test() self.assertIsNone(PTModule.SomeClass.class_attribute, "patch not restored") def test_patchobject_with_default_mock(self): class Test(object): something = sentinel.Original something2 = sentinel.Original2 @patch.object(Test, 'something') def test(mock): self.assertEqual(mock, Test.something, "Mock not passed into test function") self.assertIsInstance(mock, MagicMock, "patch with two arguments did not create a mock") test() @patch.object(Test, 'something') @patch.object(Test, 'something2') def test(this1, this2, mock1, mock2): self.assertEqual(this1, sentinel.this1, "Patched function didn't receive initial argument") self.assertEqual(this2, sentinel.this2, "Patched function didn't receive second argument") self.assertEqual(mock1, Test.something2, "Mock not passed into test function") self.assertEqual(mock2, Test.something, "Second Mock not passed into test function") self.assertIsInstance(mock2, MagicMock, "patch with two arguments did not create a mock") self.assertIsInstance(mock2, MagicMock, "patch with two arguments did not create a mock") # A hack to test that new mocks are passed the second time self.assertNotEqual(outerMock1, mock1, "unexpected value for mock1") self.assertNotEqual(outerMock2, mock2, "unexpected value for mock1") return mock1, mock2 outerMock1 = outerMock2 = None outerMock1, outerMock2 = test(sentinel.this1, sentinel.this2) # Test that executing a second time creates new mocks test(sentinel.this1, sentinel.this2) def test_patch_with_spec(self): @patch('%s.SomeClass' % __name__, spec=SomeClass) def test(MockSomeClass): self.assertEqual(SomeClass, MockSomeClass) self.assertTrue(is_instance(SomeClass.wibble, MagicMock)) self.assertRaises(AttributeError, lambda: SomeClass.not_wibble) test() def test_patchobject_with_spec(self): @patch.object(SomeClass, 'class_attribute', spec=SomeClass) def test(MockAttribute): self.assertEqual(SomeClass.class_attribute, MockAttribute) self.assertTrue(is_instance(SomeClass.class_attribute.wibble, MagicMock)) self.assertRaises(AttributeError, lambda: SomeClass.class_attribute.not_wibble) test() def test_patch_with_spec_as_list(self): @patch('%s.SomeClass' % __name__, spec=['wibble']) def test(MockSomeClass): self.assertEqual(SomeClass, MockSomeClass) self.assertTrue(is_instance(SomeClass.wibble, MagicMock)) self.assertRaises(AttributeError, lambda: SomeClass.not_wibble) test() def test_patchobject_with_spec_as_list(self): @patch.object(SomeClass, 'class_attribute', spec=['wibble']) def test(MockAttribute): self.assertEqual(SomeClass.class_attribute, MockAttribute) self.assertTrue(is_instance(SomeClass.class_attribute.wibble, MagicMock)) self.assertRaises(AttributeError, lambda: SomeClass.class_attribute.not_wibble) test() def test_nested_patch_with_spec_as_list(self): # regression test for nested decorators @patch('%s.open' % builtin_string) @patch('%s.SomeClass' % __name__, spec=['wibble']) def test(MockSomeClass, MockOpen): self.assertEqual(SomeClass, MockSomeClass) self.assertTrue(is_instance(SomeClass.wibble, MagicMock)) self.assertRaises(AttributeError, lambda: SomeClass.not_wibble) test() def test_patch_with_spec_as_boolean(self): @patch('%s.SomeClass' % __name__, spec=True) def test(MockSomeClass): self.assertEqual(SomeClass, MockSomeClass) # Should not raise attribute error MockSomeClass.wibble self.assertRaises(AttributeError, lambda: MockSomeClass.not_wibble) test() def test_patch_object_with_spec_as_boolean(self): @patch.object(PTModule, 'SomeClass', spec=True) def test(MockSomeClass): self.assertEqual(SomeClass, MockSomeClass) # Should not raise attribute error MockSomeClass.wibble self.assertRaises(AttributeError, lambda: MockSomeClass.not_wibble) test() def test_patch_class_acts_with_spec_is_inherited(self): @patch('%s.SomeClass' % __name__, spec=True) def test(MockSomeClass): self.assertTrue(is_instance(MockSomeClass, MagicMock)) instance = MockSomeClass() self.assertNotCallable(instance) # Should not raise attribute error instance.wibble self.assertRaises(AttributeError, lambda: instance.not_wibble) test() def test_patch_with_create_mocks_non_existent_attributes(self): @patch('%s.frooble' % builtin_string, sentinel.Frooble, create=True) def test(): self.assertEqual(frooble, sentinel.Frooble) test() self.assertRaises(NameError, lambda: frooble) def test_patchobject_with_create_mocks_non_existent_attributes(self): @patch.object(SomeClass, 'frooble', sentinel.Frooble, create=True) def test(): self.assertEqual(SomeClass.frooble, sentinel.Frooble) test() self.assertFalse(hasattr(SomeClass, 'frooble')) def test_patch_wont_create_by_default(self): try: @patch('%s.frooble' % builtin_string, sentinel.Frooble) def test(): self.assertEqual(frooble, sentinel.Frooble) test() except AttributeError: pass else: self.fail('Patching non existent attributes should fail') self.assertRaises(NameError, lambda: frooble) def test_patchobject_wont_create_by_default(self): try: @patch.object(SomeClass, 'ord', sentinel.Frooble) def test(): self.fail('Patching non existent attributes should fail') test() except AttributeError: pass else: self.fail('Patching non existent attributes should fail') self.assertFalse(hasattr(SomeClass, 'ord')) def test_patch_builtins_without_create(self): @patch(__name__+'.ord') def test_ord(mock_ord): mock_ord.return_value = 101 return ord('c') @patch(__name__+'.open') def test_open(mock_open): m = mock_open.return_value m.read.return_value = 'abcd' fobj = open('doesnotexists.txt') data = fobj.read() fobj.close() return data self.assertEqual(test_ord(), 101) self.assertEqual(test_open(), 'abcd') def test_patch_with_static_methods(self): class Foo(object): @staticmethod def woot(): return sentinel.Static @patch.object(Foo, 'woot', staticmethod(lambda: sentinel.Patched)) def anonymous(): self.assertEqual(Foo.woot(), sentinel.Patched) anonymous() self.assertEqual(Foo.woot(), sentinel.Static) def test_patch_local(self): foo = sentinel.Foo @patch.object(sentinel, 'Foo', 'Foo') def anonymous(): self.assertEqual(sentinel.Foo, 'Foo') anonymous() self.assertEqual(sentinel.Foo, foo) def test_patch_slots(self): class Foo(object): __slots__ = ('Foo',) foo = Foo() foo.Foo = sentinel.Foo @patch.object(foo, 'Foo', 'Foo') def anonymous(): self.assertEqual(foo.Foo, 'Foo') anonymous() self.assertEqual(foo.Foo, sentinel.Foo) def test_patchobject_class_decorator(self): class Something(object): attribute = sentinel.Original class Foo(object): def test_method(other_self): self.assertEqual(Something.attribute, sentinel.Patched, "unpatched") def not_test_method(other_self): self.assertEqual(Something.attribute, sentinel.Original, "non-test method patched") Foo = patch.object(Something, 'attribute', sentinel.Patched)(Foo) f = Foo() f.test_method() f.not_test_method() self.assertEqual(Something.attribute, sentinel.Original, "patch not restored") def test_patch_class_decorator(self): class Something(object): attribute = sentinel.Original class Foo(object): def test_method(other_self, mock_something): self.assertEqual(PTModule.something, mock_something, "unpatched") def not_test_method(other_self): self.assertEqual(PTModule.something, sentinel.Something, "non-test method patched") Foo = patch('%s.something' % __name__)(Foo) f = Foo() f.test_method() f.not_test_method() self.assertEqual(Something.attribute, sentinel.Original, "patch not restored") self.assertEqual(PTModule.something, sentinel.Something, "patch not restored") def test_patchobject_twice(self): class Something(object): attribute = sentinel.Original next_attribute = sentinel.Original2 @patch.object(Something, 'attribute', sentinel.Patched) @patch.object(Something, 'attribute', sentinel.Patched) def test(): self.assertEqual(Something.attribute, sentinel.Patched, "unpatched") test() self.assertEqual(Something.attribute, sentinel.Original, "patch not restored") def test_patch_dict(self): foo = {'initial': object(), 'other': 'something'} original = foo.copy() @patch.dict(foo) def test(): foo['a'] = 3 del foo['initial'] foo['other'] = 'something else' test() self.assertEqual(foo, original) @patch.dict(foo, {'a': 'b'}) def test(): self.assertEqual(len(foo), 3) self.assertEqual(foo['a'], 'b') test() self.assertEqual(foo, original) @patch.dict(foo, [('a', 'b')]) def test(): self.assertEqual(len(foo), 3) self.assertEqual(foo['a'], 'b') test() self.assertEqual(foo, original) def test_patch_dict_with_container_object(self): foo = Container() foo['initial'] = object() foo['other'] = 'something' original = foo.values.copy() @patch.dict(foo) def test(): foo['a'] = 3 del foo['initial'] foo['other'] = 'something else' test() self.assertEqual(foo.values, original) @patch.dict(foo, {'a': 'b'}) def test(): self.assertEqual(len(foo.values), 3) self.assertEqual(foo['a'], 'b') test() self.assertEqual(foo.values, original) def test_patch_dict_with_clear(self): foo = {'initial': object(), 'other': 'something'} original = foo.copy() @patch.dict(foo, clear=True) def test(): self.assertEqual(foo, {}) foo['a'] = 3 foo['other'] = 'something else' test() self.assertEqual(foo, original) @patch.dict(foo, {'a': 'b'}, clear=True) def test(): self.assertEqual(foo, {'a': 'b'}) test() self.assertEqual(foo, original) @patch.dict(foo, [('a', 'b')], clear=True) def test(): self.assertEqual(foo, {'a': 'b'}) test() self.assertEqual(foo, original) def test_patch_dict_with_container_object_and_clear(self): foo = Container() foo['initial'] = object() foo['other'] = 'something' original = foo.values.copy() @patch.dict(foo, clear=True) def test(): self.assertEqual(foo.values, {}) foo['a'] = 3 foo['other'] = 'something else' test() self.assertEqual(foo.values, original) @patch.dict(foo, {'a': 'b'}, clear=True) def test(): self.assertEqual(foo.values, {'a': 'b'}) test() self.assertEqual(foo.values, original) def test_name_preserved(self): foo = {} @patch('%s.SomeClass' % __name__, object()) @patch('%s.SomeClass' % __name__, object(), autospec=True) @patch.object(SomeClass, object()) @patch.dict(foo) def some_name(): pass self.assertEqual(some_name.__name__, 'some_name') def test_patch_with_exception(self): foo = {} @patch.dict(foo, {'a': 'b'}) def test(): raise NameError('Konrad') try: test() except NameError: pass else: self.fail('NameError not raised by test') self.assertEqual(foo, {}) def test_patch_dict_with_string(self): @patch.dict('os.environ', {'konrad_delong': 'some value'}) def test(): self.assertIn('konrad_delong', os.environ) test() @unittest.expectedFailure def test_patch_descriptor(self): # would be some effort to fix this - we could special case the # builtin descriptors: classmethod, property, staticmethod class Nothing(object): foo = None class Something(object): foo = {} @patch.object(Nothing, 'foo', 2) @classmethod def klass(cls): self.assertIs(cls, Something) @patch.object(Nothing, 'foo', 2) @staticmethod def static(arg): return arg @patch.dict(foo) @classmethod def klass_dict(cls): self.assertIs(cls, Something) @patch.dict(foo) @staticmethod def static_dict(arg): return arg # these will raise exceptions if patching descriptors is broken self.assertEqual(Something.static('f00'), 'f00') Something.klass() self.assertEqual(Something.static_dict('f00'), 'f00') Something.klass_dict() something = Something() self.assertEqual(something.static('f00'), 'f00') something.klass() self.assertEqual(something.static_dict('f00'), 'f00') something.klass_dict() def test_patch_spec_set(self): @patch('%s.SomeClass' % __name__, spec_set=SomeClass) def test(MockClass): MockClass.z = 'foo' self.assertRaises(AttributeError, test) @patch.object(support, 'SomeClass', spec_set=SomeClass) def test(MockClass): MockClass.z = 'foo' self.assertRaises(AttributeError, test) @patch('%s.SomeClass' % __name__, spec_set=True) def test(MockClass): MockClass.z = 'foo' self.assertRaises(AttributeError, test) @patch.object(support, 'SomeClass', spec_set=True) def test(MockClass): MockClass.z = 'foo' self.assertRaises(AttributeError, test) def test_spec_set_inherit(self): @patch('%s.SomeClass' % __name__, spec_set=True) def test(MockClass): instance = MockClass() instance.z = 'foo' self.assertRaises(AttributeError, test) def test_patch_start_stop(self): original = something patcher = patch('%s.something' % __name__) self.assertIs(something, original) mock = patcher.start() try: self.assertIsNot(mock, original) self.assertIs(something, mock) finally: patcher.stop() self.assertIs(something, original) def test_stop_without_start(self): patcher = patch(foo_name, 'bar', 3) # calling stop without start used to produce a very obscure error self.assertRaises(RuntimeError, patcher.stop) def test_patchobject_start_stop(self): original = something patcher = patch.object(PTModule, 'something', 'foo') self.assertIs(something, original) replaced = patcher.start() try: self.assertEqual(replaced, 'foo') self.assertIs(something, replaced) finally: patcher.stop() self.assertIs(something, original) def test_patch_dict_start_stop(self): d = {'foo': 'bar'} original = d.copy() patcher = patch.dict(d, [('spam', 'eggs')], clear=True) self.assertEqual(d, original) patcher.start() try: self.assertEqual(d, {'spam': 'eggs'}) finally: patcher.stop() self.assertEqual(d, original) def test_patch_dict_class_decorator(self): this = self d = {'spam': 'eggs'} original = d.copy() class Test(object): def test_first(self): this.assertEqual(d, {'foo': 'bar'}) def test_second(self): this.assertEqual(d, {'foo': 'bar'}) Test = patch.dict(d, {'foo': 'bar'}, clear=True)(Test) self.assertEqual(d, original) test = Test() test.test_first() self.assertEqual(d, original) test.test_second() self.assertEqual(d, original) test = Test() test.test_first() self.assertEqual(d, original) test.test_second() self.assertEqual(d, original) def test_get_only_proxy(self): class Something(object): foo = 'foo' class SomethingElse: foo = 'foo' for thing in Something, SomethingElse, Something(), SomethingElse: proxy = _get_proxy(thing) @patch.object(proxy, 'foo', 'bar') def test(): self.assertEqual(proxy.foo, 'bar') test() self.assertEqual(proxy.foo, 'foo') self.assertEqual(thing.foo, 'foo') self.assertNotIn('foo', proxy.__dict__) def test_get_set_delete_proxy(self): class Something(object): foo = 'foo' class SomethingElse: foo = 'foo' for thing in Something, SomethingElse, Something(), SomethingElse: proxy = _get_proxy(Something, get_only=False) @patch.object(proxy, 'foo', 'bar') def test(): self.assertEqual(proxy.foo, 'bar') test() self.assertEqual(proxy.foo, 'foo') self.assertEqual(thing.foo, 'foo') self.assertNotIn('foo', proxy.__dict__) def test_patch_keyword_args(self): kwargs = {'side_effect': KeyError, 'foo.bar.return_value': 33, 'foo': MagicMock()} patcher = patch(foo_name, **kwargs) mock = patcher.start() patcher.stop() self.assertRaises(KeyError, mock) self.assertEqual(mock.foo.bar(), 33) self.assertIsInstance(mock.foo, MagicMock) def test_patch_object_keyword_args(self): kwargs = {'side_effect': KeyError, 'foo.bar.return_value': 33, 'foo': MagicMock()} patcher = patch.object(Foo, 'f', **kwargs) mock = patcher.start() patcher.stop() self.assertRaises(KeyError, mock) self.assertEqual(mock.foo.bar(), 33) self.assertIsInstance(mock.foo, MagicMock) def test_patch_dict_keyword_args(self): original = {'foo': 'bar'} copy = original.copy() patcher = patch.dict(original, foo=3, bar=4, baz=5) patcher.start() try: self.assertEqual(original, dict(foo=3, bar=4, baz=5)) finally: patcher.stop() self.assertEqual(original, copy) def test_autospec(self): class Boo(object): def __init__(self, a): pass def f(self, a): pass def g(self): pass foo = 'bar' class Bar(object): def a(self): pass def _test(mock): mock(1) mock.assert_called_with(1) self.assertRaises(TypeError, mock) def _test2(mock): mock.f(1) mock.f.assert_called_with(1) self.assertRaises(TypeError, mock.f) mock.g() mock.g.assert_called_with() self.assertRaises(TypeError, mock.g, 1) self.assertRaises(AttributeError, getattr, mock, 'h') mock.foo.lower() mock.foo.lower.assert_called_with() self.assertRaises(AttributeError, getattr, mock.foo, 'bar') mock.Bar() mock.Bar.assert_called_with() mock.Bar.a() mock.Bar.a.assert_called_with() self.assertRaises(TypeError, mock.Bar.a, 1) mock.Bar().a() mock.Bar().a.assert_called_with() self.assertRaises(TypeError, mock.Bar().a, 1) self.assertRaises(AttributeError, getattr, mock.Bar, 'b') self.assertRaises(AttributeError, getattr, mock.Bar(), 'b') def function(mock): _test(mock) _test2(mock) _test2(mock(1)) self.assertIs(mock, Foo) return mock test = patch(foo_name, autospec=True)(function) mock = test() self.assertIsNot(Foo, mock) # test patching a second time works test() module = sys.modules[__name__] test = patch.object(module, 'Foo', autospec=True)(function) mock = test() self.assertIsNot(Foo, mock) # test patching a second time works test() def test_autospec_function(self): @patch('%s.function' % __name__, autospec=True) def test(mock): function(1) function.assert_called_with(1) function(2, 3) function.assert_called_with(2, 3) self.assertRaises(TypeError, function) self.assertRaises(AttributeError, getattr, function, 'foo') test() def test_autospec_keywords(self): @patch('%s.function' % __name__, autospec=True, return_value=3) def test(mock_function): #self.assertEqual(function.abc, 'foo') return function(1, 2) result = test() self.assertEqual(result, 3) def test_autospec_with_new(self): patcher = patch('%s.function' % __name__, new=3, autospec=True) self.assertRaises(TypeError, patcher.start) module = sys.modules[__name__] patcher = patch.object(module, 'function', new=3, autospec=True) self.assertRaises(TypeError, patcher.start) def test_autospec_with_object(self): class Bar(Foo): extra = [] patcher = patch(foo_name, autospec=Bar) mock = patcher.start() try: self.assertIsInstance(mock, Bar) self.assertIsInstance(mock.extra, list) finally: patcher.stop() def test_autospec_inherits(self): FooClass = Foo patcher = patch(foo_name, autospec=True) mock = patcher.start() try: self.assertIsInstance(mock, FooClass) self.assertIsInstance(mock(3), FooClass) finally: patcher.stop() def test_autospec_name(self): patcher = patch(foo_name, autospec=True) mock = patcher.start() try: self.assertIn(" name='Foo'", repr(mock)) self.assertIn(" name='Foo.f'", repr(mock.f)) self.assertIn(" name='Foo()'", repr(mock(None))) self.assertIn(" name='Foo().f'", repr(mock(None).f)) finally: patcher.stop() def test_tracebacks(self): @patch.object(Foo, 'f', object()) def test(): raise AssertionError try: test() except: err = sys.exc_info() result = unittest.TextTestResult(None, None, 0) traceback = result._exc_info_to_string(err, self) self.assertIn('raise AssertionError', traceback) def test_new_callable_patch(self): patcher = patch(foo_name, new_callable=NonCallableMagicMock) m1 = patcher.start() patcher.stop() m2 = patcher.start() patcher.stop() self.assertIsNot(m1, m2) for mock in m1, m2: self.assertNotCallable(m1) def test_new_callable_patch_object(self): patcher = patch.object(Foo, 'f', new_callable=NonCallableMagicMock) m1 = patcher.start() patcher.stop() m2 = patcher.start() patcher.stop() self.assertIsNot(m1, m2) for mock in m1, m2: self.assertNotCallable(m1) def test_new_callable_keyword_arguments(self): class Bar(object): kwargs = None def __init__(self, **kwargs): Bar.kwargs = kwargs patcher = patch(foo_name, new_callable=Bar, arg1=1, arg2=2) m = patcher.start() try: self.assertIs(type(m), Bar) self.assertEqual(Bar.kwargs, dict(arg1=1, arg2=2)) finally: patcher.stop() def test_new_callable_spec(self): class Bar(object): kwargs = None def __init__(self, **kwargs): Bar.kwargs = kwargs patcher = patch(foo_name, new_callable=Bar, spec=Bar) patcher.start() try: self.assertEqual(Bar.kwargs, dict(spec=Bar)) finally: patcher.stop() patcher = patch(foo_name, new_callable=Bar, spec_set=Bar) patcher.start() try: self.assertEqual(Bar.kwargs, dict(spec_set=Bar)) finally: patcher.stop() def test_new_callable_create(self): non_existent_attr = '%s.weeeee' % foo_name p = patch(non_existent_attr, new_callable=NonCallableMock) self.assertRaises(AttributeError, p.start) p = patch(non_existent_attr, new_callable=NonCallableMock, create=True) m = p.start() try: self.assertNotCallable(m, magic=False) finally: p.stop() def test_new_callable_incompatible_with_new(self): self.assertRaises( ValueError, patch, foo_name, new=object(), new_callable=MagicMock ) self.assertRaises( ValueError, patch.object, Foo, 'f', new=object(), new_callable=MagicMock ) def test_new_callable_incompatible_with_autospec(self): self.assertRaises( ValueError, patch, foo_name, new_callable=MagicMock, autospec=True ) self.assertRaises( ValueError, patch.object, Foo, 'f', new_callable=MagicMock, autospec=True ) def test_new_callable_inherit_for_mocks(self): class MockSub(Mock): pass MockClasses = ( NonCallableMock, NonCallableMagicMock, MagicMock, Mock, MockSub ) for Klass in MockClasses: for arg in 'spec', 'spec_set': kwargs = {arg: True} p = patch(foo_name, new_callable=Klass, **kwargs) m = p.start() try: instance = m.return_value self.assertRaises(AttributeError, getattr, instance, 'x') finally: p.stop() def test_new_callable_inherit_non_mock(self): class NotAMock(object): def __init__(self, spec): self.spec = spec p = patch(foo_name, new_callable=NotAMock, spec=True) m = p.start() try: self.assertTrue(is_instance(m, NotAMock)) self.assertRaises(AttributeError, getattr, m, 'return_value') finally: p.stop() self.assertEqual(m.spec, Foo) def test_new_callable_class_decorating(self): test = self original = Foo class SomeTest(object): def _test(self, mock_foo): test.assertIsNot(Foo, original) test.assertIs(Foo, mock_foo) test.assertIsInstance(Foo, SomeClass) def test_two(self, mock_foo): self._test(mock_foo) def test_one(self, mock_foo): self._test(mock_foo) SomeTest = patch(foo_name, new_callable=SomeClass)(SomeTest) SomeTest().test_one() SomeTest().test_two() self.assertIs(Foo, original) def test_patch_multiple(self): original_foo = Foo original_f = Foo.f original_g = Foo.g patcher1 = patch.multiple(foo_name, f=1, g=2) patcher2 = patch.multiple(Foo, f=1, g=2) for patcher in patcher1, patcher2: patcher.start() try: self.assertIs(Foo, original_foo) self.assertEqual(Foo.f, 1) self.assertEqual(Foo.g, 2) finally: patcher.stop() self.assertIs(Foo, original_foo) self.assertEqual(Foo.f, original_f) self.assertEqual(Foo.g, original_g) @patch.multiple(foo_name, f=3, g=4) def test(): self.assertIs(Foo, original_foo) self.assertEqual(Foo.f, 3) self.assertEqual(Foo.g, 4) test() def test_patch_multiple_no_kwargs(self): self.assertRaises(ValueError, patch.multiple, foo_name) self.assertRaises(ValueError, patch.multiple, Foo) def test_patch_multiple_create_mocks(self): original_foo = Foo original_f = Foo.f original_g = Foo.g @patch.multiple(foo_name, f=DEFAULT, g=3, foo=DEFAULT) def test(f, foo): self.assertIs(Foo, original_foo) self.assertIs(Foo.f, f) self.assertEqual(Foo.g, 3) self.assertIs(Foo.foo, foo) self.assertTrue(is_instance(f, MagicMock)) self.assertTrue(is_instance(foo, MagicMock)) test() self.assertEqual(Foo.f, original_f) self.assertEqual(Foo.g, original_g) def test_patch_multiple_create_mocks_different_order(self): # bug revealed by Jython! original_f = Foo.f original_g = Foo.g patcher = patch.object(Foo, 'f', 3) patcher.attribute_name = 'f' other = patch.object(Foo, 'g', DEFAULT) other.attribute_name = 'g' patcher.additional_patchers = [other] @patcher def test(g): self.assertIs(Foo.g, g) self.assertEqual(Foo.f, 3) test() self.assertEqual(Foo.f, original_f) self.assertEqual(Foo.g, original_g) def test_patch_multiple_stacked_decorators(self): original_foo = Foo original_f = Foo.f original_g = Foo.g @patch.multiple(foo_name, f=DEFAULT) @patch.multiple(foo_name, foo=DEFAULT) @patch(foo_name + '.g') def test1(g, **kwargs): _test(g, **kwargs) @patch.multiple(foo_name, f=DEFAULT) @patch(foo_name + '.g') @patch.multiple(foo_name, foo=DEFAULT) def test2(g, **kwargs): _test(g, **kwargs) @patch(foo_name + '.g') @patch.multiple(foo_name, f=DEFAULT) @patch.multiple(foo_name, foo=DEFAULT) def test3(g, **kwargs): _test(g, **kwargs) def _test(g, **kwargs): f = kwargs.pop('f') foo = kwargs.pop('foo') self.assertFalse(kwargs) self.assertIs(Foo, original_foo) self.assertIs(Foo.f, f) self.assertIs(Foo.g, g) self.assertIs(Foo.foo, foo) self.assertTrue(is_instance(f, MagicMock)) self.assertTrue(is_instance(g, MagicMock)) self.assertTrue(is_instance(foo, MagicMock)) test1() test2() test3() self.assertEqual(Foo.f, original_f) self.assertEqual(Foo.g, original_g) def test_patch_multiple_create_mocks_patcher(self): original_foo = Foo original_f = Foo.f original_g = Foo.g patcher = patch.multiple(foo_name, f=DEFAULT, g=3, foo=DEFAULT) result = patcher.start() try: f = result['f'] foo = result['foo'] self.assertEqual(set(result), set(['f', 'foo'])) self.assertIs(Foo, original_foo) self.assertIs(Foo.f, f) self.assertIs(Foo.foo, foo) self.assertTrue(is_instance(f, MagicMock)) self.assertTrue(is_instance(foo, MagicMock)) finally: patcher.stop() self.assertEqual(Foo.f, original_f) self.assertEqual(Foo.g, original_g) def test_patch_multiple_decorating_class(self): test = self original_foo = Foo original_f = Foo.f original_g = Foo.g class SomeTest(object): def _test(self, f, foo): test.assertIs(Foo, original_foo) test.assertIs(Foo.f, f) test.assertEqual(Foo.g, 3) test.assertIs(Foo.foo, foo) test.assertTrue(is_instance(f, MagicMock)) test.assertTrue(is_instance(foo, MagicMock)) def test_two(self, f, foo): self._test(f, foo) def test_one(self, f, foo): self._test(f, foo) SomeTest = patch.multiple( foo_name, f=DEFAULT, g=3, foo=DEFAULT )(SomeTest) thing = SomeTest() thing.test_one() thing.test_two() self.assertEqual(Foo.f, original_f) self.assertEqual(Foo.g, original_g) def test_patch_multiple_create(self): patcher = patch.multiple(Foo, blam='blam') self.assertRaises(AttributeError, patcher.start) patcher = patch.multiple(Foo, blam='blam', create=True) patcher.start() try: self.assertEqual(Foo.blam, 'blam') finally: patcher.stop() self.assertFalse(hasattr(Foo, 'blam')) def test_patch_multiple_spec_set(self): # if spec_set works then we can assume that spec and autospec also # work as the underlying machinery is the same patcher = patch.multiple(Foo, foo=DEFAULT, spec_set=['a', 'b']) result = patcher.start() try: self.assertEqual(Foo.foo, result['foo']) Foo.foo.a(1) Foo.foo.b(2) Foo.foo.a.assert_called_with(1) Foo.foo.b.assert_called_with(2) self.assertRaises(AttributeError, setattr, Foo.foo, 'c', None) finally: patcher.stop() def test_patch_multiple_new_callable(self): class Thing(object): pass patcher = patch.multiple( Foo, f=DEFAULT, g=DEFAULT, new_callable=Thing ) result = patcher.start() try: self.assertIs(Foo.f, result['f']) self.assertIs(Foo.g, result['g']) self.assertIsInstance(Foo.f, Thing) self.assertIsInstance(Foo.g, Thing) self.assertIsNot(Foo.f, Foo.g) finally: patcher.stop() def test_nested_patch_failure(self): original_f = Foo.f original_g = Foo.g @patch.object(Foo, 'g', 1) @patch.object(Foo, 'missing', 1) @patch.object(Foo, 'f', 1) def thing1(): pass @patch.object(Foo, 'missing', 1) @patch.object(Foo, 'g', 1) @patch.object(Foo, 'f', 1) def thing2(): pass @patch.object(Foo, 'g', 1) @patch.object(Foo, 'f', 1) @patch.object(Foo, 'missing', 1) def thing3(): pass for func in thing1, thing2, thing3: self.assertRaises(AttributeError, func) self.assertEqual(Foo.f, original_f) self.assertEqual(Foo.g, original_g) def test_new_callable_failure(self): original_f = Foo.f original_g = Foo.g original_foo = Foo.foo def crasher(): raise NameError('crasher') @patch.object(Foo, 'g', 1) @patch.object(Foo, 'foo', new_callable=crasher) @patch.object(Foo, 'f', 1) def thing1(): pass @patch.object(Foo, 'foo', new_callable=crasher) @patch.object(Foo, 'g', 1) @patch.object(Foo, 'f', 1) def thing2(): pass @patch.object(Foo, 'g', 1) @patch.object(Foo, 'f', 1) @patch.object(Foo, 'foo', new_callable=crasher) def thing3(): pass for func in thing1, thing2, thing3: self.assertRaises(NameError, func) self.assertEqual(Foo.f, original_f) self.assertEqual(Foo.g, original_g) self.assertEqual(Foo.foo, original_foo) def test_patch_multiple_failure(self): original_f = Foo.f original_g = Foo.g patcher = patch.object(Foo, 'f', 1) patcher.attribute_name = 'f' good = patch.object(Foo, 'g', 1) good.attribute_name = 'g' bad = patch.object(Foo, 'missing', 1) bad.attribute_name = 'missing' for additionals in [good, bad], [bad, good]: patcher.additional_patchers = additionals @patcher def func(): pass self.assertRaises(AttributeError, func) self.assertEqual(Foo.f, original_f) self.assertEqual(Foo.g, original_g) def test_patch_multiple_new_callable_failure(self): original_f = Foo.f original_g = Foo.g original_foo = Foo.foo def crasher(): raise NameError('crasher') patcher = patch.object(Foo, 'f', 1) patcher.attribute_name = 'f' good = patch.object(Foo, 'g', 1) good.attribute_name = 'g' bad = patch.object(Foo, 'foo', new_callable=crasher) bad.attribute_name = 'foo' for additionals in [good, bad], [bad, good]: patcher.additional_patchers = additionals @patcher def func(): pass self.assertRaises(NameError, func) self.assertEqual(Foo.f, original_f) self.assertEqual(Foo.g, original_g) self.assertEqual(Foo.foo, original_foo) def test_patch_multiple_string_subclasses(self): for base in (str, unicode): Foo = type('Foo', (base,), {'fish': 'tasty'}) foo = Foo() @patch.multiple(foo, fish='nearly gone') def test(): self.assertEqual(foo.fish, 'nearly gone') test() self.assertEqual(foo.fish, 'tasty') @patch('mock.patch.TEST_PREFIX', 'foo') def test_patch_test_prefix(self): class Foo(object): thing = 'original' def foo_one(self): return self.thing def foo_two(self): return self.thing def test_one(self): return self.thing def test_two(self): return self.thing Foo = patch.object(Foo, 'thing', 'changed')(Foo) foo = Foo() self.assertEqual(foo.foo_one(), 'changed') self.assertEqual(foo.foo_two(), 'changed') self.assertEqual(foo.test_one(), 'original') self.assertEqual(foo.test_two(), 'original') @patch('mock.patch.TEST_PREFIX', 'bar') def test_patch_dict_test_prefix(self): class Foo(object): def bar_one(self): return dict(the_dict) def bar_two(self): return dict(the_dict) def test_one(self): return dict(the_dict) def test_two(self): return dict(the_dict) the_dict = {'key': 'original'} Foo = patch.dict(the_dict, key='changed')(Foo) foo =Foo() self.assertEqual(foo.bar_one(), {'key': 'changed'}) self.assertEqual(foo.bar_two(), {'key': 'changed'}) self.assertEqual(foo.test_one(), {'key': 'original'}) self.assertEqual(foo.test_two(), {'key': 'original'}) def test_patch_with_spec_mock_repr(self): for arg in ('spec', 'autospec', 'spec_set'): p = patch('%s.SomeClass' % __name__, **{arg: True}) m = p.start() try: self.assertIn(" name='SomeClass'", repr(m)) self.assertIn(" name='SomeClass.class_attribute'", repr(m.class_attribute)) self.assertIn(" name='SomeClass()'", repr(m())) self.assertIn(" name='SomeClass().class_attribute'", repr(m().class_attribute)) finally: p.stop() def test_patch_nested_autospec_repr(self): p = patch('mock.tests.support', autospec=True) m = p.start() try: self.assertIn(" name='support.SomeClass.wibble()'", repr(m.SomeClass.wibble())) self.assertIn(" name='support.SomeClass().wibble()'", repr(m.SomeClass().wibble())) finally: p.stop() def test_mock_calls_with_patch(self): for arg in ('spec', 'autospec', 'spec_set'): p = patch('%s.SomeClass' % __name__, **{arg: True}) m = p.start() try: m.wibble() kalls = [call.wibble()] self.assertEqual(m.mock_calls, kalls) self.assertEqual(m.method_calls, kalls) self.assertEqual(m.wibble.mock_calls, [call()]) result = m() kalls.append(call()) self.assertEqual(m.mock_calls, kalls) result.wibble() kalls.append(call().wibble()) self.assertEqual(m.mock_calls, kalls) self.assertEqual(result.mock_calls, [call.wibble()]) self.assertEqual(result.wibble.mock_calls, [call()]) self.assertEqual(result.method_calls, [call.wibble()]) finally: p.stop() def test_patch_imports_lazily(self): sys.modules.pop('squizz', None) p1 = patch('squizz.squozz') self.assertRaises(ImportError, p1.start) squizz = Mock() squizz.squozz = 6 sys.modules['squizz'] = squizz p1 = patch('squizz.squozz') squizz.squozz = 3 p1.start() p1.stop() self.assertEqual(squizz.squozz, 3) def test_patch_propogrates_exc_on_exit(self): class holder: exc_info = None, None, None class custom_patch(_patch): def __exit__(self, etype=None, val=None, tb=None): _patch.__exit__(self, etype, val, tb) holder.exc_info = etype, val, tb stop = __exit__ def with_custom_patch(target): getter, attribute = _get_target(target) return custom_patch( getter, attribute, DEFAULT, None, False, None, None, None, {} ) @with_custom_patch('squizz.squozz') def test(mock): raise RuntimeError self.assertRaises(RuntimeError, test) self.assertIs(holder.exc_info[0], RuntimeError) self.assertIsNotNone(holder.exc_info[1], 'exception value not propgated') self.assertIsNotNone(holder.exc_info[2], 'exception traceback not propgated') def test_create_and_specs(self): for kwarg in ('spec', 'spec_set', 'autospec'): p = patch('%s.doesnotexist' % __name__, create=True, **{kwarg: True}) self.assertRaises(TypeError, p.start) self.assertRaises(NameError, lambda: doesnotexist) # check that spec with create is innocuous if the original exists p = patch(MODNAME, create=True, **{kwarg: True}) p.start() p.stop() def test_multiple_specs(self): original = PTModule for kwarg in ('spec', 'spec_set'): p = patch(MODNAME, autospec=0, **{kwarg: 0}) self.assertRaises(TypeError, p.start) self.assertIs(PTModule, original) for kwarg in ('spec', 'autospec'): p = patch(MODNAME, spec_set=0, **{kwarg: 0}) self.assertRaises(TypeError, p.start) self.assertIs(PTModule, original) for kwarg in ('spec_set', 'autospec'): p = patch(MODNAME, spec=0, **{kwarg: 0}) self.assertRaises(TypeError, p.start) self.assertIs(PTModule, original) def test_specs_false_instead_of_none(self): p = patch(MODNAME, spec=False, spec_set=False, autospec=False) mock = p.start() try: # no spec should have been set, so attribute access should not fail mock.does_not_exist mock.does_not_exist = 3 finally: p.stop() def test_falsey_spec(self): for kwarg in ('spec', 'autospec', 'spec_set'): p = patch(MODNAME, **{kwarg: 0}) m = p.start() try: self.assertRaises(AttributeError, getattr, m, 'doesnotexit') finally: p.stop() def test_spec_set_true(self): for kwarg in ('spec', 'autospec'): p = patch(MODNAME, spec_set=True, **{kwarg: True}) m = p.start() try: self.assertRaises(AttributeError, setattr, m, 'doesnotexist', 'something') self.assertRaises(AttributeError, getattr, m, 'doesnotexist') finally: p.stop() def test_callable_spec_as_list(self): spec = ('__call__',) p = patch(MODNAME, spec=spec) m = p.start() try: self.assertTrue(callable(m)) finally: p.stop() def test_not_callable_spec_as_list(self): spec = ('foo', 'bar') p = patch(MODNAME, spec=spec) m = p.start() try: self.assertFalse(callable(m)) finally: p.stop() def test_patch_stopall(self): unlink = os.unlink chdir = os.chdir path = os.path patch('os.unlink', something).start() patch('os.chdir', something_else).start() @patch('os.path') def patched(mock_path): patch.stopall() self.assertIs(os.path, mock_path) self.assertIs(os.unlink, unlink) self.assertIs(os.chdir, chdir) patched() self.assertIs(os.path, path) def test_wrapped_patch(self): decorated = patch('sys.modules')(function) self.assertIs(decorated.__wrapped__, function) def test_wrapped_several_times_patch(self): decorated = patch('sys.modules')(function) decorated = patch('sys.modules')(decorated) self.assertIs(decorated.__wrapped__, function) def test_wrapped_patch_object(self): decorated = patch.object(sys, 'modules')(function) self.assertIs(decorated.__wrapped__, function) def test_wrapped_patch_dict(self): decorated = patch.dict('sys.modules')(function) self.assertIs(decorated.__wrapped__, function) def test_wrapped_patch_multiple(self): decorated = patch.multiple('sys', modules={})(function) self.assertIs(decorated.__wrapped__, function) def test_stopall_lifo(self): stopped = [] class thing(object): one = two = three = None def get_patch(attribute): class mypatch(_patch): def stop(self): stopped.append(attribute) return super(mypatch, self).stop() return mypatch(lambda: thing, attribute, None, None, False, None, None, None, {}) [get_patch(val).start() for val in ("one", "two", "three")] patch.stopall() self.assertEqual(stopped, ["three", "two", "one"]) def test_special_attrs(self): def foo(x=0): """TEST""" return x with patch.object(foo, '__defaults__', (1, )): self.assertEqual(foo(), 1) self.assertEqual(foo(), 0) with patch.object(foo, '__doc__', "FUN"): self.assertEqual(foo.__doc__, "FUN") self.assertEqual(foo.__doc__, "TEST") with patch.object(foo, '__module__', "testpatch2"): self.assertEqual(foo.__module__, "testpatch2") self.assertEqual(foo.__module__, __name__) if hasattr(self.test_special_attrs, '__annotations__'): with patch.object(foo, '__annotations__', dict([('s', 1, )])): self.assertEqual(foo.__annotations__, dict([('s', 1, )])) self.assertEqual(foo.__annotations__, dict()) if hasattr(self.test_special_attrs, '__kwdefaults__'): foo = eval("lambda *a, x=0: x") with patch.object(foo, '__kwdefaults__', dict([('x', 1, )])): self.assertEqual(foo(), 1) self.assertEqual(foo(), 0) if __name__ == '__main__': unittest.main() mock/mock/tests/support.py 0000644 00000001050 15025013454 0011651 0 ustar 00 import sys info = sys.version_info import unittest2 try: callable = callable except NameError: def callable(obj): return hasattr(obj, '__call__') with_available = sys.version_info[:2] >= (2, 5) def is_instance(obj, klass): """Version of is_instance that doesn't access __class__""" return issubclass(type(obj), klass) class SomeClass(object): class_attribute = None def wibble(self): pass class X(object): pass try: next = next except NameError: def next(obj): return obj.next() mock/mock/tests/testwith.py 0000644 00000024533 15025013454 0012023 0 ustar 00 # Copyright (C) 2007-2012 Michael Foord & the mock team # E-mail: fuzzyman AT voidspace DOT org DOT uk # http://www.voidspace.org.uk/python/mock/ from warnings import catch_warnings import unittest2 as unittest from mock.tests.support import is_instance from mock import MagicMock, Mock, patch, sentinel, mock_open, call something = sentinel.Something something_else = sentinel.SomethingElse class WithTest(unittest.TestCase): def test_with_statement(self): with patch('%s.something' % __name__, sentinel.Something2): self.assertEqual(something, sentinel.Something2, "unpatched") self.assertEqual(something, sentinel.Something) def test_with_statement_exception(self): try: with patch('%s.something' % __name__, sentinel.Something2): self.assertEqual(something, sentinel.Something2, "unpatched") raise Exception('pow') except Exception: pass else: self.fail("patch swallowed exception") self.assertEqual(something, sentinel.Something) def test_with_statement_as(self): with patch('%s.something' % __name__) as mock_something: self.assertEqual(something, mock_something, "unpatched") self.assertTrue(is_instance(mock_something, MagicMock), "patching wrong type") self.assertEqual(something, sentinel.Something) def test_patch_object_with_statement(self): class Foo(object): something = 'foo' original = Foo.something with patch.object(Foo, 'something'): self.assertNotEqual(Foo.something, original, "unpatched") self.assertEqual(Foo.something, original) def test_with_statement_nested(self): with catch_warnings(record=True): with patch('%s.something' % __name__) as mock_something: with patch('%s.something_else' % __name__) as mock_something_else: self.assertEqual(something, mock_something, "unpatched") self.assertEqual(something_else, mock_something_else, "unpatched") self.assertEqual(something, sentinel.Something) self.assertEqual(something_else, sentinel.SomethingElse) def test_with_statement_specified(self): with patch('%s.something' % __name__, sentinel.Patched) as mock_something: self.assertEqual(something, mock_something, "unpatched") self.assertEqual(mock_something, sentinel.Patched, "wrong patch") self.assertEqual(something, sentinel.Something) def testContextManagerMocking(self): mock = Mock() mock.__enter__ = Mock() mock.__exit__ = Mock() mock.__exit__.return_value = False with mock as m: self.assertEqual(m, mock.__enter__.return_value) mock.__enter__.assert_called_with() mock.__exit__.assert_called_with(None, None, None) def test_context_manager_with_magic_mock(self): mock = MagicMock() with self.assertRaises(TypeError): with mock: 'foo' + 3 mock.__enter__.assert_called_with() self.assertTrue(mock.__exit__.called) def test_with_statement_same_attribute(self): with patch('%s.something' % __name__, sentinel.Patched) as mock_something: self.assertEqual(something, mock_something, "unpatched") with patch('%s.something' % __name__) as mock_again: self.assertEqual(something, mock_again, "unpatched") self.assertEqual(something, mock_something, "restored with wrong instance") self.assertEqual(something, sentinel.Something, "not restored") def test_with_statement_imbricated(self): with patch('%s.something' % __name__) as mock_something: self.assertEqual(something, mock_something, "unpatched") with patch('%s.something_else' % __name__) as mock_something_else: self.assertEqual(something_else, mock_something_else, "unpatched") self.assertEqual(something, sentinel.Something) self.assertEqual(something_else, sentinel.SomethingElse) def test_dict_context_manager(self): foo = {} with patch.dict(foo, {'a': 'b'}): self.assertEqual(foo, {'a': 'b'}) self.assertEqual(foo, {}) with self.assertRaises(NameError): with patch.dict(foo, {'a': 'b'}): self.assertEqual(foo, {'a': 'b'}) raise NameError('Konrad') self.assertEqual(foo, {}) class TestMockOpen(unittest.TestCase): def test_mock_open(self): mock = mock_open() with patch('%s.open' % __name__, mock, create=True) as patched: self.assertIs(patched, mock) open('foo') mock.assert_called_once_with('foo') def test_mock_open_context_manager(self): mock = mock_open() handle = mock.return_value with patch('%s.open' % __name__, mock, create=True): with open('foo') as f: f.read() expected_calls = [call('foo'), call().__enter__(), call().read(), call().__exit__(None, None, None)] self.assertEqual(mock.mock_calls, expected_calls) self.assertIs(f, handle) def test_mock_open_context_manager_multiple_times(self): mock = mock_open() with patch('%s.open' % __name__, mock, create=True): with open('foo') as f: f.read() with open('bar') as f: f.read() expected_calls = [ call('foo'), call().__enter__(), call().read(), call().__exit__(None, None, None), call('bar'), call().__enter__(), call().read(), call().__exit__(None, None, None)] self.assertEqual(mock.mock_calls, expected_calls) def test_explicit_mock(self): mock = MagicMock() mock_open(mock) with patch('%s.open' % __name__, mock, create=True) as patched: self.assertIs(patched, mock) open('foo') mock.assert_called_once_with('foo') def test_read_data(self): mock = mock_open(read_data='foo') with patch('%s.open' % __name__, mock, create=True): h = open('bar') result = h.read() self.assertEqual(result, 'foo') def test_readline_data(self): # Check that readline will return all the lines from the fake file mock = mock_open(read_data='foo\nbar\nbaz\n') with patch('%s.open' % __name__, mock, create=True): h = open('bar') line1 = h.readline() line2 = h.readline() line3 = h.readline() self.assertEqual(line1, 'foo\n') self.assertEqual(line2, 'bar\n') self.assertEqual(line3, 'baz\n') # Check that we properly emulate a file that doesn't end in a newline mock = mock_open(read_data='foo') with patch('%s.open' % __name__, mock, create=True): h = open('bar') result = h.readline() self.assertEqual(result, 'foo') def test_readlines_data(self): # Test that emulating a file that ends in a newline character works mock = mock_open(read_data='foo\nbar\nbaz\n') with patch('%s.open' % __name__, mock, create=True): h = open('bar') result = h.readlines() self.assertEqual(result, ['foo\n', 'bar\n', 'baz\n']) # Test that files without a final newline will also be correctly # emulated mock = mock_open(read_data='foo\nbar\nbaz') with patch('%s.open' % __name__, mock, create=True): h = open('bar') result = h.readlines() self.assertEqual(result, ['foo\n', 'bar\n', 'baz']) def test_read_bytes(self): mock = mock_open(read_data=b'\xc6') with patch('%s.open' % __name__, mock, create=True): with open('abc', 'rb') as f: result = f.read() self.assertEqual(result, b'\xc6') def test_readline_bytes(self): m = mock_open(read_data=b'abc\ndef\nghi\n') with patch('%s.open' % __name__, m, create=True): with open('abc', 'rb') as f: line1 = f.readline() line2 = f.readline() line3 = f.readline() self.assertEqual(line1, b'abc\n') self.assertEqual(line2, b'def\n') self.assertEqual(line3, b'ghi\n') def test_readlines_bytes(self): m = mock_open(read_data=b'abc\ndef\nghi\n') with patch('%s.open' % __name__, m, create=True): with open('abc', 'rb') as f: result = f.readlines() self.assertEqual(result, [b'abc\n', b'def\n', b'ghi\n']) def test_mock_open_read_with_argument(self): # At one point calling read with an argument was broken # for mocks returned by mock_open some_data = 'foo\nbar\nbaz' mock = mock_open(read_data=some_data) self.assertEqual(mock().read(10), some_data) def test_interleaved_reads(self): # Test that calling read, readline, and readlines pulls data # sequentially from the data we preload with mock = mock_open(read_data='foo\nbar\nbaz\n') with patch('%s.open' % __name__, mock, create=True): h = open('bar') line1 = h.readline() rest = h.readlines() self.assertEqual(line1, 'foo\n') self.assertEqual(rest, ['bar\n', 'baz\n']) mock = mock_open(read_data='foo\nbar\nbaz\n') with patch('%s.open' % __name__, mock, create=True): h = open('bar') line1 = h.readline() rest = h.read() self.assertEqual(line1, 'foo\n') self.assertEqual(rest, 'bar\nbaz\n') def test_overriding_return_values(self): mock = mock_open(read_data='foo') handle = mock() handle.read.return_value = 'bar' handle.readline.return_value = 'bar' handle.readlines.return_value = ['bar'] self.assertEqual(handle.read(), 'bar') self.assertEqual(handle.readline(), 'bar') self.assertEqual(handle.readlines(), ['bar']) # call repeatedly to check that a StopIteration is not propagated self.assertEqual(handle.readline(), 'bar') self.assertEqual(handle.readline(), 'bar') if __name__ == '__main__': unittest.main() mock/mock/tests/testhelpers.py 0000644 00000070031 15025013454 0012504 0 ustar 00 # Copyright (C) 2007-2012 Michael Foord & the mock team # E-mail: fuzzyman AT voidspace DOT org DOT uk # http://www.voidspace.org.uk/python/mock/ import six import unittest2 as unittest from mock import ( call, create_autospec, MagicMock, Mock, ANY, patch, PropertyMock ) from mock.mock import _Call, _CallList from datetime import datetime class SomeClass(object): def one(self, a, b): pass def two(self): pass def three(self, a=None): pass class AnyTest(unittest.TestCase): def test_any(self): self.assertEqual(ANY, object()) mock = Mock() mock(ANY) mock.assert_called_with(ANY) mock = Mock() mock(foo=ANY) mock.assert_called_with(foo=ANY) def test_repr(self): self.assertEqual(repr(ANY), '<ANY>') self.assertEqual(str(ANY), '<ANY>') def test_any_and_datetime(self): mock = Mock() mock(datetime.now(), foo=datetime.now()) mock.assert_called_with(ANY, foo=ANY) def test_any_mock_calls_comparison_order(self): mock = Mock() d = datetime.now() class Foo(object): def __eq__(self, other): return False def __ne__(self, other): return True for d in datetime.now(), Foo(): mock.reset_mock() mock(d, foo=d, bar=d) mock.method(d, zinga=d, alpha=d) mock().method(a1=d, z99=d) expected = [ call(ANY, foo=ANY, bar=ANY), call.method(ANY, zinga=ANY, alpha=ANY), call(), call().method(a1=ANY, z99=ANY) ] self.assertEqual(expected, mock.mock_calls) self.assertEqual(mock.mock_calls, expected) class CallTest(unittest.TestCase): def test_call_with_call(self): kall = _Call() self.assertEqual(kall, _Call()) self.assertEqual(kall, _Call(('',))) self.assertEqual(kall, _Call(((),))) self.assertEqual(kall, _Call(({},))) self.assertEqual(kall, _Call(('', ()))) self.assertEqual(kall, _Call(('', {}))) self.assertEqual(kall, _Call(('', (), {}))) self.assertEqual(kall, _Call(('foo',))) self.assertEqual(kall, _Call(('bar', ()))) self.assertEqual(kall, _Call(('baz', {}))) self.assertEqual(kall, _Call(('spam', (), {}))) kall = _Call(((1, 2, 3),)) self.assertEqual(kall, _Call(((1, 2, 3),))) self.assertEqual(kall, _Call(('', (1, 2, 3)))) self.assertEqual(kall, _Call(((1, 2, 3), {}))) self.assertEqual(kall, _Call(('', (1, 2, 3), {}))) kall = _Call(((1, 2, 4),)) self.assertNotEqual(kall, _Call(('', (1, 2, 3)))) self.assertNotEqual(kall, _Call(('', (1, 2, 3), {}))) kall = _Call(('foo', (1, 2, 4),)) self.assertNotEqual(kall, _Call(('', (1, 2, 4)))) self.assertNotEqual(kall, _Call(('', (1, 2, 4), {}))) self.assertNotEqual(kall, _Call(('bar', (1, 2, 4)))) self.assertNotEqual(kall, _Call(('bar', (1, 2, 4), {}))) kall = _Call(({'a': 3},)) self.assertEqual(kall, _Call(('', (), {'a': 3}))) self.assertEqual(kall, _Call(('', {'a': 3}))) self.assertEqual(kall, _Call(((), {'a': 3}))) self.assertEqual(kall, _Call(({'a': 3},))) def test_empty__Call(self): args = _Call() self.assertEqual(args, ()) self.assertEqual(args, ('foo',)) self.assertEqual(args, ((),)) self.assertEqual(args, ('foo', ())) self.assertEqual(args, ('foo',(), {})) self.assertEqual(args, ('foo', {})) self.assertEqual(args, ({},)) def test_named_empty_call(self): args = _Call(('foo', (), {})) self.assertEqual(args, ('foo',)) self.assertEqual(args, ('foo', ())) self.assertEqual(args, ('foo',(), {})) self.assertEqual(args, ('foo', {})) self.assertNotEqual(args, ((),)) self.assertNotEqual(args, ()) self.assertNotEqual(args, ({},)) self.assertNotEqual(args, ('bar',)) self.assertNotEqual(args, ('bar', ())) self.assertNotEqual(args, ('bar', {})) def test_call_with_args(self): args = _Call(((1, 2, 3), {})) self.assertEqual(args, ((1, 2, 3),)) self.assertEqual(args, ('foo', (1, 2, 3))) self.assertEqual(args, ('foo', (1, 2, 3), {})) self.assertEqual(args, ((1, 2, 3), {})) def test_named_call_with_args(self): args = _Call(('foo', (1, 2, 3), {})) self.assertEqual(args, ('foo', (1, 2, 3))) self.assertEqual(args, ('foo', (1, 2, 3), {})) self.assertNotEqual(args, ((1, 2, 3),)) self.assertNotEqual(args, ((1, 2, 3), {})) def test_call_with_kwargs(self): args = _Call(((), dict(a=3, b=4))) self.assertEqual(args, (dict(a=3, b=4),)) self.assertEqual(args, ('foo', dict(a=3, b=4))) self.assertEqual(args, ('foo', (), dict(a=3, b=4))) self.assertEqual(args, ((), dict(a=3, b=4))) def test_named_call_with_kwargs(self): args = _Call(('foo', (), dict(a=3, b=4))) self.assertEqual(args, ('foo', dict(a=3, b=4))) self.assertEqual(args, ('foo', (), dict(a=3, b=4))) self.assertNotEqual(args, (dict(a=3, b=4),)) self.assertNotEqual(args, ((), dict(a=3, b=4))) def test_call_with_args_call_empty_name(self): args = _Call(((1, 2, 3), {})) self.assertEqual(args, call(1, 2, 3)) self.assertEqual(call(1, 2, 3), args) self.assertIn(call(1, 2, 3), [args]) def test_call_ne(self): self.assertNotEqual(_Call(((1, 2, 3),)), call(1, 2)) self.assertFalse(_Call(((1, 2, 3),)) != call(1, 2, 3)) self.assertTrue(_Call(((1, 2), {})) != call(1, 2, 3)) def test_call_non_tuples(self): kall = _Call(((1, 2, 3),)) for value in 1, None, self, int: self.assertNotEqual(kall, value) self.assertFalse(kall == value) def test_repr(self): self.assertEqual(repr(_Call()), 'call()') self.assertEqual(repr(_Call(('foo',))), 'call.foo()') self.assertEqual(repr(_Call(((1, 2, 3), {'a': 'b'}))), "call(1, 2, 3, a='b')") self.assertEqual(repr(_Call(('bar', (1, 2, 3), {'a': 'b'}))), "call.bar(1, 2, 3, a='b')") self.assertEqual(repr(call), 'call') self.assertEqual(str(call), 'call') self.assertEqual(repr(call()), 'call()') self.assertEqual(repr(call(1)), 'call(1)') self.assertEqual(repr(call(zz='thing')), "call(zz='thing')") self.assertEqual(repr(call().foo), 'call().foo') self.assertEqual(repr(call(1).foo.bar(a=3).bing), 'call().foo.bar().bing') self.assertEqual( repr(call().foo(1, 2, a=3)), "call().foo(1, 2, a=3)" ) self.assertEqual(repr(call()()), "call()()") self.assertEqual(repr(call(1)(2)), "call()(2)") self.assertEqual( repr(call()().bar().baz.beep(1)), "call()().bar().baz.beep(1)" ) def test_call(self): self.assertEqual(call(), ('', (), {})) self.assertEqual(call('foo', 'bar', one=3, two=4), ('', ('foo', 'bar'), {'one': 3, 'two': 4})) mock = Mock() mock(1, 2, 3) mock(a=3, b=6) self.assertEqual(mock.call_args_list, [call(1, 2, 3), call(a=3, b=6)]) def test_attribute_call(self): self.assertEqual(call.foo(1), ('foo', (1,), {})) self.assertEqual(call.bar.baz(fish='eggs'), ('bar.baz', (), {'fish': 'eggs'})) mock = Mock() mock.foo(1, 2 ,3) mock.bar.baz(a=3, b=6) self.assertEqual(mock.method_calls, [call.foo(1, 2, 3), call.bar.baz(a=3, b=6)]) def test_extended_call(self): result = call(1).foo(2).bar(3, a=4) self.assertEqual(result, ('().foo().bar', (3,), dict(a=4))) mock = MagicMock() mock(1, 2, a=3, b=4) self.assertEqual(mock.call_args, call(1, 2, a=3, b=4)) self.assertNotEqual(mock.call_args, call(1, 2, 3)) self.assertEqual(mock.call_args_list, [call(1, 2, a=3, b=4)]) self.assertEqual(mock.mock_calls, [call(1, 2, a=3, b=4)]) mock = MagicMock() mock.foo(1).bar()().baz.beep(a=6) last_call = call.foo(1).bar()().baz.beep(a=6) self.assertEqual(mock.mock_calls[-1], last_call) self.assertEqual(mock.mock_calls, last_call.call_list()) def test_call_list(self): mock = MagicMock() mock(1) self.assertEqual(call(1).call_list(), mock.mock_calls) mock = MagicMock() mock(1).method(2) self.assertEqual(call(1).method(2).call_list(), mock.mock_calls) mock = MagicMock() mock(1).method(2)(3) self.assertEqual(call(1).method(2)(3).call_list(), mock.mock_calls) mock = MagicMock() int(mock(1).method(2)(3).foo.bar.baz(4)(5)) kall = call(1).method(2)(3).foo.bar.baz(4)(5).__int__() self.assertEqual(kall.call_list(), mock.mock_calls) def test_call_any(self): self.assertEqual(call, ANY) m = MagicMock() int(m) self.assertEqual(m.mock_calls, [ANY]) self.assertEqual([ANY], m.mock_calls) def test_two_args_call(self): args = _Call(((1, 2), {'a': 3}), two=True) self.assertEqual(len(args), 2) self.assertEqual(args[0], (1, 2)) self.assertEqual(args[1], {'a': 3}) other_args = _Call(((1, 2), {'a': 3})) self.assertEqual(args, other_args) class SpecSignatureTest(unittest.TestCase): def _check_someclass_mock(self, mock): self.assertRaises(AttributeError, getattr, mock, 'foo') mock.one(1, 2) mock.one.assert_called_with(1, 2) self.assertRaises(AssertionError, mock.one.assert_called_with, 3, 4) self.assertRaises(TypeError, mock.one, 1) mock.two() mock.two.assert_called_with() self.assertRaises(AssertionError, mock.two.assert_called_with, 3) self.assertRaises(TypeError, mock.two, 1) mock.three() mock.three.assert_called_with() self.assertRaises(AssertionError, mock.three.assert_called_with, 3) self.assertRaises(TypeError, mock.three, 3, 2) mock.three(1) mock.three.assert_called_with(1) mock.three(a=1) mock.three.assert_called_with(a=1) def test_basic(self): mock = create_autospec(SomeClass) self._check_someclass_mock(mock) mock = create_autospec(SomeClass()) self._check_someclass_mock(mock) def test_create_autospec_return_value(self): def f(): pass mock = create_autospec(f, return_value='foo') self.assertEqual(mock(), 'foo') class Foo(object): pass mock = create_autospec(Foo, return_value='foo') self.assertEqual(mock(), 'foo') def test_autospec_reset_mock(self): m = create_autospec(int) int(m) m.reset_mock() self.assertEqual(m.__int__.call_count, 0) def test_mocking_unbound_methods(self): class Foo(object): def foo(self, foo): pass p = patch.object(Foo, 'foo') mock_foo = p.start() Foo().foo(1) mock_foo.assert_called_with(1) @unittest.expectedFailure def test_create_autospec_unbound_methods(self): # see mock issue 128 class Foo(object): def foo(self): pass klass = create_autospec(Foo) instance = klass() self.assertRaises(TypeError, instance.foo, 1) # Note: no type checking on the "self" parameter klass.foo(1) klass.foo.assert_called_with(1) self.assertRaises(TypeError, klass.foo) def test_create_autospec_keyword_arguments(self): class Foo(object): a = 3 m = create_autospec(Foo, a='3') self.assertEqual(m.a, '3') @unittest.skipUnless(six.PY3, "Keyword only arguments Python 3 specific") def test_create_autospec_keyword_only_arguments(self): func_def = "def foo(a, *, b=None):\n pass\n" namespace = {} exec (func_def, namespace) foo = namespace['foo'] m = create_autospec(foo) m(1) m.assert_called_with(1) self.assertRaises(TypeError, m, 1, 2) m(2, b=3) m.assert_called_with(2, b=3) def test_function_as_instance_attribute(self): obj = SomeClass() def f(a): pass obj.f = f mock = create_autospec(obj) mock.f('bing') mock.f.assert_called_with('bing') def test_spec_as_list(self): # because spec as a list of strings in the mock constructor means # something very different we treat a list instance as the type. mock = create_autospec([]) mock.append('foo') mock.append.assert_called_with('foo') self.assertRaises(AttributeError, getattr, mock, 'foo') class Foo(object): foo = [] mock = create_autospec(Foo) mock.foo.append(3) mock.foo.append.assert_called_with(3) self.assertRaises(AttributeError, getattr, mock.foo, 'foo') def test_attributes(self): class Sub(SomeClass): attr = SomeClass() sub_mock = create_autospec(Sub) for mock in (sub_mock, sub_mock.attr): self._check_someclass_mock(mock) def test_builtin_functions_types(self): # we could replace builtin functions / methods with a function # with *args / **kwargs signature. Using the builtin method type # as a spec seems to work fairly well though. class BuiltinSubclass(list): def bar(self, arg): pass sorted = sorted attr = {} mock = create_autospec(BuiltinSubclass) mock.append(3) mock.append.assert_called_with(3) self.assertRaises(AttributeError, getattr, mock.append, 'foo') mock.bar('foo') mock.bar.assert_called_with('foo') self.assertRaises(TypeError, mock.bar, 'foo', 'bar') self.assertRaises(AttributeError, getattr, mock.bar, 'foo') mock.sorted([1, 2]) mock.sorted.assert_called_with([1, 2]) self.assertRaises(AttributeError, getattr, mock.sorted, 'foo') mock.attr.pop(3) mock.attr.pop.assert_called_with(3) self.assertRaises(AttributeError, getattr, mock.attr, 'foo') def test_method_calls(self): class Sub(SomeClass): attr = SomeClass() mock = create_autospec(Sub) mock.one(1, 2) mock.two() mock.three(3) expected = [call.one(1, 2), call.two(), call.three(3)] self.assertEqual(mock.method_calls, expected) mock.attr.one(1, 2) mock.attr.two() mock.attr.three(3) expected.extend( [call.attr.one(1, 2), call.attr.two(), call.attr.three(3)] ) self.assertEqual(mock.method_calls, expected) def test_magic_methods(self): class BuiltinSubclass(list): attr = {} mock = create_autospec(BuiltinSubclass) self.assertEqual(list(mock), []) self.assertRaises(TypeError, int, mock) self.assertRaises(TypeError, int, mock.attr) self.assertEqual(list(mock), []) self.assertIsInstance(mock['foo'], MagicMock) self.assertIsInstance(mock.attr['foo'], MagicMock) def test_spec_set(self): class Sub(SomeClass): attr = SomeClass() for spec in (Sub, Sub()): mock = create_autospec(spec, spec_set=True) self._check_someclass_mock(mock) self.assertRaises(AttributeError, setattr, mock, 'foo', 'bar') self.assertRaises(AttributeError, setattr, mock.attr, 'foo', 'bar') def test_descriptors(self): class Foo(object): @classmethod def f(cls, a, b): pass @staticmethod def g(a, b): pass class Bar(Foo): pass class Baz(SomeClass, Bar): pass for spec in (Foo, Foo(), Bar, Bar(), Baz, Baz()): mock = create_autospec(spec) mock.f(1, 2) mock.f.assert_called_once_with(1, 2) mock.g(3, 4) mock.g.assert_called_once_with(3, 4) @unittest.skipIf(six.PY3, "No old style classes in Python 3") def test_old_style_classes(self): class Foo: def f(self, a, b): pass class Bar(Foo): g = Foo() for spec in (Foo, Foo(), Bar, Bar()): mock = create_autospec(spec) mock.f(1, 2) mock.f.assert_called_once_with(1, 2) self.assertRaises(AttributeError, getattr, mock, 'foo') self.assertRaises(AttributeError, getattr, mock.f, 'foo') mock.g.f(1, 2) mock.g.f.assert_called_once_with(1, 2) self.assertRaises(AttributeError, getattr, mock.g, 'foo') def test_recursive(self): class A(object): def a(self): pass foo = 'foo bar baz' bar = foo A.B = A mock = create_autospec(A) mock() self.assertFalse(mock.B.called) mock.a() mock.B.a() self.assertEqual(mock.method_calls, [call.a(), call.B.a()]) self.assertIs(A.foo, A.bar) self.assertIsNot(mock.foo, mock.bar) mock.foo.lower() self.assertRaises(AssertionError, mock.bar.lower.assert_called_with) def test_spec_inheritance_for_classes(self): class Foo(object): def a(self, x): pass class Bar(object): def f(self, y): pass class_mock = create_autospec(Foo) self.assertIsNot(class_mock, class_mock()) for this_mock in class_mock, class_mock(): this_mock.a(x=5) this_mock.a.assert_called_with(x=5) this_mock.a.assert_called_with(5) self.assertRaises(TypeError, this_mock.a, 'foo', 'bar') self.assertRaises(AttributeError, getattr, this_mock, 'b') instance_mock = create_autospec(Foo()) instance_mock.a(5) instance_mock.a.assert_called_with(5) instance_mock.a.assert_called_with(x=5) self.assertRaises(TypeError, instance_mock.a, 'foo', 'bar') self.assertRaises(AttributeError, getattr, instance_mock, 'b') # The return value isn't isn't callable self.assertRaises(TypeError, instance_mock) instance_mock.Bar.f(6) instance_mock.Bar.f.assert_called_with(6) instance_mock.Bar.f.assert_called_with(y=6) self.assertRaises(AttributeError, getattr, instance_mock.Bar, 'g') instance_mock.Bar().f(6) instance_mock.Bar().f.assert_called_with(6) instance_mock.Bar().f.assert_called_with(y=6) self.assertRaises(AttributeError, getattr, instance_mock.Bar(), 'g') def test_inherit(self): class Foo(object): a = 3 Foo.Foo = Foo # class mock = create_autospec(Foo) instance = mock() self.assertRaises(AttributeError, getattr, instance, 'b') attr_instance = mock.Foo() self.assertRaises(AttributeError, getattr, attr_instance, 'b') # instance mock = create_autospec(Foo()) self.assertRaises(AttributeError, getattr, mock, 'b') self.assertRaises(TypeError, mock) # attribute instance call_result = mock.Foo() self.assertRaises(AttributeError, getattr, call_result, 'b') def test_builtins(self): # used to fail with infinite recursion create_autospec(1) create_autospec(int) create_autospec('foo') create_autospec(str) create_autospec({}) create_autospec(dict) create_autospec([]) create_autospec(list) create_autospec(set()) create_autospec(set) create_autospec(1.0) create_autospec(float) create_autospec(1j) create_autospec(complex) create_autospec(False) create_autospec(True) def test_function(self): def f(a, b): pass mock = create_autospec(f) self.assertRaises(TypeError, mock) mock(1, 2) mock.assert_called_with(1, 2) mock.assert_called_with(1, b=2) mock.assert_called_with(a=1, b=2) f.f = f mock = create_autospec(f) self.assertRaises(TypeError, mock.f) mock.f(3, 4) mock.f.assert_called_with(3, 4) mock.f.assert_called_with(a=3, b=4) def test_skip_attributeerrors(self): class Raiser(object): def __get__(self, obj, type=None): if obj is None: raise AttributeError('Can only be accessed via an instance') class RaiserClass(object): raiser = Raiser() @staticmethod def existing(a, b): return a + b s = create_autospec(RaiserClass) self.assertRaises(TypeError, lambda x: s.existing(1, 2, 3)) s.existing(1, 2) self.assertRaises(AttributeError, lambda: s.nonexisting) # check we can fetch the raiser attribute and it has no spec obj = s.raiser obj.foo, obj.bar def test_signature_class(self): class Foo(object): def __init__(self, a, b=3): pass mock = create_autospec(Foo) self.assertRaises(TypeError, mock) mock(1) mock.assert_called_once_with(1) mock(4, 5) mock.assert_called_with(4, 5) @unittest.skipIf(six.PY3, 'no old style classes in Python 3') def test_signature_old_style_class(self): class Foo: def __init__(self, a, b=3): pass mock = create_autospec(Foo) self.assertRaises(TypeError, mock) mock(1) mock.assert_called_once_with(1) mock.assert_called_once_with(a=1) self.assertRaises(AssertionError, mock.assert_called_once_with, 2) mock(4, 5) mock.assert_called_with(4, 5) mock.assert_called_with(a=4, b=5) self.assertRaises(AssertionError, mock.assert_called_with, a=5, b=4) def test_class_with_no_init(self): # this used to raise an exception # due to trying to get a signature from object.__init__ class Foo(object): pass create_autospec(Foo) @unittest.skipIf(six.PY3, 'no old style classes in Python 3') def test_old_style_class_with_no_init(self): # this used to raise an exception # due to Foo.__init__ raising an AttributeError class Foo: pass create_autospec(Foo) def test_signature_callable(self): class Callable(object): def __init__(self, x, y): pass def __call__(self, a): pass mock = create_autospec(Callable) mock(1, 2) mock.assert_called_once_with(1, 2) mock.assert_called_once_with(x=1, y=2) self.assertRaises(TypeError, mock, 'a') instance = mock(1, 2) self.assertRaises(TypeError, instance) instance(a='a') instance.assert_called_once_with('a') instance.assert_called_once_with(a='a') instance('a') instance.assert_called_with('a') instance.assert_called_with(a='a') mock = create_autospec(Callable(1, 2)) mock(a='a') mock.assert_called_once_with(a='a') self.assertRaises(TypeError, mock) mock('a') mock.assert_called_with('a') def test_signature_noncallable(self): class NonCallable(object): def __init__(self): pass mock = create_autospec(NonCallable) instance = mock() mock.assert_called_once_with() self.assertRaises(TypeError, mock, 'a') self.assertRaises(TypeError, instance) self.assertRaises(TypeError, instance, 'a') mock = create_autospec(NonCallable()) self.assertRaises(TypeError, mock) self.assertRaises(TypeError, mock, 'a') def test_create_autospec_none(self): class Foo(object): bar = None mock = create_autospec(Foo) none = mock.bar self.assertNotIsInstance(none, type(None)) none.foo() none.foo.assert_called_once_with() def test_autospec_functions_with_self_in_odd_place(self): class Foo(object): def f(a, self): pass a = create_autospec(Foo) a.f(10) a.f.assert_called_with(10) a.f.assert_called_with(self=10) a.f(self=10) a.f.assert_called_with(10) a.f.assert_called_with(self=10) def test_autospec_property(self): class Foo(object): @property def foo(self): return 3 foo = create_autospec(Foo) mock_property = foo.foo # no spec on properties self.assertIsInstance(mock_property, MagicMock) mock_property(1, 2, 3) mock_property.abc(4, 5, 6) mock_property.assert_called_once_with(1, 2, 3) mock_property.abc.assert_called_once_with(4, 5, 6) def test_autospec_slots(self): class Foo(object): __slots__ = ['a'] foo = create_autospec(Foo) mock_slot = foo.a # no spec on slots mock_slot(1, 2, 3) mock_slot.abc(4, 5, 6) mock_slot.assert_called_once_with(1, 2, 3) mock_slot.abc.assert_called_once_with(4, 5, 6) class TestCallList(unittest.TestCase): def test_args_list_contains_call_list(self): mock = Mock() self.assertIsInstance(mock.call_args_list, _CallList) mock(1, 2) mock(a=3) mock(3, 4) mock(b=6) for kall in call(1, 2), call(a=3), call(3, 4), call(b=6): self.assertIn(kall, mock.call_args_list) calls = [call(a=3), call(3, 4)] self.assertIn(calls, mock.call_args_list) calls = [call(1, 2), call(a=3)] self.assertIn(calls, mock.call_args_list) calls = [call(3, 4), call(b=6)] self.assertIn(calls, mock.call_args_list) calls = [call(3, 4)] self.assertIn(calls, mock.call_args_list) self.assertNotIn(call('fish'), mock.call_args_list) self.assertNotIn([call('fish')], mock.call_args_list) def test_call_list_str(self): mock = Mock() mock(1, 2) mock.foo(a=3) mock.foo.bar().baz('fish', cat='dog') expected = ( "[call(1, 2),\n" " call.foo(a=3),\n" " call.foo.bar(),\n" " call.foo.bar().baz('fish', cat='dog')]" ) self.assertEqual(str(mock.mock_calls), expected) @unittest.skipIf(six.PY3, "Unicode is properly handled with Python 3") def test_call_list_unicode(self): # See github issue #328 mock = Mock() class NonAsciiRepr(object): def __repr__(self): return "\xe9" mock(**{unicode("a"): NonAsciiRepr()}) self.assertEqual(str(mock.mock_calls), "[call(a=\xe9)]") def test_propertymock(self): p = patch('%s.SomeClass.one' % __name__, new_callable=PropertyMock) mock = p.start() try: SomeClass.one mock.assert_called_once_with() s = SomeClass() s.one mock.assert_called_with() self.assertEqual(mock.mock_calls, [call(), call()]) s.one = 3 self.assertEqual(mock.mock_calls, [call(), call(), call(3)]) finally: p.stop() def test_propertymock_returnvalue(self): m = MagicMock() p = PropertyMock() type(m).foo = p returned = m.foo p.assert_called_once_with() self.assertIsInstance(returned, MagicMock) self.assertNotIsInstance(returned, PropertyMock) if __name__ == '__main__': unittest.main() mock/mock/tests/__init__.py 0000644 00000000222 15025013454 0011674 0 ustar 00 # Copyright (C) 2007-2012 Michael Foord & the mock team # E-mail: fuzzyman AT voidspace DOT org DOT uk # http://www.voidspace.org.uk/python/mock/ mock/mock/tests/testsentinel.py 0000644 00000001720 15025013454 0012662 0 ustar 00 # Copyright (C) 2007-2012 Michael Foord & the mock team # E-mail: fuzzyman AT voidspace DOT org DOT uk # http://www.voidspace.org.uk/python/mock/ import unittest2 as unittest from mock import sentinel, DEFAULT class SentinelTest(unittest.TestCase): def testSentinels(self): self.assertEqual(sentinel.whatever, sentinel.whatever, 'sentinel not stored') self.assertNotEqual(sentinel.whatever, sentinel.whateverelse, 'sentinel should be unique') def testSentinelName(self): self.assertEqual(str(sentinel.whatever), 'sentinel.whatever', 'sentinel name incorrect') def testDEFAULT(self): self.assertIs(DEFAULT, sentinel.DEFAULT) def testBases(self): # If this doesn't raise an AttributeError then help(mock) is broken self.assertRaises(AttributeError, lambda: sentinel.__bases__) if __name__ == '__main__': unittest.main() mock/mock/tests/testmock.py 0000644 00000142446 15025013454 0012005 0 ustar 00 # Copyright (C) 2007-2012 Michael Foord & the mock team # E-mail: fuzzyman AT voidspace DOT org DOT uk # http://www.voidspace.org.uk/python/mock/ import copy import pickle import sys import tempfile import six import unittest2 as unittest import mock from mock import ( call, DEFAULT, patch, sentinel, MagicMock, Mock, NonCallableMock, NonCallableMagicMock, create_autospec ) from mock.mock import _CallList from mock.tests.support import ( callable, is_instance, next ) try: unicode except NameError: unicode = str class Iter(object): def __init__(self): self.thing = iter(['this', 'is', 'an', 'iter']) def __iter__(self): return self def next(self): return next(self.thing) __next__ = next class Something(object): def meth(self, a, b, c, d=None): pass @classmethod def cmeth(cls, a, b, c, d=None): pass @staticmethod def smeth(a, b, c, d=None): pass class Subclass(MagicMock): pass class Thing(object): attribute = 6 foo = 'bar' class MockTest(unittest.TestCase): def test_all(self): # if __all__ is badly defined then import * will raise an error # We have to exec it because you can't import * inside a method # in Python 3 exec("from mock import *") def test_constructor(self): mock = Mock() self.assertFalse(mock.called, "called not initialised correctly") self.assertEqual(mock.call_count, 0, "call_count not initialised correctly") self.assertTrue(is_instance(mock.return_value, Mock), "return_value not initialised correctly") self.assertEqual(mock.call_args, None, "call_args not initialised correctly") self.assertEqual(mock.call_args_list, [], "call_args_list not initialised correctly") self.assertEqual(mock.method_calls, [], "method_calls not initialised correctly") # Can't use hasattr for this test as it always returns True on a mock self.assertNotIn('_items', mock.__dict__, "default mock should not have '_items' attribute") self.assertIsNone(mock._mock_parent, "parent not initialised correctly") self.assertIsNone(mock._mock_methods, "methods not initialised correctly") self.assertEqual(mock._mock_children, {}, "children not initialised incorrectly") def test_return_value_in_constructor(self): mock = Mock(return_value=None) self.assertIsNone(mock.return_value, "return value in constructor not honoured") def test_repr(self): mock = Mock(name='foo') self.assertIn('foo', repr(mock)) self.assertIn("'%s'" % id(mock), repr(mock)) mocks = [(Mock(), 'mock'), (Mock(name='bar'), 'bar')] for mock, name in mocks: self.assertIn('%s.bar' % name, repr(mock.bar)) self.assertIn('%s.foo()' % name, repr(mock.foo())) self.assertIn('%s.foo().bing' % name, repr(mock.foo().bing)) self.assertIn('%s()' % name, repr(mock())) self.assertIn('%s()()' % name, repr(mock()())) self.assertIn('%s()().foo.bar.baz().bing' % name, repr(mock()().foo.bar.baz().bing)) def test_repr_with_spec(self): class X(object): pass mock = Mock(spec=X) self.assertIn(" spec='X' ", repr(mock)) mock = Mock(spec=X()) self.assertIn(" spec='X' ", repr(mock)) mock = Mock(spec_set=X) self.assertIn(" spec_set='X' ", repr(mock)) mock = Mock(spec_set=X()) self.assertIn(" spec_set='X' ", repr(mock)) mock = Mock(spec=X, name='foo') self.assertIn(" spec='X' ", repr(mock)) self.assertIn(" name='foo' ", repr(mock)) mock = Mock(name='foo') self.assertNotIn("spec", repr(mock)) mock = Mock() self.assertNotIn("spec", repr(mock)) mock = Mock(spec=['foo']) self.assertNotIn("spec", repr(mock)) def test_side_effect(self): mock = Mock() def effect(*args, **kwargs): raise SystemError('kablooie') mock.side_effect = effect self.assertRaises(SystemError, mock, 1, 2, fish=3) mock.assert_called_with(1, 2, fish=3) results = [1, 2, 3] def effect(): return results.pop() mock.side_effect = effect self.assertEqual([mock(), mock(), mock()], [3, 2, 1], "side effect not used correctly") mock = Mock(side_effect=sentinel.SideEffect) self.assertEqual(mock.side_effect, sentinel.SideEffect, "side effect in constructor not used") def side_effect(): return DEFAULT mock = Mock(side_effect=side_effect, return_value=sentinel.RETURN) self.assertEqual(mock(), sentinel.RETURN) def test_autospec_side_effect(self): # Test for issue17826 results = [1, 2, 3] def effect(): return results.pop() def f(): pass mock = create_autospec(f) mock.side_effect = [1, 2, 3] self.assertEqual([mock(), mock(), mock()], [1, 2, 3], "side effect not used correctly in create_autospec") # Test where side effect is a callable results = [1, 2, 3] mock = create_autospec(f) mock.side_effect = effect self.assertEqual([mock(), mock(), mock()], [3, 2, 1], "callable side effect not used correctly") def test_autospec_side_effect_exception(self): # Test for issue 23661 def f(): pass mock = create_autospec(f) mock.side_effect = ValueError('Bazinga!') self.assertRaisesRegex(ValueError, 'Bazinga!', mock) @unittest.skipUnless('java' in sys.platform, 'This test only applies to Jython') def test_java_exception_side_effect(self): import java mock = Mock(side_effect=java.lang.RuntimeException("Boom!")) # can't use assertRaises with java exceptions try: mock(1, 2, fish=3) except java.lang.RuntimeException: pass else: self.fail('java exception not raised') mock.assert_called_with(1,2, fish=3) def test_reset_mock(self): parent = Mock() spec = ["something"] mock = Mock(name="child", parent=parent, spec=spec) mock(sentinel.Something, something=sentinel.SomethingElse) something = mock.something mock.something() mock.side_effect = sentinel.SideEffect return_value = mock.return_value return_value() mock.reset_mock() self.assertEqual(mock._mock_name, "child", "name incorrectly reset") self.assertEqual(mock._mock_parent, parent, "parent incorrectly reset") self.assertEqual(mock._mock_methods, spec, "methods incorrectly reset") self.assertFalse(mock.called, "called not reset") self.assertEqual(mock.call_count, 0, "call_count not reset") self.assertEqual(mock.call_args, None, "call_args not reset") self.assertEqual(mock.call_args_list, [], "call_args_list not reset") self.assertEqual(mock.method_calls, [], "method_calls not initialised correctly: %r != %r" % (mock.method_calls, [])) self.assertEqual(mock.mock_calls, []) self.assertEqual(mock.side_effect, sentinel.SideEffect, "side_effect incorrectly reset") self.assertEqual(mock.return_value, return_value, "return_value incorrectly reset") self.assertFalse(return_value.called, "return value mock not reset") self.assertEqual(mock._mock_children, {'something': something}, "children reset incorrectly") self.assertEqual(mock.something, something, "children incorrectly cleared") self.assertFalse(mock.something.called, "child not reset") def test_reset_mock_recursion(self): mock = Mock() mock.return_value = mock # used to cause recursion mock.reset_mock() def test_reset_mock_on_mock_open_issue_18622(self): a = mock.mock_open() a.reset_mock() def test_call(self): mock = Mock() self.assertTrue(is_instance(mock.return_value, Mock), "Default return_value should be a Mock") result = mock() self.assertEqual(mock(), result, "different result from consecutive calls") mock.reset_mock() ret_val = mock(sentinel.Arg) self.assertTrue(mock.called, "called not set") self.assertEqual(mock.call_count, 1, "call_count incoreect") self.assertEqual(mock.call_args, ((sentinel.Arg,), {}), "call_args not set") self.assertEqual(mock.call_args_list, [((sentinel.Arg,), {})], "call_args_list not initialised correctly") mock.return_value = sentinel.ReturnValue ret_val = mock(sentinel.Arg, key=sentinel.KeyArg) self.assertEqual(ret_val, sentinel.ReturnValue, "incorrect return value") self.assertEqual(mock.call_count, 2, "call_count incorrect") self.assertEqual(mock.call_args, ((sentinel.Arg,), {'key': sentinel.KeyArg}), "call_args not set") self.assertEqual(mock.call_args_list, [ ((sentinel.Arg,), {}), ((sentinel.Arg,), {'key': sentinel.KeyArg}) ], "call_args_list not set") def test_call_args_comparison(self): mock = Mock() mock() mock(sentinel.Arg) mock(kw=sentinel.Kwarg) mock(sentinel.Arg, kw=sentinel.Kwarg) self.assertEqual(mock.call_args_list, [ (), ((sentinel.Arg,),), ({"kw": sentinel.Kwarg},), ((sentinel.Arg,), {"kw": sentinel.Kwarg}) ]) self.assertEqual(mock.call_args, ((sentinel.Arg,), {"kw": sentinel.Kwarg})) # Comparing call_args to a long sequence should not raise # an exception. See issue 24857. self.assertFalse(mock.call_args == "a long sequence") def test_assert_called_with(self): mock = Mock() mock() # Will raise an exception if it fails mock.assert_called_with() self.assertRaises(AssertionError, mock.assert_called_with, 1) mock.reset_mock() self.assertRaises(AssertionError, mock.assert_called_with) mock(1, 2, 3, a='fish', b='nothing') mock.assert_called_with(1, 2, 3, a='fish', b='nothing') def test_assert_called_with_function_spec(self): def f(a, b, c, d=None): pass mock = Mock(spec=f) mock(1, b=2, c=3) mock.assert_called_with(1, 2, 3) mock.assert_called_with(a=1, b=2, c=3) self.assertRaises(AssertionError, mock.assert_called_with, 1, b=3, c=2) # Expected call doesn't match the spec's signature with self.assertRaises(AssertionError) as cm: mock.assert_called_with(e=8) if hasattr(cm.exception, '__cause__'): self.assertIsInstance(cm.exception.__cause__, TypeError) def test_assert_called_with_method_spec(self): def _check(mock): mock(1, b=2, c=3) mock.assert_called_with(1, 2, 3) mock.assert_called_with(a=1, b=2, c=3) self.assertRaises(AssertionError, mock.assert_called_with, 1, b=3, c=2) mock = Mock(spec=Something().meth) _check(mock) mock = Mock(spec=Something.cmeth) _check(mock) mock = Mock(spec=Something().cmeth) _check(mock) mock = Mock(spec=Something.smeth) _check(mock) mock = Mock(spec=Something().smeth) _check(mock) def test_assert_called_once_with(self): mock = Mock() mock() # Will raise an exception if it fails mock.assert_called_once_with() mock() self.assertRaises(AssertionError, mock.assert_called_once_with) mock.reset_mock() self.assertRaises(AssertionError, mock.assert_called_once_with) mock('foo', 'bar', baz=2) mock.assert_called_once_with('foo', 'bar', baz=2) mock.reset_mock() mock('foo', 'bar', baz=2) self.assertRaises( AssertionError, lambda: mock.assert_called_once_with('bob', 'bar', baz=2) ) def test_assert_called_once_with_function_spec(self): def f(a, b, c, d=None): pass mock = Mock(spec=f) mock(1, b=2, c=3) mock.assert_called_once_with(1, 2, 3) mock.assert_called_once_with(a=1, b=2, c=3) self.assertRaises(AssertionError, mock.assert_called_once_with, 1, b=3, c=2) # Expected call doesn't match the spec's signature with self.assertRaises(AssertionError) as cm: mock.assert_called_once_with(e=8) if hasattr(cm.exception, '__cause__'): self.assertIsInstance(cm.exception.__cause__, TypeError) # Mock called more than once => always fails mock(4, 5, 6) self.assertRaises(AssertionError, mock.assert_called_once_with, 1, 2, 3) self.assertRaises(AssertionError, mock.assert_called_once_with, 4, 5, 6) def test_attribute_access_returns_mocks(self): mock = Mock() something = mock.something self.assertTrue(is_instance(something, Mock), "attribute isn't a mock") self.assertEqual(mock.something, something, "different attributes returned for same name") # Usage example mock = Mock() mock.something.return_value = 3 self.assertEqual(mock.something(), 3, "method returned wrong value") self.assertTrue(mock.something.called, "method didn't record being called") def test_attributes_have_name_and_parent_set(self): mock = Mock() something = mock.something self.assertEqual(something._mock_name, "something", "attribute name not set correctly") self.assertEqual(something._mock_parent, mock, "attribute parent not set correctly") def test_method_calls_recorded(self): mock = Mock() mock.something(3, fish=None) mock.something_else.something(6, cake=sentinel.Cake) self.assertEqual(mock.something_else.method_calls, [("something", (6,), {'cake': sentinel.Cake})], "method calls not recorded correctly") self.assertEqual(mock.method_calls, [ ("something", (3,), {'fish': None}), ("something_else.something", (6,), {'cake': sentinel.Cake}) ], "method calls not recorded correctly") def test_method_calls_compare_easily(self): mock = Mock() mock.something() self.assertEqual(mock.method_calls, [('something',)]) self.assertEqual(mock.method_calls, [('something', (), {})]) mock = Mock() mock.something('different') self.assertEqual(mock.method_calls, [('something', ('different',))]) self.assertEqual(mock.method_calls, [('something', ('different',), {})]) mock = Mock() mock.something(x=1) self.assertEqual(mock.method_calls, [('something', {'x': 1})]) self.assertEqual(mock.method_calls, [('something', (), {'x': 1})]) mock = Mock() mock.something('different', some='more') self.assertEqual(mock.method_calls, [ ('something', ('different',), {'some': 'more'}) ]) def test_only_allowed_methods_exist(self): for spec in ['something'], ('something',): for arg in 'spec', 'spec_set': mock = Mock(**{arg: spec}) # this should be allowed mock.something self.assertRaisesRegex( AttributeError, "Mock object has no attribute 'something_else'", getattr, mock, 'something_else' ) def test_from_spec(self): class Something(object): x = 3 __something__ = None def y(self): pass def test_attributes(mock): # should work mock.x mock.y mock.__something__ self.assertRaisesRegex( AttributeError, "Mock object has no attribute 'z'", getattr, mock, 'z' ) self.assertRaisesRegex( AttributeError, "Mock object has no attribute '__foobar__'", getattr, mock, '__foobar__' ) test_attributes(Mock(spec=Something)) test_attributes(Mock(spec=Something())) def test_wraps_calls(self): real = Mock() mock = Mock(wraps=real) self.assertEqual(mock(), real()) real.reset_mock() mock(1, 2, fish=3) real.assert_called_with(1, 2, fish=3) def test_wraps_call_with_nondefault_return_value(self): real = Mock() mock = Mock(wraps=real) mock.return_value = 3 self.assertEqual(mock(), 3) self.assertFalse(real.called) def test_wraps_attributes(self): class Real(object): attribute = Mock() real = Real() mock = Mock(wraps=real) self.assertEqual(mock.attribute(), real.attribute()) self.assertRaises(AttributeError, lambda: mock.fish) self.assertNotEqual(mock.attribute, real.attribute) result = mock.attribute.frog(1, 2, fish=3) Real.attribute.frog.assert_called_with(1, 2, fish=3) self.assertEqual(result, Real.attribute.frog()) def test_exceptional_side_effect(self): mock = Mock(side_effect=AttributeError) self.assertRaises(AttributeError, mock) mock = Mock(side_effect=AttributeError('foo')) self.assertRaises(AttributeError, mock) def test_baseexceptional_side_effect(self): mock = Mock(side_effect=KeyboardInterrupt) self.assertRaises(KeyboardInterrupt, mock) mock = Mock(side_effect=KeyboardInterrupt('foo')) self.assertRaises(KeyboardInterrupt, mock) def test_assert_called_with_message(self): mock = Mock() self.assertRaisesRegex(AssertionError, 'Not called', mock.assert_called_with) def test_assert_called_once_with_message(self): mock = Mock(name='geoffrey') self.assertRaisesRegex(AssertionError, r"Expected 'geoffrey' to be called once\.", mock.assert_called_once_with) def test__name__(self): mock = Mock() self.assertRaises(AttributeError, lambda: mock.__name__) mock.__name__ = 'foo' self.assertEqual(mock.__name__, 'foo') def test_spec_list_subclass(self): class Sub(list): pass mock = Mock(spec=Sub(['foo'])) mock.append(3) mock.append.assert_called_with(3) self.assertRaises(AttributeError, getattr, mock, 'foo') def test_spec_class(self): class X(object): pass mock = Mock(spec=X) self.assertIsInstance(mock, X) mock = Mock(spec=X()) self.assertIsInstance(mock, X) self.assertIs(mock.__class__, X) self.assertEqual(Mock().__class__.__name__, 'Mock') mock = Mock(spec_set=X) self.assertIsInstance(mock, X) mock = Mock(spec_set=X()) self.assertIsInstance(mock, X) def test_setting_attribute_with_spec_set(self): class X(object): y = 3 mock = Mock(spec=X) mock.x = 'foo' mock = Mock(spec_set=X) def set_attr(): mock.x = 'foo' mock.y = 'foo' self.assertRaises(AttributeError, set_attr) def test_copy(self): current = sys.getrecursionlimit() self.addCleanup(sys.setrecursionlimit, current) # can't use sys.maxint as this doesn't exist in Python 3 sys.setrecursionlimit(int(10e8)) # this segfaults without the fix in place copy.copy(Mock()) @unittest.skipIf(six.PY3, "no old style classes in Python 3") def test_spec_old_style_classes(self): class Foo: bar = 7 mock = Mock(spec=Foo) mock.bar = 6 self.assertRaises(AttributeError, lambda: mock.foo) mock = Mock(spec=Foo()) mock.bar = 6 self.assertRaises(AttributeError, lambda: mock.foo) @unittest.skipIf(six.PY3, "no old style classes in Python 3") def test_spec_set_old_style_classes(self): class Foo: bar = 7 mock = Mock(spec_set=Foo) mock.bar = 6 self.assertRaises(AttributeError, lambda: mock.foo) def _set(): mock.foo = 3 self.assertRaises(AttributeError, _set) mock = Mock(spec_set=Foo()) mock.bar = 6 self.assertRaises(AttributeError, lambda: mock.foo) def _set(): mock.foo = 3 self.assertRaises(AttributeError, _set) def test_subclass_with_properties(self): class SubClass(Mock): def _get(self): return 3 def _set(self, value): raise NameError('strange error') some_attribute = property(_get, _set) s = SubClass(spec_set=SubClass) self.assertEqual(s.some_attribute, 3) def test(): s.some_attribute = 3 self.assertRaises(NameError, test) def test(): s.foo = 'bar' self.assertRaises(AttributeError, test) def test_setting_call(self): mock = Mock() def __call__(self, a): return self._mock_call(a) type(mock).__call__ = __call__ mock('one') mock.assert_called_with('one') self.assertRaises(TypeError, mock, 'one', 'two') def test_dir(self): mock = Mock() attrs = set(dir(mock)) type_attrs = set([m for m in dir(Mock) if not m.startswith('_')]) # all public attributes from the type are included self.assertEqual(set(), type_attrs - attrs) # creates these attributes mock.a, mock.b self.assertIn('a', dir(mock)) self.assertIn('b', dir(mock)) # instance attributes mock.c = mock.d = None self.assertIn('c', dir(mock)) self.assertIn('d', dir(mock)) # magic methods mock.__iter__ = lambda s: iter([]) self.assertIn('__iter__', dir(mock)) def test_dir_from_spec(self): mock = Mock(spec=unittest.TestCase) testcase_attrs = set(dir(unittest.TestCase)) attrs = set(dir(mock)) # all attributes from the spec are included self.assertEqual(set(), testcase_attrs - attrs) # shadow a sys attribute mock.version = 3 self.assertEqual(dir(mock).count('version'), 1) def test_filter_dir(self): patcher = patch.object(mock, 'FILTER_DIR', False) patcher.start() try: attrs = set(dir(Mock())) type_attrs = set(dir(Mock)) # ALL attributes from the type are included self.assertEqual(set(), type_attrs - attrs) finally: patcher.stop() def test_configure_mock(self): mock = Mock(foo='bar') self.assertEqual(mock.foo, 'bar') mock = MagicMock(foo='bar') self.assertEqual(mock.foo, 'bar') kwargs = {'side_effect': KeyError, 'foo.bar.return_value': 33, 'foo': MagicMock()} mock = Mock(**kwargs) self.assertRaises(KeyError, mock) self.assertEqual(mock.foo.bar(), 33) self.assertIsInstance(mock.foo, MagicMock) mock = Mock() mock.configure_mock(**kwargs) self.assertRaises(KeyError, mock) self.assertEqual(mock.foo.bar(), 33) self.assertIsInstance(mock.foo, MagicMock) def assertRaisesWithMsg(self, exception, message, func, *args, **kwargs): # needed because assertRaisesRegex doesn't work easily with newlines try: func(*args, **kwargs) except: instance = sys.exc_info()[1] self.assertIsInstance(instance, exception) else: self.fail('Exception %r not raised' % (exception,)) msg = str(instance) self.assertEqual(msg, message) def test_assert_called_with_failure_message(self): mock = NonCallableMock() expected = "mock(1, '2', 3, bar='foo')" message = 'Expected call: %s\nNot called' self.assertRaisesWithMsg( AssertionError, message % (expected,), mock.assert_called_with, 1, '2', 3, bar='foo' ) mock.foo(1, '2', 3, foo='foo') asserters = [ mock.foo.assert_called_with, mock.foo.assert_called_once_with ] for meth in asserters: actual = "foo(1, '2', 3, foo='foo')" expected = "foo(1, '2', 3, bar='foo')" message = 'Expected call: %s\nActual call: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), meth, 1, '2', 3, bar='foo' ) # just kwargs for meth in asserters: actual = "foo(1, '2', 3, foo='foo')" expected = "foo(bar='foo')" message = 'Expected call: %s\nActual call: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), meth, bar='foo' ) # just args for meth in asserters: actual = "foo(1, '2', 3, foo='foo')" expected = "foo(1, 2, 3)" message = 'Expected call: %s\nActual call: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), meth, 1, 2, 3 ) # empty for meth in asserters: actual = "foo(1, '2', 3, foo='foo')" expected = "foo()" message = 'Expected call: %s\nActual call: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), meth ) def test_mock_calls(self): mock = MagicMock() # need to do this because MagicMock.mock_calls used to just return # a MagicMock which also returned a MagicMock when __eq__ was called self.assertIs(mock.mock_calls == [], True) mock = MagicMock() mock() expected = [('', (), {})] self.assertEqual(mock.mock_calls, expected) mock.foo() expected.append(call.foo()) self.assertEqual(mock.mock_calls, expected) # intermediate mock_calls work too self.assertEqual(mock.foo.mock_calls, [('', (), {})]) mock = MagicMock() mock().foo(1, 2, 3, a=4, b=5) expected = [ ('', (), {}), ('().foo', (1, 2, 3), dict(a=4, b=5)) ] self.assertEqual(mock.mock_calls, expected) self.assertEqual(mock.return_value.foo.mock_calls, [('', (1, 2, 3), dict(a=4, b=5))]) self.assertEqual(mock.return_value.mock_calls, [('foo', (1, 2, 3), dict(a=4, b=5))]) mock = MagicMock() mock().foo.bar().baz() expected = [ ('', (), {}), ('().foo.bar', (), {}), ('().foo.bar().baz', (), {}) ] self.assertEqual(mock.mock_calls, expected) self.assertEqual(mock().mock_calls, call.foo.bar().baz().call_list()) for kwargs in dict(), dict(name='bar'): mock = MagicMock(**kwargs) int(mock.foo) expected = [('foo.__int__', (), {})] self.assertEqual(mock.mock_calls, expected) mock = MagicMock(**kwargs) mock.a()() expected = [('a', (), {}), ('a()', (), {})] self.assertEqual(mock.mock_calls, expected) self.assertEqual(mock.a().mock_calls, [call()]) mock = MagicMock(**kwargs) mock(1)(2)(3) self.assertEqual(mock.mock_calls, call(1)(2)(3).call_list()) self.assertEqual(mock().mock_calls, call(2)(3).call_list()) self.assertEqual(mock()().mock_calls, call(3).call_list()) mock = MagicMock(**kwargs) mock(1)(2)(3).a.b.c(4) self.assertEqual(mock.mock_calls, call(1)(2)(3).a.b.c(4).call_list()) self.assertEqual(mock().mock_calls, call(2)(3).a.b.c(4).call_list()) self.assertEqual(mock()().mock_calls, call(3).a.b.c(4).call_list()) mock = MagicMock(**kwargs) int(mock().foo.bar().baz()) last_call = ('().foo.bar().baz().__int__', (), {}) self.assertEqual(mock.mock_calls[-1], last_call) self.assertEqual(mock().mock_calls, call.foo.bar().baz().__int__().call_list()) self.assertEqual(mock().foo.bar().mock_calls, call.baz().__int__().call_list()) self.assertEqual(mock().foo.bar().baz.mock_calls, call().__int__().call_list()) def test_subclassing(self): class Subclass(Mock): pass mock = Subclass() self.assertIsInstance(mock.foo, Subclass) self.assertIsInstance(mock(), Subclass) class Subclass(Mock): def _get_child_mock(self, **kwargs): return Mock(**kwargs) mock = Subclass() self.assertNotIsInstance(mock.foo, Subclass) self.assertNotIsInstance(mock(), Subclass) def test_arg_lists(self): mocks = [ Mock(), MagicMock(), NonCallableMock(), NonCallableMagicMock() ] def assert_attrs(mock): names = 'call_args_list', 'method_calls', 'mock_calls' for name in names: attr = getattr(mock, name) self.assertIsInstance(attr, _CallList) self.assertIsInstance(attr, list) self.assertEqual(attr, []) for mock in mocks: assert_attrs(mock) if callable(mock): mock() mock(1, 2) mock(a=3) mock.reset_mock() assert_attrs(mock) mock.foo() mock.foo.bar(1, a=3) mock.foo(1).bar().baz(3) mock.reset_mock() assert_attrs(mock) def test_call_args_two_tuple(self): mock = Mock() mock(1, a=3) mock(2, b=4) self.assertEqual(len(mock.call_args), 2) args, kwargs = mock.call_args self.assertEqual(args, (2,)) self.assertEqual(kwargs, dict(b=4)) expected_list = [((1,), dict(a=3)), ((2,), dict(b=4))] for expected, call_args in zip(expected_list, mock.call_args_list): self.assertEqual(len(call_args), 2) self.assertEqual(expected[0], call_args[0]) self.assertEqual(expected[1], call_args[1]) def test_side_effect_iterator(self): mock = Mock(side_effect=iter([1, 2, 3])) self.assertEqual([mock(), mock(), mock()], [1, 2, 3]) self.assertRaises(StopIteration, mock) mock = MagicMock(side_effect=['a', 'b', 'c']) self.assertEqual([mock(), mock(), mock()], ['a', 'b', 'c']) self.assertRaises(StopIteration, mock) mock = Mock(side_effect='ghi') self.assertEqual([mock(), mock(), mock()], ['g', 'h', 'i']) self.assertRaises(StopIteration, mock) class Foo(object): pass mock = MagicMock(side_effect=Foo) self.assertIsInstance(mock(), Foo) mock = Mock(side_effect=Iter()) self.assertEqual([mock(), mock(), mock(), mock()], ['this', 'is', 'an', 'iter']) self.assertRaises(StopIteration, mock) def test_side_effect_setting_iterator(self): mock = Mock() mock.side_effect = iter([1, 2, 3]) self.assertEqual([mock(), mock(), mock()], [1, 2, 3]) self.assertRaises(StopIteration, mock) side_effect = mock.side_effect self.assertIsInstance(side_effect, type(iter([]))) mock.side_effect = ['a', 'b', 'c'] self.assertEqual([mock(), mock(), mock()], ['a', 'b', 'c']) self.assertRaises(StopIteration, mock) side_effect = mock.side_effect self.assertIsInstance(side_effect, type(iter([]))) this_iter = Iter() mock.side_effect = this_iter self.assertEqual([mock(), mock(), mock(), mock()], ['this', 'is', 'an', 'iter']) self.assertRaises(StopIteration, mock) self.assertIs(mock.side_effect, this_iter) def test_side_effect_iterator_exceptions(self): for Klass in Mock, MagicMock: iterable = (ValueError, 3, KeyError, 6) m = Klass(side_effect=iterable) self.assertRaises(ValueError, m) self.assertEqual(m(), 3) self.assertRaises(KeyError, m) self.assertEqual(m(), 6) def test_side_effect_iterator_default(self): mock = Mock(return_value=2) mock.side_effect = iter([1, DEFAULT]) self.assertEqual([mock(), mock()], [1, 2]) def test_assert_has_calls_any_order(self): mock = Mock() mock(1, 2) mock(a=3) mock(3, 4) mock(b=6) mock(b=6) kalls = [ call(1, 2), ({'a': 3},), ((3, 4),), ((), {'a': 3}), ('', (1, 2)), ('', {'a': 3}), ('', (1, 2), {}), ('', (), {'a': 3}) ] for kall in kalls: mock.assert_has_calls([kall], any_order=True) for kall in call(1, '2'), call(b=3), call(), 3, None, 'foo': self.assertRaises( AssertionError, mock.assert_has_calls, [kall], any_order=True ) kall_lists = [ [call(1, 2), call(b=6)], [call(3, 4), call(1, 2)], [call(b=6), call(b=6)], ] for kall_list in kall_lists: mock.assert_has_calls(kall_list, any_order=True) kall_lists = [ [call(b=6), call(b=6), call(b=6)], [call(1, 2), call(1, 2)], [call(3, 4), call(1, 2), call(5, 7)], [call(b=6), call(3, 4), call(b=6), call(1, 2), call(b=6)], ] for kall_list in kall_lists: self.assertRaises( AssertionError, mock.assert_has_calls, kall_list, any_order=True ) def test_assert_has_calls(self): kalls1 = [ call(1, 2), ({'a': 3},), ((3, 4),), call(b=6), ('', (1,), {'b': 6}), ] kalls2 = [call.foo(), call.bar(1)] kalls2.extend(call.spam().baz(a=3).call_list()) kalls2.extend(call.bam(set(), foo={}).fish([1]).call_list()) mocks = [] for mock in Mock(), MagicMock(): mock(1, 2) mock(a=3) mock(3, 4) mock(b=6) mock(1, b=6) mocks.append((mock, kalls1)) mock = Mock() mock.foo() mock.bar(1) mock.spam().baz(a=3) mock.bam(set(), foo={}).fish([1]) mocks.append((mock, kalls2)) for mock, kalls in mocks: for i in range(len(kalls)): for step in 1, 2, 3: these = kalls[i:i+step] mock.assert_has_calls(these) if len(these) > 1: self.assertRaises( AssertionError, mock.assert_has_calls, list(reversed(these)) ) def test_assert_has_calls_with_function_spec(self): def f(a, b, c, d=None): pass mock = Mock(spec=f) mock(1, b=2, c=3) mock(4, 5, c=6, d=7) mock(10, 11, c=12) calls = [ ('', (1, 2, 3), {}), ('', (4, 5, 6), {'d': 7}), ((10, 11, 12), {}), ] mock.assert_has_calls(calls) mock.assert_has_calls(calls, any_order=True) mock.assert_has_calls(calls[1:]) mock.assert_has_calls(calls[1:], any_order=True) mock.assert_has_calls(calls[:-1]) mock.assert_has_calls(calls[:-1], any_order=True) # Reversed order calls = list(reversed(calls)) with self.assertRaises(AssertionError): mock.assert_has_calls(calls) mock.assert_has_calls(calls, any_order=True) with self.assertRaises(AssertionError): mock.assert_has_calls(calls[1:]) mock.assert_has_calls(calls[1:], any_order=True) with self.assertRaises(AssertionError): mock.assert_has_calls(calls[:-1]) mock.assert_has_calls(calls[:-1], any_order=True) def test_assert_any_call(self): mock = Mock() mock(1, 2) mock(a=3) mock(1, b=6) mock.assert_any_call(1, 2) mock.assert_any_call(a=3) mock.assert_any_call(1, b=6) self.assertRaises( AssertionError, mock.assert_any_call ) self.assertRaises( AssertionError, mock.assert_any_call, 1, 3 ) self.assertRaises( AssertionError, mock.assert_any_call, a=4 ) def test_assert_any_call_with_function_spec(self): def f(a, b, c, d=None): pass mock = Mock(spec=f) mock(1, b=2, c=3) mock(4, 5, c=6, d=7) mock.assert_any_call(1, 2, 3) mock.assert_any_call(a=1, b=2, c=3) mock.assert_any_call(4, 5, 6, 7) mock.assert_any_call(a=4, b=5, c=6, d=7) self.assertRaises(AssertionError, mock.assert_any_call, 1, b=3, c=2) # Expected call doesn't match the spec's signature with self.assertRaises(AssertionError) as cm: mock.assert_any_call(e=8) if hasattr(cm.exception, '__cause__'): self.assertIsInstance(cm.exception.__cause__, TypeError) def test_mock_calls_create_autospec(self): def f(a, b): pass obj = Iter() obj.f = f funcs = [ create_autospec(f), create_autospec(obj).f ] for func in funcs: func(1, 2) func(3, 4) self.assertEqual( func.mock_calls, [call(1, 2), call(3, 4)] ) #Issue21222 def test_create_autospec_with_name(self): m = mock.create_autospec(object(), name='sweet_func') self.assertIn('sweet_func', repr(m)) #Issue21238 def test_mock_unsafe(self): m = Mock() with self.assertRaises(AttributeError): m.assert_foo_call() with self.assertRaises(AttributeError): m.assret_foo_call() m = Mock(unsafe=True) m.assert_foo_call() m.assret_foo_call() #Issue21262 def test_assert_not_called(self): m = Mock() m.hello.assert_not_called() m.hello() with self.assertRaises(AssertionError): m.hello.assert_not_called() def test_assert_called(self): m = Mock() with self.assertRaises(AssertionError): m.hello.assert_called() m.hello() m.hello.assert_called() m.hello() m.hello.assert_called() def test_assert_called_once(self): m = Mock() with self.assertRaises(AssertionError): m.hello.assert_called_once() m.hello() m.hello.assert_called_once() m.hello() with self.assertRaises(AssertionError): m.hello.assert_called_once() #Issue21256 printout of keyword args should be in deterministic order def test_sorted_call_signature(self): m = Mock() m.hello(name='hello', daddy='hero') text = "call(daddy='hero', name='hello')" self.assertEqual(repr(m.hello.call_args), text) #Issue21270 overrides tuple methods for mock.call objects def test_override_tuple_methods(self): c = call.count() i = call.index(132,'hello') m = Mock() m.count() m.index(132,"hello") self.assertEqual(m.method_calls[0], c) self.assertEqual(m.method_calls[1], i) def test_mock_add_spec(self): class _One(object): one = 1 class _Two(object): two = 2 class Anything(object): one = two = three = 'four' klasses = [ Mock, MagicMock, NonCallableMock, NonCallableMagicMock ] for Klass in list(klasses): klasses.append(lambda K=Klass: K(spec=Anything)) klasses.append(lambda K=Klass: K(spec_set=Anything)) for Klass in klasses: for kwargs in dict(), dict(spec_set=True): mock = Klass() #no error mock.one, mock.two, mock.three for One, Two in [(_One, _Two), (['one'], ['two'])]: for kwargs in dict(), dict(spec_set=True): mock.mock_add_spec(One, **kwargs) mock.one self.assertRaises( AttributeError, getattr, mock, 'two' ) self.assertRaises( AttributeError, getattr, mock, 'three' ) if 'spec_set' in kwargs: self.assertRaises( AttributeError, setattr, mock, 'three', None ) mock.mock_add_spec(Two, **kwargs) self.assertRaises( AttributeError, getattr, mock, 'one' ) mock.two self.assertRaises( AttributeError, getattr, mock, 'three' ) if 'spec_set' in kwargs: self.assertRaises( AttributeError, setattr, mock, 'three', None ) # note that creating a mock, setting an instance attribute, and # *then* setting a spec doesn't work. Not the intended use case def test_mock_add_spec_magic_methods(self): for Klass in MagicMock, NonCallableMagicMock: mock = Klass() int(mock) mock.mock_add_spec(object) self.assertRaises(TypeError, int, mock) mock = Klass() mock['foo'] mock.__int__.return_value =4 mock.mock_add_spec(int) self.assertEqual(int(mock), 4) self.assertRaises(TypeError, lambda: mock['foo']) def test_adding_child_mock(self): for Klass in NonCallableMock, Mock, MagicMock, NonCallableMagicMock: mock = Klass() mock.foo = Mock() mock.foo() self.assertEqual(mock.method_calls, [call.foo()]) self.assertEqual(mock.mock_calls, [call.foo()]) mock = Klass() mock.bar = Mock(name='name') mock.bar() self.assertEqual(mock.method_calls, []) self.assertEqual(mock.mock_calls, []) # mock with an existing _new_parent but no name mock = Klass() mock.baz = MagicMock()() mock.baz() self.assertEqual(mock.method_calls, []) self.assertEqual(mock.mock_calls, []) def test_adding_return_value_mock(self): for Klass in Mock, MagicMock: mock = Klass() mock.return_value = MagicMock() mock()() self.assertEqual(mock.mock_calls, [call(), call()()]) def test_manager_mock(self): class Foo(object): one = 'one' two = 'two' manager = Mock() p1 = patch.object(Foo, 'one') p2 = patch.object(Foo, 'two') mock_one = p1.start() self.addCleanup(p1.stop) mock_two = p2.start() self.addCleanup(p2.stop) manager.attach_mock(mock_one, 'one') manager.attach_mock(mock_two, 'two') Foo.two() Foo.one() self.assertEqual(manager.mock_calls, [call.two(), call.one()]) def test_magic_methods_mock_calls(self): for Klass in Mock, MagicMock: m = Klass() m.__int__ = Mock(return_value=3) m.__float__ = MagicMock(return_value=3.0) int(m) float(m) self.assertEqual(m.mock_calls, [call.__int__(), call.__float__()]) self.assertEqual(m.method_calls, []) def test_mock_open_reuse_issue_21750(self): mocked_open = mock.mock_open(read_data='data') f1 = mocked_open('a-name') f1_data = f1.read() f2 = mocked_open('another-name') f2_data = f2.read() self.assertEqual(f1_data, f2_data) def test_mock_open_write(self): # Test exception in file writing write() mock_namedtemp = mock.mock_open(mock.MagicMock(name='JLV')) with mock.patch('tempfile.NamedTemporaryFile', mock_namedtemp): mock_filehandle = mock_namedtemp.return_value mock_write = mock_filehandle.write mock_write.side_effect = OSError('Test 2 Error') def attempt(): tempfile.NamedTemporaryFile().write('asd') self.assertRaises(OSError, attempt) def test_mock_open_alter_readline(self): mopen = mock.mock_open(read_data='foo\nbarn') mopen.return_value.readline.side_effect = lambda *args:'abc' first = mopen().readline() second = mopen().readline() self.assertEqual('abc', first) self.assertEqual('abc', second) def test_mock_parents(self): for Klass in Mock, MagicMock: m = Klass() original_repr = repr(m) m.return_value = m self.assertIs(m(), m) self.assertEqual(repr(m), original_repr) m.reset_mock() self.assertIs(m(), m) self.assertEqual(repr(m), original_repr) m = Klass() m.b = m.a self.assertIn("name='mock.a'", repr(m.b)) self.assertIn("name='mock.a'", repr(m.a)) m.reset_mock() self.assertIn("name='mock.a'", repr(m.b)) self.assertIn("name='mock.a'", repr(m.a)) m = Klass() original_repr = repr(m) m.a = m() m.a.return_value = m self.assertEqual(repr(m), original_repr) self.assertEqual(repr(m.a()), original_repr) def test_attach_mock(self): classes = Mock, MagicMock, NonCallableMagicMock, NonCallableMock for Klass in classes: for Klass2 in classes: m = Klass() m2 = Klass2(name='foo') m.attach_mock(m2, 'bar') self.assertIs(m.bar, m2) self.assertIn("name='mock.bar'", repr(m2)) m.bar.baz(1) self.assertEqual(m.mock_calls, [call.bar.baz(1)]) self.assertEqual(m.method_calls, [call.bar.baz(1)]) def test_attach_mock_return_value(self): classes = Mock, MagicMock, NonCallableMagicMock, NonCallableMock for Klass in Mock, MagicMock: for Klass2 in classes: m = Klass() m2 = Klass2(name='foo') m.attach_mock(m2, 'return_value') self.assertIs(m(), m2) self.assertIn("name='mock()'", repr(m2)) m2.foo() self.assertEqual(m.mock_calls, call().foo().call_list()) def test_attribute_deletion(self): for mock in (Mock(), MagicMock(), NonCallableMagicMock(), NonCallableMock()): self.assertTrue(hasattr(mock, 'm')) del mock.m self.assertFalse(hasattr(mock, 'm')) del mock.f self.assertFalse(hasattr(mock, 'f')) self.assertRaises(AttributeError, getattr, mock, 'f') def test_class_assignable(self): for mock in Mock(), MagicMock(): self.assertNotIsInstance(mock, int) mock.__class__ = int self.assertIsInstance(mock, int) mock.foo @unittest.expectedFailure def test_pickle(self): for Klass in (MagicMock, Mock, Subclass, NonCallableMagicMock): mock = Klass(name='foo', attribute=3) mock.foo(1, 2, 3) data = pickle.dumps(mock) new = pickle.loads(data) new.foo.assert_called_once_with(1, 2, 3) self.assertFalse(new.called) self.assertTrue(is_instance(new, Klass)) self.assertIsInstance(new, Thing) self.assertIn('name="foo"', repr(new)) self.assertEqual(new.attribute, 3) if __name__ == '__main__': unittest.main() mock/mock/mock.py 0000644 00000243730 15025013454 0007741 0 ustar 00 # mock.py # Test tools for mocking and patching. # E-mail: fuzzyman AT voidspace DOT org DOT uk # # mock 1.0.1 # http://www.voidspace.org.uk/python/mock/ # # Copyright (c) 2007-2013, Michael Foord & the mock team # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from __future__ import absolute_import __all__ = ( '__version__', 'version_info', 'Mock', 'MagicMock', 'patch', 'sentinel', 'DEFAULT', 'ANY', 'call', 'create_autospec', 'FILTER_DIR', 'CallableMixin', 'NonCallableMock', 'NonCallableMagicMock', 'mock_open', 'PropertyMock', ) from functools import partial import inspect import pprint import sys try: import builtins except ImportError: import __builtin__ as builtins from types import ModuleType import six from six import wraps # TODO(houglum): Adjust this section if we use a later version of mock. # Manually specify version so that we don't need to rely on pbr (which works # best when packages are installed via pip rather than direct download). # This allows us to include mock in other projects which can be installed # via methods other than pip (downloading a git repo, tarball, etc.). __version__ = '2.0.0' version_info = (2, 0, 0, 'final', 0) import mock try: inspectsignature = inspect.signature except AttributeError: import funcsigs inspectsignature = funcsigs.signature # TODO: use six. try: unicode except NameError: # Python 3 basestring = unicode = str try: long except NameError: # Python 3 long = int try: BaseException except NameError: # Python 2.4 compatibility BaseException = Exception if six.PY2: # Python 2's next() can't handle a non-iterator with a __next__ method. _next = next def next(obj, _next=_next): if getattr(obj, '__next__', None): return obj.__next__() return _next(obj) del _next _builtins = set(name for name in dir(builtins) if not name.startswith('_')) BaseExceptions = (BaseException,) if 'java' in sys.platform: # jython import java BaseExceptions = (BaseException, java.lang.Throwable) try: _isidentifier = str.isidentifier except AttributeError: # Python 2.X import keyword import re regex = re.compile(r'^[a-z_][a-z0-9_]*$', re.I) def _isidentifier(string): if string in keyword.kwlist: return False return regex.match(string) self = 'im_self' builtin = '__builtin__' if six.PY3: self = '__self__' builtin = 'builtins' # NOTE: This FILTER_DIR is not used. The binding in mock.FILTER_DIR is. FILTER_DIR = True # Workaround for Python issue #12370 # Without this, the __class__ properties wouldn't be set correctly _safe_super = super def _is_instance_mock(obj): # can't use isinstance on Mock objects because they override __class__ # The base class for all mocks is NonCallableMock return issubclass(type(obj), NonCallableMock) def _is_exception(obj): return ( isinstance(obj, BaseExceptions) or isinstance(obj, ClassTypes) and issubclass(obj, BaseExceptions) ) class _slotted(object): __slots__ = ['a'] DescriptorTypes = ( type(_slotted.a), property, ) def _get_signature_object(func, as_instance, eat_self): """ Given an arbitrary, possibly callable object, try to create a suitable signature object. Return a (reduced func, signature) tuple, or None. """ if isinstance(func, ClassTypes) and not as_instance: # If it's a type and should be modelled as a type, use __init__. try: func = func.__init__ except AttributeError: return None # Skip the `self` argument in __init__ eat_self = True elif not isinstance(func, FunctionTypes): # If we really want to model an instance of the passed type, # __call__ should be looked up, not __init__. try: func = func.__call__ except AttributeError: return None if eat_self: sig_func = partial(func, None) else: sig_func = func try: return func, inspectsignature(sig_func) except ValueError: # Certain callable types are not supported by inspect.signature() return None def _check_signature(func, mock, skipfirst, instance=False): sig = _get_signature_object(func, instance, skipfirst) if sig is None: return func, sig = sig def checksig(_mock_self, *args, **kwargs): sig.bind(*args, **kwargs) _copy_func_details(func, checksig) type(mock)._mock_check_sig = checksig def _copy_func_details(func, funcopy): funcopy.__name__ = func.__name__ funcopy.__doc__ = func.__doc__ try: funcopy.__text_signature__ = func.__text_signature__ except AttributeError: pass # we explicitly don't copy func.__dict__ into this copy as it would # expose original attributes that should be mocked try: funcopy.__module__ = func.__module__ except AttributeError: pass try: funcopy.__defaults__ = func.__defaults__ except AttributeError: pass try: funcopy.__kwdefaults__ = func.__kwdefaults__ except AttributeError: pass if six.PY2: funcopy.func_defaults = func.func_defaults return def _callable(obj): if isinstance(obj, ClassTypes): return True if getattr(obj, '__call__', None) is not None: return True return False def _is_list(obj): # checks for list or tuples # XXXX badly named! return type(obj) in (list, tuple) def _instance_callable(obj): """Given an object, return True if the object is callable. For classes, return True if instances would be callable.""" if not isinstance(obj, ClassTypes): # already an instance return getattr(obj, '__call__', None) is not None if six.PY3: # *could* be broken by a class overriding __mro__ or __dict__ via # a metaclass for base in (obj,) + obj.__mro__: if base.__dict__.get('__call__') is not None: return True else: klass = obj # uses __bases__ instead of __mro__ so that we work with old style classes if klass.__dict__.get('__call__') is not None: return True for base in klass.__bases__: if _instance_callable(base): return True return False def _set_signature(mock, original, instance=False): # creates a function with signature (*args, **kwargs) that delegates to a # mock. It still does signature checking by calling a lambda with the same # signature as the original. if not _callable(original): return skipfirst = isinstance(original, ClassTypes) result = _get_signature_object(original, instance, skipfirst) if result is None: return func, sig = result def checksig(*args, **kwargs): sig.bind(*args, **kwargs) _copy_func_details(func, checksig) name = original.__name__ if not _isidentifier(name): name = 'funcopy' context = {'_checksig_': checksig, 'mock': mock} src = """def %s(*args, **kwargs): _checksig_(*args, **kwargs) return mock(*args, **kwargs)""" % name six.exec_(src, context) funcopy = context[name] _setup_func(funcopy, mock) return funcopy def _setup_func(funcopy, mock): funcopy.mock = mock # can't use isinstance with mocks if not _is_instance_mock(mock): return def assert_called_with(*args, **kwargs): return mock.assert_called_with(*args, **kwargs) def assert_called_once_with(*args, **kwargs): return mock.assert_called_once_with(*args, **kwargs) def assert_has_calls(*args, **kwargs): return mock.assert_has_calls(*args, **kwargs) def assert_any_call(*args, **kwargs): return mock.assert_any_call(*args, **kwargs) def reset_mock(): funcopy.method_calls = _CallList() funcopy.mock_calls = _CallList() mock.reset_mock() ret = funcopy.return_value if _is_instance_mock(ret) and not ret is mock: ret.reset_mock() funcopy.called = False funcopy.call_count = 0 funcopy.call_args = None funcopy.call_args_list = _CallList() funcopy.method_calls = _CallList() funcopy.mock_calls = _CallList() funcopy.return_value = mock.return_value funcopy.side_effect = mock.side_effect funcopy._mock_children = mock._mock_children funcopy.assert_called_with = assert_called_with funcopy.assert_called_once_with = assert_called_once_with funcopy.assert_has_calls = assert_has_calls funcopy.assert_any_call = assert_any_call funcopy.reset_mock = reset_mock mock._mock_delegate = funcopy def _is_magic(name): return '__%s__' % name[2:-2] == name class _SentinelObject(object): "A unique, named, sentinel object." def __init__(self, name): self.name = name def __repr__(self): return 'sentinel.%s' % self.name class _Sentinel(object): """Access attributes to return a named object, usable as a sentinel.""" def __init__(self): self._sentinels = {} def __getattr__(self, name): if name == '__bases__': # Without this help(unittest.mock) raises an exception raise AttributeError return self._sentinels.setdefault(name, _SentinelObject(name)) sentinel = _Sentinel() DEFAULT = sentinel.DEFAULT _missing = sentinel.MISSING _deleted = sentinel.DELETED class OldStyleClass: pass ClassType = type(OldStyleClass) def _copy(value): if type(value) in (dict, list, tuple, set): return type(value)(value) return value ClassTypes = (type,) if six.PY2: ClassTypes = (type, ClassType) _allowed_names = set(( 'return_value', '_mock_return_value', 'side_effect', '_mock_side_effect', '_mock_parent', '_mock_new_parent', '_mock_name', '_mock_new_name' )) def _delegating_property(name): _allowed_names.add(name) _the_name = '_mock_' + name def _get(self, name=name, _the_name=_the_name): sig = self._mock_delegate if sig is None: return getattr(self, _the_name) return getattr(sig, name) def _set(self, value, name=name, _the_name=_the_name): sig = self._mock_delegate if sig is None: self.__dict__[_the_name] = value else: setattr(sig, name, value) return property(_get, _set) class _CallList(list): def __contains__(self, value): if not isinstance(value, list): return list.__contains__(self, value) len_value = len(value) len_self = len(self) if len_value > len_self: return False for i in range(0, len_self - len_value + 1): sub_list = self[i:i+len_value] if sub_list == value: return True return False def __repr__(self): return pprint.pformat(list(self)) def _check_and_set_parent(parent, value, name, new_name): if not _is_instance_mock(value): return False if ((value._mock_name or value._mock_new_name) or (value._mock_parent is not None) or (value._mock_new_parent is not None)): return False _parent = parent while _parent is not None: # setting a mock (value) as a child or return value of itself # should not modify the mock if _parent is value: return False _parent = _parent._mock_new_parent if new_name: value._mock_new_parent = parent value._mock_new_name = new_name if name: value._mock_parent = parent value._mock_name = name return True # Internal class to identify if we wrapped an iterator object or not. class _MockIter(object): def __init__(self, obj): self.obj = iter(obj) def __iter__(self): return self def __next__(self): return next(self.obj) class Base(object): _mock_return_value = DEFAULT _mock_side_effect = None def __init__(self, *args, **kwargs): pass class NonCallableMock(Base): """A non-callable version of `Mock`""" def __new__(cls, *args, **kw): # every instance has its own class # so we can create magic methods on the # class without stomping on other mocks new = type(cls.__name__, (cls,), {'__doc__': cls.__doc__}) instance = object.__new__(new) return instance def __init__( self, spec=None, wraps=None, name=None, spec_set=None, parent=None, _spec_state=None, _new_name='', _new_parent=None, _spec_as_instance=False, _eat_self=None, unsafe=False, **kwargs ): if _new_parent is None: _new_parent = parent __dict__ = self.__dict__ __dict__['_mock_parent'] = parent __dict__['_mock_name'] = name __dict__['_mock_new_name'] = _new_name __dict__['_mock_new_parent'] = _new_parent if spec_set is not None: spec = spec_set spec_set = True if _eat_self is None: _eat_self = parent is not None self._mock_add_spec(spec, spec_set, _spec_as_instance, _eat_self) __dict__['_mock_children'] = {} __dict__['_mock_wraps'] = wraps __dict__['_mock_delegate'] = None __dict__['_mock_called'] = False __dict__['_mock_call_args'] = None __dict__['_mock_call_count'] = 0 __dict__['_mock_call_args_list'] = _CallList() __dict__['_mock_mock_calls'] = _CallList() __dict__['method_calls'] = _CallList() __dict__['_mock_unsafe'] = unsafe if kwargs: self.configure_mock(**kwargs) _safe_super(NonCallableMock, self).__init__( spec, wraps, name, spec_set, parent, _spec_state ) def attach_mock(self, mock, attribute): """ Attach a mock as an attribute of this one, replacing its name and parent. Calls to the attached mock will be recorded in the `method_calls` and `mock_calls` attributes of this one.""" mock._mock_parent = None mock._mock_new_parent = None mock._mock_name = '' mock._mock_new_name = None setattr(self, attribute, mock) def mock_add_spec(self, spec, spec_set=False): """Add a spec to a mock. `spec` can either be an object or a list of strings. Only attributes on the `spec` can be fetched as attributes from the mock. If `spec_set` is True then only attributes on the spec can be set.""" self._mock_add_spec(spec, spec_set) def _mock_add_spec(self, spec, spec_set, _spec_as_instance=False, _eat_self=False): _spec_class = None _spec_signature = None if spec is not None and not _is_list(spec): if isinstance(spec, ClassTypes): _spec_class = spec else: _spec_class = _get_class(spec) res = _get_signature_object(spec, _spec_as_instance, _eat_self) _spec_signature = res and res[1] spec = dir(spec) __dict__ = self.__dict__ __dict__['_spec_class'] = _spec_class __dict__['_spec_set'] = spec_set __dict__['_spec_signature'] = _spec_signature __dict__['_mock_methods'] = spec def __get_return_value(self): ret = self._mock_return_value if self._mock_delegate is not None: ret = self._mock_delegate.return_value if ret is DEFAULT: ret = self._get_child_mock( _new_parent=self, _new_name='()' ) self.return_value = ret return ret def __set_return_value(self, value): if self._mock_delegate is not None: self._mock_delegate.return_value = value else: self._mock_return_value = value _check_and_set_parent(self, value, None, '()') __return_value_doc = "The value to be returned when the mock is called." return_value = property(__get_return_value, __set_return_value, __return_value_doc) @property def __class__(self): if self._spec_class is None: return type(self) return self._spec_class called = _delegating_property('called') call_count = _delegating_property('call_count') call_args = _delegating_property('call_args') call_args_list = _delegating_property('call_args_list') mock_calls = _delegating_property('mock_calls') def __get_side_effect(self): delegated = self._mock_delegate if delegated is None: return self._mock_side_effect sf = delegated.side_effect if (sf is not None and not callable(sf) and not isinstance(sf, _MockIter) and not _is_exception(sf)): sf = _MockIter(sf) delegated.side_effect = sf return sf def __set_side_effect(self, value): value = _try_iter(value) delegated = self._mock_delegate if delegated is None: self._mock_side_effect = value else: delegated.side_effect = value side_effect = property(__get_side_effect, __set_side_effect) def reset_mock(self, visited=None): "Restore the mock object to its initial state." if visited is None: visited = [] if id(self) in visited: return visited.append(id(self)) self.called = False self.call_args = None self.call_count = 0 self.mock_calls = _CallList() self.call_args_list = _CallList() self.method_calls = _CallList() for child in self._mock_children.values(): if isinstance(child, _SpecState): continue child.reset_mock(visited) ret = self._mock_return_value if _is_instance_mock(ret) and ret is not self: ret.reset_mock(visited) def configure_mock(self, **kwargs): """Set attributes on the mock through keyword arguments. Attributes plus return values and side effects can be set on child mocks using standard dot notation and unpacking a dictionary in the method call: >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} >>> mock.configure_mock(**attrs)""" for arg, val in sorted(kwargs.items(), # we sort on the number of dots so that # attributes are set before we set attributes on # attributes key=lambda entry: entry[0].count('.')): args = arg.split('.') final = args.pop() obj = self for entry in args: obj = getattr(obj, entry) setattr(obj, final, val) def __getattr__(self, name): if name in ('_mock_methods', '_mock_unsafe'): raise AttributeError(name) elif self._mock_methods is not None: if name not in self._mock_methods or name in _all_magics: raise AttributeError("Mock object has no attribute %r" % name) elif _is_magic(name): raise AttributeError(name) if not self._mock_unsafe: if name.startswith(('assert', 'assret')): raise AttributeError(name) result = self._mock_children.get(name) if result is _deleted: raise AttributeError(name) elif result is None: wraps = None if self._mock_wraps is not None: # XXXX should we get the attribute without triggering code # execution? wraps = getattr(self._mock_wraps, name) result = self._get_child_mock( parent=self, name=name, wraps=wraps, _new_name=name, _new_parent=self ) self._mock_children[name] = result elif isinstance(result, _SpecState): result = create_autospec( result.spec, result.spec_set, result.instance, result.parent, result.name ) self._mock_children[name] = result return result def __repr__(self): _name_list = [self._mock_new_name] _parent = self._mock_new_parent last = self dot = '.' if _name_list == ['()']: dot = '' seen = set() while _parent is not None: last = _parent _name_list.append(_parent._mock_new_name + dot) dot = '.' if _parent._mock_new_name == '()': dot = '' _parent = _parent._mock_new_parent # use ids here so as not to call __hash__ on the mocks if id(_parent) in seen: break seen.add(id(_parent)) _name_list = list(reversed(_name_list)) _first = last._mock_name or 'mock' if len(_name_list) > 1: if _name_list[1] not in ('()', '().'): _first += '.' _name_list[0] = _first name = ''.join(_name_list) name_string = '' if name not in ('mock', 'mock.'): name_string = ' name=%r' % name spec_string = '' if self._spec_class is not None: spec_string = ' spec=%r' if self._spec_set: spec_string = ' spec_set=%r' spec_string = spec_string % self._spec_class.__name__ return "<%s%s%s id='%s'>" % ( type(self).__name__, name_string, spec_string, id(self) ) def __dir__(self): """Filter the output of `dir(mock)` to only useful members.""" if not mock.FILTER_DIR and getattr(object, '__dir__', None): # object.__dir__ is not in 2.7 return object.__dir__(self) extras = self._mock_methods or [] from_type = dir(type(self)) from_dict = list(self.__dict__) if mock.FILTER_DIR: # object.__dir__ is not in 2.7 from_type = [e for e in from_type if not e.startswith('_')] from_dict = [e for e in from_dict if not e.startswith('_') or _is_magic(e)] return sorted(set(extras + from_type + from_dict + list(self._mock_children))) def __setattr__(self, name, value): if name in _allowed_names: # property setters go through here return object.__setattr__(self, name, value) elif (self._spec_set and self._mock_methods is not None and name not in self._mock_methods and name not in self.__dict__): raise AttributeError("Mock object has no attribute '%s'" % name) elif name in _unsupported_magics: msg = 'Attempting to set unsupported magic method %r.' % name raise AttributeError(msg) elif name in _all_magics: if self._mock_methods is not None and name not in self._mock_methods: raise AttributeError("Mock object has no attribute '%s'" % name) if not _is_instance_mock(value): setattr(type(self), name, _get_method(name, value)) original = value value = lambda *args, **kw: original(self, *args, **kw) else: # only set _new_name and not name so that mock_calls is tracked # but not method calls _check_and_set_parent(self, value, None, name) setattr(type(self), name, value) self._mock_children[name] = value elif name == '__class__': self._spec_class = value return else: if _check_and_set_parent(self, value, name, name): self._mock_children[name] = value return object.__setattr__(self, name, value) def __delattr__(self, name): if name in _all_magics and name in type(self).__dict__: delattr(type(self), name) if name not in self.__dict__: # for magic methods that are still MagicProxy objects and # not set on the instance itself return if name in self.__dict__: object.__delattr__(self, name) obj = self._mock_children.get(name, _missing) if obj is _deleted: raise AttributeError(name) if obj is not _missing: del self._mock_children[name] self._mock_children[name] = _deleted def _format_mock_call_signature(self, args, kwargs): name = self._mock_name or 'mock' return _format_call_signature(name, args, kwargs) def _format_mock_failure_message(self, args, kwargs): message = 'Expected call: %s\nActual call: %s' expected_string = self._format_mock_call_signature(args, kwargs) call_args = self.call_args if len(call_args) == 3: call_args = call_args[1:] actual_string = self._format_mock_call_signature(*call_args) return message % (expected_string, actual_string) def _call_matcher(self, _call): """ Given a call (or simply a (args, kwargs) tuple), return a comparison key suitable for matching with other calls. This is a best effort method which relies on the spec's signature, if available, or falls back on the arguments themselves. """ sig = self._spec_signature if sig is not None: if len(_call) == 2: name = '' args, kwargs = _call else: name, args, kwargs = _call try: return name, sig.bind(*args, **kwargs) except TypeError as e: e.__traceback__ = None return e else: return _call def assert_not_called(_mock_self): """assert that the mock was never called. """ self = _mock_self if self.call_count != 0: msg = ("Expected '%s' to not have been called. Called %s times." % (self._mock_name or 'mock', self.call_count)) raise AssertionError(msg) def assert_called(_mock_self): """assert that the mock was called at least once """ self = _mock_self if self.call_count == 0: msg = ("Expected '%s' to have been called." % self._mock_name or 'mock') raise AssertionError(msg) def assert_called_once(_mock_self): """assert that the mock was called only once. """ self = _mock_self if not self.call_count == 1: msg = ("Expected '%s' to have been called once. Called %s times." % (self._mock_name or 'mock', self.call_count)) raise AssertionError(msg) def assert_called_with(_mock_self, *args, **kwargs): """assert that the mock was called with the specified arguments. Raises an AssertionError if the args and keyword args passed in are different to the last call to the mock.""" self = _mock_self if self.call_args is None: expected = self._format_mock_call_signature(args, kwargs) raise AssertionError('Expected call: %s\nNot called' % (expected,)) def _error_message(cause): msg = self._format_mock_failure_message(args, kwargs) if six.PY2 and cause is not None: # Tack on some diagnostics for Python without __cause__ msg = '%s\n%s' % (msg, str(cause)) return msg expected = self._call_matcher((args, kwargs)) actual = self._call_matcher(self.call_args) if expected != actual: cause = expected if isinstance(expected, Exception) else None six.raise_from(AssertionError(_error_message(cause)), cause) def assert_called_once_with(_mock_self, *args, **kwargs): """assert that the mock was called exactly once and with the specified arguments.""" self = _mock_self if not self.call_count == 1: msg = ("Expected '%s' to be called once. Called %s times." % (self._mock_name or 'mock', self.call_count)) raise AssertionError(msg) return self.assert_called_with(*args, **kwargs) def assert_has_calls(self, calls, any_order=False): """assert the mock has been called with the specified calls. The `mock_calls` list is checked for the calls. If `any_order` is False (the default) then the calls must be sequential. There can be extra calls before or after the specified calls. If `any_order` is True then the calls can be in any order, but they must all appear in `mock_calls`.""" expected = [self._call_matcher(c) for c in calls] cause = expected if isinstance(expected, Exception) else None all_calls = _CallList(self._call_matcher(c) for c in self.mock_calls) if not any_order: if expected not in all_calls: six.raise_from(AssertionError( 'Calls not found.\nExpected: %r\n' 'Actual: %r' % (_CallList(calls), self.mock_calls) ), cause) return all_calls = list(all_calls) not_found = [] for kall in expected: try: all_calls.remove(kall) except ValueError: not_found.append(kall) if not_found: six.raise_from(AssertionError( '%r not all found in call list' % (tuple(not_found),) ), cause) def assert_any_call(self, *args, **kwargs): """assert the mock has been called with the specified arguments. The assert passes if the mock has *ever* been called, unlike `assert_called_with` and `assert_called_once_with` that only pass if the call is the most recent one.""" expected = self._call_matcher((args, kwargs)) actual = [self._call_matcher(c) for c in self.call_args_list] if expected not in actual: cause = expected if isinstance(expected, Exception) else None expected_string = self._format_mock_call_signature(args, kwargs) six.raise_from(AssertionError( '%s call not found' % expected_string ), cause) def _get_child_mock(self, **kw): """Create the child mocks for attributes and return value. By default child mocks will be the same type as the parent. Subclasses of Mock may want to override this to customize the way child mocks are made. For non-callable mocks the callable variant will be used (rather than any custom subclass).""" _type = type(self) if not issubclass(_type, CallableMixin): if issubclass(_type, NonCallableMagicMock): klass = MagicMock elif issubclass(_type, NonCallableMock) : klass = Mock else: klass = _type.__mro__[1] return klass(**kw) def _try_iter(obj): if obj is None: return obj if _is_exception(obj): return obj if _callable(obj): return obj try: return iter(obj) except TypeError: # XXXX backwards compatibility # but this will blow up on first call - so maybe we should fail early? return obj class CallableMixin(Base): def __init__(self, spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, parent=None, _spec_state=None, _new_name='', _new_parent=None, **kwargs): self.__dict__['_mock_return_value'] = return_value _safe_super(CallableMixin, self).__init__( spec, wraps, name, spec_set, parent, _spec_state, _new_name, _new_parent, **kwargs ) self.side_effect = side_effect def _mock_check_sig(self, *args, **kwargs): # stub method that can be replaced with one with a specific signature pass def __call__(_mock_self, *args, **kwargs): # can't use self in-case a function / method we are mocking uses self # in the signature _mock_self._mock_check_sig(*args, **kwargs) return _mock_self._mock_call(*args, **kwargs) def _mock_call(_mock_self, *args, **kwargs): self = _mock_self self.called = True self.call_count += 1 _new_name = self._mock_new_name _new_parent = self._mock_new_parent _call = _Call((args, kwargs), two=True) self.call_args = _call self.call_args_list.append(_call) self.mock_calls.append(_Call(('', args, kwargs))) seen = set() skip_next_dot = _new_name == '()' do_method_calls = self._mock_parent is not None name = self._mock_name while _new_parent is not None: this_mock_call = _Call((_new_name, args, kwargs)) if _new_parent._mock_new_name: dot = '.' if skip_next_dot: dot = '' skip_next_dot = False if _new_parent._mock_new_name == '()': skip_next_dot = True _new_name = _new_parent._mock_new_name + dot + _new_name if do_method_calls: if _new_name == name: this_method_call = this_mock_call else: this_method_call = _Call((name, args, kwargs)) _new_parent.method_calls.append(this_method_call) do_method_calls = _new_parent._mock_parent is not None if do_method_calls: name = _new_parent._mock_name + '.' + name _new_parent.mock_calls.append(this_mock_call) _new_parent = _new_parent._mock_new_parent # use ids here so as not to call __hash__ on the mocks _new_parent_id = id(_new_parent) if _new_parent_id in seen: break seen.add(_new_parent_id) ret_val = DEFAULT effect = self.side_effect if effect is not None: if _is_exception(effect): raise effect if not _callable(effect): result = next(effect) if _is_exception(result): raise result if result is DEFAULT: result = self.return_value return result ret_val = effect(*args, **kwargs) if (self._mock_wraps is not None and self._mock_return_value is DEFAULT): return self._mock_wraps(*args, **kwargs) if ret_val is DEFAULT: ret_val = self.return_value return ret_val class Mock(CallableMixin, NonCallableMock): """ Create a new `Mock` object. `Mock` takes several optional arguments that specify the behaviour of the Mock object: * `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`. If `spec` is an object (rather than a list of strings) then `mock.__class__` returns the class of the spec object. This allows mocks to pass `isinstance` tests. * `spec_set`: A stricter variant of `spec`. If used, attempting to *set* or get an attribute on the mock that isn't on the object passed as `spec_set` will raise an `AttributeError`. * `side_effect`: A function to be called whenever the Mock is called. See the `side_effect` attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns `DEFAULT`, the return value of this function is used as the return value. Alternatively `side_effect` can be an exception class or instance. In this case the exception will be raised when the mock is called. If `side_effect` is an iterable then each call to the mock will return the next value from the iterable. If any of the members of the iterable are exceptions they will be raised instead of returned. * `return_value`: The value returned when the mock is called. By default this is a new Mock (created on first access). See the `return_value` attribute. * `wraps`: Item for the mock object to wrap. If `wraps` is not None then calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the wrapped object (so attempting to access an attribute that doesn't exist will raise an `AttributeError`). If the mock has an explicit `return_value` set then calls are not passed to the wrapped object and the `return_value` is returned instead. * `name`: If the mock has a name then it will be used in the repr of the mock. This can be useful for debugging. The name is propagated to child mocks. Mocks can also be called with arbitrary keyword arguments. These will be used to set attributes on the mock after it is created. """ def _dot_lookup(thing, comp, import_path): try: return getattr(thing, comp) except AttributeError: __import__(import_path) return getattr(thing, comp) def _importer(target): components = target.split('.') import_path = components.pop(0) thing = __import__(import_path) for comp in components: import_path += ".%s" % comp thing = _dot_lookup(thing, comp, import_path) return thing def _is_started(patcher): # XXXX horrible return hasattr(patcher, 'is_local') class _patch(object): attribute_name = None _active_patches = [] def __init__( self, getter, attribute, new, spec, create, spec_set, autospec, new_callable, kwargs ): if new_callable is not None: if new is not DEFAULT: raise ValueError( "Cannot use 'new' and 'new_callable' together" ) if autospec is not None: raise ValueError( "Cannot use 'autospec' and 'new_callable' together" ) self.getter = getter self.attribute = attribute self.new = new self.new_callable = new_callable self.spec = spec self.create = create self.has_local = False self.spec_set = spec_set self.autospec = autospec self.kwargs = kwargs self.additional_patchers = [] def copy(self): patcher = _patch( self.getter, self.attribute, self.new, self.spec, self.create, self.spec_set, self.autospec, self.new_callable, self.kwargs ) patcher.attribute_name = self.attribute_name patcher.additional_patchers = [ p.copy() for p in self.additional_patchers ] return patcher def __call__(self, func): if isinstance(func, ClassTypes): return self.decorate_class(func) return self.decorate_callable(func) def decorate_class(self, klass): for attr in dir(klass): if not attr.startswith(patch.TEST_PREFIX): continue attr_value = getattr(klass, attr) if not hasattr(attr_value, "__call__"): continue patcher = self.copy() setattr(klass, attr, patcher(attr_value)) return klass def decorate_callable(self, func): if hasattr(func, 'patchings'): func.patchings.append(self) return func @wraps(func) def patched(*args, **keywargs): extra_args = [] entered_patchers = [] exc_info = tuple() try: for patching in patched.patchings: arg = patching.__enter__() entered_patchers.append(patching) if patching.attribute_name is not None: keywargs.update(arg) elif patching.new is DEFAULT: extra_args.append(arg) args += tuple(extra_args) return func(*args, **keywargs) except: if (patching not in entered_patchers and _is_started(patching)): # the patcher may have been started, but an exception # raised whilst entering one of its additional_patchers entered_patchers.append(patching) # Pass the exception to __exit__ exc_info = sys.exc_info() # re-raise the exception raise finally: for patching in reversed(entered_patchers): patching.__exit__(*exc_info) patched.patchings = [self] return patched def get_original(self): target = self.getter() name = self.attribute original = DEFAULT local = False try: original = target.__dict__[name] except (AttributeError, KeyError): original = getattr(target, name, DEFAULT) else: local = True if name in _builtins and isinstance(target, ModuleType): self.create = True if not self.create and original is DEFAULT: raise AttributeError( "%s does not have the attribute %r" % (target, name) ) return original, local def __enter__(self): """Perform the patch.""" new, spec, spec_set = self.new, self.spec, self.spec_set autospec, kwargs = self.autospec, self.kwargs new_callable = self.new_callable self.target = self.getter() # normalise False to None if spec is False: spec = None if spec_set is False: spec_set = None if autospec is False: autospec = None if spec is not None and autospec is not None: raise TypeError("Can't specify spec and autospec") if ((spec is not None or autospec is not None) and spec_set not in (True, None)): raise TypeError("Can't provide explicit spec_set *and* spec or autospec") original, local = self.get_original() if new is DEFAULT and autospec is None: inherit = False if spec is True: # set spec to the object we are replacing spec = original if spec_set is True: spec_set = original spec = None elif spec is not None: if spec_set is True: spec_set = spec spec = None elif spec_set is True: spec_set = original if spec is not None or spec_set is not None: if original is DEFAULT: raise TypeError("Can't use 'spec' with create=True") if isinstance(original, ClassTypes): # If we're patching out a class and there is a spec inherit = True Klass = MagicMock _kwargs = {} if new_callable is not None: Klass = new_callable elif spec is not None or spec_set is not None: this_spec = spec if spec_set is not None: this_spec = spec_set if _is_list(this_spec): not_callable = '__call__' not in this_spec else: not_callable = not _callable(this_spec) if not_callable: Klass = NonCallableMagicMock if spec is not None: _kwargs['spec'] = spec if spec_set is not None: _kwargs['spec_set'] = spec_set # add a name to mocks if (isinstance(Klass, type) and issubclass(Klass, NonCallableMock) and self.attribute): _kwargs['name'] = self.attribute _kwargs.update(kwargs) new = Klass(**_kwargs) if inherit and _is_instance_mock(new): # we can only tell if the instance should be callable if the # spec is not a list this_spec = spec if spec_set is not None: this_spec = spec_set if (not _is_list(this_spec) and not _instance_callable(this_spec)): Klass = NonCallableMagicMock _kwargs.pop('name') new.return_value = Klass(_new_parent=new, _new_name='()', **_kwargs) elif autospec is not None: # spec is ignored, new *must* be default, spec_set is treated # as a boolean. Should we check spec is not None and that spec_set # is a bool? if new is not DEFAULT: raise TypeError( "autospec creates the mock for you. Can't specify " "autospec and new." ) if original is DEFAULT: raise TypeError("Can't use 'autospec' with create=True") spec_set = bool(spec_set) if autospec is True: autospec = original new = create_autospec(autospec, spec_set=spec_set, _name=self.attribute, **kwargs) elif kwargs: # can't set keyword args when we aren't creating the mock # XXXX If new is a Mock we could call new.configure_mock(**kwargs) raise TypeError("Can't pass kwargs to a mock we aren't creating") new_attr = new self.temp_original = original self.is_local = local setattr(self.target, self.attribute, new_attr) if self.attribute_name is not None: extra_args = {} if self.new is DEFAULT: extra_args[self.attribute_name] = new for patching in self.additional_patchers: arg = patching.__enter__() if patching.new is DEFAULT: extra_args.update(arg) return extra_args return new def __exit__(self, *exc_info): """Undo the patch.""" if not _is_started(self): raise RuntimeError('stop called on unstarted patcher') if self.is_local and self.temp_original is not DEFAULT: setattr(self.target, self.attribute, self.temp_original) else: delattr(self.target, self.attribute) if not self.create and (not hasattr(self.target, self.attribute) or self.attribute in ('__doc__', '__module__', '__defaults__', '__annotations__', '__kwdefaults__')): # needed for proxy objects like django settings setattr(self.target, self.attribute, self.temp_original) del self.temp_original del self.is_local del self.target for patcher in reversed(self.additional_patchers): if _is_started(patcher): patcher.__exit__(*exc_info) def start(self): """Activate a patch, returning any created mock.""" result = self.__enter__() self._active_patches.append(self) return result def stop(self): """Stop an active patch.""" try: self._active_patches.remove(self) except ValueError: # If the patch hasn't been started this will fail pass return self.__exit__() def _get_target(target): try: target, attribute = target.rsplit('.', 1) except (TypeError, ValueError): raise TypeError("Need a valid target to patch. You supplied: %r" % (target,)) getter = lambda: _importer(target) return getter, attribute def _patch_object( target, attribute, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs ): """ patch the named member (`attribute`) on an object (`target`) with a mock object. `patch.object` can be used as a decorator, class decorator or a context manager. Arguments `new`, `spec`, `create`, `spec_set`, `autospec` and `new_callable` have the same meaning as for `patch`. Like `patch`, `patch.object` takes arbitrary keyword arguments for configuring the mock object it creates. When used as a class decorator `patch.object` honours `patch.TEST_PREFIX` for choosing which methods to wrap. """ getter = lambda: target return _patch( getter, attribute, new, spec, create, spec_set, autospec, new_callable, kwargs ) def _patch_multiple(target, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs): """Perform multiple patches in a single call. It takes the object to be patched (either as an object or a string to fetch the object by importing) and keyword arguments for the patches:: with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): ... Use `DEFAULT` as the value if you want `patch.multiple` to create mocks for you. In this case the created mocks are passed into a decorated function by keyword, and a dictionary is returned when `patch.multiple` is used as a context manager. `patch.multiple` can be used as a decorator, class decorator or a context manager. The arguments `spec`, `spec_set`, `create`, `autospec` and `new_callable` have the same meaning as for `patch`. These arguments will be applied to *all* patches done by `patch.multiple`. When used as a class decorator `patch.multiple` honours `patch.TEST_PREFIX` for choosing which methods to wrap. """ if type(target) in (unicode, str): getter = lambda: _importer(target) else: getter = lambda: target if not kwargs: raise ValueError( 'Must supply at least one keyword argument with patch.multiple' ) # need to wrap in a list for python 3, where items is a view items = list(kwargs.items()) attribute, new = items[0] patcher = _patch( getter, attribute, new, spec, create, spec_set, autospec, new_callable, {} ) patcher.attribute_name = attribute for attribute, new in items[1:]: this_patcher = _patch( getter, attribute, new, spec, create, spec_set, autospec, new_callable, {} ) this_patcher.attribute_name = attribute patcher.additional_patchers.append(this_patcher) return patcher def patch( target, new=DEFAULT, spec=None, create=False, spec_set=None, autospec=None, new_callable=None, **kwargs ): """ `patch` acts as a function decorator, class decorator or a context manager. Inside the body of the function or with statement, the `target` is patched with a `new` object. When the function/with statement exits the patch is undone. If `new` is omitted, then the target is replaced with a `MagicMock`. If `patch` is used as a decorator and `new` is omitted, the created mock is passed in as an extra argument to the decorated function. If `patch` is used as a context manager the created mock is returned by the context manager. `target` should be a string in the form `'package.module.ClassName'`. The `target` is imported and the specified object replaced with the `new` object, so the `target` must be importable from the environment you are calling `patch` from. The target is imported when the decorated function is executed, not at decoration time. The `spec` and `spec_set` keyword arguments are passed to the `MagicMock` if patch is creating one for you. In addition you can pass `spec=True` or `spec_set=True`, which causes patch to pass in the object being mocked as the spec/spec_set object. `new_callable` allows you to specify a different class, or callable object, that will be called to create the `new` object. By default `MagicMock` is used. A more powerful form of `spec` is `autospec`. If you set `autospec=True` then the mock will be created with a spec from the object being replaced. All attributes of the mock will also have the spec of the corresponding attribute of the object being replaced. Methods and functions being mocked will have their arguments checked and will raise a `TypeError` if they are called with the wrong signature. For mocks replacing a class, their return value (the 'instance') will have the same spec as the class. Instead of `autospec=True` you can pass `autospec=some_object` to use an arbitrary object as the spec instead of the one being replaced. By default `patch` will fail to replace attributes that don't exist. If you pass in `create=True`, and the attribute doesn't exist, patch will create the attribute for you when the patched function is called, and delete it again afterwards. This is useful for writing tests against attributes that your production code creates at runtime. It is off by default because it can be dangerous. With it switched on you can write passing tests against APIs that don't actually exist! Patch can be used as a `TestCase` class decorator. It works by decorating each test method in the class. This reduces the boilerplate code when your test methods share a common patchings set. `patch` finds tests by looking for method names that start with `patch.TEST_PREFIX`. By default this is `test`, which matches the way `unittest` finds tests. You can specify an alternative prefix by setting `patch.TEST_PREFIX`. Patch can be used as a context manager, with the with statement. Here the patching applies to the indented block after the with statement. If you use "as" then the patched object will be bound to the name after the "as"; very useful if `patch` is creating a mock object for you. `patch` takes arbitrary keyword arguments. These will be passed to the `Mock` (or `new_callable`) on construction. `patch.dict(...)`, `patch.multiple(...)` and `patch.object(...)` are available for alternate use-cases. """ getter, attribute = _get_target(target) return _patch( getter, attribute, new, spec, create, spec_set, autospec, new_callable, kwargs ) class _patch_dict(object): """ Patch a dictionary, or dictionary like object, and restore the dictionary to its original state after the test. `in_dict` can be a dictionary or a mapping like container. If it is a mapping then it must at least support getting, setting and deleting items plus iterating over keys. `in_dict` can also be a string specifying the name of the dictionary, which will then be fetched by importing it. `values` can be a dictionary of values to set in the dictionary. `values` can also be an iterable of `(key, value)` pairs. If `clear` is True then the dictionary will be cleared before the new values are set. `patch.dict` can also be called with arbitrary keyword arguments to set values in the dictionary:: with patch.dict('sys.modules', mymodule=Mock(), other_module=Mock()): ... `patch.dict` can be used as a context manager, decorator or class decorator. When used as a class decorator `patch.dict` honours `patch.TEST_PREFIX` for choosing which methods to wrap. """ def __init__(self, in_dict, values=(), clear=False, **kwargs): if isinstance(in_dict, basestring): in_dict = _importer(in_dict) self.in_dict = in_dict # support any argument supported by dict(...) constructor self.values = dict(values) self.values.update(kwargs) self.clear = clear self._original = None def __call__(self, f): if isinstance(f, ClassTypes): return self.decorate_class(f) @wraps(f) def _inner(*args, **kw): self._patch_dict() try: return f(*args, **kw) finally: self._unpatch_dict() return _inner def decorate_class(self, klass): for attr in dir(klass): attr_value = getattr(klass, attr) if (attr.startswith(patch.TEST_PREFIX) and hasattr(attr_value, "__call__")): decorator = _patch_dict(self.in_dict, self.values, self.clear) decorated = decorator(attr_value) setattr(klass, attr, decorated) return klass def __enter__(self): """Patch the dict.""" self._patch_dict() def _patch_dict(self): values = self.values in_dict = self.in_dict clear = self.clear try: original = in_dict.copy() except AttributeError: # dict like object with no copy method # must support iteration over keys original = {} for key in in_dict: original[key] = in_dict[key] self._original = original if clear: _clear_dict(in_dict) try: in_dict.update(values) except AttributeError: # dict like object with no update method for key in values: in_dict[key] = values[key] def _unpatch_dict(self): in_dict = self.in_dict original = self._original _clear_dict(in_dict) try: in_dict.update(original) except AttributeError: for key in original: in_dict[key] = original[key] def __exit__(self, *args): """Unpatch the dict.""" self._unpatch_dict() return False start = __enter__ stop = __exit__ def _clear_dict(in_dict): try: in_dict.clear() except AttributeError: keys = list(in_dict) for key in keys: del in_dict[key] def _patch_stopall(): """Stop all active patches. LIFO to unroll nested patches.""" for patch in reversed(_patch._active_patches): patch.stop() patch.object = _patch_object patch.dict = _patch_dict patch.multiple = _patch_multiple patch.stopall = _patch_stopall patch.TEST_PREFIX = 'test' magic_methods = ( "lt le gt ge eq ne " "getitem setitem delitem " "len contains iter " "hash str sizeof " "enter exit " # we added divmod and rdivmod here instead of numerics # because there is no idivmod "divmod rdivmod neg pos abs invert " "complex int float index " "trunc floor ceil " ) numerics = ( "add sub mul matmul div floordiv mod lshift rshift and xor or pow" ) if six.PY3: numerics += ' truediv' inplace = ' '.join('i%s' % n for n in numerics.split()) right = ' '.join('r%s' % n for n in numerics.split()) extra = '' if six.PY3: extra = 'bool next ' else: extra = 'unicode long nonzero oct hex truediv rtruediv ' # not including __prepare__, __instancecheck__, __subclasscheck__ # (as they are metaclass methods) # __del__ is not supported at all as it causes problems if it exists _non_defaults = set(( '__cmp__', '__getslice__', '__setslice__', '__coerce__', # <3.x '__get__', '__set__', '__delete__', '__reversed__', '__missing__', '__reduce__', '__reduce_ex__', '__getinitargs__', '__getnewargs__', '__getstate__', '__setstate__', '__getformat__', '__setformat__', '__repr__', '__dir__', '__subclasses__', '__format__', )) def _get_method(name, func): "Turns a callable object (like a mock) into a real function" def method(self, *args, **kw): return func(self, *args, **kw) method.__name__ = name return method _magics = set( '__%s__' % method for method in ' '.join([magic_methods, numerics, inplace, right, extra]).split() ) _all_magics = _magics | _non_defaults _unsupported_magics = set(( '__getattr__', '__setattr__', '__init__', '__new__', '__prepare__' '__instancecheck__', '__subclasscheck__', '__del__' )) _calculate_return_value = { '__hash__': lambda self: object.__hash__(self), '__str__': lambda self: object.__str__(self), '__sizeof__': lambda self: object.__sizeof__(self), '__unicode__': lambda self: unicode(object.__str__(self)), } _return_values = { '__lt__': NotImplemented, '__gt__': NotImplemented, '__le__': NotImplemented, '__ge__': NotImplemented, '__int__': 1, '__contains__': False, '__len__': 0, '__exit__': False, '__complex__': 1j, '__float__': 1.0, '__bool__': True, '__nonzero__': True, '__oct__': '1', '__hex__': '0x1', '__long__': long(1), '__index__': 1, } def _get_eq(self): def __eq__(other): ret_val = self.__eq__._mock_return_value if ret_val is not DEFAULT: return ret_val return self is other return __eq__ def _get_ne(self): def __ne__(other): if self.__ne__._mock_return_value is not DEFAULT: return DEFAULT return self is not other return __ne__ def _get_iter(self): def __iter__(): ret_val = self.__iter__._mock_return_value if ret_val is DEFAULT: return iter([]) # if ret_val was already an iterator, then calling iter on it should # return the iterator unchanged return iter(ret_val) return __iter__ _side_effect_methods = { '__eq__': _get_eq, '__ne__': _get_ne, '__iter__': _get_iter, } def _set_return_value(mock, method, name): fixed = _return_values.get(name, DEFAULT) if fixed is not DEFAULT: method.return_value = fixed return return_calulator = _calculate_return_value.get(name) if return_calulator is not None: try: return_value = return_calulator(mock) except AttributeError: # XXXX why do we return AttributeError here? # set it as a side_effect instead? return_value = AttributeError(name) method.return_value = return_value return side_effector = _side_effect_methods.get(name) if side_effector is not None: method.side_effect = side_effector(mock) class MagicMixin(object): def __init__(self, *args, **kw): self._mock_set_magics() # make magic work for kwargs in init _safe_super(MagicMixin, self).__init__(*args, **kw) self._mock_set_magics() # fix magic broken by upper level init def _mock_set_magics(self): these_magics = _magics if getattr(self, "_mock_methods", None) is not None: these_magics = _magics.intersection(self._mock_methods) remove_magics = set() remove_magics = _magics - these_magics for entry in remove_magics: if entry in type(self).__dict__: # remove unneeded magic methods delattr(self, entry) # don't overwrite existing attributes if called a second time these_magics = these_magics - set(type(self).__dict__) _type = type(self) for entry in these_magics: setattr(_type, entry, MagicProxy(entry, self)) class NonCallableMagicMock(MagicMixin, NonCallableMock): """A version of `MagicMock` that isn't callable.""" def mock_add_spec(self, spec, spec_set=False): """Add a spec to a mock. `spec` can either be an object or a list of strings. Only attributes on the `spec` can be fetched as attributes from the mock. If `spec_set` is True then only attributes on the spec can be set.""" self._mock_add_spec(spec, spec_set) self._mock_set_magics() class MagicMock(MagicMixin, Mock): """ MagicMock is a subclass of Mock with default implementations of most of the magic methods. You can use MagicMock without having to configure the magic methods yourself. If you use the `spec` or `spec_set` arguments then *only* magic methods that exist in the spec will be created. Attributes and the return value of a `MagicMock` will also be `MagicMocks`. """ def mock_add_spec(self, spec, spec_set=False): """Add a spec to a mock. `spec` can either be an object or a list of strings. Only attributes on the `spec` can be fetched as attributes from the mock. If `spec_set` is True then only attributes on the spec can be set.""" self._mock_add_spec(spec, spec_set) self._mock_set_magics() class MagicProxy(object): def __init__(self, name, parent): self.name = name self.parent = parent def __call__(self, *args, **kwargs): m = self.create_mock() return m(*args, **kwargs) def create_mock(self): entry = self.name parent = self.parent m = parent._get_child_mock(name=entry, _new_name=entry, _new_parent=parent) setattr(parent, entry, m) _set_return_value(parent, m, entry) return m def __get__(self, obj, _type=None): return self.create_mock() class _ANY(object): "A helper object that compares equal to everything." def __eq__(self, other): return True def __ne__(self, other): return False def __repr__(self): return '<ANY>' ANY = _ANY() def _format_call_signature(name, args, kwargs): message = '%s(%%s)' % name formatted_args = '' args_string = ', '.join([repr(arg) for arg in args]) def encode_item(item): if six.PY2 and isinstance(item, unicode): return item.encode("utf-8") else: return item kwargs_string = ', '.join([ '%s=%r' % (encode_item(key), value) for key, value in sorted(kwargs.items()) ]) if args_string: formatted_args = args_string if kwargs_string: if formatted_args: formatted_args += ', ' formatted_args += kwargs_string return message % formatted_args class _Call(tuple): """ A tuple for holding the results of a call to a mock, either in the form `(args, kwargs)` or `(name, args, kwargs)`. If args or kwargs are empty then a call tuple will compare equal to a tuple without those values. This makes comparisons less verbose:: _Call(('name', (), {})) == ('name',) _Call(('name', (1,), {})) == ('name', (1,)) _Call(((), {'a': 'b'})) == ({'a': 'b'},) The `_Call` object provides a useful shortcut for comparing with call:: _Call(((1, 2), {'a': 3})) == call(1, 2, a=3) _Call(('foo', (1, 2), {'a': 3})) == call.foo(1, 2, a=3) If the _Call has no name then it will match any name. """ def __new__(cls, value=(), name=None, parent=None, two=False, from_kall=True): name = '' args = () kwargs = {} _len = len(value) if _len == 3: name, args, kwargs = value elif _len == 2: first, second = value if isinstance(first, basestring): name = first if isinstance(second, tuple): args = second else: kwargs = second else: args, kwargs = first, second elif _len == 1: value, = value if isinstance(value, basestring): name = value elif isinstance(value, tuple): args = value else: kwargs = value if two: return tuple.__new__(cls, (args, kwargs)) return tuple.__new__(cls, (name, args, kwargs)) def __init__(self, value=(), name=None, parent=None, two=False, from_kall=True): self.name = name self.parent = parent self.from_kall = from_kall def __eq__(self, other): if other is ANY: return True try: len_other = len(other) except TypeError: return False self_name = '' if len(self) == 2: self_args, self_kwargs = self else: self_name, self_args, self_kwargs = self other_name = '' if len_other == 0: other_args, other_kwargs = (), {} elif len_other == 3: other_name, other_args, other_kwargs = other elif len_other == 1: value, = other if isinstance(value, tuple): other_args = value other_kwargs = {} elif isinstance(value, basestring): other_name = value other_args, other_kwargs = (), {} else: other_args = () other_kwargs = value elif len_other == 2: # could be (name, args) or (name, kwargs) or (args, kwargs) first, second = other if isinstance(first, basestring): other_name = first if isinstance(second, tuple): other_args, other_kwargs = second, {} else: other_args, other_kwargs = (), second else: other_args, other_kwargs = first, second else: return False if self_name and other_name != self_name: return False # this order is important for ANY to work! return (other_args, other_kwargs) == (self_args, self_kwargs) def __ne__(self, other): return not self.__eq__(other) def __call__(self, *args, **kwargs): if self.name is None: return _Call(('', args, kwargs), name='()') name = self.name + '()' return _Call((self.name, args, kwargs), name=name, parent=self) def __getattr__(self, attr): if self.name is None: return _Call(name=attr, from_kall=False) name = '%s.%s' % (self.name, attr) return _Call(name=name, parent=self, from_kall=False) def count(self, *args, **kwargs): return self.__getattr__('count')(*args, **kwargs) def index(self, *args, **kwargs): return self.__getattr__('index')(*args, **kwargs) def __repr__(self): if not self.from_kall: name = self.name or 'call' if name.startswith('()'): name = 'call%s' % name return name if len(self) == 2: name = 'call' args, kwargs = self else: name, args, kwargs = self if not name: name = 'call' elif not name.startswith('()'): name = 'call.%s' % name else: name = 'call%s' % name return _format_call_signature(name, args, kwargs) def call_list(self): """For a call object that represents multiple calls, `call_list` returns a list of all the intermediate calls as well as the final call.""" vals = [] thing = self while thing is not None: if thing.from_kall: vals.append(thing) thing = thing.parent return _CallList(reversed(vals)) call = _Call(from_kall=False) def create_autospec(spec, spec_set=False, instance=False, _parent=None, _name=None, **kwargs): """Create a mock object using another object as a spec. Attributes on the mock will use the corresponding attribute on the `spec` object as their spec. Functions or methods being mocked will have their arguments checked to check that they are called with the correct signature. If `spec_set` is True then attempting to set attributes that don't exist on the spec object will raise an `AttributeError`. If a class is used as a spec then the return value of the mock (the instance of the class) will have the same spec. You can use a class as the spec for an instance object by passing `instance=True`. The returned mock will only be callable if instances of the mock are callable. `create_autospec` also takes arbitrary keyword arguments that are passed to the constructor of the created mock.""" if _is_list(spec): # can't pass a list instance to the mock constructor as it will be # interpreted as a list of strings spec = type(spec) is_type = isinstance(spec, ClassTypes) _kwargs = {'spec': spec} if spec_set: _kwargs = {'spec_set': spec} elif spec is None: # None we mock with a normal mock without a spec _kwargs = {} if _kwargs and instance: _kwargs['_spec_as_instance'] = True _kwargs.update(kwargs) Klass = MagicMock if type(spec) in DescriptorTypes: # descriptors don't have a spec # because we don't know what type they return _kwargs = {} elif not _callable(spec): Klass = NonCallableMagicMock elif is_type and instance and not _instance_callable(spec): Klass = NonCallableMagicMock _name = _kwargs.pop('name', _name) _new_name = _name if _parent is None: # for a top level object no _new_name should be set _new_name = '' mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name, name=_name, **_kwargs) if isinstance(spec, FunctionTypes): # should only happen at the top level because we don't # recurse for functions mock = _set_signature(mock, spec) else: _check_signature(spec, mock, is_type, instance) if _parent is not None and not instance: _parent._mock_children[_name] = mock if is_type and not instance and 'return_value' not in kwargs: mock.return_value = create_autospec(spec, spec_set, instance=True, _name='()', _parent=mock) for entry in dir(spec): if _is_magic(entry): # MagicMock already does the useful magic methods for us continue # XXXX do we need a better way of getting attributes without # triggering code execution (?) Probably not - we need the actual # object to mock it so we would rather trigger a property than mock # the property descriptor. Likewise we want to mock out dynamically # provided attributes. # XXXX what about attributes that raise exceptions other than # AttributeError on being fetched? # we could be resilient against it, or catch and propagate the # exception when the attribute is fetched from the mock try: original = getattr(spec, entry) except AttributeError: continue kwargs = {'spec': original} if spec_set: kwargs = {'spec_set': original} if not isinstance(original, FunctionTypes): new = _SpecState(original, spec_set, mock, entry, instance) mock._mock_children[entry] = new else: parent = mock if isinstance(spec, FunctionTypes): parent = mock.mock skipfirst = _must_skip(spec, entry, is_type) kwargs['_eat_self'] = skipfirst new = MagicMock(parent=parent, name=entry, _new_name=entry, _new_parent=parent, **kwargs) mock._mock_children[entry] = new _check_signature(original, new, skipfirst=skipfirst) # so functions created with _set_signature become instance attributes, # *plus* their underlying mock exists in _mock_children of the parent # mock. Adding to _mock_children may be unnecessary where we are also # setting as an instance attribute? if isinstance(new, FunctionTypes): setattr(mock, entry, new) return mock def _must_skip(spec, entry, is_type): """ Return whether we should skip the first argument on spec's `entry` attribute. """ if not isinstance(spec, ClassTypes): if entry in getattr(spec, '__dict__', {}): # instance attribute - shouldn't skip return False spec = spec.__class__ if not hasattr(spec, '__mro__'): # old style class: can't have descriptors anyway return is_type for klass in spec.__mro__: result = klass.__dict__.get(entry, DEFAULT) if result is DEFAULT: continue if isinstance(result, (staticmethod, classmethod)): return False elif isinstance(getattr(result, '__get__', None), MethodWrapperTypes): # Normal method => skip if looked up on type # (if looked up on instance, self is already skipped) return is_type else: return False # shouldn't get here unless function is a dynamically provided attribute # XXXX untested behaviour return is_type def _get_class(obj): try: return obj.__class__ except AttributeError: # it is possible for objects to have no __class__ return type(obj) class _SpecState(object): def __init__(self, spec, spec_set=False, parent=None, name=None, ids=None, instance=False): self.spec = spec self.ids = ids self.spec_set = spec_set self.parent = parent self.instance = instance self.name = name FunctionTypes = ( # python function type(create_autospec), # instance method type(ANY.__eq__), ) MethodWrapperTypes = ( type(ANY.__eq__.__get__), ) file_spec = None def _iterate_read_data(read_data): # Helper for mock_open: # Retrieve lines from read_data via a generator so that separate calls to # readline, read, and readlines are properly interleaved sep = b'\n' if isinstance(read_data, bytes) else '\n' data_as_list = [l + sep for l in read_data.split(sep)] if data_as_list[-1] == sep: # If the last line ended in a newline, the list comprehension will have an # extra entry that's just a newline. Remove this. data_as_list = data_as_list[:-1] else: # If there wasn't an extra newline by itself, then the file being # emulated doesn't have a newline to end the last line remove the # newline that our naive format() added data_as_list[-1] = data_as_list[-1][:-1] for line in data_as_list: yield line def mock_open(mock=None, read_data=''): """ A helper function to create a mock to replace the use of `open`. It works for `open` called directly or used as a context manager. The `mock` argument is the mock object to configure. If `None` (the default) then a `MagicMock` will be created for you, with the API limited to methods or attributes available on standard file handles. `read_data` is a string for the `read` methoddline`, and `readlines` of the file handle to return. This is an empty string by default. """ def _readlines_side_effect(*args, **kwargs): if handle.readlines.return_value is not None: return handle.readlines.return_value return list(_state[0]) def _read_side_effect(*args, **kwargs): if handle.read.return_value is not None: return handle.read.return_value return type(read_data)().join(_state[0]) def _readline_side_effect(): if handle.readline.return_value is not None: while True: yield handle.readline.return_value for line in _state[0]: yield line global file_spec if file_spec is None: # set on first use if six.PY3: import _io file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) else: file_spec = file if mock is None: mock = MagicMock(name='open', spec=open) handle = MagicMock(spec=file_spec) handle.__enter__.return_value = handle _state = [_iterate_read_data(read_data), None] handle.write.return_value = None handle.read.return_value = None handle.readline.return_value = None handle.readlines.return_value = None handle.read.side_effect = _read_side_effect _state[1] = _readline_side_effect() handle.readline.side_effect = _state[1] handle.readlines.side_effect = _readlines_side_effect def reset_data(*args, **kwargs): _state[0] = _iterate_read_data(read_data) if handle.readline.side_effect == _state[1]: # Only reset the side effect if the user hasn't overridden it. _state[1] = _readline_side_effect() handle.readline.side_effect = _state[1] return DEFAULT mock.side_effect = reset_data mock.return_value = handle return mock class PropertyMock(Mock): """ A mock intended to be used as a property, or other descriptor, on a class. `PropertyMock` provides `__get__` and `__set__` methods so you can specify a return value when it is fetched. Fetching a `PropertyMock` instance from an object calls the mock, with no args. Setting it calls the mock with the value being set. """ def _get_child_mock(self, **kwargs): return MagicMock(**kwargs) def __get__(self, obj, obj_type): return self() def __set__(self, obj, val): self(val) mock/mock/__init__.py 0000644 00000000317 15025013454 0010537 0 ustar 00 from __future__ import absolute_import import mock.mock as _mock from mock.mock import * __all__ = _mock.__all__ #import mock.mock as _mock #for name in dir(_mock): # globals()[name] = getattr(_mock, name) mock/tools/pre-applypatch 0000644 00000001664 15025013454 0011517 0 ustar 00 #!/bin/bash # # An example hook script to verify what is about to be committed # by applypatch from an e-mail message. # # The hook should exit with non-zero status after issuing an # appropriate message if it wants to stop the commit. # # To enable this hook, rename this file to "pre-applypatch". set -eu #. git-sh-setup echo "** in hook **" function test_version { version=$1 host=$(ls ~/.virtualenvs/mock-$version-* -d | sed -e "s/^.*mock-$version-//") if [ -z "$host" ]; then echo "No host found for $version" return 1 fi echo testing $version in virtualenv mock-$version-$host on ssh host $host ssh $host "cd work/mock && . ~/.virtualenvs/mock-$version-$host/bin/activate && pip install .[test] && unit2" } find . -name "*.pyc" -exec rm "{}" \; test_version 2.6 test_version 2.7 test_version 3.3 test_version 3.4 test_version 3.5 test_version cpython test_version pypy echo '** pre-apply complete and successful **' mock/tools/applypatch-transform 0000644 00000002456 15025013454 0012744 0 ustar 00 #!/bin/sh # # An example hook script to transform a patch taken from an email # by git am. # # The hook should exit with non-zero status after issuing an # appropriate message if it wants to stop the commit. The hook is # allowed to edit the patch file. # # To enable this hook, rename this file to "applypatch-transform". # # This example changes the path of Lib/unittest/mock.py to mock.py # Lib/unittest/tests/testmock to tests and Misc/NEWS to NEWS, and # finally skips any patches that did not alter mock.py or its tests. set -eux patch_path=$1 # Pull out mock.py filterdiff --clean --strip 3 --addprefix=a/mock/ -i 'a/Lib/unittest/mock.py' -i 'b/Lib/unittest/mock.py' $patch_path > $patch_path.mock # And the tests filterdiff --clean --strip 5 --addprefix=a/mock/tests/ -i 'a/Lib/unittest/test/testmock/*.py' -i 'b/Lib/unittest/test/testmock/*.py' $patch_path > $patch_path.tests # Lastly we want to pick up any NEWS entries. filterdiff --strip 2 --addprefix=a/ -i a/Misc/NEWS -i b/Misc/NEWS $patch_path > $patch_path.NEWS cp $patch_path $patch_path.orig # bash cat $patch_path.mock $patch_path.tests > $patch_path filtered=$(cat $patch_path) if [ -n "${filtered}" ]; then cat $patch_path.NEWS >> $patch_path exitcode=0 else exitcode=1 fi rm $patch_path.mock $patch_path.tests $patch_path.NEWS exit $exitcode mock/requirements.txt 0000644 00000000331 15025013454 0010755 0 ustar 00 funcsigs>=1;python_version<"3.3" # For runtime needs this is correct. For setup_requires needs, 1.2.0 is needed # but setuptools can't cope with conflicts in setup_requires, so thats # unversioned. pbr>=0.11 six>=1.9 mock/.gitignore 0000644 00000000254 15025013454 0007465 0 ustar 00 .*\.pyc *.rej html/ mock\.egg-info/ mock\.wpu \.tox/ build dist latex .*\$py\.class runtox *~ *.pyc .testrepository .*.swp AUTHORS ChangeLog .eggs README.saved README.html mock/.testr.conf 0000644 00000000221 15025013454 0007555 0 ustar 00 [DEFAULT] test_command=${PYTHON:-python} -m subunit.run discover . $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list mock/setup.py 0000644 00000000176 15025013454 0007212 0 ustar 00 #!/usr/bin/env python import setuptools setuptools.setup( setup_requires=['pbr>=1.3', 'setuptools>=17.1'], pbr=True) mock/tox.ini 0000644 00000001340 15025013454 0007005 0 ustar 00 [tox] envlist = py25,py26,py27,py31,pypy,py32,py33,jython [testenv] deps=unittest2 commands={envbindir}/unit2 discover [] [testenv:py26] commands= {envbindir}/unit2 discover [] {envbindir}/sphinx-build -E -b doctest docs html {envbindir}/sphinx-build -E docs html deps = unittest2 sphinx [testenv:py27] commands= {envbindir}/unit2 discover [] {envbindir}/sphinx-build -E -b doctest docs html deps = unittest2 sphinx [testenv:py31] deps = unittest2py3k [testenv:py32] commands= {envbindir}/python -m unittest discover [] deps = [testenv:py33] commands= {envbindir}/python -m unittest discover [] deps = # note for jython. Execute in tests directory: # rm `find . -name '*$py.class'` mock/README.rst 0000644 00000002315 15025013454 0007164 0 ustar 00 mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects and make assertions about how they have been used. mock is now part of the Python standard library, available as `unittest.mock <https://docs.python.org/dev/library/unittest.mock.html>`_ in Python 3.3 onwards. This package contains a rolling backport of the standard library mock code compatible with Python 2.6 and up, and 3.3 and up. Please see the standard library documentation for more details. :Homepage: `Mock Homepage`_ :Download: `Mock on PyPI`_ :Documentation: `Python Docs`_ :License: `BSD License`_ :Support: `Mailing list (testing-in-python@lists.idyll.org) <http://lists.idyll.org/listinfo/testing-in-python>`_ :Issue tracker: `Github Issues <https://github.com/testing-cabal/mock/issues>`_ :Build status: .. image:: https://travis-ci.org/testing-cabal/mock.svg?branch=master :target: https://travis-ci.org/testing-cabal/mock .. _Mock Homepage: https://github.com/testing-cabal/mock .. _BSD License: http://github.com/testing-cabal/mock/blob/master/LICENSE.txt .. _Python Docs: https://docs.python.org/dev/library/unittest.mock.html .. _mock on PyPI: http://pypi.python.org/pypi/mock mock/mock.wpr 0000644 00000002234 15025013454 0007160 0 ustar 00 #!wing #!version=4.0 ################################################################## # Wing IDE project file # ################################################################## [project attributes] proj.directory-list = [{'dirloc': loc('.'), 'excludes': [u'latex', u'.hg', u'.tox', u'dist', u'htmlcov', u'extendmock.py', u'__pycache__', u'html', u'build', u'mock.egg-info', u'tests/__pycache__', u'.hgignore', u'.hgtags'], 'filter': '*', 'include_hidden': 0, 'recursive': 1, 'watch_for_changes': 1}] proj.file-type = 'shared' testing.auto-test-file-specs = ('test*.py',) mock/docs/conf.py 0000644 00000014311 15025013454 0007723 0 ustar 00 # -*- coding: utf-8 -*- # # Mock documentation build configuration file, created by # sphinx-quickstart on Mon Nov 17 18:12:00 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. import sys, os sys.path.insert(0, os.path.abspath('..')) import mock # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. #sys.path.append(os.path.abspath('some/directory')) # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.doctest'] doctest_global_setup = """ import os import sys import mock from mock import * # yeah, I know :-/ import unittest2 import __main__ if os.getcwd() not in sys.path: sys.path.append(os.getcwd()) # keep a reference to __main__ sys.modules['__main'] = __main__ class ProxyModule(object): def __init__(self): self.__dict__ = globals() sys.modules['__main__'] = ProxyModule() """ doctest_global_cleanup = """ sys.modules['__main__'] = sys.modules['__main'] """ html_theme = 'nature' html_theme_options = {} # Add any paths that contain templates here, relative to this directory. #templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.txt' # The master toctree document. master_doc = 'index' # General substitutions. project = u'Mock' copyright = u'2007-2015, Michael Foord & the mock team' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. Supplied by pbr. # # The short X.Y version. version = mock.mock._v.brief_string() # The full version, including alpha/beta/rc tags. release = mock.__version__ # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: (Set from pbr) today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directories, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = False # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'friendly' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. #html_style = 'adctheme.css' # The name for this set of Sphinx documents. If None, it defaults to # "<project> v<release> documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". #html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. html_use_modindex = False # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, the reST sources are included in the HTML build as _sources/<name>. #html_copy_source = True # If true, an OpenSearch description file will be output, and all pages will # contain a <link> tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'Mockdoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). latex_font_size = '12pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ ('index', 'Mock.tex', u'Mock Documentation', u'Michael Foord', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. latex_use_modindex = False mock/docs/index.txt 0000644 00000013375 15025013454 0010305 0 ustar 00 ==================================== Mock - Mocking and Testing Library ==================================== :Version: |release| :Date: |today| :Homepage: `Mock Homepage`_ :Download: `Mock on PyPI`_ :Documentation: `Python Docs`_ :License: `BSD License`_ :Support: `Mailing list (testing-in-python@lists.idyll.org) <http://lists.idyll.org/listinfo/testing-in-python>`_ :Issue tracker: `Github Issues <https://github.com/testing-cabal/mock/issues>`_ :Last sync: cb6aab1248c4aec4dd578bea717854505a6fb55d .. _Mock Homepage: https://github.com/testing-cabal/mock .. _BSD License: http://github.com/testing-cabal/mock/blob/master/LICENSE.txt .. _Python Docs: https://docs.python.org/dev/library/unittest.mock.html .. module:: mock :synopsis: Mock object and testing library. .. index:: introduction TOC +++ .. toctree:: :maxdepth: 2 changelog Introduction ++++++++++++ mock is a library for testing in Python. It allows you to replace parts of your system under test with mock objects and make assertions about how they have been used. mock is now part of the Python standard library, available as ``unittest.mock`` in Python 3.3 onwards. However, if you are writing code that runs on multiple versions of Python the ``mock`` package is better, as you get the newest features from the latest release of Python available for all Pythons. The ``mock`` package contains a rolling backport of the standard library mock code compatible with Python 2.6 and up, and 3.3 and up. Python 3.2 is supported by mock 1.3.0 and below - with pip no longer supporting 3.2, we cannot test against that version anymore. Please see the standard library documentation for usage details. .. index:: installing .. _installing: Installing ++++++++++ The current version is |release|. Mock is stable and widely used. * `mock on PyPI <http://pypi.python.org/pypi/mock>`_ .. index:: repository .. index:: git You can checkout the latest development version from Github repository with the following command: ``git clone https://github.com/testing-cabal/mock`` .. index:: pip You can install mock with pip: | ``pip install -U mock`` Alternatively you can download the mock distribution from PyPI and after unpacking run: ``python setup.py install`` .. index:: bug reports Bug Reports +++++++++++ Mock uses `unittest2 <http://pypi.python.org/pypi/unittest2>`_ for its own Issues with the backport process, such as compatibility with a particular Python, should be reported to the `bug tracker <https://github.com/testing-cabal/mock/issues>`_. Feature requests and issues with Mock functionality should be reported to the `Python bug tracker <https://bugs.python.org>`_. .. index:: python changes Python Changes ++++++++++++++ Python NEWS entries from cPython: .. include:: ../NEWS .. index:: older versions Older Versions of Python ++++++++++++++++++++++++ Version 1.0.1 is the last version compatible with Python < 2.6. .. index:: maintainer notes Maintainer Notes ++++++++++++++++ Development =========== Checkout from git (see :ref:`installing`) and submit pull requests. Committers can just push as desired: since all semantic development takes place in cPython, the backport process is as lightweight as we can make it. mock is CI tested using Travis-CI on Python versions 2.6, 2.7, 3.3, 3.4, 3.5, nightly Python 3 builds, pypy, pypy3. Jython support is desired, if someone could contribute a patch to .travis.yml to support it that would be excellent. Releasing ========= NB: please use semver. Bump the major component on API breaks, minor on all non-bugfix changes, patch on bugfix only changes. 1. tag -s, push --tags origin master 2. setup.py sdist bdist_wheel upload -s Backporting rules ================= isinstance checks in cPython to ``type`` need to check ``ClassTypes``. Code calling ``obj.isidentifier`` needs to change to ``_isidentifier(obj)``. Backporting process =================== 1. Patch your git am with `my patch <https://github.com/rbtcollins/git>`_. 2. Install the applypatch-transform hook from tools/ to your .git hooks dir. 3. Configure a pre-applypatch hook to test at least all the cPython versions we support on each patch that is applied. I use containers, and a sample script is in tools/pre-applypatch. 4. Pull down the cPython git mirror: https://github.com/python/cpython.git 5. Export the new revisions since the ``Last sync`` at the top of this document:: revs=${lastsync} rm migrate-export git log --pretty="format:%H " $revs.. -- Lib/unittest/mock.py \ Lib/unittest/test/testmock/ > migrate-revs tac migrate-revs > migrate-sorted-revs for rev in $(< migrate-sorted-revs); do git format-patch -1 $rev -k --stdout >> migrate-export; done echo NEW SYNC POINT: $(git rev-parse HEAD) 6. Import into mock:: git am -k --reject $path-to-cpython/migrate-export This will transform the patches automatically. Currently it will error on every NEWS change as I haven't gotten around to making those patches automatic. Fixup any errors that occur. When the patch is ready, do a ``git add -u`` to update the index and then ``git am --continue`` to move onto the next patch. If the patch is inappropriate e.g. the patch removing __ne__ which would break older pythons, then either do ``git reset --hard; git am --skip`` to discard any partially applied changes and skip over it, or, if it has a NEWS entry thats worth preserving, edit it down to just that, with a note such as we have for the ``__ne__`` patch, and continue on from there. The goal is that every patch work at all times. 7. After the import is complete, update this document with the new sync point. 8. Push to a personal branch and propose a PR to the main repo. This will make Travis-CI test it. If it works, push to the main repo. mock/unittest.cfg 0000644 00000003241 15025013454 0010034 0 ustar 00 [unittest] plugins = unittest2.plugins.debugger unittest2.plugins.checker unittest2.plugins.doctestloader unittest2.plugins.matchregexp unittest2.plugins.moduleloading unittest2.plugins.testcoverage unittest2.plugins.growl unittest2.plugins.filtertests unittest2.plugins.junitxml unittest2.plugins.timed unittest2.plugins.counttests unittest2.plugins.logchannels excluded-plugins = # 0, 1 or 2 (default is 1) # quiet, normal or verbose # can be overriden at command line verbosity = normal # true or false # even if false can be switched on at command line catch = buffer = failfast = [matchregexp] always-on = False full-path = True [debugger] always-on = False errors-only = True [coverage] always-on = False config = report-html = False # only used if report-html is false annotate = False # defaults to './htmlcov/' html-directory = # if unset will output to console text-file = branch = False timid = False cover-pylib = False exclude-lines = # Have to re-enable the standard pragma pragma: no cover # Don't complain about missing debug-only code: def __repr__ if self\.debug # Don't complain if tests don't hit defensive assertion code: raise AssertionError raise NotImplementedError # Don't complain if non-runnable code isn't run: if 0: if __name__ == .__main__. ignore-errors = False modules = [growl] always-on = False [doctest] always-on = False [module-loading] always-on = False [checker] always-on = False pep8 = False pyflakes = True [junit-xml] always-on = False path = junit.xml [timed] always-on = True threshold = 0.01 [count] always-on = True enhanced = False mock/extendmock.py 0000644 00000000042 15025013454 0010203 0 ustar 00 # merged into mock.py in Mock 0.7 mock/setup.cfg 0000644 00000003034 15025013454 0007315 0 ustar 00 [metadata] name = mock summary = Rolling backport of unittest.mock for all Pythons home-page = https://github.com/testing-cabal/mock description-file = README.rst author = Testing Cabal author-email = testing-in-python@lists.idyll.org classifier = Development Status :: 5 - Production/Stable Environment :: Console Intended Audience :: Developers License :: OSI Approved :: BSD License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.6 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.2 Programming Language :: Python :: 3.3 Programming Language :: Python :: 3.4 Programming Language :: Python :: 3.5 Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: Jython Programming Language :: Python :: Implementation :: PyPy Topic :: Software Development :: Libraries Topic :: Software Development :: Libraries :: Python Modules Topic :: Software Development :: Testing keyword = testing, test, mock, mocking, unittest, patching, stubs, fakes, doubles [extras] test = unittest2>=1.1.0 docs = jinja2<2.7:python_version<"3.3" and python_version>="3" Pygments<2:python_version<"3.3" and python_version>="3" sphinx<1.3:python_version<"3.3" and python_version>="3" sphinx:python_version<"3" or python_version>="3.3" [files] packages = mock [bdist_wheel] universal = 1 mock/.travis.yml 0000644 00000001012 15025013454 0007577 0 ustar 00 sudo: false language: python python: - "2.6" - "2.7" - "3.3" - "3.4" - pypy - pypy3 matrix: include: # Travis nightly look to be 3.5.0a4, b3 is out and the syntax error we see # doesn't happen in trunk. - python: "nightly" env: SKIP_DOCS=1 install: - pip install -U pip - pip install -U wheel setuptools - pip install -U .[docs,test] - pip list - python --version script: - unit2 - if [ -z "$SKIP_DOCS" ]; then python setup.py build_sphinx; fi - rst2html.py --strict README.rst README.html mock/LICENSE.txt 0000644 00000002476 15025013454 0007330 0 ustar 00 Copyright (c) 2003-2013, Michael Foord & the mock team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mock/NEWS 0000644 00000006030 15025013454 0006172 0 ustar 00 Library ------- - Issue #26323: Add Mock.assert_called() and Mock.assert_called_once() methods to unittest.mock. Patch written by Amit Saha. - Issue #22138: Fix mock.patch behavior when patching descriptors. Restore original values after patching. Patch contributed by Sean McCully. - Issue #24857: Comparing call_args to a long sequence now correctly returns a boolean result instead of raising an exception. Patch by A Kaptur. - Issue #23004: mock_open() now reads binary data correctly when the type of read_data is bytes. Initial patch by Aaron Hill. - Issue #21750: mock_open.read_data can now be read from each instance, as it could in Python 3.3. - Issue #18622: unittest.mock.mock_open().reset_mock would recurse infinitely. Patch from Nicola Palumbo and Laurent De Buyst. - Issue #23661: unittest.mock side_effects can now be exceptions again. This was a regression vs Python 3.4. Patch from Ignacio Rossi - Issue #23310: Fix MagicMock's initializer to work with __methods__, just like configure_mock(). Patch by Kasia Jachim. - Issue #23568: Add rdivmod support to MagicMock() objects. Patch by Håkan Lövdahl. - Issue #23581: Add matmul support to MagicMock. Patch by Håkan Lövdahl. - Issue #23326: Removed __ne__ implementations. Since fixing default __ne__ implementation in issue #21408 they are redundant. *** NOT BACKPORTED *** - Issue #21270: We now override tuple methods in mock.call objects so that they can be used as normal call attributes. - Issue #21256: Printout of keyword args should be in deterministic order in a mock function call. This will help to write better doctests. - Issue #21262: New method assert_not_called for Mock. It raises AssertionError if the mock has been called. - Issue #21238: New keyword argument `unsafe` to Mock. It raises `AttributeError` incase of an attribute startswith assert or assret. - Issue #21239: patch.stopall() didn't work deterministically when the same name was patched more than once. - Issue #21222: Passing name keyword argument to mock.create_autospec now works. - Issue #17826: setting an iterable side_effect on a mock function created by create_autospec now works. Patch by Kushal Das. - Issue #17826: setting an iterable side_effect on a mock function created by create_autospec now works. Patch by Kushal Das. - Issue #20968: unittest.mock.MagicMock now supports division. Patch by Johannes Baiter. - Issue #20189: unittest.mock now no longer assumes that any object for which it could get an inspect.Signature is a callable written in Python. Fix courtesy of Michael Foord. - Issue #17467: add readline and readlines support to mock_open in unittest.mock. - Issue #17015: When it has a spec, a Mock object now inspects its signature when matching calls, so that arguments can be matched positionally or by name. - Issue #15323: improve failure message of Mock.assert_called_once_with - Issue #14857: fix regression in references to PEP 3135 implicit __class__ closure variable (Reopens issue #12370) - Issue #14295: Add unittest.mock retry-decorator/CHANGES.txt 0000644 00000001621 15025013454 0011501 0 ustar 00 Change-log for retry_decorator. This file will be added to as part of each release ---- Version 1.0.0, Fri 22 Nov 2013 =============================== 9254d78ccc Using seed for pypi deployment; modified to use logger rather fptr (Patrick) Version 0.1.3, Fri 22 Nov 2013 =============================== 51b175d979 Version bump to 0.1.2 and updating CHANGES.txt (Patrick) d5ab8970f7 add manifest (Patrick) Version 0.1.2 (first version), Fri 22 Nov 2013 =============================================== Version 0.1.2, Fri 22 Nov 2013 =============================== 3c01cdcd38 removed manifest (Patrick) Version 0.1.1, Fri 22 Nov 2013 =============================== 09d0b03f13 added setup.py (Patrick) 7a4fe23aa9 license and change.txt (Patrick) b985477f21 - add Makefile to generate versioned tarball - add setup.py to help with installation (Robert Schweikert) 5803da54f2 new readme (Patrick) retry-decorator/.gitignore 0000644 00000000475 15025013454 0011666 0 ustar 00 *.py[cod] *.swp *~ venv # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject retry-decorator/setup.py 0000644 00000001115 15025013454 0011400 0 ustar 00 #!/usr/bin/env python from os.path import exists from setuptools import setup, find_packages from retry_decorator import __version__ setup( name='retry_decorator', version=__version__, author='Patrick Ng', author_email='pn.appdev@gmail.com', scripts=[], url='https://github.com/pnpnpn/retry-decorator', license='MIT', packages=find_packages(), description='Retry Decorator', long_description=open('README.rst').read() if exists("README.rst") else "", install_requires=[ ], ) retry-decorator/README.rst 0000644 00000000722 15025013454 0011360 0 ustar 00 Usage ----- Retry decorator :: #!/usr/bin/env python from __future__ import print_function from retry_decorator import * @retry(Exception, tries = 3, timeout_secs = 0.1) def test_retry(): import sys print('hello', file = sys.stderr) raise Exception('Testing retry') if __name__ == '__main__': try: test_retry() except Exception as e: print('Received the last exception') retry-decorator/MANIFEST.in 0000644 00000000065 15025013454 0011427 0 ustar 00 include *.txt include *.rst recursive-include docs * retry-decorator/retry_decorator/__pycache__/retry_decorator.cpython-39.pyc 0000644 00000002330 15025013454 0023145 0 ustar 00 a (�R� � @ sB d dl mZ d dlZd dlZd dlZd dlZd dlZddd�ZdS )� )�print_functionN� � �?c s � ���fdd�}|S )zL Retry calling the decorated function using an exponential backoff. c s �� ���fdd�}|S )Nc s� �� }}|dkr�z�| i |��W S � y� } zh|d }t �|| || �}d| }�d u rjt�|� n ��|� t�|� |d8 }|d9 }W Y d }~q d }~0 0 q �| i |��S )N� g�������?zRetrying in %.2f seconds ...� )�random�uniform�logging� exception�time�sleep)�args�kwargsZmtriesZmdelay�eZ half_interval�actual_delay�msg)�ExceptionToCheck�f�logger�timeout_secs�tries� �J/opt/gsutil/third_party/retry-decorator/retry_decorator/retry_decorator.py�f_retry s z*retry.<locals>.deco_retry.<locals>.f_retryr )r r �r r r r )r r � deco_retry s zretry.<locals>.deco_retryr )r r r r r r r r �retry s r )r r N)� __future__r � tracebackr r r �sysr r r r r �<module> s retry-decorator/retry_decorator/__pycache__/__init__.cpython-39.pyc 0000644 00000000356 15025013454 0021503 0 ustar 00 a (�Rm � @ s d dl T dZdZdS )� )�*�retry_decoratorz1.0.0N)r � __title__�__version__� r r �C/opt/gsutil/third_party/retry-decorator/retry_decorator/__init__.py�<module> s retry-decorator/retry_decorator/retry_decorator.py 0000644 00000002246 15025013454 0016664 0 ustar 00 #!/usr/bin/env python from __future__ import print_function import traceback import logging import time import random import sys def retry(ExceptionToCheck, tries=10, timeout_secs=1.0, logger=None): """ Retry calling the decorated function using an exponential backoff. """ def deco_retry(f): def f_retry(*args, **kwargs): mtries, mdelay = tries, timeout_secs while mtries > 1: try: return f(*args, **kwargs) except ExceptionToCheck as e: #traceback.print_exc() half_interval = mdelay * 0.10 #interval size actual_delay = random.uniform(mdelay - half_interval, mdelay + half_interval) msg = "Retrying in %.2f seconds ..." % actual_delay if logger is None: logging.exception(msg) else: logger.exception(msg) time.sleep(actual_delay) mtries -= 1 mdelay *= 2 return f(*args, **kwargs) return f_retry # true decorator return deco_retry retry-decorator/retry_decorator/__init__.py 0000644 00000000155 15025013454 0015211 0 ustar 00 # -*- coding: utf-8 -*- from .retry_decorator import * __title__ = 'retry_decorator' __version__ = "1.0.0" retry-decorator/Makefile 0000644 00000000360 15025013454 0011327 0 ustar 00 name = $(shell grep title retry_decorator/__init__.py | cut -d "'" -f2) ver = $(shell grep version retry_decorator/__init__.py| cut -d "'" -f2 ) tar: git archive --prefix="$(name)-$(ver)/" master | bzip2 --best > "$(name)-$(ver).tar.bz2" retry-decorator/LICENSE.txt 0000644 00000002026 15025013454 0011513 0 ustar 00 Copyright (c) 2012 PN Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. urllib3/.github/PULL_REQUEST_TEMPLATE/release.md 0000644 00000002230 15025013454 0014675 0 ustar 00 * [ ] See if all tests, including downstream, pass * [ ] Get the release pull request approved by a [CODEOWNER](https://github.com/urllib3/urllib3/blob/main/.github/CODEOWNERS) * [ ] Squash merge the release pull request with message "`Release <VERSION>`" * [ ] Tag with X.Y.Z, push tag on urllib3/urllib3 (not on your fork, update `<REMOTE>` accordingly) * Notice that the `<VERSION>` shouldn't have a `v` prefix (Use `1.26.6` instead of `v.1.26.6`) * ``` # Ensure the release commit is the latest in the main branch. git checkout main git pull origin main git tag -s -a '<VERSION>' -m 'Release: <VERSION>' git push <REMOTE> --tags ``` * [ ] Execute the `publish` GitHub workflow. This requires a review from a maintainer. * [ ] Ensure that all expected artifacts are added to the new GitHub release. Should be one `.whl`, one `.tar.gz`, and one `multiple.intoto.jsonl`. Update the GitHub release to have the content of the release's changelog. * [ ] Announce on: * [ ] Twitter * [ ] Discord * [ ] OpenCollective * [ ] Update Tidelift metadata * [ ] If this was a 1.26.x release, add changelog to the `main` branch urllib3/.github/dependabot.yml 0000644 00000000624 15025013454 0012311 0 ustar 00 version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" labels: - "dependencies" - "github_actions" - "Skip Changelog" ignore: # Ignore all patch releases as we can manually # upgrade if we run into a bug and need a fix. - dependency-name: "*" update-types: ["version-update:semver-patch"] urllib3/.github/codeql.yml 0000644 00000000021 15025013454 0011442 0 ustar 00 paths: - "src/" urllib3/.github/ISSUE_TEMPLATE/02_bug_report.md 0000644 00000001145 15025013454 0014636 0 ustar 00 --- name: 🐞 Bug Report about: Something is broken --- ### Subject Describe the issue here. ### Environment Describe your environment. At least, paste here the output of: ```python import platform import ssl import urllib3 print("OS", platform.platform()) print("Python", platform.python_version()) print(ssl.OPENSSL_VERSION) print("urllib3", urllib3.__version__) ``` ### Steps to Reproduce A simple and isolated way to reproduce the issue. A code snippet would be great. ### Expected Behavior What should happen. ### Actual Behavior What happens instead. You may attach logs, packet captures, etc. urllib3/.github/ISSUE_TEMPLATE/config.yml 0000644 00000000643 15025013454 0013635 0 ustar 00 blank_issues_enabled: false contact_links: - name: 📚 Documentation url: https://urllib3.readthedocs.io about: Make sure you read the relevant docs - name: ❓ Ask on StackOverflow url: https://stackoverflow.com/questions/tagged/urllib3 about: Ask questions about usage in StackOverflow - name: 💬 Ask the Community url: https://discord.gg/CHEgCZN about: Join urllib3's Discord server urllib3/.github/ISSUE_TEMPLATE/01_feature_request.md 0000644 00000001052 15025013454 0015665 0 ustar 00 --- name: 🎁 Feature Request about: Suggest a new feature --- ### Context What are you trying to do? How do you expect to do it? Is it something you currently cannot do? Is this related to an existing issue/problem? ### Alternatives Can you achieve the same result doing it in an alternative way? Is the alternative considerable? ### Duplicate Has the feature been requested before? If so, please provide a link to the issue. ### Contribution Would you be willing to submit a PR? _(Help can be provided if you need assistance submitting a PR)_ urllib3/.github/PULL_REQUEST_TEMPLATE.md 0000644 00000000431 15025013454 0013256 0 ustar 00 <!--- Thanks for your contribution! ♥️ If this is your first PR to urllib3 please review the Contributing Guide: https://urllib3.readthedocs.io/en/latest/contributing.html Adhering to the Contributing Guide means we can review, merge, and release your change faster! :) ---> urllib3/.github/CODEOWNERS 0000644 00000000646 15025013454 0011060 0 ustar 00 # Restrict all files related to deploying to # require lead maintainer approval. .github/workflows/ @sethmlarson @pquentin @shazow @illia-v .github/CODEOWNERS @sethmlarson @pquentin @shazow @illia-v src/urllib3/_version.py @sethmlarson @pquentin @shazow @illia-v pyproject.toml @sethmlarson @pquentin @shazow @illia-v ci/ @sethmlarson @pquentin @shazow @illia-v urllib3/.github/FUNDING.yml 0000644 00000000100 15025013454 0011263 0 ustar 00 tidelift: pypi/urllib3 github: urllib3 open_collective: urllib3 urllib3/.github/SECURITY.md 0000644 00000000311 15025013454 0011243 0 ustar 00 # Security Disclosures To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure with maintainers. urllib3/.github/workflows/downstream.yml 0000644 00000001321 15025013454 0014417 0 ustar 00 name: Downstream on: [push, pull_request, workflow_dispatch] permissions: "read-all" jobs: downstream: strategy: fail-fast: false matrix: downstream: [botocore, requests] runs-on: ubuntu-22.04 timeout-minutes: 30 steps: - name: "Checkout repository" uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: "Setup Python" uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: "3.x" - name: "Install dependencies" run: python -m pip install --upgrade nox - name: "Run downstream tests" run: nox -s downstream_${{ matrix.downstream }} urllib3/.github/workflows/lint.yml 0000644 00000001273 15025013454 0013210 0 ustar 00 name: lint on: [push, pull_request, workflow_dispatch] permissions: "read-all" jobs: lint: runs-on: ubuntu-20.04 timeout-minutes: 10 steps: - name: "Checkout repository" uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: "Setup Python" uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: "3.x" cache: pip - name: "Run pre-commit" uses: pre-commit/action@646c83fcd040023954eafda54b4db0192ce70507 # v3.0.0 - name: "Install dependencies" run: python -m pip install nox - name: "Run mypy" run: nox -s mypy urllib3/.github/workflows/publish.yml 0000644 00000004522 15025013454 0013710 0 ustar 00 name: Publish to PyPI on: push: tags: - "*" permissions: contents: read jobs: build: name: "Build dists" runs-on: "ubuntu-latest" environment: name: "publish" outputs: hashes: ${{ steps.hash.outputs.hashes }} steps: - name: "Checkout repository" uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: "Setup Python" uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: "3.x" - name: "Install dependencies" run: python -m pip install build==0.8.0 - name: "Build dists" run: | SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) \ python -m build - name: "Generate hashes" id: hash run: | cd dist && echo "hashes=$(sha256sum * | base64 -w0)" >> $GITHUB_OUTPUT - name: "Upload dists" uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: "dist" path: "dist/" if-no-files-found: error retention-days: 5 provenance: needs: [build] permissions: actions: read contents: write id-token: write # Needed to access the workflow's OIDC identity. uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0 with: base64-subjects: "${{ needs.build.outputs.hashes }}" upload-assets: true compile-generator: true # Workaround for https://github.com/slsa-framework/slsa-github-generator/issues/1163 publish: name: "Publish" if: startsWith(github.ref, 'refs/tags/') needs: ["build", "provenance"] permissions: contents: write id-token: write # Needed for trusted publishing to PyPI. runs-on: "ubuntu-latest" steps: - name: "Download dists" uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: "dist" path: "dist/" - name: "Upload dists to GitHub Release" env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" run: | gh release upload ${{ github.ref_name }} dist/* --repo ${{ github.repository }} - name: "Publish dists to PyPI" uses: pypa/gh-action-pypi-publish@f8c70e705ffc13c3b4d1221169b84f12a75d6ca8 # v1.8.8 urllib3/.github/workflows/codeql.yml 0000644 00000001673 15025013454 0013515 0 ustar 00 name: "CodeQL" on: push: branches: ["main"] pull_request: branches: ["main"] schedule: - cron: "0 0 * * 5" workflow_dispatch: permissions: "read-all" jobs: analyze: if: github.repository_owner == 'urllib3' name: "Analyze" runs-on: "ubuntu-latest" permissions: actions: read contents: read security-events: write steps: - name: "Checkout repository" uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: "Run CodeQL init" uses: github/codeql-action/init@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6 with: config-file: "./.github/codeql.yml" languages: "python" - name: "Run CodeQL autobuild" uses: github/codeql-action/autobuild@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6 - name: "Run CodeQL analyze" uses: github/codeql-action/analyze@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6 urllib3/.github/workflows/ci.yml 0000644 00000013631 15025013454 0012636 0 ustar 00 name: CI on: [push, pull_request, workflow_dispatch] permissions: "read-all" defaults: run: shell: bash jobs: package: runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: "Checkout repository" uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: "Setup Python" uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: "3.x" cache: "pip" - name: "Check packages" run: | python -m pip install -U pip setuptools wheel build twine rstcheck python -m build rstcheck CHANGES.rst python -m twine check dist/* test: strategy: fail-fast: false matrix: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] os: - macos-12 - windows-latest - ubuntu-22.04 nox-session: [''] include: - experimental: false # integration # 3.8 and 3.9 have a known issue with large SSL requests that we work around: # https://github.com/urllib3/urllib3/pull/3181#issuecomment-1794830698 - python-version: "3.8" os: ubuntu-latest experimental: false nox-session: test_integration - python-version: "3.9" os: ubuntu-latest experimental: false nox-session: test_integration - python-version: "3.12" os: ubuntu-latest experimental: false nox-session: test_integration # OpenSSL 1.1.1 - python-version: "3.8" os: ubuntu-20.04 experimental: false nox-session: test-3.8 # pypy - python-version: "pypy-3.8" os: ubuntu-latest experimental: false nox-session: test-pypy3.8 - python-version: "pypy-3.9-v7.3.13" os: ubuntu-latest experimental: false nox-session: test-pypy3.9 - python-version: "pypy-3.10" os: ubuntu-latest experimental: false nox-session: test-pypy3.10 - python-version: "3.x" # brotli os: ubuntu-latest experimental: false nox-session: test_brotlipy # Test CPython with a broken hostname_checks_common_name (the fix is in 3.9.3) - python-version: "3.9.2" os: ubuntu-20.04 # CPython 3.9.2 is not available for ubuntu-22.04. experimental: false nox-session: test-3.9 - python-version: "3.11" os: ubuntu-latest nox-session: emscripten experimental: true - python-version: "3.13" experimental: true exclude: # Ubuntu 22.04 comes with OpenSSL 3.0, so only CPython 3.9+ is compatible with it # https://github.com/python/cpython/issues/83001 - python-version: "3.8" os: ubuntu-22.04 runs-on: ${{ matrix.os }} name: ${{ fromJson('{"macos-12":"macOS","windows-latest":"Windows","ubuntu-latest":"Ubuntu","ubuntu-20.04":"Ubuntu 20.04 (OpenSSL 1.1.1)","ubuntu-22.04":"Ubuntu 22.04 (OpenSSL 3.0)"}')[matrix.os] }} ${{ matrix.python-version }} ${{ matrix.nox-session}} continue-on-error: ${{ matrix.experimental }} timeout-minutes: 30 steps: - name: "Checkout repository" uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: "Setup Python ${{ matrix.python-version }}" uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - name: "Install dependencies" run: python -m pip install --upgrade pip setuptools nox - name: "Install Chrome" uses: browser-actions/setup-chrome@db1b524c26f20a8d1a10f7fc385c92387e2d0477 # v1.7.1 if: ${{ matrix.nox-session == 'emscripten' }} - name: "Install Firefox" uses: browser-actions/setup-firefox@233224b712fc07910ded8c15fb95a555c86da76f # v1.5.0 if: ${{ matrix.nox-session == 'emscripten' }} - name: "Run tests" # If no explicit NOX_SESSION is set, run the default tests for the chosen Python version run: nox -s ${NOX_SESSION:-test-$PYTHON_VERSION} env: PYTHON_VERSION: ${{ matrix.python-version }} NOX_SESSION: ${{ matrix.nox-session }} - name: "Upload coverage data" uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: coverage-data-${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.experimental }}-${{ matrix.nox-session }} path: ".coverage.*" if-no-files-found: error coverage: if: always() runs-on: "ubuntu-latest" needs: test steps: - name: "Checkout repository" uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: "Setup Python" uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: "3.x" - name: "Install coverage" run: "python -m pip install -r dev-requirements.txt" - name: "Download coverage data" uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 with: pattern: coverage-data-* merge-multiple: true - name: "Combine & check coverage" run: | python -m coverage combine python -m coverage html --skip-covered --skip-empty python -m coverage report --ignore-errors --show-missing --fail-under=100 - if: ${{ failure() }} name: "Upload report if check failed" uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: coverage-report path: htmlcov urllib3/.github/workflows/scorecards.yml 0000644 00000001506 15025013454 0014371 0 ustar 00 name: "Scorecard" on: branch_protection_rule: schedule: - cron: "0 0 * * 0" push: branches: ["main", "1.26.x"] permissions: read-all jobs: analysis: if: github.repository_owner == 'urllib3' name: "Scorecard" runs-on: "ubuntu-latest" permissions: security-events: write id-token: write contents: read actions: read steps: - name: "Checkout repository" uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - name: "Run Scorecard" uses: ossf/scorecard-action@483ef80eb98fb506c348f7d62e28055e49fe2398 # v2.3.0 with: results_file: results.sarif results_format: sarif repo_token: ${{ secrets.SCORECARD_TOKEN }} publish_results: true urllib3/.github/workflows/changelog.yml 0000644 00000001526 15025013454 0014172 0 ustar 00 name: Check on: pull_request: types: [labeled, unlabeled, opened, reopened, synchronize] permissions: "read-all" jobs: check-changelog-entry: name: changelog entry runs-on: ubuntu-latest steps: - name: "Checkout repository" uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: # `towncrier check` runs `git diff --name-only origin/main...`, which # needs a non-shallow clone. fetch-depth: 0 - name: "Check changelog" if: "!contains(github.event.pull_request.labels.*.name, 'Skip Changelog')" run: | if ! pipx run towncrier check --compare-with origin/${{ github.base_ref }}; then echo "Please see https://github.com/urllib3/urllib3/blob/main/changelog/README.rst for guidance." false fi urllib3/dummyserver/testcase.py 0000644 00000031667 15025013454 0012703 0 ustar 00 from __future__ import annotations import contextlib import socket import ssl import threading import typing from test import LONG_TIMEOUT import hypercorn import pytest from dummyserver.app import hypercorn_app from dummyserver.asgi_proxy import ProxyApp from dummyserver.hypercornserver import run_hypercorn_in_thread from dummyserver.socketserver import DEFAULT_CERTS, HAS_IPV6, SocketServerThread from urllib3.connection import HTTPConnection from urllib3.util.ssltransport import SSLTransport from urllib3.util.url import parse_url def consume_socket( sock: SSLTransport | socket.socket, chunks: int = 65536, quit_event: threading.Event | None = None, ) -> bytearray: consumed = bytearray() sock.settimeout(LONG_TIMEOUT) while True: if quit_event and quit_event.is_set(): break try: b = sock.recv(chunks) except (TimeoutError, socket.timeout): continue assert isinstance(b, bytes) consumed += b if b.endswith(b"\r\n\r\n"): break return consumed class SocketDummyServerTestCase: """ A simple socket-based server is created for this class that is good for exactly one request. """ scheme = "http" host = "localhost" server_thread: typing.ClassVar[SocketServerThread] port: typing.ClassVar[int] tmpdir: typing.ClassVar[str] ca_path: typing.ClassVar[str] cert_combined_path: typing.ClassVar[str] cert_path: typing.ClassVar[str] key_path: typing.ClassVar[str] password_key_path: typing.ClassVar[str] server_context: typing.ClassVar[ssl.SSLContext] client_context: typing.ClassVar[ssl.SSLContext] proxy_server: typing.ClassVar[SocketDummyServerTestCase] @classmethod def _start_server( cls, socket_handler: typing.Callable[[socket.socket], None], quit_event: threading.Event | None = None, ) -> None: ready_event = threading.Event() cls.server_thread = SocketServerThread( socket_handler=socket_handler, ready_event=ready_event, host=cls.host, quit_event=quit_event, ) cls.server_thread.start() ready_event.wait(5) if not ready_event.is_set(): raise Exception("most likely failed to start server") cls.port = cls.server_thread.port @classmethod def start_response_handler( cls, response: bytes, num: int = 1, block_send: threading.Event | None = None, ) -> threading.Event: ready_event = threading.Event() quit_event = threading.Event() def socket_handler(listener: socket.socket) -> None: for _ in range(num): ready_event.set() listener.settimeout(LONG_TIMEOUT) while True: if quit_event.is_set(): return try: sock = listener.accept()[0] break except (TimeoutError, socket.timeout): continue consume_socket(sock, quit_event=quit_event) if quit_event.is_set(): sock.close() return if block_send: while not block_send.wait(LONG_TIMEOUT): if quit_event.is_set(): sock.close() return block_send.clear() sock.send(response) sock.close() cls._start_server(socket_handler, quit_event=quit_event) return ready_event @classmethod def start_basic_handler( cls, num: int = 1, block_send: threading.Event | None = None ) -> threading.Event: return cls.start_response_handler( b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", num, block_send, ) @staticmethod def quit_server_thread(server_thread: SocketServerThread) -> None: if server_thread.quit_event: server_thread.quit_event.set() # in principle the maximum time that the thread can take to notice # the quit_event is LONG_TIMEOUT and the thread should terminate # shortly after that, we give 5 seconds leeway just in case server_thread.join(LONG_TIMEOUT * 2 + 5.0) if server_thread.is_alive(): raise Exception("server_thread did not exit") @classmethod def teardown_class(cls) -> None: if hasattr(cls, "server_thread"): cls.quit_server_thread(cls.server_thread) def teardown_method(self) -> None: if hasattr(self, "server_thread"): self.quit_server_thread(self.server_thread) def assert_header_received( self, received_headers: typing.Iterable[bytes], header_name: str, expected_value: str | None = None, ) -> None: header_name_bytes = header_name.encode("ascii") if expected_value is None: expected_value_bytes = None else: expected_value_bytes = expected_value.encode("ascii") header_titles = [] for header in received_headers: key, value = header.split(b": ") header_titles.append(key) if key == header_name_bytes and expected_value_bytes is not None: assert value == expected_value_bytes assert header_name_bytes in header_titles class IPV4SocketDummyServerTestCase(SocketDummyServerTestCase): @classmethod def _start_server( cls, socket_handler: typing.Callable[[socket.socket], None], quit_event: threading.Event | None = None, ) -> None: ready_event = threading.Event() cls.server_thread = SocketServerThread( socket_handler=socket_handler, ready_event=ready_event, host=cls.host, quit_event=quit_event, ) cls.server_thread.USE_IPV6 = False cls.server_thread.start() ready_event.wait(5) if not ready_event.is_set(): raise Exception("most likely failed to start server") cls.port = cls.server_thread.port class HypercornDummyServerTestCase: host = "localhost" host_alt = "127.0.0.1" port: typing.ClassVar[int] base_url: typing.ClassVar[str] base_url_alt: typing.ClassVar[str] certs: typing.ClassVar[dict[str, typing.Any]] = {} _stack: typing.ClassVar[contextlib.ExitStack] @classmethod def setup_class(cls) -> None: with contextlib.ExitStack() as stack: config = hypercorn.Config() if cls.certs: config.certfile = cls.certs["certfile"] config.keyfile = cls.certs["keyfile"] config.verify_mode = cls.certs["cert_reqs"] config.ca_certs = cls.certs["ca_certs"] config.alpn_protocols = cls.certs["alpn_protocols"] config.bind = [f"{cls.host}:0"] stack.enter_context(run_hypercorn_in_thread(config, hypercorn_app)) cls._stack = stack.pop_all() cls.port = typing.cast(int, parse_url(config.bind[0]).port) @classmethod def teardown_class(cls) -> None: cls._stack.close() class HTTPSHypercornDummyServerTestCase(HypercornDummyServerTestCase): scheme = "https" host = "localhost" certs = DEFAULT_CERTS certs_dir = "" bad_ca_path = "" class HypercornDummyProxyTestCase: http_host: typing.ClassVar[str] = "localhost" http_host_alt: typing.ClassVar[str] = "127.0.0.1" http_port: typing.ClassVar[int] http_url: typing.ClassVar[str] http_url_alt: typing.ClassVar[str] https_host: typing.ClassVar[str] = "localhost" https_host_alt: typing.ClassVar[str] = "127.0.0.1" https_certs: typing.ClassVar[dict[str, typing.Any]] = DEFAULT_CERTS https_port: typing.ClassVar[int] https_url: typing.ClassVar[str] https_url_alt: typing.ClassVar[str] https_url_fqdn: typing.ClassVar[str] proxy_host: typing.ClassVar[str] = "localhost" proxy_host_alt: typing.ClassVar[str] = "127.0.0.1" proxy_port: typing.ClassVar[int] proxy_url: typing.ClassVar[str] https_proxy_port: typing.ClassVar[int] https_proxy_url: typing.ClassVar[str] certs_dir: typing.ClassVar[str] = "" bad_ca_path: typing.ClassVar[str] = "" server_thread: typing.ClassVar[threading.Thread] _stack: typing.ClassVar[contextlib.ExitStack] @classmethod def setup_class(cls) -> None: with contextlib.ExitStack() as stack: http_server_config = hypercorn.Config() http_server_config.bind = [f"{cls.http_host}:0"] stack.enter_context( run_hypercorn_in_thread(http_server_config, hypercorn_app) ) cls.http_port = typing.cast(int, parse_url(http_server_config.bind[0]).port) https_server_config = hypercorn.Config() https_server_config.certfile = cls.https_certs["certfile"] https_server_config.keyfile = cls.https_certs["keyfile"] https_server_config.verify_mode = cls.https_certs["cert_reqs"] https_server_config.ca_certs = cls.https_certs["ca_certs"] https_server_config.alpn_protocols = cls.https_certs["alpn_protocols"] https_server_config.bind = [f"{cls.https_host}:0"] stack.enter_context( run_hypercorn_in_thread(https_server_config, hypercorn_app) ) cls.https_port = typing.cast( int, parse_url(https_server_config.bind[0]).port ) http_proxy_config = hypercorn.Config() http_proxy_config.bind = [f"{cls.proxy_host}:0"] stack.enter_context(run_hypercorn_in_thread(http_proxy_config, ProxyApp())) cls.proxy_port = typing.cast(int, parse_url(http_proxy_config.bind[0]).port) https_proxy_config = hypercorn.Config() https_proxy_config.certfile = cls.https_certs["certfile"] https_proxy_config.keyfile = cls.https_certs["keyfile"] https_proxy_config.verify_mode = cls.https_certs["cert_reqs"] https_proxy_config.ca_certs = cls.https_certs["ca_certs"] https_proxy_config.alpn_protocols = cls.https_certs["alpn_protocols"] https_proxy_config.bind = [f"{cls.proxy_host}:0"] upstream_ca_certs = cls.https_certs.get("ca_certs") stack.enter_context( run_hypercorn_in_thread(https_proxy_config, ProxyApp(upstream_ca_certs)) ) cls.https_proxy_port = typing.cast( int, parse_url(https_proxy_config.bind[0]).port ) cls._stack = stack.pop_all() @classmethod def teardown_class(cls) -> None: cls._stack.close() @pytest.mark.skipif(not HAS_IPV6, reason="IPv6 not available") class IPv6HypercornDummyServerTestCase(HypercornDummyServerTestCase): host = "::1" @pytest.mark.skipif(not HAS_IPV6, reason="IPv6 not available") class IPv6HypercornDummyProxyTestCase(HypercornDummyProxyTestCase): http_host = "localhost" http_host_alt = "127.0.0.1" https_host = "localhost" https_host_alt = "127.0.0.1" https_certs = DEFAULT_CERTS proxy_host = "::1" proxy_host_alt = "127.0.0.1" class ConnectionMarker: """ Marks an HTTP(S)Connection's socket after a request was made. Helps a test server understand when a client finished a request, without implementing a complete HTTP server. """ MARK_FORMAT = b"$#MARK%04x*!" @classmethod @contextlib.contextmanager def mark( cls, monkeypatch: pytest.MonkeyPatch ) -> typing.Generator[None, None, None]: """ Mark connections under in that context. """ orig_request = HTTPConnection.request def call_and_mark( target: typing.Callable[..., None] ) -> typing.Callable[..., None]: def part( self: HTTPConnection, *args: typing.Any, **kwargs: typing.Any ) -> None: target(self, *args, **kwargs) self.sock.sendall(cls._get_socket_mark(self.sock, False)) return part with monkeypatch.context() as m: m.setattr(HTTPConnection, "request", call_and_mark(orig_request)) yield @classmethod def consume_request(cls, sock: socket.socket, chunks: int = 65536) -> bytearray: """ Consume a socket until after the HTTP request is sent. """ consumed = bytearray() mark = cls._get_socket_mark(sock, True) while True: b = sock.recv(chunks) if not b: break consumed += b if consumed.endswith(mark): break return consumed @classmethod def _get_socket_mark(cls, sock: socket.socket, server: bool) -> bytes: if server: port = sock.getpeername()[1] else: port = sock.getsockname()[1] return cls.MARK_FORMAT % (port,) urllib3/dummyserver/asgi_proxy.py 0000644 00000006765 15025013454 0013255 0 ustar 00 from __future__ import annotations import typing import httpx import trio from hypercorn.typing import ( ASGIReceiveCallable, ASGISendCallable, HTTPResponseBodyEvent, HTTPResponseStartEvent, HTTPScope, Scope, ) async def _read_body(receive: ASGIReceiveCallable) -> bytes: body = bytearray() body_consumed = False while not body_consumed: event = await receive() if event["type"] == "http.request": body.extend(event["body"]) body_consumed = not event["more_body"] else: raise ValueError(event["type"]) return bytes(body) class ProxyApp: def __init__(self, upstream_ca_certs: str | None = None): self.upstream_ca_certs = upstream_ca_certs async def __call__( self, scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable ) -> None: assert scope["type"] == "http" if scope["method"] in ["GET", "POST"]: await self.absolute_uri(scope, receive, send) elif scope["method"] == "CONNECT": await self.connect(scope, send) else: raise ValueError(scope["method"]) async def absolute_uri( self, scope: HTTPScope, receive: ASGIReceiveCallable, send: ASGISendCallable, ) -> None: async with httpx.AsyncClient(verify=self.upstream_ca_certs or True) as client: client_response = await client.request( method=scope["method"], url=scope["path"], headers=list(scope["headers"]), content=await _read_body(receive), ) headers = [] for header in ( "Date", "Cache-Control", "Server", "Content-Type", "Location", ): v = client_response.headers.get(header) if v: headers.append((header.encode(), v.encode())) headers.append((b"Content-Length", str(len(client_response.content)).encode())) await send( HTTPResponseStartEvent( type="http.response.start", status=client_response.status_code, headers=headers, ) ) await send( HTTPResponseBodyEvent( type="http.response.body", body=client_response.content, more_body=False, ) ) async def connect(self, scope: HTTPScope, send: ASGISendCallable) -> None: async def start_forward( reader: trio.SocketStream, writer: trio.SocketStream ) -> None: while True: try: data = await reader.receive_some(4096) except trio.ClosedResourceError: break if not data: break await writer.send_all(data) await writer.aclose() host, port = scope["path"].split(":") async with await trio.open_tcp_stream(host, int(port)) as upstream: await send({"type": "http.response.start", "status": 200, "headers": []}) await send({"type": "http.response.body", "body": b"", "more_body": True}) client = typing.cast(trio.SocketStream, scope["extensions"]["_transport"]) async with trio.open_nursery(strict_exception_groups=True) as nursery: nursery.start_soon(start_forward, client, upstream) nursery.start_soon(start_forward, upstream, client) urllib3/dummyserver/hypercornserver.py 0000644 00000004334 15025013454 0014317 0 ustar 00 from __future__ import annotations import concurrent.futures import contextlib import functools import sys import threading from typing import Generator import hypercorn import hypercorn.trio import hypercorn.typing import trio from quart_trio import QuartTrio # https://github.com/pgjones/hypercorn/blob/19dfb96411575a6a647cdea63fa581b48ebb9180/src/hypercorn/utils.py#L172-L178 async def graceful_shutdown(shutdown_event: threading.Event) -> None: while True: if shutdown_event.is_set(): return await trio.sleep(0.1) async def _start_server( config: hypercorn.Config, app: QuartTrio, ready_event: threading.Event, shutdown_event: threading.Event, ) -> None: async with trio.open_nursery() as nursery: config.bind = await nursery.start( functools.partial( hypercorn.trio.serve, app, config, shutdown_trigger=functools.partial(graceful_shutdown, shutdown_event), ) ) ready_event.set() @contextlib.contextmanager def run_hypercorn_in_thread( config: hypercorn.Config, app: hypercorn.typing.ASGIFramework ) -> Generator[None, None, None]: ready_event = threading.Event() shutdown_event = threading.Event() with concurrent.futures.ThreadPoolExecutor( 1, thread_name_prefix="hypercorn dummyserver" ) as executor: future = executor.submit( trio.run, _start_server, config, app, ready_event, shutdown_event, ) ready_event.wait(5) if not ready_event.is_set(): raise Exception("most likely failed to start server") try: yield finally: shutdown_event.set() future.result() def main() -> int: # For debugging dummyserver itself - PYTHONPATH=src python -m dummyserver.hypercornserver from .app import hypercorn_app config = hypercorn.Config() config.bind = ["localhost:0"] ready_event = threading.Event() shutdown_event = threading.Event() trio.run(_start_server, config, hypercorn_app, ready_event, shutdown_event) return 0 if __name__ == "__main__": sys.exit(main()) urllib3/dummyserver/socketserver.py 0000644 00000013027 15025013454 0013575 0 ustar 00 #!/usr/bin/env python """ Dummy server used for unit testing. """ from __future__ import annotations import logging import os import socket import ssl import sys import threading import typing import warnings import trustme from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from urllib3.exceptions import HTTPWarning from urllib3.util import resolve_cert_reqs, resolve_ssl_version if typing.TYPE_CHECKING: from typing_extensions import ParamSpec P = ParamSpec("P") log = logging.getLogger(__name__) CERTS_PATH = os.path.join(os.path.dirname(__file__), "certs") DEFAULT_CERTS: dict[str, typing.Any] = { "certfile": os.path.join(CERTS_PATH, "server.crt"), "keyfile": os.path.join(CERTS_PATH, "server.key"), "cert_reqs": ssl.CERT_OPTIONAL, "ca_certs": os.path.join(CERTS_PATH, "cacert.pem"), "alpn_protocols": ["h2", "http/1.1"], } DEFAULT_CA = os.path.join(CERTS_PATH, "cacert.pem") DEFAULT_CA_KEY = os.path.join(CERTS_PATH, "cacert.key") def _resolves_to_ipv6(host: str) -> bool: """Returns True if the system resolves host to an IPv6 address by default.""" resolves_to_ipv6 = False try: for res in socket.getaddrinfo(host, None, socket.AF_UNSPEC): af, _, _, _, _ = res if af == socket.AF_INET6: resolves_to_ipv6 = True except socket.gaierror: pass return resolves_to_ipv6 def _has_ipv6(host: str) -> bool: """Returns True if the system can bind an IPv6 address.""" sock = None has_ipv6 = False if socket.has_ipv6: # has_ipv6 returns true if cPython was compiled with IPv6 support. # It does not tell us if the system has IPv6 support enabled. To # determine that we must bind to an IPv6 address. # https://github.com/urllib3/urllib3/pull/611 # https://bugs.python.org/issue658327 try: sock = socket.socket(socket.AF_INET6) sock.bind((host, 0)) has_ipv6 = _resolves_to_ipv6("localhost") except Exception: pass if sock: sock.close() return has_ipv6 # Some systems may have IPv6 support but DNS may not be configured # properly. We can not count that localhost will resolve to ::1 on all # systems. See https://github.com/urllib3/urllib3/pull/611 and # https://bugs.python.org/issue18792 HAS_IPV6_AND_DNS = _has_ipv6("localhost") HAS_IPV6 = _has_ipv6("::1") # Different types of servers we have: class NoIPv6Warning(HTTPWarning): "IPv6 is not available" class SocketServerThread(threading.Thread): """ :param socket_handler: Callable which receives a socket argument for one request. :param ready_event: Event which gets set when the socket handler is ready to receive requests. """ USE_IPV6 = HAS_IPV6_AND_DNS def __init__( self, socket_handler: typing.Callable[[socket.socket], None], host: str = "localhost", ready_event: threading.Event | None = None, quit_event: threading.Event | None = None, ) -> None: super().__init__() self.daemon = True self.socket_handler = socket_handler self.host = host self.ready_event = ready_event self.quit_event = quit_event def _start_server(self) -> None: if self.USE_IPV6: sock = socket.socket(socket.AF_INET6) else: warnings.warn("No IPv6 support. Falling back to IPv4.", NoIPv6Warning) sock = socket.socket(socket.AF_INET) if sys.platform != "win32": sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) with sock: sock.bind((self.host, 0)) self.port = sock.getsockname()[1] # Once listen() returns, the server socket is ready sock.listen(1) if self.ready_event: self.ready_event.set() self.socket_handler(sock) def run(self) -> None: self._start_server() def ssl_options_to_context( # type: ignore[no-untyped-def] keyfile=None, certfile=None, server_side=None, cert_reqs=None, ssl_version: str | int | None = None, ca_certs=None, do_handshake_on_connect=None, suppress_ragged_eofs=None, ciphers=None, alpn_protocols=None, ) -> ssl.SSLContext: """Return an equivalent SSLContext based on ssl.wrap_socket args.""" ssl_version = resolve_ssl_version(ssl_version) cert_none = resolve_cert_reqs("CERT_NONE") if cert_reqs is None: cert_reqs = cert_none else: cert_reqs = resolve_cert_reqs(cert_reqs) ctx = ssl.SSLContext(ssl_version) ctx.load_cert_chain(certfile, keyfile) ctx.verify_mode = cert_reqs if ctx.verify_mode != cert_none: ctx.load_verify_locations(cafile=ca_certs) if alpn_protocols and hasattr(ctx, "set_alpn_protocols"): try: ctx.set_alpn_protocols(alpn_protocols) except NotImplementedError: pass return ctx def get_unreachable_address() -> tuple[str, int]: # reserved as per rfc2606 return ("something.invalid", 54321) def encrypt_key_pem(private_key_pem: trustme.Blob, password: bytes) -> trustme.Blob: private_key = serialization.load_pem_private_key( private_key_pem.bytes(), password=None, backend=default_backend() ) encrypted_key = private_key.private_bytes( serialization.Encoding.PEM, serialization.PrivateFormat.TraditionalOpenSSL, serialization.BestAvailableEncryption(password), ) return trustme.Blob(encrypted_key) urllib3/dummyserver/certs/server.crt 0000644 00000002361 15025013454 0013643 0 ustar 00 -----BEGIN CERTIFICATE----- MIIDeTCCAmGgAwIBAgIUQeadxkH6YMoSecB2rNbFEr1u1kUwDQYJKoZIhvcNAQEL BQAwQDEXMBUGA1UECgwOdHJ1c3RtZSB2MC41LjMxJTAjBgNVBAsMHFRlc3Rpbmcg Q0EgIzdEUWJWOTBzV2xZSEstY0wwHhcNMDAwMTAxMDAwMDAwWhcNMzgwMTAxMDAw MDAwWjBCMRcwFQYDVQQKDA50cnVzdG1lIHYwLjUuMzEnMCUGA1UECwweVGVzdGlu ZyBjZXJ0ICMtSGRsMnMyTEYyeVp0NDFOMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEArRLZX+5dyCh4N7q90sH2Q4Ea6QLK8OfoUQPWtpzAtINDUAdfSXCC /qYTtGeSCGjB4W0LfvRTI8afHoD/M+YpaCRnx7T1sy1taA2rnGrEVXEHalVP+RI4 t4ZWtX56aez2M0Fs6o4MtzAuP6fKgSdWzIvOmtCxqn0Zf2KbfEHnQylsy2LgPa/x Lg50fbZ195+h4EAB3d2/jqaeFGGhN+7zrrv4+L1eeW3bzOkvPEkTNepq3Gy/8r5e 0i2icEnM+eBfl8NYgQ1toJYvDIy5Qi2TRzaFxBVmqUOc8EFtHpL7E9YLbTTW15xd oLVLdXI5igGxkwPYoeiiAJWxIsC/hL1RRQIDAQABo2kwZzAdBgNVHQ4EFgQUMU6+ uwNmL8TxLwjrj7jzzlwDPiowDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBR1n9/R 563QoSo7DgaARp9VPtFYMTAXBgNVHREBAf8EDTALgglsb2NhbGhvc3QwDQYJKoZI hvcNAQELBQADggEBAJ6w5neQKw+/efA/I3IHzt8GaSHQ/YehMHx8GxCViJUmLg6P Vf74k856Knvh7IsVaqF1uRi6qQaFPik6CwtBCj7/ZftdseOCDljd+8EWyQ+ZWie7 +tzMIdQWZxYSdR9Ov42VD++a6oWJtfJhWV5eyDit99FFK31/M1ZXoceiDS5AsIG6 wfsxrFj1qV9pLNSIlfrnycYhYx7avVJTf+2mfZgTO9Tx+VPapkZrfCnP/2jpN39u zblFFjP9Ir0QqBw7MXjVX+Y1HkQ2TQnEeSsp1HuFRIZYx72Cttnckv1Lxcx/HiQB oebTDYiRfxOAEeIMgIhX88Jca8vNIRcXDeGK9mU= -----END CERTIFICATE----- urllib3/dummyserver/certs/README.rst 0000644 00000000777 15025013454 0013323 0 ustar 00 Generating new certificates --------------------------- Here's how you can regenerate the certificates:: import trustme ca = trustme.CA() server_cert = ca.issue_cert("localhost") ca.cert_pem.write_to_path("cacert.pem") ca.private_key_pem.write_to_path("cacert.key") server_cert.cert_chain_pems[0].write_to_path("server.crt") server_cert.private_key_pem.write_to_path("server.key") This will break a number of tests: you will need to update the relevant fingerprints and hashes. urllib3/dummyserver/certs/cacert.pem 0000644 00000002371 15025013454 0013570 0 ustar 00 -----BEGIN CERTIFICATE----- MIIDfzCCAmegAwIBAgIUVGEi+7bkaGRIPoQrp9zFFAT5JYQwDQYJKoZIhvcNAQEL BQAwQDEXMBUGA1UECgwOdHJ1c3RtZSB2MC41LjMxJTAjBgNVBAsMHFRlc3Rpbmcg Q0EgIzdEUWJWOTBzV2xZSEstY0wwHhcNMDAwMTAxMDAwMDAwWhcNMzgwMTAxMDAw MDAwWjBAMRcwFQYDVQQKDA50cnVzdG1lIHYwLjUuMzElMCMGA1UECwwcVGVzdGlu ZyBDQSAjN0RRYlY5MHNXbFlISy1jTDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBAJ999DRCEmO8PhnmUdo4fDn9cdgraXp1kSrrg9ruBTmjdBtUjaZujsLp usc4b/d45o/rWa6fZGLwvR3Lxq4Mx/cj+euqSy32NkhBACWDTXlXanoGOfVtcGur xAzeVGK7lRG8poDx6CLYmIX53YBA22oDzeXYa3Tb2kTSNnOczJgQ5c84QhuwGW17 rYU6ejsE1cnu9X9pZzYSouq81ra3v44vPH6vEJuNOpdvTtUU85jbbTeQWZz1lPb+ BpwmCl3WOgTdVG+s/AvYZ2v6EYAamtGW7+5BLgLqobBUHrhQnwYUJuzXT3f+yoei 0vlSIgrtwDnHGRzrhTo7R2/ysp3ZoAsCAwEAAaNxMG8wHQYDVR0OBBYEFHWf39Hn rdChKjsOBoBGn1U+0VgxMBIGA1UdEwEB/wQIMAYBAf8CAQkwDgYDVR0PAQH/BAQD AgEGMCoGA1UdJQEB/wQgMB4GCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMw DQYJKoZIhvcNAQELBQADggEBAIAylnWX2WTB+mrVVpE2W8i0HollTMJIPJA9Jq3Q /t2uPjXDEAVAcBmQju8qy2tHpamvzpQseVm3EF3UFNlGxwOGKsTzU4J45qOJITZk eLRAcWNEt6cgqj8ml8PuMHU7oDnp7pP6VPe5KQH1a0FYQnDNEwg7MyX+GjnXeRwd re6y9nMC+XKCYUAd1/nQcrZdnSsws1M5lzXir2vuyyN9EUkf2xMMKA2E1s0f+5he 3eNghAXtZw616ITBoMb7ckG6a0+YobbiQ0tKgB8D3MG2544Gx6xhCXf7pX4q4g// 1nTPeYFsBDyqEOEhcW1o9/MSSbjpUJC+QUmCb2Y1wYeum+w= -----END CERTIFICATE----- urllib3/dummyserver/certs/cacert.key 0000644 00000003217 15025013454 0013577 0 ustar 00 -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAn330NEISY7w+GeZR2jh8Of1x2CtpenWRKuuD2u4FOaN0G1SN pm6Owum6xzhv93jmj+tZrp9kYvC9HcvGrgzH9yP566pLLfY2SEEAJYNNeVdqegY5 9W1wa6vEDN5UYruVEbymgPHoItiYhfndgEDbagPN5dhrdNvaRNI2c5zMmBDlzzhC G7AZbXuthTp6OwTVye71f2lnNhKi6rzWtre/ji88fq8Qm406l29O1RTzmNttN5BZ nPWU9v4GnCYKXdY6BN1Ub6z8C9hna/oRgBqa0Zbv7kEuAuqhsFQeuFCfBhQm7NdP d/7Kh6LS+VIiCu3AOccZHOuFOjtHb/KyndmgCwIDAQABAoIBAGQg9wc308O5kmNA LXMKszLU4nwMBRRUaua/JPB1LeKZs3LVCnjKP+YuRox76g87X8RKxOrUNnnHGXNz UzBB5ehKNcS2DKy2Pi3uYOEsJZ9gOgCRmCF0q3dtRo+tpNy3V0bjYMTjGhGGWXsC +wRhs15DNShvTkb3H3jFYFoEvo1YUKsvImBWJGwDbdDMfMZv4aeBWXlOrF+2fwt2 TM3G1o8xzEEWBB4FLZBW+tq2zfUSa1KwqqyQ4ZIqXepjQcN6nNfuHADA+nxuruVV LPUhz4ZmsBEnJ7CL9zWJkLUw/al9/6Q14tleRmiZTTztqAlFgZUpNhaKSzVdsIc/ Xz3+OgECgYEAzgNu7eFJAOzq+ZVFRrrbA7tu+f318FTCaADJ1kFgAJyj6b9yOGan LNL4TfXVjzgqtfQ4AIVqEHwXO7yS+YgTprvgzqRxqNRZ6ikuo2IPkIwXIAXZAlwd JsWLPBXOlOFW6LHvhYxjY2xF+A9y4AbuZ3UDRUQ+tp226VfEaeY80+ECgYEAxjDV cJqeBO06YRVGmcXfAYwRLJGT4hvIZeiSbxX/kJ0rx+cYLT/XZbAguJYQ5ZK2lkeA YneXYDlSTxmBxHxiWwWe3mcmctdE4Jbw8oIZ8a49a4KE/F2ojC4gmisIt3/OqGOw C4e/pDCE/QV64LWdazgUWHPGoVEmZx9/oMm/MWsCgYEAsLtlSJFB7ZdRpTcXLSxT gwoilDf36mrsNAipHjMLRrsaKwbf197If72k4kyJHspSabHO8TOC4A10aPzHIWZJ ZXo7y0prbyhs0mLt7Z/MNnbXx9L8bffT0lUZszwJ8tK1mf47utfK05opFDs8k0+e 6gYJ/jwjiMoYBmoSx76KZEECgYBagJxHAmQcbdQV1yhZOhFe3H5PMt8sBnHZj32m +o2slQkUDQRuTVPoHKikgeqPWxLDxzzqOiBHEYXzlvs6JW6okAV/G+1jzcenI2Y9 54k/YsirWnut3nsEIGBE5lfhq5xMKtGOQlwR9xITlLgK+wQ6nO41ghD3Q15dAvY+ D0KepwKBgQChHvbyzw0t76J2gLxUSyuG7VsId651bpqTYUsbSDFlRo4g8UbBAkHd fdv5BOon3ALJFreSK+a78es0kpiLwrS2SqG/y3mb9aUoLpCVB1haDmmP4Rn4AYXz OCfUkusuSoXOR8CMjqkXYl5QjeJAUAt9GTsZnXIbOQKbaZwkeV0HEg== -----END RSA PRIVATE KEY----- urllib3/dummyserver/certs/server.key 0000644 00000003217 15025013454 0013644 0 ustar 00 -----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEArRLZX+5dyCh4N7q90sH2Q4Ea6QLK8OfoUQPWtpzAtINDUAdf SXCC/qYTtGeSCGjB4W0LfvRTI8afHoD/M+YpaCRnx7T1sy1taA2rnGrEVXEHalVP +RI4t4ZWtX56aez2M0Fs6o4MtzAuP6fKgSdWzIvOmtCxqn0Zf2KbfEHnQylsy2Lg Pa/xLg50fbZ195+h4EAB3d2/jqaeFGGhN+7zrrv4+L1eeW3bzOkvPEkTNepq3Gy/ 8r5e0i2icEnM+eBfl8NYgQ1toJYvDIy5Qi2TRzaFxBVmqUOc8EFtHpL7E9YLbTTW 15xdoLVLdXI5igGxkwPYoeiiAJWxIsC/hL1RRQIDAQABAoIBAQCZ/62f6G9WDHx7 yhPhlmjTw+r37l45YYCbpbjFoFDvzeR1LzogFJbak1fxLD8KcHwjY23ZNvlLWg53 i/yIZ4Hsgog9cM0283LoJVHPykiMZhhdCzAvxYDl/AjnUXUHD6w6CzsoseCql5pv VZOgvCpFsxjRNGUB+HJZoJoNRG7MmHaY638pGHGMiVbskT9Ww3emtMLdTKy1rQcj 9XO/4mlaBGD79wYxy5Hlysbh2UYuQRv8XN5V46Uugk6sC/i2G7VC8KkqPQ2pM+rA LaeWSuN9dfBwiKcHtJssMP95ilsXsjqh3NoVuFODDXHv3u+nBAxtg2TnLZFkDZXH FvxPJu8BAoGBANwWWzvl/dnTtVDFbhF/61v3AVZV4vVpokXRJKny05CZPZWROTc4 LXMVw9kxeecNdo0yR4jn1yyNUmKnQpTmpsR9Yo9NYH+Z1CLxswpc7ILfVRZBK6bK cCG43lM5xZprG6FXhqkHN2u9z5Y8/PuaMzC8iVs402/gakgPKmn8OjdhAoGBAMlQ mmrx24n9YY/dOn55XC5V/iN3Z6mIsHThnDIU515bwLwZVG7chOLSiWHAh4JzUH+v bV3NnlE1jhf5ln0WAadCtIeVprJG6msNTQlbTMTTV5kVNdbfYX6sFQEI+hC1LCiV yJtuNIa4P5W/PtoC3FWUlcAH8C91S/M4CeZZ0HhlAoGBAIxflgE2SBrO9S53PiTb OfqGKMwwK3nrzhxJsODUiCwKEUV8Qsn9gr+MekXlUKMV6y9Tily/wnYgDRPvKoBe PK/GaT6NU6cPLka7cj6B1jgCyfpPxs+y/qIDj4n1pxs+hXj6omDcwXRutCBW9eRk DZJgLhuIuxL4R9F+GsdOoLMBAoGAKQn1cLe9OXQd32YJ9p5m3EtLc49z4murDSiw 3sTEJcgukinXvIHX1SV2PCczeLRpRJ5OfUDddVCllt2agAVscNx4UOuA//bU8t3T RoUGMVmkEeDxCMyg42HRJlTeJWnJhryCGK1up8gHrk8+UNMkd43CuVLk88fFo99Y pUzJ4sECgYEAvBDTo3k3sD18qV6p6tQwy+MVjvQb9V81GHP18tYcVKta3LkkqUFa 3qSyVxi7gl3JtynG7NJ7+GDx6zxW2xUR72NTcJwWvesLI+1orM288pyNDVw9MJ/j AyVFnW5SEYEqdizTnQxL+rQB4CyeHfwZx2/1/Qr0ezLGUJv51lnk4mQ= -----END RSA PRIVATE KEY----- urllib3/dummyserver/__init__.py 0000644 00000000000 15025013454 0012600 0 ustar 00 urllib3/dummyserver/app.py 0000644 00000031416 15025013454 0011640 0 ustar 00 from __future__ import annotations import collections import contextlib import datetime import email.utils import gzip import mimetypes import zlib from io import BytesIO from pathlib import Path from typing import Iterator import trio from quart import Response, make_response, request from quart.typing import ResponseReturnValue from quart_trio import QuartTrio hypercorn_app = QuartTrio(__name__) # Globals are not safe in Flask/Quart but work for our Hypercorn use case RETRY_TEST_NAMES: collections.Counter[str] = collections.Counter() LAST_RETRY_AFTER_REQ: datetime.datetime = datetime.datetime.min pyodide_testing_app = QuartTrio(__name__) DEFAULT_HEADERS = [ # Allow cross-origin requests for emscripten ("Access-Control-Allow-Origin", "*"), ("Cross-Origin-Opener-Policy", "same-origin"), ("Cross-Origin-Embedder-Policy", "require-corp"), ("Feature-Policy", "sync-xhr *;"), ("Access-Control-Allow-Headers", "*"), ] @hypercorn_app.route("/") @pyodide_testing_app.route("/") @pyodide_testing_app.route("/index") async def index() -> ResponseReturnValue: return await make_response("Dummy server!") @hypercorn_app.route("/alpn_protocol") async def alpn_protocol() -> ResponseReturnValue: """Return the requester's certificate.""" alpn_protocol = request.scope["extensions"]["tls"]["alpn_protocol"] return await make_response(alpn_protocol) @hypercorn_app.route("/certificate") async def certificate() -> ResponseReturnValue: """Return the requester's certificate.""" print("scope", request.scope) subject = request.scope["extensions"]["tls"]["client_cert_name"] subject_as_dict = dict(part.split("=") for part in subject.split(", ")) return await make_response(subject_as_dict) @hypercorn_app.route("/specific_method", methods=["GET", "POST", "PUT"]) @pyodide_testing_app.route("/specific_method", methods=["GET", "POST", "PUT"]) async def specific_method() -> ResponseReturnValue: "Confirm that the request matches the desired method type" method_param = (await request.values).get("method", "") if request.method.upper() == method_param.upper(): return await make_response("", 200) else: return await make_response( f"Wrong method: {method_param} != {request.method}", 400 ) @hypercorn_app.route("/upload", methods=["POST"]) async def upload() -> ResponseReturnValue: "Confirm that the uploaded file conforms to specification" params = await request.form param = params.get("upload_param") filename_param = params.get("upload_filename") size = int(params.get("upload_size", "0")) files_ = (await request.files).getlist(param) assert files_ is not None if len(files_) != 1: return await make_response( f"Expected 1 file for '{param}', not {len(files_)}", 400 ) file_ = files_[0] # data is short enough to read synchronously without blocking the event loop with contextlib.closing(file_.stream) as stream: data = stream.read() if int(size) != len(data): return await make_response(f"Wrong size: {int(size)} != {len(data)}", 400) if filename_param != file_.filename: return await make_response( f"Wrong filename: {filename_param} != {file_.filename}", 400 ) return await make_response() @hypercorn_app.route("/chunked") async def chunked() -> ResponseReturnValue: def generate() -> Iterator[str]: for _ in range(4): yield "123" return await make_response(generate()) @hypercorn_app.route("/chunked_gzip") async def chunked_gzip() -> ResponseReturnValue: def generate() -> Iterator[bytes]: compressor = zlib.compressobj(6, zlib.DEFLATED, 16 + zlib.MAX_WBITS) for uncompressed in [b"123"] * 4: yield compressor.compress(uncompressed) yield compressor.flush() return await make_response(generate(), 200, [("Content-Encoding", "gzip")]) @hypercorn_app.route("/keepalive") async def keepalive() -> ResponseReturnValue: if request.args.get("close", b"0") == b"1": headers = [("Connection", "close")] return await make_response("Closing", 200, headers) headers = [("Connection", "keep-alive")] return await make_response("Keeping alive", 200, headers) @hypercorn_app.route("/echo", methods=["GET", "POST", "PUT"]) async def echo() -> ResponseReturnValue: "Echo back the params" if request.method == "GET": return await make_response(request.query_string) return await make_response(await request.get_data()) @hypercorn_app.route("/echo_json", methods=["POST"]) @pyodide_testing_app.route("/echo_json", methods=["POST", "OPTIONS"]) async def echo_json() -> ResponseReturnValue: "Echo back the JSON" if request.method == "OPTIONS": return await make_response("", 200) data = await request.get_data() return await make_response(data, 200, request.headers) @hypercorn_app.route("/echo_uri/<path:rest>") @hypercorn_app.route("/echo_uri", defaults={"rest": ""}) async def echo_uri(rest: str) -> ResponseReturnValue: "Echo back the requested URI" assert request.full_path is not None return await make_response(request.full_path) @hypercorn_app.route("/echo_params") async def echo_params() -> ResponseReturnValue: "Echo back the query parameters" await request.get_data() echod = sorted((k, v) for k, v in request.args.items()) return await make_response(repr(echod)) @hypercorn_app.route("/headers", methods=["GET", "POST"]) async def headers() -> ResponseReturnValue: return await make_response(dict(request.headers.items())) @hypercorn_app.route("/headers_and_params") async def headers_and_params() -> ResponseReturnValue: return await make_response( { "headers": dict(request.headers), "params": request.args, } ) @hypercorn_app.route("/multi_headers", methods=["GET", "POST"]) async def multi_headers() -> ResponseReturnValue: return await make_response({"headers": list(request.headers)}) @hypercorn_app.route("/multi_redirect") async def multi_redirect() -> ResponseReturnValue: "Performs a redirect chain based on ``redirect_codes``" params = request.args codes = params.get("redirect_codes", "200") head, tail = codes.split(",", 1) if "," in codes else (codes, None) assert head is not None status = head if not tail: return await make_response("Done redirecting", status) headers = [("Location", f"/multi_redirect?redirect_codes={tail}")] return await make_response("", status, headers) @hypercorn_app.route("/encodingrequest") async def encodingrequest() -> ResponseReturnValue: "Check for UA accepting gzip/deflate encoding" data = b"hello, world!" encoding = request.headers.get("Accept-Encoding", "") headers = [] if encoding == "gzip": headers = [("Content-Encoding", "gzip")] file_ = BytesIO() with contextlib.closing(gzip.GzipFile("", mode="w", fileobj=file_)) as zipfile: zipfile.write(data) data = file_.getvalue() elif encoding == "deflate": headers = [("Content-Encoding", "deflate")] data = zlib.compress(data) elif encoding == "garbage-gzip": headers = [("Content-Encoding", "gzip")] data = b"garbage" elif encoding == "garbage-deflate": headers = [("Content-Encoding", "deflate")] data = b"garbage" return await make_response(data, 200, headers) @hypercorn_app.route("/redirect", methods=["GET", "POST", "PUT"]) async def redirect() -> ResponseReturnValue: "Perform a redirect to ``target``" values = await request.values target = values.get("target", "/") status = values.get("status", "303 See Other") status_code = status.split(" ")[0] headers = [("Location", target)] return await make_response("", status_code, headers) @hypercorn_app.route("/redirect_after") async def redirect_after() -> ResponseReturnValue: "Perform a redirect to ``target``" params = request.args date = params.get("date") if date: dt = datetime.datetime.fromtimestamp(float(date), tz=datetime.timezone.utc) http_dt = email.utils.format_datetime(dt, usegmt=True) retry_after = str(http_dt) else: retry_after = "1" target = params.get("target", "/") headers = [("Location", target), ("Retry-After", retry_after)] return await make_response("", 303, headers) @hypercorn_app.route("/retry_after") async def retry_after() -> ResponseReturnValue: global LAST_RETRY_AFTER_REQ params = request.args if datetime.datetime.now() - LAST_RETRY_AFTER_REQ < datetime.timedelta(seconds=1): status = params.get("status", "429 Too Many Requests") status_code = status.split(" ")[0] return await make_response("", status_code, [("Retry-After", "1")]) LAST_RETRY_AFTER_REQ = datetime.datetime.now() return await make_response("", 200) @hypercorn_app.route("/status") @pyodide_testing_app.route("/status") async def status() -> ResponseReturnValue: values = await request.values status = values.get("status", "200 OK") status_code = status.split(" ")[0] return await make_response("", status_code) @hypercorn_app.route("/source_address") async def source_address() -> ResponseReturnValue: """Return the requester's IP address.""" return await make_response(request.remote_addr) @hypercorn_app.route("/successful_retry", methods=["GET", "PUT"]) async def successful_retry() -> ResponseReturnValue: """First return an error and then success It's not currently very flexible as the number of retries is hard-coded. """ test_name = request.headers.get("test-name", None) if not test_name: return await make_response("test-name header not set", 400) RETRY_TEST_NAMES[test_name] += 1 if RETRY_TEST_NAMES[test_name] >= 2: return await make_response("Retry successful!", 200) else: return await make_response("need to keep retrying!", 418) @pyodide_testing_app.after_request def apply_caching(response: Response) -> ResponseReturnValue: for header, value in DEFAULT_HEADERS: response.headers[header] = value return response @pyodide_testing_app.route("/slow") async def slow() -> ResponseReturnValue: await trio.sleep(10) return await make_response("TEN SECONDS LATER", 200) @pyodide_testing_app.route("/bigfile") async def bigfile() -> ResponseReturnValue: # great big text file, should force streaming # if supported bigdata = 1048576 * b"WOOO YAY BOOYAKAH" return await make_response(bigdata, 200) @pyodide_testing_app.route("/mediumfile") async def mediumfile() -> ResponseReturnValue: # quite big file bigdata = 1024 * b"WOOO YAY BOOYAKAH" return await make_response(bigdata, 200) @pyodide_testing_app.route("/upload", methods=["POST", "OPTIONS"]) async def pyodide_upload() -> ResponseReturnValue: if request.method == "OPTIONS": return await make_response("", 200) spare_data = await request.get_data(parse_form_data=True) if len(spare_data) != 0: return await make_response("Bad upload data", 404) files = await request.files form = await request.form if form["upload_param"] != "filefield" or form["upload_filename"] != "lolcat.txt": return await make_response("Bad upload form values", 404) if len(files) != 1 or files.get("filefield") is None: return await make_response("Missing file in form", 404) file = files["filefield"] if file.filename != "lolcat.txt": return await make_response(f"File name incorrect {file.name}", 404) with contextlib.closing(file): data = file.read().decode("utf-8") if data != "I'm in ur multipart form-data, hazing a cheezburgr": return await make_response(f"File data incorrect {data}", 200) return await make_response("Uploaded file correct", 200) @pyodide_testing_app.route("/pyodide/<py_file>") async def pyodide(py_file: str) -> ResponseReturnValue: file_path = Path(pyodide_testing_app.config["pyodide_dist_dir"], py_file) if file_path.exists(): mime_type, encoding = mimetypes.guess_type(file_path) if not mime_type: mime_type = "text/plain" return await make_response( file_path.read_bytes(), 200, [("Content-Type", mime_type)] ) else: return await make_response("", 404) @pyodide_testing_app.route("/wheel/dist.whl") async def wheel() -> ResponseReturnValue: # serve our wheel wheel_folder = Path(__file__).parent.parent / "dist" wheels = list(wheel_folder.glob("*.whl")) if len(wheels) > 0: wheel = wheels[0] headers = [("Content-Disposition", f"inline; filename='{wheel.name}'")] resp = await make_response(wheel.read_bytes(), 200, headers) return resp else: return await make_response(f"NO WHEEL IN {wheel_folder}", 404) urllib3/emscripten-requirements.txt 0000644 00000000105 15025013454 0013546 0 ustar 00 pytest-pyodide==0.54.0 pyodide-build==0.24.1 webdriver-manager==4.0.1 urllib3/.pre-commit-config.yaml 0000644 00000001354 15025013454 0012403 0 ustar 00 repos: - repo: https://github.com/asottile/pyupgrade rev: v3.3.1 hooks: - id: pyupgrade args: ["--py38-plus"] - repo: https://github.com/psf/black rev: 23.1.0 hooks: - id: black args: ["--target-version", "py38"] - repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks: - id: isort - repo: https://github.com/PyCQA/flake8 rev: 6.1.0 hooks: - id: flake8 additional_dependencies: [flake8-2020] - repo: https://github.com/pre-commit/mirrors-prettier rev: "v3.1.0" hooks: - id: prettier types_or: [javascript] - repo: https://github.com/pre-commit/mirrors-eslint rev: v8.53.0 hooks: - id: eslint args: ["--fix"] urllib3/towncrier.toml 0000644 00000000350 15025013454 0011026 0 ustar 00 [tool.towncrier] package = "urllib3" package_dir = "src" filename = "CHANGES.rst" directory = "changelog" issue_format = "`#{issue} <https://github.com/urllib3/urllib3/issues/{issue}>`__" title_format = "{version} ({project_date})" urllib3/ci/deploy.sh 0000644 00000000267 15025013454 0010347 0 ustar 00 #!/bin/bash set -exo pipefail python3 -m pip install --upgrade twine wheel build python3 -m build python3 -m twine upload dist/* -u $PYPI_USERNAME -p $PYPI_PASSWORD --skip-existing urllib3/dev-requirements.txt 0000644 00000001525 15025013454 0012162 0 ustar 00 h2==4.1.0 coverage==7.4.1 PySocks==1.7.1 pytest==8.0.2 pytest-timeout==2.1.0 pyOpenSSL==24.0.0 idna==3.7 # As of v1.1.0, child CA certificates generated by trustme fail # verification by CPython 3.13. # https://github.com/python-trio/trustme/pull/642 trustme @ git+https://github.com/python-trio/trustme@b3a767f336e20600f30c9ff78385a58352ff6ee3 cryptography==42.0.4 backports.zoneinfo==0.2.1;python_version<"3.9" towncrier==23.6.0 pytest-memray==1.5.0;python_version<"3.13" and sys_platform!="win32" and implementation_name=="cpython" trio==0.25.0 Quart==0.19.4 quart-trio==0.11.1 # https://github.com/pgjones/hypercorn/issues/62 # https://github.com/pgjones/hypercorn/issues/168 # https://github.com/pgjones/hypercorn/issues/169 hypercorn @ git+https://github.com/urllib3/hypercorn@urllib3-changes httpx==0.25.2 pytest-socket==0.7.0 cffi==1.17.0rc1 urllib3/changelog/.gitignore 0000644 00000000014 15025013454 0012031 0 ustar 00 !.gitignore urllib3/changelog/README.rst 0000644 00000002702 15025013454 0011536 0 ustar 00 This directory contains changelog entries: short files that contain a small **ReST**-formatted text that will be added to ``CHANGES.rst`` by `towncrier <https://towncrier.readthedocs.io/en/latest/>`__. The ``CHANGES.rst`` will be read by **users**, so this description should be aimed to urllib3 users instead of describing internal changes which are only relevant to the developers. Make sure to use full sentences in the **past tense** and use punctuation, examples:: Added support for HTTPS proxies contacting HTTPS servers. Upgraded ``urllib3.utils.parse_url()`` to be RFC 3986 compliant. Each file should be named like ``<ISSUE>.<TYPE>.rst``, where ``<ISSUE>`` is an issue number, and ``<TYPE>`` is one of the `five towncrier default types <https://towncrier.readthedocs.io/en/latest/#news-fragments>`_ So for example: ``123.feature.rst``, ``456.bugfix.rst``. If your pull request fixes an issue, use that number here. If there is no issue, then after you submit the pull request and get the pull request number you can add a changelog using that instead. If your change does not deserve a changelog entry, apply the `Skip Changelog` GitHub label to your pull request. You can also run ``nox -s docs`` to build the documentation with the draft changelog (``docs/_build/html/changelog.html``) if you want to get a preview of how your change will look in the final release notes. You can also see a preview from the Read the Docs check in pull requests. urllib3/notes/connection-lifecycle.md 0000644 00000011771 15025013454 0013674 0 ustar 00 # Connection lifecycle ## Current implementation `HTTPConnection` should be instantiated with `host` and `port` of the **first origin being connected to** to reach the target origin. This either means the target origin itself or the proxy origin if one is desired. ```python import urllib3.connection # Initialize the HTTPSConnection ('https://...') conn = urllib3.connection.HTTPSConnection( host="example.com", # Here you can configure other options like # 'ssl_minimum_version', 'ca_certs', etc. ) # Set the connect timeout either in the # constructor above or via the property. conn.timeout = 3.0 # (connect timeout) ``` If using CONNECT tunneling with the proxy, call `HTTPConnection.set_tunnel()` with the tunneled host, port, and headers. This should be called before calling `HTTPConnection.connect()` or sending a request. ```python conn = urllib3.connection.HTTPConnection( # Remember that the *first* origin we want to connect to should # be configured as 'host' and 'port', *not* the target origin. host="myproxy.net", port=8080, proxy="http://myproxy.net:8080" ) conn.set_tunnel("example.com", scheme="http", headers={"Proxy-Header": "value"}) ``` Connect to the first origin by calling the `HTTPConnection.connect()` method. If an error occurs here you can check whether the error occurred during the connection to the proxy if `HTTPConnection.has_connected_to_proxy` is false. If the value is true then the error didn't occur while connecting to a proxy. ```python # Explicitly connect to the origin. This isn't # required as sending the first request will # automatically connect if not done explicitly. conn.connect() ``` After connecting to the origin, the connection can be checked to see if `is_verified` is set to true. If not the `HTTPConnectionPool` would emit a warning. The warning only matters for when verification is disabled, because otherwise an error is raised on unverified TLS handshake. ```python if not conn.is_verified: # There isn't a verified TLS connection to target origin. if not conn.proxy_is_verified: # There isn't a verified TLS connection to proxy origin. ``` If the read timeout is different from the connect timeout then the `HTTPConnection.timeout` property can be changed at this point. ```python conn.timeout = 5.0 # (read timeout) ``` Then the HTTP request can be sent with `HTTPConnection.request()`. If a `BrokenPipeError` is raised while sending the request body it can be swallowed as a response can still be received from the origin even when the request isn't completely sent. ```python try: conn.request("GET", "/") except BrokenPipeError: # We can still try to get a response! resp = conn.getresponse() ``` Then response headers (and other info) are read from the connection via `HTTPConnection.getresponse()` and returned as a `urllib3.HTTPResponse`. The `HTTPResponse` instance carries a reference to the `HTTPConnection` instance so the connection can be closed if the connection gets into an undefined protocol state. ```python assert resp.connection is conn ``` If pooling is in use the `HTTPConnectionPool` will set `_pool` on the `HTTPResponse` instance. This will return the connection to the pool once the response is exhausted. If retries are in use set `retries` on the `HTTPResponse` instance. ```python # Set by the HTTPConnectionPool before returning to the caller. resp = conn.getresponse() resp._pool = pool # This will call resp._pool._put_conn(resp.connection) # Connection can get auto-released by exhausting. resp.release_conn() ``` If any error is received from connecting to the origin, sending the request, or receiving the response, the caller will call `HTTPConnection.close()` and discard the connection. Connections can be re-used after being closed, a new TCP connection to proxies and origins will be established. If instead of a tunneling proxy we were using a forwarding proxy then we configure the `HTTPConnection` similarly, except instead of `set_tunnel()` we send absolute URLs to `HTTPConnection.request()`: ```python import urllib3.connection # Initialize the HTTPConnection. conn = urllib3.connection.HTTPConnection( host="myproxy.net", port=8080, proxy="http://myproxy.net:8080" ) # You can request HTTP or HTTPS resources over the proxy # using the absolute URL. conn.request("GET", "http://example.com") resp = conn.getresponse() conn.request("GET", "https://example.com") resp = conn.getresponse() ``` ### HTTP/HTTPS/proxies This is how `HTTPConnection` instances will be configured and used when a `PoolManager` or `ProxyManager` receives a given config: - No proxy, HTTP origin -> `HTTPConnection` - No proxy, HTTPS origin -> `HTTPSConnection` - HTTP proxy, HTTP origin -> `HTTPConnection` in forwarding mode - HTTP proxy, HTTPS origin -> `HTTPSConnection` in tunnel mode - HTTPS proxy, HTTP origin -> `HTTPSConnection` in forwarding mode - HTTPS proxy, HTTPS origin -> `HTTPSConnection` in tunnel mode - HTTPS proxy, HTTPS origin, `ProxyConfig.use_forwarding_for_https=True` -> `HTTPSConnection` in forwarding mode urllib3/notes/public-and-private-apis.md 0000644 00000001224 15025013454 0014210 0 ustar 00 # Public and private APIs ## Public APIs - `urllib3.request()` - `urllib3.PoolManager` - `urllib3.ProxyManager` - `urllib3.HTTPConnectionPool` - `urllib3.HTTPSConnectionPool` - `urllib3.BaseHTTPResponse` - `urllib3.HTTPResponse` - `urllib3.HTTPHeaderDict` - `urllib3.filepost` - `urllib3.fields` - `urllib3.exceptions` - `urllib3.contrib.*` - `urllib3.util` Only public way to configure proxies is through `ProxyManager`? ## Private APIs - `urllib3.connection` - `urllib3.connection.BaseHTTPConnection` - `urllib3.connection.BaseHTTPSConnection` - `urllib3.connection.HTTPConnection` - `urllib3.connection.HTTPSConnection` - `urllib3.util.*` (submodules) urllib3/.gitignore 0000644 00000000125 15025013454 0010105 0 ustar 00 .* !/.github/ *.py[co] *.egg *.egg-info *.log /dist /build /docs/_build coverage.xml urllib3/mypy-requirements.txt 0000644 00000000302 15025013454 0012372 0 ustar 00 mypy==1.8.0 idna>=2.0.0 cryptography>=1.3.4 pytest>=6.2 trustme==1.1.0 trio==0.23.1 Quart==0.19.4 quart-trio==0.11.1 hypercorn==0.15.0 httpx==0.25.2 types-backports types-requests nox zstandard urllib3/noxfile.py 0000644 00000025063 15025013454 0010143 0 ustar 00 from __future__ import annotations import os import shutil import sys import typing from pathlib import Path import nox nox.options.error_on_missing_interpreters = True def tests_impl( session: nox.Session, extras: str = "socks,brotli,zstd,h2", # hypercorn dependency h2 compares bytes and strings # https://github.com/python-hyper/h2/issues/1236 byte_string_comparisons: bool = False, integration: bool = False, pytest_extra_args: list[str] = [], ) -> None: # Retrieve sys info from the Python implementation under test # to avoid enabling memray when nox runs under CPython but tests PyPy session_python_info = session.run( "python", "-c", "import sys; print(sys.implementation.name, sys.version_info.releaselevel)", silent=True, ).strip() # type: ignore[union-attr] # mypy doesn't know that silent=True will return a string implementation_name, release_level = session_python_info.split(" ") # zstd cannot be installed on CPython 3.13 yet because it pins # an incompatible CFFI version. # https://github.com/indygreg/python-zstandard/issues/210 if release_level != "final": extras = extras.replace(",zstd", "") # Install deps and the package itself. session.install("-r", "dev-requirements.txt") session.install(f".[{extras}]") # Show the pip version. session.run("pip", "--version") # Print the Python version and bytesize. session.run("python", "--version") session.run("python", "-c", "import struct; print(struct.calcsize('P') * 8)") # Print OpenSSL information. session.run("python", "-m", "OpenSSL.debug") memray_supported = True if implementation_name != "cpython" or release_level != "final": memray_supported = False # pytest-memray requires CPython 3.8+ elif sys.platform == "win32": memray_supported = False # Environment variables being passed to the pytest run. pytest_session_envvars = { "PYTHONWARNINGS": "always::DeprecationWarning", } # In coverage 7.4.0 we can only set the setting for Python 3.12+ # Future versions of coverage will use sys.monitoring based on availability. if ( isinstance(session.python, str) and "." in session.python and int(session.python.split(".")[1]) >= 12 ): pytest_session_envvars["COVERAGE_CORE"] = "sysmon" # Inspired from https://hynek.me/articles/ditch-codecov-python/ # We use parallel mode and then combine in a later CI step session.run( "python", *(("-bb",) if byte_string_comparisons else ()), "-m", "coverage", "run", "--parallel-mode", "-m", "pytest", *("--memray", "--hide-memray-summary") if memray_supported else (), "-v", "-ra", *(("--integration",) if integration else ()), f"--color={'yes' if 'GITHUB_ACTIONS' in os.environ else 'auto'}", "--tb=native", "--durations=10", "--strict-config", "--strict-markers", "--disable-socket", "--allow-unix-socket", "--allow-hosts=localhost,::1,127.0.0.0,240.0.0.0", # See `TARPIT_HOST` *pytest_extra_args, *(session.posargs or ("test/",)), env=pytest_session_envvars, ) @nox.session( python=[ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "pypy3.8", "pypy3.9", "pypy3.10", ] ) def test(session: nox.Session) -> None: tests_impl(session) @nox.session(python="3") def test_integration(session: nox.Session) -> None: """Run integration tests""" tests_impl(session, integration=True) @nox.session(python="3") def test_brotlipy(session: nox.Session) -> None: """Check that if 'brotlipy' is installed instead of 'brotli' or 'brotlicffi' that we still don't blow up. """ session.install("brotlipy") tests_impl(session, extras="socks", byte_string_comparisons=False) def git_clone(session: nox.Session, git_url: str) -> None: """We either clone the target repository or if already exist simply reset the state and pull. """ expected_directory = git_url.split("/")[-1] if expected_directory.endswith(".git"): expected_directory = expected_directory[:-4] if not os.path.isdir(expected_directory): session.run("git", "clone", "--depth", "1", git_url, external=True) else: session.run( "git", "-C", expected_directory, "reset", "--hard", "HEAD", external=True ) session.run("git", "-C", expected_directory, "pull", external=True) @nox.session() def downstream_botocore(session: nox.Session) -> None: root = os.getcwd() tmp_dir = session.create_tmp() session.cd(tmp_dir) git_clone(session, "https://github.com/boto/botocore") session.chdir("botocore") session.run("git", "rev-parse", "HEAD", external=True) session.run("python", "scripts/ci/install") session.cd(root) session.install(".", silent=False) session.cd(f"{tmp_dir}/botocore") session.run("python", "-c", "import urllib3; print(urllib3.__version__)") session.run("python", "scripts/ci/run-tests") @nox.session() def downstream_requests(session: nox.Session) -> None: root = os.getcwd() tmp_dir = session.create_tmp() session.cd(tmp_dir) git_clone(session, "https://github.com/psf/requests") session.chdir("requests") session.run("git", "rev-parse", "HEAD", external=True) session.install(".[socks]", silent=False) session.install("-r", "requirements-dev.txt", silent=False) session.cd(root) session.install(".", silent=False) session.cd(f"{tmp_dir}/requests") session.run("python", "-c", "import urllib3; print(urllib3.__version__)") session.run("pytest", "tests") @nox.session() def format(session: nox.Session) -> None: """Run code formatters.""" lint(session) @nox.session(python="3.12") def lint(session: nox.Session) -> None: session.install("pre-commit") session.run("pre-commit", "run", "--all-files") mypy(session) @nox.session(python="3.11") def pyodideconsole(session: nox.Session) -> None: # build wheel into dist folder session.install("build") session.run("python", "-m", "build") session.run( "cp", "test/contrib/emscripten/templates/pyodide-console.html", "dist/index.html", external=True, ) session.cd("dist") session.run("python", "-m", "http.server") # TODO: node support is not tested yet - it should work if you require('xmlhttprequest') before # loading pyodide, but there is currently no nice way to do this with pytest-pyodide # because you can't override the test runner properties easily - see # https://github.com/pyodide/pytest-pyodide/issues/118 for more @nox.session(python="3.11") @nox.parametrize("runner", ["firefox", "chrome"]) def emscripten(session: nox.Session, runner: str) -> None: """Test on Emscripten with Pyodide & Chrome / Firefox""" session.install("-r", "emscripten-requirements.txt") # build wheel into dist folder session.run("python", "-m", "build") # make sure we have a dist dir for pyodide dist_dir = None if "PYODIDE_ROOT" in os.environ: # we have a pyodide build tree checked out # use the dist directory from that dist_dir = Path(os.environ["PYODIDE_ROOT"]) / "dist" else: # we don't have a build tree, get one # that matches the version of pyodide build pyodide_version = typing.cast( str, session.run( "python", "-c", "import pyodide_build;print(pyodide_build.__version__)", silent=True, ), ).strip() pyodide_artifacts_path = Path(session.cache_dir) / f"pyodide-{pyodide_version}" if not pyodide_artifacts_path.exists(): print("Fetching pyodide build artifacts") session.run( "wget", f"https://github.com/pyodide/pyodide/releases/download/{pyodide_version}/pyodide-{pyodide_version}.tar.bz2", "-O", f"{pyodide_artifacts_path}.tar.bz2", ) pyodide_artifacts_path.mkdir(parents=True) session.run( "tar", "-xjf", f"{pyodide_artifacts_path}.tar.bz2", "-C", str(pyodide_artifacts_path), "--strip-components", "1", ) dist_dir = pyodide_artifacts_path assert dist_dir is not None assert dist_dir.exists() if runner == "chrome": # install chrome webdriver and add it to path driver = typing.cast( str, session.run( "python", "-c", "from webdriver_manager.chrome import ChromeDriverManager;print(ChromeDriverManager().install())", silent=True, ), ).strip() session.env["PATH"] = f"{Path(driver).parent}:{session.env['PATH']}" tests_impl( session, pytest_extra_args=[ "--rt", "chrome-no-host", "--dist-dir", str(dist_dir), "test", ], ) elif runner == "firefox": driver = typing.cast( str, session.run( "python", "-c", "from webdriver_manager.firefox import GeckoDriverManager;print(GeckoDriverManager().install())", silent=True, ), ).strip() session.env["PATH"] = f"{Path(driver).parent}:{session.env['PATH']}" tests_impl( session, pytest_extra_args=[ "--rt", "firefox-no-host", "--dist-dir", str(dist_dir), "test", ], ) else: raise ValueError(f"Unknown runner: {runner}") @nox.session(python="3.12") def mypy(session: nox.Session) -> None: """Run mypy.""" session.install("-r", "mypy-requirements.txt") session.run("mypy", "--version") session.run( "mypy", "-p", "dummyserver", "-m", "noxfile", "-p", "urllib3", "-p", "test", ) @nox.session def docs(session: nox.Session) -> None: session.install("-r", "docs/requirements.txt") session.install(".[socks,brotli,zstd]") session.chdir("docs") if os.path.exists("_build"): shutil.rmtree("_build") session.run("sphinx-build", "-b", "html", "-W", ".", "_build/html") urllib3/docs/advanced-usage.rst 0000644 00000051656 15025013454 0012465 0 ustar 00 Advanced Usage ============== .. currentmodule:: urllib3 Customizing Pool Behavior ------------------------- The :class:`~poolmanager.PoolManager` class automatically handles creating :class:`~connectionpool.ConnectionPool` instances for each host as needed. By default, it will keep a maximum of 10 :class:`~connectionpool.ConnectionPool` instances. If you're making requests to many different hosts it might improve performance to increase this number. .. code-block:: python import urllib3 http = urllib3.PoolManager(num_pools=50) However, keep in mind that this does increase memory and socket consumption. Similarly, the :class:`~connectionpool.ConnectionPool` class keeps a pool of individual :class:`~connection.HTTPConnection` instances. These connections are used during an individual request and returned to the pool when the request is complete. By default only one connection will be saved for re-use. If you are making many requests to the same host simultaneously it might improve performance to increase this number. .. code-block:: python import urllib3 http = urllib3.PoolManager(maxsize=10) # Alternatively pool = urllib3.HTTPConnectionPool("google.com", maxsize=10) The behavior of the pooling for :class:`~connectionpool.ConnectionPool` is different from :class:`~poolmanager.PoolManager`. By default, if a new request is made and there is no free connection in the pool then a new connection will be created. However, this connection will not be saved if more than ``maxsize`` connections exist. This means that ``maxsize`` does not determine the maximum number of connections that can be open to a particular host, just the maximum number of connections to keep in the pool. However, if you specify ``block=True`` then there can be at most ``maxsize`` connections open to a particular host. .. code-block:: python http = urllib3.PoolManager(maxsize=10, block=True) # Alternatively pool = urllib3.HTTPConnectionPool("google.com", maxsize=10, block=True) Any new requests will block until a connection is available from the pool. This is a great way to prevent flooding a host with too many connections in multi-threaded applications. .. _stream: .. _streaming_and_io: Streaming and I/O ----------------- When using ``preload_content=True`` (the default setting) the response body will be read immediately into memory and the HTTP connection will be released back into the pool without manual intervention. However, when dealing with large responses it's often better to stream the response content using ``preload_content=False``. Setting ``preload_content`` to ``False`` means that urllib3 will only read from the socket when data is requested. .. note:: When using ``preload_content=False``, you need to manually release the HTTP connection back to the connection pool so that it can be re-used. To ensure the HTTP connection is in a valid state before being re-used all data should be read off the wire. You can call the :meth:`~response.HTTPResponse.drain_conn` to throw away unread data still on the wire. This call isn't necessary if data has already been completely read from the response. After all data is read you can call :meth:`~response.HTTPResponse.release_conn` to release the connection into the pool. You can call the :meth:`~response.HTTPResponse.close` to close the connection, but this call doesn’t return the connection to the pool, throws away the unread data on the wire, and leaves the connection in an undefined protocol state. This is desirable if you prefer not reading data from the socket to re-using the HTTP connection. :meth:`~response.HTTPResponse.stream` lets you iterate over chunks of the response content. .. code-block:: python import urllib3 resp = urllib3.request( "GET", "https://httpbin.org/bytes/1024", preload_content=False ) for chunk in resp.stream(32): print(chunk) # b"\x9e\xa97'\x8e\x1eT .... resp.release_conn() However, you can also treat the :class:`~response.HTTPResponse` instance as a file-like object. This allows you to do buffering: .. code-block:: python import urllib3 resp = urllib3.request( "GET", "https://httpbin.org/bytes/1024", preload_content=False ) print(resp.read(4)) # b"\x88\x1f\x8b\xe5" Calls to :meth:`~response.HTTPResponse.read()` will block until more response data is available. .. code-block:: python import io import urllib3 resp = urllib3.request( "GET", "https://httpbin.org/bytes/1024", preload_content=False ) reader = io.BufferedReader(resp, 8) print(reader.read(4)) # b"\xbf\x9c\xd6" resp.release_conn() You can use this file-like object to do things like decode the content using :mod:`codecs`: .. code-block:: python import codecs import json import urllib3 reader = codecs.getreader("utf-8") resp = urllib3.request( "GET", "https://httpbin.org/ip", preload_content=False ) print(json.load(reader(resp))) # {"origin": "127.0.0.1"} resp.release_conn() .. _proxies: Proxies ------- You can use :class:`~poolmanager.ProxyManager` to tunnel requests through an HTTP proxy: .. code-block:: python import urllib3 proxy = urllib3.ProxyManager("https://localhost:3128/") proxy.request("GET", "https://google.com/") The usage of :class:`~poolmanager.ProxyManager` is the same as :class:`~poolmanager.PoolManager`. You can connect to a proxy using HTTP, HTTPS or SOCKS. urllib3's behavior will be different depending on the type of proxy you selected and the destination you're contacting. HTTP and HTTPS Proxies ~~~~~~~~~~~~~~~~~~~~~~ Both HTTP/HTTPS proxies support HTTP and HTTPS destinations. The only difference between them is if you need to establish a TLS connection to the proxy first. You can specify which proxy you need to contact by specifying the proper proxy scheme. (i.e ``http://`` or ``https://``) urllib3's behavior will be different depending on your proxy and destination: * HTTP proxy + HTTP destination Your request will be forwarded with the `absolute URI <https://datatracker.ietf.org/doc/html/rfc9112#name-absolute-form>`_. * HTTP proxy + HTTPS destination A TCP tunnel will be established with a `HTTP CONNECT <https://datatracker.ietf.org/doc/html/rfc9110#name-connect>`_. Afterward a TLS connection will be established with the destination and your request will be sent. * HTTPS proxy + HTTP destination A TLS connection will be established to the proxy and later your request will be forwarded with the `absolute URI <https://datatracker.ietf.org/doc/html/rfc9112#name-absolute-form>`_. * HTTPS proxy + HTTPS destination A TLS-in-TLS tunnel will be established. An initial TLS connection will be established to the proxy, then an `HTTP CONNECT <https://datatracker.ietf.org/doc/html/rfc9110#name-connect>`_ will be sent to establish a TCP connection to the destination and finally a second TLS connection will be established to the destination. You can customize the :class:`ssl.SSLContext` used for the proxy TLS connection through the ``proxy_ssl_context`` argument of the :class:`~poolmanager.ProxyManager` class. For HTTPS proxies we also support forwarding your requests to HTTPS destinations with an `absolute URI <https://datatracker.ietf.org/doc/html/rfc9112#name-absolute-form>`_ if the ``use_forwarding_for_https`` argument is set to ``True``. We strongly recommend you **only use this option with trusted or corporate proxies** as the proxy will have full visibility of your requests. .. _https_proxy_error_http_proxy: Your proxy appears to only use HTTP and not HTTPS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you're receiving the :class:`~urllib3.exceptions.ProxyError` and it mentions your proxy only speaks HTTP and not HTTPS here's what to do to solve your issue: If you're using ``urllib3`` directly, make sure the URL you're passing into :class:`urllib3.ProxyManager` starts with ``http://`` instead of ``https://``: .. code-block:: python # Do this: http = urllib3.ProxyManager("http://...") # Not this: http = urllib3.ProxyManager("https://...") If instead you're using ``urllib3`` through another library like Requests there are multiple ways your proxy could be mis-configured. You need to figure out where the configuration isn't correct and make the fix there. Some common places to look are environment variables like ``HTTP_PROXY``, ``HTTPS_PROXY``, and ``ALL_PROXY``. Ensure that the values for all of these environment variables starts with ``http://`` and not ``https://``: .. code-block:: bash # Check your existing environment variables in bash $ env | grep "_PROXY" HTTP_PROXY=http://127.0.0.1:8888 HTTPS_PROXY=https://127.0.0.1:8888 # <--- This setting is the problem! # Make the fix in your current session and test your script $ export HTTPS_PROXY="http://127.0.0.1:8888" $ python test-proxy.py # This should now pass. # Persist your change in your shell 'profile' (~/.bashrc, ~/.profile, ~/.bash_profile, etc) # You may need to logout and log back in to ensure this works across all programs. $ vim ~/.bashrc If you're on Windows or macOS your proxy may be getting set at a system level. To check this first ensure that the above environment variables aren't set then run the following: .. code-block:: bash $ python -c 'import urllib.request; print(urllib.request.getproxies())' If the output of the above command isn't empty and looks like this: .. code-block:: python { "http": "http://127.0.0.1:8888", "https": "https://127.0.0.1:8888" # <--- This setting is the problem! } Search how to configure proxies on your operating system and change the ``https://...`` URL into ``http://``. After you make the change the return value of ``urllib.request.getproxies()`` should be: .. code-block:: python { # Everything is good here! :) "http": "http://127.0.0.1:8888", "https": "http://127.0.0.1:8888" } If you still can't figure out how to configure your proxy after all these steps please `join our community Discord <https://discord.gg/urllib3>`_ and we'll try to help you with your issue. SOCKS Proxies ~~~~~~~~~~~~~ For SOCKS, you can use :class:`~contrib.socks.SOCKSProxyManager` to connect to SOCKS4 or SOCKS5 proxies. In order to use SOCKS proxies you will need to install `PySocks <https://pypi.org/project/PySocks/>`_ or install urllib3 with the ``socks`` extra: .. code-block:: bash python -m pip install urllib3[socks] Once PySocks is installed, you can use :class:`~contrib.socks.SOCKSProxyManager`: .. code-block:: python from urllib3.contrib.socks import SOCKSProxyManager proxy = SOCKSProxyManager("socks5h://localhost:8889/") proxy.request("GET", "https://google.com/") .. note:: It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in your ``proxy_url`` to ensure that DNS resolution is done from the remote server instead of client-side when connecting to a domain name. .. _ssl_custom: .. _custom_ssl_certificates: Custom TLS Certificates ----------------------- Instead of using `certifi <https://certifi.io/>`_ you can provide your own certificate authority bundle. This is useful for cases where you've generated your own certificates or when you're using a private certificate authority. Just provide the full path to the certificate bundle when creating a :class:`~poolmanager.PoolManager`: .. code-block:: python import urllib3 http = urllib3.PoolManager( cert_reqs="CERT_REQUIRED", ca_certs="/path/to/your/certificate_bundle" ) resp = http.request("GET", "https://example.com") When you specify your own certificate bundle only requests that can be verified with that bundle will succeed. It's recommended to use a separate :class:`~poolmanager.PoolManager` to make requests to URLs that do not need the custom certificate. .. _sni_custom: Custom SNI Hostname ------------------- If you want to create a connection to a host over HTTPS which uses SNI, there are two places where the hostname is expected. It must be included in the Host header sent, so that the server will know which host is being requested. The hostname should also match the certificate served by the server, which is checked by urllib3. Normally, urllib3 takes care of setting and checking these values for you when you connect to a host by name. However, it's sometimes useful to set a connection's expected Host header and certificate hostname (subject), especially when you are connecting without using name resolution. For example, you could connect to a server by IP using HTTPS like so: .. code-block:: python import urllib3 pool = urllib3.HTTPSConnectionPool( "104.154.89.105", server_hostname="badssl.com" ) pool.request( "GET", "/", headers={"Host": "badssl.com"}, assert_same_host=False ) Note that when you use a connection in this way, you must specify ``assert_same_host=False``. This is useful when DNS resolution for ``example.org`` does not match the address that you would like to use. The IP may be for a private interface, or you may want to use a specific host under round-robin DNS. .. _assert_hostname: Verifying TLS against a different host -------------------------------------- If the server you're connecting to presents a different certificate than the hostname or the SNI hostname, you can use ``assert_hostname``: .. code-block:: python import urllib3 pool = urllib3.HTTPSConnectionPool( "wrong.host.badssl.com", assert_hostname="badssl.com", ) pool.request("GET", "/") .. _ssl_client: Client Certificates ------------------- You can also specify a client certificate. This is useful when both the server and the client need to verify each other's identity. Typically these certificates are issued from the same authority. To use a client certificate, provide the full path when creating a :class:`~poolmanager.PoolManager`: .. code-block:: python http = urllib3.PoolManager( cert_file="/path/to/your/client_cert.pem", cert_reqs="CERT_REQUIRED", ca_certs="/path/to/your/certificate_bundle" ) If you have an encrypted client certificate private key you can use the ``key_password`` parameter to specify a password to decrypt the key. .. code-block:: python http = urllib3.PoolManager( cert_file="/path/to/your/client_cert.pem", cert_reqs="CERT_REQUIRED", key_file="/path/to/your/client.key", key_password="keyfile_password" ) If your key isn't encrypted the ``key_password`` parameter isn't required. TLS minimum and maximum versions -------------------------------- When the configured TLS versions by urllib3 aren't compatible with the TLS versions that the server is willing to use you'll likely see an error like this one: .. code-block:: SSLError(1, '[SSL: UNSUPPORTED_PROTOCOL] unsupported protocol (_ssl.c:1124)') Starting in v2.0 by default urllib3 uses TLS 1.2 and later so servers that only support TLS 1.1 or earlier will not work by default with urllib3. To fix the issue you'll need to use the ``ssl_minimum_version`` option along with the `TLSVersion enum`_ in the standard library ``ssl`` module to configure urllib3 to accept a wider range of TLS versions. For the best security it's a good idea to set this value to the version of TLS that's being used by the server. For example if the server requires TLS 1.0 you'd configure urllib3 like so: .. code-block:: python import ssl import urllib3 http = urllib3.PoolManager( ssl_minimum_version=ssl.TLSVersion.TLSv1 ) # This request works! resp = http.request("GET", "https://tls-v1-0.badssl.com:1010") .. _TLSVersion enum: https://docs.python.org/3/library/ssl.html#ssl.TLSVersion .. _ssl_mac: .. _certificate_validation_and_mac_os_x: Certificate Validation and macOS -------------------------------- Apple-provided Python and OpenSSL libraries contain a patches that make them automatically check the system keychain's certificates. This can be surprising if you specify custom certificates and see requests unexpectedly succeed. For example, if you are specifying your own certificate for validation and the server presents a different certificate you would expect the connection to fail. However, if that server presents a certificate that is in the system keychain then the connection will succeed. `This article <https://hynek.me/articles/apple-openssl-verification-surprises/>`_ has more in-depth analysis and explanation. .. _ssl_warnings: TLS Warnings ------------ urllib3 will issue several different warnings based on the level of certificate verification support. These warnings indicate particular situations and can be resolved in different ways. * :class:`~exceptions.InsecureRequestWarning` This happens when a request is made to an HTTPS URL without certificate verification enabled. Follow the :ref:`certificate verification <ssl>` guide to resolve this warning. .. _disable_ssl_warnings: Making unverified HTTPS requests is **strongly** discouraged, however, if you understand the risks and wish to disable these warnings, you can use :func:`~urllib3.disable_warnings`: .. code-block:: python import urllib3 urllib3.disable_warnings() Alternatively you can capture the warnings with the standard :mod:`logging` module: .. code-block:: python logging.captureWarnings(True) Finally, you can suppress the warnings at the interpreter level by setting the ``PYTHONWARNINGS`` environment variable or by using the `-W flag <https://docs.python.org/3/using/cmdline.html#cmdoption-w>`_. Brotli Encoding --------------- Brotli is a compression algorithm created by Google with better compression than gzip and deflate and is supported by urllib3 if the `Brotli <https://pypi.org/Brotli>`_ package or `brotlicffi <https://github.com/python-hyper/brotlicffi>`_ package is installed. You may also request the package be installed via the ``urllib3[brotli]`` extra: .. code-block:: bash $ python -m pip install urllib3[brotli] Here's an example using brotli encoding via the ``Accept-Encoding`` header: .. code-block:: python import urllib3 urllib3.request( "GET", "https://www.google.com/", headers={"Accept-Encoding": "br"} ) Zstandard Encoding ------------------ `Zstandard <https://datatracker.ietf.org/doc/html/rfc8878>`_ is a compression algorithm created by Facebook with better compression than brotli, gzip and deflate (see `benchmarks <https://facebook.github.io/zstd/#benchmarks>`_) and is supported by urllib3 if the `zstandard package <https://pypi.org/project/zstandard/>`_ is installed. You may also request the package be installed via the ``urllib3[zstd]`` extra: .. code-block:: bash $ python -m pip install urllib3[zstd] .. note:: Zstandard support in urllib3 requires using v0.18.0 or later of the ``zstandard`` package. If the version installed is less than v0.18.0 then Zstandard support won't be enabled. Here's an example using zstd encoding via the ``Accept-Encoding`` header: .. code-block:: python import urllib3 urllib3.request( "GET", "https://www.facebook.com/", headers={"Accept-Encoding": "zstd"} ) Decrypting Captured TLS Sessions with Wireshark ----------------------------------------------- Python 3.8 and higher support logging of TLS pre-master secrets. With these secrets tools like `Wireshark <https://wireshark.org>`_ can decrypt captured network traffic. To enable this simply define environment variable `SSLKEYLOGFILE`: .. code-block:: bash export SSLKEYLOGFILE=/path/to/keylogfile.txt Then configure the key logfile in `Wireshark <https://wireshark.org>`_, see `Wireshark TLS Decryption <https://wiki.wireshark.org/TLS#TLS_Decryption>`_ for instructions. Custom SSL Contexts ------------------- You can exercise fine-grained control over the urllib3 SSL configuration by providing a :class:`ssl.SSLContext <python:ssl.SSLContext>` object. For purposes of compatibility, we recommend you obtain one from :func:`~urllib3.util.create_urllib3_context`. Once you have a context object, you can mutate it to achieve whatever effect you'd like. For example, the code below loads the default SSL certificates, sets the :data:`ssl.OP_ENABLE_MIDDLEBOX_COMPAT<python:ssl.OP_ENABLE_MIDDLEBOX_COMPAT>` flag that isn't set by default, and then makes a HTTPS request: .. code-block:: python import ssl from urllib3 import PoolManager from urllib3.util import create_urllib3_context ctx = create_urllib3_context() ctx.load_default_certs() ctx.options |= ssl.OP_ENABLE_MIDDLEBOX_COMPAT with PoolManager(ssl_context=ctx) as pool: pool.request("GET", "https://www.google.com/") Note that this is different from passing an ``options`` argument to :func:`~urllib3.util.create_urllib3_context` because we don't overwrite the default options: we only add a new one. urllib3/docs/index.rst 0000644 00000007374 15025013454 0010723 0 ustar 00 urllib3 ======= .. toctree:: :hidden: :maxdepth: 3 For Enterprise <https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=docs> Community Discord <https://discord.gg/urllib3> v2-migration-guide sponsors user-guide advanced-usage reference/index contributing changelog urllib3 is a powerful, *user-friendly* HTTP client for Python. :ref:`Much of the Python ecosystem already uses <who-uses>` urllib3 and you should too. urllib3 brings many critical features that are missing from the Python standard libraries: - Thread safety. - Connection pooling. - Client-side TLS/SSL verification. - File uploads with multipart encoding. - Helpers for retrying requests and dealing with HTTP redirects. - Support for gzip, deflate, brotli, and zstd encoding. - Proxy support for HTTP and SOCKS. - 100% test coverage. urllib3 is powerful and easy to use: .. code-block:: pycon >>> import urllib3 >>> resp = urllib3.request("GET", "https://httpbin.org/robots.txt") >>> resp.status 200 >>> resp.data b"User-agent: *\nDisallow: /deny\n" For Enterprise -------------- .. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png :width: 75 :alt: Tidelift .. list-table:: :widths: 10 100 * - |tideliftlogo|_ - Professional support for urllib3 is available as part of the `Tidelift Subscription`_. Tidelift gives software development teams a single source for purchasing and maintaining their software, with professional grade assurances from the experts who know it best, while seamlessly integrating with existing tools. .. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=docs .. _tideliftlogo: https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=docs |learn-more|_ |request-a-demo|_ .. |learn-more| image:: https://raw.githubusercontent.com/urllib3/urllib3/master/docs/images/learn-more-button.png :width: 49% :alt: Learn more about Tidelift Subscription .. _learn-more: https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=docs .. |request-a-demo| image:: https://raw.githubusercontent.com/urllib3/urllib3/master/docs/images/demo-button.png :width: 49% :alt: Request a Demo for the Tidelift Subscription .. _request-a-demo: https://tidelift.com/subscription/request-a-demo?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=docs Installing ---------- urllib3 can be installed with `pip <https://pip.pypa.io>`_ .. code-block:: bash $ python -m pip install urllib3 Alternatively, you can grab the latest source code from `GitHub <https://github.com/urllib3/urllib3>`_: .. code-block:: bash $ git clone https://github.com/urllib3/urllib3.git $ cd urllib3 $ pip install . Usage ----- The :doc:`user-guide` is the place to go to learn how to use the library and accomplish common tasks. The more in-depth :doc:`advanced-usage` guide is the place to go for lower-level tweaking. The :doc:`reference/index` documentation provides API-level documentation. .. _who-uses: Who uses urllib3? ----------------- `urllib3 is one of the most downloaded packages on PyPI <https://pypistats.org/top>`_ and is a dependency of many popular Python packages like `Requests <https://requests.readthedocs.io>`_, `Pip <https://pip.pypa.io>`_, and more! License ------- urllib3 is made available under the MIT License. For more details, see `LICENSE.txt <https://github.com/urllib3/urllib3/blob/master/LICENSE.txt>`_. Contributing ------------ We happily welcome contributions, please see :doc:`contributing` for details. urllib3/docs/images/learn-more-button.png 0000644 00000014122 15025013454 0014374 0 ustar 00 �PNG IHDR � � ?�d pHYs �� �iTXtXML:com.adobe.xmp <?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c140 79.160451, 2017/05/06-01:08:21 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmp:CreatorTool="Adobe Photoshop CC 2018 (Macintosh)" xmp:CreateDate="2019-08-13T09:57:25-04:00" xmp:MetadataDate="2019-08-13T09:57:25-04:00" xmp:ModifyDate="2019-08-13T09:57:25-04:00" xmpMM:InstanceID="xmp.iid:b7f284be-0009-4620-9d2e-72921ad2ba20" xmpMM:DocumentID="adobe:docid:photoshop:34b17886-4aa0-4449-9185-7874c036492b" xmpMM:OriginalDocumentID="xmp.did:303dd00f-3da4-4ee2-a385-ee735ce4c88f" photoshop:ColorMode="3" dc:format="image/png"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:303dd00f-3da4-4ee2-a385-ee735ce4c88f" stEvt:when="2019-08-13T09:57:25-04:00" stEvt:softwareAgent="Adobe Photoshop CC 2018 (Macintosh)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:b7f284be-0009-4620-9d2e-72921ad2ba20" stEvt:when="2019-08-13T09:57:25-04:00" stEvt:softwareAgent="Adobe Photoshop CC 2018 (Macintosh)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <photoshop:TextLayers> <rdf:Bag> <rdf:li photoshop:LayerName="learn more" photoshop:LayerText="learn more"/> <rdf:li photoshop:LayerName="learn more" photoshop:LayerText="learn more"/> </rdf:Bag> </photoshop:TextLayers> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>� �� EIDATx���ol�y��g����6L@�6B��}����\�pJ+�)���G� ��IaGA�"� ���/$ !z!�i�E�P��v"�.��r��tTV� �0@��t6�H�e����Ļ�۾���������p�� ����nr����3��y �`�u p� | ��'