Merge branch 'master' of github.com:pecan/pecan
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
[run]
|
||||
omit = pecan/commands/*.py, pecan/templates/__init__.py
|
||||
omit = pecan/commands/*.py, pecan/templates/__init__.py, pecan/testing.py
|
||||
|
||||
24
LICENSE
Normal file
24
LICENSE
Normal file
@@ -0,0 +1,24 @@
|
||||
Copyright (c) <2011>, Jonathan LaCour
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -48,9 +48,9 @@ copyright = u'2010, Jonathan LaCour'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.0.1'
|
||||
version = '0.1.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.0.1'
|
||||
release = '0.1.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
.. _templates:
|
||||
|
||||
Templating in Pecan
|
||||
===================
|
||||
|
||||
Pecan supports a variety of templating engines out of the box, and also provides
|
||||
the ability to easily add support for new template engines. Currently, Pecan
|
||||
supports the following templating engines:
|
||||
|
||||
* `Mako <http://www.makotemplates.org/>`_
|
||||
* `Genshi <http://genshi.edgewall.org/>`_
|
||||
* `Kajiki <http://kajiki.pythonisito.com/>`_
|
||||
* `Jinja2 <http://jinja.pocoo.org/>`_
|
||||
* `JSON`
|
||||
|
||||
The default template system is `mako`, but can be configured by passing the
|
||||
``default_renderer`` key in your app configuration::
|
||||
|
||||
app = {
|
||||
'default_renderer' : 'kajiki',
|
||||
# ...
|
||||
}
|
||||
|
||||
The available renderer type strings are `mako`, `genshi`, `kajiki`, `jinja`,
|
||||
and `json`.
|
||||
|
||||
|
||||
Using Renderers
|
||||
---------------
|
||||
|
||||
:ref:`pecan_decorators` defines a decorator called ``@expose``, which is used
|
||||
to flag a method as a controller. The ``@expose`` decorator takes a variety of
|
||||
parameters, including a ``template`` argument, which is the path to the template
|
||||
file to use for that controller. A controller will use the default template
|
||||
engine, unless the path is prefixed by another renderer name::
|
||||
|
||||
class MyController(object):
|
||||
@expose('path/to/mako/template.html')
|
||||
def index(self):
|
||||
return dict(message='I am a mako template')
|
||||
|
||||
@expose('kajiki:path/to/kajiki/template.html')
|
||||
def my_controller(self):
|
||||
return dict(message='I am a kajiki template')
|
||||
|
||||
For more information on the expose decorator, refer to :ref:`pecan_decorators`,
|
||||
:ref:`pecan_core`, and :ref:`routing`.
|
||||
|
||||
|
||||
Template Overrides and Manual Rendering
|
||||
---------------------------------------
|
||||
|
||||
The :ref:`pecan_core` module contains two useful helper functions related to
|
||||
templating. The first is ``override_template``, which allows you to overrides
|
||||
which template is used in your controller, and the second is ``render``, which
|
||||
allows you to manually render output using the Pecan templating framework.
|
||||
|
||||
To use ``override_template``, simply call it within the body of your controller
|
||||
|
||||
::
|
||||
|
||||
class MyController(object):
|
||||
@expose('template_one.html')
|
||||
def index(self):
|
||||
# ...
|
||||
override_template('template_two.html')
|
||||
return dict(message='I will now render with template_two.html')
|
||||
|
||||
The ``render`` helper is also quite simple to use::
|
||||
|
||||
@expose()
|
||||
def controller(self):
|
||||
return render('my_template.html', dict(message='I am the namespace'))
|
||||
|
||||
|
||||
The JSON Renderer
|
||||
-----------------
|
||||
|
||||
Pecan provides a `JSON` renderer by simple passing ``@expose('json')``. For
|
||||
more information on using `JSON` in Pecan, please refer to :ref:`jsonify` and
|
||||
:ref:`pecan_jsonify`.
|
||||
|
||||
|
||||
Custom Renderers
|
||||
----------------
|
||||
|
||||
To define a custom renderer, you simply create a class that follows a simple
|
||||
protocol::
|
||||
|
||||
class MyRenderer(object):
|
||||
def __init__(self, path, extra_vars):
|
||||
'''
|
||||
Your renderer is provided with a path to templates,
|
||||
as configured by your application, and any extra
|
||||
template variables, also as configured
|
||||
'''
|
||||
pass
|
||||
|
||||
def render(self, template_path, namespace):
|
||||
'''
|
||||
Lookup the template based on the path, and render
|
||||
your output based upon the supplied namespace
|
||||
dictionary, as returned from the controller.
|
||||
'''
|
||||
return str(namespace)
|
||||
|
||||
|
||||
To enable your custom renderer, you can define a ``custom_renderers`` key In
|
||||
your application's configuration::
|
||||
|
||||
app = {
|
||||
'custom_renderers' : {
|
||||
'my_renderer' : MyRenderer
|
||||
},
|
||||
# ...
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ def make_app(root, static_root=None, debug=False, errorcfg={}, wrap_app=None, lo
|
||||
'''
|
||||
|
||||
'''
|
||||
if hasattr(conf, 'requestviewer'):
|
||||
existing_hooks = kw.get('hooks', [])
|
||||
existing_hooks.append(RequestViewerHook(conf.requestviewer))
|
||||
kw['hooks'] = existing_hooks
|
||||
|
||||
app = Pecan(root, **kw)
|
||||
if wrap_app:
|
||||
@@ -39,8 +43,4 @@ def make_app(root, static_root=None, debug=False, errorcfg={}, wrap_app=None, lo
|
||||
app = Cascade([StaticURLParser(static_root), app])
|
||||
if isinstance(logging, dict) or logging == True:
|
||||
app = TransLogger(app, **(isinstance(logging, dict) and logging or {}))
|
||||
if hasattr(conf, 'requestviewer'):
|
||||
existing_hooks = kw.get('hooks', [])
|
||||
existing_hooks.append(RequestViewerHook(conf.requestviewer))
|
||||
kw['hooks'] = existing_hooks
|
||||
return app
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import cgi
|
||||
|
||||
__all__ = ['RendererFactory']
|
||||
|
||||
_builtin_renderers = {}
|
||||
error_formatters = []
|
||||
|
||||
|
||||
4
setup.py
4
setup.py
@@ -1,6 +1,6 @@
|
||||
from setuptools import setup, Command, find_packages
|
||||
|
||||
version = '0.1'
|
||||
version = '0.1.0'
|
||||
|
||||
#
|
||||
# integration with py.test for `python setup.py test`
|
||||
@@ -46,7 +46,7 @@ except:
|
||||
setup(
|
||||
name = 'pecan',
|
||||
version = version,
|
||||
description = "A WSGI object-dispatching web framework, in the spirit of TurboGears, only much much smaller, with many fewer dependencies.",
|
||||
description = "A WSGI object-dispatching web framework, designed to be lean and fast, with few dependancies.",
|
||||
long_description = None,
|
||||
classifiers = [],
|
||||
keywords = '',
|
||||
|
||||
@@ -4,6 +4,7 @@ from pecan.core import state
|
||||
from pecan.hooks import PecanHook, TransactionHook, HookController, RequestViewerHook
|
||||
from pecan.configuration import Config
|
||||
from pecan.decorators import transactional, after_commit
|
||||
from copy import copy
|
||||
from formencode import Schema, validators
|
||||
from webtest import TestApp
|
||||
|
||||
@@ -1065,6 +1066,21 @@ class TestTransactionHook(object):
|
||||
|
||||
|
||||
class TestRequestViewerHook(object):
|
||||
|
||||
def test_hook_from_config(self):
|
||||
from pecan.configuration import _runtime_conf as conf
|
||||
conf['requestviewer'] = {
|
||||
'blacklist': ['/favicon.ico']
|
||||
}
|
||||
|
||||
class RootController(object):
|
||||
pass
|
||||
|
||||
app = make_app(RootController())
|
||||
while hasattr(app, 'application'):
|
||||
app = app.application
|
||||
del conf.__values__['requestviewer']
|
||||
assert app.hooks
|
||||
|
||||
def test_basic_single_default_hook(self):
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
from unittest import TestCase
|
||||
|
||||
from pecan.templating import RendererFactory
|
||||
from pecan.templating import RendererFactory, format_line_context
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
class TestTemplate(TestCase):
|
||||
def setUp(self):
|
||||
@@ -26,3 +29,19 @@ class TestTemplate(TestCase):
|
||||
|
||||
self.assertEqual(extra_vars.make_ns({'bar':2}), {'foo':1, 'bar':2})
|
||||
self.assertEqual(extra_vars.make_ns({'foo':2}), {'foo':2})
|
||||
|
||||
|
||||
class TestTemplateLineFormat(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.f = tempfile.NamedTemporaryFile()
|
||||
|
||||
def tearDown(self):
|
||||
os.remove(self.f.name)
|
||||
|
||||
def test_format_line_context(self):
|
||||
for i in range(11):
|
||||
self.f.write('Testing Line %d\n' % i)
|
||||
self.f.flush()
|
||||
|
||||
assert format_line_context(self.f.name, 0).count('Testing Line') == 10
|
||||
|
||||
Reference in New Issue
Block a user