Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pystache_custom
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
pystache_custom
Commits
bd304a1d
Commit
bd304a1d
authored
Dec 25, 2011
by
Chris Jerdonek
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix issue #66: "RenderEngine should operate only on unicode strings"
Merry Christmas! :)
parent
e542a896
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
413 additions
and
183 deletions
+413
-183
pystache/renderengine.py
+75
-44
pystache/renderer.py
+55
-49
tests/test_renderengine.py
+117
-33
tests/test_renderer.py
+166
-57
No files found.
pystache/renderengine.py
View file @
bd304a1d
...
@@ -50,6 +50,16 @@ class RenderEngine(object):
...
@@ -50,6 +50,16 @@ class RenderEngine(object):
This class is meant only for internal use by the Template class.
This class is meant only for internal use by the Template class.
As a rule, the code in this class operates on unicode strings where
possible rather than, say, strings of type str or markupsafe.Markup.
This means that strings obtained from "external" sources like partials
and variable tag values are immediately converted to unicode (or
escaped and converted to unicode) before being operated on further.
This makes maintaining, reasoning about, and testing the correctness
of the code much simpler. In particular, it keeps the implementation
of this class independent of the API details of one (or possibly more)
unicode subclasses (e.g. markupsafe.Markup).
"""
"""
tag_re
=
None
tag_re
=
None
otag
=
'{{'
otag
=
'{{'
...
@@ -61,15 +71,30 @@ class RenderEngine(object):
...
@@ -61,15 +71,30 @@ class RenderEngine(object):
"""
"""
Arguments:
Arguments:
load_partial: a function for loading templates by name when
load_partial: the function to call when loading a partial. The
loading partials. The function should accept a template name
function should accept a string template name and return a
and return a unicode template string.
template string of type unicode (not a subclass).
escape: a function that takes a unicode or str string,
literal: the function used to convert unescaped variable tag
converts it to unicode, and escapes and returns it.
values to unicode, e.g. the value corresponding to a tag
"{{{name}}}". The function should accept a string of type
literal: a function that converts a unicode or str string
str or unicode (or a subclass) and return a string of type
to unicode without escaping, and returns it.
unicode (but not a proper subclass of unicode).
This class will only pass basestring instances to this
function. For example, it will call str() on integer variable
values prior to passing them to this function.
escape: the function used to escape and convert variable tag
values to unicode, e.g. the value corresponding to a tag
"{{name}}". The function should obey the same properties
described above for the "literal" function argument.
This function should take care to convert any str
arguments to unicode just as the literal function should, as
this class will not pass tag values to literal prior to passing
them to this function. This allows for more flexibility,
for example using a custom escape function that handles
incoming strings of type markupssafe.Markup differently
from plain unicode strings.
"""
"""
self
.
escape
=
escape
self
.
escape
=
escape
...
@@ -78,9 +103,13 @@ class RenderEngine(object):
...
@@ -78,9 +103,13 @@ class RenderEngine(object):
def
render
(
self
,
template
,
context
):
def
render
(
self
,
template
,
context
):
"""
"""
Return a template rendered as a string with type unicode.
Arguments:
Arguments:
template: a unicode template string.
template: a template string of type unicode (but not a proper
subclass of unicode).
context: a Context instance.
context: a Context instance.
"""
"""
...
@@ -122,7 +151,7 @@ class RenderEngine(object):
...
@@ -122,7 +151,7 @@ class RenderEngine(object):
# Then there was no match.
# Then there was no match.
break
break
start
,
tag_type
,
tag_name
,
template
=
parts
tag_type
,
tag_name
,
template
=
parts
[
1
:]
tag_name
=
tag_name
.
strip
()
tag_name
=
tag_name
.
strip
()
func
=
self
.
modifiers
[
tag_type
]
func
=
self
.
modifiers
[
tag_type
]
...
@@ -133,6 +162,7 @@ class RenderEngine(object):
...
@@ -133,6 +162,7 @@ class RenderEngine(object):
output
.
append
(
tag_value
)
output
.
append
(
tag_value
)
output
=
""
.
join
(
output
)
output
=
""
.
join
(
output
)
return
output
return
output
def
_render_dictionary
(
self
,
template
,
context
):
def
_render_dictionary
(
self
,
template
,
context
):
...
@@ -149,35 +179,45 @@ class RenderEngine(object):
...
@@ -149,35 +179,45 @@ class RenderEngine(object):
return
''
.
join
(
insides
)
return
''
.
join
(
insides
)
@modifiers.set
(
None
)
def
_get_string_context
(
self
,
tag_name
):
def
_render_tag
(
self
,
tag_name
):
"""
"""
Return the value of a variable as an escaped unicode string
.
Get a value from the current context as a basestring instance
.
"""
"""
raw
=
self
.
context
.
get
(
tag_name
,
''
)
val
=
self
.
context
.
get
(
tag_name
)
# For methods with no return value
# We use "==" rather than "is" to compare integers, as using "is"
#
# relies on an implementation detail of CPython. The test about
# We use "==" rather than "is" to compare integers, as using "is" relies
# rendering zeroes failed while using PyPy when using "is".
# on an implementation detail of CPython. The test about rendering
# zeroes failed while using PyPy when using "is".
# See issue #34: https://github.com/defunkt/pystache/issues/34
# See issue #34: https://github.com/defunkt/pystache/issues/34
if
not
raw
and
raw
!=
0
:
if
not
val
and
val
!=
0
:
if
tag_name
==
'.'
:
if
tag_name
!=
'.'
:
raw
=
self
.
context
.
top
()
else
:
return
''
return
''
val
=
self
.
context
.
top
()
# If we don't first convert to a string type, the call to self._unicode_and_escape()
if
not
isinstance
(
val
,
basestring
):
# will yield an error like the following:
val
=
str
(
val
)
#
# TypeError: coercing to Unicode: need string or buffer, ... found
#
if
not
isinstance
(
raw
,
basestring
):
raw
=
str
(
raw
)
return
self
.
escape
(
raw
)
return
val
@modifiers.set
(
None
)
def
_render_escaped
(
self
,
tag_name
):
"""
Return a variable value as an escaped unicode string.
"""
s
=
self
.
_get_string_context
(
tag_name
)
return
self
.
escape
(
s
)
@modifiers.set
(
'{'
)
@modifiers.set
(
'&'
)
def
_render_literal
(
self
,
tag_name
):
"""
Return a variable value as a unicode string (unescaped).
"""
s
=
self
.
_get_string_context
(
tag_name
)
return
self
.
literal
(
s
)
@modifiers.set
(
'!'
)
@modifiers.set
(
'!'
)
def
_render_comment
(
self
,
tag_name
):
def
_render_comment
(
self
,
tag_name
):
...
@@ -185,8 +225,8 @@ class RenderEngine(object):
...
@@ -185,8 +225,8 @@ class RenderEngine(object):
@modifiers.set
(
'>'
)
@modifiers.set
(
'>'
)
def
_render_partial
(
self
,
template_name
):
def
_render_partial
(
self
,
template_name
):
markup
=
self
.
load_partial
(
template_name
)
template
=
self
.
load_partial
(
template_name
)
return
self
.
_render
(
markup
)
return
self
.
_render
(
template
)
@modifiers.set
(
'='
)
@modifiers.set
(
'='
)
def
_change_delimiter
(
self
,
tag_name
):
def
_change_delimiter
(
self
,
tag_name
):
...
@@ -199,20 +239,11 @@ class RenderEngine(object):
...
@@ -199,20 +239,11 @@ class RenderEngine(object):
return
''
return
''
@modifiers.set
(
'{'
)
@modifiers.set
(
'&'
)
def
_render_unescaped
(
self
,
tag_name
):
"""
Render a tag without escaping it.
"""
return
self
.
literal
(
self
.
context
.
get
(
tag_name
,
''
))
def
_render
(
self
,
template
):
def
_render
(
self
,
template
):
"""
"""
Arguments:
Arguments:
template: a
unicode template string
.
template: a
template string with type unicode
.
"""
"""
output
=
[]
output
=
[]
...
...
pystache/renderer.py
View file @
bd304a1d
...
@@ -63,9 +63,10 @@ class Renderer(object):
...
@@ -63,9 +63,10 @@ class Renderer(object):
example "utf-8". See the render() method's documentation for
example "utf-8". See the render() method's documentation for
more information.
more information.
escape: the function used to escape mustache variable values
escape: the function used to escape variable tag values when
when rendering a template. The function should accept a
rendering a template. The function should accept a unicode
unicode string and return an escaped string of the same type.
string (or subclass of unicode) and return an escaped string
that is again unicode (or a subclass of unicode).
This function need not handle strings of type `str` because
This function need not handle strings of type `str` because
this class will only pass it unicode strings. The constructor
this class will only pass it unicode strings. The constructor
assigns this function to the constructed instance's escape()
assigns this function to the constructed instance's escape()
...
@@ -91,14 +92,13 @@ class Renderer(object):
...
@@ -91,14 +92,13 @@ class Renderer(object):
default_encoding
=
sys
.
getdefaultencoding
()
default_encoding
=
sys
.
getdefaultencoding
()
if
escape
is
None
:
if
escape
is
None
:
# TODO: use 'quote=True' with cgi.escape and add tests.
escape
=
markupsafe
.
escape
if
markupsafe
else
cgi
.
escape
escape
=
markupsafe
.
escape
if
markupsafe
else
cgi
.
escape
if
loader
is
None
:
if
loader
is
None
:
loader
=
Loader
(
encoding
=
default_encoding
,
decode_errors
=
decode_errors
)
loader
=
Loader
(
encoding
=
default_encoding
,
decode_errors
=
decode_errors
)
literal
=
markupsafe
.
Markup
if
markupsafe
else
unicode
self
.
_literal
=
markupsafe
.
Markup
if
markupsafe
else
unicode
self
.
_literal
=
literal
self
.
decode_errors
=
decode_errors
self
.
decode_errors
=
decode_errors
self
.
default_encoding
=
default_encoding
self
.
default_encoding
=
default_encoding
...
@@ -106,37 +106,46 @@ class Renderer(object):
...
@@ -106,37 +106,46 @@ class Renderer(object):
self
.
loader
=
loader
self
.
loader
=
loader
self
.
output_encoding
=
output_encoding
self
.
output_encoding
=
output_encoding
def
_unicode_and_escape
(
self
,
s
):
def
_to_unicode_soft
(
self
,
s
):
if
not
isinstance
(
s
,
unicode
):
"""
s
=
self
.
unicode
(
s
)
Convert an str or unicode string to a unicode string (or subclass).
return
self
.
escape
(
s
)
def
unicode
(
self
,
s
):
"""
return
unicode
(
s
,
self
.
default_encoding
,
self
.
decode_errors
)
# Avoid the "double-decoding" TypeError.
return
s
if
isinstance
(
s
,
unicode
)
else
self
.
unicode
(
s
)
def
escape
(
self
,
u
):
def
_to_unicode_hard
(
self
,
s
):
"""
"""
Escape a unicode string, and return it
.
Convert an str or unicode string to a unicode string (not subclass)
.
This function is initialized as the escape function that was passed
"""
to the Template class's constructor when this instance was
return
unicode
(
self
.
_to_unicode_soft
(
s
))
constructed. See the constructor docstring for more information.
def
_escape_to_unicode
(
self
,
s
):
"""
"""
pass
Convert an str or unicode string to unicode, and escape it.
Returns a unicode string (not subclass).
def
literal
(
self
,
s
):
"""
"""
Convert the given string to a unicode string, without escaping it.
return
unicode
(
self
.
escape
(
self
.
_to_unicode_soft
(
s
)))
This function internally calls the built-in function unicode() and
def
unicode
(
self
,
s
):
passes it the default_encoding and decode_errors attributes for this
"""
Template instance. If markupsafe was importable when loading this
Convert a string to unicode, using default_encoding and decode_errors.
module, this function returns an instance of the class
markupsafe.Markup (which subclasses unicode).
Raises:
TypeError: Because this method calls Python's built-in unicode()
function, this method raises the following exception if the
given string is already unicode:
TypeError: decoding Unicode is not supported
"""
"""
return
self
.
_literal
(
self
.
unicode
(
s
))
# TODO: Wrap UnicodeDecodeErrors with a message about setting
# the default_encoding and decode_errors attributes.
return
unicode
(
s
,
self
.
default_encoding
,
self
.
decode_errors
)
def
_make_context
(
self
,
context
,
**
kwargs
):
def
_make_context
(
self
,
context
,
**
kwargs
):
"""
"""
...
@@ -157,21 +166,16 @@ class Renderer(object):
...
@@ -157,21 +166,16 @@ class Renderer(object):
return
context
return
context
def
_make_load_partial
(
self
):
def
_make_load_partial
(
self
):
"""
Return the load_partial function for use by RenderEngine.
"""
def
load_partial
(
name
):
def
load_partial
(
name
):
template
=
self
.
loader
.
get
(
name
)
template
=
self
.
loader
.
get
(
name
)
# Make sure the return value is unicode since RenderEngine requires
# it. Also, check that the string is not already unicode to
# avoid "double-decoding". Otherwise, we would get the following
# error:
# TypeError: decoding Unicode is not supported
if
not
isinstance
(
template
,
unicode
):
template
=
self
.
unicode
(
template
)
return
template
if
template
is
None
:
# TODO: make a TemplateNotFoundException type that provides
# the original loader as an attribute.
raise
Exception
(
"Partial not found with name:
%
s"
%
repr
(
name
))
# RenderEngine requires that the return value be unicode.
return
self
.
_to_unicode_hard
(
template
)
return
load_partial
return
load_partial
...
@@ -183,8 +187,8 @@ class Renderer(object):
...
@@ -183,8 +187,8 @@ class Renderer(object):
load_partial
=
self
.
_make_load_partial
()
load_partial
=
self
.
_make_load_partial
()
engine
=
RenderEngine
(
load_partial
=
load_partial
,
engine
=
RenderEngine
(
load_partial
=
load_partial
,
literal
=
self
.
literal
,
literal
=
self
.
_to_unicode_hard
,
escape
=
self
.
_
unicode_and_escap
e
)
escape
=
self
.
_
escape_to_unicod
e
)
return
engine
return
engine
def
render
(
self
,
template
,
context
=
None
,
**
kwargs
):
def
render
(
self
,
template
,
context
=
None
,
**
kwargs
):
...
@@ -194,30 +198,32 @@ class Renderer(object):
...
@@ -194,30 +198,32 @@ class Renderer(object):
Returns:
Returns:
If the output_encoding attribute is None, the return value is
If the output_encoding attribute is None, the return value is
a unicode string. Otherwise, the return value is encoded to a
markupsafe.Markup if markup was importable and unicode if not.
string of type str using the output encoding named by the
Otherwise, the return value is encoded to a string of type str
output_encoding attribute.
using the output encoding named by the
output_encoding attribute.
Arguments:
Arguments:
template: a template string that is either unicode or of type str.
template: a template string that is either unicode or of type str.
If the string has type str, it is first converted to unicode
If the string has type str, it is first converted to unicode
using th
e default_encoding and decode_errors attributes of thi
s
using th
is instance's default_encoding and decode_error
s
instance
. See the constructor docstring for more information.
attributes
. See the constructor docstring for more information.
context: a dictionary, Context, or object (e.g. a View instance).
context: a dictionary, Context, or object (e.g. a View instance).
**kwargs: additional key values to add to the context when rendering.
**kwargs: additional key values to add to the context when
These values take precedence over the context on any key conflicts.
rendering. These values take precedence over the context on
any key conflicts.
"""
"""
engine
=
self
.
_make_render_engine
()
engine
=
self
.
_make_render_engine
()
context
=
self
.
_make_context
(
context
,
**
kwargs
)
context
=
self
.
_make_context
(
context
,
**
kwargs
)
if
not
isinstance
(
template
,
unicode
):
# RenderEngine.render() requires that the template string be unicode.
template
=
self
.
unicode
(
template
)
template
=
self
.
_to_unicode_hard
(
template
)
rendered
=
engine
.
render
(
template
,
context
)
rendered
=
engine
.
render
(
template
,
context
)
rendered
=
self
.
_literal
(
rendered
)
if
self
.
output_encoding
is
not
None
:
if
self
.
output_encoding
is
not
None
:
rendered
=
rendered
.
encode
(
self
.
output_encoding
)
rendered
=
rendered
.
encode
(
self
.
output_encoding
)
...
...
tests/test_renderengine.py
View file @
bd304a1d
...
@@ -16,25 +16,42 @@ class RenderEngineTestCase(unittest.TestCase):
...
@@ -16,25 +16,42 @@ class RenderEngineTestCase(unittest.TestCase):
"""Test the RenderEngine class."""
"""Test the RenderEngine class."""
def
_engine
(
self
):
def
test_init
(
self
):
"""
"""
Create and return a default RenderEngine for testing
.
Test that __init__() stores all of the arguments correctly
.
"""
"""
to_unicode
=
unicode
# In real-life, these arguments would be functions
engine
=
RenderEngine
(
load_partial
=
"foo"
,
literal
=
"literal"
,
escape
=
"escape"
)
self
.
assertEquals
(
engine
.
escape
,
"escape"
)
self
.
assertEquals
(
engine
.
literal
,
"literal"
)
self
.
assertEquals
(
engine
.
load_partial
,
"foo"
)
escape
=
lambda
s
:
cgi
.
escape
(
to_unicode
(
s
))
literal
=
to_unicode
engine
=
RenderEngine
(
literal
=
literal
,
escape
=
escape
,
load_partial
=
None
)
class
RenderEngineEnderTestCase
(
unittest
.
TestCase
):
"""Test RenderEngine.render()."""
def
_engine
(
self
):
"""
Create and return a default RenderEngine for testing.
"""
escape
=
lambda
s
:
unicode
(
cgi
.
escape
(
s
))
engine
=
RenderEngine
(
literal
=
unicode
,
escape
=
escape
,
load_partial
=
None
)
return
engine
return
engine
def
_assert_render
(
self
,
expected
,
template
,
*
context
,
**
kwargs
):
def
_assert_render
(
self
,
expected
,
template
,
*
context
,
**
kwargs
):
"""
Test rendering the given template using the given context.
"""
partials
=
kwargs
.
get
(
'partials'
)
partials
=
kwargs
.
get
(
'partials'
)
engine
=
kwargs
.
get
(
'engine'
,
self
.
_engine
())
engine
=
kwargs
.
get
(
'engine'
,
self
.
_engine
())
if
partials
is
not
None
:
if
partials
is
not
None
:
engine
.
load_partial
=
lambda
key
:
partials
[
key
]
engine
.
load_partial
=
lambda
key
:
unicode
(
partials
[
key
])
context
=
Context
(
*
context
)
context
=
Context
(
*
context
)
...
@@ -42,22 +59,10 @@ class RenderEngineTestCase(unittest.TestCase):
...
@@ -42,22 +59,10 @@ class RenderEngineTestCase(unittest.TestCase):
self
.
assertEquals
(
actual
,
expected
)
self
.
assertEquals
(
actual
,
expected
)
def
test_init
(
self
):
"""
Test that __init__() stores all of the arguments correctly.
"""
# In real-life, these arguments would be functions
engine
=
RenderEngine
(
load_partial
=
"foo"
,
literal
=
"literal"
,
escape
=
"escape"
)
self
.
assertEquals
(
engine
.
load_partial
,
"foo"
)
self
.
assertEquals
(
engine
.
escape
,
"escape"
)
self
.
assertEquals
(
engine
.
literal
,
"literal"
)
def
test_render
(
self
):
def
test_render
(
self
):
self
.
_assert_render
(
'Hi Mom'
,
'Hi {{person}}'
,
{
'person'
:
'Mom'
})
self
.
_assert_render
(
'Hi Mom'
,
'Hi {{person}}'
,
{
'person'
:
'Mom'
})
def
test_
render_
_load_partial
(
self
):
def
test__load_partial
(
self
):
"""
"""
Test that render() uses the load_template attribute.
Test that render() uses the load_template attribute.
...
@@ -65,25 +70,106 @@ class RenderEngineTestCase(unittest.TestCase):
...
@@ -65,25 +70,106 @@ class RenderEngineTestCase(unittest.TestCase):
engine
=
self
.
_engine
()
engine
=
self
.
_engine
()
partials
=
{
'partial'
:
"{{person}}"
}
partials
=
{
'partial'
:
"{{person}}"
}
engine
.
load_partial
=
lambda
key
:
partials
[
key
]
engine
.
load_partial
=
lambda
key
:
partials
[
key
]
self
.
_assert_render
(
'Hi Mom'
,
'Hi {{>partial}}'
,
{
'person'
:
'Mom'
},
engine
=
engine
)
self
.
_assert_render
(
'Hi Mom'
,
'Hi {{>partial}}'
,
{
'person'
:
'Mom'
},
engine
=
engine
)
def
test_
render_
_literal
(
self
):
def
test__literal
(
self
):
"""
"""
Test that render() uses the literal attribute.
Test that render() uses the literal attribute.
"""
"""
engine
=
self
.
_engine
()
engine
=
self
.
_engine
()
engine
.
literal
=
lambda
s
:
s
.
upper
()
engine
.
literal
=
lambda
s
:
s
.
upper
()
self
.
_assert_render
(
'bar BAR'
,
'{{foo}} {{{foo}}}'
,
{
'foo'
:
'bar'
},
engine
=
engine
)
def
test_render__escape
(
self
):
self
.
_assert_render
(
'BAR'
,
'{{{foo}}}'
,
{
'foo'
:
'bar'
},
engine
=
engine
)
def
test__escape
(
self
):
"""
"""
Test that render() uses the escape attribute.
Test that render() uses the escape attribute.
"""
"""
engine
=
self
.
_engine
()
engine
=
self
.
_engine
()
engine
.
escape
=
lambda
s
:
"**"
+
s
engine
.
escape
=
lambda
s
:
"**"
+
s
self
.
_assert_render
(
'**bar bar'
,
'{{foo}} {{{foo}}}'
,
{
'foo'
:
'bar'
},
engine
=
engine
)
self
.
_assert_render
(
'**bar'
,
'{{foo}}'
,
{
'foo'
:
'bar'
},
engine
=
engine
)
def
test__escape_does_not_call_literal
(
self
):
"""
Test that render() does not call literal before or after calling escape.
"""
engine
=
self
.
_engine
()
engine
.
literal
=
lambda
s
:
s
.
upper
()
# a test version
engine
.
escape
=
lambda
s
:
"**"
+
s
template
=
'literal: {{{foo}}} escaped: {{foo}}'
context
=
{
'foo'
:
'bar'
}
self
.
_assert_render
(
'literal: BAR escaped: **bar'
,
template
,
context
,
engine
=
engine
)
def
test__escape_preserves_unicode_subclasses
(
self
):
"""
Test that render() preserves unicode subclasses when passing to escape.
This is useful, for example, if one wants to respect whether a
variable value is markupsafe.Markup when escaping.
"""
class
MyUnicode
(
unicode
):
pass
def
escape
(
s
):
if
type
(
s
)
is
MyUnicode
:
return
"**"
+
s
else
:
return
s
+
"**"
engine
=
self
.
_engine
()
engine
.
escape
=
escape
template
=
'{{foo1}} {{foo2}}'
context
=
{
'foo1'
:
MyUnicode
(
'bar'
),
'foo2'
:
'bar'
}
self
.
_assert_render
(
'**bar bar**'
,
template
,
context
,
engine
=
engine
)
def
test__non_basestring__literal_and_escaped
(
self
):
"""
Test a context value that is not a basestring instance.
"""
# We use include upper() to make sure we are actually using
# our custom function in the tests
to_unicode
=
lambda
s
:
unicode
(
s
,
encoding
=
'ascii'
)
.
upper
()
engine
=
self
.
_engine
()
engine
.
escape
=
to_unicode
engine
.
literal
=
to_unicode
self
.
assertRaises
(
TypeError
,
engine
.
literal
,
100
)
template
=
'{{text}} {{int}} {{{int}}}'
context
=
{
'int'
:
100
,
'text'
:
'foo'
}
self
.
_assert_render
(
'FOO 100 100'
,
template
,
context
,
engine
=
engine
)
def
test__implicit_iterator__literal
(
self
):
"""
Test an implicit iterator in a literal tag.
"""
template
=
"""{{#test}}{{.}}{{/test}}"""
context
=
{
'test'
:
[
'a'
,
'b'
]}
self
.
_assert_render
(
'ab'
,
template
,
context
)
def
test__implicit_iterator__escaped
(
self
):
"""
Test an implicit iterator in a normal tag.
"""
template
=
"""{{#test}}{{{.}}}{{/test}}"""
context
=
{
'test'
:
[
'a'
,
'b'
]}
self
.
_assert_render
(
'ab'
,
template
,
context
)
def
test_render_with_partial
(
self
):
def
test_render_with_partial
(
self
):
partials
=
{
'partial'
:
"{{person}}"
}
partials
=
{
'partial'
:
"{{person}}"
}
...
@@ -95,13 +181,11 @@ class RenderEngineTestCase(unittest.TestCase):
...
@@ -95,13 +181,11 @@ class RenderEngineTestCase(unittest.TestCase):
"""
"""
engine
=
self
.
_engine
()
engine
=
self
.
_engine
()
engine
.
escape
=
lambda
s
:
"**"
+
s
engine
.
literal
=
lambda
s
:
s
.
upper
()
template
=
'{{#test}}
{{foo}} {{{foo}
}}{{/test}}'
template
=
'{{#test}}
unescaped: {{{foo}}} escaped: {{foo
}}{{/test}}'
context
=
{
'test'
:
{
'foo'
:
'
bar
'
}}
context
=
{
'test'
:
{
'foo'
:
'
<
'
}}
self
.
_assert_render
(
'
**bar BAR
'
,
template
,
context
,
engine
=
engine
)
self
.
_assert_render
(
'
unescaped: < escaped: <
'
,
template
,
context
,
engine
=
engine
)
def
test_render__partial_context_values
(
self
):
def
test_render__partial_context_values
(
self
):
"""
"""
...
@@ -109,12 +193,12 @@ class RenderEngineTestCase(unittest.TestCase):
...
@@ -109,12 +193,12 @@ class RenderEngineTestCase(unittest.TestCase):
"""
"""
engine
=
self
.
_engine
()
engine
=
self
.
_engine
()
engine
.
escape
=
lambda
s
:
"**"
+
s
engine
.
literal
=
lambda
s
:
s
.
upper
()
partials
=
{
'partial'
:
'{{foo}} {{{foo}}}'
}
template
=
'{{>partial}}'
partials
=
{
'partial'
:
'unescaped: {{{foo}}} escaped: {{foo}}'
}
context
=
{
'foo'
:
'<'
}
self
.
_assert_render
(
'
**bar BAR'
,
'{{>partial}}'
,
{
'foo'
:
'bar'
}
,
engine
=
engine
,
partials
=
partials
)
self
.
_assert_render
(
'
unescaped: < escaped: <'
,
template
,
context
,
engine
=
engine
,
partials
=
partials
)
def
test_render__list_referencing_outer_context
(
self
):
def
test_render__list_referencing_outer_context
(
self
):
"""
"""
...
...
tests/test_renderer.py
View file @
bd304a1d
...
@@ -13,6 +13,7 @@ from pystache import renderer
...
@@ -13,6 +13,7 @@ from pystache import renderer
from
pystache.renderer
import
Renderer
from
pystache.renderer
import
Renderer
from
pystache.loader
import
Loader
from
pystache.loader
import
Loader
class
RendererInitTestCase
(
unittest
.
TestCase
):
class
RendererInitTestCase
(
unittest
.
TestCase
):
"""A class to test the Renderer.__init__() method."""
"""A class to test the Renderer.__init__() method."""
...
@@ -54,7 +55,6 @@ class RendererInitTestCase(unittest.TestCase):
...
@@ -54,7 +55,6 @@ class RendererInitTestCase(unittest.TestCase):
self
.
assertEquals
(
actual
.
__dict__
,
expected
.
__dict__
)
self
.
assertEquals
(
actual
.
__dict__
,
expected
.
__dict__
)
class
RendererTestCase
(
unittest
.
TestCase
):
class
RendererTestCase
(
unittest
.
TestCase
):
"""Test the Renderer class."""
"""Test the Renderer class."""
...
@@ -146,13 +146,13 @@ class RendererTestCase(unittest.TestCase):
...
@@ -146,13 +146,13 @@ class RendererTestCase(unittest.TestCase):
renderer
=
Renderer
(
decode_errors
=
"foo"
)
renderer
=
Renderer
(
decode_errors
=
"foo"
)
self
.
assertEquals
(
renderer
.
decode_errors
,
"foo"
)
self
.
assertEquals
(
renderer
.
decode_errors
,
"foo"
)
def
test_unicode
(
self
):
## Test Renderer.unicode().
renderer
=
Renderer
()
actual
=
renderer
.
literal
(
"abc"
)
self
.
assertEquals
(
actual
,
"abc"
)
self
.
assertEquals
(
type
(
actual
),
unicode
)
def
test_unicode__default_encoding
(
self
):
def
test_unicode__default_encoding
(
self
):
"""
Test that the default_encoding attribute is respected.
"""
renderer
=
Renderer
()
renderer
=
Renderer
()
s
=
"é"
s
=
"é"
...
@@ -163,40 +163,20 @@ class RendererTestCase(unittest.TestCase):
...
@@ -163,40 +163,20 @@ class RendererTestCase(unittest.TestCase):
self
.
assertEquals
(
renderer
.
unicode
(
s
),
u"é"
)
self
.
assertEquals
(
renderer
.
unicode
(
s
),
u"é"
)
def
test_unicode__decode_errors
(
self
):
def
test_unicode__decode_errors
(
self
):
renderer
=
Renderer
()
"""
s
=
"é"
Test that the decode_errors attribute is respected.
"""
renderer
=
Renderer
()
renderer
.
default_encoding
=
"ascii"
renderer
.
default_encoding
=
"ascii"
renderer
.
decode_errors
=
"strict"
s
=
"déf"
self
.
assertRaises
(
UnicodeDecodeError
,
renderer
.
unicode
,
s
)
renderer
.
decode_errors
=
"ignore"
self
.
assertEquals
(
renderer
.
unicode
(
s
),
"df"
)
renderer
.
decode_errors
=
"replace"
renderer
.
decode_errors
=
"replace"
# U+FFFD is the official Unicode replacement character.
# U+FFFD is the official Unicode replacement character.
self
.
assertEquals
(
renderer
.
unicode
(
s
),
u'
\ufffd\ufffd
'
)
self
.
assertEquals
(
renderer
.
unicode
(
s
),
u'd
\ufffd\ufffd
f'
)
def
test_literal__with_markupsafe
(
self
):
if
not
self
.
_was_markupsafe_imported
():
# Then we cannot test this case.
return
self
.
_restore_markupsafe
()
_renderer
=
Renderer
()
_renderer
.
default_encoding
=
"utf_8"
# Check the standard case.
actual
=
_renderer
.
literal
(
"abc"
)
self
.
assertEquals
(
actual
,
"abc"
)
self
.
assertEquals
(
type
(
actual
),
renderer
.
markupsafe
.
Markup
)
s
=
"é"
# Check that markupsafe respects default_encoding.
self
.
assertEquals
(
_renderer
.
literal
(
s
),
u"é"
)
_renderer
.
default_encoding
=
"ascii"
self
.
assertRaises
(
UnicodeDecodeError
,
_renderer
.
literal
,
s
)
# Check that markupsafe respects decode_errors.
_renderer
.
decode_errors
=
"replace"
self
.
assertEquals
(
_renderer
.
literal
(
s
),
u'
\ufffd\ufffd
'
)
def
test_render__unicode
(
self
):
def
test_render__unicode
(
self
):
renderer
=
Renderer
()
renderer
=
Renderer
()
...
@@ -314,47 +294,176 @@ class RendererTestCase(unittest.TestCase):
...
@@ -314,47 +294,176 @@ class RendererTestCase(unittest.TestCase):
# TypeError: decoding Unicode is not supported
# TypeError: decoding Unicode is not supported
self
.
assertEquals
(
load_partial
(
"partial"
),
"foo"
)
self
.
assertEquals
(
load_partial
(
"partial"
),
"foo"
)
# By testing that Renderer.render() constructs the RenderEngine instance
# correctly, we no longer need to test the rendering code paths through
# By testing that Renderer.render() constructs the right RenderEngine,
# the Renderer. We can test rendering paths through only the RenderEngine
# we no longer need to exercise all rendering code paths through
# for the same amount of code coverage.
# the Renderer. It suffices to test rendering paths through the
def
test_make_render_engine__load_partial
(
self
):
# RenderEngine for the same amount of code coverage.
class
Renderer_MakeRenderEngineTests
(
unittest
.
TestCase
):
"""
Check the RenderEngine returned by Renderer._make_render_engine().
"""
## Test the engine's load_partial attribute.
def
test__load_partial__returns_unicode
(
self
):
"""
"""
Test that _make_render_engine() constructs and passes load_partial correctly
.
Check that load_partial returns unicode (and not a subclass)
.
"""
"""
partials
=
{
'partial'
:
'foo'
}
class
MyUnicode
(
unicode
):
renderer
=
Renderer
(
loader
=
partials
)
pass
renderer
.
unicode
=
lambda
s
:
s
.
upper
()
# a test version.
renderer
=
Renderer
()
renderer
.
default_encoding
=
'ascii'
renderer
.
loader
=
{
'str'
:
'foo'
,
'subclass'
:
MyUnicode
(
'abc'
)}
engine
=
renderer
.
_make_render_engine
()
engine
=
renderer
.
_make_render_engine
()
# Make sure it calls unicode.
self
.
assertEquals
(
engine
.
load_partial
(
'partial'
),
"FOO"
)
def
test_make_render_engine__literal
(
self
):
actual
=
engine
.
load_partial
(
'str'
)
self
.
assertEquals
(
actual
,
"foo"
)
self
.
assertEquals
(
type
(
actual
),
unicode
)
# Check that unicode subclasses are not preserved.
actual
=
engine
.
load_partial
(
'subclass'
)
self
.
assertEquals
(
actual
,
"abc"
)
self
.
assertEquals
(
type
(
actual
),
unicode
)
def
test__load_partial__not_found
(
self
):
"""
"""
Test that _make_render_engine() passes the right literal
.
Check that load_partial provides a nice message when a template is not found
.
"""
"""
renderer
=
Renderer
()
renderer
=
Renderer
()
renderer
.
l
iteral
=
"foo"
# in real life, this would be a function.
renderer
.
l
oader
=
{}
engine
=
renderer
.
_make_render_engine
()
engine
=
renderer
.
_make_render_engine
()
self
.
assertEquals
(
engine
.
literal
,
"foo"
)
load_partial
=
engine
.
load_partial
def
test_make_render_engine__escape
(
self
):
try
:
load_partial
(
"foo"
)
raise
Exception
(
"Shouldn't get here"
)
except
Exception
,
err
:
self
.
assertEquals
(
str
(
err
),
"Partial not found with name: 'foo'"
)
## Test the engine's literal attribute.
def
test__literal__uses_renderer_unicode
(
self
):
"""
"""
Test that
_make_render_engine() passes the right escape
.
Test that
literal uses the renderer's unicode function
.
"""
"""
renderer
=
Renderer
()
renderer
=
Renderer
()
renderer
.
unicode
=
lambda
s
:
s
.
upper
()
# a test version.
renderer
.
unicode
=
lambda
s
:
s
.
upper
()
renderer
.
escape
=
lambda
s
:
"**"
+
s
# a test version.
engine
=
renderer
.
_make_render_engine
()
literal
=
engine
.
literal
self
.
assertEquals
(
literal
(
"foo"
),
"FOO"
)
def
test__literal__handles_unicode
(
self
):
"""
Test that literal doesn't try to "double decode" unicode.
"""
renderer
=
Renderer
()
renderer
.
default_encoding
=
'ascii'
engine
=
renderer
.
_make_render_engine
()
literal
=
engine
.
literal
self
.
assertEquals
(
literal
(
u"foo"
),
"foo"
)
def
test__literal__returns_unicode
(
self
):
"""
Test that literal returns unicode (and not a subclass).
"""
renderer
=
Renderer
()
renderer
.
default_encoding
=
'ascii'
engine
=
renderer
.
_make_render_engine
()
literal
=
engine
.
literal
self
.
assertEquals
(
type
(
literal
(
"foo"
)),
unicode
)
class
MyUnicode
(
unicode
):
pass
s
=
MyUnicode
(
"abc"
)
self
.
assertEquals
(
type
(
s
),
MyUnicode
)
self
.
assertTrue
(
isinstance
(
s
,
unicode
))
self
.
assertEquals
(
type
(
literal
(
s
)),
unicode
)
## Test the engine's escape attribute.
def
test__escape__uses_renderer_escape
(
self
):
"""
Test that escape uses the renderer's escape function.
"""
renderer
=
Renderer
()
renderer
.
escape
=
lambda
s
:
"**"
+
s
engine
=
renderer
.
_make_render_engine
()
escape
=
engine
.
escape
self
.
assertEquals
(
escape
(
"foo"
),
"**foo"
)
def
test__escape__uses_renderer_unicode
(
self
):
"""
Test that escape uses the renderer's unicode function.
"""
renderer
=
Renderer
()
renderer
.
unicode
=
lambda
s
:
s
.
upper
()
engine
=
renderer
.
_make_render_engine
()
escape
=
engine
.
escape
self
.
assertEquals
(
escape
(
"foo"
),
"FOO"
)
def
test__escape__has_access_to_original_unicode_subclass
(
self
):
"""
Test that escape receives strings with the unicode subclass intact.
"""
renderer
=
Renderer
()
renderer
.
escape
=
lambda
s
:
type
(
s
)
.
__name__
engine
=
renderer
.
_make_render_engine
()
engine
=
renderer
.
_make_render_engine
()
escape
=
engine
.
escape
escape
=
engine
.
escape
self
.
assertEquals
(
escape
(
u"foo"
),
"**foo"
)
class
MyUnicode
(
unicode
):
pass
self
.
assertEquals
(
escape
(
"foo"
),
"unicode"
)
self
.
assertEquals
(
escape
(
u"foo"
),
"unicode"
)
self
.
assertEquals
(
escape
(
MyUnicode
(
"foo"
)),
"MyUnicode"
)
def
test__escape__returns_unicode
(
self
):
"""
Test that literal returns unicode (and not a subclass).
"""
renderer
=
Renderer
()
renderer
.
default_encoding
=
'ascii'
engine
=
renderer
.
_make_render_engine
()
escape
=
engine
.
escape
self
.
assertEquals
(
type
(
escape
(
"foo"
)),
unicode
)
# Check that literal doesn't preserve unicode subclasses.
class
MyUnicode
(
unicode
):
pass
s
=
MyUnicode
(
"abc"
)
self
.
assertEquals
(
type
(
s
),
MyUnicode
)
self
.
assertTrue
(
isinstance
(
s
,
unicode
))
self
.
assertEquals
(
type
(
escape
(
s
)),
unicode
)
# Test that escape converts str strings to unicode first.
self
.
assertEquals
(
escape
(
"foo"
),
"**FOO"
)
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