Skip to content

Commit fdfc91c

Browse files
committed
Don't import anything before start(). Fixes #909.
1 parent 7541b4f commit fdfc91c

File tree

2 files changed

+37
-21
lines changed

2 files changed

+37
-21
lines changed

CHANGES.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ Unreleased
3030
information about the config files read now shows absolute paths to the
3131
files.
3232

33+
- When running programs as modules (``coverage run -m``) with ``--source``,
34+
some measured modules were imported before coverage starts. This resulted in
35+
unwanted warnings ("Already imported a file that will be measured") and a
36+
reduction in coverage totals (`issue 909`_). This is now fixed.
37+
3338
- The handling of source files with non-encodable file names has changed.
3439
Previously, if a file name could not be encoded as UTF-8, an error occurred,
3540
as described in `issue 891`_. Now, those files will not be measured, since
@@ -45,6 +50,7 @@ Unreleased
4550
.. _issue 891: https://github.com/nedbat/coveragepy/issues/891
4651
.. _issue 901: https://github.com/nedbat/coveragepy/issues/901
4752
.. _issue 907: https://github.com/nedbat/coveragepy/issues/907
53+
.. _issue 909: https://github.com/nedbat/coveragepy/issues/909
4854

4955

5056
.. _changes_501:

coverage/execfile.py

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -116,21 +116,45 @@ def __init__(self, args, as_module=False):
116116
self.package = self.modulename = self.pathname = self.loader = self.spec = None
117117

118118
def prepare(self):
119-
"""Do initial preparation to run Python code.
120-
121-
Includes finding the module to run, adjusting sys.argv[0], and changing
122-
sys.path to match what Python does.
119+
"""Set sys.path properly.
123120
121+
This needs to happen before any importing, and without importing anything.
124122
"""
125123
should_update_sys_path = True
126-
127124
if self.as_module:
128125
if env.PYBEHAVIOR.actual_syspath0_dash_m:
129126
path0 = os.getcwd()
130127
else:
131128
path0 = ""
132129
sys.path[0] = path0
133130
should_update_sys_path = False
131+
elif os.path.isdir(self.arg0):
132+
# Running a directory means running the __main__.py file in that
133+
# directory.
134+
path0 = self.arg0
135+
else:
136+
path0 = os.path.abspath(os.path.dirname(self.arg0))
137+
138+
139+
if should_update_sys_path:
140+
# sys.path fakery. If we are being run as a command, then sys.path[0]
141+
# is the directory of the "coverage" script. If this is so, replace
142+
# sys.path[0] with the directory of the file we're running, or the
143+
# current directory when running modules. If it isn't so, then we
144+
# don't know what's going on, and just leave it alone.
145+
top_file = inspect.stack()[-1][0].f_code.co_filename
146+
if os.path.abspath(sys.path[0]) == os.path.abspath(os.path.dirname(top_file)):
147+
# Set sys.path correctly.
148+
sys.path[0] = python_reported_file(path0)
149+
150+
def _prepare2(self):
151+
"""Do more preparation to run Python code.
152+
153+
Includes finding the module to run and adjusting sys.argv[0].
154+
This method is allowed to import code.
155+
156+
"""
157+
if self.as_module:
134158
self.modulename = self.arg0
135159
pathname, self.package, self.spec = find_module(self.modulename)
136160
if self.spec is not None:
@@ -141,7 +165,6 @@ def prepare(self):
141165
elif os.path.isdir(self.arg0):
142166
# Running a directory means running the __main__.py file in that
143167
# directory.
144-
path0 = self.arg0
145168
for ext in [".py", ".pyc", ".pyo"]:
146169
try_filename = os.path.join(self.arg0, "__main__" + ext)
147170
if os.path.exists(try_filename):
@@ -165,29 +188,16 @@ def prepare(self):
165188
self.package = ""
166189
self.loader = DummyLoader("__main__")
167190
else:
168-
path0 = os.path.abspath(os.path.dirname(self.arg0))
169191
if env.PY3:
170192
self.loader = DummyLoader("__main__")
171193

172194
self.arg0 = python_reported_file(self.arg0)
173195

174-
if self.modulename is None:
175-
self.modulename = '__main__'
176-
177-
if should_update_sys_path:
178-
# sys.path fakery. If we are being run as a command, then sys.path[0]
179-
# is the directory of the "coverage" script. If this is so, replace
180-
# sys.path[0] with the directory of the file we're running, or the
181-
# current directory when running modules. If it isn't so, then we
182-
# don't know what's going on, and just leave it alone.
183-
top_file = inspect.stack()[-1][0].f_code.co_filename
184-
if os.path.abspath(sys.path[0]) == os.path.abspath(os.path.dirname(top_file)):
185-
# Set sys.path correctly.
186-
sys.path[0] = python_reported_file(path0)
187-
188196
def run(self):
189197
"""Run the Python code!"""
190198

199+
self._prepare2()
200+
191201
# Create a module to serve as __main__
192202
main_mod = types.ModuleType('__main__')
193203

0 commit comments

Comments
 (0)