Changeset 9
- Timestamp:
- Sun Mar 19 16:38:46 2006
- Files:
-
- trunk/nose/core.py (modified) (diff)
- trunk/nose/plugins/doctests.py (modified) (diff)
- trunk/nose/plugins/base.py (modified) (diff)
- trunk/nose/plugins/cover.py (modified) (diff)
- trunk/nose/plugins/__init__.py (modified) (diff)
- trunk/nose/plugins/profile.py (modified) (diff)
- trunk/nose/plugins/attrib.py (modified) (diff)
- trunk/nose/result.py (modified) (diff)
- trunk/nose/__init__.py (modified) (diff)
- trunk/nose/inspector.py (modified) (diff)
- trunk/ez_setup.py (modified) (diff)
- trunk/mkindex.py (modified) (diff)
- trunk/st/unit_tests/test_plugins.py (modified) (diff)
- trunk/st/unit_tests/test_inspector.py (modified) (diff)
- trunk/setup.py (modified) (diff)
- trunk/examples/plugin (added)
- trunk/examples/plugin/Example_plugin.egg-info (added)
- trunk/examples/plugin/Example_plugin.egg-info/SOURCES.txt (added)
- trunk/examples/plugin/Example_plugin.egg-info/top_level.txt (added)
- trunk/examples/plugin/Example_plugin.egg-info/PKG-INFO (added)
- trunk/examples/plugin/Example_plugin.egg-info/entry_points.txt (added)
- trunk/examples/plugin/setup.py (added)
- trunk/examples/plugin/plug.py (added)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
trunk/nose/core.py
r8 r9 645 645 646 646 647 def configure(argv=None, env=None): 647 def configure(argv=None, env=None, help=False): 647 647 """Configure the nose running environment. Execute configure before 648 648 collecting tests with nose.collector() to enable output capture and 649 649 other features. 650 650 """ 651 if argv is None: 652 argv = sys.argv 653 if env is None: 654 env = os.environ 655 651 656 conf = Config() 652 657 parser = OptionParser(TestProgram.__doc__) … … 702 707 help="Don't make any changes to sys.path when " 703 708 "loading tests [NOSE_NOPATH]") 709 704 710 705 711 # add opts from plugins … … 714 720 715 721 options, args = parser.parse_args(argv) 722 if help: 723 return parser.format_help() 716 724 717 725 try: -
trunk/nose/plugins/doctests.py
r5 r9 16 16 """ 17 17 import doctest 18 import logging 18 19 import os 19 20 from nose.plugins.base import Collector, Selector 20 # from nose.selector import file_in_tests, module_in_tests, split_test_name 21 from nose.util import anyp, msg 21 from nose.util import anyp 22 23 log = logging.getLogger(__name__) 22 24 23 25 class Doctest(Collector, Selector): … … 26 28 extension = None 27 29 28 def add_options(self, parser, env): 29 super(Doctest, self).add_options(parser) 30 def add_options(self, parser, env=os.environ): 31 super(Doctest, self).add_options(parser, env) 30 32 try: 31 33 # 2.4 or better supports loading tests from non-modules … … 70 72 def tests_in_module(self, module): 71 73 if not self.matches(module.__name__): 72 msg("Doctest doesn't want module %s" % module, 4)74 log.debug("Doctest doesn't want module %s", module) 72 74 return 73 75 try: 74 76 doctests = doctest.DocTestSuite(module) 75 77 except ValueError: 76 msg("No doctests in %s" % module, 4)78 log.debug("No doctests in %s", module) 76 78 return 77 79 else: 78 80 # < 2.4 doctest (and unittest) suites don't have iterators 79 msg("Doctests found in %s" % module, 4)81 log.debug("Doctests found in %s", module) 79 81 if hasattr(doctests, '__iter__'): 80 82 doctest_suite = doctests -
trunk/nose/plugins/base.py
r7 r9 91 91 class Watcher(Plugin): 92 92 93 def after(self, test): 93 def aftertest(self, test): 93 93 # call it result.stopTest 94 94 pass -
trunk/nose/plugins/cover.py
r7 r9 11 11 .. _coverage: http://www.nedbatchelder.com/code/modules/coverage.html 12 12 """ 13 import logging 13 14 import os 14 15 import sys 15 16 from nose.plugins.base import Watcher 16 from nose.util import msg 17 18 log = logging.getLogger(__name__) 17 19 18 20 class Coverage(Watcher): … … 30 32 cover_packages = None 31 33 32 def add_options(self, parser, env): 34 def add_options(self, parser, env=os.environ): 32 34 super(Coverage, self).add_options(parser, env) 33 35 … … 50 52 import coverage 51 53 except ImportError: 52 print >> sys.stderr, "Coverage not available: " \ 53 "unable to import coverage module" 54 log.error("Coverage not available: " 55 "unable to import coverage module") 56 self.enabled = False 54 57 return 55 58 self.config = config … … 57 60 self.cover_packages = self.tolist(options.cover_packages) 58 61 if self.cover_packages: 59 msg("Coverage report will include only packages: %s" 60 % self.cover_packages, 4) 62 log.info("Coverage report will include only packages: %s", 63 self.cover_packages) 61 64 62 65 def begin(self): 66 log.debug("Coverage begin") 63 67 import coverage 64 68 self.skip_modules = sys.modules.keys()[:] 65 69 coverage.start() 66 msg("Coverage begin", 5)67 70 68 71 def report(self, stream): 69 import coverage 70 msg("Coverage report", 5) 72 log.debug("Coverage report") 73 import coverage 71 74 modules = [ module 72 75 for name, module in sys.modules.items() … … 76 79 def want_module_coverage(self, name, module): 77 80 if not hasattr(module, '__file__'): 78 msg("no coverage of %s: no __file__" % name, 5)81 log.debug("no coverage of %s: no __file__", name) 78 81 return False 79 82 root, ext = os.path.splitext(module.__file__) 80 83 if not ext in ('.py', '.pyc', '.pyo'): 81 msg("no coverage of %s: not a python file" % name, 5)84 log.debug("no coverage of %s: not a python file", name) 81 84 return False 82 85 if self.cover_packages: … … 87 90 and (self.cover_tests 88 91 or not self.config.testMatch.search(name))): 89 msg("coverage for %s" % name, 5)92 log.debug("coverage for %s", name) 89 92 return True 90 93 if name in self.skip_modules: 91 msg("no coverage for %s: loaded before coverage start" 92 % name, 5) 94 log.debug("no coverage for %s: loaded before coverage start", 95 name) 93 96 return False 94 97 if self.config.testMatch.search(name) and not self.cover_tests: 95 msg("no coverage for %s: is a test" % name, 5)98 log.debug("no coverage for %s: is a test", name) 95 98 return False 96 99 # accept any package that passed the previous tests, unless -
trunk/nose/plugins/__init__.py
r7 r9 1 """ FIXME docs onplugins1 """nose plugins 1 1 2 - classes to subclass 3 2 nose supports setuptools entry point plugins for test collection, 3 selection, observation and reporting. 4 5 Writing Plugins 6 --------------- 7 8 Plugin classes should subclass nose.plugins.Plugin or one of the other 9 classes in nose.plugins.base. 10 11 Plugins may implement any of the methods in any or all of the interfaces 12 described in nose.plugins.base. 13 14 Registering 15 =========== 16 17 For nose to find a plugin, it must be part of a package that uses 18 setuptools, and the plugin must be included in the entry points defined 19 in the setup.py for the package:: 20 21 setup(name='Some plugin', 22 ... 23 entry_points = { 24 'nose.plugins': [ 25 'someplugin = someplugin:SomePlugin' 26 ] 27 }, 28 ... 29 ) 30 31 Once the package is installed with install or develop, nose will be able 32 to load the plugin. 33 34 Defining options 35 ================ 36 37 All plugins must implement the methods `add_options(self, parser, env)` 38 and `configure(self, options, conf)`. Subclasses of nose.plugins.Plugin 39 that want the standard options should call the superclass methods. 40 41 defining command-line and environment options 42 43 configuring from selected options 44 45 logging 46 47 Examples 48 ======== 49 50 See nose.plugins.attrib, nose.plugins.cover, nose.plugins.doctests and 51 nose.plugins.profile for examples. 52 53 examples in examples/ dir... 4 54 """ 5 55 import logging … … 11 61 12 62 def call_plugins(plugins, method, *arg, **kw): 63 """Call all method on plugins in list, that define it, with provided 64 arguments. The first response that is not None is returned. 65 """ 13 66 for plug in plugins: 14 67 func = getattr(plug, method, None) … … 22 75 23 76 def load_plugins(builtin=True, others=True): 77 """Load plugins, either builtin, others, or both. 78 """ 24 79 for ep in pkg_resources.iter_entry_points('nose.plugins'): 25 80 log.debug("load plugin %s" % ep) -
trunk/nose/plugins/profile.py
r4 r9 1 """This plugin is not yet functional. 2 3 FIXME 4 5 Need to figure out the least invasive, most general point to add a hook 6 where a plugin can be passed the test to be executed and return a 7 wrapper (or another callable of any kind). 8 9 FIXME 10 """ 11 12 import hotshot, hotshot.stats 1 13 from nose.plugins.base import Watcher 2 14 3 15 class Profile(Watcher): 4 pass 16 17 # FIXME formatting options -- sort, restrict func names, % of list 18 # FIXME prof data filename option 19 # FIXME print/no print report 20 21 def begin(self): 22 self.pfile = 'nosetests.prof' 23 self.prof = hotshot.Profile(self.pfile) 24 self.prof.start() 25 26 def report(self, stream): 27 # FIXME sort 28 # FIXME how to print to the stream? 29 # FIXME only if wanted 30 31 self.pct = 10 32 33 self.prof.close() 34 stats = hotshot.stats.load(self.pfile) 35 stats.print_stats(self.pct) -
trunk/nose/plugins/attrib.py
r7 r9 53 53 parser.add_option("-a", "--attr", 54 54 dest="attr", action="append", 55 default=env.get('NOSE_ATTR' , ""),55 default=env.get('NOSE_ATTR'), 55 55 help="Run only tests that have attributes " 56 56 "specified by ATTR [NOSE_ATTR]") 57 57 parser.add_option("-A", "--eval-attr", 58 58 dest="eval_attr", metavar="EXPR", action="append", 59 default=env.get('NOSE_EVAL_ATTR' , ""),59 default=env.get('NOSE_EVAL_ATTR'), 59 59 help="Run only tests for whose attributes " 60 60 "the Python expression EXPR evaluates " -
trunk/nose/result.py
r8 r9 141 141 142 142 def start_capture(): 143 """Start capturing output to stdout 143 """Start capturing output to stdout. DOES NOT reset the buffer. 143 143 """ 144 144 sys.stdout = buffer … … 147 147 148 148 def end_capture(): 149 """Stop capturing output to stdout 149 """Stop capturing output to stdout. DOES NOT reset the buffer.x 149 149 """ 150 150 if sys.stdout is not stdout: -
trunk/nose/__init__.py
r4 r9 2 2 3 3 nose provides an alternate test discovery and running process for 4 unittest, one that is intended to mimic the behavior of py.test as much as 5 is reasonably possible without resorting to too much magic. 4 unittest, one that is intended to mimic the behavior of py.test as much 5 as is reasonably possible without resorting to too much magic. 6 6 7 7 Basic usage 8 ===========8 ----------- 8 8 9 9 Use the nosetests script (after installation by setuptools):: … … 31 31 32 32 Features 33 ========33 -------- 33 33 34 34 Run as collect 35 ============== 35 36 36 nose begins running tests as soon as the first test module is 37 loaded, it does not wait to collect all tests before running the first. 37 nose begins running tests as soon as the first test module is loaded, it 38 does not wait to collect all tests before running the first. 38 39 39 40 Output capture 41 ============== 40 42 41 Unless called with the -s (--nocapture) switch, nose will capture 42 stdout during each test run, and print the captured output only for 43 tests that fail or have errors. The captured output is printed immediately 43 Unless called with the -s (--nocapture) switch, nose will capture stdout 44 during each test run, and print the captured output only for tests that 45 fail or have errors. The captured output is printed immediately 44 46 following the error or failure output for the test. (Note that output in 45 47 teardown methods is captured, but can't be output with failing tests, 46 48 because teardown has not yet run at the time of the failure.) 47 49 48 As of nose 0.7, output capture is implemented by patching a custom 49 Exception class into exceptions, which allows it to work with the default 50 unittest test runner, so you can use nose.collector to replace any 51 unittest.TestSuite in any context (in theory). 50 When running as a setup command, output capture is implemented by 51 patching nose's test result class into unittest. 52 52 53 53 Assert introspection 54 ==================== 54 55 55 56 When run with the -d (--detailed-errors) switch, nose will try to output … … 64 62 65 63 In other words if you have a test like:: 66 64 66 64 def test_integers(): 67 65 a = 2 … … 75 73 InspectAssertionError: assert 2 is 4 76 74 >> assert 2 == 4, "assert 2 is 4" 75 76 When running as a setuptools command, assert introspection is 77 implemented by patching nose's test result class into unittest. 77 78 78 79 Setuptools integration 80 ====================== 79 81 80 nose may be used with setuptools_. Simply specify the default test 81 collector as the test suite in your setup file:: 82 nose may be used with the setuptools_ test command. Simply specify the 83 test collector as the test suite in your setup file:: 82 84 83 85 setup ( … … 97 98 98 99 Writing tests 99 =============100 ------------- 99 100 100 101 As with py.test and TestGears, nose tests need not be subclasses of … … 104 105 also matches that expression will be run as a test. For the sake of 105 106 compatibility with legacy unittest test cases, nose will also load tests from 106 unittest.TestCase subclasses just like unittest does, and wrap them in 107 nose.Wrap so that output capture will work for them too. Like py.test and 107 unittest.TestCase subclasses just like unittest does. Like py.test and 108 108 TestGears, functional tests will be run in the order in which they appear in 109 109 the module file. TestCase derived tests and other test classes are run in … … 111 111 112 112 Fixtures 113 ======== 113 114 114 115 nose supports fixtures (setup and teardown methods) at the package, … … 120 120 121 121 Test packages 122 ============= 122 123 123 124 nose allows tests to be grouped into test packages. This allows … … 136 136 137 137 Test modules 138 ============ 138 139 139 140 A test module is a python module that matches the testMatch regular … … 145 145 146 146 Test classes 147 ============ 147 148 148 149 A test class is a class defined in a test module that is either a subclass … … 156 156 157 157 Test functions 158 ============== 158 159 159 160 Any function in a test module that matches testMatch will be wrapped in a … … 192 192 test = with_setup(setup_func,teardown_func)(test) 193 193 194 or:: 195 196 test.setup = setup_func 197 test.teardown = teardown_func 198 194 199 Test generators 200 =============== 195 201 196 202 nose supports test functions that are generators. A simple example from nose's … … 222 227 223 228 About the name 224 ==============229 -------------- 224 229 225 230 * nose is the least silly short synonym for discover in the dictionary.com … … 231 236 232 237 Contact the author 233 ==================238 ------------------ 233 238 234 239 To report bugs, ask questions, or request features, please email the author at … … 237 242 238 243 Similar test runners 239 ====================244 -------------------- 239 244 240 245 nose was inspired mainly by py.test_, which is far more functional than … … 255 260 256 261 License and copyright 257 =====================262 --------------------- 257 262 258 263 nose is copyright Jason Pellerin 2005-2006 … … 274 279 275 280 TODO 276 ====281 ---- 276 281 277 * Convert doctest and coverage support into optional plugins using setuptools278 entrypoints279 282 * Docstrings for all classes 280 283 * Output better shortDescription()s 281 * Change msg() signature to msg(message, args, level)282 284 """ 283 285 284 286 from nose.core import * 287 from nose.exc import * 288 from nose import result, plugins, selector 285 289 286 290 __author__ = 'Jason Pellerin' 287 291 __version__ = '0.9.0a1' 288 __all__ = [ 'core', 'exception', 'TestCase', 'TestCollector', 289 'TestLoader', 'collector', 'main', 'run_exit', 'with_setup' ] 292 __all__ = [ 'core', 'result', 'plugins', 'selector', 293 'SkipTest', 'DeprecatedTest', 'TestCase', 'TestCollector', 294 'collector', 'main', 'run_exit', 'with_setup' ] -
trunk/nose/inspector.py
r7 r9 14 14 15 15 def inspect_traceback(tb): 16 # FIXME 10 lines might be enough, or... ? 16 17 frames = inspect.getinnerframes(tb, 10) 17 18 for frame in frames: … … 19 20 # print inspect.getframeinfo(frame[0]) 20 21 exp = Expander(frame[0].f_locals, frame[0].f_globals) 21 src = StringIO(textwrap.dedent(''.join(frame[4][frame[5]:-3])))22 tokenize.tokenize(src.readline, exp)23 return exp.expanded_source24 22 23 # figure out the set of lines to grab. 24 inspect_lines, mark_line = find_inspectable_lines(frame[4], frame[5]) 25 src = StringIO(textwrap.dedent(''.join(inspect_lines))) 26 27 # FIXME somehow want to mark the actual assert line with a sigil 25 28 29 # if a token error results, try just doing the one line, 30 # stripped of any \ it might have 31 try: 32 tokenize.tokenize(src.readline, exp) 33 except tokenize.TokenError: 34 pass 35 36 if exp.expanded_source: 37 exp_lines = exp.expanded_source.split('\n') 38 padded = [] 39 ep = 0 40 for line in exp_lines: 41 if ep == mark_line: 42 padded.append('>> ' + line) 43 else: 44 padded.append(' ' + line) 45 ep += 1 46 return '\n'.join(padded) 47 48 49 def find_inspectable_lines(lines, pos): 50 """Find lines in home that are inspectable. 51 52 Walk back from the err line up to 3 lines, but don't walk back over 53 changes in indent level. 54 55 Walk forward up to 3 lines, counting \ separated lines as 1 don't walk 56 over changes in indent level (unless part of an extended line) 57 """ 58 cnt = re.compile(r'\\[\s\n]*$') 59 df = re.compile(r':[\s\n]*$') 60 ind = re.compile(r'^(\s*)') 61 inspect = [] 62 home = lines[pos] 63 home_indent = ind.match(home).groups()[0] 64 65 before = lines[max(pos-3, 0):pos] 66 before.reverse() 67 after = lines[pos+1:min(pos+4, len(lines))] 68 69 # print "before", before 70 # print "after", after 71 for line in before: 72 if ind.match(line).groups()[0] == home_indent: 73 inspect.append(line) 74 else: 75 break 76 inspect.reverse() 77 # print "before to inspect", inspect 78 79 inspect.append(home) 80 home_pos = len(inspect)-1 81 continued = cnt.search(home) 82 for line in after: 83 if ((continued or ind.match(line).groups()[0] == home_indent) 84 and not df.search(line)): 85 inspect.append(line) 86 continued = cnt.search(line) 87 else: 88 break 89 # print inspect 90 return inspect, home_pos 91 26 92 class Expander: 27 93 """Simple expression expander. Uses tokenize to find the names and … … 54 120 self.expanded_source += ' ' * (start[1]-self.lpos) 55 121 elif start[1] < self.lpos: 56 # newline 57 self.expanded_source += '\n' 122 # newline, indent correctly 58 123 self.expanded_source += ' ' * start[1] 59 124 self.lpos = end[1] … … 62 127 pass 63 128 elif ttype == tokenize.NAME: 129 # Clean this junk up 64 130 try: 65 val = repr(self.locals[tok]) 131 val = self.locals[tok] 132 if callable(val): 133 val = tok 134 else: 135 val = repr(val) 66 136 except KeyError: 67 137 try: 68 val = repr(self.globals[tok]) 138 val = self.globals[tok] 139 if callable(val): 140 val = tok 141 else: 142 val = repr(val) 143 69 144 except KeyError: 70 145 val = tok 146 # FIXME... not sure how to handle things like funcs, classes 71 147 # FIXME this is broken for some unicode strings 72 148 self.expanded_source += val … … 74 150 self.expanded_source += tok 75 151 # FIXME if this is the end of the line and the line ends with 76 # \, then tack a \ onto the output 152 # \, then tack a \ and newline onto the output 76 152 # print line[end[1]:] 77 153 if re.match(r'\s+\\\n', line[end[1]:]): 78 self.expanded_source += ' \\' 154 self.expanded_source += ' \\\n' -
trunk/ez_setup.py
r4 r9 15 15 """ 16 16 import sys 17 DEFAULT_VERSION = "0.6a 5"17 DEFAULT_VERSION = "0.6a10" 17 17 DEFAULT_URL = "http://cheeseshop.python.org/packages/%s/s/setuptools/" % sys.version[:3] 18 18 … … 23 23 'setuptools-0.6a1-py2.3.egg': 'ee819a13b924d9696b0d6ca6d1c5833d', 24 24 'setuptools-0.6a1-py2.4.egg': '8256b5f1cd9e348ea6877b5ddd56257d', 25 'setuptools-0.6a10-py2.3.egg': '162d8357f1aff2b0349c6c247ee62987', 26 'setuptools-0.6a10-py2.4.egg': '803a2d8db501c1ac3b5b6fb4e907f788', 27 's
