-- cgit v1.2.1 From 2fa9019f20c6b7d651f2b7a4fed3696f3473b7b7 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 16 Feb 2015 21:12:34 -0500 Subject: Scooch the version to a6 --- coverage/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage/version.py b/coverage/version.py index 304a54c..526634c 100644 --- a/coverage/version.py +++ b/coverage/version.py @@ -1,7 +1,7 @@ """The version and URL for coverage.py""" # This file is exec'ed in setup.py, don't import anything! -__version__ = "4.0a5" # see detailed history in CHANGES.txt +__version__ = "4.0a6" # see detailed history in CHANGES.txt __url__ = "https://coverage.readthedocs.org" if max(__version__).isalpha(): -- cgit v1.2.1 From 318033bec0bfde3f09304294a3c3fec70ae26057 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 16 Feb 2015 21:27:00 -0500 Subject: Change the tagging scheme so rtfd.org will work better. --- coverage/version.py | 2 +- doc/install.rst | 4 ++-- howto.txt | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/coverage/version.py b/coverage/version.py index 526634c..51e1310 100644 --- a/coverage/version.py +++ b/coverage/version.py @@ -6,4 +6,4 @@ __version__ = "4.0a6" # see detailed history in CHANGES.txt __url__ = "https://coverage.readthedocs.org" if max(__version__).isalpha(): # For pre-releases, use a version-specific URL. - __url__ += "/en/coverage-" + __version__ + __url__ += "/en/" + __version__ diff --git a/doc/install.rst b/doc/install.rst index c3b1329..757b775 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -95,7 +95,7 @@ installed properly: $ coverage --version Coverage.py, version |release|. - Documentation at https://coverage.readthedocs.org/en/coverage-|release| + Documentation at https://coverage.readthedocs.org/en/|release| You can also invoke coverage as a module: @@ -113,4 +113,4 @@ You can also invoke coverage as a module: $ python -m coverage --version Coverage.py, version |release|. - Documentation at https://coverage.readthedocs.org/en/coverage-|release| + Documentation at https://coverage.readthedocs.org/en/|release| diff --git a/howto.txt b/howto.txt index e37a949..9a33d54 100644 --- a/howto.txt +++ b/howto.txt @@ -47,11 +47,13 @@ - Visit http://pypi.python.org/pypi?%3Aaction=pkg_edit&name=coverage : - show/hide the proper versions. - Tag the tree - - hg tag -m "Coverage 3.0.1" coverage-3.0.1 + - hg tag -m "Coverage 3.0.1" 3.0.1 - Update nedbatchelder.com - Edit webfaction.htaccess to make sure the proper versions are mapped to /beta - Blog post? - Update readthedocs + - Coverage / versions + - find the latest tag in the inactive list, edit it, make it active. - Update bitbucket: - Issue tracker should get new version number in picker. # Note: don't delete old version numbers: it marks changes on the tickets -- cgit v1.2.1 From 0155f1f0619640cce45dd2a3ace367e04c4eb0d2 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 17 Feb 2015 07:35:01 -0500 Subject: Properly handle crazy-long code objects. #359 --- CHANGES.txt | 9 +++++++++ coverage/bytecode.py | 16 +++++++++++++--- lab/show_pyc.py | 2 +- tests/coveragetest.py | 12 +++++++----- tests/test_arcs.py | 21 +++++++++++++++++++++ 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f96db6e..cab00a9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,15 @@ Change history for Coverage.py ------------------------------ +Latest +------ + +- Branch coverage couldn't properly handle certain extremely long files. This + is now fixed (`issue 359`_). + +.. _issue 359: https://bitbucket.org/ned/coveragepy/issue/359/xml-report-chunk-error + + Version 4.0a5 --- 16 February 2015 ---------------------------------- diff --git a/coverage/bytecode.py b/coverage/bytecode.py index 3f62dfa..d730493 100644 --- a/coverage/bytecode.py +++ b/coverage/bytecode.py @@ -1,9 +1,11 @@ """Bytecode manipulation for coverage.py""" -import opcode, types +import opcode +import types from coverage.backward import byte_to_int + class ByteCode(object): """A single bytecode.""" def __init__(self): @@ -26,6 +28,9 @@ class ByteCode(object): class ByteCodes(object): """Iterator over byte codes in `code`. + This handles the logic of EXTENDED_ARG byte codes internally. Those byte + codes are not returned by this iterator. + Returns `ByteCode` objects. """ @@ -37,6 +42,7 @@ class ByteCodes(object): def __iter__(self): offset = 0 + ext_arg = 0 while offset < len(self.code): bc = ByteCode() bc.op = self[offset] @@ -44,7 +50,7 @@ class ByteCodes(object): next_offset = offset+1 if bc.op >= opcode.HAVE_ARGUMENT: - bc.arg = self[offset+1] + 256*self[offset+2] + bc.arg = ext_arg + self[offset+1] + 256*self[offset+2] next_offset += 2 label = -1 @@ -55,7 +61,11 @@ class ByteCodes(object): bc.jump_to = label bc.next_offset = offset = next_offset - yield bc + if bc.op == opcode.EXTENDED_ARG: + ext_arg = bc.arg * 256*256 + else: + ext_arg = 0 + yield bc class CodeObjects(object): diff --git a/lab/show_pyc.py b/lab/show_pyc.py index b2cbb34..d6bbd92 100644 --- a/lab/show_pyc.py +++ b/lab/show_pyc.py @@ -4,7 +4,7 @@ def show_pyc_file(fname): f = open(fname, "rb") magic = f.read(4) moddate = f.read(4) - modtime = time.asctime(time.localtime(struct.unpack('L', moddate)[0])) + modtime = time.asctime(time.localtime(struct.unpack(' Date: Wed, 18 Feb 2015 22:22:38 -0500 Subject: Make branch=True get along with plugins. --- coverage/tracer.c | 23 +++++++++++------------ tests/test_plugins.py | 29 +++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/coverage/tracer.c b/coverage/tracer.c index 43ecd18..8577afc 100644 --- a/coverage/tracer.c +++ b/coverage/tracer.c @@ -638,31 +638,30 @@ CTracer_handle_line(CTracer *self, PyFrameObject *frame) } if (lineno_from != -1) { - if (self->tracing_arcs) { - /* Tracing arcs: key is (last_line,this_line). */ - /* TODO: this needs to deal with lineno_to also. */ - if (CTracer_record_pair(self, self->cur_entry.last_line, lineno_from) < 0) { - goto error; + for (; lineno_from <= lineno_to; lineno_from++) { + if (self->tracing_arcs) { + /* Tracing arcs: key is (last_line,this_line). */ + if (CTracer_record_pair(self, self->cur_entry.last_line, lineno_from) < 0) { + goto error; + } } - } - else { - /* Tracing lines: key is simply this_line. */ - while (lineno_from <= lineno_to) { + else { + /* Tracing lines: key is simply this_line. */ PyObject * this_line = MyInt_FromInt(lineno_from); if (this_line == NULL) { goto error; } + ret = PyDict_SetItem(self->cur_entry.file_data, this_line, Py_None); Py_DECREF(this_line); if (ret < 0) { goto error; } - lineno_from++; } + + self->cur_entry.last_line = lineno_from; } } - - self->cur_entry.last_line = lineno_to; } } diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 74f78cf..f39b062 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -277,7 +277,7 @@ class GoodPluginTest(FileTracerTest): _, statements, _, _ = cov.analysis(zzfile) self.assertEqual(statements, [105, 106, 107, 205, 206, 207]) - def test_plugin2(self): + def make_render_and_caller(self): # plugin2 emulates a dynamic tracing plugin: the caller's locals # are examined to determine the source file and line number. # The plugin is in tests/plugin2.py. @@ -295,6 +295,7 @@ class GoodPluginTest(FileTracerTest): return x+1 """) self.make_file("caller.py", """\ + import sys from render import helper, render assert render("foo_7.html", 4) == "[foo_7.html @ 4]" @@ -309,10 +310,13 @@ class GoodPluginTest(FileTracerTest): assert render("quux_5.html", 3) == "[quux_5.html @ 3]" # In Python 2, either kind of string should be OK. - if type("") == type(b""): + if sys.version_info[0] == 2: assert render(u"unicode_3.html", 2) == "[unicode_3.html @ 2]" """) + def test_plugin2(self): + self.make_render_and_caller() + cov = coverage.Coverage(omit=["*quux*"]) CheckUniqueFilenames.hook(cov, '_should_trace') CheckUniqueFilenames.hook(cov, '_check_include_omit_etc') @@ -341,6 +345,27 @@ class GoodPluginTest(FileTracerTest): self.assertEqual(missing, [1]) self.assertIn("unicode_3.html", cov.data.summary()) + def test_plugin2_with_branch(self): + self.make_render_and_caller() + + cov = coverage.Coverage(branch=True, omit=["*quux*"]) + CheckUniqueFilenames.hook(cov, '_should_trace') + CheckUniqueFilenames.hook(cov, '_check_include_omit_etc') + cov.config["run:plugins"] = ["tests.plugin2"] + + self.start_import_stop(cov, "caller") + + # The way plugin2 works, a file named foo_7.html will be claimed to + # have 7 lines in it. If render() was called with line number 4, + # then the plugin will claim that lines 4 and 5 were executed. + analysis = cov._analyze("foo_7.html") + self.assertEqual(analysis.statements, set([1, 2, 3, 4, 5, 6, 7])) + # Plugins don't do branch coverage yet. + self.assertEqual(analysis.has_arcs(), True) + self.assertEqual(analysis.arc_possibilities(), []) + + self.assertEqual(analysis.missing, set([1, 2, 3, 6, 7])) + class BadPluginTest(FileTracerTest): """Test error handling around plugins.""" -- cgit v1.2.1 From 75f7f448b14984dba4be4113cf89c8e4370efad7 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 19 Feb 2015 07:35:54 -0500 Subject: Don't need this variable --- coverage/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage/control.py b/coverage/control.py index 0bbe301..0880786 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -481,7 +481,7 @@ class Coverage(object): file_tracer.source_filename() ) break - except Exception as e: + except Exception: self._warn( "Disabling plugin %r due to an exception:" % ( plugin._coverage_plugin_name -- cgit v1.2.1 From 37a2818d706a193ae877941d7aaf49f0d3b921ae Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 19 Feb 2015 10:33:25 -0500 Subject: A Makefile target for copying kits to a local distribution point. --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 9c25cd6..94286b9 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,9 @@ kit: kit_upload: $(SDIST_CMD) upload +kit_local: + cp -v dist/* `awk -F "=" '/find-links/ {print $$2}' ~/.pip/pip.conf` + pypi: python setup.py register -- cgit v1.2.1 From ba65cc0d4f6e1d6757b2cb3fd43e8f488b68bf7d Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 19 Feb 2015 21:10:42 -0500 Subject: Pep8 fixes. --- tests/test_oddball.py | 54 +++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/test_oddball.py b/tests/test_oddball.py index 25f58d3..9fdc654 100644 --- a/tests/test_oddball.py +++ b/tests/test_oddball.py @@ -8,6 +8,7 @@ import coverage from tests.coveragetest import CoverageTest from tests import osinfo + class ThreadingTest(CoverageTest): """Tests of the threading support.""" @@ -29,7 +30,7 @@ class ThreadingTest(CoverageTest): fromMainThread() other.join() """, - [1,3,4,6,7,9,10,12,13,14,15], "10") + [1, 3, 4, 6, 7, 9, 10, 12, 13, 14, 15], "10") def test_thread_run(self): self.check_coverage("""\ @@ -48,7 +49,7 @@ class ThreadingTest(CoverageTest): thd.start() thd.join() """, - [1,3,4,5,6,7,9,10,12,13,14], "") + [1, 3, 4, 5, 6, 7, 9, 10, 12, 13, 14], "") class RecursionTest(CoverageTest): @@ -66,7 +67,7 @@ class RecursionTest(CoverageTest): recur(495) # We can get at least this many stack frames. i = 8 # and this line will be traced """, - [1,2,3,5,7,8], "") + [1, 2, 3, 5, 7, 8], "") def test_long_recursion(self): # We can't finish a very deep recursion, but we don't crash. @@ -80,7 +81,7 @@ class RecursionTest(CoverageTest): recur(100000) # This is definitely too many frames. """, - [1,2,3,5,7], "" + [1, 2, 3, 5, 7], "" ) def test_long_recursion_recovery(self): @@ -112,10 +113,10 @@ class RecursionTest(CoverageTest): pytrace = (cov.collector.tracer_name() == "PyTracer") expected_missing = [3] if pytrace: - expected_missing += [9,10,11] + expected_missing += [9, 10, 11] _, statements, missing, _ = cov.analysis("recur.py") - self.assertEqual(statements, [1,2,3,5,7,8,9,10,11]) + self.assertEqual(statements, [1, 2, 3, 5, 7, 8, 9, 10, 11]) self.assertEqual(missing, expected_missing) # Get a warning about the stackoverflow effect on the tracing function. @@ -179,7 +180,6 @@ class MemoryLeakTest(CoverageTest): self.fail("RAM grew by %d" % (ram_growth)) - class PyexpatTest(CoverageTest): """Pyexpat screws up tracing. Make sure we've counter-defended properly.""" @@ -216,11 +216,11 @@ class PyexpatTest(CoverageTest): self.start_import_stop(cov, "outer") _, statements, missing, _ = cov.analysis("trydom.py") - self.assertEqual(statements, [1,3,8,9,10,11,13]) + self.assertEqual(statements, [1, 3, 8, 9, 10, 11, 13]) self.assertEqual(missing, []) _, statements, missing, _ = cov.analysis("outer.py") - self.assertEqual(statements, [101,102]) + self.assertEqual(statements, [101, 102]) self.assertEqual(missing, []) @@ -281,26 +281,26 @@ class ExceptionTest(CoverageTest): # combinations of catching exceptions and letting them fly. runs = [ ("doit fly oops", { - 'doit.py': [302,303,304,305], - 'fly.py': [102,103], - 'oops.py': [2,3], + 'doit.py': [302, 303, 304, 305], + 'fly.py': [102, 103], + 'oops.py': [2, 3], }), ("doit catch oops", { - 'doit.py': [302,303], - 'catch.py': [202,203,204,206,207], - 'oops.py': [2,3], + 'doit.py': [302, 303], + 'catch.py': [202, 203, 204, 206, 207], + 'oops.py': [2, 3], }), ("doit fly catch oops", { - 'doit.py': [302,303], - 'fly.py': [102,103,104], - 'catch.py': [202,203,204,206,207], - 'oops.py': [2,3], + 'doit.py': [302, 303], + 'fly.py': [102, 103, 104], + 'catch.py': [202, 203, 204, 206, 207], + 'oops.py': [2, 3], }), ("doit catch fly oops", { - 'doit.py': [302,303], - 'catch.py': [202,203,204,206,207], - 'fly.py': [102,103], - 'oops.py': [2,3], + 'doit.py': [302, 303], + 'catch.py': [202, 203, 204, 206, 207], + 'fly.py': [102, 103], + 'oops.py': [2, 3], }), ] @@ -318,7 +318,7 @@ class ExceptionTest(CoverageTest): # Clean the line data and compare to expected results. # The filenames are absolute, so keep just the base. - cov._harvest_data() # private! sshhh... + cov._harvest_data() # private! sshhh... lines = cov.data.line_data() clean_lines = {} for f, llist in lines.items(): @@ -366,7 +366,7 @@ class DoctestTest(CoverageTest): import doctest, sys doctest.testmod(sys.modules[__name__]) # we're not __main__ :( ''', - [1,11,12,14,16,17], "") + [1, 11, 12, 14, 16, 17], "") class GettraceTest(CoverageTest): @@ -382,7 +382,7 @@ class GettraceTest(CoverageTest): sys.settrace(sys.gettrace()) a = bar(8) ''', - [1,2,3,4,5,6,7,8], "") + [1, 2, 3, 4, 5, 6, 7, 8], "") def test_multi_layers(self): self.check_coverage('''\ @@ -399,4 +399,4 @@ class GettraceTest(CoverageTest): level1() f = 12 ''', - [1,2,3,4,5,6,7,8,9,10,11,12], "") + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], "") -- cgit v1.2.1 From 3e901194d5fa45ede0bf23f20a407f242b6f56d8 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 19 Feb 2015 21:14:55 -0500 Subject: Don't report negative line numbers through get_line_data --- coverage/collector.py | 8 ++++++-- tests/coveragetest.py | 4 ++++ tests/test_arcs.py | 24 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/coverage/collector.py b/coverage/collector.py index c156090..948cbbb 100644 --- a/coverage/collector.py +++ b/coverage/collector.py @@ -291,10 +291,14 @@ class Collector(object): """ if self.branch: # If we were measuring branches, then we have to re-build the dict - # to show line data. + # to show line data. We'll use the first lines of all the arcs, + # if they are actual lines. We don't need the second lines, because + # the second lines will also be first lines, sometimes to exits. line_data = {} for f, arcs in self.data.items(): - line_data[f] = dict((l1, None) for l1, _ in arcs.keys() if l1) + line_data[f] = dict( + (l1, None) for l1, _ in arcs.keys() if l1 > 0 + ) return line_data else: return self.data diff --git a/tests/coveragetest.py b/tests/coveragetest.py index cf3d247..0e80f4a 100644 --- a/tests/coveragetest.py +++ b/tests/coveragetest.py @@ -166,6 +166,8 @@ class CoverageTest( `arcs_unpredicted` are the arcs executed in the code, but not deducible from the code. + Returns the Coverage object, in case you want to poke at it some more. + """ # We write the code into a file so that we can import it. # Coverage wants to deal with things as modules with file names. @@ -248,6 +250,8 @@ class CoverageTest( rep = " ".join(frep.getvalue().split("\n")[2].split()[1:]) self.assertEqual(report, rep) + return cov + def nice_file(self, *fparts): """Canonicalize the filename composed of the parts in `fparts`.""" fname = os.path.join(*fparts) diff --git a/tests/test_arcs.py b/tests/test_arcs.py index 42e1051..6f28d05 100644 --- a/tests/test_arcs.py +++ b/tests/test_arcs.py @@ -1,7 +1,10 @@ """Tests for Coverage.py's arc measurement.""" +import os.path + from tests.coveragetest import CoverageTest +import coverage from coverage import env @@ -627,3 +630,24 @@ class ExcludeTest(CoverageTest): [1,2,3,4,5], partials=["only some"], arcz=".1 12 23 34 45 25 5.", arcz_missing="") + + +class LineDataTest(CoverageTest): + """Tests that line_data gives us what we expect.""" + + def test_branch(self): + cov = coverage.Coverage(branch=True) + + self.make_file("fun1.py", """\ + def fun1(x): + if x == 1: + return + + fun1(3) + """) + + self.start_import_stop(cov, "fun1") + + cov._harvest_data() + fun1_lines = cov.data.line_data()[os.path.abspath("fun1.py")] + self.assertEqual(fun1_lines, [1, 2, 5]) -- cgit v1.2.1 From 08e662e2752ed552101cb02e82e624544916bf01 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 21 Feb 2015 09:29:22 -0500 Subject: Keep cleaning even if test_farm doesn't succeed. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 94286b9..b755d07 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ default: clean: -rm -f *.pyd */*.pyd -rm -f *.so */*.so - PYTHONPATH=. python tests/test_farm.py clean + -PYTHONPATH=. python tests/test_farm.py clean -rm -rf build coverage.egg-info dist htmlcov -rm -f *.pyc */*.pyc */*/*.pyc */*/*/*.pyc */*/*/*/*.pyc */*/*/*/*/*.pyc -rm -f *.pyo */*.pyo */*/*.pyo */*/*/*.pyo */*/*/*/*.pyo */*/*/*/*/*.pyo -- cgit v1.2.1 From ea1243c8d690d37054a2b681c62b17133741add7 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 21 Feb 2015 09:30:58 -0500 Subject: Disable plugins if we can't support them, and show that in debug output. --- coverage/control.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/coverage/control.py b/coverage/control.py index 0880786..cb85da1 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -243,7 +243,7 @@ class Coverage(object): # Early warning if we aren't going to be able to support plugins. if self.file_tracers and not self.collector.supports_plugins: - raise CoverageException( + self._warn( "Plugin file tracers (%s) aren't supported with %s" % ( ", ".join( ft._coverage_plugin_name for ft in self.file_tracers @@ -251,6 +251,8 @@ class Coverage(object): self.collector.tracer_name(), ) ) + for plugin in self.file_tracers: + plugin._coverage_enabled = False # Suffixes are a bit tricky. We want to use the data suffix only when # collecting data, not when combining data. So we save it as @@ -1009,15 +1011,20 @@ class Coverage(object): except AttributeError: implementation = "unknown" + file_tracers = [] + for ft in self.file_tracers: + ft_name = ft._coverage_plugin_name + if not ft._coverage_enabled: + ft_name += " (disabled)" + file_tracers.append(ft_name) + info = [ ('version', covmod.__version__), ('coverage', covmod.__file__), ('cover_dir', self.cover_dir), ('pylib_dirs', self.pylib_dirs), ('tracer', self.collector.tracer_name()), - ('file_tracers', [ - ft._coverage_plugin_name for ft in self.file_tracers - ]), + ('file_tracers', file_tracers), ('config_files', self.config.attempted_config_files), ('configs_read', self.config.config_files), ('data_path', self.data.filename), -- cgit v1.2.1 From 958b782348c2422f15d7a364083d64fcbe47c04b Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 21 Feb 2015 09:47:51 -0500 Subject: Oops, test broken by last commit. --- tests/test_plugins.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_plugins.py b/tests/test_plugins.py index f39b062..fa8d34e 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -230,13 +230,17 @@ class PluginWarningOnPyTracer(CoverageTest): cov = coverage.Coverage() cov.config["run:plugins"] = ["tests.plugin1"] + warnings = [] + def capture_warning(msg): + warnings.append(msg) + cov._warn = capture_warning msg = ( - r"Plugin file tracers \(tests.plugin1\) " + r"Plugin file tracers (tests.plugin1) " r"aren't supported with PyTracer" ) - with self.assertRaisesRegex(CoverageException, msg): - self.start_import_stop(cov, "simple") + self.start_import_stop(cov, "simple") + self.assertIn(msg, warnings) class FileTracerTest(CoverageTest): -- cgit v1.2.1 From 7fa9cec95bca8db858087e9febbc1b4c581240c1 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 21 Feb 2015 13:46:20 -0500 Subject: Make the test a little nicer. --- tests/test_plugins.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_plugins.py b/tests/test_plugins.py index fa8d34e..2d12a3c 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -230,17 +230,18 @@ class PluginWarningOnPyTracer(CoverageTest): cov = coverage.Coverage() cov.config["run:plugins"] = ["tests.plugin1"] + warnings = [] def capture_warning(msg): warnings.append(msg) cov._warn = capture_warning - msg = ( - r"Plugin file tracers (tests.plugin1) " - r"aren't supported with PyTracer" - ) self.start_import_stop(cov, "simple") - self.assertIn(msg, warnings) + self.assertIn( + "Plugin file tracers (tests.plugin1) " + "aren't supported with PyTracer", + warnings + ) class FileTracerTest(CoverageTest): -- cgit v1.2.1 From 64c7f2cb2959af7196182e6a745c8654d6cf6519 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 21 Feb 2015 20:11:16 -0500 Subject: Fix a symptom-less error in tracer.c, and lots of other suggested fixes noted from Yhg1s. --- coverage/tracer.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/coverage/tracer.c b/coverage/tracer.c index 8577afc..5670326 100644 --- a/coverage/tracer.c +++ b/coverage/tracer.c @@ -451,6 +451,7 @@ static int CTracer_handle_call(CTracer *self, PyFrameObject *frame) { int ret = RET_ERROR; + int ret2; /* Owned references that we clean up at the very end of the function. */ PyObject * tracename = NULL; @@ -562,9 +563,9 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame) if (file_data == NULL) { goto error; } - ret = PyDict_SetItem(self->data, tracename, file_data); + ret2 = PyDict_SetItem(self->data, tracename, file_data); Py_DECREF(file_data); - if (ret < 0) { + if (ret2 < 0) { goto error; } @@ -574,9 +575,9 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame) if (disp_plugin_name == NULL) { goto error; } - ret = PyDict_SetItem(self->plugin_data, tracename, disp_plugin_name); + ret2 = PyDict_SetItem(self->plugin_data, tracename, disp_plugin_name); Py_DECREF(disp_plugin_name); - if (ret < 0) { + if (ret2 < 0) { goto error; } } @@ -1071,3 +1072,16 @@ inittracer(void) } #endif /* Py3k */ + +/* + * TODO, from Yhg1s + * You aren't checking the return value on line 367 (can fail on MemoryError.) + * stack_index = MyInt_FromInt(the_index); + * Also 385 -- *_AsInt(...) can fail if the object isn't int-able. It'll return -1 and set an exception (you have to check PyErr_Occurred() to distinguish a -1 result from an error.) + * On line 480-482, you need to check PyErr_Occurred() as well. PyDict_GetItem returns NULL both on KeyError (with no exception set) and if an actual exception occurred. + * You're also a little inconsistent about your checking of return values. Sometimes you just use the boolean value, sometimes you explicitly check for < 0. + * You can use Py_RETURN_NONE instead of 'return Py_BuildValue("")' by the way. + * Oh, I see you doing the same thing I saw colleagues doing, setting tp_new to PyType_GenericNew after the fact. You don't need to do that. (You're setting the TPFLAGS_BASETYPE flag, so the slot should be inherited.) + * You should be checking the return value of PyModule_AddObject, at least in the Python 3 version. + * (another function that's extremely unlikely to fail, but still.) + */ -- cgit v1.2.1 From 4ce67933b19cd067046a55a8100f3a56881858ce Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 21 Feb 2015 20:40:39 -0500 Subject: Get rid of CodeUnit, FileReporter is the new thing. --- coverage/codeunit.py | 38 -------------------------------------- coverage/control.py | 2 +- coverage/python.py | 39 ++++++++++++++++++++++++++------------- coverage/xmlreport.py | 7 ++++--- tests/test_codeunit.py | 10 +++++----- 5 files changed, 36 insertions(+), 60 deletions(-) delete mode 100644 coverage/codeunit.py diff --git a/coverage/codeunit.py b/coverage/codeunit.py deleted file mode 100644 index ef7e848..0000000 --- a/coverage/codeunit.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Code unit (module) handling for Coverage.""" - -import os - -from coverage.files import FileLocator -from coverage.plugin import FileReporter - - -class CodeUnit(FileReporter): - """Code unit: a filename or module. - - Instance attributes: - - `name` is a human-readable name for this code unit. - `filename` is the os path from which we can read the source. - - """ - - def __init__(self, morf, file_locator=None): - self.file_locator = file_locator or FileLocator() - - if hasattr(morf, '__file__'): - filename = morf.__file__ - else: - filename = morf - filename = self._adjust_filename(filename) - self.filename = self.file_locator.canonical_filename(filename) - - if hasattr(morf, '__name__'): - name = morf.__name__ - name = name.replace(".", os.sep) + ".py" - else: - name = self.file_locator.relative_filename(filename) - self.name = name - - def _adjust_filename(self, f): - # TODO: This shouldn't be in the base class, right? - return f diff --git a/coverage/control.py b/coverage/control.py index cb85da1..a0c21a4 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -989,7 +989,7 @@ class Coverage(object): outfile = open(self.config.xml_output, "w") file_to_close = outfile try: - reporter = XmlReporter(self, self.config) + reporter = XmlReporter(self, self.config, self.file_locator) return reporter.report(morfs, outfile=outfile) except CoverageException: delete_file = True diff --git a/coverage/python.py b/coverage/python.py index 53da561..7a9b842 100644 --- a/coverage/python.py +++ b/coverage/python.py @@ -7,10 +7,11 @@ import zipimport from coverage import env from coverage.backward import unicode_class -from coverage.codeunit import CodeUnit +from coverage.files import FileLocator from coverage.misc import NoSource, join_regex from coverage.parser import PythonParser from coverage.phystokens import source_token_lines, source_encoding +from coverage.plugin import FileReporter def read_python_source(filename): @@ -86,13 +87,33 @@ def get_zip_bytes(filename): return None -class PythonCodeUnit(CodeUnit): - """Represents a Python file.""" +class PythonCodeUnit(FileReporter): + """Report support for a Python file.""" def __init__(self, morf, coverage=None): self.coverage = coverage - file_locator = coverage.file_locator if coverage else None - super(PythonCodeUnit, self).__init__(morf, file_locator) + file_locator = coverage.file_locator if coverage else FileLocator() + + if hasattr(morf, '__file__'): + filename = morf.__file__ + else: + filename = morf + + # .pyc files should always refer to a .py instead. + if filename.endswith(('.pyc', '.pyo')): + filename = filename[:-1] + elif filename.endswith('$py.class'): # Jython + filename = filename[:-9] + ".py" + + super(PythonCodeUnit, self).__init__(file_locator.canonical_filename(filename)) + + if hasattr(morf, '__name__'): + name = morf.__name__ + name = name.replace(".", os.sep) + ".py" + else: + name = file_locator.relative_filename(filename) + self.name = name + self._source = None self._parser = None self._statements = None @@ -138,14 +159,6 @@ class PythonCodeUnit(CodeUnit): def exit_counts(self): return self.parser.exit_counts() - def _adjust_filename(self, fname): - # .pyc files should always refer to a .py instead. - if fname.endswith(('.pyc', '.pyo')): - fname = fname[:-1] - elif fname.endswith('$py.class'): # Jython - fname = fname[:-9] + ".py" - return fname - def source(self): if self._source is None: self._source = get_python_source(self.filename) diff --git a/coverage/xmlreport.py b/coverage/xmlreport.py index f7ad2b8..df48993 100644 --- a/coverage/xmlreport.py +++ b/coverage/xmlreport.py @@ -20,9 +20,10 @@ def rate(hit, num): class XmlReporter(Reporter): """A reporter for writing Cobertura-style XML coverage results.""" - def __init__(self, coverage, config): + def __init__(self, coverage, config, file_locator): super(XmlReporter, self).__init__(coverage, config) + self.file_locator = file_locator self.source_paths = set() self.packages = {} self.xml_out = None @@ -121,7 +122,7 @@ class XmlReporter(Reporter): # Create the 'lines' and 'package' XML elements, which # are populated later. Note that a package == a directory. - filename = cu.file_locator.relative_filename(cu.filename) + filename = self.file_locator.relative_filename(cu.filename) filename = filename.replace("\\", "/") dirname = os.path.dirname(filename) or "." parts = dirname.split("/") @@ -129,7 +130,7 @@ class XmlReporter(Reporter): package_name = dirname.replace("/", ".") className = cu.name - self.source_paths.add(cu.file_locator.relative_dir.rstrip('/')) + self.source_paths.add(self.file_locator.relative_dir.rstrip('/')) package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0]) xclass = self.xml_out.createElement("class") diff --git a/tests/test_codeunit.py b/tests/test_codeunit.py index ea65d85..3b873cd 100644 --- a/tests/test_codeunit.py +++ b/tests/test_codeunit.py @@ -3,7 +3,7 @@ import os import sys -from coverage.codeunit import CodeUnit +from coverage.plugin import FileReporter from coverage.python import PythonCodeUnit from tests.coveragetest import CoverageTest @@ -93,10 +93,10 @@ class CodeUnitTest(CoverageTest): self.assertEqual(ccu.source(), "# cfile.py\n") def test_comparison(self): - acu = CodeUnit("aa/afile.py") - acu2 = CodeUnit("aa/afile.py") - zcu = CodeUnit("aa/zfile.py") - bcu = CodeUnit("aa/bb/bfile.py") + acu = FileReporter("aa/afile.py") + acu2 = FileReporter("aa/afile.py") + zcu = FileReporter("aa/zfile.py") + bcu = FileReporter("aa/bb/bfile.py") assert acu == acu2 and acu <= acu2 and acu >= acu2 assert acu < zcu and acu <= zcu and acu != zcu assert zcu > acu and zcu >= acu and zcu != acu -- cgit v1.2.1 From b376435de2f3a94fcbc2a36b9537265c46b86139 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 21 Feb 2015 22:24:50 -0500 Subject: Rename CodeUnit to FileReporter --- coverage/control.py | 6 +++--- coverage/python.py | 6 ++++-- tests/test_codeunit.py | 36 ++++++++++++++++++------------------ 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/coverage/control.py b/coverage/control.py index a0c21a4..2db5802 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -24,7 +24,7 @@ from coverage.misc import CoverageException, bool_or_none, join_regex from coverage.misc import file_be_gone, overrides from coverage.monkey import patch_multiprocessing from coverage.plugin import CoveragePlugin, FileReporter -from coverage.python import PythonCodeUnit +from coverage.python import PythonFileReporter from coverage.results import Analysis, Numbers from coverage.summary import SummaryReporter from coverage.xmlreport import XmlReporter @@ -343,7 +343,7 @@ class Coverage(object): def _canonical_dir(self, morf): """Return the canonical directory of the module or file `morf`.""" - morf_filename = PythonCodeUnit(morf, self).filename + morf_filename = PythonFileReporter(morf, self).filename return os.path.split(morf_filename)[0] def _source_for_file(self, filename): @@ -850,7 +850,7 @@ class Coverage(object): ) ) else: - file_reporter = PythonCodeUnit(morf, self) + file_reporter = PythonFileReporter(morf, self) return file_reporter diff --git a/coverage/python.py b/coverage/python.py index 7a9b842..19212a5 100644 --- a/coverage/python.py +++ b/coverage/python.py @@ -87,7 +87,7 @@ def get_zip_bytes(filename): return None -class PythonCodeUnit(FileReporter): +class PythonFileReporter(FileReporter): """Report support for a Python file.""" def __init__(self, morf, coverage=None): @@ -105,7 +105,9 @@ class PythonCodeUnit(FileReporter): elif filename.endswith('$py.class'): # Jython filename = filename[:-9] + ".py" - super(PythonCodeUnit, self).__init__(file_locator.canonical_filename(filename)) + super(PythonFileReporter, self).__init__( + file_locator.canonical_filename(filename) + ) if hasattr(morf, '__name__'): name = morf.__name__ diff --git a/tests/test_codeunit.py b/tests/test_codeunit.py index 3b873cd..473bfe4 100644 --- a/tests/test_codeunit.py +++ b/tests/test_codeunit.py @@ -4,7 +4,7 @@ import os import sys from coverage.plugin import FileReporter -from coverage.python import PythonCodeUnit +from coverage.python import PythonFileReporter from tests.coveragetest import CoverageTest @@ -17,21 +17,21 @@ def native(filename): return filename.replace("/", os.sep) -class CodeUnitTest(CoverageTest): - """Tests for coverage.codeunit""" +class FileReporterTest(CoverageTest): + """Tests for FileReporter classes.""" run_in_temp_dir = False def setUp(self): - super(CodeUnitTest, self).setUp() + super(FileReporterTest, self).setUp() # Parent class saves and restores sys.path, we can just modify it. testmods = self.nice_file(os.path.dirname(__file__), 'modules') sys.path.append(testmods) def test_filenames(self): - acu = PythonCodeUnit("aa/afile.py") - bcu = PythonCodeUnit("aa/bb/bfile.py") - ccu = PythonCodeUnit("aa/bb/cc/cfile.py") + acu = PythonFileReporter("aa/afile.py") + bcu = PythonFileReporter("aa/bb/bfile.py") + ccu = PythonFileReporter("aa/bb/cc/cfile.py") self.assertEqual(acu.name, "aa/afile.py") self.assertEqual(bcu.name, "aa/bb/bfile.py") self.assertEqual(ccu.name, "aa/bb/cc/cfile.py") @@ -43,9 +43,9 @@ class CodeUnitTest(CoverageTest): self.assertEqual(ccu.source(), "# cfile.py\n") def test_odd_filenames(self): - acu = PythonCodeUnit("aa/afile.odd.py") - bcu = PythonCodeUnit("aa/bb/bfile.odd.py") - b2cu = PythonCodeUnit("aa/bb.odd/bfile.py") + acu = PythonFileReporter("aa/afile.odd.py") + bcu = PythonFileReporter("aa/bb/bfile.odd.py") + b2cu = PythonFileReporter("aa/bb.odd/bfile.py") self.assertEqual(acu.name, "aa/afile.odd.py") self.assertEqual(bcu.name, "aa/bb/bfile.odd.py") self.assertEqual(b2cu.name, "aa/bb.odd/bfile.py") @@ -61,9 +61,9 @@ class CodeUnitTest(CoverageTest): import aa.bb import aa.bb.cc - acu = PythonCodeUnit(aa) - bcu = PythonCodeUnit(aa.bb) - ccu = PythonCodeUnit(aa.bb.cc) + acu = PythonFileReporter(aa) + bcu = PythonFileReporter(aa.bb) + ccu = PythonFileReporter(aa.bb.cc) self.assertEqual(acu.name, native("aa.py")) self.assertEqual(bcu.name, native("aa/bb.py")) self.assertEqual(ccu.name, native("aa/bb/cc.py")) @@ -79,9 +79,9 @@ class CodeUnitTest(CoverageTest): import aa.bb.bfile import aa.bb.cc.cfile - acu = PythonCodeUnit(aa.afile) - bcu = PythonCodeUnit(aa.bb.bfile) - ccu = PythonCodeUnit(aa.bb.cc.cfile) + acu = PythonFileReporter(aa.afile) + bcu = PythonFileReporter(aa.bb.bfile) + ccu = PythonFileReporter(aa.bb.cc.cfile) self.assertEqual(acu.name, native("aa/afile.py")) self.assertEqual(bcu.name, native("aa/bb/bfile.py")) self.assertEqual(ccu.name, native("aa/bb/cc/cfile.py")) @@ -114,7 +114,7 @@ class CodeUnitTest(CoverageTest): # in the path is actually the .egg zip file. self.assert_doesnt_exist(egg1.__file__) - ecu = PythonCodeUnit(egg1) - eecu = PythonCodeUnit(egg1.egg1) + ecu = PythonFileReporter(egg1) + eecu = PythonFileReporter(egg1.egg1) self.assertEqual(ecu.source(), u"") self.assertEqual(eecu.source().split("\n")[0], u"# My egg file!") -- cgit v1.2.1 From b6a6dd3f9c2411b4dea4110fac0fcad4ec9b6d7d Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 21 Feb 2015 22:25:12 -0500 Subject: codeunit -> filereporter --- tests/test_codeunit.py | 120 --------------------------------------------- tests/test_filereporter.py | 120 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 120 deletions(-) delete mode 100644 tests/test_codeunit.py create mode 100644 tests/test_filereporter.py diff --git a/tests/test_codeunit.py b/tests/test_codeunit.py deleted file mode 100644 index 473bfe4..0000000 --- a/tests/test_codeunit.py +++ /dev/null @@ -1,120 +0,0 @@ -"""Tests for coverage.codeunit""" - -import os -import sys - -from coverage.plugin import FileReporter -from coverage.python import PythonFileReporter - -from tests.coveragetest import CoverageTest - -# pylint: disable=import-error -# Unable to import 'aa' (No module named aa) - - -def native(filename): - """Make `filename` into a native form.""" - return filename.replace("/", os.sep) - - -class FileReporterTest(CoverageTest): - """Tests for FileReporter classes.""" - - run_in_temp_dir = False - - def setUp(self): - super(FileReporterTest, self).setUp() - # Parent class saves and restores sys.path, we can just modify it. - testmods = self.nice_file(os.path.dirname(__file__), 'modules') - sys.path.append(testmods) - - def test_filenames(self): - acu = PythonFileReporter("aa/afile.py") - bcu = PythonFileReporter("aa/bb/bfile.py") - ccu = PythonFileReporter("aa/bb/cc/cfile.py") - self.assertEqual(acu.name, "aa/afile.py") - self.assertEqual(bcu.name, "aa/bb/bfile.py") - self.assertEqual(ccu.name, "aa/bb/cc/cfile.py") - self.assertEqual(acu.flat_rootname(), "aa_afile_py") - self.assertEqual(bcu.flat_rootname(), "aa_bb_bfile_py") - self.assertEqual(ccu.flat_rootname(), "aa_bb_cc_cfile_py") - self.assertEqual(acu.source(), "# afile.py\n") - self.assertEqual(bcu.source(), "# bfile.py\n") - self.assertEqual(ccu.source(), "# cfile.py\n") - - def test_odd_filenames(self): - acu = PythonFileReporter("aa/afile.odd.py") - bcu = PythonFileReporter("aa/bb/bfile.odd.py") - b2cu = PythonFileReporter("aa/bb.odd/bfile.py") - self.assertEqual(acu.name, "aa/afile.odd.py") - self.assertEqual(bcu.name, "aa/bb/bfile.odd.py") - self.assertEqual(b2cu.name, "aa/bb.odd/bfile.py") - self.assertEqual(acu.flat_rootname(), "aa_afile_odd_py") - self.assertEqual(bcu.flat_rootname(), "aa_bb_bfile_odd_py") - self.assertEqual(b2cu.flat_rootname(), "aa_bb_odd_bfile_py") - self.assertEqual(acu.source(), "# afile.odd.py\n") - self.assertEqual(bcu.source(), "# bfile.odd.py\n") - self.assertEqual(b2cu.source(), "# bfile.py\n") - - def test_modules(self): - import aa - import aa.bb - import aa.bb.cc - - acu = PythonFileReporter(aa) - bcu = PythonFileReporter(aa.bb) - ccu = PythonFileReporter(aa.bb.cc) - self.assertEqual(acu.name, native("aa.py")) - self.assertEqual(bcu.name, native("aa/bb.py")) - self.assertEqual(ccu.name, native("aa/bb/cc.py")) - self.assertEqual(acu.flat_rootname(), "aa_py") - self.assertEqual(bcu.flat_rootname(), "aa_bb_py") - self.assertEqual(ccu.flat_rootname(), "aa_bb_cc_py") - self.assertEqual(acu.source(), "# aa\n") - self.assertEqual(bcu.source(), "# bb\n") - self.assertEqual(ccu.source(), "") # yes, empty - - def test_module_files(self): - import aa.afile - import aa.bb.bfile - import aa.bb.cc.cfile - - acu = PythonFileReporter(aa.afile) - bcu = PythonFileReporter(aa.bb.bfile) - ccu = PythonFileReporter(aa.bb.cc.cfile) - self.assertEqual(acu.name, native("aa/afile.py")) - self.assertEqual(bcu.name, native("aa/bb/bfile.py")) - self.assertEqual(ccu.name, native("aa/bb/cc/cfile.py")) - self.assertEqual(acu.flat_rootname(), "aa_afile_py") - self.assertEqual(bcu.flat_rootname(), "aa_bb_bfile_py") - self.assertEqual(ccu.flat_rootname(), "aa_bb_cc_cfile_py") - self.assertEqual(acu.source(), "# afile.py\n") - self.assertEqual(bcu.source(), "# bfile.py\n") - self.assertEqual(ccu.source(), "# cfile.py\n") - - def test_comparison(self): - acu = FileReporter("aa/afile.py") - acu2 = FileReporter("aa/afile.py") - zcu = FileReporter("aa/zfile.py") - bcu = FileReporter("aa/bb/bfile.py") - assert acu == acu2 and acu <= acu2 and acu >= acu2 - assert acu < zcu and acu <= zcu and acu != zcu - assert zcu > acu and zcu >= acu and zcu != acu - assert acu < bcu and acu <= bcu and acu != bcu - assert bcu > acu and bcu >= acu and bcu != acu - - def test_egg(self): - # Test that we can get files out of eggs, and read their source files. - # The egg1 module is installed by an action in igor.py. - import egg1 - import egg1.egg1 - - # Verify that we really imported from an egg. If we did, then the - # __file__ won't be an actual file, because one of the "directories" - # in the path is actually the .egg zip file. - self.assert_doesnt_exist(egg1.__file__) - - ecu = PythonFileReporter(egg1) - eecu = PythonFileReporter(egg1.egg1) - self.assertEqual(ecu.source(), u"") - self.assertEqual(eecu.source().split("\n")[0], u"# My egg file!") diff --git a/tests/test_filereporter.py b/tests/test_filereporter.py new file mode 100644 index 0000000..473bfe4 --- /dev/null +++ b/tests/test_filereporter.py @@ -0,0 +1,120 @@ +"""Tests for coverage.codeunit""" + +import os +import sys + +from coverage.plugin import FileReporter +from coverage.python import PythonFileReporter + +from tests.coveragetest import CoverageTest + +# pylint: disable=import-error +# Unable to import 'aa' (No module named aa) + + +def native(filename): + """Make `filename` into a native form.""" + return filename.replace("/", os.sep) + + +class FileReporterTest(CoverageTest): + """Tests for FileReporter classes.""" + + run_in_temp_dir = False + + def setUp(self): + super(FileReporterTest, self).setUp() + # Parent class saves and restores sys.path, we can just modify it. + testmods = self.nice_file(os.path.dirname(__file__), 'modules') + sys.path.append(testmods) + + def test_filenames(self): + acu = PythonFileReporter("aa/afile.py") + bcu = PythonFileReporter("aa/bb/bfile.py") + ccu = PythonFileReporter("aa/bb/cc/cfile.py") + self.assertEqual(acu.name, "aa/afile.py") + self.assertEqual(bcu.name, "aa/bb/bfile.py") + self.assertEqual(ccu.name, "aa/bb/cc/cfile.py") + self.assertEqual(acu.flat_rootname(), "aa_afile_py") + self.assertEqual(bcu.flat_rootname(), "aa_bb_bfile_py") + self.assertEqual(ccu.flat_rootname(), "aa_bb_cc_cfile_py") + self.assertEqual(acu.source(), "# afile.py\n") + self.assertEqual(bcu.source(), "# bfile.py\n") + self.assertEqual(ccu.source(), "# cfile.py\n") + + def test_odd_filenames(self): + acu = PythonFileReporter("aa/afile.odd.py") + bcu = PythonFileReporter("aa/bb/bfile.odd.py") + b2cu = PythonFileReporter("aa/bb.odd/bfile.py") + self.assertEqual(acu.name, "aa/afile.odd.py") + self.assertEqual(bcu.name, "aa/bb/bfile.odd.py") + self.assertEqual(b2cu.name, "aa/bb.odd/bfile.py") + self.assertEqual(acu.flat_rootname(), "aa_afile_odd_py") + self.assertEqual(bcu.flat_rootname(), "aa_bb_bfile_odd_py") + self.assertEqual(b2cu.flat_rootname(), "aa_bb_odd_bfile_py") + self.assertEqual(acu.source(), "# afile.odd.py\n") + self.assertEqual(bcu.source(), "# bfile.odd.py\n") + self.assertEqual(b2cu.source(), "# bfile.py\n") + + def test_modules(self): + import aa + import aa.bb + import aa.bb.cc + + acu = PythonFileReporter(aa) + bcu = PythonFileReporter(aa.bb) + ccu = PythonFileReporter(aa.bb.cc) + self.assertEqual(acu.name, native("aa.py")) + self.assertEqual(bcu.name, native("aa/bb.py")) + self.assertEqual(ccu.name, native("aa/bb/cc.py")) + self.assertEqual(acu.flat_rootname(), "aa_py") + self.assertEqual(bcu.flat_rootname(), "aa_bb_py") + self.assertEqual(ccu.flat_rootname(), "aa_bb_cc_py") + self.assertEqual(acu.source(), "# aa\n") + self.assertEqual(bcu.source(), "# bb\n") + self.assertEqual(ccu.source(), "") # yes, empty + + def test_module_files(self): + import aa.afile + import aa.bb.bfile + import aa.bb.cc.cfile + + acu = PythonFileReporter(aa.afile) + bcu = PythonFileReporter(aa.bb.bfile) + ccu = PythonFileReporter(aa.bb.cc.cfile) + self.assertEqual(acu.name, native("aa/afile.py")) + self.assertEqual(bcu.name, native("aa/bb/bfile.py")) + self.assertEqual(ccu.name, native("aa/bb/cc/cfile.py")) + self.assertEqual(acu.flat_rootname(), "aa_afile_py") + self.assertEqual(bcu.flat_rootname(), "aa_bb_bfile_py") + self.assertEqual(ccu.flat_rootname(), "aa_bb_cc_cfile_py") + self.assertEqual(acu.source(), "# afile.py\n") + self.assertEqual(bcu.source(), "# bfile.py\n") + self.assertEqual(ccu.source(), "# cfile.py\n") + + def test_comparison(self): + acu = FileReporter("aa/afile.py") + acu2 = FileReporter("aa/afile.py") + zcu = FileReporter("aa/zfile.py") + bcu = FileReporter("aa/bb/bfile.py") + assert acu == acu2 and acu <= acu2 and acu >= acu2 + assert acu < zcu and acu <= zcu and acu != zcu + assert zcu > acu and zcu >= acu and zcu != acu + assert acu < bcu and acu <= bcu and acu != bcu + assert bcu > acu and bcu >= acu and bcu != acu + + def test_egg(self): + # Test that we can get files out of eggs, and read their source files. + # The egg1 module is installed by an action in igor.py. + import egg1 + import egg1.egg1 + + # Verify that we really imported from an egg. If we did, then the + # __file__ won't be an actual file, because one of the "directories" + # in the path is actually the .egg zip file. + self.assert_doesnt_exist(egg1.__file__) + + ecu = PythonFileReporter(egg1) + eecu = PythonFileReporter(egg1.egg1) + self.assertEqual(ecu.source(), u"") + self.assertEqual(eecu.source().split("\n")[0], u"# My egg file!") -- cgit v1.2.1 From 22f3e364c92776a0f10d1fb12b3dd8e685cb3293 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 22 Feb 2015 08:10:32 -0500 Subject: Remove every last trace of CodeUnit, and bring me a glass of vodka --- coverage/annotate.py | 10 +++++----- coverage/html.py | 18 ++++++++--------- coverage/htmlfiles/pyfile.html | 4 ++-- coverage/report.py | 44 +++++++++++++++++++++--------------------- coverage/results.py | 6 +++--- coverage/summary.py | 14 +++++++------- coverage/xmlreport.py | 6 +++--- lab/dataflow.txt | 2 +- tests/test_filereporter.py | 2 +- 9 files changed, 53 insertions(+), 53 deletions(-) diff --git a/coverage/annotate.py b/coverage/annotate.py index 3487feb..b77df4e 100644 --- a/coverage/annotate.py +++ b/coverage/annotate.py @@ -41,10 +41,10 @@ class AnnotateReporter(Reporter): """ self.report_files(self.annotate_file, morfs, directory) - def annotate_file(self, cu, analysis): + def annotate_file(self, fr, analysis): """Annotate a single file. - `cu` is the CodeUnit for the file to annotate. + `fr` is the FileReporter for the file to annotate. """ statements = sorted(analysis.statements) @@ -52,18 +52,18 @@ class AnnotateReporter(Reporter): excluded = sorted(analysis.excluded) if self.directory: - dest_file = os.path.join(self.directory, cu.flat_rootname()) + dest_file = os.path.join(self.directory, fr.flat_rootname()) if dest_file.endswith("_py"): dest_file = dest_file[:-3] + ".py" dest_file += ",cover" else: - dest_file = cu.filename + ",cover" + dest_file = fr.filename + ",cover" with open(dest_file, 'w') as dest: i = 0 j = 0 covered = True - source = cu.source() + source = fr.source() for lineno, line in enumerate(source.splitlines(True), start=1): while i < len(statements) and statements[i] < lineno: i += 1 diff --git a/coverage/html.py b/coverage/html.py index 2a9e0d1..9628216 100644 --- a/coverage/html.py +++ b/coverage/html.py @@ -150,20 +150,20 @@ class HtmlReporter(Reporter): with open(fname, "wb") as fout: fout.write(html.encode('ascii', 'xmlcharrefreplace')) - def file_hash(self, source, cu): + def file_hash(self, source, fr): """Compute a hash that changes if the file needs to be re-reported.""" m = Hasher() m.update(source) - self.coverage.data.add_to_hash(cu.filename, m) + self.coverage.data.add_to_hash(fr.filename, m) return m.hexdigest() - def html_file(self, cu, analysis): + def html_file(self, fr, analysis): """Generate an HTML file for one source file.""" - source = cu.source() + source = fr.source() # Find out if the file on disk is already correct. - flat_rootname = cu.flat_rootname() - this_hash = self.file_hash(source.encode('utf-8'), cu) + flat_rootname = fr.flat_rootname() + this_hash = self.file_hash(source.encode('utf-8'), fr) that_hash = self.status.file_hash(flat_rootname) if this_hash == that_hash: # Nothing has changed to require the file to be reported again. @@ -186,7 +186,7 @@ class HtmlReporter(Reporter): lines = [] - for lineno, line in enumerate(cu.source_token_lines(), start=1): + for lineno, line in enumerate(fr.source_token_lines(), start=1): # Figure out how to mark this line. line_class = [] annotate_html = "" @@ -236,7 +236,7 @@ class HtmlReporter(Reporter): template_values = { 'c_exc': c_exc, 'c_mis': c_mis, 'c_par': c_par, 'c_run': c_run, 'arcs': self.arcs, 'extra_css': self.extra_css, - 'cu': cu, 'nums': nums, 'lines': lines, + 'fr': fr, 'nums': nums, 'lines': lines, } html = spaceless(self.source_tmpl.render(template_values)) @@ -248,7 +248,7 @@ class HtmlReporter(Reporter): index_info = { 'nums': nums, 'html_filename': html_filename, - 'name': cu.name, + 'name': fr.name, } self.files.append(index_info) self.status.set_index_info(flat_rootname, index_info) diff --git a/coverage/htmlfiles/pyfile.html b/coverage/htmlfiles/pyfile.html index 38dfb47..f9d898a 100644 --- a/coverage/htmlfiles/pyfile.html +++ b/coverage/htmlfiles/pyfile.html @@ -5,7 +5,7 @@ {# IE8 rounds line-height incorrectly, and adding this emulateIE7 line makes it right! #} {# http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/7684445e-f080-4d8f-8529-132763348e21 #} - Coverage for {{cu.name|escape}}: {{nums.pc_covered_str}}% + Coverage for {{fr.name|escape}}: {{nums.pc_covered_str}}% {% if extra_css %} @@ -22,7 +22,7 @@