summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark McClain <mark.mcclain@shootq.com>2011-09-02 15:55:01 -0400
committerMark McClain <mark.mcclain@shootq.com>2011-09-02 15:55:01 -0400
commit615f6a1c55ab41724db6d08c99ba1a0884c7abc4 (patch)
tree72ffe6f387b3bfbf004ea2d5bed71e46821b8911
parent3385dec6b5b22494723df90523a9bd53da2a9d7a (diff)
parentf2bbbd8e56c308ff0eba7ca44b48cef046db0851 (diff)
downloadpecan-615f6a1c55ab41724db6d08c99ba1a0884c7abc4.tar.gz
Merge branch 'master' of github.com:pecan/pecan
-rw-r--r--.coveragerc2
-rw-r--r--LICENSE24
-rw-r--r--docs/source/conf.py4
-rw-r--r--docs/source/templates.rst116
-rw-r--r--pecan/__init__.py8
-rw-r--r--pecan/configuration.py3
-rw-r--r--pecan/templating.py2
-rw-r--r--setup.py4
-rw-r--r--tests/test_hooks.py16
-rw-r--r--tests/test_templating.py21
10 files changed, 185 insertions, 15 deletions
diff --git a/.coveragerc b/.coveragerc
index ec9a6c5..1e3fc17 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,2 +1,2 @@
[run]
-omit = pecan/commands/*.py, pecan/templates/__init__.py
+omit = pecan/commands/*.py, pecan/templates/__init__.py, pecan/testing.py
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c47247d
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/docs/source/conf.py b/docs/source/conf.py
index df96d30..36e7d20 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -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.
diff --git a/docs/source/templates.rst b/docs/source/templates.rst
index e69de29..7ba33e1 100644
--- a/docs/source/templates.rst
+++ b/docs/source/templates.rst
@@ -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
+ },
+ # ...
+ }
diff --git a/pecan/__init__.py b/pecan/__init__.py
index 20ff37a..cadbcef 100644
--- a/pecan/__init__.py
+++ b/pecan/__init__.py
@@ -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
diff --git a/pecan/configuration.py b/pecan/configuration.py
index 45a2007..7844ccd 100644
--- a/pecan/configuration.py
+++ b/pecan/configuration.py
@@ -175,9 +175,6 @@ def import_module(conf):
:param conf: The string to the configuration. Automatically strips off ".py" file extensions.
'''
- if conf.endswith('.py'):
- conf = conf[:-3]
-
if '.' in conf:
parts = conf.split('.')
name = '.'.join(parts[:-1])
diff --git a/pecan/templating.py b/pecan/templating.py
index a69248a..eedfe7a 100644
--- a/pecan/templating.py
+++ b/pecan/templating.py
@@ -1,7 +1,5 @@
import cgi
-__all__ = ['RendererFactory']
-
_builtin_renderers = {}
error_formatters = []
diff --git a/setup.py b/setup.py
index 9adc27b..2564de6 100644
--- a/setup.py
+++ b/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 = '',
diff --git a/tests/test_hooks.py b/tests/test_hooks.py
index e50d5ec..f40aa44 100644
--- a/tests/test_hooks.py
+++ b/tests/test_hooks.py
@@ -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):
diff --git a/tests/test_templating.py b/tests/test_templating.py
index bfeabcd..64e6e06 100644
--- a/tests/test_templating.py
+++ b/tests/test_templating.py
@@ -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