Skip to content

Commit

Permalink
Merge pull request #6 from NextThought/non-cooperative-super
Browse files Browse the repository at this point in the history
Add comments and a test to clarify that Base_getattro and friends do not need to use super()
  • Loading branch information
tseaver committed May 22, 2015
2 parents d5bb17f + e39b80a commit 096dca9
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/ExtensionClass/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ def __setattr__(self, name, value):
return type.__setattr__(self, name, value)


# Base and object are always moved to the last two positions
# in a subclasses mro, no matter how they are declared in the
# hierarchy. This means the Base_* methods effectively don't have
# to care or worry about using super(): it's always object.

def Base_getattro(self, name):
res = object.__getattribute__(self, name)
# If it's a descriptor for something besides __parent__, call it.
Expand Down
30 changes: 27 additions & 3 deletions src/ExtensionClass/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
##############################################################################

from ExtensionClass import *

from doctest import DocTestSuite
import unittest

def print_dict(d):
d = d.items()
Expand Down Expand Up @@ -888,13 +889,36 @@ def test_unbound_function_as___class_init___hook():
B
"""

class TestEffectivelyCooperativeBase(unittest.TestCase):

from doctest import DocTestSuite
import unittest
def test___getattribute__cooperative(self):
# This is similar to test_mro() but covering a specific
# application. The fact that Base and object are *always* moved
# to the end of a given class's mro means that even though
# the actual implementation of Base.__getattribute__ is non-cooperative
# (i.e., in Python, using object.__getattribute__ directly, not super()),
# it doesn't matter: everything else in the hierarchy has already been called
class YouShallNotPass(Exception):
pass

class NoAttributes(object):
def __getattribute__(self, name):
raise YouShallNotPass()

class WithBaseAndNoAttributes(Base, NoAttributes):
pass

# Even though it's declared this way...
self.assertEqual(WithBaseAndNoAttributes.__bases__, (Base, NoAttributes))
# ... the effective value puts base at the end
self.assertEqual([Base, object], WithBaseAndNoAttributes.mro()[-2:])

# Therefore, we don't get AttributeError, we get our defined exception
self.assertRaises(YouShallNotPass, getattr, WithBaseAndNoAttributes(), 'a')

def test_suite():
return unittest.TestSuite((
DocTestSuite('ExtensionClass'),
DocTestSuite(),
unittest.makeSuite(TestEffectivelyCooperativeBase),
))

0 comments on commit 096dca9

Please sign in to comment.