summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt44
-rw-r--r--TODO.txt22
-rw-r--r--docs/api/request.rst55
-rw-r--r--docs/conf.py2
-rw-r--r--docs/designdefense.rst12
-rw-r--r--docs/index.rst1
-rw-r--r--docs/latexindex.rst1
-rw-r--r--docs/narr/MyProject/development.ini2
-rw-r--r--docs/narr/MyProject/production.ini2
-rw-r--r--docs/narr/MyProject/setup.py6
-rw-r--r--docs/narr/advconfig.rst20
-rw-r--r--docs/narr/configuration.rst11
-rw-r--r--docs/narr/firstapp.rst24
-rw-r--r--docs/narr/hooks.rst5
-rw-r--r--docs/narr/install.rst11
-rw-r--r--docs/narr/paste.rst2
-rw-r--r--docs/narr/project.rst27
-rw-r--r--docs/narr/startup.rst4
-rw-r--r--docs/tutorials/gae/index.rst231
-rw-r--r--docs/tutorials/wiki/src/authorization/development.ini2
-rw-r--r--docs/tutorials/wiki/src/authorization/production.ini2
-rw-r--r--docs/tutorials/wiki/src/authorization/setup.py1
-rw-r--r--docs/tutorials/wiki/src/basiclayout/development.ini2
-rw-r--r--docs/tutorials/wiki/src/basiclayout/production.ini2
-rw-r--r--docs/tutorials/wiki/src/basiclayout/setup.py1
-rw-r--r--docs/tutorials/wiki/src/models/development.ini2
-rw-r--r--docs/tutorials/wiki/src/models/production.ini2
-rw-r--r--docs/tutorials/wiki/src/models/setup.py1
-rw-r--r--docs/tutorials/wiki/src/tests/development.ini2
-rw-r--r--docs/tutorials/wiki/src/tests/production.ini2
-rw-r--r--docs/tutorials/wiki/src/tests/setup.py1
-rw-r--r--docs/tutorials/wiki/src/views/development.ini2
-rw-r--r--docs/tutorials/wiki/src/views/production.ini2
-rw-r--r--docs/tutorials/wiki/src/views/setup.py1
-rw-r--r--docs/tutorials/wiki2/src/authorization/development.ini2
-rw-r--r--docs/tutorials/wiki2/src/authorization/production.ini2
-rw-r--r--docs/tutorials/wiki2/src/authorization/setup.py1
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/development.ini2
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/production.ini2
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/setup.py1
-rw-r--r--docs/tutorials/wiki2/src/models/development.ini2
-rw-r--r--docs/tutorials/wiki2/src/models/production.ini2
-rw-r--r--docs/tutorials/wiki2/src/models/setup.py1
-rw-r--r--docs/tutorials/wiki2/src/tests/development.ini2
-rw-r--r--docs/tutorials/wiki2/src/tests/production.ini2
-rw-r--r--docs/tutorials/wiki2/src/tests/setup.py1
-rw-r--r--docs/tutorials/wiki2/src/views/development.ini2
-rw-r--r--docs/tutorials/wiki2/src/views/production.ini2
-rw-r--r--docs/tutorials/wiki2/src/views/setup.py1
-rw-r--r--docs/whatsnew-1.3.rst57
-rw-r--r--pyramid/compat.py2
-rw-r--r--pyramid/config/views.py13
-rw-r--r--pyramid/events.py9
-rw-r--r--pyramid/request.py3
-rw-r--r--pyramid/scaffolds/alchemy/development.ini_tmpl2
-rw-r--r--pyramid/scaffolds/alchemy/production.ini_tmpl2
-rw-r--r--pyramid/scaffolds/alchemy/setup.py_tmpl1
-rw-r--r--pyramid/scaffolds/starter/development.ini_tmpl2
-rw-r--r--pyramid/scaffolds/starter/production.ini_tmpl2
-rw-r--r--pyramid/scaffolds/starter/setup.py_tmpl6
-rw-r--r--pyramid/scaffolds/zodb/development.ini_tmpl2
-rw-r--r--pyramid/scaffolds/zodb/production.ini_tmpl2
-rw-r--r--pyramid/scaffolds/zodb/setup.py_tmpl1
-rw-r--r--pyramid/tests/test_config/test_views.py36
-rw-r--r--pyramid/tests/test_events.py10
-rw-r--r--pyramid/tests/test_request.py18
-rw-r--r--pyramid/tests/test_util.py108
-rw-r--r--pyramid/util.py83
-rw-r--r--setup.py2
69 files changed, 524 insertions, 369 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 63c84d0f8..07c0b564d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,34 @@
-Next release
-============
+Unreleased
+==========
+
+Features
+--------
+
+- New API: ``pyramid.request.Request.set_property``. Add lazy property
+ descriptors to a request without changing the request factory. New
+ properties may be reified, effectively caching the value for the lifetime
+ of the instance. Common use-cases for this would be to get a database
+ connection for the request or identify the current user.
+
+- Use the ``waitress`` WSGI server instead of ``wsgiref`` in scaffolding.
+
+Bug Fixes
+---------
+
+- The documentation of ``pyramid.events.subscriber`` indicated that using it
+ as a decorator with no arguments like this::
+
+ @subscriber()
+ def somefunc(event):
+ pass
+
+ Would register ``somefunc`` to receive all events sent via the registry,
+ but this was untrue. Instead, it would receive no events at all. This has
+ now been fixed and the code matches the documentation. See also
+ https://github.com/Pylons/pyramid/issues/386
+
+1.3a3 (2011-12-21)
+==================
Features
--------
@@ -8,6 +37,11 @@ Features
documented in the "Command-Line Pyramid" chapter in the section entitled
"Invoking a Request".
+- Add undocumented ``__discriminator__`` API to derived view callables.
+ e.g. ``adapters.lookup(...).__discriminator__(context, request)``. It will
+ be used by superdynamic systems that require the discriminator to be used
+ for introspection after manual view lookup.
+
Bug Fixes
---------
@@ -20,6 +54,12 @@ Documentation
- Added a section named "Making Your Script into a Console Script" in the
"Command-Line Pyramid" chapter.
+- Removed the "Running Pyramid on Google App Engine" tutorial from the main
+ docs. It survives on in the Cookbook
+ (http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/gae.html).
+ Rationale: it provides the correct info for the Python 2.5 version of GAE
+ only, and this version of Pyramid does not support Python 2.5.
+
1.3a2 (2011-12-14)
==================
diff --git a/TODO.txt b/TODO.txt
index bdb258f04..3d11470dd 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -4,9 +4,25 @@ Pyramid TODOs
Must-Have
---------
-- Introspection:
+- Fix deployment recipes in cookbook (discourage proxying without changing
+ server).
+
+- Use waitress instead of wsgiref.
- * Review narrative docs.
+- pyramid.config.util.ActionInfo.__str__ potentially returns Unicode under
+ Py2, fix.
+
+- Tests for view names/route patterns that contain Unicode.
+
+Nice-to-Have
+------------
+
+- Modify the urldispatch chapter examples to assume a scan rather than
+ ``add_view``.
+
+- Decorator for append_slash_notfound_view_factory?
+
+- Introspection:
* ``default root factory`` category?
@@ -25,8 +41,6 @@ Must-Have
Nice-to-Have
------------
-- Implement analogue of "paster request"?
-
- CherryPy server testing / exploded from CherryPy itself.
- Try "with transaction.manager" in an exception view with SQLA (preempt
diff --git a/docs/api/request.rst b/docs/api/request.rst
index 642e6c84f..9596e5621 100644
--- a/docs/api/request.rst
+++ b/docs/api/request.rst
@@ -8,6 +8,10 @@
.. autoclass:: Request
:members:
:inherited-members:
+ :exclude-members: add_response_callback, add_finished_callback,
+ route_url, route_path, current_route_url,
+ current_route_path, static_url, static_path,
+ model_url, resource_url, set_property
.. attribute:: context
@@ -204,6 +208,57 @@
body associated with this request, this property will raise an
exception. See also :ref:`request_json_body`.
+ .. method:: set_property(func, name=None, reify=False)
+
+ .. versionadded:: 1.3
+
+ Add a callable or a property descriptor to the request instance.
+
+ Properties, unlike attributes, are lazily evaluated by executing
+ an underlying callable when accessed. They can be useful for
+ adding features to an object without any cost if those features
+ go unused.
+
+ A property may also be reified via the
+ :class:`pyramid.decorator.reify` decorator by setting
+ ``reify=True``, allowing the result of the evaluation to be
+ cached. Thus the value of the property is only computed once for
+ the lifetime of the object.
+
+ ``func`` can either be a callable that accepts the request as
+ its single positional parameter, or it can be a property
+ descriptor.
+
+ If the ``func`` is a property descriptor a ``ValueError`` will
+ be raised if ``name`` is ``None`` or ``reify`` is ``True``.
+
+ If ``name`` is None, the name of the property will be computed
+ from the name of the ``func``.
+
+ .. code-block:: python
+ :linenos:
+
+ def _connect(request):
+ conn = request.registry.dbsession()
+ def cleanup(_):
+ conn.close()
+ request.add_finished_callback(cleanup)
+ return conn
+
+ @subscriber(NewRequest)
+ def new_request(event):
+ request = event.request
+ request.set_property(_connect, 'db', reify=True)
+
+ The subscriber doesn't actually connect to the database, it just
+ provides the API which, when accessed via ``request.db``, will
+ create the connection. Thanks to reify, only one connection is
+ made per-request even if ``request.db`` is accessed many times.
+
+ This pattern provides a way to augment the ``request`` object
+ without having to subclass it, which can be useful for extension
+ authors.
+
.. note::
For information about the API of a :term:`multidict` structure (such as
diff --git a/docs/conf.py b/docs/conf.py
index 5281017e7..39006f1bb 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -80,7 +80,7 @@ copyright = '%s, Agendaless Consulting' % datetime.datetime.now().year
# other places throughout the built documents.
#
# The short X.Y version.
-version = '1.3a2'
+version = '1.3a3'
# The full version, including alpha/beta/rc tags.
release = version
diff --git a/docs/designdefense.rst b/docs/designdefense.rst
index 80675ce83..59b0e5a2d 100644
--- a/docs/designdefense.rst
+++ b/docs/designdefense.rst
@@ -1628,8 +1628,8 @@ comments take into account what we've discussed in the
.. code-block:: python
:linenos:
- from pyramid.response import Response # explicit response objects, no TL
- from paste.httpserver import serve # explicitly WSGI
+ from pyramid.response import Response # explicit response, no TL
+ from wsgiref.simple_server import make_server # explicitly WSGI
def hello_world(request): # accepts a request; no request thread local reqd
# explicit response object means no response threadlocal
@@ -1640,7 +1640,8 @@ comments take into account what we've discussed in the
config = Configurator() # no global application object.
config.add_view(hello_world) # explicit non-decorator registration
app = config.make_wsgi_app() # explicitly WSGI
- serve(app, host='0.0.0.0') # explicitly WSGI
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever() # explicitly WSGI
Pyramid Doesn't Offer Pluggable Apps
------------------------------------
@@ -1736,7 +1737,7 @@ If you can understand this hello world program, you can use Pyramid:
.. code-block:: python
:linenos:
- from paste.httpserver import serve
+ from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
@@ -1747,7 +1748,8 @@ If you can understand this hello world program, you can use Pyramid:
config = Configurator()
config.add_view(hello_world)
app = config.make_wsgi_app()
- serve(app)
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever()
Pyramid has ~ 650 pages of documentation (printed), covering topics from the
very basic to the most advanced. *Nothing* is left undocumented, quite
diff --git a/docs/index.rst b/docs/index.rst
index 21e587992..be03448c9 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -109,7 +109,6 @@ applications to various platforms.
tutorials/wiki2/index.rst
tutorials/wiki/index.rst
tutorials/bfg/index.rst
- tutorials/gae/index.rst
tutorials/modwsgi/index.rst
Reference Material
diff --git a/docs/latexindex.rst b/docs/latexindex.rst
index 4db5b64b2..99ec2f54d 100644
--- a/docs/latexindex.rst
+++ b/docs/latexindex.rst
@@ -73,7 +73,6 @@ Tutorials
tutorials/wiki/index.rst
tutorials/wiki2/index.rst
tutorials/bfg/index.rst
- tutorials/gae/index.rst
tutorials/modwsgi/index.rst
.. _api_reference:
diff --git a/docs/narr/MyProject/development.ini b/docs/narr/MyProject/development.ini
index d61da580f..2ccedb27b 100644
--- a/docs/narr/MyProject/development.ini
+++ b/docs/narr/MyProject/development.ini
@@ -10,7 +10,7 @@ pyramid.default_locale_name = en
pyramid.includes = pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/narr/MyProject/production.ini b/docs/narr/MyProject/production.ini
index 97050e8fe..43ea1d140 100644
--- a/docs/narr/MyProject/production.ini
+++ b/docs/narr/MyProject/production.ini
@@ -9,7 +9,7 @@ pyramid.debug_templates = false
pyramid.default_locale_name = en
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/narr/MyProject/setup.py b/docs/narr/MyProject/setup.py
index 74879f65d..b119a954b 100644
--- a/docs/narr/MyProject/setup.py
+++ b/docs/narr/MyProject/setup.py
@@ -6,7 +6,11 @@ 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()
-requires = ['pyramid', 'pyramid_debugtoolbar']
+requires = [
+ 'pyramid',
+ 'pyramid_debugtoolbar',
+ 'waitress',
+ ]
setup(name='MyProject',
version='0.0',
diff --git a/docs/narr/advconfig.rst b/docs/narr/advconfig.rst
index 3a7bf2805..5f2175d2a 100644
--- a/docs/narr/advconfig.rst
+++ b/docs/narr/advconfig.rst
@@ -27,7 +27,7 @@ configured imperatively:
.. code-block:: python
:linenos:
- from paste.httpserver import serve
+ from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
@@ -38,7 +38,8 @@ configured imperatively:
config = Configurator()
config.add_view(hello_world)
app = config.make_wsgi_app()
- serve(app, host='0.0.0.0')
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever()
When you start this application, all will be OK. However, what happens if we
try to add another view to the configuration with the same set of
@@ -47,7 +48,7 @@ try to add another view to the configuration with the same set of
.. code-block:: python
:linenos:
- from paste.httpserver import serve
+ from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
@@ -66,7 +67,8 @@ try to add another view to the configuration with the same set of
config.add_view(goodbye_world, name='hello')
app = config.make_wsgi_app()
- serve(app, host='0.0.0.0')
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever()
The application now has two conflicting view configuration statements. When
we try to start it again, it won't start. Instead, we'll receive a traceback
@@ -170,7 +172,7 @@ application that generates conflicts:
.. code-block:: python
:linenos:
- from paste.httpserver import serve
+ from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
@@ -189,7 +191,8 @@ application that generates conflicts:
config.add_view(goodbye_world, name='hello')
app = config.make_wsgi_app()
- serve(app, host='0.0.0.0')
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever()
We can prevent the two ``add_view`` calls from conflicting by issuing a call
to :meth:`~pyramid.config.Configurator.commit` between them:
@@ -197,7 +200,7 @@ to :meth:`~pyramid.config.Configurator.commit` between them:
.. code-block:: python
:linenos:
- from paste.httpserver import serve
+ from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
@@ -218,7 +221,8 @@ to :meth:`~pyramid.config.Configurator.commit` between them:
config.add_view(goodbye_world, name='hello')
app = config.make_wsgi_app()
- serve(app, host='0.0.0.0')
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever()
In the above example we've issued a call to
:meth:`~pyramid.config.Configurator.commit` between the two ``add_view``
diff --git a/docs/narr/configuration.rst b/docs/narr/configuration.rst
index 597d48b09..37122e382 100644
--- a/docs/narr/configuration.rst
+++ b/docs/narr/configuration.rst
@@ -36,7 +36,7 @@ applications, configured imperatively:
.. code-block:: python
:linenos:
- from paste.httpserver import serve
+ from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
@@ -46,8 +46,8 @@ applications, configured imperatively:
if __name__ == '__main__':
config = Configurator()
config.add_view(hello_world)
- app = config.make_wsgi_app()
- serve(app, host='0.0.0.0')
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever()
We won't talk much about what this application does yet. Just note that the
"configuration' statements take place underneath the ``if __name__ ==
@@ -105,7 +105,7 @@ in a package and its subpackages. For example:
.. code-block:: python
:linenos:
- from paste.httpserver import serve
+ from wsgiref.simple_server import make_server
from pyramid.response import Response
from pyramid.view import view_config
@@ -118,7 +118,8 @@ in a package and its subpackages. For example:
config = Configurator()
config.scan()
app = config.make_wsgi_app()
- serve(app, host='0.0.0.0')
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever()
The scanning machinery imports each module and subpackage in a package or
module recursively, looking for special attributes attached to objects
diff --git a/docs/narr/firstapp.rst b/docs/narr/firstapp.rst
index c082f616b..922b0ea82 100644
--- a/docs/narr/firstapp.rst
+++ b/docs/narr/firstapp.rst
@@ -54,9 +54,8 @@ The script imports the :class:`~pyramid.config.Configurator` class from the
Like many other Python web frameworks, :app:`Pyramid` uses the :term:`WSGI`
protocol to connect an application and a web server together. The
-:mod:`paste.httpserver` server is used in this example as a WSGI server for
-convenience, as the ``paste`` package is a dependency of :app:`Pyramid`
-itself.
+:mod:`wsgiref` server is used in this example as a WSGI server for
+convenience, as it is shipped within the Python standard library.
The script also imports the :class:`pyramid.response.Response` class for
later use. An instance of this class will be used to create a web response.
@@ -205,14 +204,17 @@ WSGI Application Serving
:lines: 13
Finally, we actually serve the application to requestors by starting up a
-WSGI server. We happen to use the :func:`paste.httpserver.serve` WSGI server
-runner, passing it the ``app`` object (a :term:`router`) as the application
-we wish to serve. We also pass in an argument ``host='0.0.0.0'``, meaning
-"listen on all TCP interfaces." By default, the HTTP server listens
-only on the ``127.0.0.1`` interface, which is problematic if you're running
-the server on a remote system and you wish to access it with a web browser
-from a local system. We don't specify a TCP port number to listen on; this
-means we want to use the default TCP port, which is 8080.
+WSGI server. We happen to use the :mod:`wsgiref` ``make_server`` server
+maker for this purpose. We pass in as the first argument ``'0.0.0.0'``,
+which means "listen on all TCP interfaces." By default, the HTTP server
+listens only on the ``127.0.0.1`` interface, which is problematic if you're
+running the server on a remote system and you wish to access it with a web
+browser from a local system. We also specify a TCP port number to listen on,
+which is 8080, passing it as the second argument. The final argument ios ,
+passing it the ``app`` object (a :term:`router`), which is the the
+application we wish to serve. Finally, we call the server's
+``serve_forever`` method, which starts the main loop in which it will wait
+for requests from the outside world.
When this line is invoked, it causes the server to start listening on TCP
port 8080. The server will serve requests forever, or at least until we stop
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index a1ed6c7ff..fd6544416 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -812,7 +812,7 @@ performed, enabling you to set up the utility in advance:
.. code-block:: python
:linenos:
- from paste.httpserver import serve
+ from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from mypackage.interfaces import IMyUtility
@@ -831,7 +831,8 @@ performed, enabling you to set up the utility in advance:
config.registry.registerUtility(UtilityImplementation())
config.scan()
app = config.make_wsgi_app()
- serve(app, host='0.0.0.0')
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever()
For full details, please read the `Venusian documentation
<http://docs.repoze.org/venusian>`_.
diff --git a/docs/narr/install.rst b/docs/narr/install.rst
index 3de4d6e27..172cfd6d3 100644
--- a/docs/narr/install.rst
+++ b/docs/narr/install.rst
@@ -20,7 +20,7 @@ run :app:`Pyramid`.
: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, and :term:`PyPy` (1.6+).
+known to run on :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
@@ -315,15 +315,6 @@ Installing :app:`Pyramid` on a Windows System
c:\env> Scripts\easy_install pyramid
-.. index::
- single: installing on Google App Engine
-
-Installing :app:`Pyramid` on Google App Engine
--------------------------------------------------
-
-:ref:`appengine_tutorial` documents the steps required to install a
-:app:`Pyramid` application on Google App Engine.
-
What Gets Installed
-------------------
diff --git a/docs/narr/paste.rst b/docs/narr/paste.rst
index cf8f96c8a..86b047aec 100644
--- a/docs/narr/paste.rst
+++ b/docs/narr/paste.rst
@@ -17,7 +17,7 @@ a Pyramid application that doesn't use PasteDeploy in
:ref:`firstapp_chapter`. However, all Pyramid scaffolds render PasteDeploy
configuration files, to provide new developers with a standardized way of
setting deployment values, and to provide new users with a standardized way
-of starting, stopping, and debugging an application.
+of starting, stopping, and debugging an application.
This chapter is not a replacement for documentation about PasteDeploy; it
only contextualizes the use of PasteDeploy within Pyramid. For detailed
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index af8714573..896b65249 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -283,7 +283,7 @@ For example, on UNIX:
$ ../bin/pserve development.ini --reload
Starting subprocess with file monitor
Starting server in PID 16601.
- serving on 0.0.0.0:6543 view at http://127.0.0.1:6543
+ Starting HTTP server on http://0.0.0.0:6543
For more detailed information about the startup process, see
:ref:`startup_chapter`. For more information about environment variables and
@@ -888,26 +888,11 @@ Using an Alternate WSGI Server
The code generated by :app:`Pyramid` scaffolding assumes that you will be
using the ``pserve`` command to start your application while you do
-development. The default rendering of Pyramid scaffolding uses the *wsgiref*
-WSGI server, which is a server that is ill-suited for production usage: its
-main feature is that it works on all platforms and all systems, making it a
-good choice as a default server from the perspective of Pyramid's developers.
-
-To use a server more suitable for production, you have a number of choices.
-Replace the ``use = egg:pyramid#wsgref`` line in your ``production.ini`` with
-one of the following.
-
-``use = egg:Paste#http``
-
- ``paste.httpserver`` is Windows, UNIX, and Python 2 compatible. You'll
- need to ``easy_install Paste`` into your Pyramid virtualenv for this server
- to work.
-
-``use = egg:pyramid#cherrypy``
-
- The ``CherryPy`` WSGI server is Windows, UNIX, Python 2, and Python 3
- compatible. You'll need to ``easy_install CherryPy`` into your Pyramid
- virtualenv for this server to work.
+development. The default rendering of Pyramid scaffolding uses the
+*waitress* WSGI server, which is a server that is suited for production
+usage. It's not very fast, or very featureful: its main feature is that it
+works on all platforms and all systems, making it a good choice as a default
+server from the perspective of Pyramid's developers.
``pserve`` is by no means the only way to start up and serve a :app:`Pyramid`
application. As we saw in :ref:`firstapp_chapter`, ``pserve`` needn't be
diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst
index a7fc5d33c..78b119687 100644
--- a/docs/narr/startup.rst
+++ b/docs/narr/startup.rst
@@ -133,8 +133,8 @@ Here's a high-level time-ordered overview of what happens when you press
far as ``pserve`` is concerned, it is "just another WSGI application".
#. ``pserve`` starts the WSGI *server* defined within the ``[server:main]``
- section. In our case, this is the ``egg:pyramid#wsgiref`` server (``use =
- egg:pyramid#wsgiref``), and it will listen on all interfaces (``host =
+ section. In our case, this is the Waitress server (``use =
+ egg:waitress#main``), and it will listen on all interfaces (``host =
0.0.0.0``), on port number 6543 (``port = 6543``). The server code itself
is what prints ``serving on 0.0.0.0:6543 view at http://127.0.0.1:6543``.
The server serves the application, and the application is running, waiting
diff --git a/docs/tutorials/gae/index.rst b/docs/tutorials/gae/index.rst
deleted file mode 100644
index 3bd739480..000000000
--- a/docs/tutorials/gae/index.rst
+++ /dev/null
@@ -1,231 +0,0 @@
-.. _appengine_tutorial:
-
-Running :app:`Pyramid` on Google's App Engine
-================================================
-
-It is possible to run a :app:`Pyramid` application on Google's `App
-Engine <http://code.google.com/appengine/>`_. Content from this
-tutorial was contributed by YoungKing, based on the
-`"appengine-monkey" tutorial for Pylons
-<http://code.google.com/p/appengine-monkey/wiki/Pylons>`_. This
-tutorial is written in terms of using the command line on a UNIX
-system; it should be possible to perform similar actions on a Windows
-system.
-
-#. Download Google's `App Engine SDK
- <http://code.google.com/appengine/downloads.html>`_ and install it
- on your system.
-
-#. Use Subversion to check out the source code for
- ``appengine-monkey``.
-
- .. code-block:: text
-
- $ svn co http://appengine-monkey.googlecode.com/svn/trunk/ \
- appengine-monkey
-
-#. Use ``appengine_homedir.py`` script in ``appengine-monkey`` to
- create a :term:`virtualenv` for your application.
-
- .. code-block:: text
-
- $ export GAE_PATH=/usr/local/google_appengine
- $ python2.5 /path/to/appengine-monkey/appengine-homedir.py --gae \
- $GAE_PATH pyramidapp
-
- Note that ``$GAE_PATH`` should be the path where you have unpacked
- the App Engine SDK. (On Mac OS X at least,
- ``/usr/local/google_appengine`` is indeed where the installer puts
- it).
-
- This will set up an environment in ``pyramidapp/``, with some tools
- installed in ``pyramidapp/bin``. There will also be a directory
- ``pyramidapp/app/`` which is the directory you will upload to
- appengine.
-
-#. Install :app:`Pyramid` into the virtualenv
-
- .. code-block:: text
-
- $ cd pyramidapp/
- $ bin/easy_install pyramid
-
- This will install :app:`Pyramid` in the environment.
-
-#. Create your application
-
- We'll use the standard way to create a :app:`Pyramid`
- application, but we'll have to move some files around when we are
- done. The below commands assume your current working directory is
- the ``pyramidapp`` virtualenv directory you created in the third step
- above:
-
- .. code-block:: text
-
- $ cd app
- $ rm -rf pyramidapp
- $ bin/pcreate -s starter pyramidapp
- $ mv pyramidapp aside
- $ mv aside/pyramidapp .
- $ rm -rf aside
-
-#. Edit ``config.py``
-
- Edit the ``APP_NAME`` and ``APP_ARGS`` settings within
- ``config.py``. The ``APP_NAME`` must be ``pyramidapp:main``, and
- the APP_ARGS must be ``({},)``. Any other settings in
- ``config.py`` should remain the same.
-
- .. code-block:: python
-
- APP_NAME = 'pyramidapp:main'
- APP_ARGS = ({},)
-
-#. Edit ``runner.py``
-
- To prevent errors for ``import site``, add this code stanza before
- ``import site`` in app/runner.py:
-
- .. code-block:: python
-
- import sys
- sys.path = [path for path in sys.path if 'site-packages' not in path]
- import site
-
- You will also need to comment out the line that starts with
- ``assert sys.path`` in the file.
-
- .. code-block:: python
-
- # comment the sys.path assertion out
- # assert sys.path[:len(cur_sys_path)] == cur_sys_path, (
- # "addsitedir() caused entries to be prepended to sys.path")
-
- For GAE development environment 1.3.0 or better, you will also need
- the following somewhere near the top of the ``runner.py`` file to
- fix a compatibility issue with ``appengine-monkey``:
-
- .. code-block:: python
-
- import os
- os.mkdir = None
-
-#. Run the application. ``dev_appserver.py`` is typically installed
- by the SDK in the global path but you need to be sure to run it
- with Python 2.5 (or whatever version of Python your GAE SDK
- expects).
-
- .. code-block:: text
- :linenos:
-
- $ cd ../..
- $ python2.5 /usr/local/bin/dev_appserver.py pyramidapp/app/
-
- Startup success looks something like this:
-
- .. code-block:: text
-
- [chrism@vitaminf pyramid_gae]$ python2.5 \
- /usr/local/bin/dev_appserver.py \
- pyramidapp/app/
- INFO 2009-05-03 22:23:13,887 appengine_rpc.py:157] # ... more...
- Running application pyramidapp on port 8080: http://localhost:8080
-
- You may need to run "Make Symlinks" from the Google App Engine
- Launcher GUI application if your system doesn't already have the
- ``dev_appserver.py`` script sitting around somewhere.
-
-#. Hack on your pyramid application, using a normal run, debug, restart
- process. For tips on how to use the ``pdb`` module within Google
- App Engine, `see this blog post
- <http://jjinux.blogspot.com/2008/05/python-debugging-google-app-engine-apps.html>`_.
- In particular, you can create a function like so and call it to
- drop your console into a pdb trace:
-
- .. code-block:: python
- :linenos:
-
- def set_trace():
- import pdb, sys
- debugger = pdb.Pdb(stdin=sys.__stdin__,
- stdout=sys.__stdout__)
- debugger.set_trace(sys._getframe().f_back)
-
-#. `Sign up for a GAE account <http://code.google.com/appengine/>`_
- and create an application. You'll need a mobile phone to accept an
- SMS in order to receive authorization.
-
-#. Edit the application's ID in ``app.yaml`` to match the application
- name you created during GAE account setup.
-
- .. code-block:: yaml
-
- application: mycoolpyramidapp
-
-#. Upload the application
-
- .. code-block:: text
-
- $ python2.5 /usr/local/bin/appcfg.py update pyramidapp/app
-
- You almost certainly won't hit the 3000-file GAE file number limit
- when invoking this command. If you do, however, it will look like
- so:
-
- .. code-block:: text
-
- HTTPError: HTTP Error 400: Bad Request
- Rolling back the update.
- Error 400: --- begin server output ---
- Max number of files and blobs is 3000.
- --- end server output ---
-
- If you do experience this error, you will be able to get around
- this by zipping libraries. You can use ``pip`` to create zipfiles
- from packages. See :ref:`pip_zip` for more information about this.
-
- A successful upload looks like so:
-
- .. code-block:: text
-
- [chrism@vitaminf pyramidapp]$ python2.5 /usr/local/bin/appcfg.py \
- update ../pyramidapp/app/
- Scanning files on local disk.
- Scanned 500 files.
- # ... more output ...
- Will check again in 16 seconds.
- Checking if new version is ready to serve.
- Closing update: new version is ready to start serving.
- Uploading index definitions.
-
-#. Visit ``http://<yourapp>.appspot.com`` in a browser.
-
-.. _pip_zip:
-
-Zipping Files Via Pip
----------------------
-
-If you hit the Google App Engine 3000-file limit, you may need to
-create zipfile archives out of some distributions installed in your
-application's virtualenv.
-
-First, see which packages are available for zipping:
-
-.. code-block:: text
-
- $ bin/pip zip -l
-
-This shows your zipped packages (by default, none) and your unzipped
-packages. You can zip a package like so:
-
-.. code-block:: text
-
- $ bin/pip zip pytz-2009g-py2.5.egg
-
-Note that it requires the whole egg file name. For a :app:`Pyramid` app, the
-following packages are good candidates to be zipped.
-
-- Chameleon
-- zope.i18n
-
-Once the zipping procedure is finished you can try uploading again.
diff --git a/docs/tutorials/wiki/src/authorization/development.ini b/docs/tutorials/wiki/src/authorization/development.ini
index 0f950e260..47566515c 100644
--- a/docs/tutorials/wiki/src/authorization/development.ini
+++ b/docs/tutorials/wiki/src/authorization/development.ini
@@ -13,7 +13,7 @@ tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki/src/authorization/production.ini b/docs/tutorials/wiki/src/authorization/production.ini
index f632f89d1..919efce1e 100644
--- a/docs/tutorials/wiki/src/authorization/production.ini
+++ b/docs/tutorials/wiki/src/authorization/production.ini
@@ -12,7 +12,7 @@ tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki/src/authorization/setup.py b/docs/tutorials/wiki/src/authorization/setup.py
index 284bc216e..31c51dbcf 100644
--- a/docs/tutorials/wiki/src/authorization/setup.py
+++ b/docs/tutorials/wiki/src/authorization/setup.py
@@ -12,6 +12,7 @@ requires = [
'pyramid_tm',
'pyramid_debugtoolbar',
'ZODB3',
+ 'waitress',
'docutils',
]
diff --git a/docs/tutorials/wiki/src/basiclayout/development.ini b/docs/tutorials/wiki/src/basiclayout/development.ini
index e296ea1a1..3acff7f6d 100644
--- a/docs/tutorials/wiki/src/basiclayout/development.ini
+++ b/docs/tutorials/wiki/src/basiclayout/development.ini
@@ -13,7 +13,7 @@ tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki/src/basiclayout/production.ini b/docs/tutorials/wiki/src/basiclayout/production.ini
index f632f89d1..919efce1e 100644
--- a/docs/tutorials/wiki/src/basiclayout/production.ini
+++ b/docs/tutorials/wiki/src/basiclayout/production.ini
@@ -12,7 +12,7 @@ tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki/src/basiclayout/setup.py b/docs/tutorials/wiki/src/basiclayout/setup.py
index d5fd8891f..43600e239 100644
--- a/docs/tutorials/wiki/src/basiclayout/setup.py
+++ b/docs/tutorials/wiki/src/basiclayout/setup.py
@@ -12,6 +12,7 @@ requires = [
'pyramid_tm',
'pyramid_debugtoolbar',
'ZODB3',
+ 'waitress',
]
setup(name='tutorial',
diff --git a/docs/tutorials/wiki/src/models/development.ini b/docs/tutorials/wiki/src/models/development.ini
index 0f950e260..47566515c 100644
--- a/docs/tutorials/wiki/src/models/development.ini
+++ b/docs/tutorials/wiki/src/models/development.ini
@@ -13,7 +13,7 @@ tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki/src/models/production.ini b/docs/tutorials/wiki/src/models/production.ini
index f632f89d1..919efce1e 100644
--- a/docs/tutorials/wiki/src/models/production.ini
+++ b/docs/tutorials/wiki/src/models/production.ini
@@ -12,7 +12,7 @@ tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki/src/models/setup.py b/docs/tutorials/wiki/src/models/setup.py
index d5fd8891f..43600e239 100644
--- a/docs/tutorials/wiki/src/models/setup.py
+++ b/docs/tutorials/wiki/src/models/setup.py
@@ -12,6 +12,7 @@ requires = [
'pyramid_tm',
'pyramid_debugtoolbar',
'ZODB3',
+ 'waitress',
]
setup(name='tutorial',
diff --git a/docs/tutorials/wiki/src/tests/development.ini b/docs/tutorials/wiki/src/tests/development.ini
index 0f950e260..47566515c 100644
--- a/docs/tutorials/wiki/src/tests/development.ini
+++ b/docs/tutorials/wiki/src/tests/development.ini
@@ -13,7 +13,7 @@ tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki/src/tests/production.ini b/docs/tutorials/wiki/src/tests/production.ini
index f632f89d1..919efce1e 100644
--- a/docs/tutorials/wiki/src/tests/production.ini
+++ b/docs/tutorials/wiki/src/tests/production.ini
@@ -12,7 +12,7 @@ tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki/src/tests/setup.py b/docs/tutorials/wiki/src/tests/setup.py
index dfecb7c5c..a7ad7317c 100644
--- a/docs/tutorials/wiki/src/tests/setup.py
+++ b/docs/tutorials/wiki/src/tests/setup.py
@@ -12,6 +12,7 @@ requires = [
'pyramid_tm',
'pyramid_debugtoolbar',
'ZODB3',
+ 'waitress',
'docutils',
'WebTest', # add this
]
diff --git a/docs/tutorials/wiki/src/views/development.ini b/docs/tutorials/wiki/src/views/development.ini
index e296ea1a1..3acff7f6d 100644
--- a/docs/tutorials/wiki/src/views/development.ini
+++ b/docs/tutorials/wiki/src/views/development.ini
@@ -13,7 +13,7 @@ tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki/src/views/production.ini b/docs/tutorials/wiki/src/views/production.ini
index f632f89d1..919efce1e 100644
--- a/docs/tutorials/wiki/src/views/production.ini
+++ b/docs/tutorials/wiki/src/views/production.ini
@@ -12,7 +12,7 @@ tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki/src/views/setup.py b/docs/tutorials/wiki/src/views/setup.py
index 866f12bef..a6be89b2e 100644
--- a/docs/tutorials/wiki/src/views/setup.py
+++ b/docs/tutorials/wiki/src/views/setup.py
@@ -12,6 +12,7 @@ requires = [
'pyramid_tm',
'pyramid_debugtoolbar',
'ZODB3',
+ 'waitress',
'docutils',
]
diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini
index 4f7493cba..2bb74454c 100644
--- a/docs/tutorials/wiki2/src/authorization/development.ini
+++ b/docs/tutorials/wiki2/src/authorization/development.ini
@@ -13,7 +13,7 @@ pyramid.includes = pyramid_debugtoolbar
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki2/src/authorization/production.ini b/docs/tutorials/wiki2/src/authorization/production.ini
index 53eaf20a1..ec6dea135 100644
--- a/docs/tutorials/wiki2/src/authorization/production.ini
+++ b/docs/tutorials/wiki2/src/authorization/production.ini
@@ -12,7 +12,7 @@ pyramid.includes = pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki2/src/authorization/setup.py b/docs/tutorials/wiki2/src/authorization/setup.py
index 09769bfff..7f1ae10b1 100644
--- a/docs/tutorials/wiki2/src/authorization/setup.py
+++ b/docs/tutorials/wiki2/src/authorization/setup.py
@@ -15,6 +15,7 @@ requires = [
'pyramid_debugtoolbar',
'zope.sqlalchemy',
'docutils',
+ 'waitress',
]
if sys.version_info[:3] < (2,5,0):
diff --git a/docs/tutorials/wiki2/src/basiclayout/development.ini b/docs/tutorials/wiki2/src/basiclayout/development.ini
index 4f7493cba..2bb74454c 100644
--- a/docs/tutorials/wiki2/src/basiclayout/development.ini
+++ b/docs/tutorials/wiki2/src/basiclayout/development.ini
@@ -13,7 +13,7 @@ pyramid.includes = pyramid_debugtoolbar
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki2/src/basiclayout/production.ini b/docs/tutorials/wiki2/src/basiclayout/production.ini
index 53eaf20a1..ec6dea135 100644
--- a/docs/tutorials/wiki2/src/basiclayout/production.ini
+++ b/docs/tutorials/wiki2/src/basiclayout/production.ini
@@ -12,7 +12,7 @@ pyramid.includes = pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki2/src/basiclayout/setup.py b/docs/tutorials/wiki2/src/basiclayout/setup.py
index 0ca918cab..44796d4ad 100644
--- a/docs/tutorials/wiki2/src/basiclayout/setup.py
+++ b/docs/tutorials/wiki2/src/basiclayout/setup.py
@@ -14,6 +14,7 @@ requires = [
'pyramid_tm',
'pyramid_debugtoolbar',
'zope.sqlalchemy',
+ 'waitress',
]
if sys.version_info[:3] < (2,5,0):
diff --git a/docs/tutorials/wiki2/src/models/development.ini b/docs/tutorials/wiki2/src/models/development.ini
index 4f7493cba..2bb74454c 100644
--- a/docs/tutorials/wiki2/src/models/development.ini
+++ b/docs/tutorials/wiki2/src/models/development.ini
@@ -13,7 +13,7 @@ pyramid.includes = pyramid_debugtoolbar
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki2/src/models/production.ini b/docs/tutorials/wiki2/src/models/production.ini
index 53eaf20a1..ec6dea135 100644
--- a/docs/tutorials/wiki2/src/models/production.ini
+++ b/docs/tutorials/wiki2/src/models/production.ini
@@ -12,7 +12,7 @@ pyramid.includes = pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki2/src/models/setup.py b/docs/tutorials/wiki2/src/models/setup.py
index 0ca918cab..44796d4ad 100644
--- a/docs/tutorials/wiki2/src/models/setup.py
+++ b/docs/tutorials/wiki2/src/models/setup.py
@@ -14,6 +14,7 @@ requires = [
'pyramid_tm',
'pyramid_debugtoolbar',
'zope.sqlalchemy',
+ 'waitress',
]
if sys.version_info[:3] < (2,5,0):
diff --git a/docs/tutorials/wiki2/src/tests/development.ini b/docs/tutorials/wiki2/src/tests/development.ini
index 4f7493cba..2bb74454c 100644
--- a/docs/tutorials/wiki2/src/tests/development.ini
+++ b/docs/tutorials/wiki2/src/tests/development.ini
@@ -13,7 +13,7 @@ pyramid.includes = pyramid_debugtoolbar
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki2/src/tests/production.ini b/docs/tutorials/wiki2/src/tests/production.ini
index 53eaf20a1..ec6dea135 100644
--- a/docs/tutorials/wiki2/src/tests/production.ini
+++ b/docs/tutorials/wiki2/src/tests/production.ini
@@ -12,7 +12,7 @@ pyramid.includes = pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki2/src/tests/setup.py b/docs/tutorials/wiki2/src/tests/setup.py
index f965ccc6e..d8be7539b 100644
--- a/docs/tutorials/wiki2/src/tests/setup.py
+++ b/docs/tutorials/wiki2/src/tests/setup.py
@@ -14,6 +14,7 @@ requires = [
'pyramid_tm',
'pyramid_debugtoolbar',
'zope.sqlalchemy',
+ 'waitress',
'docutils',
'WebTest', # add this
]
diff --git a/docs/tutorials/wiki2/src/views/development.ini b/docs/tutorials/wiki2/src/views/development.ini
index 4f7493cba..2bb74454c 100644
--- a/docs/tutorials/wiki2/src/views/development.ini
+++ b/docs/tutorials/wiki2/src/views/development.ini
@@ -13,7 +13,7 @@ pyramid.includes = pyramid_debugtoolbar
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki2/src/views/production.ini b/docs/tutorials/wiki2/src/views/production.ini
index 53eaf20a1..ec6dea135 100644
--- a/docs/tutorials/wiki2/src/views/production.ini
+++ b/docs/tutorials/wiki2/src/views/production.ini
@@ -12,7 +12,7 @@ pyramid.includes = pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/docs/tutorials/wiki2/src/views/setup.py b/docs/tutorials/wiki2/src/views/setup.py
index 9c0e88eb0..05c7be2d7 100644
--- a/docs/tutorials/wiki2/src/views/setup.py
+++ b/docs/tutorials/wiki2/src/views/setup.py
@@ -14,6 +14,7 @@ requires = [
'pyramid_tm',
'pyramid_debugtoolbar',
'zope.sqlalchemy',
+ 'waitress',
'docutils',
]
diff --git a/docs/whatsnew-1.3.rst b/docs/whatsnew-1.3.rst
index 8dd3c3cdb..e8d549e60 100644
--- a/docs/whatsnew-1.3.rst
+++ b/docs/whatsnew-1.3.rst
@@ -52,7 +52,7 @@ to make some changes:
- We've replaced the ``paster`` command with Pyramid-specific analogues.
-- We've made the default WSGI server the ``wsgiref`` server.
+- We've made the default WSGI server the ``waitress`` server.
Previously (in Pyramid 1.0, 1.1 and 1.2), you created a Pyramid application
using ``paster create``, like so::
@@ -85,37 +85,25 @@ Analogues of ``paster pshell``, ``paster pviews``, ``paster request`` and
``paster ptweens`` also exist under the respective console script names
``pshell``, ``pviews``, ``prequest`` and ``ptweens``.
-We've replaced use of the Paste ``httpserver`` with the ``wsgiref`` server in
+We've replaced use of the Paste ``httpserver`` with the ``waitress`` server in
the scaffolds, so once you create a project from a scaffold, its
``development.ini`` and ``production.ini`` will have the following line::
- use = egg:pyramid#wsgiref
+ use = egg:waitress#main
Instead of this (which was the default in older versions)::
use = egg:Paste#http
-Using ``wsgiref`` as the default WSGI server is purely a default to make it
-possible to use the same scaffolding under Python 2 and Python 3; people
-running Pyramid under Python 2 can still manually install ``Paste`` and use
-the Paste ``httpserver`` by replacing the former line with the latter. This is
-actually recommended if you rely on proxying from Apache or Nginx to a
-``pserve`` -invoked application. **The wsgiref server is not a production
-quality server.** See :ref:`alternate_wsgi_server` for more information.
-
-New releases in every older major Pyramid series (1.0.2, 1.1.3, 1.2.5) also
-have the ``egg:pyramid#wsgiref`` entry point, so scaffold-writers can depend
-on it being there even in older major Pyramid versions.
-
.. warning::
- Previously, paste.httpserver "helped" by converting header values that weren't
- strings to strings. The wsgiref server, on the other hand implements the spec
- more fully. This specifically may affect you if you are modifying headers on
- your response. The following error might be an indicator of this problem:
- **AssertionError: Header values must be strings, please check the type of
- the header being returned.** A common case would be returning unicode headers
- instead of string headers.
+ Previously, paste.httpserver "helped" by converting header values that
+ weren't strings to strings. The ``waitress`` server, on the other hand
+ implements the spec more fully. This specifically may affect you if you
+ are modifying headers on your response. The following error might be an
+ indicator of this problem: **AssertionError: Header values must be
+ strings, please check the type of the header being returned.** A common
+ case would be returning unicode headers instead of string headers.
A new :mod:`pyramid.compat` module was added which provides Python 2/3
straddling support for Pyramid add-ons and development environments.
@@ -206,6 +194,16 @@ This also works for imperative view configurations that involve a class.
See :ref:`view_defaults` for more information.
+Extending a Request without Subclassing
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is now possible to extend a :class:`pyramid.request.Request` object
+with property descriptors without having to create a subclass via
+:meth:`pyramid.request.Request.set_property`. New properties may be
+reified, effectively caching the value for the lifetime of the instance.
+Common use-cases for this would be to get a database connection for the
+request or identify the current user.
+
Minor Feature Additions
-----------------------
@@ -254,12 +252,9 @@ Backwards Incompatibilities
Python 3.
- The default WSGI server run as the result of ``pserve`` from newly rendered
- scaffolding is now the ``wsgiref`` WSGI server instead of the
- ``paste.httpserver`` server. ``wsgiref``, unlike the server it replaced
- (``paste.httpserver``) is not a production quality server. See
- :ref:`alternate_wsgi_server` for information about how to use another WSGI
- server in production. Rationale: the Paste and PasteScript packages do not
- run under Python 3.
+ scaffolding is now the ``waitress`` WSGI server instead of the
+ ``paste.httpserver`` server. 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``
@@ -302,6 +297,12 @@ Documentation Enhancements
- Added a section to the "Command-Line Pyramid" chapter named
:ref:`making_a_console_script`.
+- Removed the "Running Pyramid on Google App Engine" tutorial from the main
+ docs. It survives on in the Cookbook
+ (http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/gae.html).
+ Rationale: it provides the correct info for the Python 2.5 version of GAE
+ only, and this version of Pyramid does not support Python 2.5.
+
Dependency Changes
------------------
diff --git a/pyramid/compat.py b/pyramid/compat.py
index 484cb7b97..12b8c9f37 100644
--- a/pyramid/compat.py
+++ b/pyramid/compat.py
@@ -33,7 +33,7 @@ else:
def text_(s, encoding='latin-1', errors='strict'):
""" If ``s`` is an instance of ``binary_type``, return
- ``s.encode(encoding, errors)``, otherwise return ``s``"""
+ ``s.decode(encoding, errors)``, otherwise return ``s``"""
if isinstance(s, binary_type):
return s.decode(encoding, errors)
return s # pragma: no cover
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index 5efe1f2bb..3cca1c7c5 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -492,6 +492,15 @@ class MultiView(object):
self.views = []
self.accepts = []
+ def __discriminator__(self, context, request):
+ # used by introspection systems like so:
+ # view = adapters.lookup(....)
+ # view.__discriminator__(context, request) -> view's discriminator
+ # so that superdynamic systems can feed the discriminator to
+ # the introspection system to get info about it
+ view = self.match(context, request)
+ return view.__discriminator__(context, request)
+
def add(self, view, order, accept=None, phash=None):
if phash is not None:
for i, (s, v, h) in enumerate(list(self.views)):
@@ -1034,6 +1043,10 @@ class ViewsConfiguratorMixin(object):
decorator=decorator,
http_cache=http_cache)
derived_view = deriver(view)
+ derived_view.__discriminator__ = lambda *arg: discriminator
+ # __discriminator__ is used by superdynamic systems
+ # that require it for introspection after manual view lookup;
+ # see also MultiView.__discriminator__
view_intr['derived_callable'] = derived_view
registered = self.registry.adapters.registered
diff --git a/pyramid/events.py b/pyramid/events.py
index 9cb8b31ad..e181ef33f 100644
--- a/pyramid/events.py
+++ b/pyramid/events.py
@@ -1,6 +1,9 @@
import venusian
-from zope.interface import implementer
+from zope.interface import (
+ implementer,
+ Interface
+ )
from pyramid.interfaces import (
IContextFound,
@@ -26,7 +29,7 @@ class subscriber(object):
def mysubscriber(event):
event.request.foo = 1
- More than one event type can be passed as a construtor argument. The
+ More than one event type can be passed as a constructor argument. The
decorated subscriber will be called for each event type.
.. code-block:: python
@@ -66,7 +69,7 @@ class subscriber(object):
def register(self, scanner, name, wrapped):
config = scanner.config
- for iface in self.ifaces:
+ for iface in self.ifaces or (Interface,):
config.add_subscriber(wrapped, iface)
def __call__(self, wrapped):
diff --git a/pyramid/request.py b/pyramid/request.py
index 4005213fa..c15ed7d8e 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -26,6 +26,7 @@ from pyramid.compat import (
from pyramid.decorator import reify
from pyramid.response import Response
from pyramid.url import URLMethodsMixin
+from pyramid.util import InstancePropertyMixin
class TemplateContext(object):
pass
@@ -301,7 +302,7 @@ class CallbackMethodsMixin(object):
@implementer(IRequest)
class Request(BaseRequest, DeprecatedRequestMethodsMixin, URLMethodsMixin,
- CallbackMethodsMixin):
+ CallbackMethodsMixin, InstancePropertyMixin):
"""
A subclass of the :term:`WebOb` Request class. An instance of
this class is created by the :term:`router` and is provided to a
diff --git a/pyramid/scaffolds/alchemy/development.ini_tmpl b/pyramid/scaffolds/alchemy/development.ini_tmpl
index d804a0b0e..1127aa00f 100644
--- a/pyramid/scaffolds/alchemy/development.ini_tmpl
+++ b/pyramid/scaffolds/alchemy/development.ini_tmpl
@@ -13,7 +13,7 @@ pyramid.includes = pyramid_debugtoolbar
sqlalchemy.url = sqlite:///%(here)s/{{project}}.db
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/pyramid/scaffolds/alchemy/production.ini_tmpl b/pyramid/scaffolds/alchemy/production.ini_tmpl
index 7350ce25f..9c9335d20 100644
--- a/pyramid/scaffolds/alchemy/production.ini_tmpl
+++ b/pyramid/scaffolds/alchemy/production.ini_tmpl
@@ -12,7 +12,7 @@ pyramid.includes = pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/{{project}}.db
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/pyramid/scaffolds/alchemy/setup.py_tmpl b/pyramid/scaffolds/alchemy/setup.py_tmpl
index d0a95ea4c..bffbd8bfb 100644
--- a/pyramid/scaffolds/alchemy/setup.py_tmpl
+++ b/pyramid/scaffolds/alchemy/setup.py_tmpl
@@ -14,6 +14,7 @@ requires = [
'pyramid_tm',
'pyramid_debugtoolbar',
'zope.sqlalchemy',
+ 'waitress',
]
setup(name='{{project}}',
diff --git a/pyramid/scaffolds/starter/development.ini_tmpl b/pyramid/scaffolds/starter/development.ini_tmpl
index 4d3a80286..ee53fcf1c 100644
--- a/pyramid/scaffolds/starter/development.ini_tmpl
+++ b/pyramid/scaffolds/starter/development.ini_tmpl
@@ -10,7 +10,7 @@ pyramid.default_locale_name = en
pyramid.includes = pyramid_debugtoolbar
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/pyramid/scaffolds/starter/production.ini_tmpl b/pyramid/scaffolds/starter/production.ini_tmpl
index 931cfa510..2712d2e39 100644
--- a/pyramid/scaffolds/starter/production.ini_tmpl
+++ b/pyramid/scaffolds/starter/production.ini_tmpl
@@ -9,7 +9,7 @@ pyramid.debug_templates = false
pyramid.default_locale_name = en
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/pyramid/scaffolds/starter/setup.py_tmpl b/pyramid/scaffolds/starter/setup.py_tmpl
index e63579d50..39ac6de9d 100644
--- a/pyramid/scaffolds/starter/setup.py_tmpl
+++ b/pyramid/scaffolds/starter/setup.py_tmpl
@@ -6,7 +6,11 @@ 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()
-requires = ['pyramid', 'pyramid_debugtoolbar']
+requires = [
+ 'pyramid',
+ 'pyramid_debugtoolbar',
+ 'waitress',
+ ]
setup(name='{{project}}',
version='0.0',
diff --git a/pyramid/scaffolds/zodb/development.ini_tmpl b/pyramid/scaffolds/zodb/development.ini_tmpl
index eb2b0d3de..6cffd5657 100644
--- a/pyramid/scaffolds/zodb/development.ini_tmpl
+++ b/pyramid/scaffolds/zodb/development.ini_tmpl
@@ -14,7 +14,7 @@ tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/pyramid/scaffolds/zodb/production.ini_tmpl b/pyramid/scaffolds/zodb/production.ini_tmpl
index 7c91ea0df..c0f2fbbf3 100644
--- a/pyramid/scaffolds/zodb/production.ini_tmpl
+++ b/pyramid/scaffolds/zodb/production.ini_tmpl
@@ -13,7 +13,7 @@ tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
-use = egg:pyramid#wsgiref
+use = egg:waitress#main
host = 0.0.0.0
port = 6543
diff --git a/pyramid/scaffolds/zodb/setup.py_tmpl b/pyramid/scaffolds/zodb/setup.py_tmpl
index 812b85e83..965c0178f 100644
--- a/pyramid/scaffolds/zodb/setup.py_tmpl
+++ b/pyramid/scaffolds/zodb/setup.py_tmpl
@@ -12,6 +12,7 @@ requires = [
'pyramid_tm',
'pyramid_debugtoolbar',
'ZODB3',
+ 'waitress',
]
setup(name='{{project}}',
diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py
index 30a30a1e8..bcf1faa08 100644
--- a/pyramid/tests/test_config/test_views.py
+++ b/pyramid/tests/test_config/test_views.py
@@ -115,6 +115,7 @@ class TestViewsConfigurationMixin(unittest.TestCase):
self.assertEqual(wrapper.__module__, view.__module__)
self.assertEqual(wrapper.__name__, view.__name__)
self.assertEqual(wrapper.__doc__, view.__doc__)
+ self.assertEqual(wrapper.__discriminator__(None, None)[0], 'view')
def test_add_view_view_callable_dottedname(self):
from pyramid.renderers import null_renderer
@@ -875,6 +876,41 @@ class TestViewsConfigurationMixin(unittest.TestCase):
request.params = {'param':'1'}
self.assertEqual(wrapper(ctx, request), 'view8')
+ def test_add_view_multiview___discriminator__(self):
+ from pyramid.renderers import null_renderer
+ from zope.interface import Interface
+ class IFoo(Interface):
+ pass
+ class IBar(Interface):
+ pass
+ @implementer(IFoo)
+ class Foo(object):
+ pass
+ @implementer(IBar)
+ class Bar(object):
+ pass
+ foo = Foo()
+ bar = Bar()
+
+ from pyramid.interfaces import IRequest
+ from pyramid.interfaces import IView
+ from pyramid.interfaces import IViewClassifier
+ from pyramid.interfaces import IMultiView
+ view = lambda *arg: 'OK'
+ view.__phash__ = 'abc'
+ config = self._makeOne(autocommit=True)
+ config.registry.registerAdapter(
+ view, (IViewClassifier, IRequest, Interface), IView, name='')
+ config.add_view(view=view, renderer=null_renderer,
+ containment=IFoo)
+ config.add_view(view=view, renderer=null_renderer,
+ containment=IBar)
+ wrapper = self._getViewCallable(config)
+ self.assertTrue(IMultiView.providedBy(wrapper))
+ request = self._makeRequest(config)
+ self.assertEqual(wrapper.__discriminator__(foo, request)[5], IFoo)
+ self.assertEqual(wrapper.__discriminator__(bar, request)[5], IBar)
+
def test_add_view_with_template_renderer(self):
from pyramid.tests import test_config
from pyramid.interfaces import ISettings
diff --git a/pyramid/tests/test_events.py b/pyramid/tests/test_events.py
index 4b58a129c..f35083c02 100644
--- a/pyramid/tests/test_events.py
+++ b/pyramid/tests/test_events.py
@@ -155,6 +155,16 @@ class TestSubscriber(unittest.TestCase):
dec.register(scanner, None, foo)
self.assertEqual(config.subscribed, [(foo, IFoo), (foo, IBar)])
+ def test_register_none_means_all(self):
+ from zope.interface import Interface
+ dec = self._makeOne()
+ def foo(): pass
+ config = DummyConfigurator()
+ scanner = Dummy()
+ scanner.config = config
+ dec.register(scanner, None, foo)
+ self.assertEqual(config.subscribed, [(foo, Interface)])
+
def test_register_objectevent(self):
from zope.interface import Interface
class IFoo(Interface): pass
diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py
index 546f670c0..10cda96d8 100644
--- a/pyramid/tests/test_request.py
+++ b/pyramid/tests/test_request.py
@@ -267,6 +267,24 @@ class TestRequest(unittest.TestCase):
request = self._makeOne({'REQUEST_METHOD':'GET'})
self.assertRaises(ValueError, getattr, request, 'json_body')
+ def test_set_property(self):
+ request = self._makeOne({})
+ opts = [2, 1]
+ def connect(obj):
+ return opts.pop()
+ request.set_property(connect, name='db')
+ self.assertEqual(1, request.db)
+ self.assertEqual(2, request.db)
+
+ def test_set_property_reify(self):
+ request = self._makeOne({})
+ opts = [2, 1]
+ def connect(obj):
+ return opts.pop()
+ request.set_property(connect, name='db', reify=True)
+ self.assertEqual(1, request.db)
+ self.assertEqual(1, request.db)
+
class TestRequestDeprecatedMethods(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py
index b9a9d1960..824ee329f 100644
--- a/pyramid/tests/test_util.py
+++ b/pyramid/tests/test_util.py
@@ -1,6 +1,114 @@
import unittest
from pyramid.compat import PY3
+class Test_InstancePropertyMixin(unittest.TestCase):
+ def _makeOne(self):
+ cls = self._targetClass()
+ class Foo(cls):
+ pass
+ return Foo()
+
+ def _targetClass(self):
+ from pyramid.util import InstancePropertyMixin
+ return InstancePropertyMixin
+
+ def test_callable(self):
+ def worker(obj):
+ return obj.bar
+ foo = self._makeOne()
+ foo.set_property(worker)
+ foo.bar = 1
+ self.assertEqual(1, foo.worker)
+ foo.bar = 2
+ self.assertEqual(2, foo.worker)
+
+ def test_callable_with_name(self):
+ def worker(obj):
+ return obj.bar
+ foo = self._makeOne()
+ foo.set_property(worker, name='x')
+ foo.bar = 1
+ self.assertEqual(1, foo.x)
+ foo.bar = 2
+ self.assertEqual(2, foo.x)
+
+ def test_callable_with_reify(self):
+ def worker(obj):
+ return obj.bar
+ foo = self._makeOne()
+ foo.set_property(worker, reify=True)
+ foo.bar = 1
+ self.assertEqual(1, foo.worker)
+ foo.bar = 2
+ self.assertEqual(1, foo.worker)
+
+ def test_callable_with_name_reify(self):
+ def worker(obj):
+ return obj.bar
+ foo = self._makeOne()
+ foo.set_property(worker, name='x')
+ foo.set_property(worker, name='y', reify=True)
+ foo.bar = 1
+ self.assertEqual(1, foo.y)
+ self.assertEqual(1, foo.x)
+ foo.bar = 2
+ self.assertEqual(2, foo.x)
+ self.assertEqual(1, foo.y)
+
+ def test_property_without_name(self):
+ def worker(obj): pass
+ foo = self._makeOne()
+ self.assertRaises(ValueError, foo.set_property, property(worker))
+
+ def test_property_with_name(self):
+ def worker(obj):
+ return obj.bar
+ foo = self._makeOne()
+ foo.set_property(property(worker), name='x')
+ foo.bar = 1
+ self.assertEqual(1, foo.x)
+ foo.bar = 2
+ self.assertEqual(2, foo.x)
+
+ def test_property_with_reify(self):
+ def worker(obj): pass
+ foo = self._makeOne()
+ self.assertRaises(ValueError, foo.set_property,
+ property(worker), name='x', reify=True)
+
+ def test_override_property(self):
+ def worker(obj): pass
+ foo = self._makeOne()
+ foo.set_property(worker, name='x')
+ def doit():
+ foo.x = 1
+ self.assertRaises(AttributeError, doit)
+
+ def test_override_reify(self):
+ def worker(obj): pass
+ foo = self._makeOne()
+ foo.set_property(worker, name='x', reify=True)
+ foo.x = 1
+ self.assertEqual(1, foo.x)
+ foo.x = 2
+ self.assertEqual(2, foo.x)
+
+ def test_reset_property(self):
+ foo = self._makeOne()
+ foo.set_property(lambda _: 1, name='x')
+ self.assertEqual(1, foo.x)
+ foo.set_property(lambda _: 2, name='x')
+ self.assertEqual(2, foo.x)
+
+ def test_reset_reify(self):
+ """ This is questionable behavior, but may as well get notified
+ if it changes."""
+ foo = self._makeOne()
+ foo.set_property(lambda _: 1, name='x', reify=True)
+ self.assertEqual(1, foo.x)
+ foo.set_property(lambda _: 2, name='x', reify=True)
+ self.assertEqual(1, foo.x)
+
class Test_WeakOrderedSet(unittest.TestCase):
def _makeOne(self):
from pyramid.config import WeakOrderedSet
diff --git a/pyramid/util.py b/pyramid/util.py
index 76968bbbd..852689c4d 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -14,6 +14,89 @@ class DottedNameResolver(_DottedNameResolver):
def __init__(self, package=None): # default to package = None for bw compat
return _DottedNameResolver.__init__(self, package)
+class InstancePropertyMixin(object):
+ """ Mixin that will allow an instance to add properties at
+ run-time as if they had been defined via @property or @reify
+ on the class itself.
+ """
+
+ def set_property(self, func, name=None, reify=False):
+ """ Add a callable or a property descriptor to the instance.
+
+ Properties, unlike attributes, are lazily evaluated by executing
+ an underlying callable when accessed. They can be useful for
+ adding features to an object without any cost if those features
+ go unused.
+
+ A property may also be reified via the
+ :class:`pyramid.decorator.reify` decorator by setting
+ ``reify=True``, allowing the result of the evaluation to be
+ cached. Thus the value of the property is only computed once for
+ the lifetime of the object.
+
+ ``func`` can either be a callable that accepts the instance as
+ its single positional parameter, or it can be a property
+ descriptor.
+
+ If the ``func`` is a property descriptor, the ``name`` parameter
+ must be supplied or a ``ValueError`` will be raised. Also note
+ that a property descriptor cannot be reified, so ``reify`` must
+ be ``False``.
+
+ If ``name`` is None, the name of the property will be computed
+ from the name of the ``func``.
+
+ .. code-block:: python
+ :linenos:
+
+ class Foo(InstancePropertyMixin):
+ _x = 1
+
+ def _get_x(self):
+ return _x
+
+ def _set_x(self, value):
+ self._x = value
+
+ foo = Foo()
+ foo.set_property(property(_get_x, _set_x), name='x')
+ foo.set_property(_get_x, name='y', reify=True)
+
+ >>> foo.x
+ 1
+ >>> foo.y
+ 1
+ >>> foo.x = 5
+ >>> foo.x
+ 5
+ >>> foo.y # notice y keeps the original value
+ 1
+ """
+
+ is_property = isinstance(func, property)
+ if is_property:
+ fn = func
+ if name is None:
+ raise ValueError('must specify "name" for a property')
+ if reify:
+ raise ValueError('cannot reify a property')
+ elif name is not None:
+ fn = lambda this: func(this)
+ fn.__name__ = name
+ fn.__doc__ = func.__doc__
+ else:
+ name = func.__name__
+ fn = func
+ if reify:
+ import pyramid.decorator
+ fn = pyramid.decorator.reify(fn)
+ elif not is_property:
+ fn = property(fn)
+ attrs = { name: fn }
+ parent = self.__class__
+ cls = type(parent.__name__, (parent, object), attrs)
+ self.__class__ = cls
+
class WeakOrderedSet(object):
""" Maintain a set of items.
diff --git a/setup.py b/setup.py
index b9220285f..78963289a 100644
--- a/setup.py
+++ b/setup.py
@@ -56,7 +56,7 @@ if not PY3:
])
setup(name='pyramid',
- version='1.3a2',
+ version='1.3a3',
description=('The Pyramid web application development framework, a '
'Pylons project'),
long_description=README + '\n\n' + CHANGES,