From 656319e58d80478c7aa2a783606f0b50053529ae Mon Sep 17 00:00:00 2001 From: Michael Foord Date: Fri, 13 Apr 2012 17:39:16 +0100 Subject: [PATCH] Make unittest.mock.create_autospec resilient against AttributeError on original object --- Lib/unittest/mock.py | 8 ++++++-- Lib/unittest/test/testmock/testhelpers.py | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index ec175426694..04eba918cc4 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2044,10 +2044,14 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, # 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 on being fetched + # 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 - original = getattr(spec, entry) + try: + original = getattr(spec, entry) + except AttributeError: + continue kwargs = {'spec': original} if spec_set: diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index a2ed1003071..4c43f87dbcd 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -651,6 +651,29 @@ class SpecSignatureTest(unittest.TestCase): mock.f.assert_called_with(3, 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):