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