Source code for nose2.plugins.loader.discovery

"""
Discovery-based test loader.

This plugin implements nose2's automatic test module discovery. It
looks for test modules in packages and directories whose names start
with 'test', then fires the :func:`loadTestsFromModule` hook for each
one to allow other plugins to load the actual tests.

It also fires :func:`handleFile` for every file that it sees, and
:func:`matchPath` for every python module, to allow other plugins to
load tests from other kinds of files and to influence which modules
are examined for tests.

"""


# Adapted from unittest2/loader.py from the unittest2 plugins branch.
# This module contains some code copied from unittest2/loader.py and other
# code developed in reference to that module and others within unittest2.
# unittest2 is Copyright (c) 2001-2010 Python Software Foundation; All
# Rights Reserved. See: http://docs.python.org/license.html

from fnmatch import fnmatch
import logging
import os
import sys

from nose2 import events, util

__unittest = True
log = logging.getLogger(__name__)


[docs]class DiscoveryLoader(events.Plugin): """Loader plugin that can discover tests""" alwaysOn = True configSection = 'discovery' def registerInSubprocess(self, event): event.pluginClasses.append(self.__class__)
[docs] def loadTestsFromName(self, event): """Load tests from module named by event.name""" # turn name into path or module name # fire appropriate hooks (handle file or load from module) if event.module: return name = event.name module = None _, top_level_dir = self._getStartDirs() try: # try name as a dotted module name first __import__(name) module = sys.modules[name] except ImportError: # if that fails, try it as a file or directory event.extraTests.extend( self._find_tests(event, name, top_level_dir)) else: event.extraTests.extend( self._find_tests_in_module(event, module, top_level_dir))
[docs] def loadTestsFromNames(self, event): """Discover tests if no test names specified""" log.debug("Received event %s", event) if event.names or event.module: return event.handled = True # I will handle discovery return self._discover(event)
def _getStartDirs(self): start_dir = self.session.startDir top_level_dir = self.session.topLevelDir if start_dir is None: start_dir = '.' if top_level_dir is None: top_level_dir = start_dir if not os.path.isdir(os.path.abspath(start_dir)): raise OSError("%s is not a directory" % os.path.abspath(start_dir)) is_not_importable = False start_dir = os.path.abspath(start_dir) top_level_dir = os.path.abspath(top_level_dir) if start_dir != top_level_dir: is_not_importable = not os.path.isfile( os.path.join(start_dir, '__init__.py')) if is_not_importable: raise ImportError( 'Start directory is not importable: %r' % start_dir) # this is redundant in some cases, but that's ok self.session.prepareSysPath() return start_dir, top_level_dir def _discover(self, event): loader = event.loader try: start_dir, top_level_dir = self._getStartDirs() except (OSError, ImportError): _, ev, _ = sys.exc_info() return loader.suiteClass( loader.failedLoadTests(self.session.startDir, ev)) log.debug("_discover in %s (%s)", start_dir, top_level_dir) tests = list(self._find_tests(event, start_dir, top_level_dir)) return loader.suiteClass(tests) def _find_tests(self, event, start, top_level): """Used by discovery. Yields test suites it loads.""" log.debug('_find_tests(%r, %r)', start, top_level) if start == top_level: full_path = start else: full_path = os.path.join(top_level, start) if os.path.isdir(start): for test in self._find_tests_in_dir( event, full_path, top_level): yield test elif os.path.isfile(start): for test in self._find_tests_in_file( event, start, full_path, top_level): yield test def _find_tests_in_dir(self, event, full_path, top_level): log.debug("find in dir %s (%s)", full_path, top_level) dirname = os.path.basename(full_path) pattern = self.session.testFilePattern evt = events.HandleFileEvent( event.loader, dirname, full_path, pattern, top_level) result = self.session.hooks.handleDir(evt) if evt.extraTests: for test in evt.extraTests: yield test if evt.handled: if result: yield result return evt = events.MatchPathEvent(dirname, full_path, pattern) result = self.session.hooks.matchDirPath(evt) if evt.handled and not result: return for path in os.listdir(full_path): entry_path = os.path.join(full_path, path) if os.path.isfile(entry_path): for test in self._find_tests_in_file( event, path, entry_path, top_level): yield test elif os.path.isdir(entry_path): if ('test' in path.lower() or util.ispackage(entry_path) or path in self.session.libDirs): for test in self._find_tests(event, entry_path, top_level): yield test def _find_tests_in_file(self, event, filename, full_path, top_level): log.debug("find in file %s (%s)", full_path, top_level) pattern = self.session.testFilePattern loader = event.loader evt = events.HandleFileEvent( loader, filename, full_path, pattern, top_level) result = self.session.hooks.handleFile(evt) if evt.extraTests: yield loader.suiteClass(evt.extraTests) if evt.handled: if result: yield result return if not util.valid_module_name(filename): # valid Python identifiers only return evt = events.MatchPathEvent(filename, full_path, pattern) result = self.session.hooks.matchPath(evt) if evt.handled: if not result: return elif not self._match_path(filename, full_path, pattern): return # if the test file matches, load it name = util.name_from_path(full_path) try: module = util.module_from_name(name) except: yield loader.failedImport(name) else: mod_file = os.path.abspath( getattr(module, '__file__', full_path)) realpath = os.path.splitext(mod_file)[0] fullpath_noext = os.path.splitext(full_path)[0] if realpath.lower() != fullpath_noext.lower(): module_dir = os.path.dirname(realpath) mod_name = os.path.splitext(os.path.basename(full_path))[0] expected_dir = os.path.dirname(full_path) msg = ("%r module incorrectly imported from %r. " "Expected %r. Is this module globally installed?" ) raise ImportError( msg % (mod_name, module_dir, expected_dir)) yield loader.loadTestsFromModule(module) def _find_tests_in_module(self, event, module, top_level_dir): # only called from loadTestsFromName yield event.loader.loadTestsFromModule(module) # may be a package; recurse into __path__ if so pkgpath = getattr(module, '__path__', None) if pkgpath: for entry in pkgpath: full_path = os.path.abspath(os.path.join(top_level_dir, entry)) for test in self._find_tests_in_dir( event, full_path, top_level_dir): yield test def _match_path(self, path, full_path, pattern): # override this method to use alternative matching strategy return fnmatch(path, pattern)

This Page