def test_source_for_file(tmpdir): path = tmpdir.join("a.py") src = str(path) assert source_for_file(src) == src assert source_for_file(src + 'c') == src assert source_for_file(src + 'o') == src unknown = src + 'FOO' assert source_for_file(unknown) == unknown
def test_source_for_file_windows(tmpdir): path = tmpdir.join("a.py") src = str(path) # On windows if a pyw exists, it is an acceptable source path_windows = tmpdir.ensure("a.pyw") assert str(path_windows) == source_for_file(src + 'c') # If both pyw and py exist, py is preferred path.ensure(file=True) assert source_for_file(src + 'c') == src
def _post_save_work(self): """After saving data, look for warnings, post-work, etc. Warn about things that should have happened but didn't. Look for unexecuted files. """ # 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_unmatched: self._warn_about_unmeasured_code(pkg) # Find out if we got any data. if not self.data and self._warn_no_data: self._warn("No data was collected.", slug="no-data-collected") # Find files that were never executed at all. 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 = source_for_file(sys.modules[pkg].__file__) self._find_unexecuted_files(self._canonical_path(pkg_file)) for src in self.source: self._find_unexecuted_files(src) if self.config.note: self.data.add_run_info(note=self.config.note)
def _post_save_work(self): """After saving data, look for warnings, post-work, etc. Warn about things that should have happened but didn't. Look for unexecuted files. """ # 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_unmatched: self._warn_about_unmeasured_code(pkg) # Find out if we got any data. if not self.data and self._warn_no_data: self._warn("No data was collected.", slug="no-data-collected") # Find files that were never executed at all. 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 = source_for_file(sys.modules[pkg].__file__) self._find_unexecuted_files(self._canonical_path(pkg_file)) for src in self.source: self._find_unexecuted_files(src) if self.config.note: self.data.add_run_info(note=self.config.note)
def get_data(self): """Get the collected data and reset the collector. Also warn about various problems collecting data. Returns a :class:`coverage.CoverageData`, the collected coverage data. .. versionadded:: 4.0 """ self._init() if not self._measured: return self.data self.collector.save_data(self.data) # 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_unmatched: if pkg not in sys.modules: self._warn("Module %s was never imported." % pkg) elif not (hasattr(sys.modules[pkg], '__file__') and os.path.exists(sys.modules[pkg].__file__)): self._warn("Module %s has no Python source." % pkg) else: self._warn( "Module %s was previously imported, but not measured." % pkg) # Find out if we got any data. if not self.data and self._warn_no_data: self._warn("No data was collected.") # Find files that were never executed at all. 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 = source_for_file(sys.modules[pkg].__file__) if not pkg_file.endswith('__init__.py'): # We only want to explore the source tree of packages. # For instance if pkg_file is /lib/python2.7/site-packages/args.pyc, # we do not want to explore all of /lib/python2.7/site-packages. continue self._find_unexecuted_files(self._canonical_dir(pkg_file)) for src in self.source: self._find_unexecuted_files(src) if self.config.note: self.data.add_run_info(note=self.config.note) self._measured = False return self.data
def get_data(self): """Get the collected data and reset the collector. Also warn about various problems collecting data. Returns a :class:`coverage.CoverageData`, the collected coverage data. .. versionadded:: 4.0 """ self._init() if not self._measured: return self.data self.collector.save_data(self.data) # 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_unmatched: if pkg not in sys.modules: self._warn("Module %s was never imported." % pkg) elif not ( hasattr(sys.modules[pkg], '__file__') and os.path.exists(sys.modules[pkg].__file__) ): self._warn("Module %s has no Python source." % pkg) else: self._warn("Module %s was previously imported, but not measured." % pkg) # Find out if we got any data. if not self.data and self._warn_no_data: self._warn("No data was collected.") # Find files that were never executed at all. 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 = source_for_file(sys.modules[pkg].__file__) if not pkg_file.endswith('__init__.py'): # We only want to explore the source tree of packages. # For instance if pkg_file is /lib/python2.7/site-packages/args.pyc, # we do not want to explore all of /lib/python2.7/site-packages. continue self._find_unexecuted_files(self._canonical_dir(pkg_file)) for src in self.source: self._find_unexecuted_files(src) if self.config.note: self.data.add_run_info(note=self.config.note) self._measured = False return self.data
def find_unexecuted_files(self): 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 = source_for_file(sys.modules[pkg].__file__) for ret in self._find_unexecuted_files(canonical_path(pkg_file)): yield ret for src in self.source: for ret in self._find_unexecuted_files(src): yield ret
def find_possibly_unexecuted_files(self): """Find files in the areas of interest that might be untraced. Yields pairs: file path, and responsible plug-in name. """ for pkg in self.source_pkgs: if (not pkg in sys.modules or not module_has_file(sys.modules[pkg])): continue pkg_file = source_for_file(sys.modules[pkg].__file__) yield from self._find_executable_files(canonical_path(pkg_file)) for src in self.source: yield from self._find_executable_files(src)
def find_unexecuted_files(self): """Find files in the areas of interest that weren't traced. Yields pairs: file path, and responsible plug-in name. """ for pkg in self.source_pkgs: if (not pkg in sys.modules or not module_has_file(sys.modules[pkg])): continue pkg_file = source_for_file(sys.modules[pkg].__file__) for ret in self._find_unexecuted_files(canonical_path(pkg_file)): yield ret for src in self.source: for ret in self._find_unexecuted_files(src): yield ret
def should_trace(self, filename, frame=None): """Decide whether to trace execution in `filename`, with a reason. This function is called from the trace function. As each new file name is encountered, this function determines whether it is traced or not. Returns a FileDisposition object. """ original_filename = filename disp = disposition_init(self.disp_class, filename) def nope(disp, reason): """Simple helper to make it easy to return NO.""" disp.trace = False disp.reason = reason return disp if frame is not None: # Compiled Python files have two file names: frame.f_code.co_filename is # the file name at the time the .pyc was compiled. The second name is # __file__, which is where the .pyc was actually loaded from. Since # .pyc files can be moved after compilation (for example, by being # installed), we look for __file__ in the frame and prefer it to the # co_filename value. dunder_file = frame.f_globals and frame.f_globals.get('__file__') if 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): # Files shouldn't be renamed when moved. This happens when # exec'ing code. If it seems like something is wrong with # the frame's file name, then just use the original. filename = original_filename if not filename: # Empty string is pretty useless. return nope(disp, "empty string isn't a file name") if filename.startswith('memory:'): return nope(disp, "memory isn't traceable") if filename.startswith('<'): # Lots of non-file execution is represented with artificial # file names like "<string>", "<doctest readme.txt[0]>", or # "<exec_function>". Don't ever trace these executions, since we # can't do anything with the data later anyway. return nope(disp, "not a real file name") # pyexpat does a dumb thing, calling the trace function explicitly from # C code with a C file name. if re.search(r"[/\\]Modules[/\\]pyexpat.c", filename): return nope(disp, "pyexpat lies about itself") # Jython reports the .class file to the tracer, use the source file. if filename.endswith("$py.class"): filename = filename[:-9] + ".py" canonical = canonical_filename(filename) disp.canonical_filename = canonical # Try the plugins, see if they have an opinion about the file. plugin = None for plugin in self.plugins.file_tracers: if not plugin._coverage_enabled: continue try: file_tracer = plugin.file_tracer(canonical) if file_tracer is not None: file_tracer._coverage_plugin = plugin disp.trace = True disp.file_tracer = file_tracer if file_tracer.has_dynamic_source_filename(): disp.has_dynamic_filename = True else: disp.source_filename = canonical_filename( file_tracer.source_filename() ) break except Exception: self.warn( "Disabling plug-in %r due to an exception:" % (plugin._coverage_plugin_name) ) traceback.print_exc() plugin._coverage_enabled = False continue else: # No plugin wanted it: it's Python. disp.trace = True disp.source_filename = canonical if not disp.has_dynamic_filename: if not disp.source_filename: raise CoverageException( "Plugin %r didn't set source_filename for %r" % (plugin, disp.original_filename) ) reason = self.check_include_omit_etc(disp.source_filename, frame) if reason: nope(disp, reason) return disp
def _should_trace_internal(self, filename, frame): """Decide whether to trace execution in `filename`, with a reason. This function is called from the trace function. As each new file name is encountered, this function determines whether it is traced or not. Returns a FileDisposition object. """ original_filename = filename disp = _disposition_init(self.collector.file_disposition_class, filename) def nope(disp, reason): """Simple helper to make it easy to return NO.""" disp.trace = False disp.reason = reason return disp # Compiled Python files have two file names: frame.f_code.co_filename is # the file name at the time the .pyc was compiled. The second name is # __file__, which is where the .pyc was actually loaded from. Since # .pyc files can be moved after compilation (for example, by being # installed), we look for __file__ in the frame and prefer it to the # co_filename value. dunder_file = frame.f_globals and frame.f_globals.get('__file__') if 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): # Files shouldn't be renamed when moved. This happens when # exec'ing code. If it seems like something is wrong with # the frame's file name, then just use the original. filename = original_filename if not filename: # Empty string is pretty useless. return nope(disp, "empty string isn't a file name") if filename.startswith('memory:'): return nope(disp, "memory isn't traceable") if filename.startswith('<'): # Lots of non-file execution is represented with artificial # file names like "<string>", "<doctest readme.txt[0]>", or # "<exec_function>". Don't ever trace these executions, since we # can't do anything with the data later anyway. return nope(disp, "not a real file name") # pyexpat does a dumb thing, calling the trace function explicitly from # C code with a C file name. if re.search(r"[/\\]Modules[/\\]pyexpat.c", filename): return nope(disp, "pyexpat lies about itself") # Jython reports the .class file to the tracer, use the source file. if filename.endswith("$py.class"): filename = filename[:-9] + ".py" canonical = canonical_filename(filename) disp.canonical_filename = canonical # Try the plugins, see if they have an opinion about the file. plugin = None for plugin in self.plugins.file_tracers: if not plugin._coverage_enabled: continue try: file_tracer = plugin.file_tracer(canonical) if file_tracer is not None: file_tracer._coverage_plugin = plugin disp.trace = True disp.file_tracer = file_tracer if file_tracer.has_dynamic_source_filename(): disp.has_dynamic_filename = True else: disp.source_filename = canonical_filename( file_tracer.source_filename() ) break except Exception: self._warn( "Disabling plug-in %r due to an exception:" % ( plugin._coverage_plugin_name ) ) traceback.print_exc() plugin._coverage_enabled = False continue else: # No plugin wanted it: it's Python. disp.trace = True disp.source_filename = canonical if not disp.has_dynamic_filename: if not disp.source_filename: raise CoverageException( "Plugin %r didn't set source_filename for %r" % (plugin, disp.original_filename) ) reason = self._check_include_omit_etc_internal( disp.source_filename, frame, ) if reason: nope(disp, reason) return disp
def test_source_for_file_jython(): assert source_for_file("a$py.class") == "a.py"
def test_source_for_file_jython(): assert source_for_file("a$py.class") == "a.py"