diff -Nru python-fixtures-1.3.1/debian/changelog python-fixtures-3.0.0/debian/changelog --- python-fixtures-1.3.1/debian/changelog 2015-10-16 13:09:22.000000000 +0000 +++ python-fixtures-3.0.0/debian/changelog 2016-06-27 20:03:27.000000000 +0000 @@ -1,3 +1,19 @@ +python-fixtures (3.0.0-0ubuntu1) yakkety; urgency=medium + + [ Ondřej Nový ] + * Fixed homepage (https). + * Fixed VCS URLs (https). + * d/rules: Changed UPSTREAM_GIT protocol to https + * d/copyright: Changed source URL to https protocol + + [ Ivan Udovichenko ] + * Package new version. + * d/control: + - Remove python*-mock dependencies. + - Add myself to uploaders list. + + -- Ivan Udovichenko Fri, 17 Jun 2016 15:05:58 +0300 + python-fixtures (1.3.1-2) unstable; urgency=medium * Uploading to unstable. diff -Nru python-fixtures-1.3.1/debian/control python-fixtures-3.0.0/debian/control --- python-fixtures-1.3.1/debian/control 2015-10-16 13:09:22.000000000 +0000 +++ python-fixtures-3.0.0/debian/control 2016-06-27 20:03:29.000000000 +0000 @@ -1,8 +1,10 @@ Source: python-fixtures Section: python Priority: optional -Maintainer: Robert Collins +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Robert Collins Uploaders: Thomas Goirand , + Ivan Udovichenko , Build-Depends: debhelper (>= 9), dh-python, openstack-pkg-tools, @@ -14,17 +16,15 @@ python3-pbr (>= 0.11), python3-setuptools, Build-Depends-Indep: python-docutils, - python-mock, python-nose, python-six, python-testtools, - python3-mock, python3-six, python3-testtools, -Standards-Version: 3.9.6 -Vcs-Browser: http://anonscm.debian.org/gitweb/?p=openstack/python-fixtures.git -Vcs-Git: git://anonscm.debian.org/openstack/python-fixtures.git -Homepage: http://pypi.python.org/pypi/fixtures +Standards-Version: 3.9.8 +Vcs-Browser: https://anonscm.debian.org/cgit/openstack/python-fixtures.git/ +Vcs-Git: https://anonscm.debian.org/git/openstack/python-fixtures.git +Homepage: https://pypi.python.org/pypi/fixtures Package: python-fixtures Architecture: all diff -Nru python-fixtures-1.3.1/debian/copyright python-fixtures-3.0.0/debian/copyright --- python-fixtures-1.3.1/debian/copyright 2015-10-16 13:09:22.000000000 +0000 +++ python-fixtures-3.0.0/debian/copyright 2016-06-27 18:16:23.000000000 +0000 @@ -1,7 +1,7 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: fixtures Upstream-Contact: Robert Collins -Source: http://pypi.python.org/pypi/fixtures +Source: https://pypi.python.org/pypi/fixtures Files: debian/* Copyright: (c) 2010-2012, Robert Collins diff -Nru python-fixtures-1.3.1/debian/gbp.conf python-fixtures-3.0.0/debian/gbp.conf --- python-fixtures-1.3.1/debian/gbp.conf 2015-10-16 13:09:22.000000000 +0000 +++ python-fixtures-3.0.0/debian/gbp.conf 2016-06-27 18:16:23.000000000 +0000 @@ -1,6 +1,6 @@ [DEFAULT] upstream-branch = master -debian-branch = debian/unstable +debian-branch = debian/experimental upstream-tag = %(version)s compression = xz diff -Nru python-fixtures-1.3.1/debian/rules python-fixtures-3.0.0/debian/rules --- python-fixtures-1.3.1/debian/rules 2015-10-16 13:09:22.000000000 +0000 +++ python-fixtures-3.0.0/debian/rules 2016-06-27 18:57:40.000000000 +0000 @@ -3,7 +3,7 @@ PYTHONS:=$(shell pyversions -vr) PYTHON3S:=$(shell py3versions -vr) -UPSTREAM_GIT = git://github.com/testing-cabal/fixtures.git +UPSTREAM_GIT := https://github.com/testing-cabal/fixtures.git include /usr/share/openstack-pkg-tools/pkgos.make export OSLO_PACKAGE_VERSION=$(VERSION) diff -Nru python-fixtures-1.3.1/debian/watch python-fixtures-3.0.0/debian/watch --- python-fixtures-1.3.1/debian/watch 2015-10-16 13:09:22.000000000 +0000 +++ python-fixtures-3.0.0/debian/watch 2016-06-27 18:16:23.000000000 +0000 @@ -1,2 +1,2 @@ version=3 -https://github.com/testing-cabal/fixtures/tags .*/(\d[\d\.]+)\.tar\.gz +http://pypi.python.org/packages/f/fixtures/fixtures-(.*)\.tar.gz diff -Nru python-fixtures-1.3.1/fixtures/callmany.py python-fixtures-3.0.0/fixtures/callmany.py --- python-fixtures-1.3.1/fixtures/callmany.py 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/fixtures/callmany.py 2016-05-19 23:01:14.000000000 +0000 @@ -67,7 +67,8 @@ re-raised after all the functions have run. If multiple exceptions are raised, they are all wrapped into a MultipleExceptions object, and that is raised. - Thus, to cach a specific exception from a function run by __call__, + + Thus, to catch a specific exception from a function run by __call__, you need to catch both the exception and MultipleExceptions, and then check within a MultipleExceptions instance for an occurance of the type you wish to catch. @@ -96,5 +97,4 @@ def __exit__(self, exc_type, exc_val, exc_tb): self() - return False # propogate exceptions from the with body. - + return False # Propagate exceptions from the with body. diff -Nru python-fixtures-1.3.1/fixtures/fixture.py python-fixtures-3.0.0/fixtures/fixture.py --- python-fixtures-1.3.1/fixtures/fixture.py 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/fixtures/fixture.py 2016-05-19 23:01:14.000000000 +0000 @@ -14,6 +14,7 @@ # limitations under that license. __all__ = [ + 'CompoundFixture', 'Fixture', 'FunctionFixture', 'MethodFixture', @@ -27,7 +28,6 @@ import six from testtools.compat import ( advance_iterator, - reraise, ) from testtools.helpers import try_import @@ -115,7 +115,7 @@ if a single exception is raised, it is reraised after all the cleanUps have run. If multiple exceptions are raised, they are all wrapped into a MultipleExceptions object, and that is reraised. - Thus, to cach a specific exception from cleanUp, you need to catch + Thus, to catch a specific exception from cleanUp, you need to catch both the exception and MultipleExceptions, and then check within a MultipleExceptions instance for the type you're catching. :return: A list of the exc_info() for each exception that occured if @@ -157,7 +157,7 @@ self._cleanups() finally: self._remove_state() - return False # propogate exceptions from the with body. + return False # propagate exceptions from the with body. def getDetails(self): """Get the current details registered with the fixture. @@ -206,7 +206,7 @@ errors = [err] + self.cleanUp(raise_first=False) try: raise SetupError(details) - except SetupError as e: + except SetupError: errors.append(sys.exc_info()) if issubclass(err[0], Exception): raise MultipleExceptions(*errors) @@ -381,12 +381,12 @@ if setup is None: setup = getattr(obj, 'setUp', None) if setup is None: - setup = lambda:None + setup = lambda: None self._setup = setup if cleanup is None: cleanup = getattr(obj, 'tearDown', None) if cleanup is None: - cleanup = lambda:None + cleanup = lambda: None self._cleanup = cleanup if reset is None: reset = getattr(obj, 'reset', None) @@ -404,3 +404,22 @@ super(MethodFixture, self).reset() else: self._reset() + + +class CompoundFixture(Fixture): + """A fixture that combines many fixtures. + + :ivar fixtures: The list of fixtures that make up this one. (read only). + """ + + def __init__(self, fixtures): + """Construct a fixture made of many fixtures. + + :param fixtures: An iterable of fixtures. + """ + super(CompoundFixture, self).__init__() + self.fixtures = list(fixtures) + + def _setUp(self): + for fixture in self.fixtures: + self.useFixture(fixture) diff -Nru python-fixtures-1.3.1/fixtures/_fixtures/logger.py python-fixtures-3.0.0/fixtures/_fixtures/logger.py --- python-fixtures-1.3.1/fixtures/_fixtures/logger.py 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/fixtures/_fixtures/logger.py 2016-05-19 23:01:14.000000000 +0000 @@ -14,7 +14,9 @@ # limitations under that license. from logging import StreamHandler, getLogger, INFO, Formatter +import sys +import six from testtools.compat import _u from fixtures import Fixture @@ -61,6 +63,12 @@ self.addCleanup(logger.removeHandler, self.handler) +class StreamHandlerRaiseException(StreamHandler): + """Handler class that will raise an exception on formatting errors.""" + def handleError(self, record): + six.reraise(*sys.exc_info()) + + class FakeLogger(Fixture): """Replace a logger and capture its output.""" @@ -98,7 +106,7 @@ name = _u("pythonlogging:'%s'") % self._name output = self.useFixture(StringStream(name)).stream self._output = output - handler = StreamHandler(output) + handler = StreamHandlerRaiseException(output) if self._format: formatter = (self._formatter or Formatter) handler.setFormatter(formatter(self._format, self._datefmt)) diff -Nru python-fixtures-1.3.1/fixtures/_fixtures/mockpatch.py python-fixtures-3.0.0/fixtures/_fixtures/mockpatch.py --- python-fixtures-1.3.1/fixtures/_fixtures/mockpatch.py 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/fixtures/_fixtures/mockpatch.py 2016-05-19 23:01:14.000000000 +0000 @@ -19,9 +19,9 @@ import fixtures -mock = extras.try_imports(['unittest.mock', 'mock'], None) +mock = extras.try_imports(['mock', 'unittest.mock'], None) mock_default = extras.try_imports( - ['unittest.mock.DEFAULT', 'mock.DEFAULT'], None) + ['mock.DEFAULT', 'unittest.mock.DEFAULT'], None) class _Base(fixtures.Fixture): diff -Nru python-fixtures-1.3.1/fixtures/_fixtures/monkeypatch.py python-fixtures-3.0.0/fixtures/_fixtures/monkeypatch.py --- python-fixtures-1.3.1/fixtures/_fixtures/monkeypatch.py 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/fixtures/_fixtures/monkeypatch.py 2016-05-19 23:01:14.000000000 +0000 @@ -17,12 +17,81 @@ 'MonkeyPatch' ] +import functools import sys import types from fixtures import Fixture +_class_types = (type, ) +if getattr(types, 'ClassType', None): + # Python 2 has multiple types of classes. + _class_types = _class_types + (types.ClassType,) + + +def _coerce_values(obj, name, new_value, sentinel): + """Return an adapted (new_value, old_value) for patching obj.name. + + setattr transforms a function into an instancemethod when set on a class. + This checks if the attribute to be replaced is a callable descriptor - + staticmethod, classmethod, or types.FunctionType - and wraps new_value if + necessary. + + This also checks getattr(obj, name) and wraps it if necessary + since the staticmethod wrapper isn't preserved. + + :param obj: The object with an attribute being patched. + :param name: The name of the attribute being patched. + :param new_value: The new value to be assigned. + :param sentinel: If no old_value existed, the sentinel is returned to + indicate that. + """ + old_value = getattr(obj, name, sentinel) + if not isinstance(obj, _class_types): + # We may be dealing with an instance of a class. In that case the + # attribute may be the result of a descriptor lookup (or a __getattr__ + # override etc). Its incomplete, but generally good enough to just + # look and see if name is in the instance dict. + try: + obj.__dict__[name] + except (AttributeError, KeyError): + return (new_value, sentinel) + else: + return (new_value, old_value) + + # getattr() returns a function, this access pattern will return a + # staticmethod/classmethod if the name method is defined that way + old_attribute = obj.__dict__.get(name) + if old_attribute is not None: + old_value = old_attribute + + # If new_value is not callable, no special handling is needed. + # (well, technically the same descriptor issue can happen with + # user supplied descriptors, but that is arguably a feature - someone can + # deliberately install a different descriptor. + if not callable(new_value): + return (new_value, old_value) + + if isinstance(old_value, staticmethod): + new_value = staticmethod(new_value) + elif isinstance(old_value, classmethod): + new_value = classmethod(new_value) + elif isinstance(old_value, types.FunctionType): + if hasattr(new_value, '__get__'): + # new_value is a descriptor, and that would result in it being + # rebound if we assign it to a class - we want to preserve the + # bound state rather than having it bound to the new object + # it has been patched onto. + captured_method = new_value + @functools.wraps(old_value) + def avoid_get(*args, **kwargs): + return captured_method(*args, **kwargs) + new_value = avoid_get + + return (new_value, old_value) + + class MonkeyPatch(Fixture): """Replace or delete an attribute.""" @@ -37,6 +106,26 @@ During setup the name will be deleted or assigned the requested value, and this will be restored in cleanUp. + + When patching methods, the call signature of name should be a subset + of the parameters which can be used to call new_value. + + For instance. + + >>> class T: + ... def method(self, arg1): + ... pass + >>> class N: + ... @staticmethod + ... def newmethod(arg1): + ... pass + + Patching N.newmethod on top of T.method and then calling T().method(1) + will not work because they do not have compatible call signatures - + self will be passed to newmethod because the callable (N.newmethod) + is placed onto T as a regular function. This allows capturing all the + supplied parameters while still consulting local state in your + new_value. """ Fixture.__init__(self) self.name = name @@ -55,20 +144,16 @@ for component in components[1:]: current = getattr(current, component) sentinel = object() - old_value = getattr(current, attribute, sentinel) + new_value, old_value = _coerce_values(current, attribute, + self.new_value, sentinel) if self.new_value is self.delete: if old_value is not sentinel: delattr(current, attribute) else: - setattr(current, attribute, self.new_value) + setattr(current, attribute, new_value) if old_value is sentinel: self.addCleanup(self._safe_delete, current, attribute) else: - # Python 2's setattr transforms function into instancemethod - if (sys.version_info[0] == 2 and - isinstance(current, (type, types.ClassType)) and - isinstance(old_value, types.FunctionType)): - old_value = staticmethod(old_value) self.addCleanup(setattr, current, attribute, old_value) def _safe_delete(self, obj, attribute): diff -Nru python-fixtures-1.3.1/fixtures/__init__.py python-fixtures-3.0.0/fixtures/__init__.py --- python-fixtures-1.3.1/fixtures/__init__.py 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/fixtures/__init__.py 2016-05-19 23:01:14.000000000 +0000 @@ -44,6 +44,7 @@ __all__ = [ 'ByteStream', + 'CompoundFixture', 'DetailStream', 'EnvironmentVariable', 'EnvironmentVariableFixture', @@ -78,6 +79,7 @@ from fixtures.fixture import ( + CompoundFixture, Fixture, FunctionFixture, MethodFixture, diff -Nru python-fixtures-1.3.1/fixtures/tests/_fixtures/test_logger.py python-fixtures-3.0.0/fixtures/tests/_fixtures/test_logger.py --- python-fixtures-1.3.1/fixtures/tests/_fixtures/test_logger.py 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/fixtures/tests/_fixtures/test_logger.py 2016-05-19 23:01:14.000000000 +0000 @@ -17,6 +17,7 @@ import sys import time +import testtools from testtools import TestCase from testtools.compat import StringIO @@ -140,6 +141,11 @@ except: pass + def test_exceptionraised(self): + with FakeLogger(): + with testtools.ExpectedException(TypeError): + logging.info("Some message", "wrongarg") + class LogHandlerTest(TestCase, TestWithFixtures): diff -Nru python-fixtures-1.3.1/fixtures/tests/_fixtures/test_mockpatch.py python-fixtures-3.0.0/fixtures/tests/_fixtures/test_mockpatch.py --- python-fixtures-1.3.1/fixtures/tests/_fixtures/test_mockpatch.py 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/fixtures/tests/_fixtures/test_mockpatch.py 2016-05-19 23:01:14.000000000 +0000 @@ -14,7 +14,7 @@ import extras -mock = extras.try_imports(['unittest.mock', 'mock'], None) +import mock # Yes, we only test the rolling backport import testtools from fixtures import ( diff -Nru python-fixtures-1.3.1/fixtures/tests/_fixtures/test_monkeypatch.py python-fixtures-3.0.0/fixtures/tests/_fixtures/test_monkeypatch.py --- python-fixtures-1.3.1/fixtures/tests/_fixtures/test_monkeypatch.py 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/fixtures/tests/_fixtures/test_monkeypatch.py 2016-05-19 23:01:14.000000000 +0000 @@ -13,16 +13,52 @@ # license you chose for the specific language governing permissions and # limitations under that license. +import functools + import testtools +from testtools.matchers import Is from fixtures import MonkeyPatch, TestWithFixtures reference = 23 class C(object): + def foo(self, arg): + return arg + @staticmethod + def foo_static(): pass + @classmethod + def foo_cls(cls): pass + +class D(object): + def bar(self): pass + def bar_two_args(self, arg): + return (self, arg) + @classmethod + def bar_cls(cls): + return cls + @classmethod + def bar_cls_args(cls, *args): + return (cls,) + args @staticmethod - def foo(): pass -def bar(): pass + def bar_static(): + pass + @staticmethod + def bar_static_args(*args): + return args + def bar_self_referential(self, *args, **kwargs): + self.bar() + +INST_C = C() + +def fake(*args): + return args +def fake2(arg): pass +def fake_no_args(): pass +def fake_no_args2(): pass +@staticmethod +def fake_static(): pass + class TestMonkeyPatch(testtools.TestCase, TestWithFixtures): @@ -72,12 +108,318 @@ fixture.cleanUp() self.assertFalse('new_attr' in globals()) - def test_patch_staticmethod(self): - oldfoo = C.foo + def _check_restored_static_or_class_method(self, oldmethod, oldmethod_inst, + klass, methodname): + restored_method = getattr(klass, methodname) + restored_method_inst = getattr(klass(), methodname) + self.assertEqual(oldmethod, restored_method) + self.assertEqual(oldmethod, restored_method_inst) + self.assertEqual(oldmethod_inst, restored_method) + self.assertEqual(oldmethod_inst, restored_method_inst) + restored_method() + restored_method_inst() + + def test_patch_staticmethod_with_staticmethod(self): + oldmethod = C.foo_static + oldmethod_inst = C().foo_static + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo_static', + D.bar_static) + with fixture: + C.foo_static() + C().foo_static() + self._check_restored_static_or_class_method(oldmethod, oldmethod_inst, + C, 'foo_static') + + def test_patch_staticmethod_with_classmethod(self): + oldmethod = C.foo_static + oldmethod_inst = C().foo_static + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo_static', + D.bar_cls) + with fixture: + C.foo_static() + C().foo_static() + self._check_restored_static_or_class_method(oldmethod, oldmethod_inst, + C, 'foo_static') + + def test_patch_staticmethod_with_function(self): + oldmethod = C.foo_static + oldmethod_inst = C().foo_static + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo_static', + fake_no_args) + with fixture: + C.foo_static() + C().foo_static() + self._check_restored_static_or_class_method(oldmethod, oldmethod_inst, + C, 'foo_static') + + def test_patch_staticmethod_with_boundmethod(self): + oldmethod = C.foo_static + oldmethod_inst = C().foo_static + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo_static', + D().bar) + with fixture: + C.foo_static() + C().foo_static() + self._check_restored_static_or_class_method(oldmethod, oldmethod_inst, + C, 'foo_static') + + def test_patch_classmethod_with_staticmethod(self): + oldmethod = C.foo_cls + oldmethod_inst = C().foo_cls + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo_cls', + D.bar_static_args) + with fixture: + (cls,) = C.foo_cls() + self.expectThat(cls, Is(C)) + (cls,) = C().foo_cls() + self.expectThat(cls, Is(C)) + self._check_restored_static_or_class_method(oldmethod, oldmethod_inst, + C, 'foo_cls') + + def test_patch_classmethod_with_classmethod(self): + oldmethod = C.foo_cls + oldmethod_inst = C().foo_cls + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo_cls', + D.bar_cls_args) + with fixture: + cls, target_class = C.foo_cls() + self.expectThat(cls, Is(D)) + self.expectThat(target_class, Is(C)) + cls, target_class = C().foo_cls() + self.expectThat(cls, Is(D)) + self.expectThat(target_class, Is(C)) + self._check_restored_static_or_class_method(oldmethod, oldmethod_inst, + C, 'foo_cls') + + def test_patch_classmethod_with_function(self): + oldmethod = C.foo_cls + oldmethod_inst = C().foo_cls + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo_cls', + fake) + with fixture: + (cls,) = C.foo_cls() + self.expectThat(cls, Is(C)) + (cls, arg) = C().foo_cls(1) + self.expectThat(cls, Is(C)) + self.assertEqual(1, arg) + self._check_restored_static_or_class_method(oldmethod, oldmethod_inst, + C, 'foo_cls') + + def test_patch_classmethod_with_boundmethod(self): + oldmethod = C.foo_cls + oldmethod_inst = C().foo_cls + d = D() + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo_cls', + d.bar_two_args) + with fixture: + slf, cls = C.foo_cls() + self.expectThat(slf, Is(d)) + self.expectThat(cls, Is(C)) + slf, cls = C().foo_cls() + self.expectThat(slf, Is(d)) + self.expectThat(cls, Is(C)) + self._check_restored_static_or_class_method(oldmethod, oldmethod_inst, + C, 'foo_cls') + + def test_patch_function_with_staticmethod(self): + oldmethod = fake_no_args + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.fake_no_args', + D.bar_static) + with fixture: + fake_no_args() + self.assertEqual(oldmethod, fake_no_args) + + def test_patch_function_with_classmethod(self): + oldmethod = fake_no_args + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.fake_no_args', + D.bar_cls) + with fixture: + fake_no_args() + self.assertEqual(oldmethod, fake_no_args) + + def test_patch_function_with_function(self): + oldmethod = fake_no_args + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.fake_no_args', + fake_no_args2) + with fixture: + fake_no_args() + self.assertEqual(oldmethod, fake_no_args) + + def test_patch_function_with_partial(self): + oldmethod = fake_no_args + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.fake_no_args', + functools.partial(fake, 1)) + with fixture: + (ret,) = fake_no_args() + self.assertEqual(1, ret) + self.assertEqual(oldmethod, fake_no_args) + + def test_patch_function_with_boundmethod(self): + oldmethod = fake_no_args + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.fake_no_args', + D().bar) + with fixture: + fake_no_args() + self.assertEqual(oldmethod, fake_no_args) + + def test_patch_boundmethod_with_staticmethod(self): + oldmethod = INST_C.foo + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.INST_C.foo', + D.bar_static) + with fixture: + INST_C.foo() + self.assertEqual(oldmethod, INST_C.foo) + + def test_patch_boundmethod_with_classmethod(self): + oldmethod = INST_C.foo + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.INST_C.foo', + D.bar_cls) + with fixture: + INST_C.foo() + self.assertEqual(oldmethod, INST_C.foo) + + def test_patch_boundmethod_with_function(self): + oldmethod = INST_C.foo + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.INST_C.foo', + fake_no_args) + with fixture: + INST_C.foo() + self.assertEqual(oldmethod, INST_C.foo) + + def test_patch_boundmethod_with_boundmethod(self): + oldmethod = INST_C.foo + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.INST_C.foo', + D().bar) + with fixture: + INST_C.foo() + self.assertEqual(oldmethod, INST_C.foo) + sentinel = object() + self.assertEqual(sentinel, INST_C.__dict__.get('foo', sentinel)) + + def test_patch_unboundmethod_with_staticmethod(self): + oldmethod = C.foo fixture = MonkeyPatch( 'fixtures.tests._fixtures.test_monkeypatch.C.foo', - bar) + D.bar_static_args) with fixture: - pass - self.assertEqual(oldfoo, C.foo) + target_self, arg = INST_C.foo(1) + self.expectThat(target_self, Is(INST_C)) + self.assertEqual(1, arg) + self.assertEqual(oldmethod, C.foo) + def test_patch_unboundmethod_with_classmethod(self): + oldmethod = C.foo + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo', + D.bar_cls_args) + with fixture: + c = C() + cls, target_self, arg = c.foo(1) + self.expectThat(cls, Is(D)) + self.expectThat(target_self, Is(c)) + self.assertEqual(1, arg) + self.assertEqual(oldmethod, C.foo) + + def test_patch_unboundmethod_with_function(self): + oldmethod = C.foo + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo', + fake) + with fixture: + c = C() + target_self, arg = c.foo(1) + self.expectThat(target_self, Is(c)) + self.assertTrue(1, arg) + self.assertEqual(oldmethod, C.foo) + + def test_patch_unboundmethod_with_boundmethod(self): + oldmethod = C.foo + d = D() + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo', + d.bar_two_args) + with fixture: + c = C() + slf, target_self = c.foo() + self.expectThat(slf, Is(d)) + self.expectThat(target_self, Is(c)) + self.assertEqual(oldmethod, C.foo) + + def test_double_patch_instancemethod(self): + oldmethod = C.foo + oldmethod_inst = C().foo + fixture1 = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo', + fake) + fixture2 = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo', + fake2) + with fixture1: + with fixture2: + C().foo() + self.assertEqual(oldmethod, C.foo) + # The method address changes with each instantiation of C, and method + # equivalence just tests that. Compare the code objects instead. + self.assertEqual(oldmethod_inst.__code__, C().foo.__code__) + + def test_double_patch_staticmethod(self): + oldmethod = C.foo_static + oldmethod_inst = C().foo_static + fixture1 = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo_static', + fake_no_args) + fixture2 = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo_static', + fake_static) + with fixture1: + with fixture2: + C.foo_static() + C().foo_static() + self._check_restored_static_or_class_method(oldmethod, oldmethod_inst, + C, 'foo_static') + + def test_double_patch_classmethod(self): + oldmethod = C.foo_cls + oldmethod_inst = C().foo_cls + fixture1 = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo_cls', + fake) + fixture2 = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo_cls', + fake2) + with fixture1: + with fixture2: + C.foo_cls() + C().foo_cls() + self._check_restored_static_or_class_method(oldmethod, oldmethod_inst, + C, 'foo_cls') + + def test_patch_c_foo_with_instance_d_bar_self_referential(self): + oldmethod = C.foo + oldmethod_inst = C().foo + fixture = MonkeyPatch( + 'fixtures.tests._fixtures.test_monkeypatch.C.foo', + D().bar_self_referential) + with fixture: + C().foo() + self.assertEqual(oldmethod, C.foo) + # The method address changes with each instantiation of C, and method + # equivalence just tests that. Compare the code objects instead. + self.assertEqual(oldmethod_inst.__code__, C().foo.__code__) diff -Nru python-fixtures-1.3.1/fixtures/tests/test_callmany.py python-fixtures-3.0.0/fixtures/tests/test_callmany.py --- python-fixtures-1.3.1/fixtures/tests/test_callmany.py 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/fixtures/tests/test_callmany.py 2016-05-19 23:01:14.000000000 +0000 @@ -45,7 +45,7 @@ self.assertEqual(('woo',), value.args) self.assertIsInstance(tb, types.TracebackType) - def test_exit_propogates_exceptions(self): + def test_exit_propagates_exceptions(self): call = CallMany() call.__enter__() self.assertEqual(False, call.__exit__(None, None, None)) diff -Nru python-fixtures-1.3.1/fixtures/tests/test_fixture.py python-fixtures-3.0.0/fixtures/tests/test_fixture.py --- python-fixtures-1.3.1/fixtures/tests/test_fixture.py 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/fixtures/tests/test_fixture.py 2016-05-19 23:01:14.000000000 +0000 @@ -84,7 +84,7 @@ self.assertEqual(('woo',), value.args) self.assertIsInstance(tb, types.TracebackType) - def test_exit_propogates_exceptions(self): + def test_exit_propagates_exceptions(self): fixture = fixtures.Fixture() fixture.__enter__() self.assertEqual(False, fixture.__exit__(None, None, None)) @@ -259,7 +259,7 @@ def test_setup_failures_with_base_exception(self): # when _setUp fails with a BaseException (or subclass thereof) that - # exception is propogated as is, but we still call cleanups etc. + # exception is propagated as is, but we still call cleanups etc. class MyBase(BaseException):pass log = [] class Subclass(fixtures.Fixture): diff -Nru python-fixtures-1.3.1/.gitignore python-fixtures-3.0.0/.gitignore --- python-fixtures-1.3.1/.gitignore 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/.gitignore 2016-05-19 23:01:14.000000000 +0000 @@ -13,3 +13,4 @@ ChangeLog .eggs build +.tox diff -Nru python-fixtures-1.3.1/HACKING python-fixtures-3.0.0/HACKING --- python-fixtures-1.3.1/HACKING 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/HACKING 2016-05-19 23:01:14.000000000 +0000 @@ -38,7 +38,11 @@ 1. Add a version to NEWS. -1. commit, tag. +1. commit, tag:: + + git commit -am "Release X.Y.Z" + git tag -m "Release X.Y.Z" X.Y.Z + git push --tags origin master:master 1. Upload to pypi, signed. diff -Nru python-fixtures-1.3.1/NEWS python-fixtures-3.0.0/NEWS --- python-fixtures-1.3.1/NEWS 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/NEWS 2016-05-19 23:01:14.000000000 +0000 @@ -6,6 +6,40 @@ NEXT ~~~~ +3.0.0 +~~~~~ + +* Monkeypatching of callables has been thoroughly overhauled: there were deep + flaws in usability because staticmethod and classmethods are actually + descriptors. (Andrew Laski, Robert Collins) + +2.0 +~~~ + +* Monkeypatching of staticmethod's now works correctly on Python 2 and 3. + This is an API break as previously they were bound as instancemethods's + and thus any working patches need to have their first parameter (self) + dropped. (Andrew Laski) + +* New class ``CompoundFixture`` added, allowing fixtures to be combined. + (Jonathan Lange) + +* Support for Python 3.2 dropped (as pip and setuptools have). + (Robert Collins) + +1.4 +~~~ + +* ``fixtures`` now has CI testing via Travis, no more manual work. + (Robert Collins) + +* ``mock`` is used in preference to ``unittest.mock`` now, as the rolling + backport has significant bugfixes over older but still supported CPython + stdlib versions. (Robert Collins) + +* ``fixtures.FakeLogger`` now detects incorrectly formatted log messages. + (John Villalovos, #1503049) + 1.3.1 ~~~~~ @@ -22,7 +56,7 @@ * Fixture.setUp should no longer be overridden in subclasses. Instead override _setUp. This permits the Fixture base class to detect failures during _setUp and trigger any registered cleanups, attach any details - to the failure exception and propogate that to callers. Overriding of + to the failure exception and propagate that to callers. Overriding of setUp is still supported: this adds a new interface for simpler implementation of the contract of a fixture. (Robert Collins, #1456361, #1456353) diff -Nru python-fixtures-1.3.1/README python-fixtures-3.0.0/README --- python-fixtures-1.3.1/README 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/README 2016-05-19 23:01:14.000000000 +0000 @@ -25,7 +25,7 @@ Dependencies ============ -* Python 2.6+ +* Python 2.6+, or 3.3+ This is the base language fixtures is written in and for. * pbr @@ -56,7 +56,7 @@ Standard Python unittest.py provides no obvious method for making and reusing state needed in a test case other than by adding a method on the test class. -This scales poorly - complex helper functions propogating up a test class +This scales poorly - complex helper functions propagating up a test class hierarchy is a regular pattern when this is done. Mocking while a great tool doesn't itself prevent this (and helpers to mock complex things can accumulate in the same way if placed on the test class). @@ -146,6 +146,14 @@ >>> server = MyServer() >>> fixture = fixtures.MethodFixture(server, server.start, server.stop) +You can also combine existing fixtures using ``CompoundFixture``:: + + >>> noddy_with_log = fixtures.CompoundFixture([NoddyFixture(), + ... WithLog()]) + >>> with noddy_with_log as x: + ... print (x.fixtures[0].frobnozzle) + 42 + The Fixture API =============== @@ -230,7 +238,7 @@ class implementation of ``setUp`` acts to reduce the chance of leaking external resources if an error is raised from ``_setUp``. Specifically, ``setUp`` contains a try:/except: block which catches all exceptions, captures -any registered detail objects, and calls ``self.cleanUp`` before propogating +any registered detail objects, and calls ``self.cleanUp`` before propagating the error. As long as you take care to register any cleanups before calling the code that may fail, this will cause them to be cleaned up. The captured detail objects are provided to the args of the raised exception. @@ -385,7 +393,7 @@ Adapts ``mock.patch.multiple`` to be used as a Fixture. - >>> fixture = fixtures.MockPatch('subprocess.Popen', returncode=3) + >>> fixture = fixtures.MockPatchMultiple('subprocess.Popen', returncode=3) MonkeyPatch +++++++++++ @@ -396,6 +404,9 @@ ... pass >>> fixture = fixtures.MonkeyPatch('__builtin__.open', fake_open) +Note that there are some complexities when patching methods - please see the +API documentation for details. + NestedTempfile ++++++++++++++ diff -Nru python-fixtures-1.3.1/setup.cfg python-fixtures-3.0.0/setup.cfg --- python-fixtures-1.3.1/setup.cfg 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/setup.cfg 2016-05-19 23:01:14.000000000 +0000 @@ -23,3 +23,9 @@ [bdist_wheel] universal = 1 + +[extras] +test = + mock +docs = + docutils diff -Nru python-fixtures-1.3.1/setup.py python-fixtures-3.0.0/setup.py --- python-fixtures-1.3.1/setup.py 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/setup.py 2016-05-19 23:01:14.000000000 +0000 @@ -3,5 +3,5 @@ import setuptools setuptools.setup( - setup_requires=['pbr'], + setup_requires=['pbr>0.11'], pbr=True) diff -Nru python-fixtures-1.3.1/test-requirements.txt python-fixtures-3.0.0/test-requirements.txt --- python-fixtures-1.3.1/test-requirements.txt 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/test-requirements.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -mock;python_version<'3.3' diff -Nru python-fixtures-1.3.1/tox.ini python-fixtures-3.0.0/tox.ini --- python-fixtures-1.3.1/tox.ini 2015-06-30 02:21:22.000000000 +0000 +++ python-fixtures-3.0.0/tox.ini 2016-05-19 23:01:14.000000000 +0000 @@ -1,5 +1,5 @@ [tox] -envlist = py27 +envlist = py26,py27,py32,py33,py34,py35,pypy,pypy3 minversion = 1.6 skipsdist = True @@ -8,5 +8,5 @@ install_command = pip install -U {opts} {packages} setenv = VIRTUAL_ENV={envdir} whitelist_externals = make -deps = -r{toxinidir}/requirements.txt +deps = .[docs,test] commands = make check diff -Nru python-fixtures-1.3.1/.travis.yml python-fixtures-3.0.0/.travis.yml --- python-fixtures-1.3.1/.travis.yml 1970-01-01 00:00:00.000000000 +0000 +++ python-fixtures-3.0.0/.travis.yml 2016-05-19 23:01:14.000000000 +0000 @@ -0,0 +1,23 @@ +sudo: false +language: python + +python: + - "2.6" + - "2.7" + - "3.3" + - "3.4" + - "3.5" + - "pypy" + - pypy3 + - "nightly" + +install: + - pip install -U pip + - pip install -U wheel setuptools pbr + - pip install -U .[docs,test] + - pip list + - python --version + +script: + - make check + - rst2html.py README README.html