17757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch"""Imposter encodings module that installs a coverage-style tracer.
27757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
37757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen MurdochThis is NOT the encodings module; it is an imposter that sets up tracing
47757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochinstrumentation and then replaces itself with the real encodings module.
57757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
67757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen MurdochIf the directory that holds this file is placed first in the PYTHONPATH when
77757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochusing "coverage" to run Python's tests, then this file will become the very
87757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochfirst module imported by the internals of Python 3.  It installs a
97757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochcoverage-compatible trace function that can watch Standard Library modules
107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochexecute from the very earliest stages of Python's own boot process.  This fixes
117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdocha problem with coverage - that it starts too late to trace the coverage of many
127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochof the most fundamental modules in the Standard Library.
137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
147757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch"""
157757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
167757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport sys
177757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
187757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochclass FullCoverageTracer(object):
197757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def __init__(self):
207757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        # `traces` is a list of trace events.  Frames are tricky: the same
217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        # frame object is used for a whole scope, with new line numbers
227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        # written into it.  So in one scope, all the frame objects are the
237757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        # same object, and will eventually all will point to the last line
247757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        # executed.  So we keep the line numbers alongside the frames.
257757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        # The list looks like:
267757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        #
277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        #   traces = [
287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        #       ((frame, event, arg), lineno), ...
297757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        #       ]
307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        #
317757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.traces = []
327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
337757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch    def fullcoverage_trace(self, *args):
347757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        frame, event, arg = args
357757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        self.traces.append((args, frame.f_lineno))
367757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch        return self.fullcoverage_trace
377757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
387757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochsys.settrace(FullCoverageTracer().fullcoverage_trace)
397757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
407757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# Finally, remove our own directory from sys.path; remove ourselves from
417757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# sys.modules; and re-import "encodings", which will be the real package
427757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# this time.  Note that the delete from sys.modules dictionary has to
437757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# happen last, since all of the symbols in this module will become None
447757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch# at that exact moment, including "sys".
457757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch
467757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochparentdirs = [ d for d in sys.path if __file__.startswith(d) ]
477757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochparentdirs.sort(key=len)
487757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochsys.path.remove(parentdirs[-1])
497757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochdel sys.modules['encodings']
507757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport encodings
51