From bcc6d71c52207fa4808f14234231af7d399b975f Mon Sep 17 00:00:00 2001 From: "loic@dachary.org" Date: Fri, 6 Jan 2017 15:42:56 +0100 Subject: make --source module do the same as --source directory #426 The --source argument can either be a module or a directory. The user expects that it behaves the same in both cases. Make sure the module is recursively explored so that files that are not run show in the coverage report. close #426 --HG-- branch : issue-426 --- coverage/control.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index 992ca585..d968b182 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -159,6 +159,7 @@ class Coverage(object): # Other instance attributes, set later. self.omit = self.include = self.source = None + self.source_pkgs_unmatched = None self.source_pkgs = None self.data = self.data_files = self.collector = None self.plugins = None @@ -223,6 +224,7 @@ class Coverage(object): self.source.append(files.canonical_filename(src)) else: self.source_pkgs.append(src) + self.source_pkgs_unmatched = self.source_pkgs[:] self.omit = prep_patterns(self.config.omit) self.include = prep_patterns(self.config.include) @@ -549,8 +551,8 @@ class Coverage(object): # stdlib and coverage.py directories. if self.source_match: if self.source_pkgs_match.match(modulename): - if modulename in self.source_pkgs: - self.source_pkgs.remove(modulename) + if modulename in self.source_pkgs_unmatched: + self.source_pkgs_unmatched.remove(modulename) return None # There's no reason to skip this file. if not self.source_match.match(filename): @@ -815,10 +817,10 @@ class Coverage(object): self.collector.save_data(self.data) - # If there are still entries in the source_pkgs list, then we never + # If there are still entries in the source_pkgs_unmatched list, then we never # encountered those packages. if self._warn_unimported_source: - for pkg in self.source_pkgs: + for pkg in self.source_pkgs_unmatched: if pkg not in sys.modules: self._warn("Module %s was never imported." % pkg) elif not ( @@ -833,8 +835,20 @@ class Coverage(object): if not self.data and self._warn_no_data: self._warn("No data was collected.") + src_directories = self.source[:] + + for pkg in self.source_pkgs: + if (not pkg in sys.modules or + not hasattr(sys.modules[pkg], '__file__') or + not os.path.exists(sys.modules[pkg].__file__)): + continue + pkg_file = sys.modules[pkg].__file__ + if not pkg_file.endswith(('__init__.py', '__init__.pyc', '__init__.pyo')): + continue + src_directories.append(self._canonical_dir(sys.modules[pkg])) + # Find files that were never executed at all. - for src in self.source: + for src in src_directories: for py_file in find_python_files(src): py_file = files.canonical_filename(py_file) -- cgit v1.2.1 From d93a997913baa51989e33c94588d31bd07707b22 Mon Sep 17 00:00:00 2001 From: "loic@dachary.org" Date: Tue, 10 Jan 2017 16:33:14 +0100 Subject: move _source_for_file to python.py --HG-- branch : issue-426 --- coverage/control.py | 32 -------------------------------- 1 file changed, 32 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index d968b182..91600aef 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -360,38 +360,6 @@ class Coverage(object): morf_filename = PythonFileReporter(morf, self).filename return os.path.split(morf_filename)[0] - def _source_for_file(self, filename): - """Return the source file for `filename`. - - Given a file name being traced, return the best guess as to the source - file to attribute it to. - - """ - if filename.endswith(".py"): - # .py files are themselves source files. - return filename - - elif filename.endswith((".pyc", ".pyo")): - # Bytecode files probably have source files near them. - py_filename = filename[:-1] - if os.path.exists(py_filename): - # Found a .py file, use that. - return py_filename - if env.WINDOWS: - # On Windows, it could be a .pyw file. - pyw_filename = py_filename + "w" - if os.path.exists(pyw_filename): - return pyw_filename - # Didn't find source, but it's probably the .py file we want. - return py_filename - - elif filename.endswith("$py.class"): - # Jython is easy to guess. - return filename[:-9] + ".py" - - # No idea, just use the file name as-is. - return filename - def _name_for_module(self, module_globals, filename): """Get the name of the module for a set of globals and file name. -- cgit v1.2.1 From 6da69c9cfcf8874cc423177a9376e90e27218d1d Mon Sep 17 00:00:00 2001 From: "loic@dachary.org" Date: Tue, 10 Jan 2017 17:31:34 +0100 Subject: source_for_file helper with unit tests --HG-- branch : issue-426 --- coverage/control.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index 91600aef..376d3f54 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -27,7 +27,7 @@ from coverage.misc import file_be_gone, isolate_module from coverage.multiproc import patch_multiprocessing from coverage.plugin import FileReporter from coverage.plugin_support import Plugins -from coverage.python import PythonFileReporter +from coverage.python import PythonFileReporter, source_for_file from coverage.results import Analysis, Numbers from coverage.summary import SummaryReporter from coverage.xmlreport import XmlReporter @@ -422,7 +422,7 @@ class Coverage(object): # co_filename value. dunder_file = frame.f_globals.get('__file__') if dunder_file: - filename = self._source_for_file(dunder_file) + filename = source_for_file(dunder_file) if original_filename and not original_filename.startswith('<'): orig = os.path.basename(original_filename) if orig != os.path.basename(filename): -- cgit v1.2.1 From ffc050e243b4c329f582372dd49c55aa225197b9 Mon Sep 17 00:00:00 2001 From: "loic@dachary.org" Date: Tue, 10 Jan 2017 22:21:13 +0100 Subject: use the new source_for_file helper where it makes sense --HG-- branch : issue-426 --- coverage/control.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index 376d3f54..79694320 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -810,8 +810,8 @@ class Coverage(object): not hasattr(sys.modules[pkg], '__file__') or not os.path.exists(sys.modules[pkg].__file__)): continue - pkg_file = sys.modules[pkg].__file__ - if not pkg_file.endswith(('__init__.py', '__init__.pyc', '__init__.pyo')): + pkg_file = source_for_file(sys.modules[pkg].__file__) + if not pkg_file.endswith('__init__.py'): continue src_directories.append(self._canonical_dir(sys.modules[pkg])) -- cgit v1.2.1 From df97ecb1b06192dced1aeeaf3526999c2deafb96 Mon Sep 17 00:00:00 2001 From: "loic@dachary.org" Date: Tue, 10 Jan 2017 22:44:51 +0100 Subject: comment explaining the rationale of --source pkg --HG-- branch : issue-426 --- coverage/control.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'coverage/control.py') diff --git a/coverage/control.py b/coverage/control.py index 79694320..243fe587 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -811,6 +811,12 @@ class Coverage(object): not os.path.exists(sys.modules[pkg].__file__)): continue pkg_file = source_for_file(sys.modules[pkg].__file__) + # + # Do not explore the souce tree of a module that is + # not a directory tree. For instance if + # sys.modules['args'].__file__ == /lib/python2.7/site-packages/args.pyc + # we do not want to explore all of /lib/python2.7/site-packages + # if not pkg_file.endswith('__init__.py'): continue src_directories.append(self._canonical_dir(sys.modules[pkg])) -- cgit v1.2.1