summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-05-11 03:45:02 +0000
committerChris McDonough <chrism@agendaless.com>2009-05-11 03:45:02 +0000
commit7a0439fe988499b58df7dfcd5dd9cce480b214b6 (patch)
tree0a2fe6995d2e881c6ad85bf471a069cc6dabc86d
parent0ce7d36aea659cc9bdb002d6f994565ebf8efc59 (diff)
downloadpyramid-7a0439fe988499b58df7dfcd5dd9cce480b214b6.tar.gz
pyramid-7a0439fe988499b58df7dfcd5dd9cce480b214b6.tar.bz2
pyramid-7a0439fe988499b58df7dfcd5dd9cce480b214b6.zip
- Added a ``routesalchemy`` Paster template. This paster template
sets up a BFG project that uses SQAlchemy (with SQLite) and uses Routes exclusively to resolve URLs (no traversal root factory is used). This template can be used via ``paster create -t bfg_routesalchemy``.
-rw-r--r--CHANGES.txt9
-rw-r--r--docs/narr/project.rst10
-rw-r--r--repoze/bfg/paster.py5
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/__init__.py2
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml24
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/models.py55
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/run.py_tmpl27
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/templates/mytemplate.pt99
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/default.css380
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img01.gifbin0 -> 3840 bytes
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img02.gifbin0 -> 4689 bytes
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img03.gifbin0 -> 229 bytes
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img04.gifbin0 -> 92 bytes
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/spacer.gifbin0 -> 43 bytes
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/templatelicense.txt243
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/tests.py_tmpl73
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+package+/views.py_tmpl9
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/+project+.ini_tmpl14
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/CHANGES.txt_tmpl3
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/README.txt_tmpl4
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/ez_setup.py276
-rw-r--r--repoze/bfg/paster_templates/routesalchemy/setup.py_tmpl50
-rw-r--r--setup.py1
23 files changed, 1280 insertions, 4 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 64d6309d9..15f05c030 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,15 @@
0.8dev (unreleased)
-------------------
+Features
+--------
+
+- Added a ``routesalchemy`` Paster template. This paster template
+ sets up a BFG project that uses SQAlchemy (with SQLite) and uses
+ Routes exclusively to resolve URLs (no traversal root factory is
+ used). This template can be used via ``paster create -t
+ bfg_routesalchemy``.
+
Documentation
-------------
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index 66ce467f9..f29d67099 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -87,10 +87,12 @@ application's Python code and templates.
name during ``paster create`` by adding the project name to the
command line, e.g. ``paster create -t bfg_starter MyProject``.
-.. note:: A convenience :term:`Paste` template for projects which will
- depend on :term:`ZODB` projects also exists. Use ``paster create
- -t bfg_zodb`` to use this template instead of the ``bfg_starter``
- template.
+.. note:: Convenience :term:`Paste` templates for projects which will
+ depend on :term:`ZODB` or `SQLAlchemy
+ <http://www.sqlalchemy.org/>`_ also exist. Use ``paster create -t
+ bfg_zodb`` to create a project that depends on ZODB. Use ``paster
+ create -t bfg_routesalchemy`` to create a project that depends on
+ SQLAlchemy+Routes.
Installing your Newly Created Project for Development
-----------------------------------------------------
diff --git a/repoze/bfg/paster.py b/repoze/bfg/paster.py
index dfe94ae0f..9b52a6df4 100644
--- a/repoze/bfg/paster.py
+++ b/repoze/bfg/paster.py
@@ -10,3 +10,8 @@ class ZODBProjectTemplate(Template):
_template_dir = 'paster_templates/zodb'
summary = 'repoze.bfg ZODB starter project'
template_renderer = staticmethod(paste_script_template_renderer)
+
+class RoutesAlchemyProjectTemplate(Template):
+ _template_dir = 'paster_templates/routesalchemy'
+ summary = 'repoze.bfg SQLAlchemy project using Routes (no traversal)'
+ template_renderer = staticmethod(paste_script_template_renderer)
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/__init__.py b/repoze/bfg/paster_templates/routesalchemy/+package+/__init__.py
new file mode 100644
index 000000000..cbdfd3ac6
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/__init__.py
@@ -0,0 +1,2 @@
+# A package
+
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml b/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml
new file mode 100644
index 000000000..aca4566bc
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml
@@ -0,0 +1,24 @@
+<configure xmlns="http://namespaces.repoze.org/bfg">
+
+ <!-- this must be included for the view declarations to work -->
+ <include package="repoze.bfg.includes" />
+
+ <subscriber for="repoze.bfg.interfaces.INewRequest"
+ handler=".run.handle_teardown"/>
+
+ <route path=""
+ name="home"
+ factory=".models.get_root_model"
+ />
+
+ <view
+ for=".models.Model"
+ view=".views.my_view"
+ />
+
+ <view
+ view=".views.static_view"
+ name="static"
+ />
+
+</configure>
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/models.py b/repoze/bfg/paster_templates/routesalchemy/+package+/models.py
new file mode 100644
index 000000000..48409753e
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/models.py
@@ -0,0 +1,55 @@
+import transaction
+
+from sqlalchemy import create_engine
+from sqlalchemy import Column
+from sqlalchemy import Integer
+from sqlalchemy import MetaData
+from sqlalchemy import Table
+from sqlalchemy import Unicode
+
+from sqlalchemy.exc import IntegrityError
+
+from sqlalchemy.orm import scoped_session
+from sqlalchemy.orm import sessionmaker
+from sqlalchemy.orm import mapper
+
+from zope.sqlalchemy import ZopeTransactionExtension
+
+DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
+
+metadata = MetaData()
+
+def get_root_model(**kw):
+ session = DBSession()
+ root = session.query(Model).filter(Model.name==u'root').first()
+ return root
+
+class Model(object):
+ def __init__(self, name=''):
+ self.name = name
+
+models_table = Table(
+ 'models',
+ metadata,
+ Column('id', Integer, primary_key=True),
+ Column('name', Unicode(255), unique=True),
+ )
+
+models_mapper = mapper(Model, models_table)
+
+def populate():
+ session = DBSession()
+ model = Model(name=u'root')
+ session.add(model)
+ session.flush()
+ transaction.commit()
+
+def initialize_sql(db_string, echo=False):
+ engine = create_engine(db_string, echo=echo)
+ DBSession.configure(bind=engine)
+ metadata.bind = engine
+ metadata.create_all(engine)
+ try:
+ populate()
+ except IntegrityError:
+ pass
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/run.py_tmpl b/repoze/bfg/paster_templates/routesalchemy/+package+/run.py_tmpl
new file mode 100644
index 000000000..1746fe332
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/run.py_tmpl
@@ -0,0 +1,27 @@
+from repoze.bfg.router import make_app
+
+import {{package}}
+from {{package}}.models import DBSession
+from {{package}}.models import initialize_sql
+
+class Cleanup:
+ def __init__(self, cleaner):
+ self.cleaner = cleaner
+ def __del__(self):
+ self.cleaner()
+
+def handle_teardown(event):
+ environ = event.request.environ
+ environ['usermanager.sasession'] = Cleanup(DBSession.remove)
+
+def app(global_config, **kw):
+ """ This function returns a repoze.bfg.router.Router object.
+
+ It is usually called by the PasteDeploy framework during ``paster serve``.
+ """
+ db_string = kw.get('db_string')
+ if db_string is None:
+ raise ValueError("No 'db_string' value in application configuration.")
+ initialize_sql(db_string)
+ return make_app(None, {{package}}, options=kw)
+
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/mytemplate.pt b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/mytemplate.pt
new file mode 100644
index 000000000..767252554
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/mytemplate.pt
@@ -0,0 +1,99 @@
+<!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">
+ <ul>
+ <li id="search">
+ <h2>Search<br/> <code>repoze.bfg</code> Documentation</h2>
+ <form method="get"
+ action="http://bfg.repoze.org/searchresults">
+ <fieldset>
+ <input type="text" id="q" name="text" value="" />
+ <input type="submit" id="x" value="Search" />
+ </fieldset>
+ </form>
+ </li>
+ <li>
+ <h2><code>repoze.bfg</code> links</h2>
+ <ul>
+ <li><a
+ href="http://docs.repoze.org/bfg/#narrative-documentation">Narrative
+ Documentation</a>
+ </li>
+ <li>
+ <a
+ href="http://docs.repoze.org/bfg/#api-documentation">API
+ Documentation</a>
+ </li>
+ <li>
+ <a
+ href="http://docs.repoze.org/bfg/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a
+ href="http://docs.repoze.org/bfg/#change-history">Change
+ History</a>
+ </li>
+ <li>
+ <a
+ href="http://docs.repoze.org/bfg/#sample-applications">Sample
+ Applications</a>
+ </li>
+ <li>
+ <a
+ href="http://docs.repoze.org/bfg/#support-and-development">Support
+ and Development</a>
+ </li>
+ <li>
+ <a
+ href="irc://irc.freenode.net#repoze">IRC Channel</a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </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>
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/default.css b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/default.css
new file mode 100644
index 000000000..41b3debde
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/default.css
@@ -0,0 +1,380 @@
+/*
+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;
+}
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img01.gif b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img01.gif
new file mode 100644
index 000000000..5f082bd99
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img01.gif
Binary files differ
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img02.gif b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img02.gif
new file mode 100644
index 000000000..45a3ae976
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img02.gif
Binary files differ
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img03.gif b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img03.gif
new file mode 100644
index 000000000..d92ea38f9
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img03.gif
Binary files differ
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img04.gif b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img04.gif
new file mode 100644
index 000000000..950c4af9d
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img04.gif
Binary files differ
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/spacer.gif b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/spacer.gif
new file mode 100644
index 000000000..5bfd67a2d
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/spacer.gif
Binary files differ
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/templatelicense.txt b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/templatelicense.txt
new file mode 100644
index 000000000..ccb6b06ab
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/templatelicense.txt
@@ -0,0 +1,243 @@
+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 <./>
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/tests.py_tmpl b/repoze/bfg/paster_templates/routesalchemy/+package+/tests.py_tmpl
new file mode 100644
index 000000000..c52ab2762
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/tests.py_tmpl
@@ -0,0 +1,73 @@
+import unittest
+
+from repoze.bfg import testing
+
+class ViewTests(unittest.TestCase):
+
+ """ These tests are unit tests for the view. They test the
+ functionality of *only* the view. They register and use dummy
+ implementations of repoze.bfg functionality to allow you to avoid
+ testing 'too much'"""
+
+ def setUp(self):
+ """ cleanUp() is required to clear out the application registry
+ between tests (done in setUp for good measure too)
+ """
+ testing.cleanUp()
+
+ def tearDown(self):
+ """ cleanUp() is required to clear out the application registry
+ between tests
+ """
+ testing.cleanUp()
+
+ def test_my_view(self):
+ from {{package}}.views import my_view
+ context = testing.DummyModel()
+ request = testing.DummyRequest()
+ renderer = testing.registerDummyRenderer('templates/mytemplate.pt')
+ response = my_view(context, request)
+ renderer.assert_(project='{{project}}')
+
+class ViewIntegrationTests(unittest.TestCase):
+ """ These tests are integration tests for the view. These test
+ the functionality the view *and* its integration with the rest of
+ the repoze.bfg framework. They cause the entire environment to be
+ set up and torn down as if your application was running 'for
+ real'. This is a heavy-hammer way of making sure that your tests
+ have enough context to run properly, and it tests your view's
+ integration with the rest of BFG. You should not use this style
+ of test to perform 'true' unit testing as tests will run faster
+ and will be easier to write if you use the testing facilities
+ provided by bfg and only the registrations you need, as in the
+ above ViewTests.
+ """
+ def setUp(self):
+ """ This sets up the application registry with the
+ registrations your application declares in its configure.zcml
+ (including dependent registrations for repoze.bfg itself).
+ """
+ testing.cleanUp()
+ import {{package}}
+ import zope.configuration.xmlconfig
+ zope.configuration.xmlconfig.file('configure.zcml',
+ package={{package}})
+
+ def tearDown(self):
+ """ Clear out the application registry """
+ testing.cleanUp()
+
+ def test_my_view(self):
+ from {{package}}.views import my_view
+ context = testing.DummyModel()
+ request = testing.DummyRequest()
+ result = my_view(context, request)
+ self.assertEqual(result.status, '200 OK')
+ body = result.app_iter[0]
+ self.failUnless('Welcome to' in body)
+ self.assertEqual(len(result.headerlist), 2)
+ self.assertEqual(result.headerlist[0],
+ ('Content-Type', 'text/html; charset=UTF-8'))
+ self.assertEqual(result.headerlist[1], ('Content-Length',
+ str(len(body))))
+
diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/views.py_tmpl b/repoze/bfg/paster_templates/routesalchemy/+package+/views.py_tmpl
new file mode 100644
index 000000000..9c5fe7a67
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+package+/views.py_tmpl
@@ -0,0 +1,9 @@
+from repoze.bfg.chameleon_zpt import render_template_to_response
+from repoze.bfg.view import static
+
+static_view = static('templates/static')
+
+def my_view(context, request):
+ return render_template_to_response('templates/mytemplate.pt',
+ request = request,
+ project = '{{project}}')
diff --git a/repoze/bfg/paster_templates/routesalchemy/+project+.ini_tmpl b/repoze/bfg/paster_templates/routesalchemy/+project+.ini_tmpl
new file mode 100644
index 000000000..04eaedec5
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/+project+.ini_tmpl
@@ -0,0 +1,14 @@
+[DEFAULT]
+debug = true
+
+[app:main]
+use = egg:{{project}}#app
+reload_templates = true
+debug_authorization = false
+debug_notfound = false
+db_string = sqlite:///%(here)s/{{package}}.db
+
+[server:main]
+use = egg:Paste#http
+host = 0.0.0.0
+port = 6543
diff --git a/repoze/bfg/paster_templates/routesalchemy/CHANGES.txt_tmpl b/repoze/bfg/paster_templates/routesalchemy/CHANGES.txt_tmpl
new file mode 100644
index 000000000..1544cf53b
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/CHANGES.txt_tmpl
@@ -0,0 +1,3 @@
+0.1
+
+ Initial version
diff --git a/repoze/bfg/paster_templates/routesalchemy/README.txt_tmpl b/repoze/bfg/paster_templates/routesalchemy/README.txt_tmpl
new file mode 100644
index 000000000..0ddebfc3e
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/README.txt_tmpl
@@ -0,0 +1,4 @@
+{{project}} README
+
+
+
diff --git a/repoze/bfg/paster_templates/routesalchemy/ez_setup.py b/repoze/bfg/paster_templates/routesalchemy/ez_setup.py
new file mode 100644
index 000000000..d24e845e5
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/ez_setup.py
@@ -0,0 +1,276 @@
+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+ from ez_setup import use_setuptools
+ use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c9"
+DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+ 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+ 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+ 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+ 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+ 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+ 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+ 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+ 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+ 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+ 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+ 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+ 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+ 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+ 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+ 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+ 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
+ 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
+ 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
+ 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
+ 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
+ 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
+ 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
+ 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
+ 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
+ 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
+ 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
+ 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
+ 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
+ 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
+ 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
+ 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
+ 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
+ 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
+ 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
+}
+
+import sys, os
+try: from hashlib import md5
+except ImportError: from md5 import md5
+
+def _validate_md5(egg_name, data):
+ if egg_name in md5_data:
+ digest = md5(data).hexdigest()
+ if digest != md5_data[egg_name]:
+ print >>sys.stderr, (
+ "md5 validation of %s failed! (Possible download problem?)"
+ % egg_name
+ )
+ sys.exit(2)
+ return data
+
+def use_setuptools(
+ version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+ download_delay=15
+):
+ """Automatically find/download setuptools and make it available on sys.path
+
+ `version` should be a valid setuptools version number that is available
+ as an egg for download under the `download_base` URL (which should end with
+ a '/'). `to_dir` is the directory where setuptools will be downloaded, if
+ it is not already available. If `download_delay` is specified, it should
+ be the number of seconds that will be paused before initiating a download,
+ should one be required. If an older version of setuptools is installed,
+ this routine will print a message to ``sys.stderr`` and raise SystemExit in
+ an attempt to abort the calling script.
+ """
+ was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
+ def do_download():
+ egg = download_setuptools(version, download_base, to_dir, download_delay)
+ sys.path.insert(0, egg)
+ import setuptools; setuptools.bootstrap_install_from = egg
+ try:
+ import pkg_resources
+ except ImportError:
+ return do_download()
+ try:
+ pkg_resources.require("setuptools>="+version); return
+ except pkg_resources.VersionConflict, e:
+ if was_imported:
+ print >>sys.stderr, (
+ "The required version of setuptools (>=%s) is not available, and\n"
+ "can't be installed while this script is running. Please install\n"
+ " a more recent version first, using 'easy_install -U setuptools'."
+ "\n\n(Currently using %r)"
+ ) % (version, e.args[0])
+ sys.exit(2)
+ else:
+ del pkg_resources, sys.modules['pkg_resources'] # reload ok
+ return do_download()
+ except pkg_resources.DistributionNotFound:
+ return do_download()
+
+def download_setuptools(
+ version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+ delay = 15
+):
+ """Download setuptools from a specified location and return its filename
+
+ `version` should be a valid setuptools version number that is available
+ as an egg for download under the `download_base` URL (which should end
+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
+ `delay` is the number of seconds to pause before an actual download attempt.
+ """
+ import urllib2, shutil
+ egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
+ url = download_base + egg_name
+ saveto = os.path.join(to_dir, egg_name)
+ src = dst = None
+ if not os.path.exists(saveto): # Avoid repeated downloads
+ try:
+ from distutils import log
+ if delay:
+ log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help). I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+ %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+ version, download_base, delay, url
+ ); from time import sleep; sleep(delay)
+ log.warn("Downloading %s", url)
+ src = urllib2.urlopen(url)
+ # Read/write all in one block, so we don't create a corrupt file
+ # if the download is interrupted.
+ data = _validate_md5(egg_name, src.read())
+ dst = open(saveto,"wb"); dst.write(data)
+ finally:
+ if src: src.close()
+ if dst: dst.close()
+ return os.path.realpath(saveto)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def main(argv, version=DEFAULT_VERSION):
+ """Install or upgrade setuptools and EasyInstall"""
+ try:
+ import setuptools
+ except ImportError:
+ egg = None
+ try:
+ egg = download_setuptools(version, delay=0)
+ sys.path.insert(0,egg)
+ from setuptools.command.easy_install import main
+ return main(list(argv)+[egg]) # we're done here
+ finally:
+ if egg and os.path.exists(egg):
+ os.unlink(egg)
+ else:
+ if setuptools.__version__ == '0.0.1':
+ print >>sys.stderr, (
+ "You have an obsolete version of setuptools installed. Please\n"
+ "remove it from your system entirely before rerunning this script."
+ )
+ sys.exit(2)
+
+ req = "setuptools>="+version
+ import pkg_resources
+ try:
+ pkg_resources.require(req)
+ except pkg_resources.VersionConflict:
+ try:
+ from setuptools.command.easy_install import main
+ except ImportError:
+ from easy_install import main
+ main(list(argv)+[download_setuptools(delay=0)])
+ sys.exit(0) # try to force an exit
+ else:
+ if argv:
+ from setuptools.command.easy_install import main
+ main(argv)
+ else:
+ print "Setuptools version",version,"or greater has been installed."
+ print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+def update_md5(filenames):
+ """Update our built-in md5 registry"""
+
+ import re
+
+ for name in filenames:
+ base = os.path.basename(name)
+ f = open(name,'rb')
+ md5_data[base] = md5(f.read()).hexdigest()
+ f.close()
+
+ data = [" %r: %r,\n" % it for it in md5_data.items()]
+ data.sort()
+ repl = "".join(data)
+
+ import inspect
+ srcfile = inspect.getsourcefile(sys.modules[__name__])
+ f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+ match = re.search("\nmd5_data = {\n([^}]+)}", src)
+ if not match:
+ print >>sys.stderr, "Internal error!"
+ sys.exit(2)
+
+ src = src[:match.start(1)] + repl + src[match.end(1):]
+ f = open(srcfile,'w')
+ f.write(src)
+ f.close()
+
+
+if __name__=='__main__':
+ if len(sys.argv)>2 and sys.argv[1]=='--md5update':
+ update_md5(sys.argv[2:])
+ else:
+ main(sys.argv[1:])
+
+
+
+
+
+
diff --git a/repoze/bfg/paster_templates/routesalchemy/setup.py_tmpl b/repoze/bfg/paster_templates/routesalchemy/setup.py_tmpl
new file mode 100644
index 000000000..76f3ce639
--- /dev/null
+++ b/repoze/bfg/paster_templates/routesalchemy/setup.py_tmpl
@@ -0,0 +1,50 @@
+import os
+
+from ez_setup import use_setuptools
+use_setuptools()
+
+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',
+ 'SQLAlchemy',
+ 'transaction',
+ 'zope.sqlalchemy',
+ ],
+ tests_require=[
+ 'repoze.bfg',
+ 'SQLAlchemy',
+ 'transaction',
+ 'zope.sqlalchemy',
+ ],
+ test_suite="{{package}}",
+ entry_points = """\
+ [paste.app_factory]
+ app = {{package}}.run:app
+ """
+ )
+
diff --git a/setup.py b/setup.py
index b86b21578..73f5f4660 100644
--- a/setup.py
+++ b/setup.py
@@ -70,6 +70,7 @@ setup(name='repoze.bfg',
[paste.paster_create_template]
bfg_starter=repoze.bfg.paster:StarterProjectTemplate
bfg_zodb=repoze.bfg.paster:ZODBProjectTemplate
+ bfg_routesalchemy=repoze.bfg.paster:RoutesAlchemyProjectTemplate
"""
)