PageRenderTime 51ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 1ms

/lib-python/modified-2.7/trace.py

https://bitbucket.org/evelyn559/pypy
Python | 831 lines | 682 code | 60 blank | 89 comment | 93 complexity | 8c13ea6f8c8ef52cdafd3e375d5ee4ab MD5 | raw file
  1. #!/usr/bin/env python
  2. # portions copyright 2001, Autonomous Zones Industries, Inc., all rights...
  3. # err... reserved and offered to the public under the terms of the
  4. # Python 2.2 license.
  5. # Author: Zooko O'Whielacronx
  6. # http://zooko.com/
  7. # mailto:zooko@zooko.com
  8. #
  9. # Copyright 2000, Mojam Media, Inc., all rights reserved.
  10. # Author: Skip Montanaro
  11. #
  12. # Copyright 1999, Bioreason, Inc., all rights reserved.
  13. # Author: Andrew Dalke
  14. #
  15. # Copyright 1995-1997, Automatrix, Inc., all rights reserved.
  16. # Author: Skip Montanaro
  17. #
  18. # Copyright 1991-1995, Stichting Mathematisch Centrum, all rights reserved.
  19. #
  20. #
  21. # Permission to use, copy, modify, and distribute this Python software and
  22. # its associated documentation for any purpose without fee is hereby
  23. # granted, provided that the above copyright notice appears in all copies,
  24. # and that both that copyright notice and this permission notice appear in
  25. # supporting documentation, and that the name of neither Automatrix,
  26. # Bioreason or Mojam Media be used in advertising or publicity pertaining to
  27. # distribution of the software without specific, written prior permission.
  28. #
  29. """program/module to trace Python program or function execution
  30. Sample use, command line:
  31. trace.py -c -f counts --ignore-dir '$prefix' spam.py eggs
  32. trace.py -t --ignore-dir '$prefix' spam.py eggs
  33. trace.py --trackcalls spam.py eggs
  34. Sample use, programmatically
  35. import sys
  36. # create a Trace object, telling it what to ignore, and whether to
  37. # do tracing or line-counting or both.
  38. tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,], trace=0,
  39. count=1)
  40. # run the new command using the given tracer
  41. tracer.run('main()')
  42. # make a report, placing output in /tmp
  43. r = tracer.results()
  44. r.write_results(show_missing=True, coverdir="/tmp")
  45. """
  46. import linecache
  47. import os
  48. import re
  49. import sys
  50. import time
  51. import token
  52. import tokenize
  53. import inspect
  54. import gc
  55. import dis
  56. try:
  57. import cPickle
  58. pickle = cPickle
  59. except ImportError:
  60. import pickle
  61. try:
  62. import threading
  63. except ImportError:
  64. _settrace = sys.settrace
  65. def _unsettrace():
  66. sys.settrace(None)
  67. else:
  68. def _settrace(func):
  69. threading.settrace(func)
  70. sys.settrace(func)
  71. def _unsettrace():
  72. sys.settrace(None)
  73. threading.settrace(None)
  74. def usage(outfile):
  75. outfile.write("""Usage: %s [OPTIONS] <file> [ARGS]
  76. Meta-options:
  77. --help Display this help then exit.
  78. --version Output version information then exit.
  79. Otherwise, exactly one of the following three options must be given:
  80. -t, --trace Print each line to sys.stdout before it is executed.
  81. -c, --count Count the number of times each line is executed
  82. and write the counts to <module>.cover for each
  83. module executed, in the module's directory.
  84. See also `--coverdir', `--file', `--no-report' below.
  85. -l, --listfuncs Keep track of which functions are executed at least
  86. once and write the results to sys.stdout after the
  87. program exits.
  88. -T, --trackcalls Keep track of caller/called pairs and write the
  89. results to sys.stdout after the program exits.
  90. -r, --report Generate a report from a counts file; do not execute
  91. any code. `--file' must specify the results file to
  92. read, which must have been created in a previous run
  93. with `--count --file=FILE'.
  94. Modifiers:
  95. -f, --file=<file> File to accumulate counts over several runs.
  96. -R, --no-report Do not generate the coverage report files.
  97. Useful if you want to accumulate over several runs.
  98. -C, --coverdir=<dir> Directory where the report files. The coverage
  99. report for <package>.<module> is written to file
  100. <dir>/<package>/<module>.cover.
  101. -m, --missing Annotate executable lines that were not executed
  102. with '>>>>>> '.
  103. -s, --summary Write a brief summary on stdout for each file.
  104. (Can only be used with --count or --report.)
  105. -g, --timing Prefix each line with the time since the program started.
  106. Only used while tracing.
  107. Filters, may be repeated multiple times:
  108. --ignore-module=<mod> Ignore the given module(s) and its submodules
  109. (if it is a package). Accepts comma separated
  110. list of module names
  111. --ignore-dir=<dir> Ignore files in the given directory (multiple
  112. directories can be joined by os.pathsep).
  113. """ % sys.argv[0])
  114. PRAGMA_NOCOVER = "#pragma NO COVER"
  115. # Simple rx to find lines with no code.
  116. rx_blank = re.compile(r'^\s*(#.*)?$')
  117. class Ignore:
  118. def __init__(self, modules = None, dirs = None):
  119. self._mods = modules or []
  120. self._dirs = dirs or []
  121. self._dirs = map(os.path.normpath, self._dirs)
  122. self._ignore = { '<string>': 1 }
  123. def names(self, filename, modulename):
  124. if modulename in self._ignore:
  125. return self._ignore[modulename]
  126. # haven't seen this one before, so see if the module name is
  127. # on the ignore list. Need to take some care since ignoring
  128. # "cmp" musn't mean ignoring "cmpcache" but ignoring
  129. # "Spam" must also mean ignoring "Spam.Eggs".
  130. for mod in self._mods:
  131. if mod == modulename: # Identical names, so ignore
  132. self._ignore[modulename] = 1
  133. return 1
  134. # check if the module is a proper submodule of something on
  135. # the ignore list
  136. n = len(mod)
  137. # (will not overflow since if the first n characters are the
  138. # same and the name has not already occurred, then the size
  139. # of "name" is greater than that of "mod")
  140. if mod == modulename[:n] and modulename[n] == '.':
  141. self._ignore[modulename] = 1
  142. return 1
  143. # Now check that __file__ isn't in one of the directories
  144. if filename is None:
  145. # must be a built-in, so we must ignore
  146. self._ignore[modulename] = 1
  147. return 1
  148. # Ignore a file when it contains one of the ignorable paths
  149. for d in self._dirs:
  150. # The '+ os.sep' is to ensure that d is a parent directory,
  151. # as compared to cases like:
  152. # d = "/usr/local"
  153. # filename = "/usr/local.py"
  154. # or
  155. # d = "/usr/local.py"
  156. # filename = "/usr/local.py"
  157. if filename.startswith(d + os.sep):
  158. self._ignore[modulename] = 1
  159. return 1
  160. # Tried the different ways, so we don't ignore this module
  161. self._ignore[modulename] = 0
  162. return 0
  163. def modname(path):
  164. """Return a plausible module name for the patch."""
  165. base = os.path.basename(path)
  166. filename, ext = os.path.splitext(base)
  167. return filename
  168. def fullmodname(path):
  169. """Return a plausible module name for the path."""
  170. # If the file 'path' is part of a package, then the filename isn't
  171. # enough to uniquely identify it. Try to do the right thing by
  172. # looking in sys.path for the longest matching prefix. We'll
  173. # assume that the rest is the package name.
  174. comparepath = os.path.normcase(path)
  175. longest = ""
  176. for dir in sys.path:
  177. dir = os.path.normcase(dir)
  178. if comparepath.startswith(dir) and comparepath[len(dir)] == os.sep:
  179. if len(dir) > len(longest):
  180. longest = dir
  181. if longest:
  182. base = path[len(longest) + 1:]
  183. else:
  184. base = path
  185. # the drive letter is never part of the module name
  186. drive, base = os.path.splitdrive(base)
  187. base = base.replace(os.sep, ".")
  188. if os.altsep:
  189. base = base.replace(os.altsep, ".")
  190. filename, ext = os.path.splitext(base)
  191. return filename.lstrip(".")
  192. class CoverageResults:
  193. def __init__(self, counts=None, calledfuncs=None, infile=None,
  194. callers=None, outfile=None):
  195. self.counts = counts
  196. if self.counts is None:
  197. self.counts = {}
  198. self.counter = self.counts.copy() # map (filename, lineno) to count
  199. self.calledfuncs = calledfuncs
  200. if self.calledfuncs is None:
  201. self.calledfuncs = {}
  202. self.calledfuncs = self.calledfuncs.copy()
  203. self.callers = callers
  204. if self.callers is None:
  205. self.callers = {}
  206. self.callers = self.callers.copy()
  207. self.infile = infile
  208. self.outfile = outfile
  209. if self.infile:
  210. # Try to merge existing counts file.
  211. try:
  212. counts, calledfuncs, callers = \
  213. pickle.load(open(self.infile, 'rb'))
  214. self.update(self.__class__(counts, calledfuncs, callers))
  215. except (IOError, EOFError, ValueError), err:
  216. print >> sys.stderr, ("Skipping counts file %r: %s"
  217. % (self.infile, err))
  218. def update(self, other):
  219. """Merge in the data from another CoverageResults"""
  220. counts = self.counts
  221. calledfuncs = self.calledfuncs
  222. callers = self.callers
  223. other_counts = other.counts
  224. other_calledfuncs = other.calledfuncs
  225. other_callers = other.callers
  226. for key in other_counts.keys():
  227. counts[key] = counts.get(key, 0) + other_counts[key]
  228. for key in other_calledfuncs.keys():
  229. calledfuncs[key] = 1
  230. for key in other_callers.keys():
  231. callers[key] = 1
  232. def write_results(self, show_missing=True, summary=False, coverdir=None):
  233. """
  234. @param coverdir
  235. """
  236. if self.calledfuncs:
  237. print
  238. print "functions called:"
  239. calls = self.calledfuncs.keys()
  240. calls.sort()
  241. for filename, modulename, funcname in calls:
  242. print ("filename: %s, modulename: %s, funcname: %s"
  243. % (filename, modulename, funcname))
  244. if self.callers:
  245. print
  246. print "calling relationships:"
  247. calls = self.callers.keys()
  248. calls.sort()
  249. lastfile = lastcfile = ""
  250. for ((pfile, pmod, pfunc), (cfile, cmod, cfunc)) in calls:
  251. if pfile != lastfile:
  252. print
  253. print "***", pfile, "***"
  254. lastfile = pfile
  255. lastcfile = ""
  256. if cfile != pfile and lastcfile != cfile:
  257. print " -->", cfile
  258. lastcfile = cfile
  259. print " %s.%s -> %s.%s" % (pmod, pfunc, cmod, cfunc)
  260. # turn the counts data ("(filename, lineno) = count") into something
  261. # accessible on a per-file basis
  262. per_file = {}
  263. for filename, lineno in self.counts.keys():
  264. lines_hit = per_file[filename] = per_file.get(filename, {})
  265. lines_hit[lineno] = self.counts[(filename, lineno)]
  266. # accumulate summary info, if needed
  267. sums = {}
  268. for filename, count in per_file.iteritems():
  269. # skip some "files" we don't care about...
  270. if filename == "<string>":
  271. continue
  272. if filename.startswith("<doctest "):
  273. continue
  274. if filename.endswith((".pyc", ".pyo")):
  275. filename = filename[:-1]
  276. if coverdir is None:
  277. dir = os.path.dirname(os.path.abspath(filename))
  278. modulename = modname(filename)
  279. else:
  280. dir = coverdir
  281. if not os.path.exists(dir):
  282. os.makedirs(dir)
  283. modulename = fullmodname(filename)
  284. # If desired, get a list of the line numbers which represent
  285. # executable content (returned as a dict for better lookup speed)
  286. if show_missing:
  287. lnotab = find_executable_linenos(filename)
  288. else:
  289. lnotab = {}
  290. source = linecache.getlines(filename)
  291. coverpath = os.path.join(dir, modulename + ".cover")
  292. n_hits, n_lines = self.write_results_file(coverpath, source,
  293. lnotab, count)
  294. if summary and n_lines:
  295. percent = 100 * n_hits // n_lines
  296. sums[modulename] = n_lines, percent, modulename, filename
  297. if summary and sums:
  298. mods = sums.keys()
  299. mods.sort()
  300. print "lines cov% module (path)"
  301. for m in mods:
  302. n_lines, percent, modulename, filename = sums[m]
  303. print "%5d %3d%% %s (%s)" % sums[m]
  304. if self.outfile:
  305. # try and store counts and module info into self.outfile
  306. try:
  307. pickle.dump((self.counts, self.calledfuncs, self.callers),
  308. open(self.outfile, 'wb'), 1)
  309. except IOError, err:
  310. print >> sys.stderr, "Can't save counts files because %s" % err
  311. def write_results_file(self, path, lines, lnotab, lines_hit):
  312. """Return a coverage results file in path."""
  313. try:
  314. outfile = open(path, "w")
  315. except IOError, err:
  316. print >> sys.stderr, ("trace: Could not open %r for writing: %s"
  317. "- skipping" % (path, err))
  318. return 0, 0
  319. n_lines = 0
  320. n_hits = 0
  321. for i, line in enumerate(lines):
  322. lineno = i + 1
  323. # do the blank/comment match to try to mark more lines
  324. # (help the reader find stuff that hasn't been covered)
  325. if lineno in lines_hit:
  326. outfile.write("%5d: " % lines_hit[lineno])
  327. n_hits += 1
  328. n_lines += 1
  329. elif rx_blank.match(line):
  330. outfile.write(" ")
  331. else:
  332. # lines preceded by no marks weren't hit
  333. # Highlight them if so indicated, unless the line contains
  334. # #pragma: NO COVER
  335. if lineno in lnotab and not PRAGMA_NOCOVER in lines[i]:
  336. outfile.write(">>>>>> ")
  337. n_lines += 1
  338. else:
  339. outfile.write(" ")
  340. outfile.write(lines[i].expandtabs(8))
  341. outfile.close()
  342. return n_hits, n_lines
  343. def find_lines_from_code(code, strs):
  344. """Return dict where keys are lines in the line number table."""
  345. linenos = {}
  346. for _, lineno in dis.findlinestarts(code):
  347. if lineno not in strs:
  348. linenos[lineno] = 1
  349. return linenos
  350. def find_lines(code, strs):
  351. """Return lineno dict for all code objects reachable from code."""
  352. # get all of the lineno information from the code of this scope level
  353. linenos = find_lines_from_code(code, strs)
  354. # and check the constants for references to other code objects
  355. for c in code.co_consts:
  356. if inspect.iscode(c):
  357. # find another code object, so recurse into it
  358. linenos.update(find_lines(c, strs))
  359. return linenos
  360. def find_strings(filename):
  361. """Return a dict of possible docstring positions.
  362. The dict maps line numbers to strings. There is an entry for
  363. line that contains only a string or a part of a triple-quoted
  364. string.
  365. """
  366. d = {}
  367. # If the first token is a string, then it's the module docstring.
  368. # Add this special case so that the test in the loop passes.
  369. prev_ttype = token.INDENT
  370. f = open(filename)
  371. for ttype, tstr, start, end, line in tokenize.generate_tokens(f.readline):
  372. if ttype == token.STRING:
  373. if prev_ttype == token.INDENT:
  374. sline, scol = start
  375. eline, ecol = end
  376. for i in range(sline, eline + 1):
  377. d[i] = 1
  378. prev_ttype = ttype
  379. f.close()
  380. return d
  381. def find_executable_linenos(filename):
  382. """Return dict where keys are line numbers in the line number table."""
  383. try:
  384. prog = open(filename, "rU").read()
  385. except IOError, err:
  386. print >> sys.stderr, ("Not printing coverage data for %r: %s"
  387. % (filename, err))
  388. return {}
  389. code = compile(prog, filename, "exec")
  390. strs = find_strings(filename)
  391. return find_lines(code, strs)
  392. class Trace:
  393. def __init__(self, count=1, trace=1, countfuncs=0, countcallers=0,
  394. ignoremods=(), ignoredirs=(), infile=None, outfile=None,
  395. timing=False):
  396. """
  397. @param count true iff it should count number of times each
  398. line is executed
  399. @param trace true iff it should print out each line that is
  400. being counted
  401. @param countfuncs true iff it should just output a list of
  402. (filename, modulename, funcname,) for functions
  403. that were called at least once; This overrides
  404. `count' and `trace'
  405. @param ignoremods a list of the names of modules to ignore
  406. @param ignoredirs a list of the names of directories to ignore
  407. all of the (recursive) contents of
  408. @param infile file from which to read stored counts to be
  409. added into the results
  410. @param outfile file in which to write the results
  411. @param timing true iff timing information be displayed
  412. """
  413. self.infile = infile
  414. self.outfile = outfile
  415. self.ignore = Ignore(ignoremods, ignoredirs)
  416. self.counts = {} # keys are (filename, linenumber)
  417. self.blabbed = {} # for debugging
  418. self.pathtobasename = {} # for memoizing os.path.basename
  419. self.donothing = 0
  420. self.trace = trace
  421. self._calledfuncs = {}
  422. self._callers = {}
  423. self._caller_cache = {}
  424. self.start_time = None
  425. if timing:
  426. self.start_time = time.time()
  427. if countcallers:
  428. self.globaltrace = self.globaltrace_trackcallers
  429. elif countfuncs:
  430. self.globaltrace = self.globaltrace_countfuncs
  431. elif trace and count:
  432. self.globaltrace = self.globaltrace_lt
  433. self.localtrace = self.localtrace_trace_and_count
  434. elif trace:
  435. self.globaltrace = self.globaltrace_lt
  436. self.localtrace = self.localtrace_trace
  437. elif count:
  438. self.globaltrace = self.globaltrace_lt
  439. self.localtrace = self.localtrace_count
  440. else:
  441. # Ahem -- do nothing? Okay.
  442. self.donothing = 1
  443. def run(self, cmd):
  444. import __main__
  445. dict = __main__.__dict__
  446. if not self.donothing:
  447. threading.settrace(self.globaltrace)
  448. sys.settrace(self.globaltrace)
  449. try:
  450. exec cmd in dict, dict
  451. finally:
  452. if not self.donothing:
  453. sys.settrace(None)
  454. threading.settrace(None)
  455. def runctx(self, cmd, globals=None, locals=None):
  456. if globals is None: globals = {}
  457. if locals is None: locals = {}
  458. if not self.donothing:
  459. _settrace(self.globaltrace)
  460. try:
  461. exec cmd in globals, locals
  462. finally:
  463. if not self.donothing:
  464. _unsettrace()
  465. def runfunc(self, func, *args, **kw):
  466. result = None
  467. if not self.donothing:
  468. sys.settrace(self.globaltrace)
  469. try:
  470. result = func(*args, **kw)
  471. finally:
  472. if not self.donothing:
  473. sys.settrace(None)
  474. return result
  475. def file_module_function_of(self, frame):
  476. code = frame.f_code
  477. filename = code.co_filename
  478. if filename:
  479. modulename = modname(filename)
  480. else:
  481. modulename = None
  482. funcname = code.co_name
  483. clsname = None
  484. if code in self._caller_cache:
  485. if self._caller_cache[code] is not None:
  486. clsname = self._caller_cache[code]
  487. else:
  488. self._caller_cache[code] = None
  489. ## use of gc.get_referrers() was suggested by Michael Hudson
  490. # all functions which refer to this code object
  491. funcs = [f for f in gc.get_referrers(code)
  492. if inspect.isfunction(f)]
  493. # require len(func) == 1 to avoid ambiguity caused by calls to
  494. # new.function(): "In the face of ambiguity, refuse the
  495. # temptation to guess."
  496. if len(funcs) == 1:
  497. dicts = [d for d in gc.get_referrers(funcs[0])
  498. if isinstance(d, dict)]
  499. if len(dicts) == 0:
  500. # PyPy may store functions directly on the class
  501. # (more exactly: the container is not a Python object)
  502. dicts = funcs
  503. if len(dicts) == 1:
  504. classes = [c for c in gc.get_referrers(dicts[0])
  505. if hasattr(c, "__bases__")]
  506. if len(classes) == 1:
  507. # ditto for new.classobj()
  508. clsname = classes[0].__name__
  509. # cache the result - assumption is that new.* is
  510. # not called later to disturb this relationship
  511. # _caller_cache could be flushed if functions in
  512. # the new module get called.
  513. self._caller_cache[code] = clsname
  514. if clsname is not None:
  515. funcname = "%s.%s" % (clsname, funcname)
  516. return filename, modulename, funcname
  517. def globaltrace_trackcallers(self, frame, why, arg):
  518. """Handler for call events.
  519. Adds information about who called who to the self._callers dict.
  520. """
  521. if why == 'call':
  522. # XXX Should do a better job of identifying methods
  523. this_func = self.file_module_function_of(frame)
  524. parent_func = self.file_module_function_of(frame.f_back)
  525. self._callers[(parent_func, this_func)] = 1
  526. def globaltrace_countfuncs(self, frame, why, arg):
  527. """Handler for call events.
  528. Adds (filename, modulename, funcname) to the self._calledfuncs dict.
  529. """
  530. if why == 'call':
  531. this_func = self.file_module_function_of(frame)
  532. self._calledfuncs[this_func] = 1
  533. def globaltrace_lt(self, frame, why, arg):
  534. """Handler for call events.
  535. If the code block being entered is to be ignored, returns `None',
  536. else returns self.localtrace.
  537. """
  538. if why == 'call':
  539. code = frame.f_code
  540. filename = frame.f_globals.get('__file__', None)
  541. if filename:
  542. # XXX modname() doesn't work right for packages, so
  543. # the ignore support won't work right for packages
  544. modulename = modname(filename)
  545. if modulename is not None:
  546. ignore_it = self.ignore.names(filename, modulename)
  547. if not ignore_it:
  548. if self.trace:
  549. print (" --- modulename: %s, funcname: %s"
  550. % (modulename, code.co_name))
  551. return self.localtrace
  552. else:
  553. return None
  554. def localtrace_trace_and_count(self, frame, why, arg):
  555. if why == "line":
  556. # record the file name and line number of every trace
  557. filename = frame.f_code.co_filename
  558. lineno = frame.f_lineno
  559. key = filename, lineno
  560. self.counts[key] = self.counts.get(key, 0) + 1
  561. if self.start_time:
  562. print '%.2f' % (time.time() - self.start_time),
  563. bname = os.path.basename(filename)
  564. print "%s(%d): %s" % (bname, lineno,
  565. linecache.getline(filename, lineno)),
  566. return self.localtrace
  567. def localtrace_trace(self, frame, why, arg):
  568. if why == "line":
  569. # record the file name and line number of every trace
  570. filename = frame.f_code.co_filename
  571. lineno = frame.f_lineno
  572. if self.start_time:
  573. print '%.2f' % (time.time() - self.start_time),
  574. bname = os.path.basename(filename)
  575. print "%s(%d): %s" % (bname, lineno,
  576. linecache.getline(filename, lineno)),
  577. return self.localtrace
  578. def localtrace_count(self, frame, why, arg):
  579. if why == "line":
  580. filename = frame.f_code.co_filename
  581. lineno = frame.f_lineno
  582. key = filename, lineno
  583. self.counts[key] = self.counts.get(key, 0) + 1
  584. return self.localtrace
  585. def results(self):
  586. return CoverageResults(self.counts, infile=self.infile,
  587. outfile=self.outfile,
  588. calledfuncs=self._calledfuncs,
  589. callers=self._callers)
  590. def _err_exit(msg):
  591. sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
  592. sys.exit(1)
  593. def main(argv=None):
  594. import getopt
  595. if argv is None:
  596. argv = sys.argv
  597. try:
  598. opts, prog_argv = getopt.getopt(argv[1:], "tcrRf:d:msC:lTg",
  599. ["help", "version", "trace", "count",
  600. "report", "no-report", "summary",
  601. "file=", "missing",
  602. "ignore-module=", "ignore-dir=",
  603. "coverdir=", "listfuncs",
  604. "trackcalls", "timing"])
  605. except getopt.error, msg:
  606. sys.stderr.write("%s: %s\n" % (sys.argv[0], msg))
  607. sys.stderr.write("Try `%s --help' for more information\n"
  608. % sys.argv[0])
  609. sys.exit(1)
  610. trace = 0
  611. count = 0
  612. report = 0
  613. no_report = 0
  614. counts_file = None
  615. missing = 0
  616. ignore_modules = []
  617. ignore_dirs = []
  618. coverdir = None
  619. summary = 0
  620. listfuncs = False
  621. countcallers = False
  622. timing = False
  623. for opt, val in opts:
  624. if opt == "--help":
  625. usage(sys.stdout)
  626. sys.exit(0)
  627. if opt == "--version":
  628. sys.stdout.write("trace 2.0\n")
  629. sys.exit(0)
  630. if opt == "-T" or opt == "--trackcalls":
  631. countcallers = True
  632. continue
  633. if opt == "-l" or opt == "--listfuncs":
  634. listfuncs = True
  635. continue
  636. if opt == "-g" or opt == "--timing":
  637. timing = True
  638. continue
  639. if opt == "-t" or opt == "--trace":
  640. trace = 1
  641. continue
  642. if opt == "-c" or opt == "--count":
  643. count = 1
  644. continue
  645. if opt == "-r" or opt == "--report":
  646. report = 1
  647. continue
  648. if opt == "-R" or opt == "--no-report":
  649. no_report = 1
  650. continue
  651. if opt == "-f" or opt == "--file":
  652. counts_file = val
  653. continue
  654. if opt == "-m" or opt == "--missing":
  655. missing = 1
  656. continue
  657. if opt == "-C" or opt == "--coverdir":
  658. coverdir = val
  659. continue
  660. if opt == "-s" or opt == "--summary":
  661. summary = 1
  662. continue
  663. if opt == "--ignore-module":
  664. for mod in val.split(","):
  665. ignore_modules.append(mod.strip())
  666. continue
  667. if opt == "--ignore-dir":
  668. for s in val.split(os.pathsep):
  669. s = os.path.expandvars(s)
  670. # should I also call expanduser? (after all, could use $HOME)
  671. s = s.replace("$prefix",
  672. os.path.join(sys.prefix, "lib",
  673. "python" + sys.version[:3]))
  674. s = s.replace("$exec_prefix",
  675. os.path.join(sys.exec_prefix, "lib",
  676. "python" + sys.version[:3]))
  677. s = os.path.normpath(s)
  678. ignore_dirs.append(s)
  679. continue
  680. assert 0, "Should never get here"
  681. if listfuncs and (count or trace):
  682. _err_exit("cannot specify both --listfuncs and (--trace or --count)")
  683. if not (count or trace or report or listfuncs or countcallers):
  684. _err_exit("must specify one of --trace, --count, --report, "
  685. "--listfuncs, or --trackcalls")
  686. if report and no_report:
  687. _err_exit("cannot specify both --report and --no-report")
  688. if report and not counts_file:
  689. _err_exit("--report requires a --file")
  690. if no_report and len(prog_argv) == 0:
  691. _err_exit("missing name of file to run")
  692. # everything is ready
  693. if report:
  694. results = CoverageResults(infile=counts_file, outfile=counts_file)
  695. results.write_results(missing, summary=summary, coverdir=coverdir)
  696. else:
  697. sys.argv = prog_argv
  698. progname = prog_argv[0]
  699. sys.path[0] = os.path.split(progname)[0]
  700. t = Trace(count, trace, countfuncs=listfuncs,
  701. countcallers=countcallers, ignoremods=ignore_modules,
  702. ignoredirs=ignore_dirs, infile=counts_file,
  703. outfile=counts_file, timing=timing)
  704. try:
  705. with open(progname) as fp:
  706. code = compile(fp.read(), progname, 'exec')
  707. # try to emulate __main__ namespace as much as possible
  708. globs = {
  709. '__file__': progname,
  710. '__name__': '__main__',
  711. '__package__': None,
  712. '__cached__': None,
  713. }
  714. t.runctx(code, globs, globs)
  715. except IOError, err:
  716. _err_exit("Cannot run file %r because: %s" % (sys.argv[0], err))
  717. except SystemExit:
  718. pass
  719. results = t.results()
  720. if not no_report:
  721. results.write_results(missing, summary=summary, coverdir=coverdir)
  722. if __name__=='__main__':
  723. main()