summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2016-05-08 08:25:46 -0400
committerNed Batchelder <ned@nedbatchelder.com>2016-05-08 08:25:46 -0400
commitb918987030fadd52f47cb8b2067e54c59a73d794 (patch)
tree8c0a85e120b165218bdc90398f92612ec49ad9c5
parent36d94fb250dcc838a0fd8f83bb8a6ad53665cb37 (diff)
downloadpython-coveragepy-git-b918987030fadd52f47cb8b2067e54c59a73d794.tar.gz
Don't get confused by namespace packages #456
-rw-r--r--CHANGES.rst5
-rw-r--r--coverage/python.py9
-rw-r--r--tests/test_api.py21
3 files changed, 34 insertions, 1 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index c007a235..a6f0efa4 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -42,12 +42,17 @@ Unreleased
- Officially support PyPy 5.1, which required no changes, just updates to the
docs.
+- It's never been possible to pass a namespace module to one of the analysis
+ functions, but now at least we raise a more specific error message, rather
+ than getting confused. (`issue 456`_)
+
- Make a small tweak to how we compare threads, to avoid buggy custom
comparison code in thread classes. (`issue 245`_)
.. _issue 90: https://bitbucket.org/ned/coveragepy/issues/90/lambda-expression-confuses-branch
.. _issue 245: https://bitbucket.org/ned/coveragepy/issues/245/change-solution-for-issue-164
.. _issue 440: https://bitbucket.org/ned/coveragepy/issues/440/yielded-twisted-failure-marked-as-missed
+.. _issue 456: https://bitbucket.org/ned/coveragepy/issues/456/coverage-breaks-with-implicit-namespaces
.. _issue 460: https://bitbucket.org/ned/coveragepy/issues/460/confusing-html-report-for-certain-partial
.. _issue 469: https://bitbucket.org/ned/coveragepy/issues/469/strange-1-line-number-in-branch-coverage
.. _issue 472: https://bitbucket.org/ned/coveragepy/issues/472/html-report-indents-incorrectly-for-one
diff --git a/coverage/python.py b/coverage/python.py
index 0cd2181c..8e98bea8 100644
--- a/coverage/python.py
+++ b/coverage/python.py
@@ -4,10 +4,13 @@
"""Python source expertise for coverage.py"""
import os.path
+import types
import zipimport
from coverage import env, files
-from coverage.misc import contract, expensive, NoSource, join_regex, isolate_module
+from coverage.misc import (
+ contract, CoverageException, expensive, NoSource, join_regex, isolate_module,
+)
from coverage.parser import PythonParser
from coverage.phystokens import source_token_lines, source_encoding
from coverage.plugin import FileReporter
@@ -94,6 +97,10 @@ class PythonFileReporter(FileReporter):
if hasattr(morf, '__file__'):
filename = morf.__file__
+ elif isinstance(morf, types.ModuleType):
+ # A module should have had .__file__, otherwise we can't use it.
+ # This could be a PEP-420 namespace package.
+ raise CoverageException("Module {0} has no file".format(morf))
else:
filename = morf
diff --git a/tests/test_api.py b/tests/test_api.py
index 3ea5b3ef..4c038519 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -9,6 +9,7 @@ import sys
import textwrap
import coverage
+from coverage import env
from coverage.backward import StringIO
from coverage.misc import CoverageException
@@ -322,6 +323,26 @@ class ApiTest(CoverageTest):
self.assert_exists(".coverage.foo")
+class NamespaceModuleTest(CoverageTest):
+ """Test PEP-420 namespace modules."""
+
+ def setUp(self):
+ super(NamespaceModuleTest, self).setUp()
+ if env.PYVERSION < (3, 3):
+ self.skip("Python before 3.3 doesn't have namespace packages")
+
+ def test_explicit_namespace_module(self):
+ self.make_file("namespace/package/module.py", "VAR = 1\n")
+ self.make_file("main.py", "import namespace\n")
+
+ cov = coverage.Coverage()
+ self.start_import_stop(cov, "main")
+
+ with self.assertRaisesRegex(CoverageException, r"Module .* has no file"):
+ cov.analysis(sys.modules['namespace'])
+
+
+
class UsingModulesMixin(object):
"""A mixin for importing modules from test/modules and test/moremodules."""