def run_python_module(modulename, args): openfile = None glo, loc = globals(), locals() try: if '.' in modulename: packagename, name = rsplit1(modulename, '.') package = __import__(packagename, glo, loc, ['__path__']) searchpath = package.__path__ else: packagename, name = None, modulename searchpath = None openfile, pathname, _ = imp.find_module(name, searchpath) if openfile is None and pathname is None: raise NoSource('module does not live in a file: %r' % modulename) if openfile is None: packagename = modulename name = '__main__' package = __import__(packagename, glo, loc, ['__path__']) searchpath = package.__path__ openfile, pathname, _ = imp.find_module(name, searchpath) except ImportError: _, err, _ = sys.exc_info() raise NoSource(str(err)) finally: if openfile: openfile.close() pathname = os.path.abspath(pathname) args[0] = pathname run_python_file(pathname, args, package=packagename)
def find_module(modulename): """Find the module named `modulename`. Returns the file path of the module, and the name of the enclosing package. """ # pylint: disable=no-member try: spec = importlib_util_find_spec(modulename) except ImportError as err: raise NoSource(str(err)) if not spec: raise NoSource("No module named %r" % (modulename,)) pathname = spec.origin packagename = spec.name if pathname.endswith("__init__.py"): mod_main = modulename + ".__main__" spec = importlib_util_find_spec(mod_main) if not spec: raise NoSource( "No module named %s; " "%r is a package and cannot be directly executed" % (mod_main, modulename) ) pathname = spec.origin packagename = spec.name packagename = packagename.rpartition(".")[0] return pathname, packagename
def find_module(modulename): """Find the module named `modulename`. Returns the file path of the module, the name of the enclosing package, and the spec. """ try: spec = importlib_util_find_spec(modulename) except ImportError as err: raise NoSource(str(err)) if not spec: raise NoSource("No module named %r" % (modulename, )) pathname = spec.origin packagename = spec.name if spec.submodule_search_locations: mod_main = modulename + ".__main__" spec = importlib_util_find_spec(mod_main) if not spec: raise NoSource( "No module named %s; " "%r is a package and cannot be directly executed" % (mod_main, modulename)) pathname = spec.origin packagename = spec.name packagename = packagename.rpartition(".")[0] return pathname, packagename, spec
def __init__(self, cov, code_unit): self.coverage = cov self.code_unit = code_unit self.filename = self.code_unit.filename ext = os.path.splitext(self.filename)[1] source = None if os.path.exists(self.filename): try: self.source = read_file(self.filename) except: _, err, _ = sys.exc_info() raise NoSource("No source for code: %r: %s" % (self.filename, err)) if self.source is None: raise NoSource("No source for code: %r" % self.filename) self.parser = DjangoTemplateCodeParser( text=source, filename=self.filename, exclude=self.coverage._exclude_regex('exclude')) self.statements, self.excluded = self.parser.parse_source() # Identify missing statements. executed = self.coverage.data.executed_lines(self.filename) self.missing = sorted(set(self.statements) - set(executed)) if self.coverage.data.has_arcs(): self.no_branch = self.parser.lines_matching( join_regex(self.coverage.config.partial_list), join_regex(self.coverage.config.partial_always_list)) n_branches = self.total_branches() mba = self.missing_branch_arcs() n_missing_branches = sum( [len(v) for k, v in mba.items() if k not in self.missing]) else: n_branches = n_missing_branches = 0 self.no_branch = set() self.numbers = Numbers( n_files=1, n_statements=len(self.statements), n_excluded=len(self.excluded), n_missing=len(self.missing), n_branches=n_branches, n_missing_branches=n_missing_branches, )
def get_python_source(filename): """Return the source code, as unicode.""" base, ext = os.path.splitext(filename) if ext == ".py" and env.WINDOWS: exts = [".py", ".pyw"] else: exts = [ext] for ext in exts: try_filename = base + ext if os.path.exists(try_filename): # A regular text file: open it. source = read_python_source(try_filename) break # Maybe it's in a zip file? source = get_zip_bytes(try_filename) if source is not None: break else: # Couldn't find source. raise NoSource("No source for code: '%s'." % filename) source = source.decode(source_encoding(source), "replace") # Python code should always end with a line with a newline. if source and source[-1] != '\n': source += '\n' return source
def __init__(self, text=None, filename=None, exclude=None): """ Source can be provided as `text`, the text itself, or `filename`, from which the text will be read. Excluded lines are those that match `exclude`, a regex. """ self.filename = filename or '<code>' self.text = text if not self.text: try: sourcef = open_source(self.filename) try: self.text = sourcef.read() finally: sourcef.close() except IOError: _, err, _ = sys.exc_info() raise NoSource("No source for code: '%s': %s" % (self.filename, err)) if self.text and ord(self.text[0]) == 65279: self.text = self.text[1:] self.exclude = exclude self.show_tokens = False self.lines = self.text.split('\n') self.excluded = set() self.docstrings = set() self.classdefs = set() self.multiline = {} self.statement_starts = set() self._byte_parser = None
def find_source(self, filename): """Find the source for `filename`. Returns two values: the actual filename, and the source. The source returned depends on which of these cases holds: * The filename seems to be a non-source file: returns None * The filename is a source file, and actually exists: returns None. * The filename is a source file, and is in a zip file or egg: returns the source. * The filename is a source file, but couldn't be found: raises `NoSource`. """ source = None base, ext = os.path.splitext(filename) TRY_EXTS = {'.py': ['.py', '.pyw'], '.pyw': ['.pyw']} try_exts = TRY_EXTS.get(ext) if not try_exts: return (filename, None) for try_ext in try_exts: try_filename = base + try_ext if os.path.exists(try_filename): return (try_filename, None) source = self.coverage.file_locator.get_zip_data(try_filename) if source: return (try_filename, source) raise NoSource("No source for code: '%s'" % filename)
def run_python_file(filename, args): """Run a python file as if it were the main program on the command line. `filename` is the path to the file to execute, it need not be a .py file. `args` is the argument array to present as sys.argv, including the first element representing the file being executed. """ # Create a module to serve as __main__ old_main_mod = sys.modules['__main__'] main_mod = imp.new_module('__main__') sys.modules['__main__'] = main_mod main_mod.__file__ = filename main_mod.__builtins__ = BUILTINS # Set sys.argv and the first path element properly. old_argv = sys.argv old_path0 = sys.path[0] sys.argv = args sys.path[0] = os.path.dirname(filename) try: try: source = open(filename, 'rU').read() except IOError: raise NoSource("No file to run: %r" % filename) exec_function(source, filename, main_mod.__dict__) finally: # Restore the old __main__ sys.modules['__main__'] = old_main_mod # Restore the old argv and path sys.argv = old_argv sys.path[0] = old_path0
def __init__(self, text=None, filename=None, exclude=None): self.filename = filename or '<code>' self.text = text if not self.text: try: sourcef = open_source(self.filename) try: self.text = sourcef.read() finally: sourcef.close() except IOError: _, err, _ = sys.exc_info() raise NoSource("No source for code: '%s': %s" % (self.filename, err)) if self.text and ord(self.text[0]) == 65279: self.text = self.text[1:] self.exclude = exclude self.show_tokens = False self.lines = self.text.split('\n') self.excluded = set() self.docstrings = set() self.classdefs = set() self.multiline = {} self.statement_starts = set() self._byte_parser = None
def get_python_source(filename): """Return the source code, as unicode.""" base, ext = os.path.splitext(filename) if ext == ".py" and env.WINDOWS: exts = [".py", ".pyw"] else: exts = [ext] for ext in exts: try_filename = base + ext if os.path.exists(try_filename): # A regular text file: open it. source = read_python_source(try_filename) break # Maybe it's in a zip file? source = get_zip_bytes(try_filename) if source is not None: break else: # Couldn't find source. exc_msg = "No source for code: '%s'.\n" % (filename, ) exc_msg += "Aborting report output, consider using -i." raise NoSource(exc_msg) # Replace \f because of http://bugs.python.org/issue19035 source = source.replace(b'\f', b' ') source = source.decode(source_encoding(source), "replace") # Python code should always end with a line with a newline. if source and source[-1] != '\n': source += '\n' return source
def __init__(self, text=None, filename=None, exclude=None): """ Source can be provided as `text`, the text itself, or `filename`, from which the text will be read. Excluded lines are those that match `exclude`, a regex. """ assert text or filename, "PythonParser needs either text or filename" self.filename = filename or "<code>" self.text = text if not self.text: from coverage.python import get_python_source try: self.text = get_python_source(self.filename) except IOError as err: raise NoSource( "No source for code: '%s': %s" % (self.filename, err) ) self.exclude = exclude # The text lines of the parsed code. self.lines = self.text.split('\n') # The normalized line numbers of the statements in the code. Exclusions # are taken into account, and statements are adjusted to their first # lines. self.statements = set() # The normalized line numbers of the excluded lines in the code, # adjusted to their first lines. self.excluded = set() # The raw_* attributes are only used in this class, and in # lab/parser.py to show how this class is working. # The line numbers that start statements, as reported by the line # number table in the bytecode. self.raw_statements = set() # The raw line numbers of excluded lines of code, as marked by pragmas. self.raw_excluded = set() # The line numbers of class and function definitions. self.raw_classdefs = set() # The line numbers of docstring lines. self.raw_docstrings = set() # Internal detail, used by lab/parser.py. self.show_tokens = False # A dict mapping line numbers to lexical statement starts for # multi-line statements. self._multiline = {} # Lazily-created ByteParser, arc data, and missing arc descriptions. self._byte_parser = None self._all_arcs = None self._missing_arc_fragments = None
def run_python_module(modulename, args): """Run a python module, as though with ``python -m name args...``. `modulename` is the name of the module, possibly a dot-separated name. `args` is the argument array to present as sys.argv, including the first element naming the module being executed. """ openfile = None glo, loc = globals(), locals() try: try: # Search for the module - inside its parent package, if any - using # standard import mechanics. if '.' in modulename: packagename, name = rsplit1(modulename, '.') package = __import__(packagename, glo, loc, ['__path__']) searchpath = package.__path__ else: packagename, name = None, modulename searchpath = None # "top-level search" in imp.find_module() openfile, pathname, _ = imp.find_module(name, searchpath) # Complain if this is a magic non-file module. if openfile is None and pathname is None: raise NoSource( "module does not live in a file: %r" % modulename ) # If `modulename` is actually a package, not a mere module, then we # pretend to be Python 2.7 and try running its __main__.py script. if openfile is None: packagename = modulename name = '__main__' package = __import__(packagename, glo, loc, ['__path__']) searchpath = package.__path__ openfile, pathname, _ = imp.find_module(name, searchpath) except ImportError: _, err, _ = sys.exc_info() raise NoSource(str(err)) finally: if openfile: openfile.close() # Finally, hand the file off to run_python_file for execution. args[0] = pathname run_python_file(pathname, args, package=packagename)
def run_python_file(filename, args): """Run a python file as if it were the main program on the command line. `filename` is the path to the file to execute, it need not be a .py file. `args` is the argument array to present as sys.argv, including the first element representing the file being executed. """ # Create a module to serve as __main__ old_main_mod = sys.modules['__main__'] main_mod = imp.new_module('__main__') sys.modules['__main__'] = main_mod main_mod.__file__ = filename main_mod.__builtins__ = BUILTINS # Set sys.argv and the first path element properly. old_argv = sys.argv old_path0 = sys.path[0] sys.argv = args sys.path[0] = os.path.dirname(filename) try: # Open the source file. try: source = open(filename, 'rU').read() except IOError: raise NoSource("No file to run: %r" % filename) # We have the source. `compile` still needs the last line to be clean, # so make sure it is, then compile a code object from it. if source[-1] != '\n': source += '\n' code = compile(source, filename, "exec") # Execute the source file. try: exec_code_object(code, main_mod.__dict__) except SystemExit: # The user called sys.exit(). Just pass it along to the upper # layers, where it will be handled. raise except: # Something went wrong while executing the user code. # Get the exc_info, and pack them into an exception that we can # throw up to the outer loop. We peel two layers off the traceback # so that the coverage.py code doesn't appear in the final printed # traceback. typ, err, tb = sys.exc_info() raise ExceptionDuringRun(typ, err, tb.tb_next.tb_next) finally: # Restore the old __main__ sys.modules['__main__'] = old_main_mod # Restore the old argv and path sys.argv = old_argv sys.path[0] = old_path0
def make_code_from_py(filename): """Get source from `filename` and make a code object of it.""" # Open the source file. try: source = get_python_source(filename) except (IOError, NoSource): raise NoSource("No file to run: '%s'" % filename) code = compile_unicode(source, filename, "exec") return code
def find_module(modulename): """Find the module named `modulename`. Returns the file path of the module, and the name of the enclosing package. """ openfile = None glo, loc = globals(), locals() try: # Search for the module - inside its parent package, if any - using # standard import mechanics. if '.' in modulename: packagename, name = modulename.rsplit('.', 1) package = __import__(packagename, glo, loc, ['__path__']) searchpath = package.__path__ else: packagename, name = None, modulename searchpath = None # "top-level search" in imp.find_module() openfile, pathname, _ = imp.find_module(name, searchpath) # Complain if this is a magic non-file module. if openfile is None and pathname is None: raise NoSource( "module does not live in a file: %r" % modulename ) # If `modulename` is actually a package, not a mere module, then we # pretend to be Python 2.7 and try running its __main__.py script. if openfile is None: packagename = modulename name = '__main__' package = __import__(packagename, glo, loc, ['__path__']) searchpath = package.__path__ openfile, pathname, _ = imp.find_module(name, searchpath) except ImportError as err: raise NoSource(str(err)) finally: if openfile: openfile.close() return pathname, packagename
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. """ should_update_sys_path = True if self.as_module: # Python 3.7.0b3 changed the behavior of the sys.path[0] entry for -m. It # used to be an empty string (meaning the current directory). It changed # to be the actual path to the current directory, so that os.chdir wouldn't # affect the outcome. if env.PYVERSION >= (3, 7, 0, 'beta', 3): path0 = os.getcwd() else: path0 = "" sys.path[0] = path0 should_update_sys_path = False self.modulename = self.arg0 pathname, self.package = find_module(self.modulename) self.pathname = os.path.abspath(pathname) self.args[0] = self.arg0 = self.pathname 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): self.arg0 = try_filename break else: raise NoSource("Can't find '__main__' module in '%s'" % self.arg0) else: path0 = os.path.abspath(os.path.dirname(self.arg0)) if self.modulename is None and env.PYVERSION >= (3, 3): 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] = path0
def __init__(self, text=None, filename=None, exclude=None): """ Source can be provided as `text`, the text itself, or `filename`, from which the text will be read. Excluded lines are those that match `exclude`, a regex. """ assert text or filename, "CodeParser needs either text or filename" self.filename = filename or "<code>" self.text = text if not self.text: try: sourcef = open_source(self.filename) try: self.text = sourcef.read() finally: sourcef.close() except IOError: _, err, _ = sys.exc_info() raise NoSource( "No source for code: '%s': %s" % (self.filename, err) ) # Scrap the BOM if it exists. if self.text and ord(self.text[0]) == 0xfeff: self.text = self.text[1:] self.exclude = exclude self.show_tokens = False # The text lines of the parsed code. self.lines = self.text.split('\n') # The line numbers of excluded lines of code. self.excluded = set() # The line numbers of docstring lines. self.docstrings = set() # The line numbers of class definitions. self.classdefs = set() # A dict mapping line numbers to (lo,hi) for multi-line statements. self.multiline = {} # The line numbers that start statements. self.statement_starts = set() # Lazily-created ByteParser self._byte_parser = None
def __init__(self, text=None, filename=None, exclude=None): """ Source can be provided as `text`, the text itself, or `filename`, from which the text will be read. Excluded lines are those that match `exclude`, a regex. """ assert text or filename, "PythonParser needs either text or filename" self.filename = filename or "<code>" self.text = text if not self.text: from coverage.python import get_python_source try: self.text = get_python_source(self.filename) except IOError as err: raise NoSource("No source for code: '%s': %s" % (self.filename, err)) if self.text: assert isinstance(self.text, str) # Scrap the BOM if it exists. # (Used to do this, but no longer. Not sure what bad will happen # if we don't do it.) # if ord(self.text[0]) == 0xfeff: # self.text = self.text[1:] self.exclude = exclude self.show_tokens = False # The text lines of the parsed code. self.lines = self.text.split('\n') # The line numbers of excluded lines of code. self.excluded = set() # The line numbers of docstring lines. self.docstrings = set() # The line numbers of class definitions. self.classdefs = set() # A dict mapping line numbers to (lo,hi) for multi-line statements. self.multiline = {} # The line numbers that start statements. self.statement_starts = set() # Lazily-created ByteParser self._byte_parser = None
def run_python_module(modulename, args): """Run a python module, as though with ``python -m name args...``. `modulename` is the name of the module, possibly a dot-separated name. `args` is the argument array to present as sys.argv, including the first element naming the module being executed. """ openfile = None glo, loc = globals(), locals() try: if '.' in modulename: packagename, name = rsplit1(modulename, '.') package = __import__(packagename, glo, loc, ['__path__']) searchpath = package.__path__ else: packagename, name = None, modulename searchpath = None openfile, pathname, _ = imp.find_module(name, searchpath) if openfile is None and pathname is None: raise NoSource('module does not live in a file: %r' % modulename) if openfile is None: packagename = modulename name = '__main__' package = __import__(packagename, glo, loc, ['__path__']) searchpath = package.__path__ openfile, pathname, _ = imp.find_module(name, searchpath) except ImportError: _, err, _ = sys.exc_info() raise NoSource(str(err)) finally: if openfile: openfile.close() pathname = os.path.abspath(pathname) args[0] = pathname run_python_file(pathname, args, package=packagename)
def make_code_from_py(filename): try: source_file = open_source(filename) except IOError: raise NoSource('No file to run: %r' % filename) try: source = source_file.read() finally: source_file.close() if not source or source[-1] != '\n': source += '\n' code = compile(source, filename, 'exec') return code
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: self.modulename = self.spec.name self.loader = DummyLoader(self.modulename) self.pathname = os.path.abspath(pathname) self.args[0] = self.arg0 = self.pathname elif os.path.isdir(self.arg0): # Running a directory means running the __main__.py file in that # directory. for ext in [".py", ".pyc", ".pyo"]: try_filename = os.path.join(self.arg0, "__main__" + ext) if os.path.exists(try_filename): self.arg0 = try_filename break else: raise NoSource("Can't find '__main__' module in '%s'" % self.arg0) if env.PY2: self.arg0 = os.path.abspath(self.arg0) # Make a spec. I don't know if this is the right way to do it. try: import importlib.machinery except ImportError: pass else: try_filename = python_reported_file(try_filename) self.spec = importlib.machinery.ModuleSpec("__main__", None, origin=try_filename) self.spec.has_location = True self.package = "" self.loader = DummyLoader("__main__") else: if env.PY3: self.loader = DummyLoader("__main__") self.arg0 = python_reported_file(self.arg0)
def run_python_file(filename, args, package=None): """Run a python file as if it were the main program on the command line. `filename` is the path to the file to execute, it need not be a .py file. `args` is the argument array to present as sys.argv, including the first element naming the file being executed. `package` is the name of the enclosing package, if any. """ # Create a module to serve as __main__ old_main_mod = sys.modules['__main__'] main_mod = imp.new_module('__main__') sys.modules['__main__'] = main_mod main_mod.__file__ = filename if package: main_mod.__package__ = package main_mod.__builtins__ = BUILTINS # Set sys.argv properly. old_argv = sys.argv sys.argv = args try: # Open the source file. try: source_file = open_source(filename) except IOError: raise NoSource("No file to run: %r" % filename) try: source = source_file.read() finally: source_file.close() # We have the source. `compile` still needs the last line to be clean, # so make sure it is, then compile a code object from it. if not source or source[-1] != '\n': source += '\n' code = compile(source, filename, "exec") # Execute the source file. exec_code_object(code, main_mod.__dict__) finally: # Restore the old __main__ sys.modules['__main__'] = old_main_mod # Restore the old argv and path sys.argv = old_argv
def find_source(self, filename): source = None base, ext = os.path.splitext(filename) TRY_EXTS = {'.py': ['.py', '.pyw'], '.pyw': ['.pyw']} try_exts = TRY_EXTS.get(ext) if not try_exts: return (filename, None) for try_ext in try_exts: try_filename = base + try_ext if os.path.exists(try_filename): return (try_filename, None) source = self.coverage.file_locator.get_zip_data(try_filename) if source: return (try_filename, source) raise NoSource("No source for code: '%s'" % filename)
def __init__(self, cov, code_unit): self.coverage = cov self.code_unit = code_unit self.filename = self.code_unit.filename ext = os.path.splitext(self.filename)[1] source = None if ext == '.py': if not os.path.exists(self.filename): source = self.coverage.file_locator.get_zip_data(self.filename) if not source: raise NoSource("No source for code: '%s'" % self.filename) self.parser = CodeParser( text=source, filename=self.filename, exclude=self.coverage._exclude_regex('exclude')) self.statements, self.excluded = self.parser.parse_source() # Identify missing statements. executed = self.coverage.data.executed_lines(self.filename) exec1 = self.parser.first_lines(executed) self.missing = sorted(set(self.statements) - set(exec1)) if self.coverage.data.has_arcs(): self.no_branch = self.parser.lines_matching( join_regex(self.coverage.config.partial_list), join_regex(self.coverage.config.partial_always_list)) n_branches = self.total_branches() mba = self.missing_branch_arcs() n_partial_branches = sum( [len(v) for k, v in iitems(mba) if k not in self.missing]) n_missing_branches = sum([len(v) for k, v in iitems(mba)]) else: n_branches = n_partial_branches = n_missing_branches = 0 self.no_branch = set() self.numbers = Numbers( n_files=1, n_statements=len(self.statements), n_excluded=len(self.excluded), n_missing=len(self.missing), n_branches=n_branches, n_partial_branches=n_partial_branches, n_missing_branches=n_missing_branches, )
def make_code_from_py(filename): """Get source from `filename` and make a code object of it.""" # Open the source file. try: source_file = open_python_source(filename) except IOError: raise NoSource("No file to run: %r" % filename) with source_file: source = source_file.read() # We have the source. `compile` still needs the last line to be clean, # so make sure it is, then compile a code object from it. if not source or source[-1] != '\n': source += '\n' code = compile(source, filename, "exec") return code
def __init__(self, text=None, filename=None, exclude=None): """ Source can be provided as `text`, the text itself, or `filename`, from which the text will be read. Excluded lines are those that match `exclude`, a regex. """ assert text or filename, "PythonParser needs either text or filename" self.filename = filename or "<code>" self.text = text if not self.text: from coverage.python import get_python_source try: self.text = get_python_source(self.filename) except IOError as err: raise NoSource( "No source for code: '%s': %s" % (self.filename, err) ) self.exclude = exclude self.show_tokens = False # The text lines of the parsed code. self.lines = self.text.split('\n') # The line numbers of excluded lines of code. self.excluded = set() # The line numbers of docstring lines. self.docstrings = set() # The line numbers of class definitions. self.classdefs = set() # A dict mapping line numbers to (lo,hi) for multi-line statements. self.multiline = {} # The line numbers that start statements. self.statement_starts = set() # Lazily-created ByteParser and arc data. self._byte_parser = None self._all_arcs = None
def __init__(self, cov, code_unit): self.coverage = cov self.code_unit = code_unit self.filename = self.code_unit.filename ext = os.path.splitext(self.filename)[1] source = None if ext == '.py': if not os.path.exists(self.filename): source = self.coverage.file_locator.get_zip_data(self.filename) if not source: raise NoSource("No source for code: %r" % self.filename) self.parser = CodeParser(text=source, filename=self.filename, exclude=self.coverage.exclude_re) self.statements, self.excluded = self.parser.parse_source() # Identify missing statements. executed = self.coverage.data.executed_lines(self.filename) exec1 = self.parser.first_lines(executed) self.missing = sorted(set(self.statements) - set(exec1)) if self.coverage.data.has_arcs(): n_branches = self.total_branches() mba = self.missing_branch_arcs() n_missing_branches = sum([len(v) for v in mba.values()]) else: n_branches = n_missing_branches = 0 self.numbers = Numbers( n_files=1, n_statements=len(self.statements), n_excluded=len(self.excluded), n_missing=len(self.missing), n_branches=n_branches, n_missing_branches=n_missing_branches, )
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. """ should_update_sys_path = True if self.as_module: if env.PYBEHAVIOR.actual_syspath0_dash_m: path0 = os.getcwd() else: path0 = "" sys.path[0] = path0 should_update_sys_path = False self.modulename = self.arg0 pathname, self.package, self.spec = find_module(self.modulename) if self.spec is not None: self.modulename = self.spec.name self.loader = DummyLoader(self.modulename) self.pathname = os.path.abspath(pathname) self.args[0] = self.arg0 = self.pathname 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): self.arg0 = try_filename break else: raise NoSource("Can't find '__main__' module in '%s'" % self.arg0) if env.PY2: self.arg0 = os.path.abspath(self.arg0) # Make a spec. I don't know if this is the right way to do it. try: import importlib.machinery except ImportError: pass else: try_filename = python_reported_file(try_filename) self.spec = importlib.machinery.ModuleSpec("__main__", None, origin=try_filename) self.spec.has_location = True self.package = "" self.loader = DummyLoader("__main__") else: path0 = os.path.abspath(os.path.dirname(self.arg0)) if env.PY3: self.loader = DummyLoader("__main__") self.args[0] = python_reported_file(self.args[0]) 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_python_file(filename, args, package=None, modulename=None, path0=None): """Run a Python file as if it were the main program on the command line. `filename` is the path to the file to execute, it need not be a .py file. `args` is the argument array to present as sys.argv, including the first element naming the file being executed. `package` is the name of the enclosing package, if any. `modulename` is the name of the module the file was run as. `path0` is the value to put into sys.path[0]. If it's None, then this function will decide on a value. """ if modulename is None and sys.version_info >= (3, 3): modulename = '__main__' # Create a module to serve as __main__ old_main_mod = sys.modules['__main__'] main_mod = types.ModuleType('__main__') sys.modules['__main__'] = main_mod main_mod.__file__ = filename if package: main_mod.__package__ = package if modulename: main_mod.__loader__ = DummyLoader(modulename) main_mod.__builtins__ = BUILTINS # Set sys.argv properly. old_argv = sys.argv sys.argv = args if os.path.isdir(filename): # Running a directory means running the __main__.py file in that # directory. my_path0 = filename for ext in [".py", ".pyc", ".pyo"]: try_filename = os.path.join(filename, "__main__" + ext) if os.path.exists(try_filename): filename = try_filename break else: raise NoSource("Can't find '__main__' module in '%s'" % filename) else: my_path0 = os.path.abspath(os.path.dirname(filename)) # Set sys.path correctly. old_path0 = sys.path[0] sys.path[0] = path0 if path0 is not None else my_path0 try: try: # Make a code object somehow. if filename.endswith((".pyc", ".pyo")): code = make_code_from_pyc(filename) else: code = make_code_from_py(filename) except CoverageException: raise except Exception as exc: msg = "Couldn't run {filename!r} as Python code: {exc.__class__.__name__}: {exc}" raise CoverageException(msg.format(filename=filename, exc=exc)) # Execute the code object. try: exec(code, main_mod.__dict__) except SystemExit: # The user called sys.exit(). Just pass it along to the upper # layers, where it will be handled. raise except Exception: # Something went wrong while executing the user code. # Get the exc_info, and pack them into an exception that we can # throw up to the outer loop. We peel one layer off the traceback # so that the coverage.py code doesn't appear in the final printed # traceback. typ, err, tb = sys.exc_info() # PyPy3 weirdness. If I don't access __context__, then somehow it # is non-None when the exception is reported at the upper layer, # and a nested exception is shown to the user. This getattr fixes # it somehow? https://bitbucket.org/pypy/pypy/issue/1903 getattr(err, '__context__', None) # Call the excepthook. try: if hasattr(err, "__traceback__"): err.__traceback__ = err.__traceback__.tb_next sys.excepthook(typ, err, tb.tb_next) except SystemExit: raise except Exception: # Getting the output right in the case of excepthook # shenanigans is kind of involved. sys.stderr.write("Error in sys.excepthook:\n") typ2, err2, tb2 = sys.exc_info() err2.__suppress_context__ = True if hasattr(err2, "__traceback__"): err2.__traceback__ = err2.__traceback__.tb_next sys.__excepthook__(typ2, err2, tb2.tb_next) sys.stderr.write("\nOriginal exception was:\n") raise ExceptionDuringRun(typ, err, tb.tb_next) else: sys.exit(1) finally: # Restore the old __main__, argv, and path. sys.modules['__main__'] = old_main_mod sys.argv = old_argv sys.path[0] = old_path0