mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
Update bundled coverage.py to version 4.2.0
This commit is contained in:
396
python/helpers/coveragepy/coverage/plugin.py
Normal file
396
python/helpers/coveragepy/coverage/plugin.py
Normal file
@@ -0,0 +1,396 @@
|
||||
# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
|
||||
# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
|
||||
|
||||
"""Plugin interfaces for coverage.py"""
|
||||
|
||||
from coverage import files
|
||||
from coverage.misc import contract, _needs_to_implement
|
||||
|
||||
|
||||
class CoveragePlugin(object):
|
||||
"""Base class for coverage.py plugins.
|
||||
|
||||
To write a coverage.py plugin, create a module with a subclass of
|
||||
:class:`CoveragePlugin`. You will override methods in your class to
|
||||
participate in various aspects of coverage.py's processing.
|
||||
|
||||
Currently the only plugin type is a file tracer, for implementing
|
||||
measurement support for non-Python files. File tracer plugins implement
|
||||
the :meth:`file_tracer` method to claim files and the :meth:`file_reporter`
|
||||
method to report on those files.
|
||||
|
||||
Any plugin can optionally implement :meth:`sys_info` to provide debugging
|
||||
information about their operation.
|
||||
|
||||
Coverage.py will store its own information on your plugin object, using
|
||||
attributes whose names start with ``_coverage_``. Don't be startled.
|
||||
|
||||
To register your plugin, define a function called `coverage_init` in your
|
||||
module::
|
||||
|
||||
def coverage_init(reg, options):
|
||||
reg.add_file_tracer(MyPlugin())
|
||||
|
||||
You use the `reg` parameter passed to your `coverage_init` function to
|
||||
register your plugin object. It has one method, `add_file_tracer`, which
|
||||
takes a newly created instance of your plugin.
|
||||
|
||||
If your plugin takes options, the `options` parameter is a dictionary of
|
||||
your plugin's options from the coverage.py configuration file. Use them
|
||||
however you want to configure your object before registering it.
|
||||
|
||||
"""
|
||||
|
||||
def file_tracer(self, filename): # pylint: disable=unused-argument
|
||||
"""Get a :class:`FileTracer` object for a file.
|
||||
|
||||
Every Python source file is offered to the plugin to give it a chance
|
||||
to take responsibility for tracing the file. If your plugin can handle
|
||||
the file, then return a :class:`FileTracer` object. Otherwise return
|
||||
None.
|
||||
|
||||
There is no way to register your plugin for particular files. Instead,
|
||||
this method is invoked for all files, and the plugin 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 plugin.
|
||||
|
||||
The file name will be a Python file being executed. There are two
|
||||
broad categories of behavior for a plugin, depending on the kind of
|
||||
files your plugin supports:
|
||||
|
||||
* Static file names: each of your original source files has been
|
||||
converted into a distinct Python file. Your plugin 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 plugin implements
|
||||
:meth:`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 :class:`FileTracer` object to use to trace `filename`, or
|
||||
None if this plugin cannot trace this file.
|
||||
|
||||
"""
|
||||
return None
|
||||
|
||||
def file_reporter(self, filename): # pylint: disable=unused-argument
|
||||
"""Get the :class:`FileReporter` class to use for a file.
|
||||
|
||||
This will only be invoked if `filename` returns non-None from
|
||||
:meth:`file_tracer`. It's an error to return None from this method.
|
||||
|
||||
Returns a :class:`FileReporter` object to use to report on `filename`.
|
||||
|
||||
"""
|
||||
_needs_to_implement(self, "file_reporter")
|
||||
|
||||
def sys_info(self):
|
||||
"""Get a list of information useful for debugging.
|
||||
|
||||
This method will be invoked for ``--debug=sys``. Your
|
||||
plugin can return any information it wants to be displayed.
|
||||
|
||||
Returns a list of pairs: `[(name, value), ...]`.
|
||||
|
||||
"""
|
||||
return []
|
||||
|
||||
|
||||
class FileTracer(object):
|
||||
"""Support needed for files during the execution phase.
|
||||
|
||||
You may construct this object from :meth:`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
|
||||
:meth:`CoveragePlugin.file_tracer` method.
|
||||
|
||||
See :ref:`howitworks` for details of the different coverage.py phases.
|
||||
|
||||
"""
|
||||
|
||||
def source_filename(self):
|
||||
"""The source file name for this file.
|
||||
|
||||
This may be any file name you like. A key responsibility of a plugin
|
||||
is to own the mapping from Python execution back to whatever source
|
||||
file name was originally the source of the code.
|
||||
|
||||
See :meth:`CoveragePlugin.file_tracer` for details about static and
|
||||
dynamic file names.
|
||||
|
||||
Returns the file name to credit with this execution.
|
||||
|
||||
"""
|
||||
_needs_to_implement(self, "source_filename")
|
||||
|
||||
def has_dynamic_source_filename(self):
|
||||
"""Does this FileTracer have dynamic source file names?
|
||||
|
||||
FileTracers can provide dynamically determined file names by
|
||||
implementing :meth:`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
|
||||
:meth:`dynamic_source_filename`.
|
||||
|
||||
See :meth:`CoveragePlugin.file_tracer` for details about static and
|
||||
dynamic file names.
|
||||
|
||||
Returns True if :meth:`dynamic_source_filename` should be called to get
|
||||
dynamic source file names.
|
||||
|
||||
"""
|
||||
return False
|
||||
|
||||
def dynamic_source_filename(self, filename, frame): # pylint: disable=unused-argument
|
||||
"""Get a dynamically computed source file name.
|
||||
|
||||
Some plugins need to compute the source file name dynamically for each
|
||||
frame.
|
||||
|
||||
This function will not be invoked if
|
||||
:meth:`has_dynamic_source_filename` returns False.
|
||||
|
||||
Returns the source file name for this frame, or None if this frame
|
||||
shouldn't be measured.
|
||||
|
||||
"""
|
||||
return None
|
||||
|
||||
def line_number_range(self, 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.
|
||||
|
||||
"""
|
||||
lineno = frame.f_lineno
|
||||
return lineno, lineno
|
||||
|
||||
|
||||
class FileReporter(object):
|
||||
"""Support needed for files during the analysis and reporting phases.
|
||||
|
||||
See :ref:`howitworks` for details of the different coverage.py phases.
|
||||
|
||||
`FileReporter` objects should only be created in the
|
||||
:meth:`CoveragePlugin.file_reporter` method.
|
||||
|
||||
There are many methods here, but only :meth:`lines` is required, to provide
|
||||
the set of executable lines in the file.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, filename):
|
||||
"""Simple initialization of a `FileReporter`.
|
||||
|
||||
The `filename` argument is the path to the file being reported. This
|
||||
will be available as the `.filename` attribute on the object. Other
|
||||
method implementations on this base class rely on this attribute.
|
||||
|
||||
"""
|
||||
self.filename = filename
|
||||
|
||||
def __repr__(self):
|
||||
return "<{0.__class__.__name__} filename={0.filename!r}>".format(self)
|
||||
|
||||
def relative_filename(self):
|
||||
"""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 files.relative_filename(self.filename)
|
||||
|
||||
@contract(returns='unicode')
|
||||
def source(self):
|
||||
"""Get the source for the file.
|
||||
|
||||
Returns a Unicode string.
|
||||
|
||||
The base implementation simply reads the `self.filename` file and
|
||||
decodes it as UTF8. Override this method if your file isn't readable
|
||||
as a text file, or if you need other encoding support.
|
||||
|
||||
"""
|
||||
with open(self.filename, "rb") as f:
|
||||
return f.read().decode("utf8")
|
||||
|
||||
def lines(self):
|
||||
"""Get the executable lines in this file.
|
||||
|
||||
Your plugin 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.
|
||||
|
||||
"""
|
||||
_needs_to_implement(self, "lines")
|
||||
|
||||
def excluded_lines(self):
|
||||
"""Get the excluded executable lines in this file.
|
||||
|
||||
Your plugin 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 set()
|
||||
|
||||
def translate_lines(self, 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 plugin 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.
|
||||
|
||||
"""
|
||||
return set(lines)
|
||||
|
||||
def arcs(self):
|
||||
"""Get the executable arcs in this file.
|
||||
|
||||
To support branch coverage, your plugin 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 set()
|
||||
|
||||
def no_branch_lines(self):
|
||||
"""Get the lines excused from branch coverage in this file.
|
||||
|
||||
Your plugin 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 set()
|
||||
|
||||
def translate_arcs(self, arcs):
|
||||
"""Translate recorded arcs into reported arcs.
|
||||
|
||||
Similar to :meth:`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.
|
||||
|
||||
"""
|
||||
return arcs
|
||||
|
||||
def exit_counts(self):
|
||||
"""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 plugin...
|
||||
|
||||
"""
|
||||
return {}
|
||||
|
||||
def missing_arc_description(self, start, end, executed_arcs=None): # pylint: disable=unused-argument
|
||||
"""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}".
|
||||
|
||||
"""
|
||||
return "Line {start} didn't jump to line {end}".format(start=start, end=end)
|
||||
|
||||
def source_token_lines(self):
|
||||
"""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
|
||||
* ``'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'``.
|
||||
|
||||
"""
|
||||
for line in self.source().splitlines():
|
||||
yield [('txt', line)]
|
||||
|
||||
# Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all
|
||||
# of them defined.
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, FileReporter) and self.filename == other.filename
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.filename < other.filename
|
||||
|
||||
def __le__(self, other):
|
||||
return self.filename <= other.filename
|
||||
|
||||
def __gt__(self, other):
|
||||
return self.filename > other.filename
|
||||
|
||||
def __ge__(self, other):
|
||||
return self.filename >= other.filename
|
||||
Reference in New Issue
Block a user