diff options
-rw-r--r-- | CHANGES.txt | 7 | ||||
-rw-r--r-- | coverage/results.py | 48 | ||||
-rw-r--r-- | tests/test_summary.py | 21 |
3 files changed, 66 insertions, 10 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 795bc659..10ad0b22 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,6 +15,8 @@ Change history for Coverage.py - Coverage can now run .pyc files directly, closing `issue 264`_. +- Coverage properly supports .pyw files, fixing `issue 261`_. + - Omitting files within a tree specified with the ``source`` option would cause them to be incorrectly marked as unexecuted, as described in `issue 218`_. This is now fixed. @@ -31,12 +33,12 @@ Change history for Coverage.py changed, measurement is likely wrong: None." This fixes `issue 164`_. - Static files necessary for HTML reports are found in system-installed places, - to ease OS-level packaging of coverage.py. Closes `issue 259`. + to ease OS-level packaging of coverage.py. Closes `issue 259`_. - Source files with encoding declarations, but a blank first line, were not decoded properly. Now they are. Thanks, Roger Hu. -- The source kit now includes the `__main__.py` file in the root coverage +- The source kit now includes the ``__main__.py`` file in the root coverage directory, fixing `issue 255`_. .. _issue 92: https://bitbucket.org/ned/coveragepy/issue/92/finally-clauses-arent-treated-properly-in @@ -47,6 +49,7 @@ Change history for Coverage.py .. _issue 218: https://bitbucket.org/ned/coveragepy/issue/218/run-command-does-not-respect-the-omit-flag .. _issue 255: https://bitbucket.org/ned/coveragepy/issue/255/directory-level-__main__py-not-included-in .. _issue 259: https://bitbucket.org/ned/coveragepy/issue/259/allow-use-of-system-installed-third-party +.. _issue 261: https://bitbucket.org/ned/coveragepy/issue/261/pyw-files-arent-reported-properly .. _issue 264: https://bitbucket.org/ned/coveragepy/issue/264/coverage-wont-run-pyc-files .. _issue 267: https://bitbucket.org/ned/coveragepy/issue/267/relative-path-aliases-dont-work diff --git a/coverage/results.py b/coverage/results.py index 77ff2a2d..2d13e818 100644 --- a/coverage/results.py +++ b/coverage/results.py @@ -15,16 +15,10 @@ class Analysis(object): self.code_unit = code_unit self.filename = self.code_unit.filename - ext = os.path.splitext(self.filename)[1] - source = None - if ext == '.py': - if not os.path.exists(self.filename): - source = self.coverage.file_locator.get_zip_data(self.filename) - if not source: - raise NoSource("No source for code: '%s'" % self.filename) + actual_filename, source = self.find_source(self.filename) self.parser = CodeParser( - text=source, filename=self.filename, + text=source, filename=actual_filename, exclude=self.coverage._exclude_regex('exclude') ) self.statements, self.excluded = self.parser.parse_source() @@ -59,6 +53,44 @@ class Analysis(object): n_missing_branches=n_missing_branches, ) + def find_source(self, filename): + """Find the source for `filename`. + + Returns two values: the actual filename, and the source. + + The source returned depends on which of these cases holds: + + * The filename seems to be a non-source file: returns None + + * The filename is a source file, and actually exists: returns None. + + * The filename is a source file, and is in a zip file or egg: + returns the source. + + * The filename is a source file, but couldn't be found: raises + `NoSource`. + + """ + source = None + + base, ext = os.path.splitext(filename) + TRY_EXTS = { + '.py': ['.py', '.pyw'], + '.pyw': ['.pyw'], + } + try_exts = TRY_EXTS.get(ext) + if not try_exts: + return filename, None + + for try_ext in try_exts: + try_filename = base + try_ext + if os.path.exists(try_filename): + return try_filename, None + source = self.coverage.file_locator.get_zip_data(try_filename) + if source: + return try_filename, source + raise NoSource("No source for code: '%s'" % filename) + def missing_formatted(self): """The missing line numbers, formatted nicely. diff --git a/tests/test_summary.py b/tests/test_summary.py index f8426779..d61c1bf4 100644 --- a/tests/test_summary.py +++ b/tests/test_summary.py @@ -230,6 +230,27 @@ class SummaryTest(CoverageTest): self.assertIn("TheCode", report) self.assertNotIn("thecode", report) + if sys.platform == 'win32': + def test_pyw_files(self): + # https://bitbucket.org/ned/coveragepy/issue/261 + self.make_file("start.pyw", """\ + import mod + print("In start.pyw") + """) + self.make_file("mod.pyw", """\ + print("In mod.pyw") + """) + cov = coverage.coverage() + cov.start() + import start # pragma: nested + cov.stop() # pragma: nested + + report = self.get_report(cov) + self.assertNotIn("NoSource", report) + report = report.splitlines() + self.assertIn("start 2 0 100%", report) + self.assertIn("mod 1 0 100%", report) + class SummaryTest2(CoverageTest): """Another bunch of summary tests.""" |