Commits

Blaise Laflamme committed f100f5b

Added support for module_directory

  • Participants

Comments (0)

Files changed (32)

+0.3 (2010-09-19)
+----------------
+
+- A ``repoze.bfg.mako`` renderer may now be called with a *tuple*,
+  consisting of two values in the form ``(function_name, values)``.
+  If a tuple is provided to a renderer, the ``def`` named after
+  ``function_name`` in the template is rendered instead of the entire
+  template using the ``values`` provided, which must be a dictionary).
+
+0.2 (2010-09-06)
+----------------
+
+- Removed ``repoze.bfg.mako`` functions named ``render_template``,
+  ``render_template_to_response``, ``get_template`` and
+  ``get_renderer``.  As of ``repoze.bfg`` 1.3a6, use of these
+  functions should be replaced with usage of similarly-named functions
+  present in the ``repoze.bfg.renderers`` module .
+
+- Added integration tests for imperative rendering and ZCML loading.
+
+0.1 (2010-07-24)
+----------------
+
+- Initial release.
+
+

File COPYRIGHT.txt

+Copyright (c) 2010 Agendaless Consulting and Contributors.
+(http://www.agendaless.com), All Rights Reserved
+
+License
+
+  A copyright notice accompanies this license document that identifies
+  the copyright holders.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+  1.  Redistributions in source code must retain the accompanying
+      copyright notice, this list of conditions, and the following
+      disclaimer.
+
+  2.  Redistributions in binary form must reproduce the accompanying
+      copyright notice, this list of conditions, and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+  3.  Names of the copyright holders must not be used to endorse or
+      promote products derived from this software without prior
+      written permission from the copyright holders.
+
+  4.  If any files are modified, you must cause the modified files to
+      carry prominent notices stating that you changed the files and
+      the date of any change.
+
+  Disclaimer
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND
+    ANY EXPRESSED 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 THE COPYRIGHT
+    HOLDERS 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.
+
+Metadata-Version: 1.0
+Name: repoze.bfg.mako
+Version: 0.3
+Summary: Mako template bindings for repoze.bfg
+Home-page: http://www.repoze.org
+Author: Chris McDonough / Agendaless Consulting
+Author-email: repoze-dev@lists.repoze.org
+License: BSD-derived (http://www.repoze.org/LICENSE.txt)
+Description: ``repoze.bfg`` bindings for `Mako <http://www.makotemplates.org/>`_
+        ===================================================================
+        
+        These are bindings for the `Mako templating system
+        <http://www.makotemplates.org/>`_ for the `repoze.bfg
+        <http://bfg.repoze.org/`_ web framework.
+        
+        High-Level API
+        --------------
+        
+        The API follows the pattern of the "default" template API for
+        ``repoze.bfg``, which includes three functions: ``get_template``,
+        ``render_template``, and ``render_template_to_response``.  From within
+        a repoze.bfg view function, you might do::
+        
+        from webob import Response
+        
+        from repoze.bfg.renderers import get_renderer
+        template = get_renderer('templates/foo.mak')
+        return Response(template.render_unicode(foo=1))
+        
+        Or::
+        
+        from repoze.bfg.renderers import render
+        s = render('templates/foo.mak', {foo:1})
+        return Response(s)
+        
+        Or::
+        
+        from repoze.bfg.renderers import render_to_response
+        return render_to_response('templates/foo.mak', {foo:1})
+        
+        All of these examples are equivalent.  The first argument passed in to
+        each of them (representing the template location) should be a file
+        path relative to the lookup loader root.
+        
+        The value passed to ``render`` or ``render_to_response`` should be one
+        of the following:
+        
+        - A *dictionary* representing the values passed to the template.
+        
+        - A *tuple*, consisting of two values in the form ``(function_name,
+        values)``.  If a tuple is returned, the ``def`` named after
+        ``function_name`` in the template is rendered instead of the entire
+        template using the ``values`` provided, which must be a dictionary).
+        Positional arguments to the function defined within the template are
+        filled in by name using the same-named values within the ``values``
+        dictionary.
+        
+        Configuring the Loookup Loader
+        ------------------------------
+        
+        In your bfg application's ``.ini`` file, use a ``mako.directories``
+        setting::
+        
+        [app:main]
+        use = egg:mypackage
+        mako.directories = mypackage:templates
+        anotherpackage:templates
+        reload_templates = true
+        debug_authorization = false
+        debug_notfound = false
+        
+        ``mako.directories`` should be a sequence of absolute directory names
+        or resource specifications, one per line.
+        
+        Other values:
+        
+        ``mako.input_encoding``
+        Set the mako template input encoding.  The default is ``utf-8``.
+        
+        ``reload_templates``
+        If this is ``True``, Mako templates will be checked for changes at
+        every rendering boundary.  This slows down rendering but is
+        convenient for development.
+        
+        Ensuring the Mako Renderer Extension is Active
+        ----------------------------------------------
+        
+        ``repoze.bfg.mako`` can also act as a "renderer" for a view when it is
+        active in the ``repoze.bfg`` application you're developing::
+        
+        @bfg_view(renderer='templates/foo.mak')
+        def aview(request):
+        return {'foo':1}
+        
+        There are two ways to make sure that the mako extension is active.
+        Both are completely equivalent.
+        
+        #) Ensure that some ZCML file with an analogue of the following
+        contents is executed::
+        
+        <include package="repoze.bfg.mako"/>
+        
+        #) Call the ``add_renderer`` method of a Configurator in your
+        application:
+        
+        from repoze.bfg.mako import renderer_factory
+        config.add_renderer(.'mak', renderer_factory)
+        config.add_renderer(.'mako', renderer_factory)
+        
+        In either case, files with the ``.mak`` and ``.mako`` extensions are
+        now considered to be Mako templates.
+        
+        Note that when mako is used as a ``renderer`` in this fashion, the
+        ``repoze.bfg`` context that is usually available as ``context`` within
+        the template global namespace is available as ``_context`` (the
+        ``context`` name without the underscore is reserved for internal Mako
+        usage).
+        
+        Installation
+        ------------
+        
+        Install using setuptools, e.g. (within a virtualenv)::
+        
+        $ easy_install -i http://dist.repoze.org/bfg/dev/simple repoze.bfg.mako
+        
+        Creating a Mako ``repoze.bfg`` Project
+        --------------------------------------
+        
+        After you've got ``repoze.bfg.mako`` installed, you can invoke the
+        following command to create a Mako-based ``repoze.bfg`` project::
+        
+        $ paster create -t bin/paster create -t bfg_mako_starter
+        
+        Reporting Bugs / Development Versions
+        -------------------------------------
+        
+        Visit http://bugs.repoze.org to report bugs.  Visit
+        http://svn.repoze.org to download development or tagged versions.
+        
+        
+        
+        
+        0.3 (2010-09-19)
+        ----------------
+        
+        - A ``repoze.bfg.mako`` renderer may now be called with a *tuple*,
+        consisting of two values in the form ``(function_name, values)``.
+        If a tuple is provided to a renderer, the ``def`` named after
+        ``function_name`` in the template is rendered instead of the entire
+        template using the ``values`` provided, which must be a dictionary).
+        
+        0.2 (2010-09-06)
+        ----------------
+        
+        - Removed ``repoze.bfg.mako`` functions named ``render_template``,
+        ``render_template_to_response``, ``get_template`` and
+        ``get_renderer``.  As of ``repoze.bfg`` 1.3a6, use of these
+        functions should be replaced with usage of similarly-named functions
+        present in the ``repoze.bfg.renderers`` module .
+        
+        - Added integration tests for imperative rendering and ZCML loading.
+        
+        0.1 (2010-07-24)
+        ----------------
+        
+        - Initial release.
+        
+        
+        
+Keywords: web wsgi zope
+Platform: UNKNOWN
+Classifier: Intended Audience :: Developers
+Classifier: Programming Language :: Python
+Classifier: Framework :: BFG
+Classifier: License :: Repoze Public License
+``repoze.bfg`` bindings for `Mako <http://www.makotemplates.org/>`_
+===================================================================
+
+These are bindings for the `Mako templating system
+<http://www.makotemplates.org/>`_ for the `repoze.bfg
+<http://bfg.repoze.org/`_ web framework.
+
+High-Level API
+--------------
+
+The API follows the pattern of the "default" template API for
+``repoze.bfg``, which includes three functions: ``get_template``,
+``render_template``, and ``render_template_to_response``.  From within
+a repoze.bfg view function, you might do::
+
+  from webob import Response
+
+  from repoze.bfg.renderers import get_renderer
+  template = get_renderer('templates/foo.mak')
+  return Response(template.render_unicode(foo=1))
+
+Or::
+
+  from repoze.bfg.renderers import render
+  s = render('templates/foo.mak', {foo:1})
+  return Response(s)
+
+Or::
+
+  from repoze.bfg.renderers import render_to_response
+  return render_to_response('templates/foo.mak', {foo:1})
+
+All of these examples are equivalent.  The first argument passed in to
+each of them (representing the template location) should be a file
+path relative to the lookup loader root.
+
+The value passed to ``render`` or ``render_to_response`` should be one
+of the following:
+
+- A *dictionary* representing the values passed to the template.
+
+- A *tuple*, consisting of two values in the form ``(function_name,
+  values)``.  If a tuple is returned, the ``def`` named after
+  ``function_name`` in the template is rendered instead of the entire
+  template using the ``values`` provided, which must be a dictionary).
+  Positional arguments to the function defined within the template are
+  filled in by name using the same-named values within the ``values``
+  dictionary.
+
+Configuring the Loookup Loader
+------------------------------
+
+In your bfg application's ``.ini`` file, use a ``mako.directories``
+setting::
+
+  [app:main]
+  use = egg:mypackage
+  mako.directories = mypackage:templates
+                     anotherpackage:templates
+  reload_templates = true
+  debug_authorization = false
+  debug_notfound = false
+
+``mako.directories`` should be a sequence of absolute directory names
+or resource specifications, one per line.
+
+Other values:
+
+``mako.input_encoding``
+   Set the mako template input encoding.  The default is ``utf-8``.
+
+``reload_templates``
+   If this is ``True``, Mako templates will be checked for changes at
+   every rendering boundary.  This slows down rendering but is
+   convenient for development.
+
+Ensuring the Mako Renderer Extension is Active
+----------------------------------------------
+
+``repoze.bfg.mako`` can also act as a "renderer" for a view when it is
+active in the ``repoze.bfg`` application you're developing::
+
+  @bfg_view(renderer='templates/foo.mak')
+  def aview(request):
+      return {'foo':1}
+
+There are two ways to make sure that the mako extension is active.
+Both are completely equivalent.
+
+#) Ensure that some ZCML file with an analogue of the following
+   contents is executed::
+
+    <include package="repoze.bfg.mako"/>
+
+#) Call the ``add_renderer`` method of a Configurator in your
+   application:
+
+   from repoze.bfg.mako import renderer_factory
+   config.add_renderer(.'mak', renderer_factory)
+   config.add_renderer(.'mako', renderer_factory)
+
+In either case, files with the ``.mak`` and ``.mako`` extensions are
+now considered to be Mako templates.
+
+Note that when mako is used as a ``renderer`` in this fashion, the
+``repoze.bfg`` context that is usually available as ``context`` within
+the template global namespace is available as ``_context`` (the
+``context`` name without the underscore is reserved for internal Mako
+usage).
+
+Installation
+------------
+
+Install using setuptools, e.g. (within a virtualenv)::
+
+  $ easy_install -i http://dist.repoze.org/bfg/dev/simple repoze.bfg.mako
+
+Creating a Mako ``repoze.bfg`` Project
+--------------------------------------
+
+After you've got ``repoze.bfg.mako`` installed, you can invoke the
+following command to create a Mako-based ``repoze.bfg`` project::
+
+  $ paster create -t bin/paster create -t bfg_mako_starter
+
+Reporting Bugs / Development Versions
+-------------------------------------
+
+Visit http://bugs.repoze.org to report bugs.  Visit
+http://svn.repoze.org to download development or tagged versions.
+
+

File repoze/__init__.py

+__import__('pkg_resources').declare_namespace(__name__)

File repoze/bfg/__init__.py

+__import__('pkg_resources').declare_namespace(__name__)

File repoze/bfg/mako/__init__.py

+import os
+import pkg_resources
+
+from zope.interface import implements
+from zope.interface import Interface
+
+from mako.lookup import TemplateLookup
+
+from repoze.bfg.interfaces import ITemplateRenderer
+
+from repoze.bfg.exceptions import ConfigurationError
+from repoze.bfg.threadlocal import get_current_registry
+from repoze.bfg.settings import get_settings
+
+class IMakoLookup(Interface):
+    pass
+
+def renderer_factory(path):
+    registry = get_current_registry()
+    lookup = registry.queryUtility(IMakoLookup)
+    if lookup is None:
+        settings = get_settings() or {}
+        reload_templates = settings.get('reload_templates', False)
+        directories = settings.get('mako.directories')
+        module_directory = settings.get('mako.module_directory')
+        input_encoding = settings.get('mako.input_encoding', 'utf-8')
+        if directories is None:
+            raise ConfigurationError(
+                'Mako template used without a lookup path')
+        directories = directories.splitlines()
+        directories = [ abspath_from_resource_spec(d) for d in directories ]
+        lookup = TemplateLookup(directories=directories,
+                                module_directory=module_directory,
+                                input_encoding=input_encoding,
+                                filesystem_checks=reload_templates)
+        registry.registerUtility(lookup, IMakoLookup)
+    _, path = resolve_resource_spec(path)
+    return MakoLookupTemplateRenderer(path, lookup)
+
+class MakoLookupTemplateRenderer(object):
+    implements(ITemplateRenderer)
+    def __init__(self, path, lookup):
+        self.path = path
+        self.lookup = lookup
+ 
+    def implementation(self):
+        return self.template
+
+    @property
+    def template(self):
+        return self.lookup.get_template(self.path)
+   
+    def __call__(self, value, system):
+        context = system.pop('context', None)
+        if context is not None:
+            system['_context'] = context
+        def_name = None
+        if isinstance(value, tuple):
+            def_name, value = value
+        try:
+            system.update(value)
+        except (TypeError, ValueError):
+            raise ValueError('renderer was passed non-dictionary as value')
+        template = self.template
+        if def_name is not None:
+            template = template.get_def(def_name)
+        result = template.render_unicode(**system)
+        return result
+
+def resolve_resource_spec(spec, pname='__main__'):
+    if os.path.isabs(spec):
+        return None, spec
+    filename = spec
+    if ':' in spec:
+        pname, filename = spec.split(':', 1)
+    elif pname is None:
+        pname, filename = None, spec
+    return pname, filename
+
+def abspath_from_resource_spec(spec, pname='__main__'):
+    if pname is None:
+        return spec
+    pname, filename = resolve_resource_spec(spec, pname)
+    if pname is None:
+        return filename
+    return pkg_resources.resource_filename(pname, filename)

File repoze/bfg/mako/configure.zcml

+<configure xmlns="http://namespaces.repoze.org/bfg">
+
+  <include package="repoze.bfg.includes" file="meta.zcml" />
+
+  <renderer
+     factory="repoze.bfg.mako.renderer_factory"
+     name=".mak"
+     />
+
+  <renderer
+     factory="repoze.bfg.mako.renderer_factory"
+     name=".mako"
+     />
+
+</configure>

File repoze/bfg/mako/paster.py

+from paste.script.templates import Template
+from paste.util.template import paste_script_template_renderer
+
+class MakoProjectTemplate(Template):
+    _template_dir = 'paster_template'
+    summary = 'repoze.bfg mako starter project'
+    template_renderer = staticmethod(paste_script_template_renderer)

File repoze/bfg/mako/paster_template/+package+/__init__.py

+from repoze.bfg.configuration import Configurator
+
+def main(global_config, **kw):
+    """ This function returns a repoze.bfg.router.Router object.  It
+    is usually called by the PasteDeploy framework during ``paster
+    serve``"""
+    config = Configurator(settings=kw)
+    config.begin()
+    config.load_zcml()
+    config.end()
+    return config.make_wsgi_app()
+

File repoze/bfg/mako/paster_template/+package+/configure.zcml

+<configure xmlns="http://namespaces.repoze.org/bfg">
+
+  <include package="repoze.bfg.includes" />
+  <include package="repoze.bfg.mako"/>
+
+  <view
+     view=".views.my_view"
+     renderer="mytemplate.mak"
+     />
+
+  <static
+     name="static"
+     path="templates/static"
+     />
+
+</configure>

File repoze/bfg/mako/paster_template/+package+/templates/mytemplate.mak

+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:tal="http://xml.zope.org/namespaces/tal">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<title>${project} Application</title>
+<meta name="keywords" content="python web application" />
+<meta name="description" content="repoze.bfg web application" />
+<link href="${request.application_url}/static/default.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<!-- start header -->
+<div id="logo">
+  <h2><code>${project}</code>, a <code>repoze.bfg</code> application</h2>
+</div>
+<div id="header">
+  <div id="menu">
+  </div>
+</div>
+<!-- end header -->
+<div id="wrapper">
+  <!-- start page -->
+  <div id="page">
+    <!-- start content -->
+    <div id="content">
+      <div class="post">
+	<h1 class="title">Welcome to <code>${project}</code>, an
+	application generated by the <a
+	href="http://bfg.repoze.org">repoze.bfg</a> web
+	application framework.</h1>
+      </div>
+    </div>
+    <!-- end content -->
+    <!-- start sidebar -->
+    <div id="sidebar"></div>
+    <!-- end sidebar -->
+    <div style="clear: both;">&nbsp;</div>
+  </div>
+</div>
+<!-- end page -->
+<!-- start footer -->
+<div id="footer">
+  <p id="legal">( c ) 2008. All Rights Reserved. Template design
+  by <a href="http://www.freecsstemplates.org/">Free CSS
+  Templates</a>.</p>
+</div>
+<!-- end footer -->
+</body>
+</html>

File repoze/bfg/mako/paster_template/+package+/templates/static/default.css

+/*
+Design by Free CSS Templates
+http://www.freecsstemplates.org
+Released for free under a Creative Commons Attribution 2.5 License
+*/
+
+body {
+	margin: 0;
+	padding: 0;
+	background: url(images/img01.gif) repeat-x left top;
+	font-size: 13px;
+	font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif;
+	text-align: justify;
+	color: #FFFFFF;
+}
+
+h1, h2, h3 {
+	margin: 0;
+	text-transform: lowercase;
+	font-weight: normal;
+	color: #FFFFFF;
+}
+
+h1 {
+	letter-spacing: -1px;
+	font-size: 32px;
+}
+
+h2 {
+	font-size: 23px;
+}
+
+p, ul, ol {
+	margin: 0 0 2em 0;
+	text-align: justify;
+	line-height: 26px;
+}
+
+a:link {
+	color: #8BD80E;
+}
+
+a:hover, a:active {
+	text-decoration: none;
+	color: #8BD80E;
+}
+
+a:visited {
+	color: #8BD80E;
+}
+
+img {
+	border: none;
+}
+
+img.left {
+	float: left;
+	margin-right: 15px;
+}
+
+img.right {
+	float: right;
+	margin-left: 15px;
+}
+
+/* Form */
+
+form {
+	margin: 0;
+	padding: 0;
+}
+
+fieldset {
+	margin: 0;
+	padding: 0;
+	border: none;
+}
+
+legend {
+	display: none;
+}
+
+input, textarea, select {
+	font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
+	font-size: 13px;
+	color: #333333;
+}
+
+#wrapper {
+	margin: 0;
+	padding: 0;
+	background: #000000;
+}
+
+/* Header */
+
+#header {
+	width: 713px;
+	margin: 0 auto;
+	height: 42px;
+}
+
+/* Menu */
+
+#menu {
+	float: left;
+	width: 713px;
+	height: 50px;
+	background: url(images/img02.gif) no-repeat left top;
+}
+
+#menu ul {
+	margin: 0;
+	padding: 0px 0 0 10px;
+	list-style: none;
+	line-height: normal;
+}
+
+#menu li {
+	display: block;
+	float: left;
+}
+
+#menu a {
+	display: block;
+	float: left;
+	background: url(images/img04.gif) no-repeat right 55%;
+	margin-top: 5px;
+	margin-right: 3px;
+	padding: 8px 17px;
+	text-decoration: none;
+	font-size: 13px;
+	color: #000000;
+}
+
+#menu a:hover { 
+	color: #000000;
+}
+
+#menu .current_page_item a {
+	color: #000000;
+}
+
+/** LOGO */
+
+#logo {
+	width: 713px;
+	height: 80px;
+	margin: 0 auto;
+}
+
+#logo h1, #logo h2 {
+	float: left;
+	margin: 0;
+	padding: 30px 0 0 0px;
+	line-height: normal;
+}
+
+#logo h1 { 
+	font-family: Georgia, "Times New Roman", Times, serif;
+	font-size:40px;
+}
+
+#logo h1 a {
+	text-decoration: none;
+	color: #4C4C4C; 
+}
+
+#logo h1 a:hover { text-decoration: underline; }
+
+#logo h2 {
+	float: left;
+	padding: 45px 0 0 18px;
+	font: 18px Georgia, "Times New Roman", Times, serif;
+	color: #8BD80E; 
+}
+
+#logo p a {
+	text-decoration: none;
+	color: #8BD80E;
+}
+
+#logo p a:hover { text-decoration: underline; }
+
+
+
+/* Page */
+
+#page {
+	width: 663px;
+	margin: 0 auto;
+	background: #4C4C4C url(images/img03.gif) no-repeat left bottom;
+	padding: 0 25px;
+}
+
+/* Content */
+
+#content {
+	float: left;
+	width: 410px;
+	
+}
+
+/* Post */
+
+.post {
+	padding: 15px 0px;
+	margin-bottom: 20px;
+}
+
+.post .title {
+	margin-bottom: 20px;
+	padding-bottom: 5px;
+}
+
+.post h1 {
+	padding: 0px 0 0 0px;
+	background: url(images/img08.jpg) no-repeat left top;
+	font-size: 24px;
+	color: #FFFFFF;
+}
+
+.post h2 {
+	padding: 0px 0 0 0px;
+	font-size: 22px;
+	color: #FFFFFF;
+}
+
+.post .entry {
+}
+
+.post .meta {
+	padding: 15px 15px 30px 0px;
+	font-family: Arial, Helvetica, sans-serif;
+	font-size: 11px;
+}
+
+.post .meta p {
+	margin: 0;
+	padding-top: 15px;
+	line-height: normal;
+	color: #FFFFFF;
+}
+
+.post .meta .byline {
+	float: left;
+}
+
+.post .meta .links {
+	float: right;
+}
+
+.post .meta .more {
+	padding: 0 10px 0 18px;
+}
+
+.post .meta .comments {
+}
+
+.post .meta b {
+	display: none;
+}
+
+
+/* Sidebar */
+
+#sidebar {
+	width: 210px;
+	float: right;
+	margin: 0;
+	padding: 0;
+}
+
+#sidebar ul {
+	margin: 0;
+	padding: 0;
+	list-style: none;
+}
+
+#sidebar li {
+	margin-bottom: 40px;
+}
+
+#sidebar li ul {
+}
+
+#sidebar li li {
+	margin: 0;
+}
+
+#sidebar h2 {
+	width: 250px;
+	padding: 8px 0 0 0px;
+	margin-bottom: 10px;
+	background: url(images/img07.jpg) no-repeat left top;
+	font-size: 20px;
+	color: #FFFFFF;
+}
+
+/* Search */
+
+#search {
+
+}
+
+#search h2 {
+	margin-bottom: 20px;
+}
+
+#s {
+	width: 140px;
+	margin-right: 5px;
+	padding: 3px;
+	border: 1px solid #BED99C;
+}
+
+#x {
+	padding: 3px;
+	border: none;
+	background: #8BD80E;
+	text-transform: lowercase;
+	font-size: 11px;
+	color: #FFFFFF;
+}
+
+/* Boxes */
+
+.box1 {
+	padding: 20px;
+}
+
+.box2 {
+	color: #BABABA;
+}
+
+.box2 h2 {
+	margin-bottom: 15px;
+	font-size: 16px;
+	color: #FFFFFF;
+}
+
+.box2 ul {
+	margin: 0;
+	padding: 0;
+	list-style: none;
+}
+
+.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited  {
+	color: #EDEDED;
+}
+
+/* Footer */
+#footer-wrap {
+}
+
+#footer {
+	margin: 0 auto;
+	padding: 20px 0 10px 0;
+	background: #000000;
+}
+
+html>body #footer {
+	height: auto;
+}
+
+#footer p {
+	font-size: 11px;
+}
+
+#legal {
+	clear: both;
+	padding-top: 17px;
+	text-align: center;
+	color: #FFFFFF;
+}
+
+#legal a {
+	font-weight: normal;
+	color: #FFFFFF;
+}

File repoze/bfg/mako/paster_template/+package+/templates/static/images/img01.gif

Added
New image

File repoze/bfg/mako/paster_template/+package+/templates/static/images/img02.gif

Added
New image

File repoze/bfg/mako/paster_template/+package+/templates/static/images/img03.gif

Added
New image

File repoze/bfg/mako/paster_template/+package+/templates/static/images/img04.gif

Added
New image

File repoze/bfg/mako/paster_template/+package+/templates/static/images/spacer.gif

Added
New image

File repoze/bfg/mako/paster_template/+package+/templates/static/templatelicense.txt

+Creative Commons </>
+
+Creative Commons Legal Code
+
+*Attribution 2.5*
+
+CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION
+ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE
+INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ITS USE.
+
+/License/
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE
+RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS
+AND CONDITIONS.
+
+*1. Definitions*
+
+   1. *"Collective Work"* means a work, such as a periodical issue,
+      anthology or encyclopedia, in which the Work in its entirety in
+      unmodified form, along with a number of other contributions,
+      constituting separate and independent works in themselves, are
+      assembled into a collective whole. A work that constitutes a
+      Collective Work will not be considered a Derivative Work (as
+      defined below) for the purposes of this License.
+   2. *"Derivative Work"* means a work based upon the Work or upon the
+      Work and other pre-existing works, such as a translation, musical
+      arrangement, dramatization, fictionalization, motion picture
+      version, sound recording, art reproduction, abridgment,
+      condensation, or any other form in which the Work may be recast,
+      transformed, or adapted, except that a work that constitutes a
+      Collective Work will not be considered a Derivative Work for the
+      purpose of this License. For the avoidance of doubt, where the
+      Work is a musical composition or sound recording, the
+      synchronization of the Work in timed-relation with a moving image
+      ("synching") will be considered a Derivative Work for the purpose
+      of this License.
+   3. *"Licensor"* means the individual or entity that offers the Work
+      under the terms of this License.
+   4. *"Original Author"* means the individual or entity who created the
+      Work.
+   5. *"Work"* means the copyrightable work of authorship offered under
+      the terms of this License.
+   6. *"You"* means an individual or entity exercising rights under this
+      License who has not previously violated the terms of this License
+      with respect to the Work, or who has received express permission
+      from the Licensor to exercise rights under this License despite a
+      previous violation.
+
+*2. Fair Use Rights.* Nothing in this license is intended to reduce,
+limit, or restrict any rights arising from fair use, first sale or other
+limitations on the exclusive rights of the copyright owner under
+copyright law or other applicable laws.
+
+*3. License Grant.* Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
+
+   1. to reproduce the Work, to incorporate the Work into one or more
+      Collective Works, and to reproduce the Work as incorporated in the
+      Collective Works;
+   2. to create and reproduce Derivative Works;
+   3. to distribute copies or phonorecords of, display publicly, perform
+      publicly, and perform publicly by means of a digital audio
+      transmission the Work including as incorporated in Collective Works;
+   4. to distribute copies or phonorecords of, display publicly, perform
+      publicly, and perform publicly by means of a digital audio
+      transmission Derivative Works.
+   5.
+
+      For the avoidance of doubt, where the work is a musical composition:
+
+         1. *Performance Royalties Under Blanket Licenses*. Licensor
+            waives the exclusive right to collect, whether individually
+            or via a performance rights society (e.g. ASCAP, BMI,
+            SESAC), royalties for the public performance or public
+            digital performance (e.g. webcast) of the Work.
+         2. *Mechanical Rights and Statutory Royalties*. Licensor waives
+            the exclusive right to collect, whether individually or via
+            a music rights agency or designated agent (e.g. Harry Fox
+            Agency), royalties for any phonorecord You create from the
+            Work ("cover version") and distribute, subject to the
+            compulsory license created by 17 USC Section 115 of the US
+            Copyright Act (or the equivalent in other jurisdictions).
+   6. *Webcasting Rights and Statutory Royalties*. For the avoidance of
+      doubt, where the Work is a sound recording, Licensor waives the
+      exclusive right to collect, whether individually or via a
+      performance-rights society (e.g. SoundExchange), royalties for the
+      public digital performance (e.g. webcast) of the Work, subject to
+      the compulsory license created by 17 USC Section 114 of the US
+      Copyright Act (or the equivalent in other jurisdictions).
+
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights
+in other media and formats. All rights not expressly granted by Licensor
+are hereby reserved.
+
+*4. Restrictions.*The license granted in Section 3 above is expressly
+made subject to and limited by the following restrictions:
+
+   1. You may distribute, publicly display, publicly perform, or
+      publicly digitally perform the Work only under the terms of this
+      License, and You must include a copy of, or the Uniform Resource
+      Identifier for, this License with every copy or phonorecord of the
+      Work You distribute, publicly display, publicly perform, or
+      publicly digitally perform. You may not offer or impose any terms
+      on the Work that alter or restrict the terms of this License or
+      the recipients' exercise of the rights granted hereunder. You may
+      not sublicense the Work. You must keep intact all notices that
+      refer to this License and to the disclaimer of warranties. You may
+      not distribute, publicly display, publicly perform, or publicly
+      digitally perform the Work with any technological measures that
+      control access or use of the Work in a manner inconsistent with
+      the terms of this License Agreement. The above applies to the Work
+      as incorporated in a Collective Work, but this does not require
+      the Collective Work apart from the Work itself to be made subject
+      to the terms of this License. If You create a Collective Work,
+      upon notice from any Licensor You must, to the extent practicable,
+      remove from the Collective Work any credit as required by clause
+      4(b), as requested. If You create a Derivative Work, upon notice
+      from any Licensor You must, to the extent practicable, remove from
+      the Derivative Work any credit as required by clause 4(b), as
+      requested.
+   2. If you distribute, publicly display, publicly perform, or publicly
+      digitally perform the Work or any Derivative Works or Collective
+      Works, You must keep intact all copyright notices for the Work and
+      provide, reasonable to the medium or means You are utilizing: (i)
+      the name of the Original Author (or pseudonym, if applicable) if
+      supplied, and/or (ii) if the Original Author and/or Licensor
+      designate another party or parties (e.g. a sponsor institute,
+      publishing entity, journal) for attribution in Licensor's
+      copyright notice, terms of service or by other reasonable means,
+      the name of such party or parties; the title of the Work if
+      supplied; to the extent reasonably practicable, the Uniform
+      Resource Identifier, if any, that Licensor specifies to be
+      associated with the Work, unless such URI does not refer to the
+      copyright notice or licensing information for the Work; and in the
+      case of a Derivative Work, a credit identifying the use of the
+      Work in the Derivative Work (e.g., "French translation of the Work
+      by Original Author," or "Screenplay based on original Work by
+      Original Author"). Such credit may be implemented in any
+      reasonable manner; provided, however, that in the case of a
+      Derivative Work or Collective Work, at a minimum such credit will
+      appear where any other comparable authorship credit appears and in
+      a manner at least as prominent as such other comparable authorship
+      credit.
+
+*5. Representations, Warranties and Disclaimer*
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
+OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
+KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
+WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE
+EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+
+*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY
+APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL
+THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY
+DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF
+LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+*7. Termination*
+
+   1. This License and the rights granted hereunder will terminate
+      automatically upon any breach by You of the terms of this License.
+      Individuals or entities who have received Derivative Works or
+      Collective Works from You under this License, however, will not
+      have their licenses terminated provided such individuals or
+      entities remain in full compliance with those licenses. Sections
+      1, 2, 5, 6, 7, and 8 will survive any termination of this License.
+   2. Subject to the above terms and conditions, the license granted
+      here is perpetual (for the duration of the applicable copyright in
+      the Work). Notwithstanding the above, Licensor reserves the right
+      to release the Work under different license terms or to stop
+      distributing the Work at any time; provided, however that any such
+      election will not serve to withdraw this License (or any other
+      license that has been, or is required to be, granted under the
+      terms of this License), and this License will continue in full
+      force and effect unless terminated as stated above.
+
+*8. Miscellaneous*
+
+   1. Each time You distribute or publicly digitally perform the Work or
+      a Collective Work, the Licensor offers to the recipient a license
+      to the Work on the same terms and conditions as the license
+      granted to You under this License.
+   2. Each time You distribute or publicly digitally perform a
+      Derivative Work, Licensor offers to the recipient a license to the
+      original Work on the same terms and conditions as the license
+      granted to You under this License.
+   3. If any provision of this License is invalid or unenforceable under
+      applicable law, it shall not affect the validity or enforceability
+      of the remainder of the terms of this License, and without further
+      action by the parties to this agreement, such provision shall be
+      reformed to the minimum extent necessary to make such provision
+      valid and enforceable.
+   4. No term or provision of this License shall be deemed waived and no
+      breach consented to unless such waiver or consent shall be in
+      writing and signed by the party to be charged with such waiver or
+      consent.
+   5. This License constitutes the entire agreement between the parties
+      with respect to the Work licensed here. There are no
+      understandings, agreements or representations with respect to the
+      Work not specified here. Licensor shall not be bound by any
+      additional provisions that may appear in any communication from
+      You. This License may not be modified without the mutual written
+      agreement of the Licensor and You.
+
+Creative Commons is not a party to this License, and makes no warranty
+whatsoever in connection with the Work. Creative Commons will not be
+liable to You or any party on any legal theory for any damages
+whatsoever, including without limitation any general, special,
+incidental or consequential damages arising in connection to this
+license. Notwithstanding the foregoing two (2) sentences, if Creative
+Commons has expressly identified itself as the Licensor hereunder, it
+shall have all rights and obligations of Licensor.
+
+Except for the limited purpose of indicating to the public that the Work
+is licensed under the CCPL, neither party will use the trademark
+"Creative Commons" or any related trademark or logo of Creative Commons
+without the prior written consent of Creative Commons. Any permitted use
+will be in compliance with Creative Commons' then-current trademark
+usage guidelines, as may be published on its website or otherwise made
+available upon request from time to time.
+
+Creative Commons may be contacted at http://creativecommons.org/
+<http://creativecommons.org>.
+
+� Back to Commons Deed <./>

File repoze/bfg/mako/paster_template/+package+/tests.py_tmpl

+import unittest
+
+from repoze.bfg import testing
+
+class ViewTests(unittest.TestCase):
+
+    def setUp(self):
+        testing.setUp()
+        
+    def tearDown(self):
+        testing.tearDown()
+
+    def test_my_view(self):
+        from {{package}}.views import my_view
+        context = testing.DummyModel()
+        request = testing.DummyRequest()
+        response = my_view(context, request)
+        self.assertEqual(response['project'], '{{project}}')
+

File repoze/bfg/mako/paster_template/+package+/views.py_tmpl

+
+def my_view(context, request):
+    return {'project':'{{project}}'}

File repoze/bfg/mako/paster_template/+project+.ini_tmpl

+[DEFAULT]
+debug = true
+
+[app:main]
+use = egg:{{project}}
+mako.directories = {{project}}:templates
+reload_templates = true
+debug_authorization = false
+debug_notfound = false
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 6543

File repoze/bfg/mako/paster_template/CHANGES.txt_tmpl

+0.1
+---
+
+- Initial version

File repoze/bfg/mako/paster_template/README.txt_tmpl

+{{project}} README
+
+
+

File repoze/bfg/mako/paster_template/setup.py_tmpl

+import os
+
+from setuptools import setup, find_packages
+
+here = os.path.abspath(os.path.dirname(__file__))
+README = open(os.path.join(here, 'README.txt')).read()
+CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
+
+setup(name='{{project}}',
+      version='0.1',
+      description='{{project}}',
+      long_description=README + '\n\n' +  CHANGES,
+      classifiers=[
+        "Development Status :: 3 - Alpha",
+        "Intended Audience :: Developers",
+        "Programming Language :: Python",
+        "Topic :: Internet :: WWW/HTTP",
+        "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
+        "Topic :: Internet :: WWW/HTTP :: WSGI",
+        "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
+        ],
+      author='',
+      author_email='',
+      url='',
+      keywords='web wsgi bfg zope',
+      packages=find_packages(),
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=[
+            'repoze.bfg',
+            ],
+      tests_require=[
+            'repoze.bfg',
+            ],
+      test_suite="{{package}}",
+      entry_points = """\
+      [paste.app_factory]
+      main = {{package}}:main
+      """
+      )
+

File repoze/bfg/mako/tests/__init__.py

+# tests package

File repoze/bfg/mako/tests/templates/helloworld.mak

+## -*- coding: utf-8 -*-
+<% a, b = 'foo', u'föö' %>
+Hello ${u'föö'}

File repoze/bfg/mako/tests/templates/helloworld.mako

+## -*- coding: utf-8 -*-
+<% a, b = 'foo', u'föö' %>
+Hello ${u'föö'}

File repoze/bfg/mako/tests/test_it.py

+## come on python gimme some of that -*- coding: utf-8 -*-
+
+import unittest
+
+class Base(object):
+    def setUp(self):
+        from repoze.bfg.configuration import Configurator
+        self.config = Configurator()
+        self.config.begin()
+        import os
+        here = os.path.abspath(os.path.dirname(__file__))
+        self.templates_dir = os.path.join(here, 'templates')
+
+    def tearDown(self):
+        self.config.end()
+
+class Test_renderer_factory(Base, unittest.TestCase):
+    def _callFUT(self, path):
+        from repoze.bfg.mako import renderer_factory
+        return renderer_factory(path)
+
+    def test_no_directories(self):
+        from repoze.bfg.exceptions import ConfigurationError
+        self.assertRaises(ConfigurationError, self._callFUT, 'path')
+
+    def test_no_lookup(self):
+        from repoze.bfg.mako import IMakoLookup
+        self.config.add_settings({'mako.directories':self.templates_dir})
+        renderer = self._callFUT('helloworld.mak')
+        lookup = self.config.registry.getUtility(IMakoLookup)
+        self.assertEqual(lookup.directories, [self.templates_dir])
+        self.assertEqual(lookup.filesystem_checks, False)
+        self.assertEqual(renderer.path, 'helloworld.mak')
+        self.assertEqual(renderer.lookup, lookup)
+
+    def test_composite_directories_path(self):
+        from repoze.bfg.mako import IMakoLookup
+        twice = self.templates_dir + '\n' + self.templates_dir
+        self.config.add_settings({'mako.directories':twice})
+        self._callFUT('helloworld.mak')
+        lookup = self.config.registry.getUtility(IMakoLookup)
+        self.assertEqual(lookup.directories, [self.templates_dir]*2)
+
+    def test_with_lookup(self):
+        from repoze.bfg.mako import IMakoLookup
+        lookup = dict()
+        self.config.registry.registerUtility(lookup, IMakoLookup)
+        renderer = self._callFUT('helloworld.mak')
+        self.assertEqual(renderer.lookup, lookup)
+        self.assertEqual(renderer.path, 'helloworld.mak')
+
+class MakoLookupTemplateRendererTests(Base, unittest.TestCase):
+    def _getTargetClass(self):
+        from repoze.bfg.mako import MakoLookupTemplateRenderer
+        return MakoLookupTemplateRenderer
+
+    def _makeOne(self, *arg, **kw):
+        klass = self._getTargetClass()
+        return klass(*arg, **kw)
+
+    def test_instance_implements_ITemplate(self):
+        from zope.interface.verify import verifyObject
+        from repoze.bfg.interfaces import ITemplateRenderer
+        verifyObject(ITemplateRenderer, self._makeOne(None, None))
+
+    def test_class_implements_ITemplate(self):
+        from zope.interface.verify import verifyClass
+        from repoze.bfg.interfaces import ITemplateRenderer
+        verifyClass(ITemplateRenderer, self._getTargetClass())
+
+    def test_call(self):
+        lookup = DummyLookup()
+        instance = self._makeOne('path', lookup)
+        result = instance({}, {'system':1})
+        self.failUnless(isinstance(result, unicode))
+        self.assertEqual(result, u'result')
+
+    def test_call_with_system_context(self):
+        # lame
+        lookup = DummyLookup()
+        instance = self._makeOne('path', lookup)
+        result = instance({}, {'context':1})
+        self.failUnless(isinstance(result, unicode))
+        self.assertEqual(result, u'result')
+        self.assertEqual(lookup.values, {'_context':1})
+
+    def test_call_with_tuple_value(self):
+        lookup = DummyLookup()
+        instance = self._makeOne('path', lookup)
+        result = instance(('fub', {}), {'context':1})
+        self.assertEqual(lookup.deffed, 'fub')
+        self.assertEqual(result, u'result')
+        self.assertEqual(lookup.values, {'_context':1})
+
+    def test_call_with_nondict_value(self):
+        lookup = DummyLookup()
+        instance = self._makeOne('path', lookup)
+        self.assertRaises(ValueError, instance, None, {})
+
+    def test_implementation(self):
+        lookup = DummyLookup()
+        instance = self._makeOne('path', lookup)
+        result = instance.implementation().render_unicode()
+        self.failUnless(isinstance(result, unicode))
+        self.assertEqual(result, u'result')
+        
+class Test_resolve_resource_spec(unittest.TestCase):
+    def _callFUT(self, spec, package_name='__main__'):
+        from repoze.bfg.mako import resolve_resource_spec
+        return resolve_resource_spec(spec, package_name)
+
+    def test_abspath(self):
+        import os
+        here = os.path.dirname(__file__)
+        path = os.path.abspath(here)
+        package_name, filename = self._callFUT(path, 'apackage')
+        self.assertEqual(filename, path)
+        self.assertEqual(package_name, None)
+
+    def test_rel_spec(self):
+        pkg = 'repoze.bfg.mako.tests'
+        path = 'test_resource.py'
+        package_name, filename = self._callFUT(path, pkg)
+        self.assertEqual(package_name, 'repoze.bfg.mako.tests')
+        self.assertEqual(filename, 'test_resource.py')
+        
+    def test_abs_spec(self):
+        pkg = 'repoze.bfg.mako.tests'
+        path = 'repoze.bfg.mako.nottests:test_resource.py'
+        package_name, filename = self._callFUT(path, pkg)
+        self.assertEqual(package_name, 'repoze.bfg.mako.nottests')
+        self.assertEqual(filename, 'test_resource.py')
+
+    def test_package_name_is_None(self):
+        pkg = None
+        path = 'test_resource.py'
+        package_name, filename = self._callFUT(path, pkg)
+        self.assertEqual(package_name, None)
+        self.assertEqual(filename, 'test_resource.py')
+        
+class Test_abspath_from_resource_spec(unittest.TestCase):
+    def _callFUT(self, spec, pname='__main__'):
+        from repoze.bfg.mako import abspath_from_resource_spec
+        return abspath_from_resource_spec(spec, pname)
+
+    def test_pname_is_None_before_resolve_resource_spec(self):
+        result = self._callFUT('abc', None)
+        self.assertEqual(result, 'abc')
+
+    def test_pname_is_None_after_resolve_resource_spec(self):
+        result = self._callFUT('/abc', '__main__')
+        self.assertEqual(result, '/abc')
+
+    def test_pkgrelative(self):
+        import os
+        here = os.path.dirname(__file__)
+        path = os.path.abspath(here)
+        result = self._callFUT('abc', 'repoze.bfg.mako.tests')
+        self.assertEqual(result, os.path.join(path, 'abc'))
+
+class TestIntegration(unittest.TestCase):
+    def setUp(self):
+        import repoze.bfg.mako
+        from repoze.bfg.configuration import Configurator
+        self.config = Configurator()
+        self.config.begin()
+        self.config.add_settings({'mako.directories':
+                                'repoze.bfg.mako.tests:templates'})
+        self.config.add_renderer('.mak', repoze.bfg.mako.renderer_factory)
+
+    def tearDown(self):
+        self.config.end()
+
+    def test_render(self):
+        from repoze.bfg.renderers import render
+        result = render('helloworld.mak', {'a':1})
+        self.assertEqual(result, u'\nHello föö\n')
+
+    def test_render_to_response(self):
+        from repoze.bfg.renderers import render_to_response
+        result = render_to_response('helloworld.mak', {'a':1})
+        self.assertEqual(result.ubody, u'\nHello föö\n')
+
+    def test_get_renderer(self):
+        from repoze.bfg.renderers import get_renderer
+        result = get_renderer('helloworld.mak')
+        self.assertEqual(result.implementation().render_unicode(),
+                         u'\nHello föö\n')
+
+class TestZCMLIntegration(unittest.TestCase):
+    def setUp(self):
+        from repoze.bfg.configuration import Configurator
+        self.config = Configurator()
+        self.config.begin()
+        self.config.add_settings({'mako.directories':
+                                'repoze.bfg.mako.tests:templates'})
+        self.config.load_zcml('repoze.bfg.mako:configure.zcml')
+
+    def tearDown(self):
+        self.config.end()
+
+    def test_mak(self):
+        from repoze.bfg.renderers import render
+        result = render('helloworld.mak', {'a':1})
+        self.assertEqual(result, u'\nHello föö\n')
+
+    def test_mako(self):
+        from repoze.bfg.renderers import render
+        result = render('helloworld.mako', {'a':1})
+        self.assertEqual(result, u'\nHello föö\n')
+
+
+class DummyLookup(object):
+    def get_template(self, path):
+        self.path = path
+        return self
+
+    def get_def(self, path):
+        self.deffed = path
+        return self
+
+    def render_unicode(self, **values):
+        self.values = values
+        return u'result'
+        
+[easy_install]
+zip_ok = false
+
+[nosetests]
+cover-package = repoze.bfg.mako
+nocapture = 1
+cover-erase = 1
+where = repoze/bfg/mako
+match = ^test
+
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
+##############################################################################
+#
+# Copyright (c) 2008 Agendaless Consulting and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the BSD-like license at
+# http://www.repoze.org/LICENSE.txt.  A copy of the license should accompany
+# this distribution.  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
+# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
+# FITNESS FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
+__version__ = '0.3'
+
+import os
+
+from setuptools import setup, find_packages
+
+here = os.path.abspath(os.path.dirname(__file__))
+README = open(os.path.join(here, 'README.txt')).read()
+CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
+
+setup(name='repoze.bfg.mako',
+      version=__version__,
+      description='Mako template bindings for repoze.bfg',
+      long_description=README + '\n\n' +  CHANGES,
+      classifiers=[
+        "Intended Audience :: Developers",
+        "Programming Language :: Python",
+        "Framework :: BFG",
+        "License :: Repoze Public License",
+        ],
+      keywords='web wsgi zope',
+      author="Chris McDonough / Agendaless Consulting",
+      author_email="repoze-dev@lists.repoze.org",
+      url="http://www.repoze.org",
+      license="BSD-derived (http://www.repoze.org/LICENSE.txt)",
+      packages=find_packages(),
+      include_package_data=True,
+      namespace_packages=['repoze', 'repoze.bfg'],
+      zip_safe=False,
+      tests_require = ['repoze.bfg', 'Mako'],
+      install_requires=['repoze.bfg', 'Mako'],
+      test_suite="repoze.bfg.mako",
+      entry_points = """\
+        [paste.paster_create_template]
+        bfg_mako_starter=repoze.bfg.mako.paster:MakoProjectTemplate
+      """
+      )
+