Skip to content

Commit fdb0892

Browse files
committed
Check settings later, to avoid errors. #18
Some programs don't need settings. If we check early, then our plugin will fail where the original program would not. Now we check later, when we know that we will actually be involved.
1 parent 5378120 commit fdb0892

File tree

4 files changed

+63
-16
lines changed

4 files changed

+63
-16
lines changed

django_coverage_plugin/plugin.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import django
1515
import django.template
16+
from django.core.exceptions import ImproperlyConfigured
1617
from django.template.base import (
1718
Lexer, TextNode, NodeList, Template,
1819
TOKEN_BLOCK, TOKEN_MAPPING, TOKEN_TEXT, TOKEN_VAR,
@@ -49,7 +50,16 @@ def check_debug():
4950
# settings exist, use them, otherwise use the old.
5051

5152
from django.conf import settings
52-
templates = getattr(settings, 'TEMPLATES', [])
53+
54+
try:
55+
templates = getattr(settings, 'TEMPLATES', [])
56+
except ImproperlyConfigured:
57+
# Maybe there are no settings at all. We are fine with this. Our
58+
# code will need settings, but if this program we're in runs without
59+
# settings, then it must be that it never uses templates, and our code
60+
# will never try to use the settings anyway.
61+
return
62+
5363
if templates:
5464
for template_settings in templates:
5565
if template_settings['BACKEND'] != 'django.template.backends.django.DjangoTemplates':
@@ -118,7 +128,7 @@ class DjangoTemplatePlugin(
118128
):
119129

120130
def __init__(self):
121-
check_debug()
131+
self.debug_checked = False
122132

123133
self.django_template_dir = os.path.realpath(
124134
os.path.dirname(django.template.__file__)
@@ -140,6 +150,10 @@ def sys_info(self):
140150

141151
def file_tracer(self, filename):
142152
if filename.startswith(self.django_template_dir):
153+
if not self.debug_checked:
154+
check_debug()
155+
self.debug_checked = True
156+
143157
return self
144158
return None
145159

tests/plugin_test.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
"""Base classes and helpers for testing the plugin."""
55

6+
import contextlib
67
import os
78
import os.path
89
import re
@@ -14,6 +15,8 @@
1415
import django
1516
from django.conf import settings
1617

18+
from django_coverage_plugin.plugin import DjangoTemplatePlugin
19+
1720

1821
def test_settings():
1922
"""Create a dict full of default Django settings for the tests."""
@@ -69,7 +72,7 @@ def test_settings():
6972
from django.test import TestCase # noqa
7073

7174

72-
class DjangoPluginTestCase(TempDirMixin, TestCase):
75+
class DjangoPluginTestCase(StdStreamCapturingMixin, TempDirMixin, TestCase):
7376
"""A base class for all our tests."""
7477

7578
def setUp(self):
@@ -110,6 +113,8 @@ def run_django_coverage(
110113
str: the text produced by the template.
111114
112115
"""
116+
use_real_context = (django.VERSION < (1, 8))
117+
113118
if options is None:
114119
options = {'source': ["."]}
115120

@@ -122,9 +127,15 @@ def run_django_coverage(
122127
tem = engine.get_template(name or self.template_file)
123128
elif text is not None:
124129
tem = Template(text)
130+
use_real_context = True
125131
else:
126132
tem = get_template(name or self.template_file)
127-
ctx = Context(context or {})
133+
134+
ctx = context or {}
135+
if use_real_context:
136+
# Before 1.8, render() wanted a Context. After, it wants a dict.
137+
ctx = Context(ctx)
138+
128139
self.cov = coverage.Coverage(**options)
129140
self.append_config("run:plugins", "django_coverage_plugin")
130141
if 0:
@@ -133,6 +144,11 @@ def run_django_coverage(
133144
text = tem.render(ctx)
134145
self.cov.stop()
135146
self.cov.save()
147+
# Warning! Accessing secret internals!
148+
for pl in self.cov.plugins:
149+
if isinstance(pl, DjangoTemplatePlugin):
150+
if not pl._coverage_enabled:
151+
raise PluginDisabled()
136152
return text
137153

138154
def append_config(self, option, value):
@@ -209,6 +225,25 @@ def get_xml_report(self, name=None):
209225
xml_coverage = self.cov.xml_report(os.path.abspath(path))
210226
return xml_coverage
211227

228+
@contextlib.contextmanager
229+
def assert_plugin_disabled(self, msg):
230+
"""Assert that our plugin was disabled during an operation."""
231+
# self.run_django_coverage will raise PluginDisabled if the plugin
232+
# was disabled.
233+
with self.assertRaises(PluginDisabled):
234+
yield
235+
stderr = self.stderr()
236+
self.assertIn(
237+
"Coverage.py warning: "
238+
"Disabling plugin 'django_coverage_plugin.DjangoTemplatePlugin' "
239+
"due to an exception:",
240+
stderr
241+
)
242+
self.assertIn(
243+
"DjangoTemplatePluginException: " + msg,
244+
stderr
245+
)
246+
212247

213248
def squashed(s):
214249
"""Remove all of the whitespace from s."""
@@ -241,3 +276,8 @@ def test_thing(self):
241276
return lambda func: func
242277
else:
243278
return unittest.skip("Django version must be older")
279+
280+
281+
class PluginDisabled(Exception):
282+
"""Raised if we find that our plugin has been disabled."""
283+
pass

tests/test_engines.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
"""Tests of multiple engines for django_coverage_plugin."""
55

6-
from django_coverage_plugin import DjangoTemplatePluginException
7-
86
from .plugin_test import DjangoPluginTestCase, django_start_at
97

108

@@ -51,6 +49,5 @@ def test_third_engine_not_debug(self):
5149
self.addCleanup(modified_settings.disable)
5250

5351
self.make_template('Hello')
54-
msg = "Template debugging must be enabled in settings."
55-
with self.assertRaisesRegexp(DjangoTemplatePluginException, msg):
52+
with self.assert_plugin_disabled("Template debugging must be enabled in settings."):
5653
self.run_django_coverage()

tests/test_settings.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
import django
77
from django.test.utils import override_settings
88

9-
from django_coverage_plugin import DjangoTemplatePluginException
10-
119
from .plugin_test import DjangoPluginTestCase, test_settings, django_start_at
1210

1311

@@ -30,26 +28,24 @@
3028

3129

3230
class SettingsTest(DjangoPluginTestCase):
31+
"""Tests of detecting that the settings need to be right for the plugin to work."""
3332

3433
@override_settings(**DEBUG_FALSE_OVERRIDES)
3534
def test_debug_false(self):
3635
self.make_template('Hello')
37-
msg = "Template debugging must be enabled in settings."
38-
with self.assertRaisesRegexp(DjangoTemplatePluginException, msg):
36+
with self.assert_plugin_disabled("Template debugging must be enabled in settings."):
3937
self.run_django_coverage()
4038

4139
@django_start_at(1, 8)
4240
@override_settings(**NO_OPTIONS_OVERRIDES)
4341
def test_no_options(self):
4442
self.make_template('Hello')
45-
msg = "Template debugging must be enabled in settings."
46-
with self.assertRaisesRegexp(DjangoTemplatePluginException, msg):
43+
with self.assert_plugin_disabled("Template debugging must be enabled in settings."):
4744
self.run_django_coverage()
4845

4946
@django_start_at(1, 8)
5047
@override_settings(**OTHER_ENGINE_OVERRIDES)
5148
def test_other_engine(self):
5249
self.make_template('Hello')
53-
msg = "Can't use non-Django templates."
54-
with self.assertRaisesRegexp(DjangoTemplatePluginException, msg):
50+
with self.assert_plugin_disabled("Can't use non-Django templates."):
5551
self.run_django_coverage()

0 commit comments

Comments
 (0)