Plug-in classes

New in version 4.0.

Plug-in interfaces for coverage.py.

Coverage.py supports a few different kinds of plug-ins that change its behavior:

  • File tracers implement tracing of non-Python file types.

  • Configurers add custom configuration, using Python code to change the configuration.

  • Dynamic context switchers decide when the dynamic context has changed, for example, to record what test function produced the coverage.

To write a coverage.py plug-in, create a module with a subclass of CoveragePlugin. You will override methods in your class to participate in various aspects of coverage.py’s processing. Different types of plug-ins have to override different methods.

Any plug-in can optionally implement sys_info() to provide debugging information about their operation.

Your module must also contain a coverage_init function that registers an instance of your plug-in class:

import coverage

class MyPlugin(coverage.CoveragePlugin):
    ...

def coverage_init(reg, options):
    reg.add_file_tracer(MyPlugin())

You use the reg parameter passed to your coverage_init function to register your plug-in object. The registration method you call depends on what kind of plug-in it is.

If your plug-in takes options, the options parameter is a dictionary of your plug-in’s options from the coverage.py configuration file. Use them however you want to configure your object before registering it.

Coverage.py will store its own information on your plug-in object, using attributes whose names start with _coverage_. Don’t be startled.

Warning

Plug-ins are imported by coverage.py before it begins measuring code. If you write a plugin in your own project, it might import your product code before coverage.py can start measuring. This can result in your own code being reported as missing.

One solution is to put your plugins in your project tree, but not in your importable Python package.

File Tracers

File tracers implement measurement support for non-Python files. File tracers implement the file_tracer() method to claim files and the file_reporter() method to report on those files.

In your coverage_init function, use the add_file_tracer method to register your file tracer.

Configurers

New in version 4.5.

Configurers modify the configuration of coverage.py during start-up. Configurers implement the configure() method to change the configuration.

In your coverage_init function, use the add_configurer method to register your configurer.

Dynamic Context Switchers

New in version 5.0.

Dynamic context switcher plugins implement the dynamic_context() method to dynamically compute the context label for each measured frame.

Computed context labels are useful when you want to group measured data without modifying the source code.

For example, you could write a plugin that checks frame.f_code to inspect the currently executed method, and set the context label to a fully qualified method name if it’s an instance method of unittest.TestCase and the method name starts with ‘test’. Such a plugin would provide basic coverage grouping by test and could be used with test runners that have no built-in coveragepy support.

In your coverage_init function, use the add_dynamic_context method to register your dynamic context switcher.

The CoveragePlugin class

class coverage.CoveragePlugin

Base class for coverage.py plug-ins.

file_tracer(filename)

Get a FileTracer object for a file.

Plug-in type: file tracer.

Every Python source file is offered to your plug-in to give it a chance to take responsibility for tracing the file. If your plug-in can handle the file, it should return a FileTracer object. Otherwise return None.

There is no way to register your plug-in for particular files. Instead, this method is invoked for all files as they are executed, and the plug-in decides whether it can trace the file or not. Be prepared for filename to refer to all kinds of files that have nothing to do with your plug-in.

The file name will be a Python file being executed. There are two broad categories of behavior for a plug-in, depending on the kind of files your plug-in supports:

  • Static file names: each of your original source files has been converted into a distinct Python file. Your plug-in is invoked with the Python file name, and it maps it back to its original source file.

  • Dynamic file names: all of your source files are executed by the same Python file. In this case, your plug-in implements FileTracer.dynamic_source_filename() to provide the actual source file for each execution frame.

filename is a string, the path to the file being considered. This is the absolute real path to the file. If you are comparing to other paths, be sure to take this into account.

Returns a FileTracer object to use to trace filename, or None if this plug-in cannot trace this file.

Parameters:

filename (str) –

Return type:

FileTracer | None

file_reporter(filename)

Get the FileReporter class to use for a file.

Plug-in type: file tracer.

This will only be invoked if filename returns non-None from file_tracer(). It’s an error to return None from this method.

Returns a FileReporter object to use to report on filename, or the string “python” to have coverage.py treat the file as Python.

Parameters:

filename (str) –

Return type:

FileReporter | str

dynamic_context(frame)

Get the dynamically computed context label for frame.

Plug-in type: dynamic context.

This method is invoked for each frame when outside of a dynamic context, to see if a new dynamic context should be started. If it returns a string, a new context label is set for this and deeper frames. The dynamic context ends when this frame returns.

Returns a string to start a new dynamic context, or None if no new context should be started.

Parameters:

frame (frame) –

Return type:

str | None

find_executable_files(src_dir)

Yield all of the executable files in src_dir, recursively.

Plug-in type: file tracer.

Executability is a plug-in-specific property, but generally means files which would have been considered for coverage analysis, had they been included automatically.

Returns or yields a sequence of strings, the paths to files that could have been executed, including files that had been executed.

Parameters:

src_dir (str) –

Return type:

Iterable[str]

configure(config)

Modify the configuration of coverage.py.

Plug-in type: configurer.

This method is called during coverage.py start-up, to give your plug-in a chance to change the configuration. The config parameter is an object with get_option() and set_option() methods. Do not call any other methods on the config object.

Parameters:

config (TConfigurable) –

Return type:

None

sys_info()

Get a list of information useful for debugging.

Plug-in type: any.

This method will be invoked for --debug=sys. Your plug-in can return any information it wants to be displayed.

Returns a list of pairs: [(name, value), …].

Return type:

Iterable[tuple[str, Any]]

The FileTracer class

class coverage.FileTracer

Support needed for files during the execution phase.

File tracer plug-ins implement subclasses of FileTracer to return from their file_tracer() method.

You may construct this object from CoveragePlugin.file_tracer() any way you like. A natural choice would be to pass the file name given to file_tracer.

FileTracer objects should only be created in the CoveragePlugin.file_tracer() method.

See How coverage.py works for details of the different coverage.py phases.

source_filename()

The source file name for this file.

This may be any file name you like. A key responsibility of a plug-in is to own the mapping from Python execution back to whatever source file name was originally the source of the code.

See CoveragePlugin.file_tracer() for details about static and dynamic file names.

Returns the file name to credit with this execution.

Return type:

str

has_dynamic_source_filename()

Does this FileTracer have dynamic source file names?

FileTracers can provide dynamically determined file names by implementing dynamic_source_filename(). Invoking that function is expensive. To determine whether to invoke it, coverage.py uses the result of this function to know if it needs to bother invoking dynamic_source_filename().

See CoveragePlugin.file_tracer() for details about static and dynamic file names.

Returns True if dynamic_source_filename() should be called to get dynamic source file names.

Return type:

bool

dynamic_source_filename(filename, frame)

Get a dynamically computed source file name.

Some plug-ins need to compute the source file name dynamically for each frame.

This function will not be invoked if has_dynamic_source_filename() returns False.

Returns the source file name for this frame, or None if this frame shouldn’t be measured.

Parameters:
  • filename (str) –

  • frame (frame) –

Return type:

str | None

line_number_range(frame)

Get the range of source line numbers for a given a call frame.

The call frame is examined, and the source line number in the original file is returned. The return value is a pair of numbers, the starting line number and the ending line number, both inclusive. For example, returning (5, 7) means that lines 5, 6, and 7 should be considered executed.

This function might decide that the frame doesn’t indicate any lines from the source file were executed. Return (-1, -1) in this case to tell coverage.py that no lines should be recorded for this frame.

Parameters:

frame (frame) –

Return type:

tuple[int, int]

The FileReporter class

class coverage.FileReporter(filename)

Support needed for files during the analysis and reporting phases.

File tracer plug-ins implement a subclass of FileReporter, and return instances from their CoveragePlugin.file_reporter() method.

There are many methods here, but only lines() is required, to provide the set of executable lines in the file.

See How coverage.py works for details of the different coverage.py phases.

Parameters:

filename (str) –

relative_filename()

Get the relative file name for this file.

This file path will be displayed in reports. The default implementation will supply the actual project-relative file path. You only need to supply this method if you have an unusual syntax for file paths.

Return type:

str

source()

Get the source for the file.

Returns a Unicode string.

The base implementation simply reads the self.filename file and decodes it as UTF-8. Override this method if your file isn’t readable as a text file, or if you need other encoding support.

Return type:

str

lines()

Get the executable lines in this file.

Your plug-in must determine which lines in the file were possibly executable. This method returns a set of those line numbers.

Returns a set of line numbers.

Return type:

set[int]

excluded_lines()

Get the excluded executable lines in this file.

Your plug-in can use any method it likes to allow the user to exclude executable lines from consideration.

Returns a set of line numbers.

The base implementation returns the empty set.

Return type:

set[int]

translate_lines(lines)

Translate recorded lines into reported lines.

Some file formats will want to report lines slightly differently than they are recorded. For example, Python records the last line of a multi-line statement, but reports are nicer if they mention the first line.

Your plug-in can optionally define this method to perform these kinds of adjustment.

lines is a sequence of integers, the recorded line numbers.

Returns a set of integers, the adjusted line numbers.

The base implementation returns the numbers unchanged.

Parameters:

lines (Iterable[int]) –

Return type:

set[int]

arcs()

Get the executable arcs in this file.

To support branch coverage, your plug-in needs to be able to indicate possible execution paths, as a set of line number pairs. Each pair is a (prev, next) pair indicating that execution can transition from the prev line number to the next line number.

Returns a set of pairs of line numbers. The default implementation returns an empty set.

Return type:

set[Tuple[int, int]]

no_branch_lines()

Get the lines excused from branch coverage in this file.

Your plug-in can use any method it likes to allow the user to exclude lines from consideration of branch coverage.

Returns a set of line numbers.

The base implementation returns the empty set.

Return type:

set[int]

translate_arcs(arcs)

Translate recorded arcs into reported arcs.

Similar to translate_lines(), but for arcs. arcs is a set of line number pairs.

Returns a set of line number pairs.

The default implementation returns arcs unchanged.

Parameters:

arcs (Iterable[Tuple[int, int]]) –

Return type:

set[Tuple[int, int]]

exit_counts()

Get a count of exits from that each line.

To determine which lines are branches, coverage.py looks for lines that have more than one exit. This function creates a dict mapping each executable line number to a count of how many exits it has.

To be honest, this feels wrong, and should be refactored. Let me know if you attempt to implement this method in your plug-in…

Return type:

dict[int, int]

missing_arc_description(start, end, executed_arcs=None)

Provide an English sentence describing a missing arc.

The start and end arguments are the line numbers of the missing arc. Negative numbers indicate entering or exiting code objects.

The executed_arcs argument is a set of line number pairs, the arcs that were executed in this file.

By default, this simply returns the string “Line {start} didn’t jump to {end}”.

Parameters:
Return type:

str

source_token_lines()

Generate a series of tokenized lines, one for each line in source.

These tokens are used for syntax-colored reports.

Each line is a list of pairs, each pair is a token:

[("key", "def"), ("ws", " "), ("nam", "hello"), ("op", "("), ... ]

Each pair has a token class, and the token text. The token classes are:

  • "com": a comment

  • "key": a keyword

  • "nam": a name, or identifier

  • "num": a number

  • "op": an operator

  • "str": a string literal

  • "ws": some white space

  • "txt": some other kind of text

If you concatenate all the token texts, and then join them with newlines, you should have your original source back.

The default implementation simply returns each line tagged as "txt".

Return type:

Iterable[List[Tuple[str, str]]]