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