summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO30
-rw-r--r--functional_tests/test_plugins.py2
-rw-r--r--nose/case.py38
-rw-r--r--nose/plugins/builtin.py45
-rw-r--r--nose/plugins/doctests.py106
-rw-r--r--nose/proxy.py2
-rw-r--r--unit_tests/test_plugins.py4
7 files changed, 105 insertions, 122 deletions
diff --git a/TODO b/TODO
index 3764a68..b411330 100644
--- a/TODO
+++ b/TODO
@@ -11,12 +11,22 @@ DOCUMENTATION
FEATURES
-doctest: must return wrapped suite
+doctest:
+ -- can't use DocTestCase/DocTestSuite/DocFileSuite -- the cases
+ returned don't implement things correctly. Need to subclass DocTestCase
+ locally and return instances of that subclass. May have to do something
+ icky like call DocTestSuite and then pull the ._dt_test out of the cases?
+
+ - good thing: might make it possible to support file tests in 2.3
new plugin:
testid
-- needs tests with doctests
- -- needs to be able to handle cases w/out address() method
+ -- change the command line parsing to look for just digits -- # is a shell
+ meta character that causes parsing to stop othe options:
+ * digit-dot: 3.
+ * dash digit -3
+ * -id digit -id 3
rename Failure and split into subclasses for Import and other, and make
it optionally include the name of the file being considered so that
@@ -43,18 +53,6 @@ OK (skipped=2)
BUGS
-BIG PROBLEM
-
-given an input like:
-
-python nose/core.py functional_tests/support/idp/example.py:add_one --with-doctest
-
-the natural expectation is that the target function will be loaded as
-a DOCTEST not as a regular test. Doctest probably needs to implement
-loadTestsFromName? or makeTest? -- maybe plugins need FIRST crack at
-makeTest? One way or another, the doctest plugin has to be allowed to
-examine the proposed test object first and turn it into a test if it
-wants to.
-- when run vs spine suite, makeTest seemingly was called on an object
@@ -105,7 +103,9 @@ PROFILE
need to profile -- on 250 tests with discovery, 0.10-dev is ~ 1 second
slower than 0.9. Profile and optimize.
-
+one possiblity -- instead of instantiating a result proxy for every
+test, tag each test with a weakref to the nose.case.Test that wraps
+it.
older notes:
diff --git a/functional_tests/test_plugins.py b/functional_tests/test_plugins.py
index 42b0476..1d86b03 100644
--- a/functional_tests/test_plugins.py
+++ b/functional_tests/test_plugins.py
@@ -33,7 +33,7 @@ class TestPluginCalls(unittest.TestCase):
'prepareTestRunner', 'prepareTest', 'setOutputStream',
'prepareTestResult', 'beforeDirectory', 'wantFile',
'wantDirectory', 'beforeImport', 'afterImport', 'wantModule',
- 'wantClass', 'wantFunction', 'wantMethod',
+ 'wantClass', 'wantFunction', 'makeTest', 'wantMethod',
'loadTestsFromTestClass', 'loadTestsFromModule',
'beforeTest', 'prepareTestCase', 'startTest', 'addSuccess',
'stopTest', 'afterTest', 'loadTestsFromDir', 'afterDirectory',
diff --git a/nose/case.py b/nose/case.py
index f2135f2..df0d21c 100644
--- a/nose/case.py
+++ b/nose/case.py
@@ -373,41 +373,3 @@ class MethodTestCase(TestBase):
return self.descriptor, self.arg
else:
return self.method, self.arg
-
-
-class TestWrapper(object):
- """A proxy for test cases constructed by plugins. Subclass this
- class to provide compatible wrappers for your test
- cases. Instances of this class (or subclasses) act as proxies to
- test cases, adding two attributes: an address() method that may be
- used to return the test address of the object made into a test
- (which you may have to override) and a _nose_case property that
- the nose ResultProxy can use for sanity checking to be sure that
- the case it sees belongs to the nose.case.Test to which it is
- bound."""
- __test__ = False # do not collect
- _nose_case = None
- _nose_obj = None
-
- def __init__(self, case, obj=None):
- self.__dict__['_nose_case'] = case
- self.__dict__['_nose_obj'] = obj
-
- def address(self):
- if self.__dict__['_nose_obj'] is not None:
- return test_address(self.__dict__['_nose_obj'])
-
- def __getattr__(self, attr):
- return getattr(self.__dict__['_nose_case'], attr)
-
- def __setattr__(self, attr, val):
- setattr(self.__dict__['_nose_case'], attr, val)
-
- def __call__(self, *arg, **kw):
- return self.__dict__['_nose_case'](*arg, **kw)
-
- def __str__(self):
- return str(self.__dict__['_nose_case'])
-
- def __repr__(self):
- return repr(self.__dict__['_nose_case'])
diff --git a/nose/plugins/builtin.py b/nose/plugins/builtin.py
index 195b3b1..d0b6421 100644
--- a/nose/plugins/builtin.py
+++ b/nose/plugins/builtin.py
@@ -1,27 +1,26 @@
"""
Lists builtin plugins
"""
+plugins = []
+builtins = (
+ ('nose.plugins.attrib', 'AttributeSelector'),
+ ('nose.plugins.capture', 'Capture'),
+ ('nose.plugins.cover', 'Coverage'),
+ ('nose.plugins.debug', 'Pdb'),
+ ('nose.plugins.deprecated', 'Deprecated'),
+ ('nose.plugins.doctests', 'Doctest'),
+ ## ('nose.plugins.isolation', 'Isolation'),
+ ('nose.plugins.failuredetail', 'FailureDetail'),
+ ('nose.plugins.prof', 'Profile'),
+ ('nose.plugins.skip', 'Skip'),
+ ('nose.plugins.testid', 'TestId')
+ )
-from nose.plugins.attrib import AttributeSelector
-from nose.plugins.capture import Capture
-from nose.plugins.cover import Coverage
-from nose.plugins.debug import Pdb
-from nose.plugins.deprecated import Deprecated
-from nose.plugins.doctests import Doctest
-## from nose.plugins.isolation import
-from nose.plugins.failuredetail import FailureDetail
-from nose.plugins.prof import Profile
-from nose.plugins.skip import Skip
-from nose.plugins.testid import TestId
-
-plugins = [
- AttributeSelector,
- Capture,
- Coverage,
- Deprecated,
- Skip,
- Doctest,
- FailureDetail,
- Pdb,
- Profile,
- TestId]
+for module, cls in builtins:
+ try:
+ plugmod = __import__(module, globals(), locals(), [cls])
+ except ImportError:
+ continue
+ plug = getattr(plugmod, cls)
+ plugins.append(plug)
+ globals()[cls] = plug
diff --git a/nose/plugins/doctests.py b/nose/plugins/doctests.py
index 847e5b0..a5f1019 100644
--- a/nose/plugins/doctests.py
+++ b/nose/plugins/doctests.py
@@ -18,9 +18,9 @@ import doctest
import logging
import os
import sys
-from nose.case import TestWrapper
+from inspect import getmodule
from nose.plugins.base import Plugin
-from nose.util import anyp, test_address, resolve_name, tolist
+from nose.util import anyp, getpackage, test_address, resolve_name, tolist
log = logging.getLogger(__name__)
@@ -72,50 +72,44 @@ class Doctest(Plugin):
if not self.matches(module.__name__):
log.debug("Doctest doesn't want module %s", module)
return
- try:
- doctests = doctest.DocTestSuite(module)
- except ValueError:
- log.debug("No doctests in %s", module)
+ tests = self.finder.find(module)
+ if not tests:
return
- else:
- return self.makeTests(doctests)
+ tests.sort()
+ module_file = module.__file__
+ if module_file[-4:] in ('.pyc', '.pyo'):
+ module_file = module_file[:-1]
+ for test in tests:
+ if not test.examples:
+ continue
+ if not test.filename:
+ test.filename = module_file
+ yield DocTestCase(test)
def loadTestsFromFile(self, filename):
if self.extension and anyp(filename.endswith, self.extension):
+ name = os.path.basename(filename)
+ dh = open(filename)
try:
- return self.makeTests(
- doctest.DocFileSuite(filename, module_relative=False))
- except AttributeError:
- raise Exception("Doctests in files other than .py "
- "(python source) not supported in this "
- "version of doctest")
- else:
- return
-
+ doc = dh.read()
+ finally:
+ dh.close()
+ parser = doctest.DocTestParser()
+ test = parser.get_doctest(
+ doc, globs={}, name=name, filename=filename, lineno=0)
+ if test.examples:
+ yield DocFileCase(test)
+
def makeTest(self, obj, parent):
"""Look for doctests in the given object, which will be a
function, method or class.
"""
- try:
- # FIXME really find the module, don't assume parent
- # is a module
- doctests = self.finder.find(obj, module=parent)
- except ValueError:
- yield Failure(*sys.exc_info())
- return
+ doctests = self.finder.find(obj, module=getmodule(parent))
if doctests:
for test in doctests:
if len(test.examples) == 0:
continue
- yield DoctestWrapper(doctest.DocTestCase(test), obj)
-
-
- def makeTests(self, doctests):
- if hasattr(doctests, '__iter__'):
- doctest_suite = doctests
- else:
- doctest_suite = doctests._tests
- return map(DoctestWrapper, doctest_suite)
+ yield DocTestCase(test, obj=obj)
def matches(self, name):
"""Doctest wants only non-test modules in general.
@@ -148,21 +142,49 @@ class Doctest(Plugin):
return None
-class DoctestWrapper(TestWrapper):
+class DocTestCase(doctest.DocTestCase):
"""Proxy for DocTestCase: provides an address() method that
returns the correct address for the doctest case. Otherwise
acts as a proxy to the test case. To provide hints for address(),
an obj may also be passed -- this will be used as the test object
for purposes of determining the test address, if it is provided.
"""
+ def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
+ checker=None, obj=None):
+ self._nose_obj = obj
+ super(DocTestCase, self).__init__(
+ test, optionflags=optionflags, setUp=None, tearDown=None,
+ checker=None)
+
def address(self):
- adr = super(DoctestWrapper, self).address()
- if adr is None:
- adr = test_address(
- resolve_name(self.__dict__['_nose_case']._dt_test.name))
- return adr
-
- # FIXME
+ if self._nose_obj is not None:
+ return test_address(self._nose_obj)
+ return test_address(resolve_name(self._dt_test.name))
+
# Annoyingly, doctests loaded via find(obj) omit the module name
- # so we need to override id, __str__ and shortDescription
+ # so we need to override id, __repr__ and shortDescription
# bonus: this will squash a 2.3 vs 2.4 incompatiblity
+ def id(self):
+ name = self._dt_test.name
+ filename = self._dt_test.filename
+ if filename is not None:
+ pk = getpackage(filename)
+ if not name.startswith(pk):
+ name = "%s.%s" % (pk, name)
+ return name
+
+ def __repr__(self):
+ name = self.id()
+ name = name.split('.')
+ return "%s (%s)" % (name[-1], '.'.join(name[:-1]))
+ __str__ = __repr__
+
+ def shortDescription(self):
+ return 'Doctest: %s' % self.id()
+
+
+class DocFileCase(doctest.DocFileCase):
+ """Overrides to provide filename
+ """
+ def address(self):
+ return (self._dt_test_filename, None, None)
diff --git a/nose/proxy.py b/nose/proxy.py
index 7b8f9c5..ade3cfe 100644
--- a/nose/proxy.py
+++ b/nose/proxy.py
@@ -88,7 +88,7 @@ class ResultProxy(object):
case = getattr(self.test, 'test', None)
assert (test is self.test
or test is case
- or test is getattr(case, 'case', None),
+ or test is getattr(case, '_nose_case', None),
"ResultProxy for %r (%s) was called with test %r (%s)"
% (self.test, id(self.test), test, id(test)))
diff --git a/unit_tests/test_plugins.py b/unit_tests/test_plugins.py
index c711c1b..3999819 100644
--- a/unit_tests/test_plugins.py
+++ b/unit_tests/test_plugins.py
@@ -205,8 +205,8 @@ class TestDoctestPlugin(unittest.TestCase):
here = os.path.abspath(os.path.dirname(__file__))
support = os.path.join(here, 'support')
plug = Doctest()
- suite = plug.loadTestsFromFile(os.path.join(support, 'foo'))
- assert not suite
+ for test in plug.loadTestsFromFile(os.path.join(support, 'foo')):
+ self.fail("Expected no tests, got %s" % test)
class TestAttribPlugin(unittest.TestCase):