summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBlaise Laflamme <blaise@laflamme.org>2011-11-26 18:31:22 -0500
committerBlaise Laflamme <blaise@laflamme.org>2011-11-26 18:31:22 -0500
commit7613a6441f273ccc5642e20b2ce5788d01291466 (patch)
tree8de6ce23bf54325b8524c50336020cb509ce1b16
parent11129ca073b4926037368d9a0bf079dc422e28f7 (diff)
parent50e89abccc9c23949fb7cdd928a865712c34aa75 (diff)
downloadpyramid-7613a6441f273ccc5642e20b2ce5788d01291466.tar.gz
pyramid-7613a6441f273ccc5642e20b2ce5788d01291466.tar.bz2
pyramid-7613a6441f273ccc5642e20b2ce5788d01291466.zip
Merge branch 'master' of github.com:Pylons/pyramid
-rw-r--r--CHANGES.txt19
-rw-r--r--CONTRIBUTORS.txt2
-rw-r--r--HACKING.txt26
-rw-r--r--TODO.txt9
-rw-r--r--docs/api/settings.rst2
-rw-r--r--docs/conf.py2
-rw-r--r--docs/glossary.rst15
-rw-r--r--docs/narr/MyProject/myproject/static/pylons.css15
-rw-r--r--docs/narr/MyProject/myproject/templates/mytemplate.pt76
-rw-r--r--docs/narr/commandline.rst22
-rw-r--r--docs/narr/install.rst30
-rw-r--r--docs/narr/introduction.rst10
-rw-r--r--docs/narr/templates.rst8
-rw-r--r--docs/tutorials/wiki/authorization.rst21
-rw-r--r--docs/tutorials/wiki/basiclayout.rst34
-rw-r--r--docs/tutorials/wiki/definingviews.rst2
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/views.py3
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/views.py3
-rw-r--r--docs/tutorials/wiki2/basiclayout.rst159
-rw-r--r--docs/tutorials/wiki2/definingmodels.rst5
-rw-r--r--docs/tutorials/wiki2/installation.rst148
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py11
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/views.py12
-rw-r--r--pyramid/config/views.py3
-rw-r--r--pyramid/path.py2
-rw-r--r--pyramid/scaffolds/__init__.py13
-rw-r--r--pyramid/scaffolds/alchemy/+package+/__init__.py (renamed from pyramid/scaffolds/alchemy/+package+/__init__.py_tmpl)10
-rw-r--r--pyramid/scaffolds/alchemy/+package+/models.py18
-rw-r--r--pyramid/scaffolds/alchemy/+package+/scripts/__init__.py1
-rw-r--r--pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl30
-rw-r--r--pyramid/scaffolds/alchemy/+package+/tests.py_tmpl26
-rw-r--r--pyramid/scaffolds/alchemy/+package+/views.py_tmpl12
-rw-r--r--pyramid/scaffolds/alchemy/setup.py_tmpl2
-rw-r--r--pyramid/scripts/proutes.py7
-rw-r--r--pyramid/scripts/pshell.py46
-rw-r--r--pyramid/settings.py7
-rw-r--r--pyramid/static.py4
-rw-r--r--pyramid/tests/fixtures/static/arcs.svg.tgz73
-rw-r--r--pyramid/tests/test_chameleon_zpt.py2
-rw-r--r--pyramid/tests/test_config/test_views.py21
-rw-r--r--pyramid/tests/test_path.py5
-rw-r--r--pyramid/tests/test_scripts/dummy.py9
-rw-r--r--pyramid/tests/test_scripts/test_proutes.py12
-rw-r--r--pyramid/tests/test_scripts/test_pshell.py92
-rw-r--r--pyramid/tests/test_settings.py8
-rw-r--r--pyramid/tests/test_static.py18
-rw-r--r--pyramid/view.py5
47 files changed, 735 insertions, 325 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 42f07a275..effce62b9 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -14,6 +14,9 @@ Features
spec. An entire application can be written with such asset specs,
requiring no ordered lookup path.
+- ``bpython`` interpreter compatibility in ``pshell``. See the "Command-Line
+ Pyramid" narrative docs chapter for more information.
+
Bug Fixes
---------
@@ -33,11 +36,15 @@ Bug Fixes
- The DummySession in ``pyramid.testing`` now generates a new CSRF token if
one doesn't yet exist.
+- ``request.static_url`` now generates URL-quoted URLs when fed a ``path``
+ argument which contains characters that are unsuitable for URLs. See
+ https://github.com/Pylons/pyramid/issues/349 for more info.
+
Backwards Incompatibilities
---------------------------
- Pyramid no longer runs on Python 2.5 (which includes the most recent
- release of Jython, and the current version of GAE as of this writing).
+ release of Jython and the Python 2.5 version of GAE as of this writing).
- The ``paster`` command is no longer the documented way to create projects,
start the server, or run debugging commands. To create projects from
@@ -53,6 +60,11 @@ Backwards Incompatibilities
``paste.httpserver`` server. Rationale: Rationale: the Paste and
PasteScript packages do not run under Python 3.
+- The ``pshell`` command (see "paster pshell") no longer accepts a
+ ``--disable-ipython`` command-line argument. Instead, it accepts a ``-p``
+ or ``--python-shell`` argument, which can be any of the values ``python``,
+ ``ipython`` or ``bpython``.
+
Dependencies
------------
@@ -73,7 +85,6 @@ Scaffolds
- Rendered scaffolds have now been changed to be more relocatable (fewer
mentions of the package name within files in the package).
-- The ``alchemy`` scaffold has been removed.
-
-- The ``routesalchemy`` scaffold has been renamed ``alchemy``.
+- The ``routesalchemy`` scaffold has been renamed ``alchemy``, replacing the
+ older (traversal-based) ``alchemy`` scaffold (which has been retired).
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 2654801c3..a449333cb 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -156,3 +156,5 @@ Contributors
- Ken Manheimer, 2011/11/07
- Reed O'Brien, 2011/11/07
+
+- Klee Dienes, 2011/10/30
diff --git a/HACKING.txt b/HACKING.txt
index d122a7a25..593e89ac1 100644
--- a/HACKING.txt
+++ b/HACKING.txt
@@ -61,10 +61,10 @@ In order to add a feature to Pyramid:
- The feature must work on the latest version of PyPy.
-- The feature must not cause installation or runtime failure on Jython
- or App Engine. If it doesn't cause installation or runtime failure,
- but doesn't actually *work* on these platforms, that caveat should be
- spelled out in the documentation.
+- The feature must not cause installation or runtime failure on App Engine.
+ If it doesn't cause installation or runtime failure, but doesn't actually
+ *work* on these platforms, that caveat should be spelled out in the
+ documentation.
- The feature must not depend on any particular persistence layer
(filesystem, SQL, etc).
@@ -73,10 +73,10 @@ In order to add a feature to Pyramid:
"unnecessary" is of course subjective, but new dependencies should
be discussed).
-The above requirements are relaxed for paster template dependencies.
-If a paster template has an install-time dependency on something that
-doesn't work on a particular platform, that caveat should be spelled
-out clearly in *its* documentation (within its ``docs/`` directory).
+The above requirements are relaxed for scaffolding dependencies. If a
+scaffold has an install-time dependency on something that doesn't work on a
+particular platform, that caveat should be spelled out clearly in *its*
+documentation (within its ``docs/`` directory).
Coding Style
------------
@@ -121,6 +121,16 @@ Documentation Coverage
that API or behavior must change to reflect the bug fix, ideally in
the same commit that fixes the bug or adds the feature.
+- To build and review docs:
+
+ 1. Install ``tests_require`` dependencies from Pyramid's setup.py into your
+ virtualenv.
+
+ 2. From the ``docs`` directory of the Pyramid checkout run ``make html
+ SPHINXBUILD=/path/to/your/virtualenv/bin/sphinx-build``.
+
+ 3. Open the _build/html/index.html file to see the resulting rendering.
+
Change Log
----------
diff --git a/TODO.txt b/TODO.txt
index 5a030a84c..a3de49e2f 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -4,14 +4,11 @@ Pyramid TODOs
Must-Have
---------
-- Fix ZODB tutorial docs to match ZODB tutorial code (I removed program name
- from ``scan``).
-
- Fix SQLA tutorial to match ZODB tutorial.
-- Fix routesalchemy scaffold to match ZODB scaffold.
-
-- Remove alchemy scaffold.
+- Fix SQLA tutorial to match alchemy scaffold. See
+ https://github.com/Pylons/pyramid/issues/359 for additional suggestions
+ from Mike.
Nice-to-Have
------------
diff --git a/docs/api/settings.rst b/docs/api/settings.rst
index ac1cd3f9c..6b12c038c 100644
--- a/docs/api/settings.rst
+++ b/docs/api/settings.rst
@@ -9,4 +9,6 @@
.. autofunction:: asbool
+ .. autofunction:: aslist
+
diff --git a/docs/conf.py b/docs/conf.py
index f97ad1547..4010cf111 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -94,7 +94,7 @@ copyright = '%s, Agendaless Consulting' % datetime.datetime.now().year
# other places throughout the built documents.
#
# The short X.Y version.
-version = '1.2'
+version = '1.3dev'
# The full version, including alpha/beta/rc tags.
release = version
diff --git a/docs/glossary.rst b/docs/glossary.rst
index fc282b2da..c98637698 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -342,14 +342,13 @@ Glossary
file. It was developed by Ian Bicking.
Chameleon
- `chameleon <http://chameleon.repoze.org>`_ is an attribute
- language template compiler which supports both the :term:`ZPT` and
- :term:`Genshi` templating specifications. It is written and
- maintained by Malthe Borch. It has several extensions, such as
- the ability to use bracketed (Genshi-style) ``${name}`` syntax,
- even within ZPT. It is also much faster than the reference
- implementations of both ZPT and Genshi. :app:`Pyramid` offers
- Chameleon templating out of the box in ZPT and text flavors.
+ `chameleon <http://chameleon.repoze.org>`_ is an attribute language
+ template compiler which supports the :term:`ZPT` templating
+ specification. It is written and maintained by Malthe Borch. It has
+ several extensions, such as the ability to use bracketed (Mako-style)
+ ``${name}`` syntax. It is also much faster than the reference
+ implementation of ZPT. :app:`Pyramid` offers Chameleon templating out
+ of the box in ZPT and text flavors.
ZPT
The `Zope Page Template <http://wiki.zope.org/ZPT/FrontPage>`_
diff --git a/docs/narr/MyProject/myproject/static/pylons.css b/docs/narr/MyProject/myproject/static/pylons.css
index 33b21ac1a..c54499ddd 100644
--- a/docs/narr/MyProject/myproject/static/pylons.css
+++ b/docs/narr/MyProject/myproject/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
+body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
@@ -31,19 +31,20 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
#wrap{min-height:100%;}
#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
#header{background:#000000;top:0;font-size:14px;}
#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#000000;height:230px;
-background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
#bottom{color:#222;background-color:#ffffff;}
-.top,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
.top{padding-top:40px;}
+.top-small{padding-top:10px;}
#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
@@ -58,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{width:205px;}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/docs/narr/MyProject/myproject/templates/mytemplate.pt b/docs/narr/MyProject/myproject/templates/mytemplate.pt
index 97f1e1aa3..ab698123e 100644
--- a/docs/narr/MyProject/myproject/templates/mytemplate.pt
+++ b/docs/narr/MyProject/myproject/templates/mytemplate.pt
@@ -1,42 +1,29 @@
-<!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"
- xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
+<!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" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
<head>
<title>The Pyramid Web Application Development Framework</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="${request.static_url('myproject:static/favicon.ico')}" />
- <link rel="stylesheet"
- href="${request.static_url('myproject:static/pylons.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet"
- href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&amp;subset=latin"
- type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="/static/favicon.ico" />
+ <link rel="stylesheet" href="/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
+ <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
<!--[if lte IE 6]>
- <link rel="stylesheet"
- href="${request.static_url('myproject:static/ie6.css')}"
- type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="/static/ie6.css" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
<div id="top">
<div class="top align-center">
- <div>
- <img src="${request.static_url('myproject:static/pyramid.png')}"
- width="750" height="169" alt="pyramid"/>
- </div>
+ <div><img src="/static/pyramid.png" width="750" height="169" alt="pyramid"/></div>
</div>
</div>
<div id="middle">
<div class="middle align-center">
<p class="app-welcome">
- Welcome to <span class="app-name">${project}</span>,
- an application generated by<br/>
+ Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
</p>
</div>
@@ -45,62 +32,45 @@
<div class="bottom">
<div id="left" class="align-right">
<h2>Search documentation</h2>
- <form method="get"
- action="http://docs.pylonsproject.org/pyramid/current/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
+ <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
+ <input type="text" id="q" name="q" value="" />
+ <input type="submit" id="x" value="Go" />
+ </form>
</div>
<div id="right" class="align-left">
<h2>Pyramid links</h2>
<ul class="links">
<li>
- <a href="http://pylonsproject.org">
- Pylons Website
- </a>
+ <a href="http://pylonsproject.org">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">
- Narrative Documentation
- </a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">
- API Documentation
- </a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">
- Tutorials
- </a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">
- Change History
- </a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">
- Sample Applications
- </a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">
- Support and Development
- </a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
</li>
<li>
- <a href="irc://irc.freenode.net#pyramid">
- IRC Channel
- </a>
+ <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
</li>
- </ul>
+ </ul>
</div>
</div>
</div>
</div>
<div id="footer">
- <div class="footer">&copy; Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">&copy; Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html>
diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst
index 0dc41e919..0f0e17ca6 100644
--- a/docs/narr/commandline.rst
+++ b/docs/narr/commandline.rst
@@ -111,6 +111,7 @@ For a URL that doesn't match any views, ``pviews`` will simply print out a
single: interactive shell
single: IPython
single: pshell
+ single: bpython
.. _interactive_shell:
@@ -267,23 +268,24 @@ exposed, and the request is configured to generate urls from the host
.. index::
single: IPython
+ single: bpython
-IPython
-~~~~~~~
+IPython or bpython
+~~~~~~~~~~~~~~~~~~
-If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_ installed in
-the interpreter you use to invoke the ``pshell`` command, ``pshell`` will use
-an IPython interactive shell instead of a standard Python interpreter shell.
-If you don't want this to happen, even if you have IPython installed, you can
-pass the ``--disable-ipython`` flag to the ``pshell`` command to use a
-standard Python interpreter shell unconditionally.
+If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_ or
+`bpython <http://bpython-interpreter.org/>`_ or both installed in
+the interpreter you use to invoke the ``pshell`` command, ``pshell`` will
+autodiscover them and use the first respectively found in this order :
+IPython, bpython, standard Python interpreter. However you could
+specifically invoke one of your choice with the ``-p choice`` or
+``--python-shell choice`` option.
.. code-block:: text
- [chrism@vitaminf shellenv]$ ../bin/pshell --disable-ipython \
+ [chrism@vitaminf shellenv]$ ../bin/pshell -p ipython | bpython | python \
development.ini#MyProject
-
.. index::
pair: routes; printing
single: proutes
diff --git a/docs/narr/install.rst b/docs/narr/install.rst
index 66bcea706..3de4d6e27 100644
--- a/docs/narr/install.rst
+++ b/docs/narr/install.rst
@@ -9,19 +9,18 @@ Installing :app:`Pyramid`
Before You Install
------------------
-You will need `Python <http://python.org>`_ version 2.5 or better to
+You will need `Python <http://python.org>`_ version 2.6 or better to
run :app:`Pyramid`.
.. sidebar:: Python Versions
- As of this writing, :app:`Pyramid` has been tested under Python 2.5.5,
- Python 2.6.6, and Python 2.7.2. :app:`Pyramid` does not run under any
- version of Python before 2.5, and does not yet run under Python 3.X.
+ As of this writing, :app:`Pyramid` has been tested under Python 2.6.6,
+ Python 2.7.2, and Python 3.2. :app:`Pyramid` does not run under any
+ version of Python before 2.6.
:app:`Pyramid` is known to run on all popular UNIX-like systems such as
Linux, MacOS X, and FreeBSD as well as on Windows platforms. It is also
-known to run on Google's App Engine, :term:`PyPy` (1.5 and 1.6), and
-:term:`Jython` (2.5.2).
+known to run on Google's App Engine, and :term:`PyPy` (1.6+).
:app:`Pyramid` installation does not require the compilation of any
C code, so you need only a Python interpreter that meets the
@@ -325,25 +324,6 @@ Installing :app:`Pyramid` on Google App Engine
:ref:`appengine_tutorial` documents the steps required to install a
:app:`Pyramid` application on Google App Engine.
-.. index::
- single: installing on Jython
-
-Installing :app:`Pyramid` on Jython
---------------------------------------
-
-:app:`Pyramid` is known to work under :term:`Jython` version 2.5.1.
-Install :term:`Jython`, and then follow the installation steps for
-:app:`Pyramid` on your platform described in one of the sections
-entitled :ref:`installing_unix` or :ref:`installing_windows` above,
-replacing the ``python`` command with ``jython`` as necessary. The
-steps are exactly the same except you should use the ``jython``
-command name instead of the ``python`` command name.
-
-One caveat exists to using :app:`Pyramid` under Jython: the :term:`Chameleon`
-templating engine does not work on Jython. However, the :term:`Mako`
-templating system, which is also included with Pyramid, does work under
-Jython; use it instead.
-
What Gets Installed
-------------------
diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst
index 547f88ef3..2387db6a7 100644
--- a/docs/narr/introduction.rst
+++ b/docs/narr/introduction.rst
@@ -808,11 +808,11 @@ Every release of Pyramid has 100% statement coverage via unit and integration
tests, as measured by the ``coverage`` tool available on PyPI. It also has
greater than 95% decision/condition coverage as measured by the
``instrumental`` tool available on PyPI. It is automatically tested by the
-Jenkins tool on Python 2.5, Python 2.6, Python 2.7, Jython and PyPy after
-each commit to its GitHub repository. Official Pyramid add-ons are held to a
-similar testing standard. We still find bugs in Pyramid and its official
-add-ons, but we've noticed we find a lot more of them while working on other
-projects that don't have a good testing regime.
+Jenkins tool on Python 2.6, Python 2.7, Python 3.2 and PyPy after each commit
+to its GitHub repository. Official Pyramid add-ons are held to a similar
+testing standard. We still find bugs in Pyramid and its official add-ons,
+but we've noticed we find a lot more of them while working on other projects
+that don't have a good testing regime.
Example: http://jenkins.pylonsproject.org/
diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst
index fb9dd56c2..11318d9eb 100644
--- a/docs/narr/templates.rst
+++ b/docs/narr/templates.rst
@@ -403,14 +403,6 @@ The language definition documentation for Chameleon ZPT-style
templates is available from `the Chameleon website
<http://chameleon.repoze.org/>`_.
-.. warning::
-
- :term:`Chameleon` only works on :term:`CPython` platforms and
- :term:`Google App Engine`. On :term:`Jython` and other non-CPython
- platforms, you should use Mako (see :ref:`mako_templates`) or
- ``pyramid_jinja2`` instead. See
- :ref:`available_template_system_bindings`.
-
Given a :term:`Chameleon` ZPT template named ``foo.pt`` in a directory
in your application named ``templates``, you can render the template as
a :term:`renderer` like so:
diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst
index bf88c3bd8..d900f17a3 100644
--- a/docs/tutorials/wiki/authorization.rst
+++ b/docs/tutorials/wiki/authorization.rst
@@ -42,7 +42,7 @@ declarative security checking. We need to import the new policies:
Then, we'll add those policies to the configuration:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 20-25
+ :lines: 17-22
:linenos:
:language: python
@@ -60,10 +60,6 @@ look like so:
:linenos:
:language: python
-.. note::
- (Your ``config.scan('tutorial')`` needs the package name you used
- instead of "tutorial", if you used a different name.)
-
Add ``security.py``
~~~~~~~~~~~~~~~~~~~
@@ -77,14 +73,13 @@ content:
The ``groupfinder`` function defined here is an :term:`authentication policy`
"callback"; it is a callable that accepts a userid and a request. If the
-userid exists in the system, the callback will
-return a sequence of group identifiers (or an empty sequence if the user
-isn't a member of any groups). If the userid *does not* exist in the system,
-the callback will return ``None``. In a production system, user and group data will
-most often come from a database, but here we use "dummy" data to represent
-user and groups sources. Note that the ``editor`` user is a member of the
-``group:editors`` group in our dummy group data (the ``GROUPS`` data
-structure).
+userid exists in the system, the callback will return a sequence of group
+identifiers (or an empty sequence if the user isn't a member of any groups).
+If the userid *does not* exist in the system, the callback will return
+``None``. In a production system, user and group data will most often come
+from a database, but here we use "dummy" data to represent user and groups
+sources. Note that the ``editor`` user is a member of the ``group:editors``
+group in our dummy group data (the ``GROUPS`` data structure).
Give Our Root Resource an ACL
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/tutorials/wiki/basiclayout.rst b/docs/tutorials/wiki/basiclayout.rst
index 897775428..56f817a85 100644
--- a/docs/tutorials/wiki/basiclayout.rst
+++ b/docs/tutorials/wiki/basiclayout.rst
@@ -10,8 +10,8 @@ The source code for this tutorial stage can be browsed via
`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/basiclayout/
<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/basiclayout/>`_.
-App Startup with ``__init__.py``
---------------------------------
+Appplication Configuration with ``__init__.py``
+------------------------------------------------
A directory on disk can be turned into a Python :term:`package` by containing
an ``__init__.py`` file. Even if empty, this marks a directory as a Python
@@ -45,16 +45,19 @@ point happens to be the ``main`` function within the file named
``http://localhost:6543/static/`` and below. The first argument is the
"name" ``static``, which indicates that the URL path prefix of the view
will be ``/static``. the The second argument of this tag is the "path",
- which is an :term:`asset specification`, so it finds the resources it
- should serve within the ``static`` directory inside the ``tutorial``
- package.
+ which is a relative :term:`asset specification`, so it finds the resources
+ it should serve within the ``static`` directory inside the ``tutorial``
+ package. The scaffold could have alternately used an *absolute* asset
+ specification as the path (``tutorial:static``) but it does not.
#. *Line 14*. Perform a :term:`scan`. A scan will find :term:`configuration
- decoration`, such as view configuration decorators
- (e.g. ``@view_config``) in the source code of the ``tutorial`` package and
- will take actions based on these decorators. The argument to
- :meth:`~pyramid.config.Configurator.scan` is the package name to scan,
- which is ``tutorial``.
+ decoration`, such as view configuration decorators (e.g. ``@view_config``)
+ in the source code of the ``tutorial`` package and will take actions based
+ on these decorators. We don't pass any arguments to
+ :meth:`~pyramid.config.Configurator.scan`, which implies that the scan
+ should take place in the current package (in this case, ``tutorial``).
+ The scaffold could have equivalently said ``config.scan('tutorial')`` but
+ it chose to omit the package name argument.
#. *Line 15*. Use the
:meth:`pyramid.config.Configurator.make_wsgi_app` method
@@ -119,7 +122,7 @@ Let's try to understand the components in this module:
decoration` to perform a :term:`view configuration` registration. This
view configuration registration will be activated when the application is
started. It will be activated by virtue of it being found as the result
- of a :term:`scan` (when Line 17 of ``__init__.py`` is run).
+ of a :term:`scan` (when Line 14 of ``__init__.py`` is run).
The ``@view_config`` decorator accepts a number of keyword arguments. We
use two keyword arguments here: ``context`` and ``renderer``.
@@ -131,12 +134,15 @@ Let's try to understand the components in this module:
model, this view callable will be invoked.
The ``renderer`` argument names an :term:`asset specification` of
- ``tutorial:templates/mytemplate.pt``. This asset specification points at
- a :term:`Chameleon` template which lives in the ``mytemplate.pt`` file
+ ``templates/mytemplate.pt``. This asset specification points at a
+ :term:`Chameleon` template which lives in the ``mytemplate.pt`` file
within the ``templates`` directory of the ``tutorial`` package. And
indeed if you look in the ``templates`` directory of this package, you'll
see a ``mytemplate.pt`` template file, which renders the default home page
- of the generated project.
+ of the generated project. This asset specification is *relative* (to the
+ view.py's current package). We could have alternately an used the
+ absolute asset specification ``tutorial:templates/mytemplate.pt``, but
+ chose to use the relative version.
Since this call to ``@view_config`` doesn't pass a ``name`` argument, the
``my_view`` function which it decorates represents the "default" view
diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst
index 15b9f13ab..371cae8eb 100644
--- a/docs/tutorials/wiki/definingviews.rst
+++ b/docs/tutorials/wiki/definingviews.rst
@@ -293,7 +293,7 @@ replicate within the body of this guide, however it is available `online
<http://github.com/Pylons/pyramid/blob/master/docs/tutorials/wiki/src/views/tutorial/static/pylons.css>`_.
This CSS file will be accessed via
-e.g. ``http://localhost:6543/static/pylons.css`` by virtue of the call to
+e.g. ``/static/pylons.css`` by virtue of the call to
``add_static_view`` directive we've made in the ``__init__.py`` file. Any
number and type of static assets can be placed in this directory (or
subdirectories) and are just referred to by URL.
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/views.py b/docs/tutorials/wiki/src/basiclayout/tutorial/views.py
index 9dfdee3f1..4265b6bf7 100644
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/views.py
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/views.py
@@ -1,7 +1,6 @@
from pyramid.view import view_config
from .models import MyModel
-@view_config(context=MyModel,
- renderer='templates/mytemplate.pt')
+@view_config(context=MyModel, renderer='templates/mytemplate.pt')
def my_view(request):
return {'project':'tutorial'}
diff --git a/docs/tutorials/wiki/src/views/tutorial/views.py b/docs/tutorials/wiki/src/views/tutorial/views.py
index 862bd0da9..016f5b6bb 100644
--- a/docs/tutorials/wiki/src/views/tutorial/views.py
+++ b/docs/tutorials/wiki/src/views/tutorial/views.py
@@ -13,8 +13,7 @@ wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
def view_wiki(context, request):
return HTTPFound(location=request.resource_url(context, 'FrontPage'))
-@view_config(context='.models.Page',
- renderer='templates/view.pt')
+@view_config(context='.models.Page', renderer='templates/view.pt')
def view_page(context, request):
wiki = context.__parent__
diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst
index db8ab1fbe..dc5664c5b 100644
--- a/docs/tutorials/wiki2/basiclayout.rst
+++ b/docs/tutorials/wiki2/basiclayout.rst
@@ -2,82 +2,96 @@
Basic Layout
============
-The starter files generated by the ``alchemy`` scaffold are
-basic, but they provide a good orientation for the high-level patterns common
-to most :term:`url dispatch` -based :app:`Pyramid` projects.
+The starter files generated by the ``alchemy`` scaffold are very basic, but
+they provide a good orientation for the high-level patterns common to most
+:term:`url dispatch` -based :app:`Pyramid` projects.
The source code for this tutorial stage can be browsed at
`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/basiclayout/
<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/basiclayout/>`_.
-App Startup with ``__init__.py``
---------------------------------
+Application Configuration with ``__init__.py``
+----------------------------------------------
A directory on disk can be turned into a Python :term:`package` by containing
an ``__init__.py`` file. Even if empty, this marks a directory as a Python
-package. We use ``__init__.py`` both as a package marker and to contain
-configuration code.
+package. We use ``__init__.py`` both as a marker indicating the directory
+it's contained within is a package, and to contain configuration code. Our
+``__init__.py`` file will look like this:
-The generated ``development.ini`` file is read by ``pserve`` which looks for
-the application module in the ``use`` variable of the ``app:main``
-section. The *entry point* is defined in the Setuptools configuration of this
-module, specifically in the ``setup.py`` file. For this tutorial, the *entry
-point* is defined as ``tutorial:main`` and points to a function named
-``main``.
+ .. literalinclude:: src/basiclayout/tutorial/__init__.py
+ :linenos:
+ :language: py
-First we need some imports to support later code:
+Let's go over this piece-by-piece. First, we need some imports to support
+later code:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:end-before: main
:linenos:
:language: py
-Next we define the main function and create a SQLAlchemy database engine from
-the ``sqlalchemy.`` prefixed settings in the ``development.ini`` file's
-``[app:main]`` section. This will be a URI (something like
-``sqlite://``):
+``__init__.py`` defines a function named ``main``. Here is the entirety of
+the ``main`` function we've defined in our ``__init__.py``:
+
+ .. literalinclude:: src/basiclayout/tutorial/__init__.py
+ :pyobject: main
+ :linenos:
+ :language: py
+
+When you invoke the ``pserve development.ini`` command, the ``main`` function
+above is executed. It accepts some settings and returns a :term:`WSGI`
+application. You can read :ref:`startup_chapter` for details about *how*
+this function is found and called when you run ``pserve``, but for purposes
+of brevity, we'll elide the details here.
+
+The main function first creates a SQLAlchemy database engine using
+``engine_from_config`` from the ``sqlalchemy.`` prefixed settings in the
+``development.ini`` file's ``[app:main]`` section. This will be a URI
+(something like ``sqlite://``):
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 6-9
+ :lines: 9
:linenos:
:language: py
-We then initialize our SQL database using SQLAlchemy, passing
-it the engine:
+``main`` then initializes our SQL database using SQLAlchemy, passing it the
+engine:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 10
:language: py
-The next step is to construct a :term:`Configurator`:
+The next step of ``main`` is to construct a :term:`Configurator` object:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 11
:language: py
``settings`` is passed to the Configurator as a keyword argument with the
-dictionary values passed by PasteDeploy as the ``**settings`` argument. This
-will be a dictionary of settings parsed from the ``.ini`` file, which
-contains deployment-related values such as ``pyramid.reload_templates``,
+dictionary values passed as the ``**settings`` argument. This will be a
+dictionary of settings parsed from the ``.ini`` file, which contains
+deployment-related values such as ``pyramid.reload_templates``,
``db_string``, etc.
-We now can call :meth:`pyramid.config.Configurator.add_static_view` with the
-arguments ``static`` (the name), and ``tutorial:static`` (the path):
+``'main`` now calls :meth:`pyramid.config.Configurator.add_static_view` with
+two arguments: ``static`` (the name), and ``static`` (the path):
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 12
:language: py
-This registers a static resource view which will match any URL that starts with
-``/static/``. This will serve up static resources for us from within the
-``static`` directory of our ``tutorial`` package, in this case,
-via ``http://localhost:6543/static/`` and below. With this declaration,
-we're saying that any URL that starts with ``/static`` should go to the
-static view; any remainder of its path (e.g. the ``/foo`` in
-``/static/foo``) will be used to compose a path to a static file resource,
-such as a CSS file.
+This registers a static resource view which will match any URL that starts
+with the prefix ``/static`` (by virtue of the first argument to add_static
+view). This will serve up static resources for us from within the ``static``
+directory of our ``tutorial`` package, in this case, via
+``http://localhost:6543/static/`` and below (by virtue of the second argument
+to add_static_view). With this declaration, we're saying that any URL that
+starts with ``/static`` should go to the static view; any remainder of its
+path (e.g. the ``/foo`` in ``/static/foo``) will be used to compose a path to
+a static file resource, such as a CSS file.
-Using the configurator we can also register a :term:`route configuration`
+Using the configurator ``main`` also registers a :term:`route configuration`
via the :meth:`pyramid.config.Configurator.add_route` method that will be
used when the URL is ``/``:
@@ -88,44 +102,71 @@ used when the URL is ``/``:
Since this route has a ``pattern`` equalling ``/`` it is the route that will
be matched when the URL ``/`` is visted, e.g. ``http://localhost:6543/``.
-Mapping the ``home`` route to code is done by registering a view. You will
-use :meth:`pyramid.config.Configurator.add_view` in :term:`URL dispatch` to
-register views for the routes, mapping your patterns to code:
+``main`` next calls the ``scan`` method of the configurator, which will
+recursively scan our ``tutorial`` package, looking for ``@view_config`` (and
+other special) decorators. When it finds a ``@view_config`` decorator, a
+view configuration will be registered, which will allow one of our
+application URLs to be mapped to some code.
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 14-15
+ :lines: 14
:language: py
-The first positional ``add_view`` argument ``tutorial.views.my_view`` is the
-dotted name to a *function* we write (generated by the
-``alchemy`` scaffold) that is given a ``request`` object and
-which returns a response or a dictionary. This view also names a
-``renderer``, which is a template which lives in the ``templates``
-subdirectory of the package. When the ``tutorial.views.my_view`` view
-returns a dictionary, a :term:`renderer` will use this template to create a
-response.
-
-Finally, we use the :meth:`pyramid.config.Configurator.make_wsgi_app`
-method to return a :term:`WSGI` application:
+Finally, ``main`` is finished configuring things, so it uses the
+:meth:`pyramid.config.Configurator.make_wsgi_app` method to return a
+:term:`WSGI` application:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 16
+ :lines: 15
:language: py
-Our final ``__init__.py`` file will look like this:
+View Declarations via ``views.py``
+----------------------------------
- .. literalinclude:: src/basiclayout/tutorial/__init__.py
+Mapping a :term:`route` to code that will be executed when that route's
+pattern matches is done by registering a :term:`view configuration`. Our
+application uses the :meth:`pyramid.view.view_config` decorator to map view
+callables to each route, thereby mapping URL patterns to code.
+
+Here is the code in the ``views.py`` file within our package:
+
+ .. literalinclude:: src/basiclayout/tutorial/views.py
:linenos:
:language: py
+The important part to point out here is the ``@view_config`` decorator. In
+fact, ``@view_config`` is so important that we're going to ignore the rest of
+the code in the module at this point just to explain it. The
+``@view_config`` decorator associates the function it decorates with a
+:term:`view configuration`. The view configuration names a ``route_name``
+(``home``), and names a ``renderer``, which is a template which lives in the
+``templates`` subdirectory of the package.
+
+As the result of this view configuration, when the pattern associated with
+the view named ``home`` is matched during a request, the function named
+``my_view`` will be executed. The the function named ``my_view`` returns a
+dictionary; the renderer will use the ``templates/mytemplate.pt`` template to
+create a response based on the values in the dictionary.
+
+Note that the decorated function named ``my_view`` accepts a single argument
+named ``request``. This is the standard call signature for a Pyramid
+:term:`view callable`.
+
+Remember in our ``__init__.py`` when we executed the
+:meth:`pyramid.config.Configurator.scan` method, e.g. ``config.scan()``? The
+purpose of calling the scan method was to find and process this
+``@view_config`` decorator in order to create a view configuration within our
+application. Without being processed by ``scan``, the decorator effectively
+does nothing. ``@view_config`` is inert without being detected via a
+:term:`scan`.
+
Content Models with ``models.py``
---------------------------------
-In a SQLAlchemy-based application, a *model* object is an object
-composed by querying the SQL database which backs an application.
-SQLAlchemy is an "object relational mapper" (an ORM). The
-``models.py`` file is where the ``alchemy`` scaffold
-put the classes that implement our models.
+In a SQLAlchemy-based application, a *model* object is an object composed by
+querying the SQL database. SQLAlchemy is an "object relational mapper" (an
+ORM). The ``models.py`` file is where the ``alchemy`` scaffold put the
+classes that implement our models.
Let's take a look. First, we need some imports to support later code.
diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst
index 083ec0aa8..bc2c2de5f 100644
--- a/docs/tutorials/wiki2/definingmodels.rst
+++ b/docs/tutorials/wiki2/definingmodels.rst
@@ -80,6 +80,11 @@ something like this:
:linenos:
:language: python
+Populating the Database
+-----------------------
+
+XXX The ``populate_tutorial`` :term:`console script``.
+
Viewing the Application in a Browser
------------------------------------
diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst
index 147f7f563..381e0a90f 100644
--- a/docs/tutorials/wiki2/installation.rst
+++ b/docs/tutorials/wiki2/installation.rst
@@ -40,13 +40,6 @@ Preparation, UNIX
$ bin/easy_install pyramid
-#. Use ``easy_install`` to install various packages from PyPI.
-
- .. code-block:: text
-
- $ bin/easy_install docutils nose coverage zope.sqlalchemy \
- SQLAlchemy pyramid_tm
-
Preparation, Windows
--------------------
@@ -69,14 +62,6 @@ Preparation, Windows
c:\pyramidtut> Scripts\easy_install pyramid
-#. Use ``easy_install`` to install various packages from PyPI.
-
- .. code-block:: text
-
- c:\pyramidtut> Scripts\easy_install docutils \
- nose coverage zope.sqlalchemy SQLAlchemy pyramid_tm
-
-
.. _sql_making_a_project:
Making a Project
@@ -108,6 +93,13 @@ On Windows:
startup problems, try putting both the virtualenv and the project
into directories that do not contain spaces in their paths.
+Success executing this command will end with a line to the console something
+like::
+
+ Please run the "populate_tutorial" script to set up the SQL
+ database before starting the application (e.g.
+ "$myvirtualenv/bin/populate_tutorial development.ini".)
+
Installing the Project in "Development Mode"
============================================
@@ -131,6 +123,11 @@ On Windows:
c:\pyramidtut> cd tutorial
c:\pyramidtut\tutorial> ..\Scripts\python setup.py develop
+Success executing this command will end with a line to the console something
+like::
+
+ Finished processing dependencies for tutorial==0.0
+
.. _sql_running_tests:
Running the Tests
@@ -151,6 +148,14 @@ On Windows:
c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
+For a successful test run, you should see output like this::
+
+ .
+ ----------------------------------------------------------------------
+ Ran 1 test in 0.094s
+
+ OK
+
Exposing Test Coverage Information
==================================
@@ -191,8 +196,24 @@ On Windows:
c:\pyramidtut\tutorial> ..\Scripts\nosetests --cover-package=tutorial ^
--cover-erase --with-coverage
-Looks like our package's ``models`` module doesn't quite have 100%
-test coverage.
+If successful, you will see output something like this::
+
+ .
+ Name Stmts Miss Cover Missing
+ ------------------------------------------------
+ tutorial 11 7 36% 9-15
+ tutorial.models 17 0 100%
+ tutorial.scripts 0 0 100%
+ tutorial.tests 24 0 100%
+ tutorial.views 6 0 100%
+ ------------------------------------------------
+ TOTAL 58 7 88%
+ ----------------------------------------------------------------------
+ Ran 1 test in 0.459s
+
+ OK
+
+Looks like our package doesn't quite have 100% test coverage.
Starting the Application
========================
@@ -211,11 +232,96 @@ On Windows:
c:\pyramidtut\tutorial> ..\Scripts\pserve development.ini --reload
-Visit the Application in a Browser
-==================================
+If successful, you will see something like this on your console::
+
+ Starting subprocess with file monitor
+ Starting server in PID 8966.
+ Starting HTTP server on http://0.0.0.0:6543
+
+This means the server is ready to accept requests.
+
+Populating the Database
+=======================
+
+In a web browser, visit ``http://localhost:6543/``.
+
+You will see an error page with a title something like this::
+
+ sqlalchemy.exc.OperationalError
+
+ OperationalError: (OperationalError) no such table: models ...
+
+Oh no! Something isn't working!
+
+This happens because we haven't populated the database with any table
+information yet. We need to use the ``populate_tutorial`` :term:`console
+script` to populate our database before we can see the page render correctly.
+
+Stop the running Pyramid application by pressing ``ctrl-C`` in the console.
+Make sure you're still in the ``tutorial`` directory (the directory with a
+``development.ini`` in it) and type the following command:
+
+On UNIX:
+
+.. code-block:: text
+
+ $ ../bin/populate_tutorial development.ini
+
+On Windows:
+
+.. code-block:: text
+
+ c:\pyramidtut\tutorial> ..\Scripts\populate_tutorial development.ini
+
+The output to your console should be something like this::
+
+ 2011-11-26 14:42:25,012 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ PRAGMA table_info("models")
+ 2011-11-26 14:42:25,013 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
+ 2011-11-26 14:42:25,013 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ CREATE TABLE models (
+ id INTEGER NOT NULL,
+ name VARCHAR(255),
+ value INTEGER,
+ PRIMARY KEY (id),
+ UNIQUE (name)
+ )
+ 2011-11-26 14:42:25,013 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
+ 2011-11-26 14:42:25,135 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ COMMIT
+ 2011-11-26 14:42:25,137 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ BEGIN (implicit)
+ 2011-11-26 14:42:25,138 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ INSERT INTO models (name, value) VALUES (?, ?)
+ 2011-11-26 14:42:25,139 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ (u'one', 1)
+ 2011-11-26 14:42:25,140 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ COMMIT
+
+Success! You should now have a ``tutorial.db`` file in your current working
+directory. This will be a SQLite database with a single table defined in it
+(``models``).
+
+Starting the Application (Again)
+================================
+
+Start the application again.
+
+On UNIX:
+
+.. code-block:: text
+
+ $ ../bin/pserve development.ini --reload
+
+On Windows:
+
+.. code-block:: text
+
+ c:\pyramidtut\tutorial> ..\Scripts\pserve development.ini --reload
-In a browser, visit ``http://localhost:6543/``. You will see the
-generated application's default page.
+At this point, when you visit ``http://localhost:6543/`` in your web browser,
+you will no longer see an error; instead you will see the generated
+application's default page.
One thing you'll notice is the "debug toolbar" icon on right hand side of the
page. You can read more about the purpose of the icon at
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
index b4038de3c..1f2cfd307 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
@@ -1,18 +1,15 @@
from pyramid.config import Configurator
from sqlalchemy import engine_from_config
-from tutorial.models import initialize_sql
+from .models import DBSession
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
engine = engine_from_config(settings, 'sqlalchemy.')
- initialize_sql(engine)
+ DBSession.configure(bind=engine)
config = Configurator(settings=settings)
- config.add_static_view('static', 'tutorial:static', cache_max_age=3600)
+ config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
- config.add_view('tutorial.views.my_view', route_name='home',
- renderer='templates/mytemplate.pt')
+ config.scan()
return config.make_wsgi_app()
-
-
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py
index e550e3257..631af9b6a 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py
@@ -1,7 +1,9 @@
-from tutorial.models import DBSession
-from tutorial.models import MyModel
+from pyramid.view import view_config
+from .models import DBSession
+from .models import MyModel
+
+@view_config(route_name='home', renderer='templates/mytemplate.pt')
def my_view(request):
- dbsession = DBSession()
- root = dbsession.query(MyModel).filter(MyModel.name==u'root').first()
- return {'root':root, 'project':'tutorial'}
+ one = DBSession.query(MyModel).filter(MyModel.name==u'root').first()
+ return {'one':one, 'project':'tutorial'}
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index a88c22b12..cf27c3514 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -30,6 +30,7 @@ from pyramid import renderers
from pyramid.compat import string_types
from pyramid.compat import urlparse
from pyramid.compat import im_func
+from pyramid.compat import url_quote
from pyramid.exceptions import ConfigurationError
from pyramid.exceptions import PredicateMismatch
from pyramid.httpexceptions import HTTPForbidden
@@ -1429,7 +1430,7 @@ class StaticURLInfo(object):
registry = get_current_registry()
for (url, spec, route_name) in self._get_registrations(registry):
if path.startswith(spec):
- subpath = path[len(spec):]
+ subpath = url_quote(path[len(spec):])
if url is None:
kw['subpath'] = subpath
return request.route_url(route_name, **kw)
diff --git a/pyramid/path.py b/pyramid/path.py
index 9c7be4c57..86c1c5857 100644
--- a/pyramid/path.py
+++ b/pyramid/path.py
@@ -25,7 +25,7 @@ def package_name(pkg_or_module):
package name of the package in which the module lives. If this
function is passed a package, return the dotted Python package
name of the package itself."""
- if pkg_or_module is None:
+ if pkg_or_module is None or pkg_or_module.__name__ == '__main__':
return '__main__'
pkg_filename = pkg_or_module.__file__
pkg_name = pkg_or_module.__name__
diff --git a/pyramid/scaffolds/__init__.py b/pyramid/scaffolds/__init__.py
index 3fba05390..dfde9d855 100644
--- a/pyramid/scaffolds/__init__.py
+++ b/pyramid/scaffolds/__init__.py
@@ -2,12 +2,16 @@ import binascii
import os
from pyramid.compat import native_
+from pyramid.compat import text_
from pyramid.scaffolds.template import Template
class PyramidTemplate(Template):
def pre(self, command, output_dir, vars):
vars['random_string'] = native_(binascii.hexlify(os.urandom(20)))
+ # placeholder text values
+ vars['one'] = text_('one')
+ vars['two'] = text_('two')
package_logger = vars['package']
if package_logger == 'root':
# Rename the app logger in the rare case a project is named 'root'
@@ -33,4 +37,11 @@ class ZODBProjectTemplate(PyramidTemplate):
class AlchemyProjectTemplate(PyramidTemplate):
_template_dir = 'alchemy'
summary = 'Pyramid SQLAlchemy project using url dispatch'
-
+ def post(self, command, output_dir, vars): # pragma: no cover
+ val = PyramidTemplate.post(self, command, output_dir, vars)
+ self.out('')
+ self.out('Please run the "populate_%(package)s" script to set up the '
+ 'SQL database before starting the application (e.g. '
+ '"$myvirtualenv/bin/populate_%(package)s development.ini".)'
+ % vars)
+ return val
diff --git a/pyramid/scaffolds/alchemy/+package+/__init__.py_tmpl b/pyramid/scaffolds/alchemy/+package+/__init__.py
index 24201912b..253341563 100644
--- a/pyramid/scaffolds/alchemy/+package+/__init__.py_tmpl
+++ b/pyramid/scaffolds/alchemy/+package+/__init__.py
@@ -1,18 +1,16 @@
from pyramid.config import Configurator
from sqlalchemy import engine_from_config
-from {{package}}.models import initialize_sql
+from .models import DBSession
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
engine = engine_from_config(settings, 'sqlalchemy.')
- initialize_sql(engine)
+ DBSession.configure(bind=engine)
config = Configurator(settings=settings)
- config.add_static_view('static', '{{package}}:static', cache_max_age=3600)
+ config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
- config.add_view('{{package}}.views.my_view',
- route_name='home',
- renderer='templates/mytemplate.pt')
+ config.scan()
return config.make_wsgi_app()
diff --git a/pyramid/scaffolds/alchemy/+package+/models.py b/pyramid/scaffolds/alchemy/+package+/models.py
index bef483d3a..000dadd91 100644
--- a/pyramid/scaffolds/alchemy/+package+/models.py
+++ b/pyramid/scaffolds/alchemy/+package+/models.py
@@ -1,10 +1,7 @@
-import transaction
-
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Unicode
-from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session
@@ -25,18 +22,3 @@ class MyModel(Base):
self.name = name
self.value = value
-def populate():
- session = DBSession()
- model = MyModel(name='root', value=55)
- session.add(model)
- session.flush()
- transaction.commit()
-
-def initialize_sql(engine):
- DBSession.configure(bind=engine)
- Base.metadata.bind = engine
- Base.metadata.create_all(engine)
- try:
- populate()
- except IntegrityError:
- transaction.abort()
diff --git a/pyramid/scaffolds/alchemy/+package+/scripts/__init__.py b/pyramid/scaffolds/alchemy/+package+/scripts/__init__.py
new file mode 100644
index 000000000..5bb534f79
--- /dev/null
+++ b/pyramid/scaffolds/alchemy/+package+/scripts/__init__.py
@@ -0,0 +1 @@
+# package
diff --git a/pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl b/pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl
new file mode 100644
index 000000000..dd11a64d7
--- /dev/null
+++ b/pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl
@@ -0,0 +1,30 @@
+import os
+import sys
+import logging.config
+
+from paste.deploy.loadwsgi import appconfig
+from sqlalchemy import engine_from_config
+import transaction
+
+from ..models import DBSession
+from ..models import MyModel
+from ..models import Base
+
+def usage(argv):
+ print('usage: %s <Pyramid ini filename>' % os.path.basename(argv[0]))
+ sys.exit(1)
+
+def main(argv=sys.argv):
+ try:
+ config_filename = argv[1]
+ except IndexError:
+ usage(argv)
+ config_filename = os.path.abspath(config_filename)
+ logging.config.fileConfig(config_filename)
+ settings = appconfig('config:' + config_filename)
+ engine = engine_from_config(settings, 'sqlalchemy.')
+ DBSession.configure(bind=engine)
+ Base.metadata.create_all(engine)
+ with transaction.manager:
+ model = MyModel(name={{repr(one)}}, value=1)
+ DBSession.add(model)
diff --git a/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl b/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl
index 29aea7258..3b4b028a9 100644
--- a/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl
+++ b/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl
@@ -1,24 +1,32 @@
import unittest
-from pyramid.config import Configurator
+import transaction
+
from pyramid import testing
-def _initTestingDB():
- from sqlalchemy import create_engine
- from {{package}}.models import initialize_sql
- session = initialize_sql(create_engine('sqlite://'))
- return session
+from .models import DBSession
class TestMyView(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
- _initTestingDB()
+ from sqlalchemy import create_engine
+ engine = create_engine('sqlite://')
+ from .models import (
+ Base,
+ MyModel,
+ )
+ DBSession.configure(bind=engine)
+ Base.metadata.create_all(engine)
+ with transaction.manager:
+ model = MyModel(name={{repr(one)}}, value=55)
+ DBSession.add(model)
def tearDown(self):
+ DBSession.remove()
testing.tearDown()
def test_it(self):
- from {{package}}.views import my_view
+ from .views import my_view
request = testing.DummyRequest()
info = my_view(request)
- self.assertEqual(info['root'].name, 'root')
+ self.assertEqual(info['one'].name, 'one')
self.assertEqual(info['project'], '{{project}}')
diff --git a/pyramid/scaffolds/alchemy/+package+/views.py_tmpl b/pyramid/scaffolds/alchemy/+package+/views.py_tmpl
index 45532b47b..568b73c18 100644
--- a/pyramid/scaffolds/alchemy/+package+/views.py_tmpl
+++ b/pyramid/scaffolds/alchemy/+package+/views.py_tmpl
@@ -1,7 +1,9 @@
-from {{package}}.models import DBSession
-from {{package}}.models import MyModel
+from pyramid.view import view_config
+from .models import DBSession
+from .models import MyModel
+
+@view_config(route_name='home', renderer='templates/mytemplate.pt')
def my_view(request):
- dbsession = DBSession()
- root = dbsession.query(MyModel).filter(MyModel.name=='root').first()
- return {'root':root, 'project':'{{project}}'}
+ one = DBSession.query(MyModel).filter(MyModel.name=={{repr(one)}}).first()
+ return {'one':one, 'project':'{{project}}'}
diff --git a/pyramid/scaffolds/alchemy/setup.py_tmpl b/pyramid/scaffolds/alchemy/setup.py_tmpl
index a2cdaac60..a2766547d 100644
--- a/pyramid/scaffolds/alchemy/setup.py_tmpl
+++ b/pyramid/scaffolds/alchemy/setup.py_tmpl
@@ -41,6 +41,8 @@ setup(name='{{project}}',
entry_points = """\
[paste.app_factory]
main = {{package}}:main
+ [console_scripts]
+ populate_{{project}} = {{package}}.scripts.populate:main
""",
)
diff --git a/pyramid/scripts/proutes.py b/pyramid/scripts/proutes.py
index 9b25ed169..570417e95 100644
--- a/pyramid/scripts/proutes.py
+++ b/pyramid/scripts/proutes.py
@@ -64,14 +64,17 @@ class PRoutesCommand(object):
self.out(
fmt % ('-'*len('Name'), '-'*len('Pattern'), '-'*len('View')))
for route in routes:
+ pattern = route.pattern
+ if not pattern.startswith('/'):
+ pattern = '/' + pattern
request_iface = registry.queryUtility(IRouteRequest,
name=route.name)
view_callable = None
if (request_iface is None) or (route.factory is not None):
- self.out(fmt % (route.name, route.pattern, '<unknown>'))
+ self.out(fmt % (route.name, pattern, '<unknown>'))
else:
view_callable = registry.adapters.lookup(
(IViewClassifier, request_iface, Interface),
IView, name='', default=None)
- self.out(fmt % (route.name, route.pattern, view_callable))
+ self.out(fmt % (route.name, pattern, view_callable))
diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py
index 499d96aca..72e2c0a89 100644
--- a/pyramid/scripts/pshell.py
+++ b/pyramid/scripts/pshell.py
@@ -36,10 +36,9 @@ class PShellCommand(object):
summary = "Open an interactive shell with a Pyramid application loaded"
parser = optparse.OptionParser()
- parser.add_option('-d', '--disable-ipython',
- action='store_true',
- dest='disable_ipython',
- help="Don't use IPython even if it is available")
+ parser.add_option('-p', '--python-shell',
+ action='store', type='string', dest='python_shell',
+ default='', help='ipython | bpython | python')
parser.add_option('--setup',
dest='setup',
help=("A callable that will be passed the environment "
@@ -142,18 +141,36 @@ class PShellCommand(object):
for var in sorted(self.object_help.keys()):
help += '\n %-12s %s' % (var, self.object_help[var])
- if shell is None and not self.options.disable_ipython:
+ if shell is None:
+ shell = self.make_shell()
+
+ try:
+ shell(env, help)
+ finally:
+ closer()
+
+ def make_shell(self):
+ shell = None
+ user_shell = self.options.python_shell.lower()
+ if not user_shell:
+ shell = self.make_ipython_v0_11_shell()
+ if shell is None:
+ shell = self.make_ipython_v0_10_shell()
+ if shell is None:
+ shell = self.make_bpython_shell()
+
+ elif user_shell == 'ipython':
shell = self.make_ipython_v0_11_shell()
if shell is None:
shell = self.make_ipython_v0_10_shell()
+ elif user_shell == 'bpython':
+ shell = self.make_bpython_shell()
+
if shell is None:
shell = self.make_default_shell()
- try:
- shell(env, help)
- finally:
- closer()
+ return shell
def make_default_shell(self, interact=interact):
def shell(env, help):
@@ -163,6 +180,17 @@ class PShellCommand(object):
interact(banner, local=env)
return shell
+ def make_bpython_shell(self, BPShell=None):
+ if BPShell is None: # pragma: no cover
+ try:
+ from bpython import embed
+ BPShell = embed
+ except ImportError:
+ return None
+ def shell(env, help):
+ BPShell(locals_=env, banner=help + '\n')
+ return shell
+
def make_ipython_v0_11_shell(self, IPShellFactory=None):
if IPShellFactory is None: # pragma: no cover
try:
diff --git a/pyramid/settings.py b/pyramid/settings.py
index de91042eb..11587a8be 100644
--- a/pyramid/settings.py
+++ b/pyramid/settings.py
@@ -44,8 +44,13 @@ def aslist_cronly(value):
value = filter(None, [x.strip() for x in value.splitlines()])
return list(value)
-def aslist(value):
+def aslist(value, flatten=True):
+ """ Return a list of strings, separating the input based on newlines
+ and, if flatten=True (the default), also split on spaces within
+ each line."""
values = aslist_cronly(value)
+ if not flatten:
+ return values
result = []
for value in values:
subvalues = value.split()
diff --git a/pyramid/static.py b/pyramid/static.py
index 50a8b918b..ba4ac0fa1 100644
--- a/pyramid/static.py
+++ b/pyramid/static.py
@@ -43,10 +43,12 @@ class _FileResponse(Response):
def __init__(self, path, cache_max_age):
super(_FileResponse, self).__init__(conditional_response=True)
self.last_modified = getmtime(path)
- content_type = mimetypes.guess_type(path, strict=False)[0]
+ content_type, content_encoding = mimetypes.guess_type(path,
+ strict=False)
if content_type is None:
content_type = 'application/octet-stream'
self.content_type = content_type
+ self.content_encoding = content_encoding
content_length = getsize(path)
self.app_iter = _FileIter(open(path, 'rb'), content_length)
# assignment of content_length must come after assignment of app_iter
diff --git a/pyramid/tests/fixtures/static/arcs.svg.tgz b/pyramid/tests/fixtures/static/arcs.svg.tgz
new file mode 100644
index 000000000..376c42ac8
--- /dev/null
+++ b/pyramid/tests/fixtures/static/arcs.svg.tgz
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ id="canvas"
+ width="2000"
+ height="2000"
+ onload="init()">
+
+ <style type="text/css">
+ .ellipse
+ {
+ stroke: red;
+ stroke-width: 2;
+ fill: blue;
+ fill-opacity: 0.1;
+ }
+
+ .axes
+ {
+ stroke: blue;
+ stroke-width: 1;
+ }
+ </style>
+
+ <script>
+ <![CDATA[
+ var COLS = 4;
+ var ROWS = 4;
+ var RX = 80;
+ var RY = 30;
+
+ function init()
+ {
+ var canvas = document.getElementById("canvas");
+
+ var angleStep = 360.0/(COLS*ROWS);
+ var spacing = 2*Math.max(RX, RY)+10;
+ for (var c = 0; c < COLS; ++c) {
+ for (var r = 0; r < ROWS; ++r) {
+ var ellipse = createEllipse((c+ COLS*r)*angleStep, spacing*(c+0.5), spacing*(r+0.5), RX, RY);
+ canvas.appendChild(ellipse);
+ }
+ }
+ }
+
+ function createEllipse(phi, x, y, rx, ry)
+ {
+ var degPerRad = Math.PI/180.0;
+ var e1x = rx*Math.cos(phi*degPerRad);
+ var e1y = rx*Math.sin(phi*degPerRad);
+ var e2x = ry*Math.cos((phi+90)*degPerRad);
+ var e2y = ry*Math.sin((phi+90)*degPerRad);
+
+ var axes = document.createElementNS("http://www.w3.org/2000/svg", "path");
+ axes.setAttribute("class", "axes");
+ axes.setAttribute("d", "M"+x+","+y+" l"+e1x+","+e1y+"M"+x+","+y+" l"+e2x+","+e2y);
+ var ellipse = document.createElementNS("http://www.w3.org/2000/svg", "path");
+ ellipse.setAttribute("class", "ellipse");
+ ellipse.setAttribute("d", "M" + (x+e1x) + "," + (y+e1y) +
+ "A" + rx + "," + ry + " " + phi + " 0,1 " + (x+e2x) + "," + (y+e2y) +
+ "A" + rx + "," + ry + " " + phi + " 1,1 " + (x+e1x) + "," + (y+e1y) +"z");
+
+ var group = document.createElementNS("http://www.w3.org/2000/svg", "g");
+ group.appendChild(axes);
+ group.appendChild(ellipse);
+ return group;
+ }
+
+ ]]>
+ </script>
+</svg>
diff --git a/pyramid/tests/test_chameleon_zpt.py b/pyramid/tests/test_chameleon_zpt.py
index 1a8e6767e..e7a1499e6 100644
--- a/pyramid/tests/test_chameleon_zpt.py
+++ b/pyramid/tests/test_chameleon_zpt.py
@@ -121,7 +121,7 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
instance = self._makeOne(minimal, lookup)
self.assertRaises(ValueError, instance, None, {})
- @skip_on('java', 'pypy')
+ @skip_on('java')
def test_implementation(self):
minimal = self._getTemplatePath('minimal.pt')
lookup = DummyLookup()
diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py
index fa263a311..a9a4d5836 100644
--- a/pyramid/tests/test_config/test_views.py
+++ b/pyramid/tests/test_config/test_views.py
@@ -3362,6 +3362,27 @@ class TestStaticURLInfo(unittest.TestCase):
result = inst.generate('package:path/abc', request, a=1)
self.assertEqual(result, 'url')
+ def test_generate_url_quoted_local(self):
+ inst = self._makeOne()
+ registrations = [(None, 'package:path/', '__viewname/')]
+ inst._get_registrations = lambda *x: registrations
+ def route_url(n, **kw):
+ self.assertEqual(n, '__viewname/')
+ self.assertEqual(kw, {'subpath':'abc%20def', 'a':1})
+ return 'url'
+ request = self._makeRequest()
+ request.route_url = route_url
+ result = inst.generate('package:path/abc def', request, a=1)
+ self.assertEqual(result, 'url')
+
+ def test_generate_url_quoted_remote(self):
+ inst = self._makeOne()
+ registrations = [('http://example.com/', 'package:path/', None)]
+ inst._get_registrations = lambda *x: registrations
+ request = self._makeRequest()
+ result = inst.generate('package:path/abc def', request, a=1)
+ self.assertEqual(result, 'http://example.com/abc%20def')
+
def test_add_already_exists(self):
inst = self._makeOne()
config = self._makeConfig(
diff --git a/pyramid/tests/test_path.py b/pyramid/tests/test_path.py
index c2261d223..29b9baf1f 100644
--- a/pyramid/tests/test_path.py
+++ b/pyramid/tests/test_path.py
@@ -162,6 +162,11 @@ class TestPackageName(unittest.TestCase):
def test_it_None(self):
result = self._callFUT(None)
self.assertEqual(result, '__main__')
+
+ def test_it_main(self):
+ import __main__
+ result = self._callFUT(__main__)
+ self.assertEqual(result, '__main__')
class DummyPackageOrModule:
def __init__(self, real_package_or_module, raise_exc=None):
diff --git a/pyramid/tests/test_scripts/dummy.py b/pyramid/tests/test_scripts/dummy.py
index 3275f7804..d580203af 100644
--- a/pyramid/tests/test_scripts/dummy.py
+++ b/pyramid/tests/test_scripts/dummy.py
@@ -5,7 +5,7 @@ class DummyTweens(object):
self.name_to_alias = {}
def implicit(self):
return self._implicit
-
+
class Dummy:
pass
@@ -31,6 +31,11 @@ class DummyInteractor:
self.banner = banner
self.local = local
+class DummyBPythonShell:
+ def __call__(self, locals_, banner):
+ self.locals_ = locals_
+ self.banner = banner
+
class DummyIPShell(object):
IP = Dummy()
IP.BANNER = 'foo'
@@ -72,7 +77,7 @@ class DummyRoute(object):
def match(self, route):
return self.matchdict
-
+
class DummyRequest:
application_url = 'http://example.com:5432'
script_name = ''
diff --git a/pyramid/tests/test_scripts/test_proutes.py b/pyramid/tests/test_scripts/test_proutes.py
index af6ff19d0..328d1001d 100644
--- a/pyramid/tests/test_scripts/test_proutes.py
+++ b/pyramid/tests/test_scripts/test_proutes.py
@@ -43,6 +43,18 @@ class TestPRoutesCommand(unittest.TestCase):
self.assertEqual(len(L), 3)
self.assertEqual(L[-1].split(), ['a', '/a', '<unknown>'])
+ def test_route_with_no_slash_prefix(self):
+ command = self._makeOne()
+ route = dummy.DummyRoute('a', 'a')
+ mapper = dummy.DummyMapper(route)
+ command._get_mapper = lambda *arg: mapper
+ L = []
+ command.out = L.append
+ result = command.run()
+ self.assertEqual(result, None)
+ self.assertEqual(len(L), 3)
+ self.assertEqual(L[-1].split(), ['a', '/a', '<unknown>'])
+
def test_single_route_no_views_registered(self):
from zope.interface import Interface
from pyramid.registry import Registry
diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py
index e38da2077..765042152 100644
--- a/pyramid/tests/test_scripts/test_pshell.py
+++ b/pyramid/tests/test_scripts/test_pshell.py
@@ -21,7 +21,7 @@ class TestPShellCommand(unittest.TestCase):
if patch_options:
class Options(object): pass
self.options = Options()
- self.options.disable_ipython = True
+ self.options.python_shell = ''
self.options.setup = None
cmd.options = self.options
return cmd
@@ -34,6 +34,14 @@ class TestPShellCommand(unittest.TestCase):
self.assertEqual(interact.local, {'foo': 'bar'})
self.assertTrue('a help message' in interact.banner)
+ def test_make_bpython_shell(self):
+ command = self._makeOne()
+ bpython = dummy.DummyBPythonShell()
+ shell = command.make_bpython_shell(bpython)
+ shell({'foo': 'bar'}, 'a help message')
+ self.assertEqual(bpython.locals_, {'foo': 'bar'})
+ self.assertTrue('a help message' in bpython.banner)
+
def test_make_ipython_v0_11_shell(self):
command = self._makeOne()
ipshell_factory = dummy.DummyIPShellFactory()
@@ -58,6 +66,7 @@ class TestPShellCommand(unittest.TestCase):
shell = dummy.DummyShell()
command.make_ipython_v0_11_shell = lambda: None
command.make_ipython_v0_10_shell = lambda: None
+ command.make_bpython_shell = lambda: None
command.make_default_shell = lambda: shell
command.run()
self.assertTrue(self.config_factory.parser)
@@ -73,14 +82,15 @@ class TestPShellCommand(unittest.TestCase):
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
- def test_command_loads_default_shell_with_ipython_disabled(self):
+ def test_command_loads_default_shell_with_unknown_shell(self):
command = self._makeOne()
shell = dummy.DummyShell()
bad_shell = dummy.DummyShell()
command.make_ipython_v0_11_shell = lambda: bad_shell
command.make_ipython_v0_10_shell = lambda: bad_shell
+ command.make_bpython_shell = lambda: bad_shell
command.make_default_shell = lambda: shell
- command.options.disable_ipython = True
+ command.options.python_shell = 'unknow_python_shell'
command.run()
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
@@ -101,8 +111,9 @@ class TestPShellCommand(unittest.TestCase):
shell = dummy.DummyShell()
command.make_ipython_v0_11_shell = lambda: shell
command.make_ipython_v0_10_shell = lambda: None
+ command.make_bpython_shell = lambda: None
command.make_default_shell = lambda: None
- command.options.disable_ipython = False
+ command.options.python_shell = 'ipython'
command.run()
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
@@ -122,8 +133,9 @@ class TestPShellCommand(unittest.TestCase):
shell = dummy.DummyShell()
command.make_ipython_v0_11_shell = lambda: None
command.make_ipython_v0_10_shell = lambda: shell
+ command.make_bpython_shell = lambda: None
command.make_default_shell = lambda: None
- command.options.disable_ipython = False
+ command.options.python_shell = 'ipython'
command.run()
self.assertTrue(self.config_factory.parser)
self.assertEqual(self.config_factory.parser.filename,
@@ -138,6 +150,76 @@ class TestPShellCommand(unittest.TestCase):
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
+ def test_command_loads_bpython_shell(self):
+ command = self._makeOne()
+ shell = dummy.DummyBPythonShell()
+ command.make_ipython_v0_11_shell = lambda: None
+ command.make_ipython_v0_10_shell = lambda: None
+ command.make_bpython_shell = lambda: shell
+ command.options.python_shell = 'bpython'
+ command.run()
+ self.assertTrue(self.config_factory.parser)
+ self.assertEqual(self.config_factory.parser.filename,
+ '/foo/bar/myapp.ini')
+ self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
+ self.assertEqual(shell.locals_, {
+ 'app':self.bootstrap.app, 'root':self.bootstrap.root,
+ 'registry':self.bootstrap.registry,
+ 'request':self.bootstrap.request,
+ 'root_factory':self.bootstrap.root_factory,
+ })
+ self.assertTrue(self.bootstrap.closer.called)
+ self.assertTrue(shell.banner)
+
+ def test_shell_ipython_ordering(self):
+ command = self._makeOne()
+ shell0_11 = dummy.DummyShell()
+ shell0_10 = dummy.DummyShell()
+ command.make_ipython_v0_11_shell = lambda: shell0_11
+ command.make_ipython_v0_10_shell = lambda: shell0_10
+ command.make_bpython_shell = lambda: None
+ shell = command.make_shell()
+ self.assertEqual(shell, shell0_11)
+
+ command.options.python_shell = 'ipython'
+ shell = command.make_shell()
+ self.assertEqual(shell, shell0_11)
+
+ def test_shell_ordering(self):
+ command = self._makeOne()
+ ipshell = dummy.DummyShell()
+ bpshell = dummy.DummyShell()
+ dshell = dummy.DummyShell()
+ command.make_ipython_v0_11_shell = lambda: None
+ command.make_ipython_v0_10_shell = lambda: None
+ command.make_bpython_shell = lambda: None
+ command.make_default_shell = lambda: dshell
+
+ shell = command.make_shell()
+ self.assertEqual(shell, dshell)
+
+ command.options.python_shell = 'ipython'
+ shell = command.make_shell()
+ self.assertEqual(shell, dshell)
+
+ command.options.python_shell = 'bpython'
+ shell = command.make_shell()
+ self.assertEqual(shell, dshell)
+
+ command.make_ipython_v0_11_shell = lambda: ipshell
+ command.make_bpython_shell = lambda: bpshell
+ command.options.python_shell = 'ipython'
+ shell = command.make_shell()
+ self.assertEqual(shell, ipshell)
+
+ command.options.python_shell = 'bpython'
+ shell = command.make_shell()
+ self.assertEqual(shell, bpshell)
+
+ command.options.python_shell = 'python'
+ shell = command.make_shell()
+ self.assertEqual(shell, dshell)
+
def test_command_loads_custom_items(self):
command = self._makeOne()
model = dummy.Dummy()
diff --git a/pyramid/tests/test_settings.py b/pyramid/tests/test_settings.py
index d02b3cd3e..2ef15f62a 100644
--- a/pyramid/tests/test_settings.py
+++ b/pyramid/tests/test_settings.py
@@ -81,9 +81,9 @@ class Test_aslist_cronly(unittest.TestCase):
self.assertEqual(result, ['abc', 'def'])
class Test_aslist(unittest.TestCase):
- def _callFUT(self, val):
+ def _callFUT(self, val, **kw):
from pyramid.settings import aslist
- return aslist(val)
+ return aslist(val, **kw)
def test_with_list(self):
result = self._callFUT(['abc', 'def'])
@@ -100,3 +100,7 @@ class Test_aslist(unittest.TestCase):
def test_with_string_crsep_spacesep(self):
result = self._callFUT(' abc\n def ghi')
self.assertEqual(result, ['abc', 'def', 'ghi'])
+
+ def test_with_string_crsep_spacesep_no_flatten(self):
+ result = self._callFUT(' abc\n def ghi ', flatten=False)
+ self.assertEqual(result, ['abc', 'def ghi'])
diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py
index a04a47397..bd2c2adef 100644
--- a/pyramid/tests/test_static.py
+++ b/pyramid/tests/test_static.py
@@ -160,6 +160,24 @@ class Test_static_view_use_subpath_False(unittest.TestCase):
response = inst(context, request)
self.assertEqual(response.status, '404 Not Found')
+ def test_resource_with_content_encoding(self):
+ inst = self._makeOne('pyramid.tests:fixtures/static')
+ request = self._makeRequest({'PATH_INFO':'/arcs.svg.tgz'})
+ context = DummyContext()
+ response = inst(context, request)
+ self.assertEqual(response.status, '200 OK')
+ self.assertEqual(response.content_type, 'application/x-tar')
+ self.assertEqual(response.content_encoding, 'gzip')
+
+ def test_resource_no_content_encoding(self):
+ inst = self._makeOne('pyramid.tests:fixtures/static')
+ request = self._makeRequest({'PATH_INFO':'/index.html'})
+ context = DummyContext()
+ response = inst(context, request)
+ self.assertEqual(response.status, '200 OK')
+ self.assertEqual(response.content_type, 'text/html')
+ self.assertEqual(response.content_encoding, None)
+
class Test_static_view_use_subpath_True(unittest.TestCase):
def _getTargetClass(self):
from pyramid.static import static_view
diff --git a/pyramid/view.py b/pyramid/view.py
index 13d5cfe7b..f7d4d8945 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -156,11 +156,12 @@ class view_config(object):
backwards compatibility purposes, as the name
:class:`pyramid.view.bfg_view`.
- The following arguments are supported as arguments to
+ The following arguments are supported to
:class:`pyramid.view.view_config`: ``context``, ``permission``, ``name``,
``request_type``, ``route_name``, ``request_method``, ``request_param``,
``containment``, ``xhr``, ``accept``, ``header``, ``path_info``,
- ``custom_predicates``, ``decorator``, ``mapper``, and ``http_cache``.
+ ``custom_predicates``, ``decorator``, ``mapper``, ``http_cache``,
+ and ``match_param``.
The meanings of these arguments are the same as the arguments passed to
:meth:`pyramid.config.Configurator.add_view`.