Skip to content
Browse files

First attempt at making venusian work on Python 3, Passes 12/13 tests.

Removed code not in use in advice.py, only kept getFrameInfo. Also did
clean up of unittests for advice.py. Added Python 3 to tox and standard
porting changes.
  • Loading branch information...
1 parent 535e180 commit fcbd34b7477d483b046402b5fecfb809d6716d81 @jbohman committed Jul 25, 2011
Showing with 10 additions and 234 deletions.
  1. +1 −1 tox.ini
  2. +1 −1 venusian/__init__.py
  3. +0 −127 venusian/advice.py
  4. +3 −2 venusian/compat/pkgutil_26.py
  5. +1 −87 venusian/tests/test_advice.py
  6. +4 −16 venusian/tests/test_venusian.py
View
2 tox.ini
@@ -1,6 +1,6 @@
[tox]
envlist =
- py24,py25,py26,py27,jython,pypy,cover
+ py24,py25,py26,py27,py32,jython,pypy,cover
[testenv]
commands =
View
2 venusian/__init__.py
@@ -56,7 +56,7 @@ def invoke(name, ob):
except:
return
if category_keys is None:
- category_keys = attached_categories.keys()
+ category_keys = list(attached_categories.keys())
category_keys.sort()
for category in category_keys:
callbacks = attached_categories.get(category, [])
View
127 venusian/advice.py
@@ -28,7 +28,6 @@
"""
import inspect
-from types import ClassType, FunctionType
import sys
def getFrameInfo(frame):
@@ -73,129 +72,3 @@ def getFrameInfo(frame):
# This is probably module-level code, but with a '__module__' variable.
kind = "unknown"
return kind, module, f_locals, f_globals, codeinfo
-
-
-def addClassAdvisor(callback, depth=2):
- """Set up 'callback' to be passed the containing class upon creation
-
- This function is designed to be called by an "advising" function executed
- in a class suite. The "advising" function supplies a callback that it
- wishes to have executed when the containing class is created. The
- callback will be given one argument: the newly created containing class.
- The return value of the callback will be used in place of the class, so
- the callback should return the input if it does not wish to replace the
- class.
-
- The optional 'depth' argument to this function determines the number of
- frames between this function and the targeted class suite. 'depth'
- defaults to 2, since this skips this function's frame and one calling
- function frame. If you use this function from a function called directly
- in the class suite, the default will be correct, otherwise you will need
- to determine the correct depth yourself.
-
- This function works by installing a special class factory function in
- place of the '__metaclass__' of the containing class. Therefore, only
- callbacks *after* the last '__metaclass__' assignment in the containing
- class will be executed. Be sure that classes using "advising" functions
- declare any '__metaclass__' *first*, to ensure all callbacks are run."""
-
- frame = sys._getframe(depth)
- kind, module, caller_locals, caller_globals, codeinfo = getFrameInfo(frame)
-
- # This causes a problem when zope interfaces are used from doctest.
- # In these cases, kind == "exec".
- #
- #if kind != "class":
- # raise SyntaxError(
- # "Advice must be in the body of a class statement"
- # )
-
- previousMetaclass = caller_locals.get('__metaclass__')
- defaultMetaclass = caller_globals.get('__metaclass__', ClassType)
-
-
- def advise(name, bases, cdict):
-
- if '__metaclass__' in cdict:
- del cdict['__metaclass__']
-
- if previousMetaclass is None:
- if bases:
- # find best metaclass or use global __metaclass__ if no bases
- meta = determineMetaclass(bases)
- else:
- meta = defaultMetaclass
-
- elif isClassAdvisor(previousMetaclass):
- # special case: we can't compute the "true" metaclass here,
- # so we need to invoke the previous metaclass and let it
- # figure it out for us (and apply its own advice in the process)
- meta = previousMetaclass
-
- else:
- meta = determineMetaclass(bases, previousMetaclass)
-
- newClass = meta(name,bases,cdict)
-
- # this lets the callback replace the class completely, if it wants to
- return callback(newClass)
-
- # introspection data only, not used by inner function
- advise.previousMetaclass = previousMetaclass
- advise.callback = callback
-
- # install the advisor
- caller_locals['__metaclass__'] = advise
-
-
-def isClassAdvisor(ob):
- """True if 'ob' is a class advisor function"""
- return isinstance(ob,FunctionType) and hasattr(ob,'previousMetaclass')
-
-
-def determineMetaclass(bases, explicit_mc=None):
- """Determine metaclass from 1+ bases and optional explicit __metaclass__"""
-
- meta = [getattr(b,'__class__',type(b)) for b in bases]
-
- if explicit_mc is not None:
- # The explicit metaclass needs to be verified for compatibility
- # as well, and allowed to resolve the incompatible bases, if any
- meta.append(explicit_mc)
-
- if len(meta)==1:
- # easy case
- return meta[0]
-
- candidates = minimalBases(meta) # minimal set of metaclasses
-
- if not candidates:
- # they're all "classic" classes
- return ClassType
-
- elif len(candidates)>1:
- # We could auto-combine, but for now we won't...
- raise TypeError("Incompatible metatypes",bases)
-
- # Just one, return it
- return candidates[0]
-
-
-def minimalBases(classes):
- """Reduce a list of base classes to its ordered minimum equivalent"""
-
- classes = [c for c in classes if c is not ClassType]
- candidates = []
-
- for m in classes:
- for n in classes:
- if issubclass(n,m) and m is not n:
- break
- else:
- # m has no subclasses in 'classes'
- if m in candidates:
- candidates.remove(m) # ensure that we're later in the list
- candidates.append(m)
-
- return candidates
-
View
5 venusian/compat/pkgutil_26.py
@@ -534,9 +534,10 @@ def extend_path(path, name):
if os.path.isfile(pkgfile):
try:
f = open(pkgfile)
- except IOError, msg:
+ except IOError:
+ msg = sys.exc_info()[1]
sys.stderr.write("Can't open %s: %s\n" %
- (pkgfile, msg))
+ (pkgfile, msg.args[0]))
else:
for line in f:
line = line.rstrip('\n')
View
88 venusian/tests/test_advice.py
@@ -28,24 +28,13 @@
"""
import unittest
-from types import ClassType
import sys
from venusian import advice
-def ping(log, value):
-
- def pong(klass):
- log.append((value,klass))
- return [klass]
-
- advice.addClassAdvisor(pong)
-
class ClassicClass:
- __metaclass__ = ClassType
classLevelFrameInfo = advice.getFrameInfo(sys._getframe())
-class NewStyleClass:
- __metaclass__ = type
+class NewStyleClass(object):
classLevelFrameInfo = advice.getFrameInfo(sys._getframe())
moduleLevelFrameInfo = advice.getFrameInfo(sys._getframe())
@@ -88,78 +77,3 @@ def testCallInfo(self):
for d in module.__dict__, f_globals:
self.assert_(d is globals())
self.assertEqual(len(codeinfo), 4)
-
-
-class AdviceTests(unittest.TestCase):
-
- def testOrder(self):
- log = []
- class Foo(object):
- ping(log, 1)
- ping(log, 2)
- ping(log, 3)
-
- # Strip the list nesting
- for i in 1,2,3:
- self.assert_(isinstance(Foo, list))
- Foo, = Foo
-
- self.assertEquals(log, [(1, Foo), (2, [Foo]), (3, [[Foo]])])
-
- def testDoubleType(self): # pragma: no cover
- if sys.hexversion >= 0x02030000:
- return # you can't duplicate bases in 2.3
- class aType(type,type):
- ping([],1)
- aType, = aType
- self.assert_(aType.__class__ is type)
-
- def testSingleExplicitMeta(self):
-
- class M(type):
- pass
-
- class C(M):
- __metaclass__ = M
- ping([],1)
-
- C, = C
- self.assert_(C.__class__ is M)
-
-
- def testMixedMetas(self):
-
- class M1(type): pass
- class M2(type): pass
-
- class B1: __metaclass__ = M1
- class B2: __metaclass__ = M2
-
- try:
- class C(B1,B2):
- ping([],1)
- except TypeError:
- pass
- else: # pragma: no cover
- raise AssertionError("Should have gotten incompatibility error")
-
- class M3(M1,M2): pass
-
- class C(B1,B2):
- __metaclass__ = M3
- ping([],1)
-
- self.assert_(isinstance(C,list))
- C, = C
- self.assert_(isinstance(C,M3))
-
- def testMetaOfClass(self):
-
- class metameta(type):
- pass
-
- class meta(type):
- __metaclass__ = metameta
-
- self.assertEquals(advice.determineMetaclass((meta, type)), metameta)
-
View
20 venusian/tests/test_venusian.py
@@ -19,10 +19,7 @@ def test_package(self):
scanner = self._makeOne(test=test)
scanner.scan(one)
self.assertEqual(len(test.registrations), 6)
- test.registrations.sort(
- lambda x, y: cmp((x['name'], x['ob'].__module__),
- (y['name'], y['ob'].__module__))
- )
+ test.registrations.sort(key=lambda x: (x['name'], x['ob'].__module__))
from venusian.tests.fixtures.one.module import function as func1
from venusian.tests.fixtures.one.module2 import function as func2
from venusian.tests.fixtures.one.module import inst as inst1
@@ -63,10 +60,7 @@ def test_package_with_orphaned_pyc_file(self):
scanner = self._makeOne(test=test)
scanner.scan(pyc)
self.assertEqual(len(test.registrations), 4)
- test.registrations.sort(
- lambda x, y: cmp((x['name'], x['ob'].__module__),
- (y['name'], y['ob'].__module__))
- )
+ test.registrations.sort(key=lambda x: (x['name'], x['ob'].__module__))
from venusian.tests.fixtures.pyc.module import function as func1
from venusian.tests.fixtures.pyc.module import inst as inst1
from venusian.tests.fixtures.pyc.module import Class as Class1
@@ -94,10 +88,7 @@ def test_module(self):
scanner = self._makeOne(test=test)
scanner.scan(module)
self.assertEqual(len(test.registrations), 3)
- test.registrations.sort(
- lambda x, y: cmp((x['name'], x['ob'].__module__),
- (y['name'], y['ob'].__module__))
- )
+ test.registrations.sort(key=lambda x: (x['name'], x['ob'].__module__))
from venusian.tests.fixtures.one.module import function as func1
from venusian.tests.fixtures.one.module import inst as inst1
from venusian.tests.fixtures.one.module import Class as Class1
@@ -167,10 +158,7 @@ def test_classdecorator(self): # pragma: no cover
test = Test()
scanner = self._makeOne(test=test)
scanner.scan(classdecorator)
- test.registrations.sort(
- lambda x, y: cmp((x['name'], x['ob'].__module__),
- (y['name'], y['ob'].__module__))
- )
+ test.registrations.sort(key=lambda x: (x['name'], x['ob'].__module__))
self.assertEqual(len(test.registrations), 2)
self.assertEqual(test.registrations[0]['name'], 'SubClass')
self.assertEqual(test.registrations[0]['ob'],

0 comments on commit fcbd34b

Please sign in to comment.
Something went wrong with that request. Please try again.