summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2014-11-10 00:57:29 -0600
committerMichael Merickel <michael@merickel.org>2014-11-10 00:57:29 -0600
commitd1d3edbbafd5ed0160e70f960025a12fa0783b5b (patch)
treed4bbc47ee9c469d3a89cf84f9df27b0edffe0f29
parentfe83c6bfdab16818cb434d95a09bd6510b43aa24 (diff)
parent7dd39020afbd50f6b27e03bb81ace700ae280bef (diff)
downloadpyramid-d1d3edbbafd5ed0160e70f960025a12fa0783b5b.tar.gz
pyramid-d1d3edbbafd5ed0160e70f960025a12fa0783b5b.tar.bz2
pyramid-d1d3edbbafd5ed0160e70f960025a12fa0783b5b.zip
Merge branch 'master' into feature.security-docs-enhancements
-rw-r--r--.travis.yml1
-rw-r--r--CHANGES.txt28
-rw-r--r--HACKING.txt1
-rw-r--r--docs/glossary.rst9
-rw-r--r--docs/narr/configuration.rst4
-rw-r--r--docs/narr/i18n.rst8
-rw-r--r--docs/narr/logging.rst52
-rw-r--r--docs/narr/startup.rst8
-rw-r--r--docs/quick_tour.rst2
-rw-r--r--docs/quick_tutorial/debugtoolbar.rst52
-rw-r--r--docs/quick_tutorial/forms.rst2
-rw-r--r--docs/quick_tutorial/functional_testing.rst7
-rw-r--r--docs/quick_tutorial/hello_world.rst2
-rw-r--r--docs/quick_tutorial/ini.rst6
-rw-r--r--docs/quick_tutorial/jinja2.rst7
-rw-r--r--docs/quick_tutorial/logging.rst2
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/views.py6
-rw-r--r--pyramid/config/__init__.py17
-rw-r--r--pyramid/config/factories.py9
-rw-r--r--pyramid/httpexceptions.py69
-rw-r--r--pyramid/renderers.py14
-rw-r--r--pyramid/request.py17
-rw-r--r--pyramid/scaffolds/alchemy/development.ini_tmpl2
-rw-r--r--pyramid/scaffolds/alchemy/production.ini_tmpl2
-rw-r--r--pyramid/scaffolds/starter/development.ini_tmpl2
-rw-r--r--pyramid/scaffolds/starter/production.ini_tmpl2
-rw-r--r--pyramid/scaffolds/zodb/development.ini_tmpl2
-rw-r--r--pyramid/scaffolds/zodb/production.ini_tmpl2
-rw-r--r--pyramid/scripts/pcreate.py3
-rw-r--r--pyramid/tests/test_config/test_init.py12
-rw-r--r--pyramid/tests/test_request.py46
-rw-r--r--pyramid/tests/test_router.py6
-rw-r--r--pyramid/tests/test_scripts/test_pcreate.py17
-rw-r--r--pyramid/tests/test_session.py2
-rw-r--r--pyramid/tests/test_testing.py2
35 files changed, 314 insertions, 109 deletions
diff --git a/.travis.yml b/.travis.yml
index ce27b5ec3..4ca998c42 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,6 +8,7 @@ python:
- 3.2
- 3.3
- 3.4
+ - pypy3
install: python setup.py dev
diff --git a/CHANGES.txt b/CHANGES.txt
index 63987d980..c1b729b3f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -6,22 +6,39 @@ Features
- Cache busting for static resources has been added and is available via a new
argument to ``pyramid.config.Configurator.add_static_view``: ``cachebust``.
+ See https://github.com/Pylons/pyramid/pull/1380
+
+- Add ``pyramid.config.Configurator.root_package`` attribute and init
+ parameter to assist with includeable packages that wish to resolve
+ resources relative to the package in which the ``Configurator`` was created.
+ See https://github.com/Pylons/pyramid/pull/1337
+
+- Added line numbers to the log formatters in the scaffolds to assist with
+ debugging. See https://github.com/Pylons/pyramid/pull/1326
+
+- Add new HTTP exception objects for status codes
+ ``428 Precondition Required``, ``429 Too Many Requests`` and
+ ``431 Request Header Fields Too Large`` in ``pyramid.httpexceptions``.
+ See https://github.com/Pylons/pyramid/pull/1372/files
Bug Fixes
---------
- ``pyramid.wsgi.wsgiapp`` and ``pyramid.wsgi.wsgiapp2`` now raise
``ValueError`` when accidentally passed ``None``.
+ See https://github.com/Pylons/pyramid/pull/1320
- Fix an issue whereby predicates would be resolved as maybe_dotted in the
introspectable but not when passed for registration. This would mean that
- add_route_predicate for example can not take a string and turn it into the
- actual callable function.
+ ``add_route_predicate`` for example can not take a string and turn it into
+ the actual callable function.
+ See https://github.com/Pylons/pyramid/pull/1306
- Fix ``pyramid.testing.setUp`` to return a ``Configurator`` with a proper
package. Previously it was not possible to do package-relative includes
using the returned ``Configurator`` during testing. There is now a
``package`` argument that can override this behavior as well.
+ See https://github.com/Pylons/pyramid/pull/1322
- Fix an issue where a ``pyramid.response.FileResponse`` may apply a charset
where it does not belong. See https://github.com/Pylons/pyramid/pull/1251
@@ -31,6 +48,13 @@ Bug Fixes
type, unlike any previous version of Python. See
https://github.com/Pylons/pyramid/issues/1360 for more information.
+- ``pcreate`` now normalizes the package name by converting hyphens to
+ underscores. See https://github.com/Pylons/pyramid/pull/1376
+
+- Fix an issue with the final response/finished callback being unable to
+ add another callback to the list. See
+ https://github.com/Pylons/pyramid/pull/1373
+
Docs
----
diff --git a/HACKING.txt b/HACKING.txt
index 1386be3af..e3afbf241 100644
--- a/HACKING.txt
+++ b/HACKING.txt
@@ -113,6 +113,7 @@ for this use case) and inside that a simple pyramid application named
``hacking`` that you can then fire up like so:
cd env27/hacking
+ ../bin/python setup.py develop
../bin/pserve development.ini
Adding Features
diff --git a/docs/glossary.rst b/docs/glossary.rst
index ef207a4bb..01300a0be 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -758,9 +758,16 @@ Glossary
made. For example the word "java" might be translated
differently if the translation domain is "programming-languages"
than would be if the translation domain was "coffee". A
- translation domain is represnted by a collection of ``.mo`` files
+ translation domain is represented by a collection of ``.mo`` files
within one or more :term:`translation directory` directories.
+ Translation Context
+ A string representing the "context" in which a translation was
+ made within a given :term:`translation domain`. See the gettext
+ documentation, `11.2.5 Using contexts for solving ambiguities
+ <https://www.gnu.org/software/gettext/manual/gettext.html#Contexts>`_
+ for more information.
+
Translator
A callable which receives a :term:`translation string` and returns a
translated Unicode object for the purposes of internationalization. A
diff --git a/docs/narr/configuration.rst b/docs/narr/configuration.rst
index 52615533d..f7fa94daf 100644
--- a/docs/narr/configuration.rst
+++ b/docs/narr/configuration.rst
@@ -17,6 +17,10 @@ plugging application code that you've written into :app:`Pyramid` is also
referred to within this documentation as "configuration"; you are configuring
:app:`Pyramid` to call the code that makes up your application.
+.. seealso::
+ For information on ``.ini`` files for Pyramid applications see the
+ :ref:`startup_chapter` chapter.
+
There are two ways to configure a :app:`Pyramid` application:
:term:`imperative configuration` and :term:`declarative configuration`. Both
are described below.
diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst
index 95f663584..3313f8dad 100644
--- a/docs/narr/i18n.rst
+++ b/docs/narr/i18n.rst
@@ -792,9 +792,11 @@ Then as a part of the code of a custom :term:`locale negotiator`:
.. code-block:: python
:linenos:
- from pyramid.threadlocal import get_current_registry
- settings = get_current_registry().settings
- languages = settings['available_languages'].split()
+ from pyramid.settings import aslist
+
+ def my_locale_negotiator(request):
+ languages = aslist(request.registry.settings['available_languages'])
+ # ...
This is only a suggestion. You can create your own "available
languages" configuration scheme as necessary.
diff --git a/docs/narr/logging.rst b/docs/narr/logging.rst
index 71029bb33..c16673ae6 100644
--- a/docs/narr/logging.rst
+++ b/docs/narr/logging.rst
@@ -16,6 +16,11 @@ how to send log messages to loggers that you've configured.
a third-party scaffold which does not create these files, the
configuration information in this chapter may not be applicable.
+.. index:
+ pair: settings; logging
+ pair: .ini; logging
+ pair: logging; configuration
+
.. _logging_config:
Logging Configuration
@@ -242,7 +247,7 @@ level is set to ``INFO``, whereas the application's log level is set to
[logger_myapp]
level = DEBUG
handlers =
- qualname = helloworld
+ qualname = myapp
All of the child loggers of the ``myapp`` logger will inherit the ``DEBUG``
level unless they're explicitly set differently. Meaning the ``myapp.views``,
@@ -294,15 +299,26 @@ use the :term:`pyramid_exclog` package. Details about its configuration are
in its `documentation
<http://docs.pylonsproject.org/projects/pyramid_exclog/dev/>`_.
+.. index::
+ single: TransLogger
+ single: middleware; TransLogger
+ pair: configuration; middleware
+ single: settings; middleware
+ pair: .ini; middleware
+
+.. _request_logging_with_pastes_translogger:
+
Request Logging with Paste's TransLogger
----------------------------------------
-Paste provides the `TransLogger
-<http://pythonpaste.org/modules/translogger.html>`_ :term:`middleware` for
-logging requests using the `Apache Combined Log Format
-<http://httpd.apache.org/docs/2.2/logs.html#combined>`_. TransLogger combined
-with a FileHandler can be used to create an ``access.log`` file similar to
-Apache's.
+The term:`WSGI` design is modular. Waitress logs error conditions, debugging
+output, etc., but not web traffic. For web traffic logging Paste provides the
+`TransLogger <http://pythonpaste.org/modules/translogger.html>`_
+:term:`middleware`. TransLogger produces logs in the `Apache Combined Log
+Format <http://httpd.apache.org/docs/2.2/logs.html#combined>`_. But
+TransLogger does not write to files, the Python logging system must be
+configured to do this. The Python FileHandler_ logging handler can be used
+alongside TransLogger to create an ``access.log`` file similar to Apache's.
Like any standard :term:`middleware` with a Paste entry point, TransLogger can
be configured to wrap your application using ``.ini`` file syntax. First,
@@ -343,10 +359,12 @@ function of your project's ``__init__`` file:
app = TransLogger(app, setup_console_handler=False)
return app
-TransLogger will automatically setup a logging handler to the console when
-called with no arguments, so it 'just works' in environments that don't
-configure logging. Since we've configured our own logging handlers, we need
-to disable that option via ``setup_console_handler = False``.
+
+.. note::
+ TransLogger will automatically setup a logging handler to the console when
+ called with no arguments, so it 'just works' in environments that don't
+ configure logging. Since our logging handlers are configured we disable
+ the automation via ``setup_console_handler = False``.
With the filter in place, TransLogger's logger (named the ``wsgi`` logger) will
propagate its log messages to the parent logger (the root logger), sending
@@ -361,9 +379,9 @@ its output to the console when we request a page:
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.6) Gecko/20070725
Firefox/2.0.0.6"
-To direct TransLogger to an ``access.log`` FileHandler, we need to add that
-FileHandler to the list of handlers (named ``accesslog``), and ensure that the
-``wsgi`` logger is configured and uses this handler accordingly:
+To direct TransLogger to an ``access.log`` FileHandler, we need the following
+to add a FileHandler (named ``accesslog``) to the list of handlers, and ensure
+that the ``wsgi`` logger is configured and uses this handler accordingly:
.. code-block:: ini
@@ -395,7 +413,7 @@ directs its records only to the ``accesslog`` handler.
Finally, there's no need to use the ``generic`` formatter with TransLogger as
TransLogger itself provides all the information we need. We'll use a
formatter that passes-through the log messages as is. Add a new formatter
-called ``accesslog`` by include the following in your configuration file:
+called ``accesslog`` by including the following in your configuration file:
.. code-block:: ini
@@ -405,7 +423,9 @@ called ``accesslog`` by include the following in your configuration file:
[formatter_accesslog]
format = %(message)s
-Then wire this new ``accesslog`` formatter into the FileHandler:
+
+Finally alter the existing configuration to wire this new
+``accesslog`` formatter into the FileHandler:
.. code-block:: ini
diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst
index 7b4a7ea08..a1a23ed52 100644
--- a/docs/narr/startup.rst
+++ b/docs/narr/startup.rst
@@ -19,6 +19,7 @@ console.
.. index::
single: startup process
+ pair: settings; .ini
The Startup Process
-------------------
@@ -139,6 +140,13 @@ Here's a high-level time-ordered overview of what happens when you press
The server serves the application, and the application is running, waiting
to receive requests.
+.. seealso::
+ Logging configuration is described in the :ref:`logging_chapter`
+ chapter. There, in :ref:`request_logging_with_pastes_translogger`,
+ you will also find an example of how to configure
+ :term:`middleware` to add pre-packaged functionality to your
+ application.
+
.. index::
pair: settings; deployment
single: custom settings
diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst
index 4ab39bb11..41a0dc8c0 100644
--- a/docs/quick_tour.rst
+++ b/docs/quick_tour.rst
@@ -700,7 +700,7 @@ we might need to detect situations when other people use the site. We
need *logging*.
Fortunately Pyramid uses the normal Python approach to logging. The
-scaffold generated in your ``development.ini`` a number of lines that
+scaffold generated in your ``development.ini`` has a number of lines that
configure the logging for you to some reasonable defaults. You then see
messages sent by Pyramid (for example, when a new request comes in).
diff --git a/docs/quick_tutorial/debugtoolbar.rst b/docs/quick_tutorial/debugtoolbar.rst
index 90750c633..d138eb760 100644
--- a/docs/quick_tutorial/debugtoolbar.rst
+++ b/docs/quick_tutorial/debugtoolbar.rst
@@ -58,33 +58,31 @@ Steps
Analysis
========
-``pyramid_debugtoolbar`` is a full-fledged Python package,
-available on PyPI just like thousands of other Python packages. Thus we
-start by installing the ``pyramid_debugtoolbar`` package into our
-virtual environment using normal Python package installation commands.
-
-The ``pyramid_debugtoolbar`` Python package is also a Pyramid add-on,
-which means we need to include its add-on configuration into our web
-application. We could do this with imperative configuration in
-``tutorial/__init__.py`` by using ``config.include``. Pyramid also
-supports wiring in add-on configuration via our ``development.ini``
-using ``pyramid.includes``. We use this to load the configuration for
-the debugtoolbar.
-
-You'll now see an attractive button on the right side of
-your browser, which you may click to provide introspective access to debugging
-information in a new browser tab. Even better, if your web application
-generates an error,
-you will see a nice traceback on the screen. When you want to disable
-this toolbar, no need to change code: you can remove it from
-``pyramid.includes`` in the relevant ``.ini`` configuration file (thus
-showing why configuration files are handy.)
-
-Note injects a small amount of html/css into your app just before the closing
-``</body>`` tag in order to display itself. If you
-start to experience otherwise inexplicable client-side weirdness, you can shut
-it off by commenting out the ``pyramid_debugtoolbar`` line in
-``pyramid.includes`` temporarily.
+``pyramid_debugtoolbar`` is a full-fledged Python package, available on PyPI
+just like thousands of other Python packages. Thus we start by installing the
+``pyramid_debugtoolbar`` package into our virtual environment using normal
+Python package installation commands.
+
+The ``pyramid_debugtoolbar`` Python package is also a Pyramid add-on, which
+means we need to include its add-on configuration into our web application. We
+could do this with imperative configuration in ``tutorial/__init__.py`` by
+using ``config.include``. Pyramid also supports wiring in add-on configuration
+via our ``development.ini`` using ``pyramid.includes``. We use this to load
+the configuration for the debugtoolbar.
+
+You'll now see an attractive button on the right side of your browser, which
+you may click to provide introspective access to debugging information in a
+new browser tab. Even better, if your web application generates an error, you
+will see a nice traceback on the screen. When you want to disable this
+toolbar, no need to change code: you can remove it from ``pyramid.includes``
+in the relevant ``.ini`` configuration file (thus showing why configuration
+files are handy.)
+
+Note that the toolbar injects a small amount of html/css into your app just
+before the closing ``</body>`` tag in order to display itself. If you start to
+experience otherwise inexplicable client-side weirdness, you can shut it off
+by commenting out the ``pyramid_debugtoolbar`` line in ``pyramid.includes``
+temporarily.
.. seealso:: See also :ref:`pyramid_debugtoolbar <toolbar:overview>`.
diff --git a/docs/quick_tutorial/forms.rst b/docs/quick_tutorial/forms.rst
index e8bc0c8b4..b08167edc 100644
--- a/docs/quick_tutorial/forms.rst
+++ b/docs/quick_tutorial/forms.rst
@@ -104,7 +104,7 @@ assets which need to be published. We don't have to know where on disk
it is located. We point at the package, then the path inside the package.
We just need to include a call to ``add_static_view`` to make that
-directory available at a URL. For Pyramid-specific pages,
+directory available at a URL. For Pyramid-specific packages,
Pyramid provides a facility (``config.include()``) which even makes
that unnecessary for consumers of a package. (Deform is not specific to
Pyramid.)
diff --git a/docs/quick_tutorial/functional_testing.rst b/docs/quick_tutorial/functional_testing.rst
index 205ddf5cb..09b05b0bc 100644
--- a/docs/quick_tutorial/functional_testing.rst
+++ b/docs/quick_tutorial/functional_testing.rst
@@ -37,12 +37,15 @@ Steps
$ $VENV/bin/python setup.py develop
$ $VENV/bin/easy_install webtest
-#. Let's extend ``unit_testing/tutorial/tests.py`` to include a
+#. Let's extend ``functional_testing/tutorial/tests.py`` to include a
functional test:
.. literalinclude:: functional_testing/tutorial/tests.py
:linenos:
+ Be sure this file is not executable, or ``nosetests`` may not
+ include your tests.
+
#. Now run the tests:
.. code-block:: bash
@@ -67,4 +70,4 @@ execution time of our tests.
Extra Credit
============
-#. Why do our functional tests use ``b''``? \ No newline at end of file
+#. Why do our functional tests use ``b''``?
diff --git a/docs/quick_tutorial/hello_world.rst b/docs/quick_tutorial/hello_world.rst
index 1a9ba4c9d..4ae80ca87 100644
--- a/docs/quick_tutorial/hello_world.rst
+++ b/docs/quick_tutorial/hello_world.rst
@@ -77,7 +77,7 @@ explanation:
#. *Lines 12-14*. Use Pyramid's :term:`configurator` to connect
:term:`view` code to a particular URL :term:`route`.
-#. *Lines 6-7*. Implement the view code that generates the
+#. *Lines 6-8*. Implement the view code that generates the
:term:`response`.
#. *Lines 15-17*. Publish a :term:`WSGI` app using an HTTP
diff --git a/docs/quick_tutorial/ini.rst b/docs/quick_tutorial/ini.rst
index 3402c50e8..b8720711b 100644
--- a/docs/quick_tutorial/ini.rst
+++ b/docs/quick_tutorial/ini.rst
@@ -14,9 +14,9 @@ Pyramid has a first-class concept of
:ref:`configuration <configuration_narr>` distinct from code.
This approach is optional, but its presence makes it distinct from
other Python web frameworks. It taps into Python's ``setuptools``
-library, which establishes conventions for how Python projects can be
-installed and provide "entry points". Pyramid uses an entry point to
-let a Pyramid application it where to find the WSGI app.
+library, which establishes conventions for installing and providing
+"entry points" for Python projects. Pyramid uses an entry point to
+let a Pyramid application know where to find the WSGI app.
Objectives
==========
diff --git a/docs/quick_tutorial/jinja2.rst b/docs/quick_tutorial/jinja2.rst
index ad6da7a9e..613542349 100644
--- a/docs/quick_tutorial/jinja2.rst
+++ b/docs/quick_tutorial/jinja2.rst
@@ -20,8 +20,8 @@ Objectives
Steps
=====
-#. In this step let's start by installing the ``pyramid_jinja2``
- add-on, the copying the ``view_class`` step's directory:
+#. In this step let's start by copying the ``view_class`` step's
+ directory, and then installing the ``pyramid_jinja2`` add-on.
.. code-block:: bash
@@ -72,9 +72,6 @@ Our view code stayed largely the same. We simply changed the file
extension on the renderer. For the template, the syntax for Chameleon
and Jinja2's basic variable insertion is very similar.
-Our functional tests don't have ``development.ini`` so they needed the
-``pyramid.includes`` to be setup in the test setup.
-
Extra Credit
============
diff --git a/docs/quick_tutorial/logging.rst b/docs/quick_tutorial/logging.rst
index 855ded59f..e07d23d6d 100644
--- a/docs/quick_tutorial/logging.rst
+++ b/docs/quick_tutorial/logging.rst
@@ -16,7 +16,7 @@ we might need to detect problems when other people use the site. We
need *logging*.
Fortunately Pyramid uses the normal Python approach to logging. The
-scaffold generated, in your ``development.ini``, a number of lines that
+scaffold generated, in your ``development.ini``, has a number of lines that
configure the logging for you to some reasonable defaults. You then see
messages sent by Pyramid (for example, when a new request comes in.)
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/views.py b/docs/quick_tutorial/more_view_classes/tutorial/views.py
index 635de0520..156e468a9 100644
--- a/docs/quick_tutorial/more_view_classes/tutorial/views.py
+++ b/docs/quick_tutorial/more_view_classes/tutorial/views.py
@@ -5,7 +5,7 @@ from pyramid.view import (
@view_defaults(route_name='hello')
-class TutorialViews:
+class TutorialViews(object):
def __init__(self, request):
self.request = request
self.view_name = 'TutorialViews'
@@ -25,13 +25,13 @@ class TutorialViews:
def hello(self):
return {'page_title': 'Hello View'}
- # Posting to /home via the "Edit" submit button
+ # Posting to /howdy/first/last via the "Edit" submit button
@view_config(request_method='POST', renderer='edit.pt')
def edit(self):
new_name = self.request.params['new_name']
return {'page_title': 'Edit View', 'new_name': new_name}
- # Posting to /home via the "Delete" submit button
+ # Posting to /howdy/first/last via the "Delete" submit button
@view_config(request_method='POST', request_param='form.delete',
renderer='delete.pt')
def delete(self):
diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py
index ebaae38a9..cfa35ec6c 100644
--- a/pyramid/config/__init__.py
+++ b/pyramid/config/__init__.py
@@ -125,6 +125,14 @@ class Configurator(
is passed (the default), the package is assumed to be the Python package
in which the *caller* of the ``Configurator`` constructor lives.
+ If the ``root_package`` is passed, it will propagate through the
+ configuration hierarchy as a way for included packages to locate
+ resources relative to the package in which the main ``Configurator`` was
+ created. If ``None`` is passed (the default), the ``root_package`` will
+ be derived from the ``package`` argument. The ``package`` attribute is
+ always pointing at the package being included when using :meth:`.include`,
+ whereas the ``root_package`` does not change.
+
If the ``settings`` argument is passed, it should be a Python dictionary
representing the :term:`deployment settings` for this application. These
are later retrievable using the
@@ -243,6 +251,9 @@ class Configurator(
.. versionadded:: 1.3
The ``introspection`` argument.
+
+ .. versionadded:: 1.6
+ The ``root_package`` argument.
"""
manager = manager # for testing injection
venusian = venusian # for testing injection
@@ -272,13 +283,17 @@ class Configurator(
exceptionresponse_view=default_exceptionresponse_view,
route_prefix=None,
introspection=True,
+ root_package=None,
):
if package is None:
package = caller_package()
+ if root_package is None:
+ root_package = package
name_resolver = DottedNameResolver(package)
self.name_resolver = name_resolver
self.package_name = name_resolver.get_package_name()
self.package = name_resolver.get_package()
+ self.root_package = root_package
self.registry = registry
self.autocommit = autocommit
self.route_prefix = route_prefix
@@ -747,6 +762,7 @@ class Configurator(
configurator = self.__class__(
registry=self.registry,
package=package_of(module),
+ root_package=self.root_package,
autocommit=self.autocommit,
route_prefix=route_prefix,
)
@@ -806,6 +822,7 @@ class Configurator(
configurator = self.__class__(
registry=self.registry,
package=package,
+ root_package=self.root_package,
autocommit=self.autocommit,
route_prefix=self.route_prefix,
introspection=self.introspection,
diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py
index 1990c377a..5ce1081c6 100644
--- a/pyramid/config/factories.py
+++ b/pyramid/config/factories.py
@@ -1,4 +1,4 @@
-from zope.deprecation import deprecate
+from zope.deprecation import deprecated
from zope.interface import implementer
from pyramid.interfaces import (
@@ -180,8 +180,6 @@ class FactoriesConfiguratorMixin(object):
introspectables=(intr,))
@action_method
- @deprecate('set_request_propery() is deprecated as of Pyramid 1.5; use '
- 'add_request_method() with the property=True argument instead')
def set_request_property(self, callable, name=None, reify=False):
""" Add a property to the request object.
@@ -195,6 +193,11 @@ class FactoriesConfiguratorMixin(object):
self.add_request_method(
callable, name=name, property=not reify, reify=reify)
+ deprecated(
+ set_request_property,
+ 'set_request_propery() is deprecated as of Pyramid 1.5; use '
+ 'add_request_method() with the property=True argument instead')
+
@implementer(IRequestExtensions)
class _RequestExtensions(object):
def __init__(self):
diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py
index ebee39ada..a30129e16 100644
--- a/pyramid/httpexceptions.py
+++ b/pyramid/httpexceptions.py
@@ -52,6 +52,9 @@ Exception
* 422 - HTTPUnprocessableEntity
* 423 - HTTPLocked
* 424 - HTTPFailedDependency
+ * 428 - HTTPPreconditionRequired
+ * 429 - HTTPTooManyRequests
+ * 431 - HTTPRequestHeaderFieldsTooLarge
HTTPServerError
* 500 - HTTPInternalServerError
* 501 - HTTPNotImplemented
@@ -868,7 +871,12 @@ class HTTPUnprocessableEntity(HTTPClientError):
subclass of :class:`~HTTPClientError`
This indicates that the server is unable to process the contained
- instructions. Only for WebDAV.
+ instructions.
+
+ May be used to notify the client that their JSON/XML is well formed, but
+ not correct for the current request.
+
+ See RFC4918 section 11 for more information.
code: 422, title: Unprocessable Entity
"""
@@ -881,7 +889,7 @@ class HTTPLocked(HTTPClientError):
"""
subclass of :class:`~HTTPClientError`
- This indicates that the resource is locked. Only for WebDAV
+ This indicates that the resource is locked.
code: 423, title: Locked
"""
@@ -896,7 +904,6 @@ class HTTPFailedDependency(HTTPClientError):
This indicates that the method could not be performed because the
requested action depended on another action and that action failed.
- Only for WebDAV.
code: 424, title: Failed Dependency
"""
@@ -907,6 +914,62 @@ class HTTPFailedDependency(HTTPClientError):
'The method could not be performed because the requested '
'action dependended on another action and that action failed')
+class HTTPPreconditionRequired(HTTPClientError):
+ """
+ subclass of :class:`~HTTPClientError`
+
+ This indicates that the origin server requires the
+ request to be conditional.
+
+ Its typical use is to avoid the "lost update" problem, where a client
+ GETs a resource's state, modifies it, and PUTs it back to the server,
+ when meanwhile a third party has modified the state on the server,
+ leading to a conflict. By requiring requests to be conditional, the
+ server can assure that clients are working with the correct copies.
+
+ RFC 6585.3
+
+ code: 428, title: Precondition Required
+ """
+ code = 428
+ title = 'Precondition Required'
+ explanation = (
+ 'The origin server requires the request to be conditional.')
+
+class HTTPTooManyRequests(HTTPClientError):
+ """
+ subclass of :class:`~HTTPClientError`
+
+ This indicates that the user has sent too many
+ requests in a given amount of time ("rate limiting").
+
+ RFC 6585.4
+
+ code: 429, title: Too Many Requests
+ """
+ code = 429
+ title = 'Too Many Requests'
+ explanation = (
+ 'The action could not be performed because there were too '
+ 'many requests by the client.')
+
+class HTTPRequestHeaderFieldsTooLarge(HTTPClientError):
+ """
+ subclass of :class:`~HTTPClientError`
+
+ This indicates that the server is unwilling to process
+ the request because its header fields are too large. The request MAY
+ be resubmitted after reducing the size of the request header fields.
+
+ RFC 6585.5
+
+ code: 431, title: Request Header Fields Too Large
+ """
+ code = 431
+ title = 'Request Header Fields Too Large'
+ explanation = (
+ 'The requests header fields were too large.')
+
############################################################
## 5xx Server Error
############################################################
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index 108255ee4..e647ebacf 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -248,7 +248,7 @@ class JSON(object):
When you've done this, the JSON renderer will be able to serialize
instances of the ``Foo`` class when they're encountered in your view
results."""
-
+
self.components.registerAdapter(adapter, (type_or_iface,),
IJSONAdapter)
@@ -265,7 +265,7 @@ class JSON(object):
response.content_type = 'application/json'
default = self._make_default(request)
return self.serializer(value, default=default, **self.kw)
-
+
return _render
def _make_default(self, request):
@@ -286,7 +286,7 @@ json_renderer_factory = JSON() # bw compat
class JSONP(JSON):
""" `JSONP <http://en.wikipedia.org/wiki/JSONP>`_ renderer factory helper
which implements a hybrid json/jsonp renderer. JSONP is useful for
- making cross-domain AJAX requests.
+ making cross-domain AJAX requests.
Configure a JSONP renderer using the
:meth:`pyramid.config.Configurator.add_renderer` API at application
@@ -309,7 +309,7 @@ class JSONP(JSON):
config = Configurator()
config.add_renderer('jsonp', JSONP(param_name='callback', indent=4))
-
+
.. versionchanged:: 1.4
The ability of this class to accept a ``**kw`` in its constructor.
@@ -487,18 +487,18 @@ class NullRendererHelper(RendererHelper):
@property
def settings(self):
- return get_current_registry().settings or {}
+ return {}
def render_view(self, request, value, view, context):
return value
def render(self, value, system_values, request=None):
return value
-
+
def render_to_response(self, value, system_values, request=None):
return value
def clone(self, name=None, package=None, registry=None):
return self
-
+
null_renderer = NullRendererHelper()
diff --git a/pyramid/request.py b/pyramid/request.py
index 6318049ee..bc2889310 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -1,3 +1,4 @@
+from collections import deque
import json
from zope.interface import implementer
@@ -32,8 +33,8 @@ class TemplateContext(object):
pass
class CallbackMethodsMixin(object):
- response_callbacks = ()
- finished_callbacks = ()
+ response_callbacks = None
+ finished_callbacks = None
def add_response_callback(self, callback):
"""
Add a callback to the set of callbacks to be called by the
@@ -72,15 +73,15 @@ class CallbackMethodsMixin(object):
"""
callbacks = self.response_callbacks
- if not callbacks:
- callbacks = []
+ if callbacks is None:
+ callbacks = deque()
callbacks.append(callback)
self.response_callbacks = callbacks
def _process_response_callbacks(self, response):
callbacks = self.response_callbacks
while callbacks:
- callback = callbacks.pop(0)
+ callback = callbacks.popleft()
callback(self, response)
def add_finished_callback(self, callback):
@@ -132,15 +133,15 @@ class CallbackMethodsMixin(object):
"""
callbacks = self.finished_callbacks
- if not callbacks:
- callbacks = []
+ if callbacks is None:
+ callbacks = deque()
callbacks.append(callback)
self.finished_callbacks = callbacks
def _process_finished_callbacks(self):
callbacks = self.finished_callbacks
while callbacks:
- callback = callbacks.pop(0)
+ callback = callbacks.popleft()
callback(self)
@implementer(IRequest)
diff --git a/pyramid/scaffolds/alchemy/development.ini_tmpl b/pyramid/scaffolds/alchemy/development.ini_tmpl
index e54a8609c..448803c8f 100644
--- a/pyramid/scaffolds/alchemy/development.ini_tmpl
+++ b/pyramid/scaffolds/alchemy/development.ini_tmpl
@@ -68,4 +68,4 @@ level = NOTSET
formatter = generic
[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
diff --git a/pyramid/scaffolds/alchemy/production.ini_tmpl b/pyramid/scaffolds/alchemy/production.ini_tmpl
index b316ec9ca..022bc0b7b 100644
--- a/pyramid/scaffolds/alchemy/production.ini_tmpl
+++ b/pyramid/scaffolds/alchemy/production.ini_tmpl
@@ -59,4 +59,4 @@ level = NOTSET
formatter = generic
[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
diff --git a/pyramid/scaffolds/starter/development.ini_tmpl b/pyramid/scaffolds/starter/development.ini_tmpl
index 842cd61d9..c2a28e178 100644
--- a/pyramid/scaffolds/starter/development.ini_tmpl
+++ b/pyramid/scaffolds/starter/development.ini_tmpl
@@ -57,4 +57,4 @@ level = NOTSET
formatter = generic
[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
diff --git a/pyramid/scaffolds/starter/production.ini_tmpl b/pyramid/scaffolds/starter/production.ini_tmpl
index 6a123abf5..b2681c71d 100644
--- a/pyramid/scaffolds/starter/production.ini_tmpl
+++ b/pyramid/scaffolds/starter/production.ini_tmpl
@@ -51,4 +51,4 @@ level = NOTSET
formatter = generic
[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
diff --git a/pyramid/scaffolds/zodb/development.ini_tmpl b/pyramid/scaffolds/zodb/development.ini_tmpl
index f57d559bf..199ddfab4 100644
--- a/pyramid/scaffolds/zodb/development.ini_tmpl
+++ b/pyramid/scaffolds/zodb/development.ini_tmpl
@@ -62,4 +62,4 @@ level = NOTSET
formatter = generic
[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
diff --git a/pyramid/scaffolds/zodb/production.ini_tmpl b/pyramid/scaffolds/zodb/production.ini_tmpl
index c231e159d..522ff7651 100644
--- a/pyramid/scaffolds/zodb/production.ini_tmpl
+++ b/pyramid/scaffolds/zodb/production.ini_tmpl
@@ -57,4 +57,4 @@ level = NOTSET
formatter = generic
[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
diff --git a/pyramid/scripts/pcreate.py b/pyramid/scripts/pcreate.py
index 4c1f432fb..edf2c39f7 100644
--- a/pyramid/scripts/pcreate.py
+++ b/pyramid/scripts/pcreate.py
@@ -81,7 +81,8 @@ class PCreateCommand(object):
args = self.args
output_dir = os.path.abspath(os.path.normpath(args[0]))
project_name = os.path.basename(os.path.split(output_dir)[1])
- pkg_name = _bad_chars_re.sub('', project_name.lower())
+ pkg_name = _bad_chars_re.sub(
+ '', project_name.lower().replace('-', '_'))
safe_name = pkg_resources.safe_name(project_name)
egg_name = pkg_resources.to_filename(safe_name)
diff --git a/pyramid/tests/test_config/test_init.py b/pyramid/tests/test_config/test_init.py
index d6dba17f6..1e58e4d0f 100644
--- a/pyramid/tests/test_config/test_init.py
+++ b/pyramid/tests/test_config/test_init.py
@@ -736,6 +736,18 @@ pyramid.tests.test_config.dummy_include2""",
else: # pragma: no cover
raise AssertionError
+ def test_include_constant_root_package(self):
+ from pyramid import tests
+ from pyramid.tests import test_config
+ config = self._makeOne(root_package=tests)
+ results = {}
+ def include(config):
+ results['package'] = config.package
+ results['root_package'] = config.root_package
+ config.include(include)
+ self.assertEqual(results['root_package'], tests)
+ self.assertEqual(results['package'], test_config)
+
def test_action_branching_kw_is_None(self):
config = self._makeOne(autocommit=True)
self.assertEqual(config.action('discrim'), None)
diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py
index ed41b62ff..48af98f59 100644
--- a/pyramid/tests/test_request.py
+++ b/pyramid/tests/test_request.py
@@ -1,3 +1,4 @@
+from collections import deque
import unittest
from pyramid import testing
@@ -119,13 +120,13 @@ class TestRequest(unittest.TestCase):
def test_add_response_callback(self):
inst = self._makeOne()
- self.assertEqual(inst.response_callbacks, ())
+ self.assertEqual(inst.response_callbacks, None)
def callback(request, response):
""" """
inst.add_response_callback(callback)
- self.assertEqual(inst.response_callbacks, [callback])
+ self.assertEqual(list(inst.response_callbacks), [callback])
inst.add_response_callback(callback)
- self.assertEqual(inst.response_callbacks, [callback, callback])
+ self.assertEqual(list(inst.response_callbacks), [callback, callback])
def test__process_response_callbacks(self):
inst = self._makeOne()
@@ -135,24 +136,48 @@ class TestRequest(unittest.TestCase):
def callback2(request, response):
request.called2 = True
response.called2 = True
- inst.response_callbacks = [callback1, callback2]
+ inst.add_response_callback(callback1)
+ inst.add_response_callback(callback2)
response = DummyResponse()
inst._process_response_callbacks(response)
self.assertEqual(inst.called1, True)
self.assertEqual(inst.called2, True)
self.assertEqual(response.called1, True)
self.assertEqual(response.called2, True)
- self.assertEqual(inst.response_callbacks, [])
+ self.assertEqual(len(inst.response_callbacks), 0)
+
+ def test__process_response_callback_adding_response_callback(self):
+ """
+ When a response callback adds another callback, that new callback should still be called.
+
+ See https://github.com/Pylons/pyramid/pull/1373
+ """
+ inst = self._makeOne()
+ def callback1(request, response):
+ request.called1 = True
+ response.called1 = True
+ request.add_response_callback(callback2)
+ def callback2(request, response):
+ request.called2 = True
+ response.called2 = True
+ inst.add_response_callback(callback1)
+ response = DummyResponse()
+ inst._process_response_callbacks(response)
+ self.assertEqual(inst.called1, True)
+ self.assertEqual(inst.called2, True)
+ self.assertEqual(response.called1, True)
+ self.assertEqual(response.called2, True)
+ self.assertEqual(len(inst.response_callbacks), 0)
def test_add_finished_callback(self):
inst = self._makeOne()
- self.assertEqual(inst.finished_callbacks, ())
+ self.assertEqual(inst.finished_callbacks, None)
def callback(request):
""" """
inst.add_finished_callback(callback)
- self.assertEqual(inst.finished_callbacks, [callback])
+ self.assertEqual(list(inst.finished_callbacks), [callback])
inst.add_finished_callback(callback)
- self.assertEqual(inst.finished_callbacks, [callback, callback])
+ self.assertEqual(list(inst.finished_callbacks), [callback, callback])
def test__process_finished_callbacks(self):
inst = self._makeOne()
@@ -160,11 +185,12 @@ class TestRequest(unittest.TestCase):
request.called1 = True
def callback2(request):
request.called2 = True
- inst.finished_callbacks = [callback1, callback2]
+ inst.add_finished_callback(callback1)
+ inst.add_finished_callback(callback2)
inst._process_finished_callbacks()
self.assertEqual(inst.called1, True)
self.assertEqual(inst.called2, True)
- self.assertEqual(inst.finished_callbacks, [])
+ self.assertEqual(len(inst.finished_callbacks), 0)
def test_resource_url(self):
self._registerResourceURL()
diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py
index 838e52db0..c6c6eea1c 100644
--- a/pyramid/tests/test_router.py
+++ b/pyramid/tests/test_router.py
@@ -522,7 +522,7 @@ class TestRouter(unittest.TestCase):
def view(context, request):
def callback(request, response):
response.called_back = True
- request.response_callbacks = [callback]
+ request.add_response_callback(callback)
return response
environ = self._makeEnviron()
self._registerView(view, '', IViewClassifier, IRequest, IContext)
@@ -545,7 +545,7 @@ class TestRouter(unittest.TestCase):
def view(context, request):
def callback(request):
request.environ['called_back'] = True
- request.finished_callbacks = [callback]
+ request.add_finished_callback(callback)
return response
environ = self._makeEnviron()
self._registerView(view, '', IViewClassifier, IRequest, IContext)
@@ -567,7 +567,7 @@ class TestRouter(unittest.TestCase):
def view(context, request):
def callback(request):
request.environ['called_back'] = True
- request.finished_callbacks = [callback]
+ request.add_finished_callback(callback)
raise NotImplementedError
environ = self._makeEnviron()
self._registerView(view, '', IViewClassifier, IRequest, IContext)
diff --git a/pyramid/tests/test_scripts/test_pcreate.py b/pyramid/tests/test_scripts/test_pcreate.py
index 2488e9595..020721ca7 100644
--- a/pyramid/tests/test_scripts/test_pcreate.py
+++ b/pyramid/tests/test_scripts/test_pcreate.py
@@ -73,6 +73,23 @@ class TestPCreateCommand(unittest.TestCase):
{'project': 'Distro', 'egg': 'Distro', 'package': 'distro',
'pyramid_version': '0.1', 'pyramid_docs_branch':'0.1-branch'})
+ def test_scaffold_with_hyphen_in_project_name(self):
+ import os
+ cmd = self._makeOne('-s', 'dummy', 'Distro-')
+ scaffold = DummyScaffold('dummy')
+ cmd.scaffolds = [scaffold]
+ cmd.pyramid_dist = DummyDist("0.1")
+ result = cmd.run()
+ self.assertEqual(result, 0)
+ self.assertEqual(
+ scaffold.output_dir,
+ os.path.normpath(os.path.join(os.getcwd(), 'Distro-'))
+ )
+ self.assertEqual(
+ scaffold.vars,
+ {'project': 'Distro-', 'egg': 'Distro_', 'package': 'distro_',
+ 'pyramid_version': '0.1', 'pyramid_docs_branch':'0.1-branch'})
+
def test_known_scaffold_absolute_path(self):
import os
path = os.path.abspath('Distro')
diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py
index 35c234e99..b013ffa66 100644
--- a/pyramid/tests/test_session.py
+++ b/pyramid/tests/test_session.py
@@ -521,7 +521,7 @@ class Test_manage_accessed(unittest.TestCase):
result = wrapper(session, 'a')
self.assertEqual(result, 1)
callbacks = request.response_callbacks
- self.assertEqual(len(callbacks), 0)
+ if callbacks is not None: self.assertEqual(len(callbacks), 0)
class Test_manage_changed(unittest.TestCase):
def _makeOne(self, wrapped):
diff --git a/pyramid/tests/test_testing.py b/pyramid/tests/test_testing.py
index 2d0548b33..dfcad2a0c 100644
--- a/pyramid/tests/test_testing.py
+++ b/pyramid/tests/test_testing.py
@@ -217,7 +217,7 @@ class TestDummyRequest(unittest.TestCase):
def test_add_response_callback(self):
request = self._makeOne()
request.add_response_callback(1)
- self.assertEqual(request.response_callbacks, [1])
+ self.assertEqual(list(request.response_callbacks), [1])
def test_registry_is_config_registry_when_setup_is_called_after_ctor(self):
# see https://github.com/Pylons/pyramid/issues/165