[pypy-svn] r7261 - in pypy/trunk/src: goal pypy/interpreter pypy/interpreter/test pypy/objspace/flow pypy/objspace/std pypy/objspace/test pypy/tool pypy/tool/test
hpk at codespeak.net
hpk at codespeak.net
Mon Nov 15 19:40:16 CET 2004
Author: hpk
Date: Mon Nov 15 19:40:09 2004
New Revision: 7261
Added:
pypy/trunk/src/pypy/tool/frozendict.py
Removed:
pypy/trunk/src/pypy/tool/newtest.py
pypy/trunk/src/pypy/tool/test/test_newtest.py
Modified:
pypy/trunk/src/goal/testcachebuild.py
pypy/trunk/src/pypy/interpreter/baseobjspace.py
pypy/trunk/src/pypy/interpreter/extmodule.py
pypy/trunk/src/pypy/interpreter/gateway.py
pypy/trunk/src/pypy/interpreter/test/test_gateway.py
pypy/trunk/src/pypy/interpreter/unittest_w.py
pypy/trunk/src/pypy/objspace/flow/objspace.py
pypy/trunk/src/pypy/objspace/std/objspace.py
pypy/trunk/src/pypy/objspace/test/test_traceobjspace.py
Log:
- reworked the "space.generalcache" into
space._typecache # cache for types
space._faketypecache # cache for faked types
space._gatewaycache # cache for gateway's interp2app/app2interp
and changed space.loadfromcache to require a third parameter
'cache' which the different users have to provide
- the testing parts of pypy (unittest_w and test_gateway.py etc.pp)
don't participate in the above caches but use their own cache
- the new goal/testcachebuild.py shows that we can run all
tests, then turn the above caches into frozendict(cache)'s
and still run all tests successfully (so no new building of
cache content takes place anymore)
- removed the unused newtest.py and related files
(some leftover from three sprints ago or so)
Modified: pypy/trunk/src/goal/testcachebuild.py
==============================================================================
--- pypy/trunk/src/goal/testcachebuild.py (original)
+++ pypy/trunk/src/goal/testcachebuild.py Mon Nov 15 19:40:09 2004
@@ -1,5 +1,6 @@
-from pypy.tool import option, autopath, testit
+from pypy.tool import option, autopath, testit
from pypy.interpreter import gateway
+from pypy.tool.frozendict import frozendict
#######################################################
def app_triggergenerator():
@@ -19,5 +20,9 @@
space = option.objspace('std')
#triggercachebuild(space)
testit.main(autopath.pypydir)
- space.allowbuildcache = False
+
+ space._typecache = frozendict(space._typecache)
+ space._faketypecache = frozendict(space._faketypecache)
+ space._gatewaycache = frozendict(space._gatewaycache)
+
testit.main(autopath.pypydir)
Modified: pypy/trunk/src/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/trunk/src/pypy/interpreter/baseobjspace.py (original)
+++ pypy/trunk/src/pypy/interpreter/baseobjspace.py Mon Nov 15 19:40:09 2004
@@ -2,6 +2,7 @@
from pypy.interpreter.error import OperationError
from pypy.interpreter.miscutils import getthreadlocals
from pypy.interpreter.argument import Arguments
+from pypy.tool.frozendict import frozendict
__all__ = ['ObjSpace', 'OperationError', 'Wrappable', 'BaseWrappable',
'W_Root']
@@ -32,20 +33,17 @@
def __init__(self):
"Basic initialization of objects."
- self.generalcache = {}
- self.allowbuildcache = True
+ self._gatewaycache = {}
# sets all the internal descriptors
self.initialize()
- def loadfromcache(self, key, builder):
+ def loadfromcache(self, key, builder, cache):
try:
- return self.generalcache[key]
+ return cache[key]
except KeyError:
- if self.allowbuildcache:
- #print "building for key %r" % key
- return self.generalcache.setdefault(key, builder(key, self))
- raise
- loadfromcache.translated_version = lambda s, k, b: s.generalcache[k]
+ assert not isinstance(cache, frozendict)
+ #print "building for key %r" % key
+ return cache.setdefault(key, builder(key, self))
def make_builtins(self, for_builtins):
# initializing builtins may require creating a frame which in
Modified: pypy/trunk/src/pypy/interpreter/extmodule.py
==============================================================================
--- pypy/trunk/src/pypy/interpreter/extmodule.py (original)
+++ pypy/trunk/src/pypy/interpreter/extmodule.py Mon Nov 15 19:40:09 2004
@@ -55,7 +55,7 @@
('__interplevel__eval', self.interpleveleval.im_func),
('__interplevel__execfile', self.interplevelexecfile.im_func),
('__import__', self.interplevelimport.im_func)]:
- hook = gateway.interp2app(impl).get_method(self)
+ hook = gateway.interp2app_temp(impl).get_method(self)
w_name = space.wrap(name)
try:
self.__saved_hooks[name] = space.getitem(w_builtins, w_name)
Modified: pypy/trunk/src/pypy/interpreter/gateway.py
==============================================================================
--- pypy/trunk/src/pypy/interpreter/gateway.py (original)
+++ pypy/trunk/src/pypy/interpreter/gateway.py Mon Nov 15 19:40:09 2004
@@ -182,7 +182,9 @@
return self.get_function(space)
def get_function(self, space):
- return space.loadfromcache(self, Gateway.build_all_functions)
+ return space.loadfromcache(self,
+ Gateway.build_all_functions,
+ self.getcache(space))
def build_all_functions(self, space):
# the construction is supposed to be done only once in advance,
@@ -197,25 +199,31 @@
else:
# is there another Gateway in staticglobals for which we
# already have a w_globals for this space ?
+ cache = self.getcache(space)
for value in self.staticglobals.itervalues():
if isinstance(value, Gateway):
- if self in space.generalcache:
+ if value in cache:
# yes, we share its w_globals
- fn = space.generalcache[value]
+ fn = cache[value]
w_globals = fn.w_func_globals
break
else:
# no, we build all Gateways in the staticglobals now.
w_globals = build_dict(self.staticglobals, space)
- return self.build_function(space, w_globals)
+ return self._build_function(space, w_globals)
- def build_function(self, space, w_globals):
- if not space.allowbuildcache:
- return space.generalcache[self]
- defs = self.getdefaults(space) # needs to be implemented by subclass
- fn = Function(space, self.code, w_globals, defs, forcename = self.name)
- space.generalcache[self] = fn
- return fn
+ def getcache(self, space):
+ return space._gatewaycache
+
+ def _build_function(self, space, w_globals):
+ cache = self.getcache(space)
+ try:
+ return cache[self]
+ except KeyError:
+ defs = self.getdefaults(space) # needs to be implemented by subclass
+ fn = Function(space, self.code, w_globals, defs, forcename = self.name)
+ cache[self] = fn
+ return fn
def get_method(self, obj):
# to get the Gateway as a method out of an instance, we build a
@@ -289,8 +297,12 @@
def getdefaults(self, space):
return self.staticdefs
-def exportall(d):
+def exportall(d, temporary=False):
"""Publish every function from a dict."""
+ if temporary:
+ i2a = interp2app_temp
+ else:
+ i2a = interp2app
for name, obj in d.items():
if isinstance(obj, types.FunctionType):
# names starting in 'app_' are supposedly already app-level
@@ -303,7 +315,7 @@
if name.startswith('_') and not name.endswith('_'):
continue
if 'app_'+name not in d:
- d['app_'+name] = interp2app(obj, name)
+ d['app_'+name] = i2a(obj, name)
def export_values(space, dic, w_namespace):
for name, w_value in dic.items():
@@ -316,22 +328,40 @@
w_name = space.wrap(name[2:])
space.setitem(w_namespace, w_name, w_value)
-def importall(d):
+def importall(d, temporary=False):
"""Import all app_-level functions as Gateways into a dict."""
+ if temporary:
+ a2i = app2interp_temp
+ else:
+ a2i = app2interp
for name, obj in d.items():
if name.startswith('app_') and name[4:] not in d:
if isinstance(obj, types.FunctionType):
- d[name[4:]] = app2interp(obj, name[4:])
+ d[name[4:]] = a2i(obj, name[4:])
def build_dict(d, space):
"""Search all Gateways and put them into a wrapped dictionary."""
w_globals = space.newdict([])
for value in d.itervalues():
if isinstance(value, Gateway):
- fn = value.build_function(space, w_globals)
+ fn = value._build_function(space, w_globals)
w_name = space.wrap(value.name)
w_object = space.wrap(fn)
space.setitem(w_globals, w_name, w_object)
if hasattr(space, 'w_sys'): # give them 'sys' if it exists already
space.setitem(w_globals, space.wrap('sys'), space.w_sys)
return w_globals
+
+
+#
+# the next gateways are to be used only for
+# temporary/initialization purposes
+class app2interp_temp(app2interp):
+ def getcache(self, space):
+ return self.__dict__.setdefault(space, {})
+ # ^^^^^
+ # armin suggested this
+
+class interp2app_temp(interp2app):
+ def getcache(self, space):
+ return self.__dict__.setdefault(space, {})
Modified: pypy/trunk/src/pypy/interpreter/test/test_gateway.py
==============================================================================
--- pypy/trunk/src/pypy/interpreter/test/test_gateway.py (original)
+++ pypy/trunk/src/pypy/interpreter/test/test_gateway.py Mon Nov 15 19:40:09 2004
@@ -3,7 +3,6 @@
from pypy.tool import testit
from pypy.interpreter import gateway
-
class TestBuiltinCode(testit.IntTestCase):
def setUp(self):
self.space = testit.objspace()
@@ -66,7 +65,7 @@
w = self.space.wrap
def app_g3(a, b):
return a+b
- g3 = gateway.app2interp(app_g3)
+ g3 = gateway.app2interp_temp(app_g3)
self.assertEqual_w(g3(self.space, w('foo'), w('bar')), w('foobar'))
def test_interp2app(self):
@@ -74,7 +73,7 @@
w = space.wrap
def g3(space, w_a, w_b):
return space.add(w_a, w_b)
- app_g3 = gateway.interp2app(g3)
+ app_g3 = gateway.interp2app_temp(g3)
w_app_g3 = space.wrap(app_g3)
self.assertEqual_w(
space.call(w_app_g3,
@@ -94,7 +93,7 @@
def app_g1(x):
return g3('foo', x)
""" in g
- gateway.importall(g)
+ gateway.importall(g, temporary=True)
g1 = g['g1']
self.assertEqual_w(g1(self.space, w('bar')), w('foobar'))
@@ -107,8 +106,8 @@
def app_g1(x):
return g3('foo', x)
""" in g
- gateway.exportall(g)
- g1 = gateway.app2interp(g['app_g1'])
+ gateway.exportall(g, temporary=True)
+ g1 = gateway.app2interp_temp(g['app_g1'])
self.assertEqual_w(g1(self.space, w('bar')), w('foobar'))
Modified: pypy/trunk/src/pypy/interpreter/unittest_w.py
==============================================================================
--- pypy/trunk/src/pypy/interpreter/unittest_w.py (original)
+++ pypy/trunk/src/pypy/interpreter/unittest_w.py Mon Nov 15 19:40:09 2004
@@ -12,7 +12,6 @@
IGNORE_TESTS = [s.strip() for s in f.readlines()]
f.close()
-
def make_testcase_class(space, tc_w):
# XXX this is all a bit insane (but it works)
@@ -22,7 +21,7 @@
for name in dir(AppTestCase):
if ( name.startswith('assert') or name.startswith('fail')
and name != 'failureException'):
- fn = gateway.app2interp(getattr(tc_w, name).im_func, name)
+ fn = gateway.app2interp_temp(getattr(tc_w, name).im_func, name)
space.setitem(w_dict, w(name), w(fn))
# space-dependent part: make an object-space-level dictionary
@@ -52,7 +51,7 @@
setattr(space, w_tc_attr, w_tc)
f = self.testMethod.im_func
- gway = gateway.app2interp(f, f.func_name)
+ gway = gateway.app2interp_temp(f, f.func_name)
gway(space, w_tc)
Modified: pypy/trunk/src/pypy/objspace/flow/objspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/flow/objspace.py (original)
+++ pypy/trunk/src/pypy/objspace/flow/objspace.py Mon Nov 15 19:40:09 2004
@@ -28,9 +28,9 @@
#self.make_builtins()
#self.make_sys()
- def loadfromcache(self, key, builder):
+ def loadfromcache(self, key, builder, cache):
try:
- return self.generalcache[key]
+ return cache[key]
except KeyError:
# this method is overloaded to allow the space to switch to
# "concrete mode" when building the object that goes into
@@ -39,7 +39,7 @@
self.executioncontext.crnt_ops = flowcontext.ConcreteNoOp()
self.concrete_mode += 1
try:
- return self.generalcache.setdefault(key, builder(key, self))
+ return cache.setdefault(key, builder(key, self))
finally:
self.executioncontext.crnt_ops = previous_ops
self.concrete_mode -= 1
Modified: pypy/trunk/src/pypy/objspace/std/objspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/std/objspace.py (original)
+++ pypy/trunk/src/pypy/objspace/std/objspace.py Mon Nov 15 19:40:09 2004
@@ -7,7 +7,6 @@
from pypy.objspace.std import stdtypedef
import types
-
class W_Object(W_Root, object):
"Parent base class for wrapped objects."
typedef = None
@@ -136,6 +135,9 @@
return done
def initialize(self):
+ self._typecache = {}
+ self._faketypecache = {}
+
# The object implementations that we want to 'link' into PyPy must be
# imported here. This registers them into the multimethod tables,
# *before* the type objects are built from these multimethod tables.
@@ -206,7 +208,9 @@
def gettypeobject(self, typedef):
# types_w maps each StdTypeDef instance to its
# unique-for-this-space W_TypeObject instance
- return self.loadfromcache(typedef, stdtypedef.buildtypeobject)
+ return self.loadfromcache(typedef,
+ stdtypedef.buildtypeobject,
+ self._typecache)
def wrap(self, x):
"Wraps the Python value 'x' into one of the wrapper classes."
@@ -251,9 +255,9 @@
if hasattr(self, 'w_' + x.__name__):
return getattr(self, 'w_' + x.__name__)
if isinstance(x, type):
- ft = self.loadfromcache(x, fake_type)
+ ft = self.loadfromcache(x, fake_type, self._faketypecache)
return self.gettypeobject(ft.typedef)
- ft = self.loadfromcache(type(x), fake_type)
+ ft = self.loadfromcache(type(x), fake_type, self._faketypecache)
return ft(self, x)
def newint(self, intval):
Modified: pypy/trunk/src/pypy/objspace/test/test_traceobjspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/test/test_traceobjspace.py (original)
+++ pypy/trunk/src/pypy/objspace/test/test_traceobjspace.py Mon Nov 15 19:40:09 2004
@@ -1,8 +1,8 @@
import autopath
from pypy.tool import testit
from pypy.objspace import trace
-from pypy.interpreter.gateway import app2interp
from pypy.tool import pydis
+from pypy.interpreter import gateway
class Test_TraceObjSpace(testit.IntTestCase):
@@ -15,7 +15,7 @@
def perform_trace(self, app_func):
tspace = self.space
- func_gw = app2interp(app_func)
+ func_gw = gateway.app2interp_temp(app_func)
func = func_gw.get_function(tspace)
tspace.settrace()
tspace.call_function(tspace.wrap(func))
Added: pypy/trunk/src/pypy/tool/frozendict.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/tool/frozendict.py Mon Nov 15 19:40:09 2004
@@ -0,0 +1,7 @@
+
+# hacks += 1
+class frozendict(dict):
+ def __setitem__(self, *args):
+ raise TypeError, "this dict is already frozen, you are too late!"
+ __delitem__ = setdefault = update = pop = popitem = clear = __setitem__
+
Deleted: /pypy/trunk/src/pypy/tool/newtest.py
==============================================================================
--- /pypy/trunk/src/pypy/tool/newtest.py Mon Nov 15 19:40:09 2004
+++ (empty file)
@@ -1,672 +0,0 @@
-"""
-Unit testing framework for PyPy.
-
-The following picture is an UML class diagram of the framework.
-
- +----------+ 1 1 +------------+
- | TestItem |------------------------| TestResult |
- +----------+ | (abstract) |
- | * +------------+
- | A
- | -
- | 1 |
- +-----------+ +-----------+--------+----------+
- | TestSuite | | | |
- +-----------+ +---------+ +---------+ +-------------------------+
- A | Success | | Skipped | | TestResultWithTraceback |
- | +---------+ +---------+ | (abstract) |
- | loaded by +-------------------------+
- | A A
- | * - -
- +------------+ | |
- | TestCase | | |
- | (abstract) | +-------+ +---------+
- +------------+ | Error | | Failure |
- A +-------+ +---------+
- -
- |
- |
- concrete test
- case classes
-
-Like the unittest framework of Python, our framework implements
-tests as test methods in TestCase classes. Custom test case classes
-derive from the shown TestCase class, defined in this module. As in
-Python's unittest, a test case class can contain setUp and tearDown
-methods. Additionally, it contains a method 'skip' which can be
-called to stop a test prematurely. This won't be counted as a failure
-or error.
-
-Test cases are loaded by a TestSuite class via the method init_from_dir.
-This method will read all test modules in and below a specified
-directory, inspect them for classes derived from TestCase (i. e. _our_
-TestCase), and in turn inspect them for test methods (like in
-unittest, all methods which start with "test").
-
-For every found method, TestSuite will store its module, class and
-unbound method objects in a TestItem object. There are also other
-properties stored, e. g. the source code for each method and its
-docstring.
-
-When the TestSuite's method 'run' is called, all collected TestItems
-are run and, according to the outcome of the test, a TestResult object
-is generated which holds a reference to "its" TestItem object.
-
-The TestResult classes Success and Skipped denote a passed test. A
-skipped test means that not all test code has been run (and no error
-or failure occurred before the skip method was called). If a test
-fails, resulting in a Failure object, a test, e. g. tested with
-assertEqual, has failed. An Error object is generated if something
-else causes an unforeseen exception to be raised.
-"""
-
-# for Python 2.2 compatibilty
-from __future__ import generators
-
-import autopath
-
-import cStringIO as StringIO
-import inspect
-import os
-import sys
-import traceback
-import types
-from std import path
-
-#TODO
-# - add support for ignored tests (do we need to differentiate between
-# skipped and ignored tests at all?)
-# - support TestItem.run with different object spaces
-# - unify naming of methods/functions; what about the TestCase class?
-# - support for pickling and retrieving TestItems and TestResults?
-
-#
-# custom TestCase class (adapted from Python's unittest module)
-#
-class TestCase:
- """A class whose instances are single test cases.
-
- By default, the test code itself should be placed in a method named
- 'runTest'.
-
- If the fixture may be used for many test cases, create as
- many test methods as are needed. When instantiating such a TestCase
- subclass, specify in the constructor arguments the name of the test method
- that the instance is to execute.
-
- Test authors should subclass TestCase for their own tests. Construction
- and deconstruction of the test's environment ('fixture') can be
- implemented by overriding the 'setUp' and 'tearDown' methods respectively.
- """
- def setUp(self):
- "Hook method for setting up the test fixture before exercising it."
- pass
-
- def tearDown(self):
- "Hook method for deconstructing the test fixture after testing it."
- pass
-
- def skip(self, msg=None):
- """Skip this test by raising exception Skipped."""
- raise Skipped(msg=msg)
-
- def fail(self, msg=None):
- """Fail immediately, with the given message."""
- raise Failure(msg=msg)
-
- def failIf(self, expr, msg=None):
- """Fail the test if the expression is true."""
- if expr:
- raise Failure(msg=msg)
-
- def failUnless(self, expr, msg=None):
- """Fail the test unless the expression is true."""
- if not expr:
- raise Failure(msg=msg)
-
- def failUnlessRaises(self, excClass, callableObj, *args, **kwargs):
- """
- Fail unless an exception of class excClass is thrown
- by callableObj when invoked with arguments args and keyword
- arguments kwargs. If a different type of exception is
- thrown, it will not be caught, and the test case will be
- deemed to have suffered an error, exactly as for an
- unexpected exception.
- """
- try:
- callableObj(*args, **kwargs)
- except excClass:
- return
- else:
- if hasattr(excClass,'__name__'):
- excName = excClass.__name__
- else:
- excName = str(excClass)
- raise Failure(msg=excName)
-
- def failUnlessEqual(self, first, second, msg=None):
- """
- Fail if the two objects are unequal as determined by the '=='
- operator.
- """
- if not first == second:
- raise Failure(msg=(msg or '%s != %s' % (`first`, `second`)))
-
- def failIfEqual(self, first, second, msg=None):
- """
- Fail if the two objects are equal as determined by the '=='
- operator.
- """
- if first == second:
- raise Failure(msg=(msg or '%s == %s' % (`first`, `second`)))
-
- def failUnlessAlmostEqual(self, first, second, places=7, msg=None):
- """
- Fail if the two objects are unequal as determined by their
- difference rounded to the given number of decimal places
- (default 7) and comparing to zero.
-
- Note that decimal places (from zero) is usually not the same
- as significant digits (measured from the most signficant digit).
- """
- if round(second-first, places) != 0:
- raise Failure(msg=(msg or '%s != %s within %s places' %
- (`first`, `second`, `places`)))
-
- def failIfAlmostEqual(self, first, second, places=7, msg=None):
- """
- Fail if the two objects are equal as determined by their
- difference rounded to the given number of decimal places
- (default 7) and comparing to zero.
-
- Note that decimal places (from zero) is usually not the same
- as significant digits (measured from the most signficant digit).
- """
- if round(second-first, places) == 0:
- raise Failure(msg=(msg or '%s == %s within %s places' %
- (`first`, `second`, `places`)))
-
- # aliases
- assertEqual = assertEquals = failUnlessEqual
- assertNotEqual = assertNotEquals = failIfEqual
- assertAlmostEqual = assertAlmostEquals = failUnlessAlmostEqual
- assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual
- assertRaises = failUnlessRaises
- assert_ = failUnless
-
-
-# provide services from TestCase class also to test functions, e. g.
-# def test_func4711():
-# service.assertEqual(3, 3.0, msg="objects with same value should be equal")
-#XXX maybe use another name instead of 'service'
-service = TestCase()
-
-#
-# TestResult class family
-#
-class TestResult(Exception):
- """Abstract class representing the outcome of a test."""
- def __init__(self, msg="", item=None):
- Exception.__init__(self, msg)
- self.msg = msg
- self.item = item
- self.name = self.__class__.__name__
- self.traceback = None
-
- #XXX possibly, we need an attribute/method has_traceback?
-
- def __eq__(self, other):
- """
- Return True if both TestResult objects are semantically the same.
- Else, return False.
- """
- # trivial case
- if (self is other) or (self.item is other.item):
- return True
- return False
-
- def __ne__(self, other):
- return not (self == other)
-
- def __hash__(self):
- return id(self.item)
-
-class Success(TestResult):
- pass
-
-class Skipped(TestResult):
- pass
-
-class Ignored(TestResult):
- pass
-
-
-class TestResultWithTraceback(TestResult):
- def __init__(self, msg='', item=None):
- TestResult.__init__(self, msg, item)
- self.set_exception()
-
- def __eq__(self, other):
- return TestResult.__eq__(self, other) and \
- self.formatted_traceback == other.formatted_traceback
-
- def set_exception(self):
- self.excinfo = sys.exc_info()
- self.traceback = self.excinfo[2]
- # store formatted traceback
- output = StringIO.StringIO()
- args = self.excinfo + (None, output)
- traceback.print_exception(*args)
- # strip trailing newline
- self.formatted_traceback = output.getvalue().rstrip()
-
-class Error(TestResultWithTraceback):
- pass
-
-class Failure(TestResultWithTraceback):
- pass
-
-#
-# other classes
-#
-class TestItem:
- """
- Represent either a test function, or a single test method from a
- TestCase class.
- """
- def __init__(self, callable_, name=None):
- """
- Construct a test item.
-
- The argument callable_ must be a callable object, i. e. a
- plain function, a bound or unbound method of a class, or an
- object with __call__ attribute.
-
- The optional argument name can be a string which will be used
- as the object's name. Else, the name will be tried to be
- determined from the object's __name__ attribute.
- """
- assert callable(callable_), \
- "must get a callable item, but got '%s'" % callable_
- self.callable = callable_
- # determine additional attributes (e. g. for error reporting)
- self.call_via_class = False
- # - class
- if hasattr(callable_, 'im_class'):
- # unbound and bound methods; __class__ would be instancemethod,
- # not the one containing the methods
- self.cls = callable_.im_class
- if callable_.im_self is None:
- # unbound methods are not directly callable without
- # arguments
- self.call_via_class = True
- elif hasattr(callable_, '__class__') and \
- not inspect.isfunction(callable_):
- # class instances with __call__ attribute
- self.cls = callable_.__class__
- else:
- self.cls = None
- # - module (sometimes, using the class works better than the callable)
- self.module = inspect.getmodule(self.cls or callable_)
- # - name
- try:
- self.name = name or callable_.__name__
- except AttributeError:
- self.name = '<unnamed object>'
- # - full name (including module and class, if applicable)
- try:
- parts = [self.module.__name__]
- except AttributeError:
- parts = []
- if self.cls:
- parts.append(self.cls.__name__)
- parts.append(self.name)
- self.full_name = '.'.join(parts)
- # - file
- try:
- if self.module is None:
- self.file = inspect.getsourcefile(self.cls or callable_)
- else:
- self.file = inspect.getsourcefile(self.module)
- except IOError:
- self.file = '<unknown>'
- except TypeError:
- self.file = '<built-in or from __main__>'
- # - docstrings
- self.docs = (self._docstring(self.module), self._docstring(self.cls),
- self._docstring(self.callable))
- # - source code properties
- #XXX inspect.getsourcelines may fail if the file path stored
- # in a module's pyc/pyo file doesn't match the py file's
- # actual location. This can happen if a py file, together with
- # its pyc/pyo file is moved to a new location. See Python
- # bug "[570300] inspect.getmodule symlink-related failure":
- # http://sourceforge.net/tracker/index.php?func=detail&aid=570300&group_id=5470&atid=105470
- try:
- lines, self.lineno = inspect.getsourcelines(callable)
- # removing trailing newline(s) but not the indentation
- self.source = ''.join(lines).rstrip()
- except IOError:
- self.source, self.lineno = '<unknown>', None
- except TypeError:
- self.source, self.lineno = '<built-in or from __main__>', None
-
- def _docstring(self, obj):
- """
- Return the docstring of object obj or an empty string, if the
- docstring doesn't exist, i. e. is None.
- """
- if obj is None:
- return None
- return inspect.getdoc(obj) or ""
-
- def __eq__(self, other):
- """
- Return True if this and the other item compare equal. (This doesn't
- necessarily mean that they are the same object.) Else, return False.
- """
- # trivial case
- if self is other:
- return True
- # If module, cls and unbound method are the same, the files must
- # also be equal. For the methods, we compare the names, not the
- # methods themselves; see
- # http://mail.python.org/pipermail/python-list/2002-September/121655.html
- # for an explanation.
- if (self.module is other.module) and (self.cls is other.cls) and \
- (self.callable.__name__ == other.callable.__name__):
- return True
- return False
-
- def __ne__(self, other):
- return not (self == other)
-
- def __hash__(self):
- return id(self.module) ^ id(self.cls)
-
- # credit: adapted from Python's unittest.TestCase.run
- def run(self, test_runner=lambda callable: callable()):
- """
- Run this TestItem and return a corresponding TestResult object.
-
- If the test item corresponds to a class method, the setUp and
- tearDown methods of the associated class are called before and
- after the invocation of the test method, repectively.
-
- If the optional argument test_runner is absent, the test function
- or method is merely called with no arguments. Else, test_runner
- must be a callable accepting one argument. Instead of only calling
- the test callable, the test_runner object will be called with the
- test function/method as its argument.
- """
- if self.call_via_class:
- # turn the test callable, an unbound method, into a bound method
- cls_object = self.cls()
- test = getattr(cls_object, self.callable.__name__)
- else:
- # use the test function directly
- test = self.callable
-
- try:
- # call setUp only for a class
- self.call_via_class and cls_object.setUp()
- except KeyboardInterrupt:
- raise
- except TestResult, result:
- # reconstruct TestResult object, implicitly set exception
- return result.__class__(msg=result.msg, item=self)
- except Exception, exc:
- return Error(msg=str(exc), item=self)
-
- try:
- test_runner(test)
- result = Success(msg='success', item=self)
- except KeyboardInterrupt:
- raise
- except TestResult, result:
- # reconstruct TestResult object, implicitly set exception
- result = result.__class__(msg=result.msg, item=self)
- except Exception, exc:
- result = Error(msg=str(exc), item=self)
-
- try:
- # call tearDown only for a class
- self.call_via_class and cls_object.tearDown()
- except KeyboardInterrupt:
- raise
- except Exception, exc:
- # if we already had an exception in the test method,
- # don't overwrite/mask it
- if result.traceback is None:
- result = Error(msg=str(exc), item=self)
- return result
-
- def __str__(self):
- return "TestItem from %s" % self.full_name
-
- def __repr__(self):
- return "<%s at %#x>" % (str(self), id(self))
-
-
-class TestSuite:
- """Represent a collection of test items."""
- def __init__(self):
- self.reset()
-
- def reset(self):
- """
- Clear this TestSuite instance from all stored items and
- test results.
- """
- self.items = []
- self.last_results = {}
-
- #
- # get lists of TestItems from a class, a module, or a directory tree
- #
- def _module_from_modpath(self, modpath):
- """
- Return a module object derived from the module path
- (e. g. "pypy.module.builtin.test.test_minmax").
- """
- # This __import__ call is only used to ensure that the module
- # is present in sys.modules. Unfortunately, the module returned
- # from the __import__ function doesn't correspond to the last
- # component of the module path but the first. In the example
- # listed in the docstring we thus would get the pypy module
- # (i. e. package), not the test_minmax module.
- __import__(modpath)
- return sys.modules[modpath]
-
- def _add_items(self, items):
- """Add TestItems from list 'items' to TestSuite."""
- self.items.extend(items)
-
- def _items_from_class(self, cls):
- """
- Return TestItems extracted from class cls.
-
- This includes all unbound methods whose names start with
- "test_".
- """
- items = []
- for attrname in cls.__dict__:
- # don't use cls.__dict__[attrname]; for unbound methods,
- # we would get function objects, i. e.
- # cls.__dict__[attrname] != getattr(cls, attrname)
- attr = getattr(cls, attrname)
- if callable(attr) and attrname.startswith("test_"):
- items.append(TestItem(attr, attrname))
- return items
-
- def _items_from_module(self, module):
- """
- Return TestItems extracted from module.
-
- This includes all TestItems of classes derived from
- TestCase and functions whose names start with "test_".
- """
- items = []
- for attrname in module.__dict__:
- # see comment in _items_from_class
- attr = getattr(module, attrname)
- if inspect.isclass(attr) and issubclass(attr, TestCase):
- items.extend(self._items_from_class(attr))
- elif inspect.isfunction(attr) and attrname.startswith("test_"):
- items.append(TestItem(attr, attrname))
- return items
-
- def _items_from_dir(self, dirname, filterfunc=None, recursive=True):
- """
- Return a list of TestItems found by reading the directory denoted
- by dirname. Find all test modules in it. Test modules are files that
- comply with the shell pattern "test_*.py".
-
- filterfunc is a callable that can be used to filter the test
- modules by module path. By default, all test modules are used.
-
- If recursive is true, which is the default, find all test modules
- by scanning the start directory recursively.
- """
- items = []
- dirname = path.local(dirname)
-
- def testfilefilter(path):
- return path.check(file=1, fnmatch='test_*.py')
- def recfilter(path):
- return recursive and path.check(dotfile=0)
-
- for testfn in dirname.visit(testfilefilter, recfilter):
- # strip the leading pypy directory and the .py suffix
- modpath = str(testfn)[len(autopath.pypydir)+1:-3]
- modpath = 'pypy.' + modpath.replace(os.sep, '.')
- if (filterfunc is None) or filterfunc(modpath):
- try:
- module = self._module_from_modpath(modpath)
- module_items = self._items_from_module(module)
- except:
- print >> sys.stderr, \
- "Warning: can't load module %s" % modpath
- #raise
- else:
- items.extend(module_items)
- return items
-
- def add(self, *args):
- """
- Extract TestItems from the given args and add them to this
- TestSuite.
-
- The given objects can be:
- - callable (function, unbound or bound method, class instance
- with __call__ attribute): convert to TestItem
- - class: extract all test methods, i. e. whose names start
- with "test_"
- - module: extract all test classes, i. e. classes derived
- from newtest.TestCase, and all functions whose names
- start with "test_"
- - string: interpret the string as a file system path and
- search it for Python files located in (sub)directories
- named "test" and matching the shell pattern "test_*.py"
- """
- for arg in args:
- if isinstance(arg, (types.ClassType, types.TypeType)):
- self._add_items(self._items_from_class(arg))
- elif callable(arg):
- self._add_items([TestItem(arg)])
- elif isinstance(arg, types.ModuleType):
- self._add_items(self._items_from_module(arg))
- elif isinstance(arg, (str, unicode)):
- self._add_items(self._items_from_dir(arg))
- else:
- raise TypeError("unsupported TestItem source '%s'" % arg)
-
- #
- # running tests and getting results
- #
- def result_generator(self,
- classify=lambda result: result.item.module.__name__):
- """
- Return a generator to get the test result for each test item.
-
- The optional argument classify must be callable which accepts
- a TestResult instance as the argument and returns something
- that can be used as a dictionary key.
-
- During the iteration over the generator, the TestSuite object
- will contain a dictionary named lastresult which maps these
- keys to a list of TestResult objects that correspond to the key.
- """
- self.last_results = {}
- for item in self.items:
- result = item.run()
- key = classify(result)
- self.last_results.setdefault(key, []).append(result)
- yield result
-
- def run(self):
- """
- Run all the test items and return a list of the results. After
- that, the results are available via the attribute last_results.
- """
- # perform all the tests by using the existing generator; discard
- # the results; they are then available via self.last_results
- return [result for result in self.result_generator()]
-
-
-def _print_results(suite):
- """Print results for the items in a test suite."""
- # iterate over tests and collect data
- for result in suite.result_generator():
- if result.traceback is None:
- continue
- print 79 * '-'
- # print a line with the qualified name of the bad callable
- item = result.item
- print "%s: %s" % (item.full_name, result.name.upper())
- print
- print result.formatted_traceback
- # emit a summary
- print 79 * '='
- modules = suite.last_results.keys()
- modules.sort()
- for module in modules:
- results = suite.last_results[module]
- resultstring = ''
- for result in results:
- status_char = {Success: '.', Ignored: 'i', Skipped: 's',
- Error: 'E', Failure: 'F'}[result.__class__]
- resultstring += status_char
- print "%s [%d] %s" % (module, len(results), resultstring)
-
-def main():
- """
- Find all tests in the current module (i. e. the module from which this
- function is called), execute them and print results.
- """
- import __main__
- from pypy.tool import newtest
- suite = TestSuite()
- suite.add(__main__)
- _print_results(suite)
-
-def test(do_selftest=False):
- # possibly ignore dummy unit tests
- if do_selftest:
- # include only selftest module
- filterfunc = lambda m: m.find("pypy.tool.testdata.") != -1
- else:
- # exclude selftest module
- filterfunc = lambda m: m.find("pypy.tool.testdata.") == -1
- # collect tests
- suite = TestSuite()
- print "Loading test modules ..."
- suite.init_from_dir(autopath.pypydir, filterfunc=filterfunc)
- _print_results(suite)
-
-
-if __name__ == '__main__':
- # used to avoid subtle problems with class matching after different
- # import statements
- from pypy.tool import newtest
- newtest.test(do_selftest=True)
Deleted: /pypy/trunk/src/pypy/tool/test/test_newtest.py
==============================================================================
--- /pypy/trunk/src/pypy/tool/test/test_newtest.py Mon Nov 15 19:40:09 2004
+++ (empty file)
@@ -1,135 +0,0 @@
-import inspect
-import new
-import unittest
-
-import autopath
-from pypy.tool import newtest
-
-#TODO test(s) for adding TestItems from directory
-
-
-class TestTestItem(unittest.TestCase):
- def _test_function(self, func, expected_name):
- item = newtest.TestItem(func)
- self.assertEqual(item.name, expected_name)
- self.assertEqual(item.call_via_class, False)
- self.assertEqual(item.cls, None)
- self.failUnless(item.module is module)
- self.assertEqual(item.file, file)
-
- def test_plain_function(self):
- f = lambda: 'anything'
- self._test_function(f, expected_name='<lambda>')
- def f(): pass
- self._test_function(f, expected_name='f')
-
- def test_bound_method(self):
- class X:
- def f(self): pass
- x = X()
- item = newtest.TestItem(x.f)
- self.assertEqual(item.name, 'f')
- self.assertEqual(item.call_via_class, False)
- self.failUnless(item.cls is X)
- self.failUnless(item.module is module)
- self.assertEqual(item.file, file)
-
- def test_unbound_method(self):
- class X:
- def f(self): pass
- item = newtest.TestItem(X.f)
- self.assertEqual(item.name, 'f')
- self.assertEqual(item.call_via_class, True)
- self.failUnless(item.cls is X)
- self.failUnless(item.module is module)
- self.assertEqual(item.file, file)
-
- def test_class_instance(self):
- class X:
- def __call__(self): pass
- item = newtest.TestItem(X())
- self.assertEqual(item.name, '<unnamed object>')
- self.assertEqual(item.call_via_class, False)
- self.failUnless(item.cls is X)
- self.failUnless(item.module is module)
- self.assertEqual(item.file, file)
-
- #XXX best way to trigger execptions in TestItem's constructor
- # without getting rather complicated?
-
- def test_docstrings(self):
- class X:
- def f(self):
- "Method docstring"
- item = newtest.TestItem(X.f)
- self.assertEqual(item.docs, ('', '', "Method docstring"))
-
- class X:
- "Class docstring"
- def f(self): pass
- item = newtest.TestItem(X.f)
- self.assertEqual(item.docs, ('', "Class docstring", ''))
-
- def test_name_argument(self):
- def f(): pass
- item = newtest.TestItem(f, 'g')
- self.assertEqual(item.name, 'g')
-
-
-class TestTestSuite(unittest.TestCase):
- def check_names(self, test_suite, expected_item_names):
- item_names = [item.name for item in test_suite.items]
- item_names.sort()
- expected_item_names.sort()
- self.assertEqual(item_names, expected_item_names)
-
- def test_items_from_callables(self):
- def f(): pass
- g = lambda: None
- class X:
- def thisone(self): pass
- ts = newtest.TestSuite()
- ts.add(f, g, X.thisone)
- self.check_names(ts, ['f', '<lambda>', 'thisone'])
- # add a bound method and an instance with __call__ attribute
- class Y:
- def thatone(self): pass
- class Z:
- def __call__(self): pass
- ts.add(Y().thatone, Z())
- self.check_names(ts, ['f', '<lambda>', 'thisone', 'thatone',
- '<unnamed object>'])
-
- def test_items_from_class(self):
- class X:
- """Docstring - don't find it."""
- def test_this(self): pass
- def no_test(self): pass
- def test_that(self): pass
- ts = newtest.TestSuite()
- ts.add(X)
- self.check_names(ts, ['test_this', 'test_that'])
-
- def test_items_from_module(self):
- mod = new.module('new_module')
- class X(newtest.TestCase):
- "Don't add docstring."
- def dont_add_method(self): pass
- def test_this_method(self): pass
- def dont_add(): pass
- def add_this(): pass
- for name, object in [('X', X), ('dont_add', dont_add),
- ('test_other_name', add_this)]:
- setattr(mod, name, object)
- ts = newtest.TestSuite()
- ts.add(mod)
- self.check_names(ts, ['test_this_method', 'test_other_name'])
-
-
-# used in unit tests above; placed here, so that TestTestItem is accessible
-module = inspect.getmodule(TestTestItem)
-file = inspect.getsourcefile(TestTestItem)
-
-
-if __name__ == '__main__':
- unittest.main()
More information about the Pypy-commit
mailing list