|
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 |