summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2020-01-03 07:07:33 -0500
committerNed Batchelder <ned@nedbatchelder.com>2020-01-03 10:11:05 -0500
commitfdfc91c70a37e79f1ebf84bbbfb7c5513d0d6b27 (patch)
treef6ffe697a42542722c5b97d8dc97faa92c42d5ff
parent7541b4f642534b382244f1174991df00b2efb9f3 (diff)
downloadpython-coveragepy-git-fdfc91c70a37e79f1ebf84bbbfb7c5513d0d6b27.tar.gz
Don't import anything before start(). Fixes #909.
-rw-r--r--CHANGES.rst6
-rw-r--r--coverage/execfile.py52
2 files changed, 37 insertions, 21 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 226e3701..817921f7 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -30,6 +30,11 @@ Unreleased
information about the config files read now shows absolute paths to the
files.
+- When running programs as modules (``coverage run -m``) with ``--source``,
+ some measured modules were imported before coverage starts. This resulted in
+ unwanted warnings ("Already imported a file that will be measured") and a
+ reduction in coverage totals (`issue 909`_). This is now fixed.
+
- The handling of source files with non-encodable file names has changed.
Previously, if a file name could not be encoded as UTF-8, an error occurred,
as described in `issue 891`_. Now, those files will not be measured, since
@@ -45,6 +50,7 @@ Unreleased
.. _issue 891: https://github.com/nedbat/coveragepy/issues/891
.. _issue 901: https://github.com/nedbat/coveragepy/issues/901
.. _issue 907: https://github.com/nedbat/coveragepy/issues/907
+.. _issue 909: https://github.com/nedbat/coveragepy/issues/909
.. _changes_501:
diff --git a/coverage/execfile.py b/coverage/execfile.py
index 628b5c94..928f9d35 100644
--- a/coverage/execfile.py
+++ b/coverage/execfile.py
@@ -116,14 +116,11 @@ class PyRunner(object):
self.package = self.modulename = self.pathname = self.loader = self.spec = None
def prepare(self):
- """Do initial preparation to run Python code.
-
- Includes finding the module to run, adjusting sys.argv[0], and changing
- sys.path to match what Python does.
+ """Set sys.path properly.
+ This needs to happen before any importing, and without importing anything.
"""
should_update_sys_path = True
-
if self.as_module:
if env.PYBEHAVIOR.actual_syspath0_dash_m:
path0 = os.getcwd()
@@ -131,6 +128,33 @@ class PyRunner(object):
path0 = ""
sys.path[0] = path0
should_update_sys_path = False
+ elif os.path.isdir(self.arg0):
+ # Running a directory means running the __main__.py file in that
+ # directory.
+ path0 = self.arg0
+ else:
+ path0 = os.path.abspath(os.path.dirname(self.arg0))
+
+
+ if should_update_sys_path:
+ # sys.path fakery. If we are being run as a command, then sys.path[0]
+ # is the directory of the "coverage" script. If this is so, replace
+ # sys.path[0] with the directory of the file we're running, or the
+ # current directory when running modules. If it isn't so, then we
+ # don't know what's going on, and just leave it alone.
+ top_file = inspect.stack()[-1][0].f_code.co_filename
+ if os.path.abspath(sys.path[0]) == os.path.abspath(os.path.dirname(top_file)):
+ # Set sys.path correctly.
+ sys.path[0] = python_reported_file(path0)
+
+ def _prepare2(self):
+ """Do more preparation to run Python code.
+
+ Includes finding the module to run and adjusting sys.argv[0].
+ This method is allowed to import code.
+
+ """
+ if self.as_module:
self.modulename = self.arg0
pathname, self.package, self.spec = find_module(self.modulename)
if self.spec is not None:
@@ -141,7 +165,6 @@ class PyRunner(object):
elif os.path.isdir(self.arg0):
# Running a directory means running the __main__.py file in that
# directory.
- path0 = self.arg0
for ext in [".py", ".pyc", ".pyo"]:
try_filename = os.path.join(self.arg0, "__main__" + ext)
if os.path.exists(try_filename):
@@ -165,29 +188,16 @@ class PyRunner(object):
self.package = ""
self.loader = DummyLoader("__main__")
else:
- path0 = os.path.abspath(os.path.dirname(self.arg0))
if env.PY3:
self.loader = DummyLoader("__main__")
self.arg0 = python_reported_file(self.arg0)
- if self.modulename is None:
- self.modulename = '__main__'
-
- if should_update_sys_path:
- # sys.path fakery. If we are being run as a command, then sys.path[0]
- # is the directory of the "coverage" script. If this is so, replace
- # sys.path[0] with the directory of the file we're running, or the
- # current directory when running modules. If it isn't so, then we
- # don't know what's going on, and just leave it alone.
- top_file = inspect.stack()[-1][0].f_code.co_filename
- if os.path.abspath(sys.path[0]) == os.path.abspath(os.path.dirname(top_file)):
- # Set sys.path correctly.
- sys.path[0] = python_reported_file(path0)
-
def run(self):
"""Run the Python code!"""
+ self._prepare2()
+
# Create a module to serve as __main__
main_mod = types.ModuleType('__main__')