Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Refs #2333 - Re-added the template rendering signal for testing purpo…

…ses; however, the signal is not available during normal operation. It is only added as part of an instrumentation step that occurs during test framework setup. Previous attempt (r3659) was reverted (r3666) due to performance concerns.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@3707 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit d043200077244cfcc2b204dbb173d5d494d16f60 1 parent d78e2ae
Russell Keith-Magee authored September 02, 2006
6  django/template/__init__.py
@@ -60,6 +60,8 @@
60 60
 from django.template.context import Context, RequestContext, ContextPopException
61 61
 from django.utils.functional import curry
62 62
 from django.utils.text import smart_split
  63
+from django.dispatch import dispatcher
  64
+from django.template import signals
63 65
 
64 66
 __all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
65 67
 
@@ -137,13 +139,14 @@ def reload(self):
137 139
         return self.source
138 140
 
139 141
 class Template(object):
140  
-    def __init__(self, template_string, origin=None):
  142
+    def __init__(self, template_string, origin=None, name='<Unknown Template>'):
141 143
         "Compilation stage"
142 144
         if settings.TEMPLATE_DEBUG and origin == None:
143 145
             origin = StringOrigin(template_string)
144 146
             # Could do some crazy stack-frame stuff to record where this string
145 147
             # came from...
146 148
         self.nodelist = compile_string(template_string, origin)
  149
+        self.name = name
147 150
 
148 151
     def __iter__(self):
149 152
         for node in self.nodelist:
@@ -152,6 +155,7 @@ def __iter__(self):
152 155
 
153 156
     def render(self, context):
154 157
         "Display stage -- can be called many times"
  158
+        dispatcher.send(signal=signals.template_rendered, sender=self, template=self, context=context)
155 159
         return self.nodelist.render(context)
156 160
 
157 161
 def compile_string(template_string, origin):
2  django/template/defaulttags.py
@@ -251,7 +251,7 @@ def render(self, context):
251 251
             output = ''
252 252
         if self.parsed:
253 253
             try:
254  
-                t = Template(output)
  254
+                t = Template(output, name=self.filepath)
255 255
                 return t.render(context)
256 256
             except TemplateSyntaxError, e:
257 257
                 if settings.DEBUG:
8  django/template/loader.py
@@ -76,14 +76,16 @@ def get_template(template_name):
76 76
     Returns a compiled Template object for the given template name,
77 77
     handling template inheritance recursively.
78 78
     """
79  
-    return get_template_from_string(*find_template_source(template_name))
  79
+    source, origin = find_template_source(template_name)
  80
+    template = get_template_from_string(source, origin, template_name)
  81
+    return template
80 82
 
81  
-def get_template_from_string(source, origin=None):
  83
+def get_template_from_string(source, origin=None, name=None):
82 84
     """
83 85
     Returns a compiled Template object for the given template code,
84 86
     handling template inheritance recursively.
85 87
     """
86  
-    return Template(source, origin)
  88
+    return Template(source, origin, name)
87 89
 
88 90
 def render_to_string(template_name, dictionary=None, context_instance=None):
89 91
     """
2  django/template/loader_tags.py
@@ -57,7 +57,7 @@ def get_parent(self, context):
57 57
         except TemplateDoesNotExist:
58 58
             raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
59 59
         else:
60  
-            return get_template_from_string(source, origin)
  60
+            return get_template_from_string(source, origin, parent)
61 61
 
62 62
     def render(self, context):
63 63
         compiled_parent = self.get_parent(context)
48  django/test/client.py
... ...
@@ -1,10 +1,9 @@
1 1
 from cStringIO import StringIO
2  
-from django.contrib.admin.views.decorators import LOGIN_FORM_KEY, _encode_post_data
3 2
 from django.core.handlers.base import BaseHandler
4 3
 from django.core.handlers.wsgi import WSGIRequest
5 4
 from django.dispatch import dispatcher
6 5
 from django.http import urlencode, SimpleCookie
7  
-from django.template import signals
  6
+from django.test import signals
8 7
 from django.utils.functional import curry
9 8
 
10 9
 class ClientHandler(BaseHandler):
@@ -96,7 +95,7 @@ class Client:
96 95
     HTML rendered to the end-user.
97 96
     """
98 97
     def __init__(self, **defaults):
99  
-        self.handler = TestHandler()
  98
+        self.handler = ClientHandler()
100 99
         self.defaults = defaults
101 100
         self.cookie = SimpleCookie()
102 101
         
@@ -126,7 +125,7 @@ def request(self, **request):
126 125
         data = {}
127 126
         on_template_render = curry(store_rendered_templates, data)
128 127
         dispatcher.connect(on_template_render, signal=signals.template_rendered)
129  
-
  128
+        
130 129
         response = self.handler(environ)
131 130
         
132 131
         # Add any rendered template detail to the response
@@ -180,29 +179,38 @@ def post(self, path, data={}, **extra):
180 179
     def login(self, path, username, password, **extra):
181 180
         """
182 181
         A specialized sequence of GET and POST to log into a view that
183  
-        is protected by @login_required or a similar access decorator.
  182
+        is protected by a @login_required access decorator.
184 183
         
185  
-        path should be the URL of the login page, or of any page that
186  
-        is login protected.
  184
+        path should be the URL of the page that is login protected.
187 185
         
188  
-        Returns True if login was successful; False if otherwise.        
  186
+        Returns the response from GETting the requested URL after 
  187
+        login is complete. Returns False if login process failed.
189 188
         """
190  
-        # First, GET the login page. 
191  
-        # This is required to establish the session.
  189
+        # First, GET the page that is login protected. 
  190
+        # This page will redirect to the login page.
192 191
         response = self.get(path)
193  
-        if response.status_code != 200:
  192
+        if response.status_code != 302:
194 193
             return False
  194
+            
  195
+        login_path, data = response['Location'].split('?')
  196
+        next = data.split('=')[1]
195 197
 
196  
-        # Set up the block of form data required by the login page.
  198
+        # Second, GET the login page; required to set up cookies
  199
+        response = self.get(login_path, **extra)
  200
+        if response.status_code != 200:
  201
+            return False
  202
+            
  203
+        # Last, POST the login data.
197 204
         form_data = {
198 205
             'username': username,
199 206
             'password': password,
200  
-            'this_is_the_login_form': 1,
201  
-            'post_data': _encode_post_data({LOGIN_FORM_KEY: 1})
  207
+            'next' : next,
202 208
         }
203  
-        response = self.post(path, data=form_data, **extra)
204  
-        
205  
-        # login page should give response 200 (if you requested the login
206  
-        # page specifically), or 302 (if you requested a login
207  
-        # protected page, to which the login can redirect).
208  
-        return response.status_code in (200,302)
  209
+        response = self.post(login_path, data=form_data, **extra)
  210
+
  211
+        # Login page should 302 redirect to the originally requested page
  212
+        if response.status_code != 302 or response['Location'] != path:
  213
+            return False
  214
+
  215
+        # Since we are logged in, request the actual page again
  216
+        return self.get(path)
1  django/test/signals.py
... ...
@@ -0,0 +1 @@
  1
+template_rendered = object()
4  django/test/simple.py
... ...
@@ -1,6 +1,7 @@
1 1
 import unittest, doctest
2 2
 from django.conf import settings
3 3
 from django.core import management
  4
+from django.test.utils import setup_test_environment, teardown_test_environment
4 5
 from django.test.utils import create_test_db, destroy_test_db
5 6
 from django.test.testcases import OutputChecker, DocTestRunner
6 7
 
@@ -51,6 +52,7 @@ def run_tests(module_list, verbosity=1, extra_tests=[]):
51 52
     the module. A list of 'extra' tests may also be provided; these tests
52 53
     will be added to the test suite.
53 54
     """
  55
+    setup_test_environment()
54 56
     
55 57
     settings.DEBUG = False    
56 58
     suite = unittest.TestSuite()
@@ -66,3 +68,5 @@ def run_tests(module_list, verbosity=1, extra_tests=[]):
66 68
     management.syncdb(verbosity, interactive=False)
67 69
     unittest.TextTestRunner(verbosity=verbosity).run(suite)
68 70
     destroy_test_db(old_name, verbosity)
  71
+    
  72
+    teardown_test_environment()
30  django/test/utils.py
... ...
@@ -1,11 +1,40 @@
1 1
 import sys, time
2 2
 from django.conf import settings
3 3
 from django.db import connection, transaction, backend
  4
+from django.dispatch import dispatcher
  5
+from django.test import signals
  6
+from django.template import Template
4 7
 
5 8
 # The prefix to put on the default database name when creating
6 9
 # the test database.
7 10
 TEST_DATABASE_PREFIX = 'test_'
8 11
 
  12
+def instrumented_test_render(self, context):
  13
+    """An instrumented Template render method, providing a signal 
  14
+    that can be intercepted by the test system Client
  15
+    
  16
+    """
  17
+    dispatcher.send(signal=signals.template_rendered, sender=self, template=self, context=context)
  18
+    return self.nodelist.render(context)
  19
+    
  20
+def setup_test_environment():
  21
+    """Perform any global pre-test setup. This involves:
  22
+        
  23
+        - Installing the instrumented test renderer
  24
+        
  25
+    """
  26
+    Template.original_render = Template.render
  27
+    Template.render = instrumented_test_render
  28
+    
  29
+def teardown_test_environment():
  30
+    """Perform any global post-test teardown. This involves:
  31
+
  32
+        - Restoring the original test renderer
  33
+        
  34
+    """
  35
+    Template.render = Template.original_render
  36
+    del Template.original_render
  37
+    
9 38
 def _set_autocommit(connection):
10 39
     "Make sure a connection is in autocommit mode."
11 40
     if hasattr(connection.connection, "autocommit"):
@@ -75,4 +104,3 @@ def destroy_test_db(old_database_name, verbosity=1):
75 104
         time.sleep(1) # To avoid "database is being accessed by other users" errors.
76 105
         cursor.execute("DROP DATABASE %s" % backend.quote_name(TEST_DATABASE_NAME))
77 106
         connection.close()
78  
-
6  django/views/debug.py
@@ -115,7 +115,7 @@ def technical_500_response(request, exc_type, exc_value, tb):
115 115
             'function': '?',
116 116
             'lineno': '?',
117 117
         }]
118  
-    t = Template(TECHNICAL_500_TEMPLATE)
  118
+    t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 Template')
119 119
     c = Context({
120 120
         'exception_type': exc_type.__name__,
121 121
         'exception_value': exc_value,
@@ -141,7 +141,7 @@ def technical_404_response(request, exception):
141 141
             # tried exists but is an empty list. The URLconf must've been empty.
142 142
             return empty_urlconf(request)
143 143
 
144  
-    t = Template(TECHNICAL_404_TEMPLATE)
  144
+    t = Template(TECHNICAL_404_TEMPLATE, name='Technical 404 Template')
145 145
     c = Context({
146 146
         'root_urlconf': settings.ROOT_URLCONF,
147 147
         'urlpatterns': tried,
@@ -154,7 +154,7 @@ def technical_404_response(request, exception):
154 154
 
155 155
 def empty_urlconf(request):
156 156
     "Create an empty URLconf 404 error response."
157  
-    t = Template(EMPTY_URLCONF_TEMPLATE)
  157
+    t = Template(EMPTY_URLCONF_TEMPLATE, name='Empty URLConf Template')
158 158
     c = Context({
159 159
         'project_name': settings.SETTINGS_MODULE.split('.')[0]
160 160
     })
2  django/views/static.py
@@ -81,7 +81,7 @@ def directory_index(path, fullpath):
81 81
     try:
82 82
         t = loader.get_template('static/directory_index')
83 83
     except TemplateDoesNotExist:
84  
-        t = Template(DEFAULT_DIRECTORY_INDEX_TEMPLATE)
  84
+        t = Template(DEFAULT_DIRECTORY_INDEX_TEMPLATE, name='Default Directory Index Template')
85 85
     files = []
86 86
     for f in os.listdir(fullpath):
87 87
         if not f.startswith('.'):

0 notes on commit d043200

Please sign in to comment.
Something went wrong with that request. Please try again.