diff --git a/README.rst b/README.rst index b363428..0057273 100644 --- a/README.rst +++ b/README.rst @@ -130,6 +130,17 @@ To run the tests:: History ~~~~~~~ +Unreleased +---------- + +If a non-UTF8 file was found when looking for templates, it would fail when +reading during the reporting phase, ending execution. This failure is now +raised in a way that can be ignored with a .coveragerc setting of ``[report] +ignore_errors=True`` (`issue 78`_). + +.. _issue 78: https://github.com/nedbat/django_coverage_plugin/issues/78 + + v2.0.1 --- 2021-10-06 --------------------- diff --git a/django_coverage_plugin/plugin.py b/django_coverage_plugin/plugin.py index a3e4867..a60273d 100644 --- a/django_coverage_plugin/plugin.py +++ b/django_coverage_plugin/plugin.py @@ -8,6 +8,11 @@ import os.path import re +try: + from coverage.exceptions import NoSource +except ImportError: + # for coverage 5.x + from coverage.misc import NoSource import coverage.plugin import django import django.template @@ -303,7 +308,10 @@ def __init__(self, filename): def source(self): if self._source is None: - self._source = read_template_source(self.filename) + try: + self._source = read_template_source(self.filename) + except (IOError, UnicodeError) as exc: + raise NoSource("Couldn't read {}: {}".format(self.filename, exc)) return self._source def lines(self): diff --git a/tests/test_source.py b/tests/test_source.py index 76992d3..a4ab11b 100644 --- a/tests/test_source.py +++ b/tests/test_source.py @@ -3,6 +3,14 @@ """Tests of template inheritance for django_coverage_plugin.""" +import os + +try: + from coverage.exceptions import NoSource +except ImportError: + # for coverage 5.x + from coverage.misc import NoSource + from .plugin_test import DjangoPluginTestCase @@ -64,3 +72,65 @@ def test_customized_extensions(self): self.assert_analysis([1], name="phd.tex", missing=[1]) # The editor leave-behinds are not in the measured files. self.assert_measured_files("main.html", "unused.html", "phd.tex") + + def test_non_utf8_error(self): + # A non-UTF8 text file will raise an error. + self.make_file(".coveragerc", """\ + [run] + plugins = django_coverage_plugin + source = . + """) + # This is a template that is rendered. + self.make_template(name="main.html", text="Hello") + # Extra file containing a word encoded in CP-1252 + self.make_file(self._path("static/changelog.txt"), bytes=b"sh\xf6n") + + text = self.run_django_coverage(name="main.html") + self.assertEqual(text, "Hello") + + self.assert_measured_files("main.html", "static{}changelog.txt".format(os.sep)) + self.assert_analysis([1], name="main.html") + with self.assertRaisesRegexp(NoSource, r"changelog.txt.*invalid start byte"): + self.cov.html_report() + + def test_non_utf8_omitted(self): + # If we omit the directory with the non-UTF8 file, all is well. + self.make_file(".coveragerc", """\ + [run] + plugins = django_coverage_plugin + source = . + [report] + omit = */static/* + """) + # This is a template that is rendered. + self.make_template(name="main.html", text="Hello") + # Extra file containing a word encoded in CP-1252 + self.make_file(self._path("static/changelog.txt"), bytes=b"sh\xf6n") + + text = self.run_django_coverage(name="main.html") + self.assertEqual(text, "Hello") + + self.assert_measured_files("main.html", "static{}changelog.txt".format(os.sep)) + self.assert_analysis([1], name="main.html") + self.cov.html_report() + + def test_non_utf8_ignored(self): + # If we ignore reporting errors, a non-UTF8 text file is fine. + self.make_file(".coveragerc", """\ + [run] + plugins = django_coverage_plugin + source = . + [report] + ignore_errors = True + """) + # This is a template that is rendered. + self.make_template(name="main.html", text="Hello") + # Extra file containing a word encoded in CP-1252 + self.make_file(self._path("static/changelog.txt"), bytes=b"sh\xf6n") + + text = self.run_django_coverage(name="main.html") + self.assertEqual(text, "Hello") + + self.assert_measured_files("main.html", "static{}changelog.txt".format(os.sep)) + self.assert_analysis([1], name="main.html") + self.cov.html_report()