17db96d56Sopenharmony_ci#!/usr/bin/env python3 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ci# portions copyright 2001, Autonomous Zones Industries, Inc., all rights... 47db96d56Sopenharmony_ci# err... reserved and offered to the public under the terms of the 57db96d56Sopenharmony_ci# Python 2.2 license. 67db96d56Sopenharmony_ci# Author: Zooko O'Whielacronx 77db96d56Sopenharmony_ci# http://zooko.com/ 87db96d56Sopenharmony_ci# mailto:zooko@zooko.com 97db96d56Sopenharmony_ci# 107db96d56Sopenharmony_ci# Copyright 2000, Mojam Media, Inc., all rights reserved. 117db96d56Sopenharmony_ci# Author: Skip Montanaro 127db96d56Sopenharmony_ci# 137db96d56Sopenharmony_ci# Copyright 1999, Bioreason, Inc., all rights reserved. 147db96d56Sopenharmony_ci# Author: Andrew Dalke 157db96d56Sopenharmony_ci# 167db96d56Sopenharmony_ci# Copyright 1995-1997, Automatrix, Inc., all rights reserved. 177db96d56Sopenharmony_ci# Author: Skip Montanaro 187db96d56Sopenharmony_ci# 197db96d56Sopenharmony_ci# Copyright 1991-1995, Stichting Mathematisch Centrum, all rights reserved. 207db96d56Sopenharmony_ci# 217db96d56Sopenharmony_ci# 227db96d56Sopenharmony_ci# Permission to use, copy, modify, and distribute this Python software and 237db96d56Sopenharmony_ci# its associated documentation for any purpose without fee is hereby 247db96d56Sopenharmony_ci# granted, provided that the above copyright notice appears in all copies, 257db96d56Sopenharmony_ci# and that both that copyright notice and this permission notice appear in 267db96d56Sopenharmony_ci# supporting documentation, and that the name of neither Automatrix, 277db96d56Sopenharmony_ci# Bioreason or Mojam Media be used in advertising or publicity pertaining to 287db96d56Sopenharmony_ci# distribution of the software without specific, written prior permission. 297db96d56Sopenharmony_ci# 307db96d56Sopenharmony_ci"""program/module to trace Python program or function execution 317db96d56Sopenharmony_ci 327db96d56Sopenharmony_ciSample use, command line: 337db96d56Sopenharmony_ci trace.py -c -f counts --ignore-dir '$prefix' spam.py eggs 347db96d56Sopenharmony_ci trace.py -t --ignore-dir '$prefix' spam.py eggs 357db96d56Sopenharmony_ci trace.py --trackcalls spam.py eggs 367db96d56Sopenharmony_ci 377db96d56Sopenharmony_ciSample use, programmatically 387db96d56Sopenharmony_ci import sys 397db96d56Sopenharmony_ci 407db96d56Sopenharmony_ci # create a Trace object, telling it what to ignore, and whether to 417db96d56Sopenharmony_ci # do tracing or line-counting or both. 427db96d56Sopenharmony_ci tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,], 437db96d56Sopenharmony_ci trace=0, count=1) 447db96d56Sopenharmony_ci # run the new command using the given tracer 457db96d56Sopenharmony_ci tracer.run('main()') 467db96d56Sopenharmony_ci # make a report, placing output in /tmp 477db96d56Sopenharmony_ci r = tracer.results() 487db96d56Sopenharmony_ci r.write_results(show_missing=True, coverdir="/tmp") 497db96d56Sopenharmony_ci""" 507db96d56Sopenharmony_ci__all__ = ['Trace', 'CoverageResults'] 517db96d56Sopenharmony_ci 527db96d56Sopenharmony_ciimport io 537db96d56Sopenharmony_ciimport linecache 547db96d56Sopenharmony_ciimport os 557db96d56Sopenharmony_ciimport sys 567db96d56Sopenharmony_ciimport sysconfig 577db96d56Sopenharmony_ciimport token 587db96d56Sopenharmony_ciimport tokenize 597db96d56Sopenharmony_ciimport inspect 607db96d56Sopenharmony_ciimport gc 617db96d56Sopenharmony_ciimport dis 627db96d56Sopenharmony_ciimport pickle 637db96d56Sopenharmony_cifrom time import monotonic as _time 647db96d56Sopenharmony_ci 657db96d56Sopenharmony_ciimport threading 667db96d56Sopenharmony_ci 677db96d56Sopenharmony_ciPRAGMA_NOCOVER = "#pragma NO COVER" 687db96d56Sopenharmony_ci 697db96d56Sopenharmony_ciclass _Ignore: 707db96d56Sopenharmony_ci def __init__(self, modules=None, dirs=None): 717db96d56Sopenharmony_ci self._mods = set() if not modules else set(modules) 727db96d56Sopenharmony_ci self._dirs = [] if not dirs else [os.path.normpath(d) 737db96d56Sopenharmony_ci for d in dirs] 747db96d56Sopenharmony_ci self._ignore = { '<string>': 1 } 757db96d56Sopenharmony_ci 767db96d56Sopenharmony_ci def names(self, filename, modulename): 777db96d56Sopenharmony_ci if modulename in self._ignore: 787db96d56Sopenharmony_ci return self._ignore[modulename] 797db96d56Sopenharmony_ci 807db96d56Sopenharmony_ci # haven't seen this one before, so see if the module name is 817db96d56Sopenharmony_ci # on the ignore list. 827db96d56Sopenharmony_ci if modulename in self._mods: # Identical names, so ignore 837db96d56Sopenharmony_ci self._ignore[modulename] = 1 847db96d56Sopenharmony_ci return 1 857db96d56Sopenharmony_ci 867db96d56Sopenharmony_ci # check if the module is a proper submodule of something on 877db96d56Sopenharmony_ci # the ignore list 887db96d56Sopenharmony_ci for mod in self._mods: 897db96d56Sopenharmony_ci # Need to take some care since ignoring 907db96d56Sopenharmony_ci # "cmp" mustn't mean ignoring "cmpcache" but ignoring 917db96d56Sopenharmony_ci # "Spam" must also mean ignoring "Spam.Eggs". 927db96d56Sopenharmony_ci if modulename.startswith(mod + '.'): 937db96d56Sopenharmony_ci self._ignore[modulename] = 1 947db96d56Sopenharmony_ci return 1 957db96d56Sopenharmony_ci 967db96d56Sopenharmony_ci # Now check that filename isn't in one of the directories 977db96d56Sopenharmony_ci if filename is None: 987db96d56Sopenharmony_ci # must be a built-in, so we must ignore 997db96d56Sopenharmony_ci self._ignore[modulename] = 1 1007db96d56Sopenharmony_ci return 1 1017db96d56Sopenharmony_ci 1027db96d56Sopenharmony_ci # Ignore a file when it contains one of the ignorable paths 1037db96d56Sopenharmony_ci for d in self._dirs: 1047db96d56Sopenharmony_ci # The '+ os.sep' is to ensure that d is a parent directory, 1057db96d56Sopenharmony_ci # as compared to cases like: 1067db96d56Sopenharmony_ci # d = "/usr/local" 1077db96d56Sopenharmony_ci # filename = "/usr/local.py" 1087db96d56Sopenharmony_ci # or 1097db96d56Sopenharmony_ci # d = "/usr/local.py" 1107db96d56Sopenharmony_ci # filename = "/usr/local.py" 1117db96d56Sopenharmony_ci if filename.startswith(d + os.sep): 1127db96d56Sopenharmony_ci self._ignore[modulename] = 1 1137db96d56Sopenharmony_ci return 1 1147db96d56Sopenharmony_ci 1157db96d56Sopenharmony_ci # Tried the different ways, so we don't ignore this module 1167db96d56Sopenharmony_ci self._ignore[modulename] = 0 1177db96d56Sopenharmony_ci return 0 1187db96d56Sopenharmony_ci 1197db96d56Sopenharmony_cidef _modname(path): 1207db96d56Sopenharmony_ci """Return a plausible module name for the path.""" 1217db96d56Sopenharmony_ci 1227db96d56Sopenharmony_ci base = os.path.basename(path) 1237db96d56Sopenharmony_ci filename, ext = os.path.splitext(base) 1247db96d56Sopenharmony_ci return filename 1257db96d56Sopenharmony_ci 1267db96d56Sopenharmony_cidef _fullmodname(path): 1277db96d56Sopenharmony_ci """Return a plausible module name for the path.""" 1287db96d56Sopenharmony_ci 1297db96d56Sopenharmony_ci # If the file 'path' is part of a package, then the filename isn't 1307db96d56Sopenharmony_ci # enough to uniquely identify it. Try to do the right thing by 1317db96d56Sopenharmony_ci # looking in sys.path for the longest matching prefix. We'll 1327db96d56Sopenharmony_ci # assume that the rest is the package name. 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_ci comparepath = os.path.normcase(path) 1357db96d56Sopenharmony_ci longest = "" 1367db96d56Sopenharmony_ci for dir in sys.path: 1377db96d56Sopenharmony_ci dir = os.path.normcase(dir) 1387db96d56Sopenharmony_ci if comparepath.startswith(dir) and comparepath[len(dir)] == os.sep: 1397db96d56Sopenharmony_ci if len(dir) > len(longest): 1407db96d56Sopenharmony_ci longest = dir 1417db96d56Sopenharmony_ci 1427db96d56Sopenharmony_ci if longest: 1437db96d56Sopenharmony_ci base = path[len(longest) + 1:] 1447db96d56Sopenharmony_ci else: 1457db96d56Sopenharmony_ci base = path 1467db96d56Sopenharmony_ci # the drive letter is never part of the module name 1477db96d56Sopenharmony_ci drive, base = os.path.splitdrive(base) 1487db96d56Sopenharmony_ci base = base.replace(os.sep, ".") 1497db96d56Sopenharmony_ci if os.altsep: 1507db96d56Sopenharmony_ci base = base.replace(os.altsep, ".") 1517db96d56Sopenharmony_ci filename, ext = os.path.splitext(base) 1527db96d56Sopenharmony_ci return filename.lstrip(".") 1537db96d56Sopenharmony_ci 1547db96d56Sopenharmony_ciclass CoverageResults: 1557db96d56Sopenharmony_ci def __init__(self, counts=None, calledfuncs=None, infile=None, 1567db96d56Sopenharmony_ci callers=None, outfile=None): 1577db96d56Sopenharmony_ci self.counts = counts 1587db96d56Sopenharmony_ci if self.counts is None: 1597db96d56Sopenharmony_ci self.counts = {} 1607db96d56Sopenharmony_ci self.counter = self.counts.copy() # map (filename, lineno) to count 1617db96d56Sopenharmony_ci self.calledfuncs = calledfuncs 1627db96d56Sopenharmony_ci if self.calledfuncs is None: 1637db96d56Sopenharmony_ci self.calledfuncs = {} 1647db96d56Sopenharmony_ci self.calledfuncs = self.calledfuncs.copy() 1657db96d56Sopenharmony_ci self.callers = callers 1667db96d56Sopenharmony_ci if self.callers is None: 1677db96d56Sopenharmony_ci self.callers = {} 1687db96d56Sopenharmony_ci self.callers = self.callers.copy() 1697db96d56Sopenharmony_ci self.infile = infile 1707db96d56Sopenharmony_ci self.outfile = outfile 1717db96d56Sopenharmony_ci if self.infile: 1727db96d56Sopenharmony_ci # Try to merge existing counts file. 1737db96d56Sopenharmony_ci try: 1747db96d56Sopenharmony_ci with open(self.infile, 'rb') as f: 1757db96d56Sopenharmony_ci counts, calledfuncs, callers = pickle.load(f) 1767db96d56Sopenharmony_ci self.update(self.__class__(counts, calledfuncs, callers=callers)) 1777db96d56Sopenharmony_ci except (OSError, EOFError, ValueError) as err: 1787db96d56Sopenharmony_ci print(("Skipping counts file %r: %s" 1797db96d56Sopenharmony_ci % (self.infile, err)), file=sys.stderr) 1807db96d56Sopenharmony_ci 1817db96d56Sopenharmony_ci def is_ignored_filename(self, filename): 1827db96d56Sopenharmony_ci """Return True if the filename does not refer to a file 1837db96d56Sopenharmony_ci we want to have reported. 1847db96d56Sopenharmony_ci """ 1857db96d56Sopenharmony_ci return filename.startswith('<') and filename.endswith('>') 1867db96d56Sopenharmony_ci 1877db96d56Sopenharmony_ci def update(self, other): 1887db96d56Sopenharmony_ci """Merge in the data from another CoverageResults""" 1897db96d56Sopenharmony_ci counts = self.counts 1907db96d56Sopenharmony_ci calledfuncs = self.calledfuncs 1917db96d56Sopenharmony_ci callers = self.callers 1927db96d56Sopenharmony_ci other_counts = other.counts 1937db96d56Sopenharmony_ci other_calledfuncs = other.calledfuncs 1947db96d56Sopenharmony_ci other_callers = other.callers 1957db96d56Sopenharmony_ci 1967db96d56Sopenharmony_ci for key in other_counts: 1977db96d56Sopenharmony_ci counts[key] = counts.get(key, 0) + other_counts[key] 1987db96d56Sopenharmony_ci 1997db96d56Sopenharmony_ci for key in other_calledfuncs: 2007db96d56Sopenharmony_ci calledfuncs[key] = 1 2017db96d56Sopenharmony_ci 2027db96d56Sopenharmony_ci for key in other_callers: 2037db96d56Sopenharmony_ci callers[key] = 1 2047db96d56Sopenharmony_ci 2057db96d56Sopenharmony_ci def write_results(self, show_missing=True, summary=False, coverdir=None): 2067db96d56Sopenharmony_ci """ 2077db96d56Sopenharmony_ci Write the coverage results. 2087db96d56Sopenharmony_ci 2097db96d56Sopenharmony_ci :param show_missing: Show lines that had no hits. 2107db96d56Sopenharmony_ci :param summary: Include coverage summary per module. 2117db96d56Sopenharmony_ci :param coverdir: If None, the results of each module are placed in its 2127db96d56Sopenharmony_ci directory, otherwise it is included in the directory 2137db96d56Sopenharmony_ci specified. 2147db96d56Sopenharmony_ci """ 2157db96d56Sopenharmony_ci if self.calledfuncs: 2167db96d56Sopenharmony_ci print() 2177db96d56Sopenharmony_ci print("functions called:") 2187db96d56Sopenharmony_ci calls = self.calledfuncs 2197db96d56Sopenharmony_ci for filename, modulename, funcname in sorted(calls): 2207db96d56Sopenharmony_ci print(("filename: %s, modulename: %s, funcname: %s" 2217db96d56Sopenharmony_ci % (filename, modulename, funcname))) 2227db96d56Sopenharmony_ci 2237db96d56Sopenharmony_ci if self.callers: 2247db96d56Sopenharmony_ci print() 2257db96d56Sopenharmony_ci print("calling relationships:") 2267db96d56Sopenharmony_ci lastfile = lastcfile = "" 2277db96d56Sopenharmony_ci for ((pfile, pmod, pfunc), (cfile, cmod, cfunc)) \ 2287db96d56Sopenharmony_ci in sorted(self.callers): 2297db96d56Sopenharmony_ci if pfile != lastfile: 2307db96d56Sopenharmony_ci print() 2317db96d56Sopenharmony_ci print("***", pfile, "***") 2327db96d56Sopenharmony_ci lastfile = pfile 2337db96d56Sopenharmony_ci lastcfile = "" 2347db96d56Sopenharmony_ci if cfile != pfile and lastcfile != cfile: 2357db96d56Sopenharmony_ci print(" -->", cfile) 2367db96d56Sopenharmony_ci lastcfile = cfile 2377db96d56Sopenharmony_ci print(" %s.%s -> %s.%s" % (pmod, pfunc, cmod, cfunc)) 2387db96d56Sopenharmony_ci 2397db96d56Sopenharmony_ci # turn the counts data ("(filename, lineno) = count") into something 2407db96d56Sopenharmony_ci # accessible on a per-file basis 2417db96d56Sopenharmony_ci per_file = {} 2427db96d56Sopenharmony_ci for filename, lineno in self.counts: 2437db96d56Sopenharmony_ci lines_hit = per_file[filename] = per_file.get(filename, {}) 2447db96d56Sopenharmony_ci lines_hit[lineno] = self.counts[(filename, lineno)] 2457db96d56Sopenharmony_ci 2467db96d56Sopenharmony_ci # accumulate summary info, if needed 2477db96d56Sopenharmony_ci sums = {} 2487db96d56Sopenharmony_ci 2497db96d56Sopenharmony_ci for filename, count in per_file.items(): 2507db96d56Sopenharmony_ci if self.is_ignored_filename(filename): 2517db96d56Sopenharmony_ci continue 2527db96d56Sopenharmony_ci 2537db96d56Sopenharmony_ci if filename.endswith(".pyc"): 2547db96d56Sopenharmony_ci filename = filename[:-1] 2557db96d56Sopenharmony_ci 2567db96d56Sopenharmony_ci if coverdir is None: 2577db96d56Sopenharmony_ci dir = os.path.dirname(os.path.abspath(filename)) 2587db96d56Sopenharmony_ci modulename = _modname(filename) 2597db96d56Sopenharmony_ci else: 2607db96d56Sopenharmony_ci dir = coverdir 2617db96d56Sopenharmony_ci if not os.path.exists(dir): 2627db96d56Sopenharmony_ci os.makedirs(dir) 2637db96d56Sopenharmony_ci modulename = _fullmodname(filename) 2647db96d56Sopenharmony_ci 2657db96d56Sopenharmony_ci # If desired, get a list of the line numbers which represent 2667db96d56Sopenharmony_ci # executable content (returned as a dict for better lookup speed) 2677db96d56Sopenharmony_ci if show_missing: 2687db96d56Sopenharmony_ci lnotab = _find_executable_linenos(filename) 2697db96d56Sopenharmony_ci else: 2707db96d56Sopenharmony_ci lnotab = {} 2717db96d56Sopenharmony_ci source = linecache.getlines(filename) 2727db96d56Sopenharmony_ci coverpath = os.path.join(dir, modulename + ".cover") 2737db96d56Sopenharmony_ci with open(filename, 'rb') as fp: 2747db96d56Sopenharmony_ci encoding, _ = tokenize.detect_encoding(fp.readline) 2757db96d56Sopenharmony_ci n_hits, n_lines = self.write_results_file(coverpath, source, 2767db96d56Sopenharmony_ci lnotab, count, encoding) 2777db96d56Sopenharmony_ci if summary and n_lines: 2787db96d56Sopenharmony_ci percent = int(100 * n_hits / n_lines) 2797db96d56Sopenharmony_ci sums[modulename] = n_lines, percent, modulename, filename 2807db96d56Sopenharmony_ci 2817db96d56Sopenharmony_ci 2827db96d56Sopenharmony_ci if summary and sums: 2837db96d56Sopenharmony_ci print("lines cov% module (path)") 2847db96d56Sopenharmony_ci for m in sorted(sums): 2857db96d56Sopenharmony_ci n_lines, percent, modulename, filename = sums[m] 2867db96d56Sopenharmony_ci print("%5d %3d%% %s (%s)" % sums[m]) 2877db96d56Sopenharmony_ci 2887db96d56Sopenharmony_ci if self.outfile: 2897db96d56Sopenharmony_ci # try and store counts and module info into self.outfile 2907db96d56Sopenharmony_ci try: 2917db96d56Sopenharmony_ci with open(self.outfile, 'wb') as f: 2927db96d56Sopenharmony_ci pickle.dump((self.counts, self.calledfuncs, self.callers), 2937db96d56Sopenharmony_ci f, 1) 2947db96d56Sopenharmony_ci except OSError as err: 2957db96d56Sopenharmony_ci print("Can't save counts files because %s" % err, file=sys.stderr) 2967db96d56Sopenharmony_ci 2977db96d56Sopenharmony_ci def write_results_file(self, path, lines, lnotab, lines_hit, encoding=None): 2987db96d56Sopenharmony_ci """Return a coverage results file in path.""" 2997db96d56Sopenharmony_ci # ``lnotab`` is a dict of executable lines, or a line number "table" 3007db96d56Sopenharmony_ci 3017db96d56Sopenharmony_ci try: 3027db96d56Sopenharmony_ci outfile = open(path, "w", encoding=encoding) 3037db96d56Sopenharmony_ci except OSError as err: 3047db96d56Sopenharmony_ci print(("trace: Could not open %r for writing: %s " 3057db96d56Sopenharmony_ci "- skipping" % (path, err)), file=sys.stderr) 3067db96d56Sopenharmony_ci return 0, 0 3077db96d56Sopenharmony_ci 3087db96d56Sopenharmony_ci n_lines = 0 3097db96d56Sopenharmony_ci n_hits = 0 3107db96d56Sopenharmony_ci with outfile: 3117db96d56Sopenharmony_ci for lineno, line in enumerate(lines, 1): 3127db96d56Sopenharmony_ci # do the blank/comment match to try to mark more lines 3137db96d56Sopenharmony_ci # (help the reader find stuff that hasn't been covered) 3147db96d56Sopenharmony_ci if lineno in lines_hit: 3157db96d56Sopenharmony_ci outfile.write("%5d: " % lines_hit[lineno]) 3167db96d56Sopenharmony_ci n_hits += 1 3177db96d56Sopenharmony_ci n_lines += 1 3187db96d56Sopenharmony_ci elif lineno in lnotab and not PRAGMA_NOCOVER in line: 3197db96d56Sopenharmony_ci # Highlight never-executed lines, unless the line contains 3207db96d56Sopenharmony_ci # #pragma: NO COVER 3217db96d56Sopenharmony_ci outfile.write(">>>>>> ") 3227db96d56Sopenharmony_ci n_lines += 1 3237db96d56Sopenharmony_ci else: 3247db96d56Sopenharmony_ci outfile.write(" ") 3257db96d56Sopenharmony_ci outfile.write(line.expandtabs(8)) 3267db96d56Sopenharmony_ci 3277db96d56Sopenharmony_ci return n_hits, n_lines 3287db96d56Sopenharmony_ci 3297db96d56Sopenharmony_cidef _find_lines_from_code(code, strs): 3307db96d56Sopenharmony_ci """Return dict where keys are lines in the line number table.""" 3317db96d56Sopenharmony_ci linenos = {} 3327db96d56Sopenharmony_ci 3337db96d56Sopenharmony_ci for _, lineno in dis.findlinestarts(code): 3347db96d56Sopenharmony_ci if lineno not in strs: 3357db96d56Sopenharmony_ci linenos[lineno] = 1 3367db96d56Sopenharmony_ci 3377db96d56Sopenharmony_ci return linenos 3387db96d56Sopenharmony_ci 3397db96d56Sopenharmony_cidef _find_lines(code, strs): 3407db96d56Sopenharmony_ci """Return lineno dict for all code objects reachable from code.""" 3417db96d56Sopenharmony_ci # get all of the lineno information from the code of this scope level 3427db96d56Sopenharmony_ci linenos = _find_lines_from_code(code, strs) 3437db96d56Sopenharmony_ci 3447db96d56Sopenharmony_ci # and check the constants for references to other code objects 3457db96d56Sopenharmony_ci for c in code.co_consts: 3467db96d56Sopenharmony_ci if inspect.iscode(c): 3477db96d56Sopenharmony_ci # find another code object, so recurse into it 3487db96d56Sopenharmony_ci linenos.update(_find_lines(c, strs)) 3497db96d56Sopenharmony_ci return linenos 3507db96d56Sopenharmony_ci 3517db96d56Sopenharmony_cidef _find_strings(filename, encoding=None): 3527db96d56Sopenharmony_ci """Return a dict of possible docstring positions. 3537db96d56Sopenharmony_ci 3547db96d56Sopenharmony_ci The dict maps line numbers to strings. There is an entry for 3557db96d56Sopenharmony_ci line that contains only a string or a part of a triple-quoted 3567db96d56Sopenharmony_ci string. 3577db96d56Sopenharmony_ci """ 3587db96d56Sopenharmony_ci d = {} 3597db96d56Sopenharmony_ci # If the first token is a string, then it's the module docstring. 3607db96d56Sopenharmony_ci # Add this special case so that the test in the loop passes. 3617db96d56Sopenharmony_ci prev_ttype = token.INDENT 3627db96d56Sopenharmony_ci with open(filename, encoding=encoding) as f: 3637db96d56Sopenharmony_ci tok = tokenize.generate_tokens(f.readline) 3647db96d56Sopenharmony_ci for ttype, tstr, start, end, line in tok: 3657db96d56Sopenharmony_ci if ttype == token.STRING: 3667db96d56Sopenharmony_ci if prev_ttype == token.INDENT: 3677db96d56Sopenharmony_ci sline, scol = start 3687db96d56Sopenharmony_ci eline, ecol = end 3697db96d56Sopenharmony_ci for i in range(sline, eline + 1): 3707db96d56Sopenharmony_ci d[i] = 1 3717db96d56Sopenharmony_ci prev_ttype = ttype 3727db96d56Sopenharmony_ci return d 3737db96d56Sopenharmony_ci 3747db96d56Sopenharmony_cidef _find_executable_linenos(filename): 3757db96d56Sopenharmony_ci """Return dict where keys are line numbers in the line number table.""" 3767db96d56Sopenharmony_ci try: 3777db96d56Sopenharmony_ci with tokenize.open(filename) as f: 3787db96d56Sopenharmony_ci prog = f.read() 3797db96d56Sopenharmony_ci encoding = f.encoding 3807db96d56Sopenharmony_ci except OSError as err: 3817db96d56Sopenharmony_ci print(("Not printing coverage data for %r: %s" 3827db96d56Sopenharmony_ci % (filename, err)), file=sys.stderr) 3837db96d56Sopenharmony_ci return {} 3847db96d56Sopenharmony_ci code = compile(prog, filename, "exec") 3857db96d56Sopenharmony_ci strs = _find_strings(filename, encoding) 3867db96d56Sopenharmony_ci return _find_lines(code, strs) 3877db96d56Sopenharmony_ci 3887db96d56Sopenharmony_ciclass Trace: 3897db96d56Sopenharmony_ci def __init__(self, count=1, trace=1, countfuncs=0, countcallers=0, 3907db96d56Sopenharmony_ci ignoremods=(), ignoredirs=(), infile=None, outfile=None, 3917db96d56Sopenharmony_ci timing=False): 3927db96d56Sopenharmony_ci """ 3937db96d56Sopenharmony_ci @param count true iff it should count number of times each 3947db96d56Sopenharmony_ci line is executed 3957db96d56Sopenharmony_ci @param trace true iff it should print out each line that is 3967db96d56Sopenharmony_ci being counted 3977db96d56Sopenharmony_ci @param countfuncs true iff it should just output a list of 3987db96d56Sopenharmony_ci (filename, modulename, funcname,) for functions 3997db96d56Sopenharmony_ci that were called at least once; This overrides 4007db96d56Sopenharmony_ci `count' and `trace' 4017db96d56Sopenharmony_ci @param ignoremods a list of the names of modules to ignore 4027db96d56Sopenharmony_ci @param ignoredirs a list of the names of directories to ignore 4037db96d56Sopenharmony_ci all of the (recursive) contents of 4047db96d56Sopenharmony_ci @param infile file from which to read stored counts to be 4057db96d56Sopenharmony_ci added into the results 4067db96d56Sopenharmony_ci @param outfile file in which to write the results 4077db96d56Sopenharmony_ci @param timing true iff timing information be displayed 4087db96d56Sopenharmony_ci """ 4097db96d56Sopenharmony_ci self.infile = infile 4107db96d56Sopenharmony_ci self.outfile = outfile 4117db96d56Sopenharmony_ci self.ignore = _Ignore(ignoremods, ignoredirs) 4127db96d56Sopenharmony_ci self.counts = {} # keys are (filename, linenumber) 4137db96d56Sopenharmony_ci self.pathtobasename = {} # for memoizing os.path.basename 4147db96d56Sopenharmony_ci self.donothing = 0 4157db96d56Sopenharmony_ci self.trace = trace 4167db96d56Sopenharmony_ci self._calledfuncs = {} 4177db96d56Sopenharmony_ci self._callers = {} 4187db96d56Sopenharmony_ci self._caller_cache = {} 4197db96d56Sopenharmony_ci self.start_time = None 4207db96d56Sopenharmony_ci if timing: 4217db96d56Sopenharmony_ci self.start_time = _time() 4227db96d56Sopenharmony_ci if countcallers: 4237db96d56Sopenharmony_ci self.globaltrace = self.globaltrace_trackcallers 4247db96d56Sopenharmony_ci elif countfuncs: 4257db96d56Sopenharmony_ci self.globaltrace = self.globaltrace_countfuncs 4267db96d56Sopenharmony_ci elif trace and count: 4277db96d56Sopenharmony_ci self.globaltrace = self.globaltrace_lt 4287db96d56Sopenharmony_ci self.localtrace = self.localtrace_trace_and_count 4297db96d56Sopenharmony_ci elif trace: 4307db96d56Sopenharmony_ci self.globaltrace = self.globaltrace_lt 4317db96d56Sopenharmony_ci self.localtrace = self.localtrace_trace 4327db96d56Sopenharmony_ci elif count: 4337db96d56Sopenharmony_ci self.globaltrace = self.globaltrace_lt 4347db96d56Sopenharmony_ci self.localtrace = self.localtrace_count 4357db96d56Sopenharmony_ci else: 4367db96d56Sopenharmony_ci # Ahem -- do nothing? Okay. 4377db96d56Sopenharmony_ci self.donothing = 1 4387db96d56Sopenharmony_ci 4397db96d56Sopenharmony_ci def run(self, cmd): 4407db96d56Sopenharmony_ci import __main__ 4417db96d56Sopenharmony_ci dict = __main__.__dict__ 4427db96d56Sopenharmony_ci self.runctx(cmd, dict, dict) 4437db96d56Sopenharmony_ci 4447db96d56Sopenharmony_ci def runctx(self, cmd, globals=None, locals=None): 4457db96d56Sopenharmony_ci if globals is None: globals = {} 4467db96d56Sopenharmony_ci if locals is None: locals = {} 4477db96d56Sopenharmony_ci if not self.donothing: 4487db96d56Sopenharmony_ci threading.settrace(self.globaltrace) 4497db96d56Sopenharmony_ci sys.settrace(self.globaltrace) 4507db96d56Sopenharmony_ci try: 4517db96d56Sopenharmony_ci exec(cmd, globals, locals) 4527db96d56Sopenharmony_ci finally: 4537db96d56Sopenharmony_ci if not self.donothing: 4547db96d56Sopenharmony_ci sys.settrace(None) 4557db96d56Sopenharmony_ci threading.settrace(None) 4567db96d56Sopenharmony_ci 4577db96d56Sopenharmony_ci def runfunc(self, func, /, *args, **kw): 4587db96d56Sopenharmony_ci result = None 4597db96d56Sopenharmony_ci if not self.donothing: 4607db96d56Sopenharmony_ci sys.settrace(self.globaltrace) 4617db96d56Sopenharmony_ci try: 4627db96d56Sopenharmony_ci result = func(*args, **kw) 4637db96d56Sopenharmony_ci finally: 4647db96d56Sopenharmony_ci if not self.donothing: 4657db96d56Sopenharmony_ci sys.settrace(None) 4667db96d56Sopenharmony_ci return result 4677db96d56Sopenharmony_ci 4687db96d56Sopenharmony_ci def file_module_function_of(self, frame): 4697db96d56Sopenharmony_ci code = frame.f_code 4707db96d56Sopenharmony_ci filename = code.co_filename 4717db96d56Sopenharmony_ci if filename: 4727db96d56Sopenharmony_ci modulename = _modname(filename) 4737db96d56Sopenharmony_ci else: 4747db96d56Sopenharmony_ci modulename = None 4757db96d56Sopenharmony_ci 4767db96d56Sopenharmony_ci funcname = code.co_name 4777db96d56Sopenharmony_ci clsname = None 4787db96d56Sopenharmony_ci if code in self._caller_cache: 4797db96d56Sopenharmony_ci if self._caller_cache[code] is not None: 4807db96d56Sopenharmony_ci clsname = self._caller_cache[code] 4817db96d56Sopenharmony_ci else: 4827db96d56Sopenharmony_ci self._caller_cache[code] = None 4837db96d56Sopenharmony_ci ## use of gc.get_referrers() was suggested by Michael Hudson 4847db96d56Sopenharmony_ci # all functions which refer to this code object 4857db96d56Sopenharmony_ci funcs = [f for f in gc.get_referrers(code) 4867db96d56Sopenharmony_ci if inspect.isfunction(f)] 4877db96d56Sopenharmony_ci # require len(func) == 1 to avoid ambiguity caused by calls to 4887db96d56Sopenharmony_ci # new.function(): "In the face of ambiguity, refuse the 4897db96d56Sopenharmony_ci # temptation to guess." 4907db96d56Sopenharmony_ci if len(funcs) == 1: 4917db96d56Sopenharmony_ci dicts = [d for d in gc.get_referrers(funcs[0]) 4927db96d56Sopenharmony_ci if isinstance(d, dict)] 4937db96d56Sopenharmony_ci if len(dicts) == 1: 4947db96d56Sopenharmony_ci classes = [c for c in gc.get_referrers(dicts[0]) 4957db96d56Sopenharmony_ci if hasattr(c, "__bases__")] 4967db96d56Sopenharmony_ci if len(classes) == 1: 4977db96d56Sopenharmony_ci # ditto for new.classobj() 4987db96d56Sopenharmony_ci clsname = classes[0].__name__ 4997db96d56Sopenharmony_ci # cache the result - assumption is that new.* is 5007db96d56Sopenharmony_ci # not called later to disturb this relationship 5017db96d56Sopenharmony_ci # _caller_cache could be flushed if functions in 5027db96d56Sopenharmony_ci # the new module get called. 5037db96d56Sopenharmony_ci self._caller_cache[code] = clsname 5047db96d56Sopenharmony_ci if clsname is not None: 5057db96d56Sopenharmony_ci funcname = "%s.%s" % (clsname, funcname) 5067db96d56Sopenharmony_ci 5077db96d56Sopenharmony_ci return filename, modulename, funcname 5087db96d56Sopenharmony_ci 5097db96d56Sopenharmony_ci def globaltrace_trackcallers(self, frame, why, arg): 5107db96d56Sopenharmony_ci """Handler for call events. 5117db96d56Sopenharmony_ci 5127db96d56Sopenharmony_ci Adds information about who called who to the self._callers dict. 5137db96d56Sopenharmony_ci """ 5147db96d56Sopenharmony_ci if why == 'call': 5157db96d56Sopenharmony_ci # XXX Should do a better job of identifying methods 5167db96d56Sopenharmony_ci this_func = self.file_module_function_of(frame) 5177db96d56Sopenharmony_ci parent_func = self.file_module_function_of(frame.f_back) 5187db96d56Sopenharmony_ci self._callers[(parent_func, this_func)] = 1 5197db96d56Sopenharmony_ci 5207db96d56Sopenharmony_ci def globaltrace_countfuncs(self, frame, why, arg): 5217db96d56Sopenharmony_ci """Handler for call events. 5227db96d56Sopenharmony_ci 5237db96d56Sopenharmony_ci Adds (filename, modulename, funcname) to the self._calledfuncs dict. 5247db96d56Sopenharmony_ci """ 5257db96d56Sopenharmony_ci if why == 'call': 5267db96d56Sopenharmony_ci this_func = self.file_module_function_of(frame) 5277db96d56Sopenharmony_ci self._calledfuncs[this_func] = 1 5287db96d56Sopenharmony_ci 5297db96d56Sopenharmony_ci def globaltrace_lt(self, frame, why, arg): 5307db96d56Sopenharmony_ci """Handler for call events. 5317db96d56Sopenharmony_ci 5327db96d56Sopenharmony_ci If the code block being entered is to be ignored, returns `None', 5337db96d56Sopenharmony_ci else returns self.localtrace. 5347db96d56Sopenharmony_ci """ 5357db96d56Sopenharmony_ci if why == 'call': 5367db96d56Sopenharmony_ci code = frame.f_code 5377db96d56Sopenharmony_ci filename = frame.f_globals.get('__file__', None) 5387db96d56Sopenharmony_ci if filename: 5397db96d56Sopenharmony_ci # XXX _modname() doesn't work right for packages, so 5407db96d56Sopenharmony_ci # the ignore support won't work right for packages 5417db96d56Sopenharmony_ci modulename = _modname(filename) 5427db96d56Sopenharmony_ci if modulename is not None: 5437db96d56Sopenharmony_ci ignore_it = self.ignore.names(filename, modulename) 5447db96d56Sopenharmony_ci if not ignore_it: 5457db96d56Sopenharmony_ci if self.trace: 5467db96d56Sopenharmony_ci print((" --- modulename: %s, funcname: %s" 5477db96d56Sopenharmony_ci % (modulename, code.co_name))) 5487db96d56Sopenharmony_ci return self.localtrace 5497db96d56Sopenharmony_ci else: 5507db96d56Sopenharmony_ci return None 5517db96d56Sopenharmony_ci 5527db96d56Sopenharmony_ci def localtrace_trace_and_count(self, frame, why, arg): 5537db96d56Sopenharmony_ci if why == "line": 5547db96d56Sopenharmony_ci # record the file name and line number of every trace 5557db96d56Sopenharmony_ci filename = frame.f_code.co_filename 5567db96d56Sopenharmony_ci lineno = frame.f_lineno 5577db96d56Sopenharmony_ci key = filename, lineno 5587db96d56Sopenharmony_ci self.counts[key] = self.counts.get(key, 0) + 1 5597db96d56Sopenharmony_ci 5607db96d56Sopenharmony_ci if self.start_time: 5617db96d56Sopenharmony_ci print('%.2f' % (_time() - self.start_time), end=' ') 5627db96d56Sopenharmony_ci bname = os.path.basename(filename) 5637db96d56Sopenharmony_ci print("%s(%d): %s" % (bname, lineno, 5647db96d56Sopenharmony_ci linecache.getline(filename, lineno)), end='') 5657db96d56Sopenharmony_ci return self.localtrace 5667db96d56Sopenharmony_ci 5677db96d56Sopenharmony_ci def localtrace_trace(self, frame, why, arg): 5687db96d56Sopenharmony_ci if why == "line": 5697db96d56Sopenharmony_ci # record the file name and line number of every trace 5707db96d56Sopenharmony_ci filename = frame.f_code.co_filename 5717db96d56Sopenharmony_ci lineno = frame.f_lineno 5727db96d56Sopenharmony_ci 5737db96d56Sopenharmony_ci if self.start_time: 5747db96d56Sopenharmony_ci print('%.2f' % (_time() - self.start_time), end=' ') 5757db96d56Sopenharmony_ci bname = os.path.basename(filename) 5767db96d56Sopenharmony_ci print("%s(%d): %s" % (bname, lineno, 5777db96d56Sopenharmony_ci linecache.getline(filename, lineno)), end='') 5787db96d56Sopenharmony_ci return self.localtrace 5797db96d56Sopenharmony_ci 5807db96d56Sopenharmony_ci def localtrace_count(self, frame, why, arg): 5817db96d56Sopenharmony_ci if why == "line": 5827db96d56Sopenharmony_ci filename = frame.f_code.co_filename 5837db96d56Sopenharmony_ci lineno = frame.f_lineno 5847db96d56Sopenharmony_ci key = filename, lineno 5857db96d56Sopenharmony_ci self.counts[key] = self.counts.get(key, 0) + 1 5867db96d56Sopenharmony_ci return self.localtrace 5877db96d56Sopenharmony_ci 5887db96d56Sopenharmony_ci def results(self): 5897db96d56Sopenharmony_ci return CoverageResults(self.counts, infile=self.infile, 5907db96d56Sopenharmony_ci outfile=self.outfile, 5917db96d56Sopenharmony_ci calledfuncs=self._calledfuncs, 5927db96d56Sopenharmony_ci callers=self._callers) 5937db96d56Sopenharmony_ci 5947db96d56Sopenharmony_cidef main(): 5957db96d56Sopenharmony_ci import argparse 5967db96d56Sopenharmony_ci 5977db96d56Sopenharmony_ci parser = argparse.ArgumentParser() 5987db96d56Sopenharmony_ci parser.add_argument('--version', action='version', version='trace 2.0') 5997db96d56Sopenharmony_ci 6007db96d56Sopenharmony_ci grp = parser.add_argument_group('Main options', 6017db96d56Sopenharmony_ci 'One of these (or --report) must be given') 6027db96d56Sopenharmony_ci 6037db96d56Sopenharmony_ci grp.add_argument('-c', '--count', action='store_true', 6047db96d56Sopenharmony_ci help='Count the number of times each line is executed and write ' 6057db96d56Sopenharmony_ci 'the counts to <module>.cover for each module executed, in ' 6067db96d56Sopenharmony_ci 'the module\'s directory. See also --coverdir, --file, ' 6077db96d56Sopenharmony_ci '--no-report below.') 6087db96d56Sopenharmony_ci grp.add_argument('-t', '--trace', action='store_true', 6097db96d56Sopenharmony_ci help='Print each line to sys.stdout before it is executed') 6107db96d56Sopenharmony_ci grp.add_argument('-l', '--listfuncs', action='store_true', 6117db96d56Sopenharmony_ci help='Keep track of which functions are executed at least once ' 6127db96d56Sopenharmony_ci 'and write the results to sys.stdout after the program exits. ' 6137db96d56Sopenharmony_ci 'Cannot be specified alongside --trace or --count.') 6147db96d56Sopenharmony_ci grp.add_argument('-T', '--trackcalls', action='store_true', 6157db96d56Sopenharmony_ci help='Keep track of caller/called pairs and write the results to ' 6167db96d56Sopenharmony_ci 'sys.stdout after the program exits.') 6177db96d56Sopenharmony_ci 6187db96d56Sopenharmony_ci grp = parser.add_argument_group('Modifiers') 6197db96d56Sopenharmony_ci 6207db96d56Sopenharmony_ci _grp = grp.add_mutually_exclusive_group() 6217db96d56Sopenharmony_ci _grp.add_argument('-r', '--report', action='store_true', 6227db96d56Sopenharmony_ci help='Generate a report from a counts file; does not execute any ' 6237db96d56Sopenharmony_ci 'code. --file must specify the results file to read, which ' 6247db96d56Sopenharmony_ci 'must have been created in a previous run with --count ' 6257db96d56Sopenharmony_ci '--file=FILE') 6267db96d56Sopenharmony_ci _grp.add_argument('-R', '--no-report', action='store_true', 6277db96d56Sopenharmony_ci help='Do not generate the coverage report files. ' 6287db96d56Sopenharmony_ci 'Useful if you want to accumulate over several runs.') 6297db96d56Sopenharmony_ci 6307db96d56Sopenharmony_ci grp.add_argument('-f', '--file', 6317db96d56Sopenharmony_ci help='File to accumulate counts over several runs') 6327db96d56Sopenharmony_ci grp.add_argument('-C', '--coverdir', 6337db96d56Sopenharmony_ci help='Directory where the report files go. The coverage report ' 6347db96d56Sopenharmony_ci 'for <package>.<module> will be written to file ' 6357db96d56Sopenharmony_ci '<dir>/<package>/<module>.cover') 6367db96d56Sopenharmony_ci grp.add_argument('-m', '--missing', action='store_true', 6377db96d56Sopenharmony_ci help='Annotate executable lines that were not executed with ' 6387db96d56Sopenharmony_ci '">>>>>> "') 6397db96d56Sopenharmony_ci grp.add_argument('-s', '--summary', action='store_true', 6407db96d56Sopenharmony_ci help='Write a brief summary for each file to sys.stdout. ' 6417db96d56Sopenharmony_ci 'Can only be used with --count or --report') 6427db96d56Sopenharmony_ci grp.add_argument('-g', '--timing', action='store_true', 6437db96d56Sopenharmony_ci help='Prefix each line with the time since the program started. ' 6447db96d56Sopenharmony_ci 'Only used while tracing') 6457db96d56Sopenharmony_ci 6467db96d56Sopenharmony_ci grp = parser.add_argument_group('Filters', 6477db96d56Sopenharmony_ci 'Can be specified multiple times') 6487db96d56Sopenharmony_ci grp.add_argument('--ignore-module', action='append', default=[], 6497db96d56Sopenharmony_ci help='Ignore the given module(s) and its submodules ' 6507db96d56Sopenharmony_ci '(if it is a package). Accepts comma separated list of ' 6517db96d56Sopenharmony_ci 'module names.') 6527db96d56Sopenharmony_ci grp.add_argument('--ignore-dir', action='append', default=[], 6537db96d56Sopenharmony_ci help='Ignore files in the given directory ' 6547db96d56Sopenharmony_ci '(multiple directories can be joined by os.pathsep).') 6557db96d56Sopenharmony_ci 6567db96d56Sopenharmony_ci parser.add_argument('--module', action='store_true', default=False, 6577db96d56Sopenharmony_ci help='Trace a module. ') 6587db96d56Sopenharmony_ci parser.add_argument('progname', nargs='?', 6597db96d56Sopenharmony_ci help='file to run as main program') 6607db96d56Sopenharmony_ci parser.add_argument('arguments', nargs=argparse.REMAINDER, 6617db96d56Sopenharmony_ci help='arguments to the program') 6627db96d56Sopenharmony_ci 6637db96d56Sopenharmony_ci opts = parser.parse_args() 6647db96d56Sopenharmony_ci 6657db96d56Sopenharmony_ci if opts.ignore_dir: 6667db96d56Sopenharmony_ci _prefix = sysconfig.get_path("stdlib") 6677db96d56Sopenharmony_ci _exec_prefix = sysconfig.get_path("platstdlib") 6687db96d56Sopenharmony_ci 6697db96d56Sopenharmony_ci def parse_ignore_dir(s): 6707db96d56Sopenharmony_ci s = os.path.expanduser(os.path.expandvars(s)) 6717db96d56Sopenharmony_ci s = s.replace('$prefix', _prefix).replace('$exec_prefix', _exec_prefix) 6727db96d56Sopenharmony_ci return os.path.normpath(s) 6737db96d56Sopenharmony_ci 6747db96d56Sopenharmony_ci opts.ignore_module = [mod.strip() 6757db96d56Sopenharmony_ci for i in opts.ignore_module for mod in i.split(',')] 6767db96d56Sopenharmony_ci opts.ignore_dir = [parse_ignore_dir(s) 6777db96d56Sopenharmony_ci for i in opts.ignore_dir for s in i.split(os.pathsep)] 6787db96d56Sopenharmony_ci 6797db96d56Sopenharmony_ci if opts.report: 6807db96d56Sopenharmony_ci if not opts.file: 6817db96d56Sopenharmony_ci parser.error('-r/--report requires -f/--file') 6827db96d56Sopenharmony_ci results = CoverageResults(infile=opts.file, outfile=opts.file) 6837db96d56Sopenharmony_ci return results.write_results(opts.missing, opts.summary, opts.coverdir) 6847db96d56Sopenharmony_ci 6857db96d56Sopenharmony_ci if not any([opts.trace, opts.count, opts.listfuncs, opts.trackcalls]): 6867db96d56Sopenharmony_ci parser.error('must specify one of --trace, --count, --report, ' 6877db96d56Sopenharmony_ci '--listfuncs, or --trackcalls') 6887db96d56Sopenharmony_ci 6897db96d56Sopenharmony_ci if opts.listfuncs and (opts.count or opts.trace): 6907db96d56Sopenharmony_ci parser.error('cannot specify both --listfuncs and (--trace or --count)') 6917db96d56Sopenharmony_ci 6927db96d56Sopenharmony_ci if opts.summary and not opts.count: 6937db96d56Sopenharmony_ci parser.error('--summary can only be used with --count or --report') 6947db96d56Sopenharmony_ci 6957db96d56Sopenharmony_ci if opts.progname is None: 6967db96d56Sopenharmony_ci parser.error('progname is missing: required with the main options') 6977db96d56Sopenharmony_ci 6987db96d56Sopenharmony_ci t = Trace(opts.count, opts.trace, countfuncs=opts.listfuncs, 6997db96d56Sopenharmony_ci countcallers=opts.trackcalls, ignoremods=opts.ignore_module, 7007db96d56Sopenharmony_ci ignoredirs=opts.ignore_dir, infile=opts.file, 7017db96d56Sopenharmony_ci outfile=opts.file, timing=opts.timing) 7027db96d56Sopenharmony_ci try: 7037db96d56Sopenharmony_ci if opts.module: 7047db96d56Sopenharmony_ci import runpy 7057db96d56Sopenharmony_ci module_name = opts.progname 7067db96d56Sopenharmony_ci mod_name, mod_spec, code = runpy._get_module_details(module_name) 7077db96d56Sopenharmony_ci sys.argv = [code.co_filename, *opts.arguments] 7087db96d56Sopenharmony_ci globs = { 7097db96d56Sopenharmony_ci '__name__': '__main__', 7107db96d56Sopenharmony_ci '__file__': code.co_filename, 7117db96d56Sopenharmony_ci '__package__': mod_spec.parent, 7127db96d56Sopenharmony_ci '__loader__': mod_spec.loader, 7137db96d56Sopenharmony_ci '__spec__': mod_spec, 7147db96d56Sopenharmony_ci '__cached__': None, 7157db96d56Sopenharmony_ci } 7167db96d56Sopenharmony_ci else: 7177db96d56Sopenharmony_ci sys.argv = [opts.progname, *opts.arguments] 7187db96d56Sopenharmony_ci sys.path[0] = os.path.dirname(opts.progname) 7197db96d56Sopenharmony_ci 7207db96d56Sopenharmony_ci with io.open_code(opts.progname) as fp: 7217db96d56Sopenharmony_ci code = compile(fp.read(), opts.progname, 'exec') 7227db96d56Sopenharmony_ci # try to emulate __main__ namespace as much as possible 7237db96d56Sopenharmony_ci globs = { 7247db96d56Sopenharmony_ci '__file__': opts.progname, 7257db96d56Sopenharmony_ci '__name__': '__main__', 7267db96d56Sopenharmony_ci '__package__': None, 7277db96d56Sopenharmony_ci '__cached__': None, 7287db96d56Sopenharmony_ci } 7297db96d56Sopenharmony_ci t.runctx(code, globs, globs) 7307db96d56Sopenharmony_ci except OSError as err: 7317db96d56Sopenharmony_ci sys.exit("Cannot run file %r because: %s" % (sys.argv[0], err)) 7327db96d56Sopenharmony_ci except SystemExit: 7337db96d56Sopenharmony_ci pass 7347db96d56Sopenharmony_ci 7357db96d56Sopenharmony_ci results = t.results() 7367db96d56Sopenharmony_ci 7377db96d56Sopenharmony_ci if not opts.no_report: 7387db96d56Sopenharmony_ci results.write_results(opts.missing, opts.summary, opts.coverdir) 7397db96d56Sopenharmony_ci 7407db96d56Sopenharmony_ciif __name__=='__main__': 7417db96d56Sopenharmony_ci main() 742