Skip to content
Projeler
Gruplar
Parçacıklar
Yardım
Yükleniyor...
Oturum aç / Kaydol
Gezinmeyi değiştir
D
django
Proje
Proje
Ayrıntılar
Etkinlik
Cycle Analytics
Depo (repository)
Depo (repository)
Dosyalar
Kayıtlar (commit)
Dallar (branch)
Etiketler
Katkıda bulunanlar
Grafik
Karşılaştır
Grafikler
Konular (issue)
0
Konular (issue)
0
Liste
Pano
Etiketler
Kilometre Taşları
Birleştirme (merge) Talepleri
0
Birleştirme (merge) Talepleri
0
CI / CD
CI / CD
İş akışları (pipeline)
İşler
Zamanlamalar
Grafikler
Paketler
Paketler
Wiki
Wiki
Parçacıklar
Parçacıklar
Üyeler
Üyeler
Collapse sidebar
Close sidebar
Etkinlik
Grafik
Grafikler
Yeni bir konu (issue) oluştur
İşler
Kayıtlar (commit)
Konu (issue) Panoları
Kenar çubuğunu aç
Batuhan Osman TASKAYA
django
Commits
adff499e
Kaydet (Commit)
adff499e
authored
Nis 24, 2015
tarafından
Preston Timmons
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
Fixed #24119, #24120 -- Formalized debug integration for template backends.
üst
d1df1fd2
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
354 additions
and
63 deletions
+354
-63
django.py
django/template/backends/django.py
+24
-6
dummy.py
django/template/backends/dummy.py
+12
-4
jinja2.py
django/template/backends/jinja2.py
+48
-4
base.py
django/template/base.py
+19
-5
loader.py
django/template/loader.py
+9
-9
debug.py
django/views/debug.py
+5
-23
1.9.txt
docs/releases/1.9.txt
+3
-0
postmortem.png
docs/topics/_images/postmortem.png
+0
-0
template-lines.png
docs/topics/_images/template-lines.png
+0
-0
templates.txt
docs/topics/templates.txt
+137
-5
syntax_error2.html
...late_backends/jinja2/template_backends/syntax_error2.html
+31
-0
test_dummy.py
tests/template_backends/test_dummy.py
+2
-1
test_jinja2.py
tests/template_backends/test_jinja2.py
+42
-0
tests.py
tests/template_loader/tests.py
+22
-6
No files found.
django/template/backends/django.py
Dosyayı görüntüle @
adff499e
# Since this package contains a "django" module, this is required on Python 2.
# Since this package contains a "django" module, this is required on Python 2.
from
__future__
import
absolute_import
from
__future__
import
absolute_import
import
sys
import
warnings
import
warnings
from
django.conf
import
settings
from
django.conf
import
settings
from
django.template
import
TemplateDoesNotExist
from
django.template.context
import
Context
,
RequestContext
,
make_context
from
django.template.context
import
Context
,
RequestContext
,
make_context
from
django.template.engine
import
Engine
,
_dirs_undefined
from
django.template.engine
import
Engine
,
_dirs_undefined
from
django.utils
import
six
from
django.utils.deprecation
import
RemovedInDjango20Warning
from
django.utils.deprecation
import
RemovedInDjango20Warning
from
.base
import
BaseEngine
from
.base
import
BaseEngine
...
@@ -24,21 +27,23 @@ class DjangoTemplates(BaseEngine):
...
@@ -24,21 +27,23 @@ class DjangoTemplates(BaseEngine):
self
.
engine
=
Engine
(
self
.
dirs
,
self
.
app_dirs
,
**
options
)
self
.
engine
=
Engine
(
self
.
dirs
,
self
.
app_dirs
,
**
options
)
def
from_string
(
self
,
template_code
):
def
from_string
(
self
,
template_code
):
return
Template
(
self
.
engine
.
from_string
(
template_code
))
return
Template
(
self
.
engine
.
from_string
(
template_code
)
,
self
)
def
get_template
(
self
,
template_name
,
dirs
=
_dirs_undefined
):
def
get_template
(
self
,
template_name
,
dirs
=
_dirs_undefined
):
return
Template
(
self
.
engine
.
get_template
(
template_name
,
dirs
))
try
:
return
Template
(
self
.
engine
.
get_template
(
template_name
,
dirs
),
self
)
except
TemplateDoesNotExist
as
exc
:
reraise
(
exc
,
self
)
class
Template
(
object
):
class
Template
(
object
):
def
__init__
(
self
,
template
):
def
__init__
(
self
,
template
,
backend
):
self
.
template
=
template
self
.
template
=
template
self
.
backend
=
backend
@property
@property
def
origin
(
self
):
def
origin
(
self
):
# TODO: define the Origin API. For now simply forwarding to the
# underlying Template preserves backwards-compatibility.
return
self
.
template
.
origin
return
self
.
template
.
origin
def
render
(
self
,
context
=
None
,
request
=
None
):
def
render
(
self
,
context
=
None
,
request
=
None
):
...
@@ -71,4 +76,17 @@ class Template(object):
...
@@ -71,4 +76,17 @@ class Template(object):
else
:
else
:
context
=
make_context
(
context
,
request
)
context
=
make_context
(
context
,
request
)
return
self
.
template
.
render
(
context
)
try
:
return
self
.
template
.
render
(
context
)
except
TemplateDoesNotExist
as
exc
:
reraise
(
exc
,
self
.
backend
)
def
reraise
(
exc
,
backend
):
"""
Reraise TemplateDoesNotExist while maintaining template debug information.
"""
new
=
exc
.
__class__
(
*
exc
.
args
,
tried
=
exc
.
tried
,
backend
=
backend
)
if
hasattr
(
exc
,
'template_debug'
):
new
.
template_debug
=
exc
.
template_debug
six
.
reraise
(
exc
.
__class__
,
new
,
sys
.
exc_info
()[
2
])
django/template/backends/dummy.py
Dosyayı görüntüle @
adff499e
# Since this package contains a "django" module, this is required on Python 2.
# Since this package contains a "django" module, this is required on Python 2.
from
__future__
import
absolute_import
from
__future__
import
absolute_import
import
errno
import
io
import
io
import
string
import
string
from
django.conf
import
settings
from
django.conf
import
settings
from
django.core.exceptions
import
ImproperlyConfigured
from
django.core.exceptions
import
ImproperlyConfigured
from
django.template
import
TemplateDoesNotExist
from
django.template
import
Origin
,
TemplateDoesNotExist
from
django.utils.html
import
conditional_escape
from
django.utils.html
import
conditional_escape
from
.base
import
BaseEngine
from
.base
import
BaseEngine
...
@@ -29,17 +30,24 @@ class TemplateStrings(BaseEngine):
...
@@ -29,17 +30,24 @@ class TemplateStrings(BaseEngine):
return
Template
(
template_code
)
return
Template
(
template_code
)
def
get_template
(
self
,
template_name
):
def
get_template
(
self
,
template_name
):
tried
=
[]
for
template_file
in
self
.
iter_template_filenames
(
template_name
):
for
template_file
in
self
.
iter_template_filenames
(
template_name
):
try
:
try
:
with
io
.
open
(
template_file
,
encoding
=
settings
.
FILE_CHARSET
)
as
fp
:
with
io
.
open
(
template_file
,
encoding
=
settings
.
FILE_CHARSET
)
as
fp
:
template_code
=
fp
.
read
()
template_code
=
fp
.
read
()
except
IOError
:
except
IOError
as
e
:
continue
if
e
.
errno
==
errno
.
ENOENT
:
tried
.
append
((
Origin
(
template_file
,
template_name
,
self
),
'Source does not exist'
,
))
continue
raise
return
Template
(
template_code
)
return
Template
(
template_code
)
else
:
else
:
raise
TemplateDoesNotExist
(
template_name
)
raise
TemplateDoesNotExist
(
template_name
,
tried
=
tried
,
backend
=
self
)
class
Template
(
string
.
Template
):
class
Template
(
string
.
Template
):
...
...
django/template/backends/jinja2.py
Dosyayı görüntüle @
adff499e
...
@@ -41,17 +41,24 @@ class Jinja2(BaseEngine):
...
@@ -41,17 +41,24 @@ class Jinja2(BaseEngine):
try
:
try
:
return
Template
(
self
.
env
.
get_template
(
template_name
))
return
Template
(
self
.
env
.
get_template
(
template_name
))
except
jinja2
.
TemplateNotFound
as
exc
:
except
jinja2
.
TemplateNotFound
as
exc
:
six
.
reraise
(
TemplateDoesNotExist
,
TemplateDoesNotExist
(
exc
.
args
),
six
.
reraise
(
sys
.
exc_info
()[
2
])
TemplateDoesNotExist
,
TemplateDoesNotExist
(
exc
.
name
,
backend
=
self
),
sys
.
exc_info
()[
2
],
)
except
jinja2
.
TemplateSyntaxError
as
exc
:
except
jinja2
.
TemplateSyntaxError
as
exc
:
six
.
reraise
(
TemplateSyntaxError
,
TemplateSyntaxError
(
exc
.
args
),
new
=
TemplateSyntaxError
(
exc
.
args
)
sys
.
exc_info
()[
2
])
new
.
template_debug
=
get_exception_info
(
exc
)
six
.
reraise
(
TemplateSyntaxError
,
new
,
sys
.
exc_info
()[
2
])
class
Template
(
object
):
class
Template
(
object
):
def
__init__
(
self
,
template
):
def
__init__
(
self
,
template
):
self
.
template
=
template
self
.
template
=
template
self
.
origin
=
Origin
(
name
=
template
.
filename
,
template_name
=
template
.
name
,
)
def
render
(
self
,
context
=
None
,
request
=
None
):
def
render
(
self
,
context
=
None
,
request
=
None
):
if
context
is
None
:
if
context
is
None
:
...
@@ -61,3 +68,40 @@ class Template(object):
...
@@ -61,3 +68,40 @@ class Template(object):
context
[
'csrf_input'
]
=
csrf_input_lazy
(
request
)
context
[
'csrf_input'
]
=
csrf_input_lazy
(
request
)
context
[
'csrf_token'
]
=
csrf_token_lazy
(
request
)
context
[
'csrf_token'
]
=
csrf_token_lazy
(
request
)
return
self
.
template
.
render
(
context
)
return
self
.
template
.
render
(
context
)
class
Origin
(
object
):
"""
A container to hold debug information as described in the template API
documentation.
"""
def
__init__
(
self
,
name
,
template_name
):
self
.
name
=
name
self
.
template_name
=
template_name
def
get_exception_info
(
exception
):
"""
Formats exception information for display on the debug page using the
structure described in the template API documentation.
"""
context_lines
=
10
lineno
=
exception
.
lineno
lines
=
list
(
enumerate
(
exception
.
source
.
strip
()
.
split
(
"
\n
"
),
start
=
1
))
during
=
lines
[
lineno
-
1
][
1
]
total
=
len
(
lines
)
top
=
max
(
0
,
lineno
-
context_lines
-
1
)
bottom
=
min
(
total
,
lineno
+
context_lines
)
return
{
'name'
:
exception
.
filename
,
'message'
:
exception
.
message
,
'source_lines'
:
lines
[
top
:
bottom
],
'line'
:
lineno
,
'before'
:
''
,
'during'
:
during
,
'after'
:
''
,
'total'
:
total
,
'top'
:
top
,
'bottom'
:
bottom
,
}
django/template/base.py
Dosyayı görüntüle @
adff499e
...
@@ -135,13 +135,27 @@ class TemplateSyntaxError(Exception):
...
@@ -135,13 +135,27 @@ class TemplateSyntaxError(Exception):
class
TemplateDoesNotExist
(
Exception
):
class
TemplateDoesNotExist
(
Exception
):
"""
"""
This exception is used when template loaders are unable to find a
The exception used by backends when a template does not exist. Accepts the
template. The tried argument is an optional list of tuples containing
following optional arguments:
(origin, status), where origin is an Origin object and status is a string
with the reason the template wasn't found.
backend
The template backend class used when raising this exception.
tried
A list of sources that were tried when finding the template. This
is formatted as a list of tuples containing (origin, status), where
origin is an Origin object and status is a string with the reason the
template wasn't found.
chain
A list of intermediate TemplateDoesNotExist exceptions. This is used to
encapsulate multiple exceptions when loading templates from multiple
engines.
"""
"""
def
__init__
(
self
,
msg
,
tried
=
None
):
def
__init__
(
self
,
msg
,
tried
=
None
,
backend
=
None
,
chain
=
None
):
self
.
backend
=
backend
self
.
tried
=
tried
or
[]
self
.
tried
=
tried
or
[]
self
.
chain
=
chain
or
[]
super
(
TemplateDoesNotExist
,
self
)
.
__init__
(
msg
)
super
(
TemplateDoesNotExist
,
self
)
.
__init__
(
msg
)
...
...
django/template/loader.py
Dosyayı görüntüle @
adff499e
...
@@ -17,7 +17,7 @@ def get_template(template_name, dirs=_dirs_undefined, using=None):
...
@@ -17,7 +17,7 @@ def get_template(template_name, dirs=_dirs_undefined, using=None):
Raises TemplateDoesNotExist if no such template exists.
Raises TemplateDoesNotExist if no such template exists.
"""
"""
tried
=
[]
chain
=
[]
engines
=
_engine_list
(
using
)
engines
=
_engine_list
(
using
)
for
engine
in
engines
:
for
engine
in
engines
:
try
:
try
:
...
@@ -33,9 +33,9 @@ def get_template(template_name, dirs=_dirs_undefined, using=None):
...
@@ -33,9 +33,9 @@ def get_template(template_name, dirs=_dirs_undefined, using=None):
else
:
else
:
return
engine
.
get_template
(
template_name
)
return
engine
.
get_template
(
template_name
)
except
TemplateDoesNotExist
as
e
:
except
TemplateDoesNotExist
as
e
:
tried
.
extend
(
e
.
tried
)
chain
.
append
(
e
)
raise
TemplateDoesNotExist
(
template_name
,
tried
=
tried
)
raise
TemplateDoesNotExist
(
template_name
,
chain
=
chain
)
def
select_template
(
template_name_list
,
dirs
=
_dirs_undefined
,
using
=
None
):
def
select_template
(
template_name_list
,
dirs
=
_dirs_undefined
,
using
=
None
):
...
@@ -46,7 +46,7 @@ def select_template(template_name_list, dirs=_dirs_undefined, using=None):
...
@@ -46,7 +46,7 @@ def select_template(template_name_list, dirs=_dirs_undefined, using=None):
Raises TemplateDoesNotExist if no such template exists.
Raises TemplateDoesNotExist if no such template exists.
"""
"""
tried
=
[]
chain
=
[]
engines
=
_engine_list
(
using
)
engines
=
_engine_list
(
using
)
for
template_name
in
template_name_list
:
for
template_name
in
template_name_list
:
for
engine
in
engines
:
for
engine
in
engines
:
...
@@ -63,10 +63,10 @@ def select_template(template_name_list, dirs=_dirs_undefined, using=None):
...
@@ -63,10 +63,10 @@ def select_template(template_name_list, dirs=_dirs_undefined, using=None):
else
:
else
:
return
engine
.
get_template
(
template_name
)
return
engine
.
get_template
(
template_name
)
except
TemplateDoesNotExist
as
e
:
except
TemplateDoesNotExist
as
e
:
tried
.
extend
(
e
.
tried
)
chain
.
append
(
e
)
if
template_name_list
:
if
template_name_list
:
raise
TemplateDoesNotExist
(
', '
.
join
(
template_name_list
),
tried
=
tried
)
raise
TemplateDoesNotExist
(
', '
.
join
(
template_name_list
),
chain
=
chain
)
else
:
else
:
raise
TemplateDoesNotExist
(
"No template names provided"
)
raise
TemplateDoesNotExist
(
"No template names provided"
)
...
@@ -92,7 +92,7 @@ def render_to_string(template_name, context=None,
...
@@ -92,7 +92,7 @@ def render_to_string(template_name, context=None,
return
template
.
render
(
context
,
request
)
return
template
.
render
(
context
,
request
)
else
:
else
:
tried
=
[]
chain
=
[]
# Some deprecated arguments were passed - use the legacy code path
# Some deprecated arguments were passed - use the legacy code path
for
engine
in
_engine_list
(
using
):
for
engine
in
_engine_list
(
using
):
try
:
try
:
...
@@ -124,13 +124,13 @@ def render_to_string(template_name, context=None,
...
@@ -124,13 +124,13 @@ def render_to_string(template_name, context=None,
"method doesn't support the dictionary argument."
%
"method doesn't support the dictionary argument."
%
engine
.
name
,
stacklevel
=
2
)
engine
.
name
,
stacklevel
=
2
)
except
TemplateDoesNotExist
as
e
:
except
TemplateDoesNotExist
as
e
:
tried
.
extend
(
e
.
tried
)
chain
.
append
(
e
)
continue
continue
if
template_name
:
if
template_name
:
if
isinstance
(
template_name
,
(
list
,
tuple
)):
if
isinstance
(
template_name
,
(
list
,
tuple
)):
template_name
=
', '
.
join
(
template_name
)
template_name
=
', '
.
join
(
template_name
)
raise
TemplateDoesNotExist
(
template_name
,
tried
=
tried
)
raise
TemplateDoesNotExist
(
template_name
,
chain
=
chain
)
else
:
else
:
raise
TemplateDoesNotExist
(
"No template names provided"
)
raise
TemplateDoesNotExist
(
"No template names provided"
)
...
...
django/views/debug.py
Dosyayı görüntüle @
adff499e
...
@@ -9,7 +9,7 @@ from django.core.urlresolvers import Resolver404, resolve
...
@@ -9,7 +9,7 @@ from django.core.urlresolvers import Resolver404, resolve
from
django.http
import
(
from
django.http
import
(
HttpRequest
,
HttpResponse
,
HttpResponseNotFound
,
build_request_repr
,
HttpRequest
,
HttpResponse
,
HttpResponseNotFound
,
build_request_repr
,
)
)
from
django.template
import
Context
,
Engine
,
TemplateDoesNotExist
,
engines
from
django.template
import
Context
,
Engine
,
TemplateDoesNotExist
from
django.template.defaultfilters
import
force_escape
,
pprint
from
django.template.defaultfilters
import
force_escape
,
pprint
from
django.utils
import
lru_cache
,
six
,
timezone
from
django.utils
import
lru_cache
,
six
,
timezone
from
django.utils.datastructures
import
MultiValueDict
from
django.utils.datastructures
import
MultiValueDict
...
@@ -276,25 +276,7 @@ class ExceptionReporter(object):
...
@@ -276,25 +276,7 @@ class ExceptionReporter(object):
"""Return a dictionary containing traceback information."""
"""Return a dictionary containing traceback information."""
if
self
.
exc_type
and
issubclass
(
self
.
exc_type
,
TemplateDoesNotExist
):
if
self
.
exc_type
and
issubclass
(
self
.
exc_type
,
TemplateDoesNotExist
):
self
.
template_does_not_exist
=
True
self
.
template_does_not_exist
=
True
postmortem
=
[]
self
.
postmortem
=
self
.
exc_value
.
chain
or
[
self
.
exc_value
]
# TODO: add support for multiple template engines (#24120).
# TemplateDoesNotExist should carry all the information, including
# the backend, rather than looping through engines.all.
for
engine
in
engines
.
all
():
if
hasattr
(
engine
,
'engine'
):
e
=
engine
.
engine
else
:
e
=
engine
postmortem
.
append
(
dict
(
engine
=
engine
,
tried
=
[
entry
for
entry
in
self
.
exc_value
.
tried
if
entry
[
0
]
.
loader
.
engine
==
e
],
))
self
.
postmortem
=
postmortem
frames
=
self
.
get_traceback_frames
()
frames
=
self
.
get_traceback_frames
()
for
i
,
frame
in
enumerate
(
frames
):
for
i
,
frame
in
enumerate
(
frames
):
...
@@ -751,7 +733,7 @@ TECHNICAL_500_TEMPLATE = ("""
...
@@ -751,7 +733,7 @@ TECHNICAL_500_TEMPLATE = ("""
{
%
if postmortem
%
}
{
%
if postmortem
%
}
<p class="append-bottom">Django tried loading these templates, in this order:</p>
<p class="append-bottom">Django tried loading these templates, in this order:</p>
{
%
for entry in postmortem
%
}
{
%
for entry in postmortem
%
}
<p class="postmortem-section">Using engine <code>{{ entry.
engine
.name }}</code>:</p>
<p class="postmortem-section">Using engine <code>{{ entry.
backend
.name }}</code>:</p>
<ul>
<ul>
{
%
if entry.tried
%
}
{
%
if entry.tried
%
}
{
%
for attempt in entry.tried
%
}
{
%
for attempt in entry.tried
%
}
...
@@ -890,7 +872,7 @@ Installed Middleware:
...
@@ -890,7 +872,7 @@ Installed Middleware:
{
%
if template_does_not_exist
%
}Template loader postmortem
{
%
if template_does_not_exist
%
}Template loader postmortem
{
%
if postmortem
%
}Django tried loading these templates, in this order:
{
%
if postmortem
%
}Django tried loading these templates, in this order:
{
%
for entry in postmortem
%
}
{
%
for entry in postmortem
%
}
Using engine {{ entry.
engine
.name }}:
Using engine {{ entry.
backend
.name }}:
{
%
if entry.tried
%
}{
%
for attempt in entry.tried
%
} * {{ attempt.0.loader_name }}: {{ attempt.0.name }} ({{ attempt.1 }})
{
%
if entry.tried
%
}{
%
for attempt in entry.tried
%
} * {{ attempt.0.loader_name }}: {{ attempt.0.name }} ({{ attempt.1 }})
{
%
endfor
%
}{
%
else
%
} This engine did not provide a list of tried templates.
{
%
endfor
%
}{
%
else
%
} This engine did not provide a list of tried templates.
{
%
endif
%
}{
%
endfor
%
}
{
%
endif
%
}{
%
endfor
%
}
...
@@ -1083,7 +1065,7 @@ Installed Middleware:
...
@@ -1083,7 +1065,7 @@ Installed Middleware:
{
%
if template_does_not_exist
%
}Template loader postmortem
{
%
if template_does_not_exist
%
}Template loader postmortem
{
%
if postmortem
%
}Django tried loading these templates, in this order:
{
%
if postmortem
%
}Django tried loading these templates, in this order:
{
%
for entry in postmortem
%
}
{
%
for entry in postmortem
%
}
Using engine {{ entry.
engine
.name }}:
Using engine {{ entry.
backend
.name }}:
{
%
if entry.tried
%
}{
%
for attempt in entry.tried
%
} * {{ attempt.0.loader_name }}: {{ attempt.0.name }} ({{ attempt.1 }})
{
%
if entry.tried
%
}{
%
for attempt in entry.tried
%
} * {{ attempt.0.loader_name }}: {{ attempt.0.name }} ({{ attempt.1 }})
{
%
endfor
%
}{
%
else
%
} This engine did not provide a list of tried templates.
{
%
endfor
%
}{
%
else
%
} This engine did not provide a list of tried templates.
{
%
endif
%
}{
%
endfor
%
}
{
%
endif
%
}{
%
endfor
%
}
...
...
docs/releases/1.9.txt
Dosyayı görüntüle @
adff499e
...
@@ -249,6 +249,9 @@ Templates
...
@@ -249,6 +249,9 @@ Templates
* The debug page template postmortem now include output from each engine that
* The debug page template postmortem now include output from each engine that
is installed.
is installed.
* :ref:`Debug page integration <template-debug-integration>` for custom
template engines was added.
Requests and Responses
Requests and Responses
^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^
...
...
docs/topics/_images/postmortem.png
0 → 100644
Dosyayı görüntüle @
adff499e
36.2 KB
docs/topics/_images/template-lines.png
0 → 100644
Dosyayı görüntüle @
adff499e
21 KB
docs/topics/templates.txt
Dosyayı görüntüle @
adff499e
...
@@ -152,11 +152,32 @@ The ``django.template.loader`` module defines two functions to load templates.
...
@@ -152,11 +152,32 @@ The ``django.template.loader`` module defines two functions to load templates.
If loading a template fails, the following two exceptions, defined in
If loading a template fails, the following two exceptions, defined in
``django.template``, may be raised:
``django.template``, may be raised:
.. exception:: TemplateDoesNotExist
.. exception:: TemplateDoesNotExist
(msg, tried=None, backend=None, chain=None)
This exception is raised when a template cannot be found.
This exception is raised when a template cannot be found. It accepts the
following optional arguments for populating the :ref:`template postmortem
<template-postmortem>` on the debug page:
.. exception:: TemplateSyntaxError
``backend``
The template backend instance from which the exception originated.
``tried``
A list of sources that were tried when finding the template. This is
formatted as a list of tuples containing ``(origin, status)``, where
``origin`` is an :ref:`origin-like <template-origin-api>` object and
``status`` is a string with the reason the template wasn't found.
``chain``
A list of intermediate :exc:`~django.template.TemplateDoesNotExist`
exceptions raised when trying to load a template. This is used by
functions, such as :func:`~django.template.loader.get_template`, that
try to load a given template from multiple engines.
.. versionadded:: 1.9
The ``backend``, ``tried``, and ``chain`` arguments were added.
.. exception:: TemplateSyntaxError(msg)
This exception is raised when a template was found but contains errors.
This exception is raised when a template was found but contains errors.
...
@@ -478,7 +499,6 @@ fictional ``foobar`` template library::
...
@@ -478,7 +499,6 @@ fictional ``foobar`` template library::
self.engine = foobar.Engine(**options)
self.engine = foobar.Engine(**options)
def from_string(self, template_code):
def from_string(self, template_code):
try:
try:
return Template(self.engine.from_string(template_code))
return Template(self.engine.from_string(template_code))
...
@@ -489,7 +509,7 @@ fictional ``foobar`` template library::
...
@@ -489,7 +509,7 @@ fictional ``foobar`` template library::
try:
try:
return Template(self.engine.get_template(template_name))
return Template(self.engine.get_template(template_name))
except foobar.TemplateNotFound as exc:
except foobar.TemplateNotFound as exc:
raise TemplateDoesNotExist(exc.args)
raise TemplateDoesNotExist(exc.args
, backend=self
)
except foobar.TemplateCompilationFailed as exc:
except foobar.TemplateCompilationFailed as exc:
raise TemplateSyntaxError(exc.args)
raise TemplateSyntaxError(exc.args)
...
@@ -510,6 +530,117 @@ fictional ``foobar`` template library::
...
@@ -510,6 +530,117 @@ fictional ``foobar`` template library::
See `DEP 182`_ for more information.
See `DEP 182`_ for more information.
.. _template-debug-integration:
Debug integration for custom engines
------------------------------------
.. versionadded:: 1.9
Debug page integration for non-Django template engines was added.
The Django debug page has hooks to provide detailed information when a template
error arises. Custom template engines can use these hooks to enhance the
traceback information that appears to users. The following hooks are available:
.. _template-postmortem:
Template postmortem
~~~~~~~~~~~~~~~~~~~
The postmortem appears when :exc:`~django.template.TemplateDoesNotExist` is
raised. It lists the template engines and loaders that were used when trying
to find a given template. For example, if two Django engines are configured,
the postmortem will appear like:
.. image:: _images/postmortem.png
Custom engines can populate the postmortem by passing the ``backend`` and
``tried`` arguments when raising :exc:`~django.template.TemplateDoesNotExist`.
Backends that use the postmortem :ref:`should specify an origin
<template-origin-api>` on the template object.
Contextual line information
~~~~~~~~~~~~~~~~~~~~~~~~~~~
If an error happens during template parsing or rendering, Django can display
the line the error happened on. For example:
.. image:: _images/template-lines.png
Custom engines can populate this information by setting a ``template_debug``
attribute on exceptions raised during parsing and rendering. This attribute
is a :class:`dict` with the following values:
* ``'name'``: The name of the template in which the exception occurred.
* ``'message'``: The exception message.
* ``'source_lines'``: The lines before, after, and including the line the
exception occurred on. This is for context, so it shouldn't contain more than
20 lines or so.
* ``'line'``: The line number on which the exception occurred.
* ``'before'``: The content on the error line before the token that raised the
error.
* ``'during'``: The token that raised the error.
* ``'after'``: The content on the error line after the token that raised the
error.
* ``'total'``: The number of lines in ``source_lines``.
* ``'top'``: The line number where ``source_lines`` starts.
* ``'bottom'``: The line number where ``source_lines`` ends.
Given the above template error, ``template_debug`` would look like::
{
'name': '/path/to/template.html',
'message': "Invalid block tag: 'syntax'",
'source_lines': [
(1, 'some\n'),
(2, 'lines\n'),
(3, 'before\n'),
(4, 'Hello {% syntax error %} {{ world }}\n'),
(5, 'some\n'),
(6, 'lines\n'),
(7, 'after\n'),
(8, ''),
],
'line': 4,
'before': 'Hello ',
'during': '{% syntax error %}',
'after': ' {{ world }}\n',
'total': 9,
'bottom': 9,
'top': 1,
}
.. _template-origin-api:
Origin API and 3rd-party integration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Django templates have an :class:`~django.template.base.Origin` object available
through the ``template.origin`` attribute. This enables debug information to be
displayed in the :ref:`template postmortem <template-postmortem>`, as well as
in 3rd-party libraries, like the `Django Debug Toolbar`_.
Custom engines can provide their own ``template.origin`` information by
creating an object that specifies the following attributes:
* ``'name'``: The full path to the template.
* ``'template_name'``: The relative path to the template as passed into the
the template loading methods.
* ``'loader_name'``: An optional string identifying the function or class used
to load the template, e.g. ``django.template.loaders.filesystem.Loader``.
.. currentmodule:: django.template
.. currentmodule:: django.template
.. _template-language-intro:
.. _template-language-intro:
...
@@ -687,3 +818,4 @@ Implementing a custom context processor is as simple as defining a function.
...
@@ -687,3 +818,4 @@ Implementing a custom context processor is as simple as defining a function.
.. _Jinja2: http://jinja.pocoo.org/
.. _Jinja2: http://jinja.pocoo.org/
.. _DEP 182: https://github.com/django/deps/blob/master/accepted/0182-multiple-template-engines.rst
.. _DEP 182: https://github.com/django/deps/blob/master/accepted/0182-multiple-template-engines.rst
.. _Django Debug Toolbar: https://github.com/django-debug-toolbar/django-debug-toolbar
tests/template_backends/jinja2/template_backends/syntax_error2.html
0 → 100644
Dosyayı görüntüle @
adff499e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{% block %}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tests/template_backends/test_dummy.py
Dosyayı görüntüle @
adff499e
...
@@ -37,8 +37,9 @@ class TemplateStringsTests(SimpleTestCase):
...
@@ -37,8 +37,9 @@ class TemplateStringsTests(SimpleTestCase):
self
.
assertEqual
(
content
,
"Hello world!
\n
"
)
self
.
assertEqual
(
content
,
"Hello world!
\n
"
)
def
test_get_template_non_existing
(
self
):
def
test_get_template_non_existing
(
self
):
with
self
.
assertRaises
(
TemplateDoesNotExist
):
with
self
.
assertRaises
(
TemplateDoesNotExist
)
as
e
:
self
.
engine
.
get_template
(
'template_backends/non_existing.html'
)
self
.
engine
.
get_template
(
'template_backends/non_existing.html'
)
self
.
assertEqual
(
e
.
exception
.
backend
,
self
.
engine
)
def
test_get_template_syntax_error
(
self
):
def
test_get_template_syntax_error
(
self
):
# There's no way to trigger a syntax error with the dummy backend.
# There's no way to trigger a syntax error with the dummy backend.
...
...
tests/template_backends/test_jinja2.py
Dosyayı görüntüle @
adff499e
...
@@ -4,6 +4,8 @@ from __future__ import absolute_import
...
@@ -4,6 +4,8 @@ from __future__ import absolute_import
from
unittest
import
skipIf
from
unittest
import
skipIf
from
django.template
import
TemplateSyntaxError
from
.test_dummy
import
TemplateStringsTests
from
.test_dummy
import
TemplateStringsTests
try
:
try
:
...
@@ -22,6 +24,16 @@ class Jinja2Tests(TemplateStringsTests):
...
@@ -22,6 +24,16 @@ class Jinja2Tests(TemplateStringsTests):
backend_name
=
'jinja2'
backend_name
=
'jinja2'
options
=
{
'keep_trailing_newline'
:
True
}
options
=
{
'keep_trailing_newline'
:
True
}
def
test_origin
(
self
):
template
=
self
.
engine
.
get_template
(
'template_backends/hello.html'
)
self
.
assertTrue
(
template
.
origin
.
name
.
endswith
(
'hello.html'
))
self
.
assertEqual
(
template
.
origin
.
template_name
,
'template_backends/hello.html'
)
def
test_origin_from_string
(
self
):
template
=
self
.
engine
.
from_string
(
'Hello!
\n
'
)
self
.
assertEqual
(
template
.
origin
.
name
,
'<template>'
)
self
.
assertEqual
(
template
.
origin
.
template_name
,
None
)
def
test_self_context
(
self
):
def
test_self_context
(
self
):
"""
"""
Using 'self' in the context should not throw errors (#24538).
Using 'self' in the context should not throw errors (#24538).
...
@@ -32,3 +44,33 @@ class Jinja2Tests(TemplateStringsTests):
...
@@ -32,3 +44,33 @@ class Jinja2Tests(TemplateStringsTests):
template
=
self
.
engine
.
from_string
(
'hello {{ foo }}!'
)
template
=
self
.
engine
.
from_string
(
'hello {{ foo }}!'
)
content
=
template
.
render
(
context
=
{
'self'
:
'self'
,
'foo'
:
'world'
})
content
=
template
.
render
(
context
=
{
'self'
:
'self'
,
'foo'
:
'world'
})
self
.
assertEqual
(
content
,
'hello world!'
)
self
.
assertEqual
(
content
,
'hello world!'
)
def
test_exception_debug_info_min_context
(
self
):
with
self
.
assertRaises
(
TemplateSyntaxError
)
as
e
:
self
.
engine
.
get_template
(
'template_backends/syntax_error.html'
)
debug
=
e
.
exception
.
template_debug
self
.
assertEqual
(
debug
[
'after'
],
''
)
self
.
assertEqual
(
debug
[
'before'
],
''
)
self
.
assertEqual
(
debug
[
'during'
],
'{
%
block
%
}'
)
self
.
assertEqual
(
debug
[
'bottom'
],
1
)
self
.
assertEqual
(
debug
[
'top'
],
0
)
self
.
assertEqual
(
debug
[
'line'
],
1
)
self
.
assertEqual
(
debug
[
'total'
],
1
)
self
.
assertEqual
(
len
(
debug
[
'source_lines'
]),
1
)
self
.
assertTrue
(
debug
[
'name'
]
.
endswith
(
'syntax_error.html'
))
self
.
assertTrue
(
'message'
in
debug
)
def
test_exception_debug_info_max_context
(
self
):
with
self
.
assertRaises
(
TemplateSyntaxError
)
as
e
:
self
.
engine
.
get_template
(
'template_backends/syntax_error2.html'
)
debug
=
e
.
exception
.
template_debug
self
.
assertEqual
(
debug
[
'after'
],
''
)
self
.
assertEqual
(
debug
[
'before'
],
''
)
self
.
assertEqual
(
debug
[
'during'
],
'{
%
block
%
}'
)
self
.
assertEqual
(
debug
[
'bottom'
],
26
)
self
.
assertEqual
(
debug
[
'top'
],
5
)
self
.
assertEqual
(
debug
[
'line'
],
16
)
self
.
assertEqual
(
debug
[
'total'
],
31
)
self
.
assertEqual
(
len
(
debug
[
'source_lines'
]),
21
)
self
.
assertTrue
(
debug
[
'name'
]
.
endswith
(
'syntax_error2.html'
))
self
.
assertTrue
(
'message'
in
debug
)
tests/template_loader/tests.py
Dosyayı görüntüle @
adff499e
...
@@ -36,9 +36,10 @@ class TemplateLoaderTests(SimpleTestCase):
...
@@ -36,9 +36,10 @@ class TemplateLoaderTests(SimpleTestCase):
with
self
.
assertRaises
(
TemplateDoesNotExist
)
as
e
:
with
self
.
assertRaises
(
TemplateDoesNotExist
)
as
e
:
get_template
(
"template_loader/unknown.html"
)
get_template
(
"template_loader/unknown.html"
)
self
.
assertEqual
(
self
.
assertEqual
(
e
.
exception
.
tried
[
-
1
][
0
]
.
template_name
,
e
.
exception
.
chain
[
-
1
]
.
tried
[
0
][
0
]
.
template_name
,
'template_loader/unknown.html'
,
'template_loader/unknown.html'
,
)
)
self
.
assertEqual
(
e
.
exception
.
chain
[
-
1
]
.
backend
.
name
,
'django'
)
def
test_select_template_first_engine
(
self
):
def
test_select_template_first_engine
(
self
):
template
=
select_template
([
"template_loader/unknown.html"
,
template
=
select_template
([
"template_loader/unknown.html"
,
...
@@ -64,13 +65,15 @@ class TemplateLoaderTests(SimpleTestCase):
...
@@ -64,13 +65,15 @@ class TemplateLoaderTests(SimpleTestCase):
select_template
([
"template_loader/unknown.html"
,
select_template
([
"template_loader/unknown.html"
,
"template_loader/missing.html"
])
"template_loader/missing.html"
])
self
.
assertEqual
(
self
.
assertEqual
(
e
.
exception
.
tried
[
0
][
0
]
.
template_name
,
e
.
exception
.
chain
[
0
]
.
tried
[
0
][
0
]
.
template_name
,
'template_loader/unknown.html'
,
'template_loader/unknown.html'
,
)
)
self
.
assertEqual
(
e
.
exception
.
chain
[
0
]
.
backend
.
name
,
'dummy'
)
self
.
assertEqual
(
self
.
assertEqual
(
e
.
exception
.
tried
[
-
1
][
0
]
.
template_name
,
e
.
exception
.
chain
[
-
1
]
.
tried
[
0
][
0
]
.
template_name
,
'template_loader/missing.html'
,
'template_loader/missing.html'
,
)
)
self
.
assertEqual
(
e
.
exception
.
chain
[
-
1
]
.
backend
.
name
,
'django'
)
def
test_select_template_tries_all_engines_before_names
(
self
):
def
test_select_template_tries_all_engines_before_names
(
self
):
template
=
select_template
([
"template_loader/goodbye.html"
,
template
=
select_template
([
"template_loader/goodbye.html"
,
...
@@ -98,9 +101,10 @@ class TemplateLoaderTests(SimpleTestCase):
...
@@ -98,9 +101,10 @@ class TemplateLoaderTests(SimpleTestCase):
with
self
.
assertRaises
(
TemplateDoesNotExist
)
as
e
:
with
self
.
assertRaises
(
TemplateDoesNotExist
)
as
e
:
render_to_string
(
"template_loader/unknown.html"
)
render_to_string
(
"template_loader/unknown.html"
)
self
.
assertEqual
(
self
.
assertEqual
(
e
.
exception
.
tried
[
-
1
][
0
]
.
template_name
,
e
.
exception
.
chain
[
-
1
]
.
tried
[
0
][
0
]
.
template_name
,
'template_loader/unknown.html'
,
'template_loader/unknown.html'
,
)
)
self
.
assertEqual
(
e
.
exception
.
chain
[
-
1
]
.
backend
.
name
,
'django'
)
def
test_render_to_string_with_list_first_engine
(
self
):
def
test_render_to_string_with_list_first_engine
(
self
):
content
=
render_to_string
([
"template_loader/unknown.html"
,
content
=
render_to_string
([
"template_loader/unknown.html"
,
...
@@ -126,13 +130,25 @@ class TemplateLoaderTests(SimpleTestCase):
...
@@ -126,13 +130,25 @@ class TemplateLoaderTests(SimpleTestCase):
render_to_string
([
"template_loader/unknown.html"
,
render_to_string
([
"template_loader/unknown.html"
,
"template_loader/missing.html"
])
"template_loader/missing.html"
])
self
.
assertEqual
(
self
.
assertEqual
(
e
.
exception
.
tried
[
0
][
0
]
.
template_name
,
e
.
exception
.
chain
[
0
]
.
tried
[
0
][
0
]
.
template_name
,
'template_loader/unknown.html'
,
'template_loader/unknown.html'
,
)
)
self
.
assertEqual
(
e
.
exception
.
chain
[
0
]
.
backend
.
name
,
'dummy'
)
self
.
assertEqual
(
self
.
assertEqual
(
e
.
exception
.
tried
[
-
1
][
0
]
.
template_name
,
e
.
exception
.
chain
[
1
]
.
tried
[
0
][
0
]
.
template_name
,
'template_loader/unknown.html'
,
)
self
.
assertEqual
(
e
.
exception
.
chain
[
1
]
.
backend
.
name
,
'django'
)
self
.
assertEqual
(
e
.
exception
.
chain
[
2
]
.
tried
[
0
][
0
]
.
template_name
,
'template_loader/missing.html'
,
)
self
.
assertEqual
(
e
.
exception
.
chain
[
2
]
.
backend
.
name
,
'dummy'
)
self
.
assertEqual
(
e
.
exception
.
chain
[
3
]
.
tried
[
0
][
0
]
.
template_name
,
'template_loader/missing.html'
,
'template_loader/missing.html'
,
)
)
self
.
assertEqual
(
e
.
exception
.
chain
[
3
]
.
backend
.
name
,
'django'
)
def
test_render_to_string_with_list_tries_all_engines_before_names
(
self
):
def
test_render_to_string_with_list_tries_all_engines_before_names
(
self
):
content
=
render_to_string
([
"template_loader/goodbye.html"
,
content
=
render_to_string
([
"template_loader/goodbye.html"
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment