--- a/DebugClients/Python/coverage/plugin.py Sat Apr 07 13:17:06 2018 +0200 +++ b/DebugClients/Python/coverage/plugin.py Sat Apr 07 13:35:10 2018 +0200 @@ -1,70 +1,118 @@ # 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""" +""" +.. versionadded:: 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. + +To write a coverage.py plug-in, create a module with a subclass of +:class:`~coverage.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 :meth:`~coverage.CoveragePlugin.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 :meth:`~coverage.CoveragePlugin.file_tracer` method to claim +files and the :meth:`~coverage.CoveragePlugin.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 +=========== + +.. versionadded:: 4.5 + +Configurers modify the configuration of coverage.py during start-up. +Configurers implement the :meth:`~coverage.CoveragePlugin.configure` method to +change the configuration. + +In your ``coverage_init`` function, use the ``add_configurer`` method to +register your configurer. + +""" 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. - - """ + """Base class for coverage.py plug-ins.""" 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. + Plug-in type: file tracer. - 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. + 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, then return a :class:`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, 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 plugin, depending on the kind of - files your plugin supports: + 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 plugin is invoked with + 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 plugin implements + Python file. In this case, your plug-in implements :meth:`FileTracer.dynamic_source_filename` to provide the actual source file for each execution frame. @@ -73,7 +121,7 @@ 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. + None if this plug-in cannot trace this file. """ return None @@ -81,6 +129,8 @@ def file_reporter(self, filename): # pylint: disable=unused-argument """Get the :class:`FileReporter` class to use for a file. + Plug-in type: file tracer. + This will only be invoked if `filename` returns non-None from :meth:`file_tracer`. It's an error to return None from this method. @@ -89,11 +139,42 @@ """ _needs_to_implement(self, "file_reporter") + def find_executable_files(self, src_dir): # pylint: disable=unused-argument + """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. + + """ + return [] + + def configure(self, 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 :meth:`~coverage.Coverage.get_option` and + :meth:`~coverage.Coverage.set_option` methods. Do not call any other + methods on the `config` object. + + """ + pass + def sys_info(self): """Get a list of information useful for debugging. + Plug-in type: any. + This method will be invoked for ``--debug=sys``. Your - plugin can return any information it wants to be displayed. + plug-in can return any information it wants to be displayed. Returns a list of pairs: `[(name, value), ...]`. @@ -104,6 +185,9 @@ class FileTracer(object): """Support needed for files during the execution phase. + File tracer plug-ins implement subclasses of FileTracer to return from + their :meth:`~CoveragePlugin.file_tracer` method. + 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`. @@ -118,7 +202,7 @@ 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 + 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. @@ -151,7 +235,7 @@ 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 + Some plug-ins need to compute the source file name dynamically for each frame. This function will not be invoked if @@ -184,14 +268,14 @@ 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. + File tracer plug-ins implement a subclass of `FileReporter`, and return + instances from their :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. + See :ref:`howitworks` for details of the different coverage.py phases. + """ def __init__(self, filename): @@ -235,7 +319,7 @@ def lines(self): """Get the executable lines in this file. - Your plugin must determine which lines in the file were possibly + 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. @@ -246,7 +330,7 @@ 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 + 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. @@ -264,8 +348,8 @@ 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. + Your plug-in can optionally define this method to perform these kinds + of adjustment. `lines` is a sequence of integers, the recorded line numbers. @@ -279,7 +363,7 @@ def arcs(self): """Get the executable arcs in this file. - To support branch coverage, your plugin needs to be able to indicate + 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. @@ -293,7 +377,7 @@ 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 + 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. @@ -324,7 +408,7 @@ 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... + if you attempt to implement this method in your plug-in... """ return {} @@ -394,3 +478,5 @@ def __ge__(self, other): return self.filename >= other.filename + + __hash__ = None # This object doesn't need to be hashed.