summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Pellerin <jpellerin@gmail.com>2009-02-04 03:03:03 +0000
committerJason Pellerin <jpellerin@gmail.com>2009-02-04 03:03:03 +0000
commit44eee6ca0607353257867c252653bdd25a9feadc (patch)
treeced47a54c2bc624b4d9e41ee097e7a5bdb0a5cac
parente7c9adce0db6a6facc0c90b99a7ed040677f1bae (diff)
downloadnose-44eee6ca0607353257867c252653bdd25a9feadc.tar.gz
Applied patch making namespace package path traversal optional
-rw-r--r--functional_tests/support/empty/.hidden0
-rw-r--r--functional_tests/test_namespace_pkg.py27
-rw-r--r--nose/config.py7
-rw-r--r--nose/loader.py17
-rw-r--r--nose/plugins/base.py4
-rw-r--r--nose/plugins/manager.py5
6 files changed, 46 insertions, 14 deletions
diff --git a/functional_tests/support/empty/.hidden b/functional_tests/support/empty/.hidden
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/functional_tests/support/empty/.hidden
diff --git a/functional_tests/test_namespace_pkg.py b/functional_tests/test_namespace_pkg.py
index 8e29c1a..ba366b3 100644
--- a/functional_tests/test_namespace_pkg.py
+++ b/functional_tests/test_namespace_pkg.py
@@ -8,7 +8,7 @@ from test_program import TestRunner
here = os.path.dirname(__file__)
support = os.path.join(here, 'support')
-class TestTestProgram(unittest.TestCase):
+class TestNamespacePackages(unittest.TestCase):
def setUp(self):
self.cwd = os.getcwd()
@@ -21,12 +21,8 @@ class TestTestProgram(unittest.TestCase):
sys.path = self.orig_path
os.chdir(self.cwd)
- def test_run_support_ctx(self):
- """Collect and run tests in functional_tests/support/ctx
-
- This should collect no tests in the default configuration, since
- none of the modules have test-like names.
- """
+ def test_namespace_pkg(self):
+ """Ensure namespace packages work/can import from each other"""
stream = StringIO()
runner = TestRunner(stream=stream)
runner.verbosity = 2
@@ -40,6 +36,23 @@ class TestTestProgram(unittest.TestCase):
assert not res.errors
assert not res.failures
+ def test_no_traverse_namespace(self):
+ """Ensure the --no-traverse-namespace option only tests the specified
+ module, not its other namespace package sibling.
+ """
+ stream = StringIO()
+ runner = TestRunner(stream=stream)
+ runner.verbosity = 2
+ prog = TestProgram(argv=['', '--no-traverse-namespace'],
+ testRunner=runner,
+ exit=False)
+ res = runner.result
+ self.assertEqual(res.testsRun, 1,
+ "Expected to run 1 tests, ran %s" % res.testsRun)
+ assert res.wasSuccessful()
+ assert not res.errors
+ assert not res.failures
+
if __name__ == '__main__':
unittest.main()
diff --git a/nose/config.py b/nose/config.py
index 90bd5a9..a3a752d 100644
--- a/nose/config.py
+++ b/nose/config.py
@@ -203,6 +203,7 @@ class Config(object):
self.verbosity = int(env.get('NOSE_VERBOSE', 1))
self.where = ()
self.workingDir = os.getcwd()
+ self.traverseNamespace = True
self._default = self.__dict__.copy()
self.update(kw)
@@ -270,6 +271,7 @@ class Config(object):
self.stopOnError = options.stopOnError
self.verbosity = options.verbosity
self.includeExe = options.includeExe
+ self.traverseNamespace = options.traverseNamespace
self.debug = options.debug
self.debugLog = options.debugLog
self.loggingConfig = options.loggingConfig
@@ -479,6 +481,11 @@ class Config(object):
help="DO NOT look for tests in python modules that are "
"executable. (The default on the windows platform is to "
"do so.)")
+ parser.add_option(
+ "--no-traverse-namespace", action="store_false",
+ default=self.traverseNamespace, dest="traverseNamespace",
+ help="DO NOT traverse through all path entries of a "
+ "namespace package")
self.plugins.loadPlugins()
self.pluginOpts(parser)
diff --git a/nose/loader.py b/nose/loader.py
index 15544be..6a3874e 100644
--- a/nose/loader.py
+++ b/nose/loader.py
@@ -260,7 +260,7 @@ class TestLoader(unittest.TestLoader):
"%s is not a function or method" % test_func)
return self.suiteClass(generate, context=generator, can_split=False)
- def loadTestsFromModule(self, module, discovered=False):
+ def loadTestsFromModule(self, module, path=None, discovered=False):
"""Load all tests from module and return a suite containing
them. If the module has been discovered and is not test-like,
the suite will be empty by default, though plugins may add
@@ -290,11 +290,15 @@ class TestLoader(unittest.TestLoader):
# Now, descend into packages
# FIXME can or should this be lazy?
# is this syntax 2.2 compatible?
- paths = getattr(module, '__path__', [])
- for path in paths:
- tests.extend(self.loadTestsFromDir(path))
+ module_paths = getattr(module, '__path__', [])
+ if path:
+ path = os.path.realpath(path)
+ for module_path in module_paths:
+ if (self.config.traverseNamespace or not path) or \
+ os.path.realpath(module_path).startswith(path):
+ tests.extend(self.loadTestsFromDir(module_path))
- for test in self.config.plugins.loadTestsFromModule(module):
+ for test in self.config.plugins.loadTestsFromModule(module, path):
tests.append(test)
return self.suiteClass(ContextList(tests, context=module))
@@ -368,7 +372,8 @@ class TestLoader(unittest.TestLoader):
return self.loadTestsFromName(addr.call, module)
else:
return self.loadTestsFromModule(
- module, discovered=discovered)
+ module, addr.filename,
+ discovered=discovered)
elif addr.filename:
path = addr.filename
if addr.call:
diff --git a/nose/plugins/base.py b/nose/plugins/base.py
index 7f6309f..dbab752 100644
--- a/nose/plugins/base.py
+++ b/nose/plugins/base.py
@@ -524,7 +524,7 @@ class IPluginInterface(object):
loadTestsFromDir.generative = True
loadTestsFromDir._new = True
- def loadTestsFromModule(self, module):
+ def loadTestsFromModule(self, module, path=None):
"""Return iterable of tests in a module. May be a
generator. Each item returned must be a runnable
unittest.TestCase (or subclass) instance.
@@ -534,6 +534,8 @@ class IPluginInterface(object):
:Parameters:
module : python module
The module object
+ path : the path of the module to search, to
+ distinguish from namespace package modules
"""
pass
loadTestsFromModule.generative = True
diff --git a/nose/plugins/manager.py b/nose/plugins/manager.py
index 7cf6918..31a9571 100644
--- a/nose/plugins/manager.py
+++ b/nose/plugins/manager.py
@@ -44,6 +44,7 @@ you wish to make available. Make sure to call
other than ``PluginManager``.
"""
+import inspect
import logging
import os
import sys
@@ -86,6 +87,10 @@ class PluginProxy(object):
"""
meth = getattr(plugin, call, None)
if meth is not None:
+ if call == 'loadTestsFromModule' and \
+ len(inspect.getargspec(meth)[0]) == 2:
+ orig_meth = meth
+ meth = lambda module, path, **kwargs: orig_meth(module)
self.plugins.append((plugin, meth))
def makeCall(self, call):