Skip to content

Instantly share code, notes, and snippets.

@fschulze
Last active July 15, 2018 06:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fschulze/fce8a15ca8cc615443fc70bb5f16595c to your computer and use it in GitHub Desktop.
Save fschulze/fce8a15ca8cc615443fc70bb5f16595c to your computer and use it in GitHub Desktop.
coverage.py issue #678
[run]
plugins =
chameleon_coverage_plugin
/.coverage
/.installed.cfg
/bin/
/develop-eggs/
/eggs/
/include/
/lib/
/pip-selfcheck.json
  • Install zc.buildout with pip
  • Run prepare.sh (Needed because gist doesn't allow directories)
  • Run buildout in the directory where the buildout.cfg is located
  • Try running coverage run bin/buildout
Traceback (most recent call last):
File "/Users/fschulze/temp/coverage/bin/coverage", line 12, in <module>

sys.exit(coverage.cmdline.main())

File "/Users/fschulze/temp/coverage/eggs/coverage-4.5.1-py2.7-macosx-10.13-x86_64.egg/coverage/cmdline.py", line 753, in main

status = CoverageScript().command_line(argv)

File "/Users/fschulze/temp/coverage/eggs/coverage-4.5.1-py2.7-macosx-10.13-x86_64.egg/coverage/cmdline.py", line 488, in command_line

return self.do_run(options, args)

File "/Users/fschulze/temp/coverage/eggs/coverage-4.5.1-py2.7-macosx-10.13-x86_64.egg/coverage/cmdline.py", line 617, in do_run

self.coverage.erase()

File "/Users/fschulze/temp/coverage/eggs/coverage-4.5.1-py2.7-macosx-10.13-x86_64.egg/coverage/control.py", line 726, in erase

self._init()

File "/Users/fschulze/temp/coverage/eggs/coverage-4.5.1-py2.7-macosx-10.13-x86_64.egg/coverage/control.py", line 226, in _init

self.plugins = Plugins.load_plugins(self.config.plugins, self.config, self.debug)

File "/Users/fschulze/temp/coverage/eggs/coverage-4.5.1-py2.7-macosx-10.13-x86_64.egg/coverage/plugin_support.py", line 40, in load_plugins

__import__(module)

ImportError: No module named chameleon_coverage_plugin

[buildout]
parts =
coverage
develop =
src/chameleon_coverage_plugin
[coverage]
recipe = zc.recipe.egg
eggs =
chameleon_coverage_plugin
coverage
from __future__ import print_function
from coverage.plugin import CoveragePlugin, FileReporter, FileTracer
import inspect
import os
import re
class ChameleonTemplatePlugin(CoveragePlugin, FileTracer):
def __init__(self):
self.source_filename_map = {}
self.filename_line_map = {}
def file_reporter(self, filename):
return ChameleonFileReporter(filename)
def file_tracer(self, filename):
directory = os.environ.get('CHAMELEON_CACHE', None) or os.getcwd()
if os.path.dirname(filename) == directory:
return self
return None
def find_executable_files(self, src_dir):
for (dirpath, dirnames, filenames) in os.walk(src_dir):
for filename in filenames:
# We're only interested in files that look like reasonable HTML
# files: Must end with .pt, and must not have certain funny
# characters that probably mean they are editor junk.
if re.match(r"^[^.#~!$@%^&*()+=,]+\.pt$", filename):
yield os.path.join(dirpath, filename)
def has_dynamic_source_filename(self):
return True
def dynamic_source_filename(self, filename, frame):
result = None
# result = self.source_filename_map.get(frame.f_code.co_filename)
if result is None:
if frame.f_code.co_name.startswith('render'):
if 'econtext' in frame.f_locals:
econtext = frame.f_locals['econtext']
if 1:
dump_frame(frame, label="dynamic_source_filename")
if 'template' in econtext:
template_fn = econtext['template'].filename
basename = os.path.basename(frame.f_code.co_filename)
template_basename = os.path.basename(template_fn)
template_basename, ext = os.path.splitext(template_basename)
print(basename, template_basename)
if result is not None:
self.source_filename_map[frame.f_code.co_filename] = result
print(self.source_filename_map)
return result
def get_line_map(self, frame):
filename = inspect.getfile(frame)
name = frame.f_code.co_name
key = (filename, name)
if key in self.filename_line_map:
return self.filename_line_map[key]
matcher = re.compile(r'^.*#.*\((\d+):\d+\)')
print()
print(key)
line_map = {}
(source, start_line) = inspect.getsourcelines(frame)
if name == 'render':
end_line = len(source) + start_line
template_lines = (1, 1)
template = self.source_filename_map.get(filename)
if template is not None:
with open(template) as f:
template_lines = (1, len(f.readlines()) + 1)
print(template, template_lines)
for i in range(start_line, end_line + 1):
line_map[i] = template_lines
lineno = None
for index, line in enumerate(source, start=start_line):
m = matcher.match(line)
if m is None:
continue
lineno = int(m.group(1))
for i in range(start_line, index + 1):
line_map[i] = (lineno, lineno)
start_line = index + 1
if lineno is not None:
for i in range(start_line, index + 1):
line_map[i] = (lineno, lineno)
# print(line_map)
self.filename_line_map[key] = line_map
return line_map
def line_number_range(self, frame):
if 0:
dump_frame(frame, label="line_number_range")
line_map = self.get_line_map(frame)
result = line_map.get(frame.f_lineno, (-1, -1))
if result == (-1, -1):
print(frame.f_code.co_filename, frame.f_code.co_name, frame.f_lineno)
return result
def sys_info(self):
return [
("environment", sorted(
("%s = %s" % (k, v))
for k, v in os.environ.items()
if "CHAMELEON_" in k
)),
]
class ChameleonFileReporter(FileReporter):
def lines(self):
# print(self.filename)
lines = self.source().splitlines()
source_lines = set(range(len(lines)))
return source_lines
def coverage_init(reg, options):
reg.add_file_tracer(ChameleonTemplatePlugin())
def dump_frame(frame, label=""):
"""Dump interesting information about this frame."""
locals = dict(frame.f_locals)
econtext = locals.pop('econtext', None)
rcontext = locals.pop('rcontext', None)
if "__builtins__" in locals:
del locals["__builtins__"]
if label:
label = " ( %s ) " % label
print("-- frame --%s---------------------" % label)
print("{}:{} {}".format(
os.path.basename(frame.f_code.co_filename),
frame.f_lineno,
frame.f_code.co_name))
print("locals:", locals.keys())
if econtext:
print("econtext:", econtext.keys())
if rcontext:
print("rcontext:", rcontext.keys())
print("\\--")
#!/bin/sh
mkdir -p src/chameleon_coverage_plugin
mv chameleon_coverage_plugin.py src/chameleon_coverage_plugin/chameleon_coverage_plugin.py
mv setup.py src/chameleon_coverage_plugin/setup.py
from setuptools import setup
setup(
name='chameleon_coverage_plugin',
version='0.1.0.dev0',
description="Chameleon template coverage.py plugin",
url='https://github.com/fschulze/chameleon_coverage_plugin',
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Topic :: Software Development :: Quality Assurance",
"Topic :: Software Development :: Testing",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6"],
install_requires=[
'coverage >= 4.0'],
license='MIT license',
py_modules=['chameleon_coverage_plugin'])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment