Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tighten PytestRun coverage plugin. #5542

Merged
merged 1 commit into from
Mar 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/python/pants/backend/python/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
__import__('pkg_resources').declare_namespace(__name__)
1 change: 0 additions & 1 deletion src/python/pants/backend/python/tasks/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
__import__('pkg_resources').declare_namespace(__name__)
21 changes: 10 additions & 11 deletions src/python/pants/backend/python/tasks/coverage/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,18 @@
from coverage.python import PythonFileReporter


class MyFileTracer(FileTracer):
class SimpleFileTracer(FileTracer):
def __init__(self, filename):
super(MyFileTracer, self).__init__()
super(SimpleFileTracer, self).__init__()
self._filename = filename

def source_filename(self):
return self._filename


class MyFileReporter(PythonFileReporter):
"""A python file reporter that knows how to map Pants PEX chroots back to repo source code."""

class SimpleFileReporter(PythonFileReporter):
def __init__(self, morf, relpath):
super(MyFileReporter, self).__init__(morf, coverage=None)
super(SimpleFileReporter, self).__init__(morf, coverage=None)
self._relpath = relpath

def relative_filename(self):
Expand All @@ -50,11 +48,11 @@ def no_branch_lines(self):
join_regex(DEFAULT_PARTIAL_ALWAYS[:]))


class MyPlugin(CoveragePlugin):
class ChrootRemappingPlugin(CoveragePlugin):
"""A plugin that knows how to map Pants PEX chroots back to repo source code when reporting."""

def __init__(self, buildroot, src_chroot_path, src_to_target_base):
super(MyPlugin, self).__init__()
super(ChrootRemappingPlugin, self).__init__()
self._buildroot = buildroot
self._src_chroot_path = src_chroot_path
self._src_to_target_base = src_to_target_base
Expand All @@ -68,14 +66,15 @@ def find_executable_files(self, top):
yield os.path.join(root, f)

def file_tracer(self, filename):
# Don't trace third party libraries or the standard lib, just user code in the src chroot.
if filename.startswith(self._src_chroot_path):
src = os.path.relpath(filename, self._src_chroot_path)
if src in self._src_to_target_base:
return MyFileTracer(filename)
return SimpleFileTracer(filename)

def file_reporter(self, filename):
mapped_relpath = self._map_relpath(filename)
return MyFileReporter(filename, mapped_relpath or filename)
return SimpleFileReporter(filename, mapped_relpath)

def _map_relpath(self, filename):
src = os.path.relpath(filename, self._src_chroot_path)
Expand All @@ -92,4 +91,4 @@ def coverage_init(reg, options):
buildroot = options['buildroot']
src_chroot_path = options['src_chroot_path']
src_to_target_base = json.loads(options['src_to_target_base'])
reg.add_file_tracer(MyPlugin(buildroot, src_chroot_path, src_to_target_base))
reg.add_file_tracer(ChrootRemappingPlugin(buildroot, src_chroot_path, src_to_target_base))
17 changes: 12 additions & 5 deletions src/python/pants/backend/python/tasks/pytest_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class PytestPrep(PythonExecutionTaskBase):
class PytestBinary(object):
"""A `py.test` PEX binary with an embedded default (empty) `pytest.ini` config file."""

_COVERAGE_PLUGIN_MODULE_NAME = '__{}__'.format(__name__.replace('.', '_'))

def __init__(self, pex):
self._pex = pex

Expand All @@ -39,9 +41,17 @@ def config_path(self):
"""
return os.path.join(self._pex.path(), 'pytest.ini')

@classmethod
def coverage_plugin_module(cls):
"""Return the name of the coverage plugin module embedded in this py.test binary.

:rtype: str
"""
return cls._COVERAGE_PLUGIN_MODULE_NAME

@classmethod
def implementation_version(cls):
return super(PytestPrep, cls).implementation_version() + [('PytestPrep', 1)]
return super(PytestPrep, cls).implementation_version() + [('PytestPrep', 2)]

@classmethod
def product_types(cls):
Expand All @@ -56,10 +66,7 @@ def extra_requirements(self):

def extra_files(self):
yield self.ExtraFile.empty('pytest.ini')

enclosing_dir = os.path.dirname(__name__.replace('.', os.sep))
plugin_path = os.path.join(enclosing_dir, 'coverage/plugin.py')
yield self.ExtraFile(path=plugin_path,
yield self.ExtraFile(path='{}.py'.format(self.PytestBinary.coverage_plugin_module()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to refer to a different file than it used to... but the tests are passing, so I'm going to assume that that is intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests ride above the layer of the task, they just create it and execute it and so plugin details (for both py.test and coverage) are opaque - though important - to the test. The key paired change to this one is in PytestRun where PytestPrep.PytestBinary.coverage_plugin_module() is used to setup the coverage.rc ini file.

content=pkg_resources.resource_string(__name__, 'coverage/plugin.py'))

def execute(self):
Expand Down
2 changes: 1 addition & 1 deletion src/python/pants/backend/python/tasks/pytest_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def _ensure_section(cp, section):
def _add_plugin_config(cls, cp, src_chroot_path, src_to_target_base):
# We use a coverage plugin to map PEX chroot source paths back to their original repo paths for
# report output.
plugin_module = 'pants.backend.python.tasks.coverage.plugin'
plugin_module = PytestPrep.PytestBinary.coverage_plugin_module()
cls._ensure_section(cp, 'run')
cp.set('run', 'plugins', plugin_module)

Expand Down