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
22d5f044
Commit
22d5f044
authored
Dec 27, 2011
by
Chris Jerdonek
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch into development: more refactoring around partial-loading.
parents
66eca808
9d2da6ae
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
161 additions
and
104 deletions
+161
-104
pystache/renderer.py
+64
-31
pystache/view.py
+11
-12
tests/test_renderer.py
+85
-60
tests/test_view.py
+1
-1
No files found.
pystache/renderer.py
View file @
22d5f044
...
@@ -16,6 +16,12 @@ from .reader import Reader
...
@@ -16,6 +16,12 @@ from .reader import Reader
from
.renderengine
import
RenderEngine
from
.renderengine
import
RenderEngine
# The quote=True argument causes double quotes to be escaped,
# but not single quotes:
# http://docs.python.org/library/cgi.html#cgi.escape
DEFAULT_ESCAPE
=
lambda
s
:
cgi
.
escape
(
s
,
quote
=
True
)
class
Renderer
(
object
):
class
Renderer
(
object
):
"""
"""
...
@@ -23,36 +29,37 @@ class Renderer(object):
...
@@ -23,36 +29,37 @@ class Renderer(object):
This class supports several rendering options which are described in
This class supports several rendering options which are described in
the constructor's docstring. Among these, the constructor supports
the constructor's docstring. Among these, the constructor supports
passing a custom
template
loader.
passing a custom
partial
loader.
Here is an example of
passing a custom template loader to render a
Here is an example of
rendering a template using a custom partial loader
t
emplate using
partials loaded from a string-string dictionary.
t
hat loads
partials loaded from a string-string dictionary.
>>> partials = {'partial': 'Hello, {{thing}}!'}
>>> partials = {'partial': 'Hello, {{thing}}!'}
>>> renderer = Renderer(
loader
=partials)
>>> renderer = Renderer(
partials
=partials)
>>> renderer.render('{{>partial}}', {'thing': 'world'})
>>> renderer.render('{{>partial}}', {'thing': 'world'})
u'Hello, world!'
u'Hello, world!'
"""
"""
def
__init__
(
self
,
loader
=
None
,
file_encoding
=
None
,
default_encoding
=
None
,
def
__init__
(
self
,
file_encoding
=
None
,
default_encoding
=
None
,
decode_errors
=
'strict'
,
search_dirs
=
None
,
file_extension
=
None
,
decode_errors
=
'strict'
,
search_dirs
=
None
,
file_extension
=
None
,
escape
=
None
):
escape
=
None
,
partials
=
None
):
"""
"""
Construct an instance.
Construct an instance.
Arguments:
Arguments:
loader: the object (e.g. pystache.Loader or dictionary) that will
partials: an object (e.g. pystache.Loader or dictionary) for
load templates during the rendering process, for example when
custom partial loading during the rendering process.
loading a partial.
The object should have a get() method that accepts a string
The loader should have a get() method that accepts a string
and returns the corresponding template as a string, preferably
and returns the corresponding template as a string, preferably
as a unicode string. If there is no template with that name,
as a unicode string. If there is no template with that name,
the method should either return None (as dict.get() does) or
the get() method should either return None (as dict.get() does)
raise an exception.
or raise an exception.
Defaults to constructing a default Loader, but using the
If this argument is None, the rendering process will use
file_encoding and decode_errors arguments.
the normal procedure of locating and reading templates from
the file system -- using the Loader-related instance attributes
like search_dirs, file_encoding, etc.
escape: the function used to escape variable tag values when
escape: the function used to escape variable tag values when
rendering a template. The function should accept a unicode
rendering a template. The function should accept a unicode
...
@@ -96,10 +103,7 @@ class Renderer(object):
...
@@ -96,10 +103,7 @@ class Renderer(object):
default_encoding
=
sys
.
getdefaultencoding
()
default_encoding
=
sys
.
getdefaultencoding
()
if
escape
is
None
:
if
escape
is
None
:
# The quote=True argument causes double quotes to be escaped,
escape
=
DEFAULT_ESCAPE
# but not single quotes:
# http://docs.python.org/library/cgi.html#cgi.escape
escape
=
lambda
s
:
cgi
.
escape
(
s
,
quote
=
True
)
# This needs to be after we set the default default_encoding.
# This needs to be after we set the default default_encoding.
if
file_encoding
is
None
:
if
file_encoding
is
None
:
...
@@ -114,22 +118,12 @@ class Renderer(object):
...
@@ -114,22 +118,12 @@ class Renderer(object):
if
isinstance
(
search_dirs
,
basestring
):
if
isinstance
(
search_dirs
,
basestring
):
search_dirs
=
[
search_dirs
]
search_dirs
=
[
search_dirs
]
# This needs to be after we set some of the defaults above.
if
loader
is
None
:
reader
=
Reader
(
encoding
=
file_encoding
,
decode_errors
=
decode_errors
)
loader
=
Loader
(
reader
=
reader
,
search_dirs
=
search_dirs
,
extension
=
file_extension
)
self
.
decode_errors
=
decode_errors
self
.
decode_errors
=
decode_errors
self
.
default_encoding
=
default_encoding
self
.
default_encoding
=
default_encoding
self
.
escape
=
escape
self
.
escape
=
escape
self
.
file_encoding
=
file_encoding
self
.
file_encoding
=
file_encoding
self
.
file_extension
=
file_extension
self
.
file_extension
=
file_extension
# TODO: we should not store a loader attribute because the loader
self
.
partials
=
partials
# would no longer reflect the current attributes if, say, someone
# changed the search_dirs attribute after instantiation. Instead,
# we should construct the Loader instance each time on the fly,
# as we do with the Reader in the read() method.
self
.
loader
=
loader
self
.
search_dirs
=
search_dirs
self
.
search_dirs
=
search_dirs
def
_to_unicode_soft
(
self
,
s
):
def
_to_unicode_soft
(
self
,
s
):
...
@@ -191,9 +185,39 @@ class Renderer(object):
...
@@ -191,9 +185,39 @@ class Renderer(object):
return
context
return
context
def
_make_reader
(
self
):
"""
Create a Reader instance using current attributes.
"""
return
Reader
(
encoding
=
self
.
file_encoding
,
decode_errors
=
self
.
decode_errors
)
def
_make_loader
(
self
):
"""
Create a Loader instance using current attributes.
"""
reader
=
self
.
_make_reader
()
loader
=
Loader
(
reader
=
reader
,
search_dirs
=
self
.
search_dirs
,
extension
=
self
.
file_extension
)
return
loader
def
_make_load_partial
(
self
):
def
_make_load_partial
(
self
):
"""
Return the load_partial function to pass to RenderEngine.__init__().
"""
if
self
.
partials
is
None
:
loader
=
self
.
_make_loader
()
return
loader
.
get
# Otherwise, create a load_partial function from the custom loader
# that satisfies RenderEngine requirements (and that provides a
# nicer exception, etc).
get_partial
=
self
.
partials
.
get
def
load_partial
(
name
):
def
load_partial
(
name
):
template
=
self
.
loader
.
get
(
name
)
template
=
get_partial
(
name
)
if
template
is
None
:
if
template
is
None
:
# TODO: make a TemplateNotFoundException type that provides
# TODO: make a TemplateNotFoundException type that provides
...
@@ -226,9 +250,18 @@ class Renderer(object):
...
@@ -226,9 +250,18 @@ class Renderer(object):
attributes.
attributes.
"""
"""
reader
=
Reader
(
encoding
=
self
.
file_encoding
,
decode_errors
=
self
.
decode_errors
)
reader
=
self
.
_make_reader
(
)
return
reader
.
read
(
path
)
return
reader
.
read
(
path
)
# TODO: add unit tests for this method.
def
load_template
(
self
,
template_name
):
"""
Load a template by name from the file system.
"""
loader
=
self
.
_make_loader
()
return
loader
.
get
(
template_name
)
def
render_path
(
self
,
template_path
,
context
=
None
,
**
kwargs
):
def
render_path
(
self
,
template_path
,
context
=
None
,
**
kwargs
):
"""
"""
Render the template at the given path using the given context.
Render the template at the given path using the given context.
...
...
pystache/view.py
View file @
22d5f044
...
@@ -25,23 +25,20 @@ class View(object):
...
@@ -25,23 +25,20 @@ class View(object):
_loader
=
None
_loader
=
None
_renderer
=
None
_renderer
=
None
def
__init__
(
self
,
template
=
None
,
context
=
None
,
loader
=
None
,
**
kwargs
):
def
__init__
(
self
,
template
=
None
,
context
=
None
,
partials
=
None
,
**
kwargs
):
"""
"""
Construct a View instance.
Construct a View instance.
Arguments:
Arguments:
loader: the object (e.g. pystache.Loader or dictionary) responsible
partials: the object (e.g. pystache.Loader or dictionary)
for loading templates during the rendering process, for example
responsible for loading partials during the rendering process.
when loading partials. The object should have a get() metho
d
The object should have a get() method that accepts a string an
d
that accepts a string and returns the corresponding template
returns the corresponding template as a string, preferably as a
as a string, preferably as a unicode string. The method should
unicode string. The method should return None if there is no
return None if there is no template with that name
.
template with that name, or raise an exception
.
"""
"""
if
loader
is
not
None
:
self
.
_loader
=
loader
if
template
is
not
None
:
if
template
is
not
None
:
self
.
template
=
template
self
.
template
=
template
...
@@ -51,6 +48,8 @@ class View(object):
...
@@ -51,6 +48,8 @@ class View(object):
if
kwargs
:
if
kwargs
:
_context
.
push
(
kwargs
)
_context
.
push
(
kwargs
)
self
.
_partials
=
partials
self
.
context
=
_context
self
.
context
=
_context
def
_get_renderer
(
self
):
def
_get_renderer
(
self
):
...
@@ -60,7 +59,7 @@ class View(object):
...
@@ -60,7 +59,7 @@ class View(object):
# instantiation some of the attributes on which the Renderer
# instantiation some of the attributes on which the Renderer
# depends. This lets users set the template_extension attribute,
# depends. This lets users set the template_extension attribute,
# etc. after View.__init__() has already been called.
# etc. after View.__init__() has already been called.
renderer
=
Renderer
(
loader
=
self
.
_loader
,
renderer
=
Renderer
(
partials
=
self
.
_partials
,
file_encoding
=
self
.
template_encoding
,
file_encoding
=
self
.
template_encoding
,
search_dirs
=
self
.
template_path
,
search_dirs
=
self
.
template_path
,
file_extension
=
self
.
template_extension
)
file_extension
=
self
.
template_extension
)
...
@@ -76,7 +75,7 @@ class View(object):
...
@@ -76,7 +75,7 @@ class View(object):
if
not
self
.
template
:
if
not
self
.
template
:
template_name
=
self
.
_get_template_name
()
template_name
=
self
.
_get_template_name
()
renderer
=
self
.
_get_renderer
()
renderer
=
self
.
_get_renderer
()
self
.
template
=
renderer
.
load
er
.
get
(
template_name
)
self
.
template
=
renderer
.
load
_template
(
template_name
)
return
self
.
template
return
self
.
template
...
...
tests/test_renderer.py
View file @
22d5f044
...
@@ -23,69 +23,21 @@ class RendererInitTestCase(unittest.TestCase):
...
@@ -23,69 +23,21 @@ class RendererInitTestCase(unittest.TestCase):
"""
"""
def
test_loader
(
self
):
def
test_partials__default
(
self
):
"""
Test that the loader attribute is set correctly.
"""
loader
=
{
'foo'
:
'bar'
}
r
=
Renderer
(
loader
=
loader
)
self
.
assertEquals
(
r
.
loader
,
{
'foo'
:
'bar'
})
def
test_loader__default
(
self
):
"""
"""
Test that the default loader is constructed correctly.
Test that the default loader is constructed correctly.
"""
"""
renderer
=
Renderer
()
renderer
=
Renderer
()
actual
=
renderer
.
loader
self
.
assertTrue
(
renderer
.
partials
is
None
)
expected
=
Loader
()
def
test_partials
(
self
):
self
.
assertEquals
(
type
(
actual
),
type
(
expected
))
self
.
assertEquals
(
actual
.
template_extension
,
expected
.
template_extension
)
self
.
assertEquals
(
actual
.
search_dirs
,
expected
.
search_dirs
)
self
.
assertEquals
(
actual
.
reader
.
__dict__
,
expected
.
reader
.
__dict__
)
def
test_loader__default__encoding
(
self
):
"""
Test that the default loader inherits the correct encoding.
"""
renderer
=
Renderer
(
file_encoding
=
'foo'
)
reader
=
renderer
.
loader
.
reader
self
.
assertEquals
(
reader
.
encoding
,
'foo'
)
def
test_loader__default__decode_errors
(
self
):
"""
"""
Test that the default loader inherits decode_errors.
Test that the loader attribute is set correctly.
"""
renderer
=
Renderer
(
decode_errors
=
'foo'
)
reader
=
renderer
.
loader
.
reader
self
.
assertEquals
(
reader
.
decode_errors
,
'foo'
)
def
test_loader__default__file_extension
(
self
):
"""
Test that the default loader inherits file_extension.
"""
renderer
=
Renderer
(
file_extension
=
'foo'
)
loader
=
renderer
.
loader
self
.
assertEquals
(
loader
.
template_extension
,
'foo'
)
def
test_loader__default__search_dirs
(
self
):
"""
Test that the default loader inherits search_dirs.
"""
"""
renderer
=
Renderer
(
search_dirs
=
'foo'
)
renderer
=
Renderer
(
partials
=
{
'foo'
:
'bar'
})
loader
=
renderer
.
loader
self
.
assertEquals
(
renderer
.
partials
,
{
'foo'
:
'bar'
})
self
.
assertEquals
(
loader
.
search_dirs
,
[
'foo'
])
def
test_escape__default
(
self
):
def
test_escape__default
(
self
):
escape
=
Renderer
()
.
escape
escape
=
Renderer
()
.
escape
...
@@ -264,6 +216,79 @@ class RendererTestCase(unittest.TestCase):
...
@@ -264,6 +216,79 @@ class RendererTestCase(unittest.TestCase):
actual
=
self
.
_read
(
renderer
,
filename
)
actual
=
self
.
_read
(
renderer
,
filename
)
self
.
assertEquals
(
actual
,
'non-ascii: '
)
self
.
assertEquals
(
actual
,
'non-ascii: '
)
## Test the _make_loader() method.
def
test__make_loader__return_type
(
self
):
"""
Test that _make_loader() returns a Loader.
"""
renderer
=
Renderer
()
loader
=
renderer
.
_make_loader
()
self
.
assertEquals
(
type
(
loader
),
Loader
)
def
test__make_loader__file_encoding
(
self
):
"""
Test that _make_loader() respects the file_encoding attribute.
"""
renderer
=
Renderer
()
renderer
.
file_encoding
=
'foo'
loader
=
renderer
.
_make_loader
()
self
.
assertEquals
(
loader
.
reader
.
encoding
,
'foo'
)
def
test__make_loader__decode_errors
(
self
):
"""
Test that _make_loader() respects the decode_errors attribute.
"""
renderer
=
Renderer
()
renderer
.
decode_errors
=
'foo'
loader
=
renderer
.
_make_loader
()
self
.
assertEquals
(
loader
.
reader
.
decode_errors
,
'foo'
)
def
test__make_loader__file_extension
(
self
):
"""
Test that _make_loader() respects the file_extension attribute.
"""
renderer
=
Renderer
()
renderer
.
file_extension
=
'foo'
loader
=
renderer
.
_make_loader
()
self
.
assertEquals
(
loader
.
template_extension
,
'foo'
)
def
test__make_loader__search_dirs
(
self
):
"""
Test that _make_loader() respects the search_dirs attribute.
"""
renderer
=
Renderer
()
renderer
.
search_dirs
=
[
'foo'
]
loader
=
renderer
.
_make_loader
()
self
.
assertEquals
(
loader
.
search_dirs
,
[
'foo'
])
# This test is a sanity check. Strictly speaking, it shouldn't
# be necessary based on our tests above.
def
test__make_loader__default
(
self
):
renderer
=
Renderer
()
actual
=
renderer
.
_make_loader
()
expected
=
Loader
()
self
.
assertEquals
(
type
(
actual
),
type
(
expected
))
self
.
assertEquals
(
actual
.
template_extension
,
expected
.
template_extension
)
self
.
assertEquals
(
actual
.
search_dirs
,
expected
.
search_dirs
)
self
.
assertEquals
(
actual
.
reader
.
__dict__
,
expected
.
reader
.
__dict__
)
## Test the render() method.
## Test the render() method.
def
test_render__return_type
(
self
):
def
test_render__return_type
(
self
):
...
@@ -354,8 +379,8 @@ class RendererTestCase(unittest.TestCase):
...
@@ -354,8 +379,8 @@ class RendererTestCase(unittest.TestCase):
Test the _make_load_partial() method.
Test the _make_load_partial() method.
"""
"""
partials
=
{
'foo'
:
'bar'
}
renderer
=
Renderer
()
renderer
=
Renderer
(
loader
=
partials
)
renderer
.
partials
=
{
'foo'
:
'bar'
}
load_partial
=
renderer
.
_make_load_partial
()
load_partial
=
renderer
.
_make_load_partial
()
actual
=
load_partial
(
'foo'
)
actual
=
load_partial
(
'foo'
)
...
@@ -370,12 +395,12 @@ class RendererTestCase(unittest.TestCase):
...
@@ -370,12 +395,12 @@ class RendererTestCase(unittest.TestCase):
"""
"""
renderer
=
Renderer
()
renderer
=
Renderer
()
renderer
.
loader
=
{
'partial'
:
'foo'
}
renderer
.
partials
=
{
'partial'
:
'foo'
}
load_partial
=
renderer
.
_make_load_partial
()
load_partial
=
renderer
.
_make_load_partial
()
self
.
assertEquals
(
load_partial
(
"partial"
),
"foo"
)
self
.
assertEquals
(
load_partial
(
"partial"
),
"foo"
)
# Now with a value that is already unicode.
# Now with a value that is already unicode.
renderer
.
loader
=
{
'partial'
:
u'foo'
}
renderer
.
partials
=
{
'partial'
:
u'foo'
}
load_partial
=
renderer
.
_make_load_partial
()
load_partial
=
renderer
.
_make_load_partial
()
# If the next line failed, we would get the following error:
# If the next line failed, we would get the following error:
# TypeError: decoding Unicode is not supported
# TypeError: decoding Unicode is not supported
...
@@ -415,7 +440,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase):
...
@@ -415,7 +440,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase):
renderer
=
Renderer
()
renderer
=
Renderer
()
renderer
.
default_encoding
=
'ascii'
renderer
.
default_encoding
=
'ascii'
renderer
.
loader
=
{
'str'
:
'foo'
,
'subclass'
:
MyUnicode
(
'abc'
)}
renderer
.
partials
=
{
'str'
:
'foo'
,
'subclass'
:
MyUnicode
(
'abc'
)}
engine
=
renderer
.
_make_render_engine
()
engine
=
renderer
.
_make_render_engine
()
...
@@ -434,7 +459,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase):
...
@@ -434,7 +459,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase):
"""
"""
renderer
=
Renderer
()
renderer
=
Renderer
()
renderer
.
loader
=
{}
renderer
.
partials
=
{}
engine
=
renderer
.
_make_render_engine
()
engine
=
renderer
.
_make_render_engine
()
load_partial
=
engine
.
load_partial
load_partial
=
engine
.
load_partial
...
...
tests/test_view.py
View file @
22d5f044
...
@@ -53,7 +53,7 @@ class ViewTestCase(unittest.TestCase):
...
@@ -53,7 +53,7 @@ class ViewTestCase(unittest.TestCase):
"""
"""
template
=
"{{>partial}}"
template
=
"{{>partial}}"
partials
=
{
"partial"
:
"Loaded from dictionary"
}
partials
=
{
"partial"
:
"Loaded from dictionary"
}
view
=
Simple
(
template
=
template
,
loader
=
partials
)
view
=
Simple
(
template
=
template
,
partials
=
partials
)
actual
=
view
.
render
()
actual
=
view
.
render
()
self
.
assertEquals
(
actual
,
"Loaded from dictionary"
)
self
.
assertEquals
(
actual
,
"Loaded from dictionary"
)
...
...
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