Paul Everitt
2013-08-13 a66e00e9bd51fdffd85fef14e83f12502f2d864e
Merge remote-tracking branch 'origin/master' into docs.gettingstarted

Conflicts:
docs/index.rst
docs/latexindex.rst
setup.py
3 files added
47 files modified
998 ■■■■ changed files
CHANGES.txt 97 ●●●● patch | view | raw | blame | history
CONTRIBUTORS.txt 8 ●●●●● patch | view | raw | blame | history
HACKING.txt 2 ●●● patch | view | raw | blame | history
HISTORY.txt 2 ●●● patch | view | raw | blame | history
docs/Makefile 9 ●●●● patch | view | raw | blame | history
docs/_themes @ 8673c4 2 ●●● patch | view | raw | blame | history
docs/api/config.rst 1 ●●●● patch | view | raw | blame | history
docs/api/registry.rst 9 ●●●●● patch | view | raw | blame | history
docs/api/request.rst 4 ●●●● patch | view | raw | blame | history
docs/authorintro.rst 2 ●●● patch | view | raw | blame | history
docs/conf.py 4 ●●● patch | view | raw | blame | history
docs/foreword.rst 2 ●●●●● patch | view | raw | blame | history
docs/index.rst 13 ●●●●● patch | view | raw | blame | history
docs/latexindex.rst 6 ●●●● patch | view | raw | blame | history
docs/narr/commandline.rst 24 ●●●●● patch | view | raw | blame | history
docs/narr/events.rst 89 ●●●●● patch | view | raw | blame | history
docs/narr/i18n.rst 4 ●●●● patch | view | raw | blame | history
docs/narr/introduction.rst 4 ●●● patch | view | raw | blame | history
docs/narr/project.rst 12 ●●●● patch | view | raw | blame | history
docs/narr/renderers.rst 2 ●●● patch | view | raw | blame | history
docs/narr/sessions.rst 17 ●●●● patch | view | raw | blame | history
docs/narr/templates.rst 2 ●●● patch | view | raw | blame | history
docs/narr/traversal.rst 2 ●●● patch | view | raw | blame | history
docs/narr/urldispatch.rst 2 ●●● patch | view | raw | blame | history
docs/narr/viewconfig.rst 43 ●●●● patch | view | raw | blame | history
docs/whatsnew-1.1.rst 4 ●●●● patch | view | raw | blame | history
docs/whatsnew-1.5.rst 155 ●●●●● patch | view | raw | blame | history
pyramid/authentication.py 40 ●●●● patch | view | raw | blame | history
pyramid/config/__init__.py 5 ●●●● patch | view | raw | blame | history
pyramid/config/adapters.py 4 ●●●● patch | view | raw | blame | history
pyramid/config/predicates.py 1 ●●●● patch | view | raw | blame | history
pyramid/config/routes.py 4 ●●●● patch | view | raw | blame | history
pyramid/config/util.py 72 ●●●●● patch | view | raw | blame | history
pyramid/interfaces.py 2 ●●● patch | view | raw | blame | history
pyramid/renderers.py 4 ●●●● patch | view | raw | blame | history
pyramid/router.py 6 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/alchemy/+package+/models.py 5 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/alchemy/+package+/tests.py_tmpl 26 ●●●●● patch | view | raw | blame | history
pyramid/scripts/pdistreport.py 37 ●●●●● patch | view | raw | blame | history
pyramid/scripts/prequest.py 23 ●●●●● patch | view | raw | blame | history
pyramid/scripts/pserve.py 43 ●●●●● patch | view | raw | blame | history
pyramid/testing.py 2 ●●● patch | view | raw | blame | history
pyramid/tests/test_authentication.py 24 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_util.py 47 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_router.py 8 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_scripts/test_pdistreport.py 74 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_scripts/test_prequest.py 43 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_scripts/test_pserve.py 2 ●●● patch | view | raw | blame | history
pyramid/url.py 2 ●●● patch | view | raw | blame | history
setup.py 3 ●●●● patch | view | raw | blame | history
CHANGES.txt
@@ -1,11 +1,42 @@
next release
Next Release
============
Features
--------
- ``scripts/prequest.py``:  add support for submitting ``PUT`` and ``PATCH``
  requests.  See https://github.com/Pylons/pyramid/pull/1033.
- Add ``pdistreport`` script, which prints the Python version in use, the
  Pyramid version in use, and the version number and location of all Python
  distributions currently installed.
- Add the ability to invert the result of any view, route, or subscriber
  predicate using the ``not_`` class.  For example::
     from pyramid.config import not_
     @view_config(route_name='myroute', request_method=not_('POST'))
     def myview(request): ...
  The above example will ensure that the view is called if the request method
  is not POST (at least if no other view is more specific).
  The :class:`pyramid.config.not_` class can be used against any value that is
  a predicate value passed in any of these contexts:
  - ``pyramid.config.Configurator.add_view``
  - ``pyramid.config.Configurator.add_route``
  - ``pyramid.config.Configurator.add_subscriber``
  - ``pyramid.view.view_config``
  - ``pyramid.events.subscriber``
- ``scripts/prequest.py``: add support for submitting ``PUT`` and ``PATCH``
  requests.  See https://github.com/Pylons/pyramid/pull/1033.  add support for
  submitting ``OPTIONS`` and ``PROPFIND`` requests, and  allow users to specify
  basic authentication credentials in the request via a ``--login`` argument to
  the script.  See https://github.com/Pylons/pyramid/pull/1039.
- ``ACLAuthorizationPolicy`` supports ``__acl__`` as a callable. This
  removes the ambiguity between the potential ``AttributeError`` that would
@@ -17,6 +48,10 @@
- Allow a protocol-relative URL (e.g. ``//example.com/images``) to be passed to
  ``pyramid.config.Configurator.add_static_view``. This allows
  externally-hosted static URLs to be generated based on the current protocol.
- The ``AuthTktAuthenticationPolicy`` has a new ``parent_domain`` option to
  set the authentication cookie as a wildcard cookie on the parent domain. This
  is useful if you have multiple sites sharing the same domain.
- The ``AuthTktAuthenticationPolicy`` now supports IPv6 addresses when using
  the ``include_ip=True`` option. This is possibly incompatible with
@@ -37,22 +72,6 @@
  ``X-CSRF-Token`` (as well as the ``csrf_token`` form parameter, which they
  always did).  The header is tried when the form parameter does not exist.
Bug Fixes
---------
- Make the ``pyramid.config.assets.PackageOverrides`` object implement the API
  for ``__loader__`` objects specified in PEP 302.  Proxies to the
  ``__loader__`` set by the importer, if present; otherwise, raises
  ``NotImplementedError``.  This makes Pyramid static view overrides work
  properly under Python 3.3 (previously they would not).  See
  https://github.com/Pylons/pyramid/pull/1015 for more information.
- ``mako_templating``: added defensive workaround for non-importability of
  ``mako`` due to upstream ``markupsafe`` dropping Python 3.2 support.  Mako
  templating will no longer work under the combination of MarkupSafe 0.17 and
  Python 3.2 (although the combination of MarkupSafe 0.17 and Python 3.3 or any
  supported Python 2 version will work OK).
- View lookup will now search for valid views based on the inheritance
  hierarchy of the context. It tries to find views based on the most
  specific context first, and upon predicate failure, will move up the
@@ -60,7 +79,7 @@
  In the past, only the most specific type containing views would be checked
  and if no matching view could be found then a PredicateMismatch would be
  raised. Now predicate mismatches don't hide valid views registered on
  super-types. Here's an example that now works::
  super-types. Here's an example that now works:
  .. code-block:: python
@@ -98,7 +117,34 @@
  predicate mismatch error when trying to use GET or DELETE
  methods. Now the views are found and no predicate mismatch is
  raised.
  See https://github.com/Pylons/pyramid/pull/786
  See https://github.com/Pylons/pyramid/pull/786 and
  https://github.com/Pylons/pyramid/pull/1004 and
  https://github.com/Pylons/pyramid/pull/1046
- The ``pserve`` command now takes a ``-v`` (or ``--verbose``) flag and a
  ``-q`` (or ``--quiet``) flag.  Output from running ``pserve`` can be
  controlled using these flags.  ``-v`` can be specified multiple times to
  increase verbosity.  ``-q`` sets verbosity to ``0`` unconditionally.  The
  default verbosity level is ``1``.
- The ``alchemy`` scaffold tests now provide better coverage.  See
  https://github.com/Pylons/pyramid/pull/1029
Bug Fixes
---------
- Make the ``pyramid.config.assets.PackageOverrides`` object implement the API
  for ``__loader__`` objects specified in PEP 302.  Proxies to the
  ``__loader__`` set by the importer, if present; otherwise, raises
  ``NotImplementedError``.  This makes Pyramid static view overrides work
  properly under Python 3.3 (previously they would not).  See
  https://github.com/Pylons/pyramid/pull/1015 for more information.
- ``mako_templating``: added defensive workaround for non-importability of
  ``mako`` due to upstream ``markupsafe`` dropping Python 3.2 support.  Mako
  templating will no longer work under the combination of MarkupSafe 0.17 and
  Python 3.2 (although the combination of MarkupSafe 0.17 and Python 3.3 or any
  supported Python 2 version will work OK).
- Spaces and dots may now be in mako renderer template paths. This was
  broken when support for the new makodef syntax was added in 1.4a1.
@@ -115,9 +161,12 @@
  https://github.com/Pylons/pyramid/issues/981
- ``pyramid.testing.DummyResource`` didn't define ``__bool__``, so code under
   Python 3 would use ``__len__`` to find truthiness; this usually caused an
   instance of DummyResource to be "falsy" instead of "truthy".  See
   https://github.com/Pylons/pyramid/pull/1032
  Python 3 would use ``__len__`` to find truthiness; this usually caused an
  instance of DummyResource to be "falsy" instead of "truthy".  See
  https://github.com/Pylons/pyramid/pull/1032
- The ``alchemy`` scaffold would break when the database was MySQL during
  tables creation.  See https://github.com/Pylons/pyramid/pull/1049
1.4 (2012-12-18)
================
CONTRIBUTORS.txt
@@ -200,3 +200,11 @@
- Jason McKellar, 2013/03/28
- Luke Cyca, 2013/05/30
- Laurence Rowe, 2013/04/24
- Julian P. Glass, 2013/08/10
- Junaid Ali, 2013/08/10
- Chris Davies, 2013/08/11
HACKING.txt
@@ -126,7 +126,7 @@
change to reflect the bug fix, ideally in the same commit that fixes the bug
or adds the feature.
To build and review docs (where ``$yourvenv`` refers to the virtualenv you're
To build and review docs (where ``$VENV`` refers to the virtualenv you're
using to develop Pyramid):
1. Run ``$VENV/bin/python setup.py dev docs``.  This will cause Sphinx
HISTORY.txt
@@ -2150,7 +2150,7 @@
- Add ``wild_domain`` argument to AuthTktAuthenticationPolicy, which defaults
  to ``True``.  If it is set to ``False``, the feature of the policy which
  sets a cookie with a wilcard domain will be turned off.
  sets a cookie with a wildcard domain will be turned off.
- Add a ``MANIFEST.in`` file to each paster template. See
  https://github.com/Pylons/pyramid/issues#issue/95
docs/Makefile
@@ -63,8 +63,13 @@
    cp _static/latex-note.png _build/latex
    @echo
    @echo "Build finished; the LaTeX files are in _build/latex."
    @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
          "run these through (pdf)latex."
    @echo "Run \`make latexpdf' to build a PDF file from them."
latexpdf:
    $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
    @echo "Running LaTeX files through pdflatex..."
    $(MAKE) -C _build/latex all-pdf
    @echo "pdflatex finished; the PDF file is in _build/latex."
changes:
    mkdir -p _build/changes _build/doctrees
docs/_themes
@@ -1 +1 @@
Subproject commit f59f7bfce5259f50fbb67b9040c03ecb080130b4
Subproject commit 8673c4a14f192c15f1949dc9862821e60f31604a
docs/api/config.rst
@@ -135,3 +135,4 @@
   will only exist for the lifetime of the actual applications for which they
   are being used.
.. autoclass:: not_
docs/api/registry.rst
@@ -29,6 +29,15 @@
     This attribute is often accessed as ``request.registry.introspector`` in
     a typical Pyramid application.
   .. method:: notify(*events)
     Fire one or more events. All event subscribers to the event(s)
     will be notified. The subscribers will be called synchronously.
     This method is often accessed as ``request.registry.notify``
     in Pyramid applications to fire custom events. See
     :ref:`custom_events` for more information.
.. class:: Introspectable
   .. versionadded:: 1.3
docs/api/request.rst
@@ -156,7 +156,7 @@
   .. attribute:: matched_route
      If a :term:`route` has matched during this request, this attribute will
      be an obect representing the route matched by the URL pattern
      be an object representing the route matched by the URL pattern
      associated with the route.  If a route has not matched during this
      request, the value of this attribute will be ``None``. See
      :ref:`matched_route`.
@@ -238,7 +238,7 @@
   .. attribute::  response_*
      In Pyramid 1.0, you could set attributes on a
      :class:`pyramid.request.Request` which influenced the behavor of
      :class:`pyramid.request.Request` which influenced the behavior of
      *rendered* responses (views which use a :term:`renderer` and which
      don't directly return a response).  These attributes began with
      ``response_``, such as ``response_headerlist``. If you needed to
docs/authorintro.rst
@@ -73,7 +73,7 @@
  concepts in terms of the sample.  You should read the tutorials if
  you want a guided tour of :app:`Pyramid`.
:ref:`api_reference`
:ref:`api_documentation`
  Comprehensive reference material for every public API exposed by
  :app:`Pyramid`.  The API documentation is organized
docs/conf.py
@@ -19,6 +19,8 @@
warnings.simplefilter('ignore', DeprecationWarning)
import pkg_resources
# skip raw nodes
from sphinx.writers.text import TextTranslator
from sphinx.writers.latex import LaTeXTranslator
@@ -108,7 +110,7 @@
# other places throughout the built documents.
#
# The short X.Y version.
version = '1.4'
version = pkg_resources.get_distribution('pyramid').version
# The full version, including alpha/beta/rc tags.
release = version
docs/foreword.rst
@@ -1,3 +1,5 @@
:orphan:
Foreword
========
docs/index.rst
@@ -1,8 +1,8 @@
.. _index:
=================================================
=========================
The Pyramid Web Framework
=================================================
=========================
:app:`Pyramid` is a small, fast, down-to-earth Python web framework.  It
is developed as part of the `Pylons Project
@@ -65,7 +65,7 @@
.. _html_narrative_documentation:
Narrative documentation
Narrative Documentation
=======================
Narrative documentation in chapter form explaining how to use
@@ -130,7 +130,7 @@
   tutorials/modwsgi/index.rst
API Documentation
==================
=================
Comprehensive reference material for every public API exposed by :app:`Pyramid`:
@@ -146,6 +146,7 @@
.. toctree::
   :maxdepth: 1
   whatsnew-1.5
   whatsnew-1.4
   whatsnew-1.3
   whatsnew-1.2
@@ -197,12 +198,8 @@
* :ref:`search`
.. add glossary, foreword, and latexindex in a hidden toc to avoid warnings
.. toctree::
   :hidden:
   glossary
   foreword.rst
   latexindex.rst
docs/latexindex.rst
@@ -1,8 +1,8 @@
.. _latexindex:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
The :app:`Pyramid` Web Framework
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
=========================
The Pyramid Web Framework
=========================
.. frontmatter::
docs/narr/commandline.rst
@@ -474,6 +474,30 @@
   $ $VENV/bin/prequest -mPOST development.ini / < somefile
Showing All Installed Distributions and their Versions
------------------------------------------------------
.. versionadded:: 1.5
You can use the ``pdistreport`` command to show the Pyramid version in use, the
Python version in use, and all installed versions of Python distributions in
your Python environment::
   $ $VENV/bin/pdistreport
   Pyramid version: 1.5dev
   Platform Linux-3.2.0-51-generic-x86_64-with-debian-wheezy-sid
   Packages:
     authapp 0.0
       /home/chrism/projects/foo/src/authapp
     beautifulsoup4 4.1.3
       /home/chrism/projects/foo/lib/python2.7/site-packages/beautifulsoup4-4.1.3-py2.7.egg
   ... more output ...
``pdistreport`` takes no options.  Its output is useful to paste into a
pastebin when you are having problems and need someone with more familiarity
with Python packaging and distribution than you have to look at your
environment.
.. _writing_a_script:
Writing a Script
docs/narr/events.rst
@@ -53,7 +53,7 @@
  from subscribers import mysubscriber
  # "config" below is assumed to be an instance of a
  # "config" below is assumed to be an instance of a
  # pyramid.config.Configurator object
  config.add_subscriber(mysubscriber, NewRequest)
@@ -77,7 +77,7 @@
  @subscriber(NewRequest)
  def mysubscriber(event):
      event.request.foo = 1
      event.request.foo = 1
When the :func:`~pyramid.events.subscriber` decorator is used a
:term:`scan` must be performed against the package containing the
@@ -113,7 +113,7 @@
   :linenos:
   def handle_new_request(event):
       print 'request', event.request
       print 'request', event.request
   def handle_new_response(event):
       print 'response', event.response
@@ -150,3 +150,86 @@
:class:`pyramid.interfaces.INewResponse` says it must
(:class:`pyramid.events.NewResponse` objects also have a ``request``).
.. _custom_events:
Creating Your Own Events
------------------------
In addition to using the events that the Pyramid framework creates,
you can create your own events for use in your application. This can
be useful to decouple parts of your application.
For example, suppose your application has to do many things when a new
document is created. Rather than putting all this logic in the view
that creates the document, you can create the document in your view
and then fire a custom event. Subscribers to the custom event can take
other actions, such as indexing the document, sending email, or
sending a message to a remote system.
An event is simply an object. There are no required attributes or
method for your custom events. In general, your events should keep
track of the information that subscribers will need. Here are some
example custom event classes:
.. code-block:: python
   :linenos:
    class DocCreated(object):
        def __init__(self, doc, request):
            self.doc = doc
            self.request = request
    class UserEvent(object):
        def __init__(self, user):
            self.user = user
    class UserLoggedIn(UserEvent):
        pass
Some Pyramid applications choose to define custom events classes in an
``events`` module.
You can subscribe to custom events in the same way that you subscribe
to Pyramid events -- either imperatively or with a decorator. You can
also use custom events with :ref:`subscriber predicates
<subscriber_predicates>`. Here's an example of subscribing to a custom
event with a decorator:
.. code-block:: python
   :linenos:
    from pyramid.events import subscriber
    from .events import DocCreated
    from .index import index_doc
    @subscriber(DocCreated)
    def index_doc(event):
        # index the document using our application's index_doc function
        index_doc(event.doc, event.request)
The above example assumes that the application defines a
``DocCreated`` event class and an ``index_doc`` function.
To fire your custom events use the
:meth:`pyramid.registry.Registry.notify` method, which is most often
accessed as ``request.registry.notify``. For example:
.. code-block:: python
   :linenos:
    from .events import DocCreated
    def new_doc_view(request):
        doc = MyDoc()
        event = DocCreated(doc, request)
        request.registry.notify(event)
        return {'document': doc}
This example view will notify all subscribers to the custom
``DocCreated`` event.
Note that when you fire an event, all subscribers are run
synchronously so it's generally not a good idea
to create event handlers that may take a long time to run. Although
event handlers could be used as a central place to spawn tasks on your
own message queues.
docs/narr/i18n.rst
@@ -808,7 +808,7 @@
   default_locale_name = settings['pyramid.default_locale_name']
.. index::
   single: detecting langauges
   single: detecting languages
"Detecting" Available Languages
-------------------------------
@@ -984,7 +984,7 @@
The default locale negotiator implementation named
:class:`~pyramid.i18n.default_locale_negotiator` uses the following
set of steps to dermine the locale name.
set of steps to determine the locale name.
- First, the negotiator looks for the ``_LOCALE_`` attribute of the
  request object (possibly set directly by view code or by a listener
docs/narr/introduction.rst
@@ -217,6 +217,8 @@
easily send email, let you use the Jinja2 templating system, let you use
XML-RPC or JSON-RPC, let you integrate with jQuery Mobile, etc.
Examples: http://docs.pylonsproject.org/en/latest/docs/pyramid.html#pyramid-add-on-documentation
Class-based and function-based views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -857,7 +859,7 @@
question on IRC, on the Pylons-discuss maillist, or on StackOverflow, you're
likely to get a reasonably prompt response.  We don't tolerate "support
trolls" or other people who seem to get their rocks off by berating fellow
users in our various offical support channels.  We try to keep it well-lit
users in our various official support channels.  We try to keep it well-lit
and new-user-friendly.
Example: Visit irc\://freenode.net#pyramid (the ``#pyramid`` channel on
docs/narr/project.rst
@@ -1007,12 +1007,12 @@
application.  As we saw in :ref:`firstapp_chapter`, ``pserve`` needn't be
invoked at all to run a :app:`Pyramid` application.  The use of ``pserve`` to
run a :app:`Pyramid` application is purely conventional based on the output
of its scaffolding.  But we strongly recommend using while developing your
application, because many other convenience introspection commands (such as
``pviews``, ``prequest``, ``proutes`` and others) are also implemented in
terms of configuration availability of this ``.ini`` file format.  It also
configures Pyramid logging and provides the ``--reload`` switch for
convenient restarting of the server when code changes.
of its scaffolding.  But we strongly recommend using ``pserve`` while
developing your application, because many other convenience introspection
commands (such as ``pviews``, ``prequest``, ``proutes`` and others) are also
implemented in terms of configuration availability of this ``.ini`` file
format.  It also configures Pyramid logging and provides the ``--reload``
switch for convenient restarting of the server when code changes.
.. _alternate_wsgi_server:
docs/narr/renderers.rst
@@ -198,7 +198,7 @@
.. code-block:: python
   '{"content": "Hello!"}'
   {"content": "Hello!"}
The return value needn't be a dictionary, but the return value must contain
values serializable by the configured serializer (by default ``json.dumps``).
docs/narr/sessions.rst
@@ -148,6 +148,7 @@
.. index::
   single: pyramid_beaker
   single: Beaker
   single: pyramid_redis_sessions
   single: session factory (alternates)
.. _using_alternate_session_factories:
@@ -155,11 +156,17 @@
Using Alternate Session Factories
---------------------------------
At the time of this writing, exactly one alternate session factory
implementation exists, named ``pyramid_beaker``. This is a session factory
that uses the `Beaker <http://beaker.groovie.org/>`_ library as a backend.
Beaker has support for file-based sessions, database based sessions, and
encrypted cookie-based sessions.  See `the pyramid_beaker documentation
At the time of this writing, exactly two alternate session factories
exist.
The first is named ``pyramid_redis_sessions``.  It can be downloaded from PyPI.
It uses Redis as a backend.  It is the recommended persistent session solution
at the time of this writing.
The second is named ``pyramid_beaker``. This is a session factory that uses the
`Beaker <http://beaker.groovie.org/>`_ library as a backend.  Beaker has
support for file-based sessions, database based sessions, and encrypted
cookie-based sessions.  See `the pyramid_beaker documentation
<http://docs.pylonsproject.org/projects/pyramid_beaker/en/latest/>`_ for more
information about ``pyramid_beaker``.
docs/narr/templates.rst
@@ -725,7 +725,7 @@
Using A Mako def name Within a Renderer Name
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sommetime you'd like to render a ``def`` inside of a Mako template instead of
Sometimes you'd like to render a ``def`` inside of a Mako template instead of
the full Mako template. To render a def inside a Mako template, given a
:term:`Mako` template file named ``foo.mak`` and a def named ``bar``, you can
configure the template as a :term:`renderer` like so:
docs/narr/traversal.rst
@@ -289,7 +289,7 @@
    return resource "C".
#.  Traversal ends when a) the entire path is exhausted or b) when any
    resouce raises a :exc:`KeyError` from its ``__getitem__`` or c) when any
    resource raises a :exc:`KeyError` from its ``__getitem__`` or c) when any
    non-final path element traversal does not have a ``__getitem__`` method
    (resulting in a :exc:`AttributeError`) or d) when any path element is
    prefixed with the set of characters ``@@`` (indicating that the characters
docs/narr/urldispatch.rst
@@ -865,7 +865,7 @@
------------------------
It's useful to be able to take a peek under the hood when requests that enter
your application arent matching your routes as you expect them to.  To debug
your application aren't matching your routes as you expect them to.  To debug
route matching, use the ``PYRAMID_DEBUG_ROUTEMATCH`` environment variable or the
``pyramid.debug_routematch`` configuration file setting (set either to ``true``).
Details of the route matching decision for a particular request to the
docs/narr/viewconfig.rst
@@ -290,9 +290,9 @@
  of the ``REQUEST_METHOD`` of the :term:`WSGI` environment.
``request_param``
  This value can be any string or a sequence of strings.  A view declaration
  with this argument ensures that the view will only be called when the
  :term:`request` has a key in the ``request.params`` dictionary (an HTTP
  This value can be any string or a sequence of strings.  A view declaration
  with this argument ensures that the view will only be called when the
  :term:`request` has a key in the ``request.params`` dictionary (an HTTP
  ``GET`` or ``POST`` variable) that has a name which matches the
  supplied value.
@@ -306,8 +306,6 @@
  consideration of keys and values in the ``request.params`` dictionary.
``match_param``
  .. versionadded:: 1.2
  This param may be either a single string of the format "key=value" or a
  dict of key/value pairs.
@@ -323,6 +321,8 @@
  If ``match_param`` is not supplied, the view will be invoked without
  consideration of the keys and values in ``request.matchdict``.
  .. versionadded:: 1.2
``containment``
  This value should be a reference to a Python class or :term:`interface`
@@ -505,7 +505,7 @@
.. code-block:: python
   :linenos:
   config.add_view('mypackage.views.my_view', route_name='ok',
   config.add_view('mypackage.views.my_view', route_name='ok',
                   request_method='POST', permission='read')
All arguments to ``view_config`` may be omitted.  For example:
@@ -556,6 +556,35 @@
form of :term:`declarative configuration`, while
:meth:`pyramid.config.Configurator.add_view` is a form of :term:`imperative
configuration`.  However, they both do the same thing.
Inverting Predicate Values
~~~~~~~~~~~~~~~~~~~~~~~~~~
You can invert the meaning of any predicate value by wrapping it in a call to
:class:`pyramid.config.not_`.
.. code-block:: python
   :linenos:
   from pyramid.config import not_
   config.add_view(
       'mypackage.views.my_view',
       route_name='ok',
       request_method=not_('POST')
       )
The above example will ensure that the view is called if the request method
is *not* ``POST``, at least if no other view is more specific.
This technique of wrapping a predicate value in ``not_`` can be used anywhere
predicate values are accepted:
- :meth:`pyramid.config.Configurator.add_view`
- :meth:`pyramid.view.view_config`
.. versionadded:: 1.5
.. index::
   single: view_config placement
@@ -802,7 +831,7 @@
       config.add_view(
           RESTView, route_name='rest', attr='delete', request_method='DELETE')
To reduce the amount of repetion in the ``config.add_view`` statements, we
To reduce the amount of repetition in the ``config.add_view`` statements, we
can move the ``route_name='rest'`` argument to a ``@view_default`` class
decorator on the RESTView class:
docs/whatsnew-1.1.rst
@@ -13,7 +13,7 @@
The term "template" used by the Pyramid documentation used to refer to both
"paster templates" and "rendered templates" (templates created by a rendering
engine.  i.e. Mako, Chameleon, Jinja, etc.).  "Paster templates" will now be
refered to as "scaffolds", whereas the name for "rendered templates" will
referred to as "scaffolds", whereas the name for "rendered templates" will
remain as "templates."
Major Feature Additions
@@ -397,7 +397,7 @@
   shell you use to invoke ``paster serve`` to see these warnings, e.g. on
   UNIX, ``PYTHONWARNINGS=all $VENV/bin/paster serve development.ini``.
   Python 2.5 and 2.6 show deprecation warnings by default,
   so this is unecessary there.
   so this is unnecessary there.
   All deprecation warnings are emitted to the console.
- The :class:`pyramid.view.static` class has been deprecated in favor of the
docs/whatsnew-1.5.rst
New file
@@ -0,0 +1,155 @@
What's New In Pyramid 1.5
=========================
This article explains the new features in :app:`Pyramid` version 1.5 as
compared to its predecessor, :app:`Pyramid` 1.4.  It also documents backwards
incompatibilities between the two versions and deprecations added to
:app:`Pyramid` 1.5, as well as software dependency changes and notable
documentation additions.
Feature Additions
-----------------
The feature additions in Pyramid 1.5 follow.
- Add ``pdistreport`` script, which prints the Python version in use, the
  Pyramid version in use, and the version number and location of all Python
  distributions currently installed.
- Add the ability to invert the result of any view, route, or subscriber
  predicate value using the ``not_`` class.  For example:
  .. code-block:: python
     from pyramid.config import not_
     @view_config(route_name='myroute', request_method=not_('POST'))
     def myview(request): ...
  The above example will ensure that the view is called if the request method
  is not POST, at least if no other view is more specific.
  The :class:`pyramid.config.not_` class can be used against any value that is
  a predicate value passed in any of these contexts:
  - :meth:`pyramid.config.Configurator.add_view`
  - :meth:`pyramid.config.Configurator.add_route`
  - :meth:`pyramid.config.Configurator.add_subscriber`
  - :meth:`pyramid.view.view_config`
  - :meth:`pyramid.events.subscriber`
- View lookup will now search for valid views based on the inheritance
  hierarchy of the context. It tries to find views based on the most specific
  context first, and upon predicate failure, will move up the inheritance chain
  to test views found by the super-type of the context.  In the past, only the
  most specific type containing views would be checked and if no matching view
  could be found then a PredicateMismatch would be raised. Now predicate
  mismatches don't hide valid views registered on super-types. Here's an
  example that now works:
  .. code-block:: python
     class IResource(Interface):
         ...
     @view_config(context=IResource)
     def get(context, request):
         ...
     @view_config(context=IResource, request_method='POST')
     def post(context, request):
         ...
     @view_config(context=IResource, request_method='DELETE')
     def delete(context, request):
         ...
     @implementor(IResource)
     class MyResource:
         ...
     @view_config(context=MyResource, request_method='POST')
     def override_post(context, request):
         ...
  Previously the override_post view registration would hide the get
  and delete views in the context of MyResource -- leading to a
  predicate mismatch error when trying to use GET or DELETE
  methods. Now the views are found and no predicate mismatch is
  raised.
  See https://github.com/Pylons/pyramid/pull/786 and
  https://github.com/Pylons/pyramid/pull/1004 and
  https://github.com/Pylons/pyramid/pull/1046
- ``scripts/prequest.py`` (aka the ``prequest`` console script): added support
  for submitting ``PUT`` and ``PATCH`` requests.  See
  https://github.com/Pylons/pyramid/pull/1033.  add support for submitting
  ``OPTIONS`` and ``PROPFIND`` requests, and allow users to specify basic
  authentication credentials in the request via a ``--login`` argument to the
  script.  See https://github.com/Pylons/pyramid/pull/1039.
- :class:`pyramid.authorization.ACLAuthorizationPolicy` supports ``__acl__`` as
  a callable. This removes the ambiguity between the potential
  ``AttributeError`` that would be raised on the ``context`` when the property
  was not defined and the ``AttributeError`` that could be raised from any
  user-defined code within a dynamic property. It is recommended to define a
  dynamic ACL as a callable to avoid this ambiguity. See
  https://github.com/Pylons/pyramid/issues/735.
- Allow a protocol-relative URL (e.g. ``//example.com/images``) to be passed to
  :meth:`pyramid.config.Configurator.add_static_view`. This allows
  externally-hosted static URLs to be generated based on the current protocol.
- The :class:`pyramid.authentication.AuthTktAuthenticationPolicy` has a new
  ``parent_domain`` option to set the authentication cookie as a wildcard
  cookie on the parent domain. This is useful if you have multiple sites
  sharing the same domain.  It also now supports IPv6 addresses when using
  the ``include_ip=True`` option. This is possibly incompatible with
  alternative ``auth_tkt`` implementations, as the specification does not
  define how to properly handle IPv6. See
  https://github.com/Pylons/pyramid/issues/831.
- Make it possible to use variable arguments via
  :func:`pyramid.paster.get_appsettings`. This also allowed the generated
  ``initialize_db`` script from the ``alchemy`` scaffold to grow support for
  options in the form ``a=1 b=2`` so you can fill in values in a parameterized
  ``.ini`` file, e.g.  ``initialize_myapp_db etc/development.ini a=1 b=2``.  See
  https://github.com/Pylons/pyramid/pull/911
- The ``request.session.check_csrf_token()`` method and the ``check_csrf`` view
  predicate now take into account the value of the HTTP header named
  ``X-CSRF-Token`` (as well as the ``csrf_token`` form parameter, which they
  always did).  The header is tried when the form parameter does not exist.
Backwards Incompatibilities
---------------------------
This release has no known backwards incompatibilities with Pyramid 1.4.X.
Deprecations
------------
This release has no new deprecations as compared to Pyramid 1.4.X.
Documentation Enhancements
--------------------------
Many documentation enhancements have been added, but we did not track them as
they were added.
Dependency Changes
------------------
No dependency changes from Pyramid 1.4.X were made in Pyramid 1.5.
pyramid/authentication.py
@@ -511,8 +511,22 @@
    ``wild_domain``
       Default: ``True``. An auth_tkt cookie will be generated for the
       wildcard domain.
       wildcard domain. If your site is hosted as ``example.com`` this
       will make the cookie available for sites underneath ``example.com``
       such as ``www.example.com``.
       Optional.
    ``parent_domain``
       Default: ``False``. An auth_tkt cookie will be generated for the
       parent domain of the current site. For example if your site is
       hosted under ``www.example.com`` a cookie will be generated for
       ``.example.com``. This can be useful if you have multiple sites
       sharing the same domain. This option supercedes the ``wild_domain``
       option.
       Optional.
       This option is available as of :app:`Pyramid` 1.5.
    ``hashalg``
@@ -565,7 +579,8 @@
                 http_only=False,
                 wild_domain=True,
                 debug=False,
                 hashalg=_marker
                 hashalg=_marker,
                 parent_domain=False,
                 ):
        if hashalg is _marker:
            hashalg = 'md5'
@@ -603,6 +618,7 @@
            path=path,
            wild_domain=wild_domain,
            hashalg=hashalg,
            parent_domain=parent_domain,
            )
        self.callback = callback
        self.debug = debug
@@ -800,7 +816,7 @@
    def __init__(self, secret, cookie_name='auth_tkt', secure=False,
                 include_ip=False, timeout=None, reissue_time=None,
                 max_age=None, http_only=False, path="/", wild_domain=True,
                 hashalg='md5'):
                 hashalg='md5', parent_domain=False):
        self.secret = secret
        self.cookie_name = cookie_name
        self.include_ip = include_ip
@@ -811,6 +827,7 @@
        self.http_only = http_only
        self.path = path
        self.wild_domain = wild_domain
        self.parent_domain = parent_domain
        self.hashalg = hashalg
        static_flags = []
@@ -850,16 +867,19 @@
        cookies = [
            ('Set-Cookie', '%s="%s"; Path=%s%s%s' % (
            self.cookie_name, value, self.path, max_age, self.static_flags)),
            ('Set-Cookie', '%s="%s"; Path=%s; Domain=%s%s%s' % (
            self.cookie_name, value, self.path, cur_domain, max_age,
                self.static_flags)),
            self.cookie_name, value, self.path, max_age, self.static_flags))
            ]
        if self.wild_domain:
            wild_domain = '.' + cur_domain
        domains = []
        if self.parent_domain and cur_domain.count('.') > 1:
            domains.append('.' + cur_domain.split('.', 1)[1])
        else:
            domains.append(cur_domain)
            if self.wild_domain:
                domains.append('.' + cur_domain)
        for domain in domains:
            cookies.append(('Set-Cookie', '%s="%s"; Path=%s; Domain=%s%s%s' % (
                self.cookie_name, value, self.path, wild_domain, max_age,
                self.cookie_name, value, self.path, domain, max_age,
                self.static_flags)))
        return cookies
pyramid/config/__init__.py
@@ -70,7 +70,7 @@
from pyramid.config.settings import SettingsConfiguratorMixin
from pyramid.config.testing import TestingConfiguratorMixin
from pyramid.config.tweens import TweensConfiguratorMixin
from pyramid.config.util import PredicateList
from pyramid.config.util import PredicateList, not_
from pyramid.config.views import ViewsConfiguratorMixin
from pyramid.config.zca import ZCAConfiguratorMixin
@@ -86,6 +86,9 @@
ConfigurationError = ConfigurationError # pyflakes
not_ = not_ # pyflakes, this is an API
class Configurator(
    TestingConfiguratorMixin,
    TweensConfiguratorMixin,
pyramid/config/adapters.py
@@ -216,7 +216,7 @@
           config.add_traverser(MyCustomTraverser)
        This would cause the Pyramid superdefault traverser to never be used;
        intead all traversal would be done using your ``MyCustomTraverser``
        instead all traversal would be done using your ``MyCustomTraverser``
        class, no matter which object was returned by the :term:`root
        factory` of this application.  Note that we passed no arguments to
        the ``iface`` keyword parameter.  The default value of ``iface``,
@@ -228,7 +228,7 @@
        time.  The traverser used can depend on the result of the :term:`root
        factory`.  For instance, if your root factory returns more than one
        type of object conditionally, you could claim that an alternate
        traverser adapter should be used agsinst one particular class or
        traverser adapter should be used against one particular class or
        interface returned by that root factory.  When the root factory
        returned an object that implemented that class or interface, a custom
        traverser would be used.  Otherwise, the default traverser would be
pyramid/config/predicates.py
@@ -294,3 +294,4 @@
            if self.val.issubset(rpset):
                return True
        return False
pyramid/config/routes.py
@@ -90,10 +90,10 @@
          ``traverse`` argument provided to ``add_route`` is
          ``/{article}``, when a request comes in that causes the route
          to match in such a way that the ``article`` match value is
          '1' (when the request URI is ``/articles/1/edit``), the
          ``'1'`` (when the request URI is ``/articles/1/edit``), the
          traversal path will be generated as ``/1``.  This means that
          the root object's ``__getitem__`` will be called with the
          name ``1`` during the traversal phase.  If the ``1`` object
          name ``'1'`` during the traversal phase.  If the ``'1'`` object
          exists, it will become the :term:`context` of the request.
          :ref:`traversal_chapter` has more information about
          traversal.
pyramid/config/util.py
@@ -28,6 +28,69 @@
    val = tuple(sorted(val))
    return val
class not_(object):
    """
    You can invert the meaning of any predicate value by wrapping it in a call
    to :class:`pyramid.config.not_`.
    .. code-block:: python
       :linenos:
       from pyramid.config import not_
       config.add_view(
           'mypackage.views.my_view',
           route_name='ok',
           request_method=not_('POST')
           )
    The above example will ensure that the view is called if the request method
    is *not* ``POST``, at least if no other view is more specific.
    This technique of wrapping a predicate value in ``not_`` can be used
    anywhere predicate values are accepted:
    - :meth:`pyramid.config.Configurator.add_view`
    - :meth:`pyramid.config.Configurator.add_route`
    - :meth:`pyramid.config.Configurator.add_subscriber`
    - :meth:`pyramid.view.view_config`
    - :meth:`pyramid.events.subscriber`
    .. versionadded:: 1.5
    """
    def __init__(self, value):
        self.value = value
class Notted(object):
    def __init__(self, predicate):
        self.predicate = predicate
    def _notted_text(self, val):
        # if the underlying predicate doesnt return a value, it's not really
        # a predicate, it's just something pretending to be a predicate,
        # so dont update the hash
        if val:
            val = '!' + val
        return val
    def text(self):
        return self._notted_text(self.predicate.text())
    def phash(self):
        return self._notted_text(self.predicate.phash())
    def __call__(self, context, request):
        result = self.predicate(context, request)
        phash = self.phash()
        if phash:
            result = not result
        return result
# under = after
# over = before
@@ -74,7 +137,14 @@
            if not isinstance(vals, predvalseq):
                vals = (vals,)
            for val in vals:
                pred = predicate_factory(val, config)
                realval = val
                notted = False
                if isinstance(val, not_):
                    realval = val.value
                    notted = True
                pred = predicate_factory(realval, config)
                if notted:
                    pred = Notted(pred)
                hashes = pred.phash()
                if not is_nonstr_iter(hashes):
                    hashes = [hashes]
pyramid/interfaces.py
@@ -915,7 +915,7 @@
        by ``queue``.  An alternate flash message queue can used by passing
        an optional ``queue``, which must be a string.  If
        ``allow_duplicate`` is false, if the ``msg`` already exists in the
        queue, it will not be readded."""
        queue, it will not be re-added."""
    def pop_flash(queue=''):
        """ Pop a queue from the flash storage.  The queue is removed from
pyramid/renderers.py
@@ -114,7 +114,7 @@
    top-level system names, such as ``request``, ``context``,
    ``renderer_name``, and ``view``.  See :ref:`renderer_system_values` for
    the full list.  If :term:`renderer globals` have been specified, these
    will also be used to agument the value.
    will also be used to argument the value.
    Supply a ``request`` parameter in order to provide the renderer
    with the most correct 'system' values (``request`` and ``context``
@@ -200,7 +200,7 @@
    The default serializer uses ``json.JSONEncoder``. A different
    serializer can be specified via the ``serializer`` argument.
    Custom serializers should accept the object, a callback
    ``default``, and any extra ``kw`` keyword argments passed during
    ``default``, and any extra ``kw`` keyword arguments passed during
    renderer construction.
    .. versionadded:: 1.4
pyramid/router.py
@@ -164,10 +164,14 @@
            except PredicateMismatch:
                # look for other views that meet the predicate
                # criteria
                for iface in context_iface.flattened():
                for iface in context_iface.__sro__[1:]:
                    previous_view_callable = view_callable
                    view_callable = adapters.lookup(
                        (IViewClassifier, request.request_iface, iface),
                        IView, name=view_name, default=None)
                    # intermediate bases may lookup same view_callable
                    if view_callable is previous_view_callable:
                        continue
                    if view_callable is not None:
                        try:
                            response = view_callable(context, request)
pyramid/scaffolds/alchemy/+package+/models.py
@@ -1,5 +1,6 @@
from sqlalchemy import (
    Column,
    Index,
    Integer,
    Text,
    )
@@ -20,9 +21,11 @@
class MyModel(Base):
    __tablename__ = 'models'
    id = Column(Integer, primary_key=True)
    name = Column(Text, unique=True)
    name = Column(Text)
    value = Column(Integer)
    def __init__(self, name, value):
        self.name = name
        self.value = value
Index('my_index', MyModel.name, unique=True, mysql_length=255)
pyramid/scaffolds/alchemy/+package+/tests.py_tmpl
@@ -6,7 +6,7 @@
from .models import DBSession
class TestMyView(unittest.TestCase):
class TestMyViewSuccessCondition(unittest.TestCase):
    def setUp(self):
        self.config = testing.setUp()
        from sqlalchemy import create_engine
@@ -25,9 +25,31 @@
        DBSession.remove()
        testing.tearDown()
    def test_it(self):
    def test_passing_view(self):
        from .views import my_view
        request = testing.DummyRequest()
        info = my_view(request)
        self.assertEqual(info['one'].name, 'one')
        self.assertEqual(info['project'], '{{project}}')
class TestMyViewFailureCondition(unittest.TestCase):
    def setUp(self):
        self.config = testing.setUp()
        from sqlalchemy import create_engine
        engine = create_engine('sqlite://')
        from .models import (
            Base,
            MyModel,
            )
        DBSession.configure(bind=engine)
    def tearDown(self):
        DBSession.remove()
        testing.tearDown()
    def test_failing_view(self):
        from .views import my_view
        request = testing.DummyRequest()
        info = my_view(request)
        self.assertEqual(info.status_int, 500)
pyramid/scripts/pdistreport.py
New file
@@ -0,0 +1,37 @@
import sys
import platform
import pkg_resources
import optparse
from operator import itemgetter
def out(*args): # pragma: no cover
    for arg in args:
        sys.stdout.write(arg)
        sys.stdout.write(' ')
    sys.stdout.write('\n')
def main(argv=sys.argv, pkg_resources=pkg_resources, platform=platform.platform,
         out=out):
    # all args except argv are for unit testing purposes only
    description = "Show Python distribution versions and locations in use"
    usage = "usage: %prog"
    parser = optparse.OptionParser(usage, description=description)
    parser.parse_args(argv[1:])
    packages = []
    for distribution in pkg_resources.working_set:
        name = distribution.project_name
        packages.append(
            {'version': distribution.version,
             'lowername': name.lower(),
             'name': name,
             'location':distribution.location}
            )
    packages = sorted(packages, key=itemgetter('lowername'))
    pyramid_version = pkg_resources.get_distribution('pyramid').version
    plat = platform()
    out('Pyramid version:', pyramid_version)
    out('Platform:', plat)
    out('Packages:')
    for package in packages:
        out(' ', package['name'], package['version'])
        out('   ', package['location'])
pyramid/scripts/prequest.py
@@ -1,3 +1,4 @@
import base64
import optparse
import sys
import textwrap
@@ -28,6 +29,12 @@
    Use "prequest --method=PATCH config.ini /path < data" to do a
    PATCH with the given request body.
    Use "prequest --method=OPTIONS config.ini /path" to do an
    OPTIONS request.
    Use "prequest --method=PROPFIND config.ini /path" to do a
    PROPFIND request.
    If the path is relative (doesn't begin with "/") it is interpreted as
    relative to "/".  The path passed to this script should be URL-quoted.
@@ -66,9 +73,17 @@
    parser.add_option(
        '-m', '--method',
        dest='method',
        choices=['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'],
        choices=['GET', 'HEAD', 'POST', 'PUT', 'PATCH','DELETE',
                 'PROPFIND', 'OPTIONS'],
        type='choice',
        help='Request method type',
        help='Request method type (GET, POST, PUT, PATCH, DELETE, '
             'PROPFIND, OPTIONS)',
        )
    parser.add_option(
        '-l', '--login',
        dest='login',
        type='string',
        help='HTTP basic auth username:password pair',
        )
    get_app = staticmethod(get_app)
@@ -99,6 +114,10 @@
        path = url_unquote(path)
        headers = {}
        if self.options.login:
            enc = base64.b64encode(self.options.login.encode('ascii'))
            headers['Authorization'] = 'Basic ' + enc.decode('ascii')
        if self.options.headers:
            for item in self.options.headers:
                if ':' not in item:
pyramid/scripts/pserve.py
@@ -65,7 +65,7 @@
    You can also include variable assignments like 'http_port=8080'
    and then use %(http_port)s in your config files.
    """
    verbose = 1
    default_verbosity = 1
    parser = optparse.OptionParser(
        usage,
@@ -125,6 +125,18 @@
        action='store_true',
        dest='show_status',
        help="Show the status of the (presumably daemonized) server")
    parser.add_option(
        '-v', '--verbose',
        default=default_verbosity,
        dest='verbose',
        action='count',
        help="Set verbose level (default "+str(default_verbosity)+")")
    parser.add_option(
        '-q', '--quiet',
        action='store_const',
        const=0,
        dest='verbose',
        help="Suppress verbose output")
    if hasattr(os, 'setuid'):
        # I don't think these are available on Windows
@@ -148,19 +160,18 @@
    _scheme_re = re.compile(r'^[a-z][a-z]+:', re.I)
    default_verbosity = 1
    _reloader_environ_key = 'PYTHON_RELOADER_SHOULD_RUN'
    _monitor_environ_key = 'PASTE_MONITOR_SHOULD_RUN'
    possible_subcommands = ('start', 'stop', 'restart', 'status')
    def __init__(self, argv, quiet=False):
        self.quiet = quiet
        self.options, self.args = self.parser.parse_args(argv[1:])
        if quiet:
            self.options.verbose = 0
    def out(self, msg): # pragma: no cover
        if not self.quiet:
        if self.options.verbose > 0:
            print(msg)
    def get_options(self):
@@ -197,7 +208,7 @@
        if self.options.reload:
            if os.environ.get(self._reloader_environ_key):
                if self.verbose > 1:
                if self.options.verbose > 1:
                    self.out('Running reloading file monitor')
                install_reloader(int(self.options.reload_interval), [app_spec])
                # if self.requires_config_file:
@@ -271,7 +282,7 @@
            try:
                self.daemonize()
            except DaemonizeException as ex:
                if self.verbose > 0:
                if self.options.verbose > 0:
                    self.out(str(ex))
                return 2
@@ -303,7 +314,7 @@
        app = self.loadapp(app_spec, name=app_name, relative_to=base,
                global_conf=vars)
        if self.verbose > 0:
        if self.options.verbose > 0:
            if hasattr(os, 'getpid'):
                msg = 'Starting server in PID %i.' % os.getpid()
            else:
@@ -314,7 +325,7 @@
            try:
                server(app)
            except (SystemExit, KeyboardInterrupt) as e:
                if self.verbose > 1:
                if self.options.verbose > 1:
                    raise
                if str(e):
                    msg = ' ' + str(e)
@@ -358,7 +369,7 @@
                "Daemon is already running (PID: %s from PID file %s)"
                % (pid, self.options.pid_file))
        if self.verbose > 0:
        if self.options.verbose > 0:
            self.out('Entering daemon mode')
        pid = os.fork()
        if pid:
@@ -433,11 +444,11 @@
    def record_pid(self, pid_file):
        pid = os.getpid()
        if self.verbose > 1:
        if self.options.verbose > 1:
            self.out('Writing PID %s to %s' % (pid, pid_file))
        with open(pid_file, 'w') as f:
            f.write(str(pid))
        atexit.register(self._remove_pid_file, pid, pid_file, self.verbose)
        atexit.register(self._remove_pid_file, pid, pid_file, self.options.verbose)
    def stop_daemon(self): # pragma: no cover
        pid_file = self.options.pid_file or 'pyramid.pid'
@@ -490,7 +501,7 @@
        self.restart_with_monitor(reloader=True)
    def restart_with_monitor(self, reloader=False): # pragma: no cover
        if self.verbose > 0:
        if self.options.verbose > 0:
            if reloader:
                self.out('Starting subprocess with file monitor')
            else:
@@ -511,7 +522,7 @@
                    proc = None
                except KeyboardInterrupt:
                    self.out('^C caught in monitor process')
                    if self.verbose > 1:
                    if self.options.verbose > 1:
                        raise
                    return 1
            finally:
@@ -527,7 +538,7 @@
                # a monitor, any exit code will restart
                if exit_code != 3:
                    return exit_code
            if self.verbose > 0:
            if self.options.verbose > 0:
                self.out('%s %s %s' % ('-' * 20, 'Restarting', '-' * 20))
    def change_user_group(self, user, group): # pragma: no cover
@@ -559,7 +570,7 @@
            if not gid:
                gid = entry.pw_gid
            uid = entry.pw_uid
        if self.verbose > 0:
        if self.options.verbose > 0:
            self.out('Changing user to %s:%s (%s:%s)' % (
                user, group or '(unknown)', uid, gid))
        if gid:
pyramid/testing.py
@@ -411,7 +411,7 @@
    suitable testing analogue.
    After ``setUp`` is finished, the registry returned by the
    :func:`pyramid.threadlocal.get_current_request` function will
    :func:`pyramid.threadlocal.get_current_registry` function will
    be the passed (or constructed) registry until
    :func:`pyramid.testing.tearDown` is called (or
    :func:`pyramid.testing.setUp` is called again) .
pyramid/tests/test_authentication.py
@@ -947,6 +947,30 @@
        self.assertTrue(result[1][1].endswith('; Path=/; Domain=localhost'))
        self.assertTrue(result[1][1].startswith('auth_tkt='))
    def test_remember_parent_domain(self):
        helper = self._makeOne('secret', parent_domain=True)
        request = self._makeRequest()
        request.environ['HTTP_HOST'] = 'www.example.com'
        result = helper.remember(request, 'other')
        self.assertEqual(len(result), 2)
        self.assertEqual(result[0][0], 'Set-Cookie')
        self.assertTrue(result[0][1].endswith('; Path=/'))
        self.assertTrue(result[0][1].startswith('auth_tkt='))
        self.assertEqual(result[1][0], 'Set-Cookie')
        self.assertTrue(result[1][1].endswith('; Path=/; Domain=.example.com'))
        self.assertTrue(result[1][1].startswith('auth_tkt='))
    def test_remember_parent_domain_supercedes_wild_domain(self):
        helper = self._makeOne('secret', parent_domain=True, wild_domain=True)
        request = self._makeRequest()
        request.environ['HTTP_HOST'] = 'www.example.com'
        result = helper.remember(request, 'other')
        self.assertEqual(len(result), 2)
        self.assertTrue(result[0][1].endswith('; Path=/'))
        self.assertTrue(result[1][1].endswith('; Path=/; Domain=.example.com'))
    def test_remember_domain_has_port(self):
        helper = self._makeOne('secret', wild_domain=False)
        request = self._makeRequest()
pyramid/tests/test_config/test_util.py
@@ -364,6 +364,23 @@
    def test_unknown_predicate(self):
        from pyramid.exceptions import ConfigurationError
        self.assertRaises(ConfigurationError, self._callFUT, unknown=1)
    def test_notted(self):
        from pyramid.config import not_
        from pyramid.testing import DummyRequest
        request = DummyRequest()
        _, predicates, _ = self._callFUT(
            xhr='xhr',
            request_method=not_('POST'),
            header=not_('header'),
            )
        self.assertEqual(predicates[0].text(), 'xhr = True')
        self.assertEqual(predicates[1].text(),
                         "!request_method = POST")
        self.assertEqual(predicates[2].text(), '!header header')
        self.assertEqual(predicates[1](None, request), True)
        self.assertEqual(predicates[2](None, request), True)
        
class Test_takes_one_arg(unittest.TestCase):
    def _callFUT(self, view, attr=None, argname=None):
@@ -551,7 +568,37 @@
        foo = Foo()
        self.assertTrue(self._callFUT(foo.method))
class TestNotted(unittest.TestCase):
    def _makeOne(self, predicate):
        from pyramid.config.util import Notted
        return Notted(predicate)
    def test_it_with_phash_val(self):
        pred = DummyPredicate('val')
        inst = self._makeOne(pred)
        self.assertEqual(inst.text(), '!val')
        self.assertEqual(inst.phash(), '!val')
        self.assertEqual(inst(None, None), False)
    def test_it_without_phash_val(self):
        pred = DummyPredicate('')
        inst = self._makeOne(pred)
        self.assertEqual(inst.text(), '')
        self.assertEqual(inst.phash(), '')
        self.assertEqual(inst(None, None), True)
class DummyPredicate(object):
    def __init__(self, result):
        self.result = result
    def text(self):
        return self.result
    phash = text
    def __call__(self, context, request):
        return True
class DummyCustomPredicate(object):
    def __init__(self):
        self.__text__ = 'custom predicate'
pyramid/tests/test_router.py
@@ -1180,11 +1180,9 @@
        from pyramid.interfaces import IViewClassifier
        from pyramid.interfaces import IRequest, IResponse
        from pyramid.response import Response
        from zope.interface import Interface, implementer
        class IContext(Interface):
        class BaseContext:
            pass
        @implementer(IContext)
        class DummyContext:
        class DummyContext(BaseContext):
            pass
        context = DummyContext()
        self._registerTraverserFactory(context)
@@ -1193,7 +1191,7 @@
                           DummyContext)
        good_view = DummyView('abc')
        self._registerView(self.config.derive_view(good_view),
                            '', IViewClassifier, IRequest, IContext)
                            '', IViewClassifier, IRequest, BaseContext)
        router = self._makeOne()
        def make_response(s):
            return Response(s)
pyramid/tests/test_scripts/test_pdistreport.py
New file
@@ -0,0 +1,74 @@
import unittest
from pyramid.tests.test_scripts import dummy
class TestPDistReportCommand(unittest.TestCase):
    def _callFUT(self, **kw):
        argv = []
        from pyramid.scripts.pdistreport import main
        return main(argv, **kw)
    def test_no_dists(self):
        def platform():
            return 'myplatform'
        pkg_resources = DummyPkgResources()
        L = []
        def out(*args):
            L.extend(args)
        result = self._callFUT(pkg_resources=pkg_resources, platform=platform,
                               out=out)
        self.assertEqual(result, None)
        self.assertEqual(
            L,
            ['Pyramid version:', '1',
             'Platform:', 'myplatform',
             'Packages:']
            )
    def test_with_dists(self):
        def platform():
            return 'myplatform'
        working_set = (DummyDistribution('abc'), DummyDistribution('def'))
        pkg_resources = DummyPkgResources(working_set)
        L = []
        def out(*args):
            L.extend(args)
        result = self._callFUT(pkg_resources=pkg_resources, platform=platform,
                               out=out)
        self.assertEqual(result, None)
        self.assertEqual(
            L,
            ['Pyramid version:',
             '1',
             'Platform:',
             'myplatform',
             'Packages:',
             ' ',
             'abc',
             '1',
             '   ',
             '/projects/abc',
             ' ',
             'def',
             '1',
             '   ',
             '/projects/def']
            )
class DummyPkgResources(object):
    def __init__(self, working_set=()):
        self.working_set = working_set
    def get_distribution(self, name):
        return Version('1')
class Version(object):
    def __init__(self, version):
        self.version = version
class DummyDistribution(object):
    def __init__(self, name):
        self.project_name = name
        self.version = '1'
        self.location = '/projects/%s' % name
pyramid/tests/test_scripts/test_prequest.py
@@ -68,6 +68,19 @@
        self.assertEqual(self._app_name, None)
        self.assertEqual(self._out, ['abc'])
    def test_command_w_basic_auth(self):
        command = self._makeOne(
            ['', '--login=user:password',
                 '--header=name:value','development.ini', '/'])
        command.run()
        self.assertEqual(self._environ['HTTP_NAME'], 'value')
        self.assertEqual(self._environ['HTTP_AUTHORIZATION'],
                        'Basic dXNlcjpwYXNzd29yZA==')
        self.assertEqual(self._path_info, '/')
        self.assertEqual(self._spec, 'development.ini')
        self.assertEqual(self._app_name, None)
        self.assertEqual(self._out, ['abc'])
    def test_command_has_content_type_header_var(self):
        command = self._makeOne(
            ['', '--header=content-type:app/foo','development.ini', '/'])
@@ -96,6 +109,7 @@
    def test_command_method_get(self):
        command = self._makeOne(['', '--method=GET', 'development.ini', '/'])
        command.run()
        self.assertEqual(self._environ['REQUEST_METHOD'], 'GET')
        self.assertEqual(self._path_info, '/')
        self.assertEqual(self._spec, 'development.ini')
        self.assertEqual(self._app_name, None)
@@ -107,6 +121,7 @@
        stdin = NativeIO()
        command.stdin = stdin
        command.run()
        self.assertEqual(self._environ['REQUEST_METHOD'], 'POST')
        self.assertEqual(self._environ['CONTENT_LENGTH'], '-1')
        self.assertEqual(self._environ['wsgi.input'], stdin)
        self.assertEqual(self._path_info, '/')
@@ -120,6 +135,7 @@
        stdin = NativeIO()
        command.stdin = stdin
        command.run()
        self.assertEqual(self._environ['REQUEST_METHOD'], 'PUT')
        self.assertEqual(self._environ['CONTENT_LENGTH'], '-1')
        self.assertEqual(self._environ['wsgi.input'], stdin)
        self.assertEqual(self._path_info, '/')
@@ -133,6 +149,7 @@
        stdin = NativeIO()
        command.stdin = stdin
        command.run()
        self.assertEqual(self._environ['REQUEST_METHOD'], 'PATCH')
        self.assertEqual(self._environ['CONTENT_LENGTH'], '-1')
        self.assertEqual(self._environ['wsgi.input'], stdin)
        self.assertEqual(self._path_info, '/')
@@ -140,6 +157,32 @@
        self.assertEqual(self._app_name, None)
        self.assertEqual(self._out, ['abc'])
    def test_command_method_propfind(self):
        from pyramid.compat import NativeIO
        command = self._makeOne(['', '--method=PROPFIND', 'development.ini',
                                '/'])
        stdin = NativeIO()
        command.stdin = stdin
        command.run()
        self.assertEqual(self._environ['REQUEST_METHOD'], 'PROPFIND')
        self.assertEqual(self._path_info, '/')
        self.assertEqual(self._spec, 'development.ini')
        self.assertEqual(self._app_name, None)
        self.assertEqual(self._out, ['abc'])
    def test_command_method_options(self):
        from pyramid.compat import NativeIO
        command = self._makeOne(['', '--method=OPTIONS', 'development.ini',
                                '/'])
        stdin = NativeIO()
        command.stdin = stdin
        command.run()
        self.assertEqual(self._environ['REQUEST_METHOD'], 'OPTIONS')
        self.assertEqual(self._path_info, '/')
        self.assertEqual(self._spec, 'development.ini')
        self.assertEqual(self._app_name, None)
        self.assertEqual(self._out, ['abc'])
    def test_command_with_query_string(self):
        command = self._makeOne(['', 'development.ini', '/abc?a=1&b=2&c'])
        command.run()
pyramid/tests/test_scripts/test_pserve.py
@@ -156,7 +156,7 @@
        self.pid_file = tempfile.mktemp()
        pid = os.getpid()
        inst = self._makeOne()
        inst.verbose = verbosity
        inst.options.verbose = verbosity
        try:
            atexit.register = fake_atexit
pyramid/url.py
@@ -387,7 +387,7 @@
        resulting url of a resource that has a path of ``/baz/bar`` will be
        ``http://foo/baz/bar``.  If you want to generate completely relative
        URLs with no leading scheme, host, port, or initial path, you can
        pass ``app_url=''`.  Passing ``app_url=''` when the resource path is
        pass ``app_url=''``.  Passing ``app_url=''`` when the resource path is
        ``/baz/bar`` will return ``/baz/bar``.
        .. versionadded:: 1.3
setup.py
@@ -70,7 +70,7 @@
    ]
setup(name='pyramid',
      version='1.4',
      version='1.5dev',
      description=('The Pyramid Web Framework, a '
                   'Pylons project'),
      long_description=README + '\n\n' +  CHANGES,
@@ -118,6 +118,7 @@
        pviews = pyramid.scripts.pviews:main
        ptweens = pyramid.scripts.ptweens:main
        prequest = pyramid.scripts.prequest:main
        pdistreport = pyramid.scripts.pdistreport:main
        [paste.server_runner]
        wsgiref = pyramid.scripts.pserve:wsgiref_server_runner
        cherrypy = pyramid.scripts.pserve:cherrypy_server_runner