OLD | NEW |
(Empty) | |
| 1 """Imposter encodings module that installs a coverage-style tracer. |
| 2 |
| 3 This is NOT the encodings module; it is an imposter that sets up tracing |
| 4 instrumentation and then replaces itself with the real encodings module. |
| 5 |
| 6 If the directory that holds this file is placed first in the PYTHONPATH when |
| 7 using "coverage" to run Python's tests, then this file will become the very |
| 8 first module imported by the internals of Python 3. It installs a |
| 9 coverage-compatible trace function that can watch Standard Library modules |
| 10 execute from the very earliest stages of Python's own boot process. This fixes |
| 11 a problem with coverage - that it starts too late to trace the coverage of many |
| 12 of the most fundamental modules in the Standard Library. |
| 13 |
| 14 """ |
| 15 |
| 16 import sys |
| 17 |
| 18 class FullCoverageTracer(object): |
| 19 def __init__(self): |
| 20 # `traces` is a list of trace events. Frames are tricky: the same |
| 21 # frame object is used for a whole scope, with new line numbers |
| 22 # written into it. So in one scope, all the frame objects are the |
| 23 # same object, and will eventually all will point to the last line |
| 24 # executed. So we keep the line numbers alongside the frames. |
| 25 # The list looks like: |
| 26 # |
| 27 # traces = [ |
| 28 # ((frame, event, arg), lineno), ... |
| 29 # ] |
| 30 # |
| 31 self.traces = [] |
| 32 |
| 33 def fullcoverage_trace(self, *args): |
| 34 frame, event, arg = args |
| 35 self.traces.append((args, frame.f_lineno)) |
| 36 return self.fullcoverage_trace |
| 37 |
| 38 sys.settrace(FullCoverageTracer().fullcoverage_trace) |
| 39 |
| 40 # In coverage/files.py is actual_filename(), which uses glob.glob. I don't |
| 41 # understand why, but that use of glob borks everything if fullcoverage is in |
| 42 # effect. So here we make an ugly hail-mary pass to switch off glob.glob over |
| 43 # there. This means when using fullcoverage, Windows path names will not be |
| 44 # their actual case. |
| 45 |
| 46 #sys.fullcoverage = True |
| 47 |
| 48 # Finally, remove our own directory from sys.path; remove ourselves from |
| 49 # sys.modules; and re-import "encodings", which will be the real package |
| 50 # this time. Note that the delete from sys.modules dictionary has to |
| 51 # happen last, since all of the symbols in this module will become None |
| 52 # at that exact moment, including "sys". |
| 53 |
| 54 parentdir = max(filter(__file__.startswith, sys.path), key=len) |
| 55 sys.path.remove(parentdir) |
| 56 del sys.modules['encodings'] |
| 57 import encodings |
OLD | NEW |