diff options
27 files changed, 402 insertions, 154 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 44e594afb..94c331cef 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -160,6 +160,9 @@ Features - ``request.context`` of environment request during ``bootstrap`` is now the root object if a context isn't already set on a provided request. +- The ``pyramid.decorator.reify`` function is now an API, and was added to + the API documentation. + Deprecations ------------ @@ -246,6 +249,14 @@ Backwards Incompatibilities * ``registerSettings``, use ``pyramid.config.Configurator.add_settings`` instead. +Documentation +------------- + +- Added an "Upgrading Pyramid" chapter to the narrative documentation. It + describes how to cope with deprecations and removals of Pyramid APIs and + how to show Pyramid-generated deprecation warnings while running tests and + while running a server. + Dependencies ------------ @@ -95,6 +95,45 @@ Nice-to-Have - Update App engine chapter with less creaky directions. +- Idea from Zart: + + diff --git a/pyramid/paster.py b/pyramid/paster.py + index b0e4d79..b3bd82a 100644 + --- a/pyramid/paster.py + +++ b/pyramid/paster.py + @@ -8,6 +8,7 @@ from paste.deploy import ( + from pyramid.compat import configparser + from logging.config import fileConfig + from pyramid.scripting import prepare + +from pyramid.config import Configurator + + def get_app(config_uri, name=None, loadapp=loadapp): + """ Return the WSGI application named ``name`` in the PasteDeploy + @@ -111,3 +112,10 @@ def bootstrap(config_uri, request=None): + env['app'] = app + return env + + +def make_pyramid_app(global_conf, app=None, **settings): + + """Return pyramid application configured with provided settings""" + + config = Configurator(package='pyramid', settings=settings) + + if app: + + config.include(app) + + app = config.make_wsgi_app() + + return app + diff --git a/setup.py b/setup.py + index 03ebb42..91e0e21 100644 + --- a/setup.py + +++ b/setup.py + @@ -118,6 +118,8 @@ setup(name='pyramid', + [paste.server_runner] + wsgiref = pyramid.scripts.pserve:wsgiref_server_runner + cherrypy = pyramid.scripts.pserve:cherrypy_server_runner + + [paster.app_factory] + + main = pyramid.paster:make_pyramid_app + """ + ) + + Future ------ @@ -104,7 +143,8 @@ Future original dict (after ``__getattr__`` deprecation period, it was deprecated in 1.2). -- 1.5: Remove ``pyramid.requests.DeprecatedRequestMethodsMixin``. +- 1.5: Remove ``pyramid.requests.DeprecatedRequestMethodsMixin`` and code in + renderers module that looks for _response_content_type, et. al. - 1.5: Maybe? deprecate set_request_property in favor of pointing people at add_request_method, schedule removal for 1.8? diff --git a/docs/api.rst b/docs/api.rst index e33fd6a74..9e540b49b 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -12,6 +12,7 @@ documentation is organized alphabetically by module name. api/authentication api/compat api/config + api/decorator api/events api/exceptions api/httpexceptions diff --git a/docs/api/decorator.rst b/docs/api/decorator.rst new file mode 100644 index 000000000..35d9131df --- /dev/null +++ b/docs/api/decorator.rst @@ -0,0 +1,9 @@ +.. _decorator_module: + +:mod:`pyramid.decorator` +-------------------------- + +.. automodule:: pyramid.decorator + +.. autofunction:: reify + diff --git a/docs/changes.rst b/docs/changes.rst index 6294123ed..fdeaf1e99 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -1,3 +1,5 @@ +.. _changelog: + :app:`Pyramid` Change History ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/index.rst b/docs/index.rst index c84314274..321fe1fed 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -88,6 +88,7 @@ Narrative documentation in chapter form explaining how to use narr/advconfig narr/extconfig narr/scaffolding + narr/upgrading narr/threadlocals narr/zca diff --git a/docs/latexindex.rst b/docs/latexindex.rst index 99ec2f54d..604e6e7c6 100644 --- a/docs/latexindex.rst +++ b/docs/latexindex.rst @@ -85,8 +85,6 @@ API Reference api/authorization api/authentication - api/chameleon_text - api/chameleon_zpt api/config api/events api/exceptions diff --git a/docs/narr/advconfig.rst b/docs/narr/advconfig.rst index 165cf7474..ad22a82c9 100644 --- a/docs/narr/advconfig.rst +++ b/docs/narr/advconfig.rst @@ -414,3 +414,10 @@ constraints: the routes they imply require relative ordering. Such ordering constraints are not absolved by two-phase configuration. Routes are still added in configuration execution order. +More Information +---------------- + +For more information, see the article entitled `"A Whirlwind Rour of Advanced +Configuration Tactics" +<http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/configuration/whirlwind_tour.html>`_ +in the Pyramid Cookbook. diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst index 656cf4773..ba72ebfbf 100644 --- a/docs/narr/templates.rst +++ b/docs/narr/templates.rst @@ -46,20 +46,6 @@ within the body of a view callable like so: {'foo':1, 'bar':2}, request=request) -.. warning:: - - Earlier iterations of this documentation - (pre-version-1.3) encouraged the application developer to use - ZPT-specific APIs such as - :func:`pyramid.chameleon_zpt.render_template_to_response` and - :func:`pyramid.chameleon_zpt.render_template` to render templates - directly. This style of rendering still works, but at least for - purposes of this documentation, those functions are deprecated. - Application developers are encouraged instead to use the functions - available in the :mod:`pyramid.renderers` module to perform - rendering tasks. This set of functions works to render templates - for all renderer extensions registered with :app:`Pyramid`. - The ``sample_view`` :term:`view callable` function above returns a :term:`response` object which contains the body of the ``templates/foo.pt`` template. In this case, the ``templates`` @@ -79,12 +65,12 @@ prefix on Windows. .. warning:: Only :term:`Chameleon` templates support defining a renderer for a - template relative to the location of the module where the view - callable is defined. Mako templates, and other templating system - bindings work differently. In particular, Mako templates use a - "lookup path" as defined by the ``mako.directories`` configuration - file instead of treating relative paths as relative to the current - view module. See :ref:`mako_templates`. + template relative to the location of the module where the view callable is + defined. Mako templates, and other templating system bindings work + differently. In particular, Mako templates use a "lookup path" as defined + by the ``mako.directories`` configuration file instead of treating + relative paths as relative to the current view module. See + :ref:`mako_templates`. The path can alternately be a :term:`asset specification` in the form ``some.dotted.package_name:relative/path``. This makes it possible to @@ -542,7 +528,7 @@ Sommetime you'd like to render a macro inside of a Chameleon ZPT template instead of the full Chameleon ZPT template. To render the content of a ``define-macro`` field inside a Chameleon ZPT template, given a Chameleon template file named ``foo.pt`` and a macro named ``bar`` defined within it -(e.g. ``<div metal:define-macro="bar">...</div>``, you can configure the +(e.g. ``<div metal:define-macro="bar">...</div>``), you can configure the template as a :term:`renderer` like so: .. code-block:: python @@ -554,8 +540,8 @@ template as a :term:`renderer` like so: def my_view(request): return {'project':'my project'} -The above will render the ``bar`` macro from within the ``foo.pt`` template -instead of the entire template. +The above will render only the ``bar`` macro defined within the ``foo.pt`` +template instead of the entire template. .. note:: @@ -600,10 +586,6 @@ When the template is rendered, it will show: Hello, world! -If you'd rather use templates directly within a view callable (without -the indirection of using a renderer), see :ref:`chameleon_text_module` -for the API description. - See also :ref:`built_in_renderers` for more general information about renderers, including Chameleon text renderers. diff --git a/docs/narr/upgrading.rst b/docs/narr/upgrading.rst new file mode 100644 index 000000000..92f125c0a --- /dev/null +++ b/docs/narr/upgrading.rst @@ -0,0 +1,232 @@ +Upgrading Pyramid +================= + +When a new version of Pyramid is released, it will sometimes deprecate a +feature or remove a feature that was deprecated in an older release. When +features are removed from Pyramid, applications that depend on those features +will begin to break. This chapter explains how to ensure your Pyramid +applications keep working when you upgrade the Pyramid version you're using. + +.. sidebar:: About Release Numbering + + Conventionally, application version numbering in Python is described as + ``major.minor.micro``. If your Pyramid version is "1.2.3", it means + you're running a version of Pyramid with the major version "1", the minor + version "2" and the micro version "3". A "major" release is one that + increments the first-dot number; 2.X.X might follow 1.X.X. A "minor" + release is one that increments the second-dot number; 1.3.X might follow + 1.2.X. A "micro" release is one that increments the third-dot number; + 1.2.3 might follow 1.2.2. In general, micro releases are "bugfix-only", + and contain no new features, minor releases contain new features but are + largely backwards compatible with older versions, and a major release + indicates a large set of backwards incompatibilities. + +The Pyramid core team is conservative when it comes to removing features. We +don't remove features unnecessarily, but we're human, and we make mistakes +which cause some features to be evolutionary dead ends. Though we are +willing to support dead-end features for some amount of time, some eventually +have to be removed when the cost of supporting them outweighs the benefit of +keeping them around, because each feature in Pyramid represents a certain +documentation and maintenance burden. + +Deprecation and Removal Policy +------------------------------ + +When a feature is scheduled for removal from Pyramid or any of its official +add-ons, the core development team takes these steps: + +- Using the feature will begin to generate a `DeprecationWarning`, indicating + the version in which the feature became deprecated. + +- A note is added to the documentation indicating that the feature is + deprecated. + +- A note is added to the :ref:`changelog` about the deprecation. + +When a deprecated feature is eventually removed: + +- The feature is removed. + +- A note is added to the :ref:`changelog` about the removal. + +Features are never removed in *micro* releases. They are only removed in +minor and major releases. Deprecated features are kept around for at least +*three* minor releases from the time the feature became deprecated. +Therefore, if a feature is added in Pyramid 1.0, but it's deprecated in +Pyramid 1.1, it will be kept around through all 1.1.X releases, all 1.2.X +releases and all 1.3.X releases. It will finally be removed in the first +1.4.X release. + +Sometimes features are "docs-deprecated" instead of formally deprecated. +This means that the feature will be kept around indefinitely, but it will be +removed from the documentation or a note will be added to the documentation +telling folks to use some other newer feature. This happens when the cost of +keeping an old feature around is very minimal and the support and +documentation burden is very low. For example, we might rename a function +that is an API without changing the arguments it accepts. In this case, +we'll often rename the function, and change the docs to point at the new +function name, but leave around a backwards compatibility alias to the old +function name so older code doesn't break. + +"Docs deprecated" features tend to work "forever", meaning that they won't be +removed, and they'll never generate a deprecation warning. However, such +changes are noted in the :ref:`changelog`, so it's possible to know that you +should change older spellings to newer ones to ensure that people reading +your code can find the APIs you're using in the Pyramid docs. + +Consulting the Change History +----------------------------- + +Your first line of defense against application failures caused by upgrading +to a newer Pyramid release is always to read the :ref:`changelog`. to find +the deprecations and removals for each release between the release you're +currently running and the one you wish to upgrade to. The change history +notes every deprecation within a ``Deprecation`` section and every removal +within a ``Backwards Incompatibilies`` section for each release. + +The change history often contains instructions for changing your code to +avoid deprecation warnings and how to change docs-deprecated spellings to +newer ones. You can follow along with each deprecation explanation in the +change history, simply doing a grep or other code search to your application, +using the change log examples to remediate each potential problem. + +.. _testing_under_new_release: + +Testing Your Application Under a New Pyramid Release +---------------------------------------------------- + +Once you've upgraded your application to a new Pyramid release and you've +remediated as much as possible by using the change history notes, you'll want +to run your application's tests (see :ref:`running_tests`) in such a way that +you can see DeprecationWarnings printed to the console when the tests run. + +.. code-block:: bash + + $ python -Wd setup.py test -q + +The ``-Wd`` argument is an argument that tells Python to print deprecation +warnings to the console. Note that the ``-Wd`` flag is only required for +Python 2.7 and better: Python versions 2.6 and older print deprecation +warnings to the console by default. See `the Python -W flag documentation +<http://docs.python.org/using/cmdline.html#cmdoption-W>`_ for more +information. + +As your tests run, deprecation warnings will be printed to the console +explaining the deprecation and providing instructions about how to prevent +the deprecation warning from being issued. For example: + +.. code-block:: text + + $ python -Wd setup.py test -q + # .. elided ... + running build_ext + /home/chrism/projects/pyramid/env27/myproj/myproj/views.py:3: + DeprecationWarning: static: The "pyramid.view.static" class is deprecated + as of Pyramid 1.1; use the "pyramid.static.static_view" class instead with + the "use_subpath" argument set to True. + from pyramid.view import static + . + ---------------------------------------------------------------------- + Ran 1 test in 0.014s + + OK + +In the above case, it's line #3 in the ``myproj.views`` module (``from +pyramid.view import static``) that is causing the problem: + +.. code-block:: python + :linenos: + + from pyramid.view import view_config + + from pyramid.view import static + myview = static('static', 'static') + +The deprecation warning tells me how to fix it, so I can change the code to +do things the newer way: + +.. code-block:: python + :linenos: + + from pyramid.view. import view_config + + from pyramid.static import static_view + myview = static_view('static', 'static', use_subpath=True) + +When I run the tests again, the deprecation warning is no longer printed to +my console: + +.. code-block:: text + + $ python -Wd setup.py test -q + # .. elided ... + running build_ext + . + ---------------------------------------------------------------------- + Ran 1 test in 0.014s + + OK + + +My Application Doesn't Have Any Tests or Has Few Tests +------------------------------------------------------ + +If your application has no tests, or has only moderate test coverage, running +tests won't tell you very much, because the Pyramid codepaths that generate +deprecation warnings won't be executed. + +In this circumstance, you can start your application interactively under a +server run with the ``PYTHONWARNINGS`` environment variable set to +``default``. On UNIX, you can do that via: + +.. code-block:: bash + + $ PYTHONWARNINGS=default bin/pserve development.ini + +On Windows, you need to issue two commands: + +.. code-block:: bash + + C:\> set PYTHONWARNINGS=default + C:\> Scripts/pserve.exe development.ini + +At this point, it's ensured that deprecation warnings will be printed to the +console whenever a codepath is hit that generates one. You can then click +around in your application interactively to try to generate them, and +remediate as explained in :ref:`testing_under_new_release`. + +See `the PYTHONWARNINGS environment variable documentation +<http://docs.python.org/using/cmdline.html#envvar-PYTHONWARNINGS>`_ or `the +Python -W flag documentation +<http://docs.python.org/using/cmdline.html#cmdoption-W>`_ for more +information. + +Upgrading to the Very Latest Pyramid Release +-------------------------------------------- + +When you upgrade your application to the very most recent Pyramid release, +it's advisable to upgrade step-wise through each most recent minor release, +beginning with the one that you know your application currently runs under, +and ending on the most recent release. For example, if your application is +running in production on Pyramid 1.2.1, and the most recent Pyramid 1.3 +release is Pyramid 1.3.3, and the most recent Pyramid release is 1.4.4, it's +advisable to do this: + +- Upgrade your environment to the most recent 1.2 release. For example, the + most recent 1.2 release might be 1.2.3, so upgrade to it. Then run your + application's tests under 1.2.3 as described in + :ref:`testing_under_new_release`. Note any deprecation warnings and + remediate. + +- Upgrade to the most recent 1.3 release, 1.3.3. Run your application's + tests, note any deprecation warnings and remediate. + +- Upgrade to 1.4.4. Run your application's tests, note any deprecation + warnings and remediate. + +If you skip testing your application under each minor release (for example if +you upgrade directly from 1.2.1 to 1.4.4), you might miss a deprecation +warning and waste more time trying to figure out an error caused by a feature +removal than it would take to upgrade stepwise through each minor release. + + diff --git a/docs/tutorials/modwsgi/index.rst b/docs/tutorials/modwsgi/index.rst index d11167344..a22f12610 100644 --- a/docs/tutorials/modwsgi/index.rst +++ b/docs/tutorials/modwsgi/index.rst @@ -7,12 +7,11 @@ Running a :app:`Pyramid` Application under ``mod_wsgi`` It allows :term:`WSGI` programs to be served using the Apache web server. -This guide will outline broad steps that can be used to get a -:app:`Pyramid` application running under Apache via ``mod_wsgi``. -This particular tutorial was developed under Apple's Mac OS X platform -(Snow Leopard, on a 32-bit Mac), but the instructions should be -largely the same for all systems, delta specific path information for -commands and files. +This guide will outline broad steps that can be used to get a :app:`Pyramid` +application running under Apache via ``mod_wsgi``. This particular tutorial +was developed under Apple's Mac OS X platform (Snow Leopard, on a 32-bit +Mac), but the instructions should be largely the same for all systems, delta +specific path information for commands and files. .. note:: Unfortunately these instructions almost certainly won't work for deploying a :app:`Pyramid` application on a Windows system using @@ -90,12 +89,11 @@ commands and files. `logging` module to allow logging within your application. See :ref:`logging_config`. -#. Make the ``pyramid.wsgi`` script executable. - - .. code-block:: text - - $ cd ~/modwsgi/env - $ chmod 755 pyramid.wsgi + There is no need to make the ``pyramid.wsgi`` script executable. + However, you'll need to make sure that *two* users have access to change + into the ``~/modwsgi/env`` directory: your current user (mine is + ``chrism`` and the user that Apache will run as often named ``apache`` or + ``httpd``). Make sure both of these users can "cd" into that directory. #. Edit your Apache configuration and add some stuff. I happened to create a file named ``/etc/apache2/other/modwsgi.conf`` on my own diff --git a/docs/tutorials/wiki/src/authorization/tutorial/views.py b/docs/tutorials/wiki/src/authorization/tutorial/views.py index 21f12b31d..50485d279 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/views.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/views.py @@ -51,17 +51,17 @@ def view_page(context, request): renderer='templates/edit.pt', permission='edit') def add_page(context, request): - name = request.subpath[0] + pagename = request.subpath[0] if 'form.submitted' in request.params: body = request.params['body'] page = Page(body) - page.__name__ = name + page.__name__ = pagename page.__parent__ = context - context[name] = page + context[pagename] = page return HTTPFound(location = request.resource_url(page)) - save_url = request.resource_url(context, 'add_page', name) + save_url = request.resource_url(context, 'add_page', pagename) page = Page('') - page.__name__ = name + page.__name__ = pagename page.__parent__ = context return dict(page = page, save_url = save_url, diff --git a/docs/tutorials/wiki/src/tests/tutorial/views.py b/docs/tutorials/wiki/src/tests/tutorial/views.py index 21f12b31d..50485d279 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/views.py +++ b/docs/tutorials/wiki/src/tests/tutorial/views.py @@ -51,17 +51,17 @@ def view_page(context, request): renderer='templates/edit.pt', permission='edit') def add_page(context, request): - name = request.subpath[0] + pagename = request.subpath[0] if 'form.submitted' in request.params: body = request.params['body'] page = Page(body) - page.__name__ = name + page.__name__ = pagename page.__parent__ = context - context[name] = page + context[pagename] = page return HTTPFound(location = request.resource_url(page)) - save_url = request.resource_url(context, 'add_page', name) + save_url = request.resource_url(context, 'add_page', pagename) page = Page('') - page.__name__ = name + page.__name__ = pagename page.__parent__ = context return dict(page = page, save_url = save_url, diff --git a/docs/tutorials/wiki/src/views/tutorial/views.py b/docs/tutorials/wiki/src/views/tutorial/views.py index 016f5b6bb..b0c15297f 100644 --- a/docs/tutorials/wiki/src/views/tutorial/views.py +++ b/docs/tutorials/wiki/src/views/tutorial/views.py @@ -35,17 +35,17 @@ def view_page(context, request): @view_config(name='add_page', context='.models.Wiki', renderer='templates/edit.pt') def add_page(context, request): - name = request.subpath[0] + pagename = request.subpath[0] if 'form.submitted' in request.params: body = request.params['body'] page = Page(body) - page.__name__ = name + page.__name__ = pagename page.__parent__ = context - context[name] = page + context[pagename] = page return HTTPFound(location = request.resource_url(page)) - save_url = request.resource_url(context, 'add_page', name) + save_url = request.resource_url(context, 'add_page', pagename) page = Page('') - page.__name__ = name + page.__name__ = pagename page.__parent__ = context return dict(page = page, save_url = save_url) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views.py b/docs/tutorials/wiki2/src/authorization/tutorial/views.py index c7670b049..0d085b0e2 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views.py @@ -60,14 +60,14 @@ def view_page(request): @view_config(route_name='add_page', renderer='templates/edit.pt', permission='edit') def add_page(request): - name = request.matchdict['pagename'] + pagename = request.matchdict['pagename'] if 'form.submitted' in request.params: body = request.params['body'] - page = Page(name, body) + page = Page(pagename, body) DBSession.add(page) return HTTPFound(location = request.route_url('view_page', - pagename=name)) - save_url = request.route_url('add_page', pagename=name) + pagename=pagename)) + save_url = request.route_url('add_page', pagename=pagename) page = Page('', '') return dict(page=page, save_url=save_url, logged_in=authenticated_userid(request)) @@ -75,16 +75,16 @@ def add_page(request): @view_config(route_name='edit_page', renderer='templates/edit.pt', permission='edit') def edit_page(request): - name = request.matchdict['pagename'] - page = DBSession.query(Page).filter_by(name=name).one() + pagename = request.matchdict['pagename'] + page = DBSession.query(Page).filter_by(name=pagename).one() if 'form.submitted' in request.params: page.data = request.params['body'] DBSession.add(page) return HTTPFound(location = request.route_url('view_page', - pagename=name)) + pagename=pagename)) return dict( page=page, - save_url = request.route_url('edit_page', pagename=name), + save_url = request.route_url('edit_page', pagename=pagename), logged_in=authenticated_userid(request), ) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views.py b/docs/tutorials/wiki2/src/tests/tutorial/views.py index f2a33af1e..42ac0eb7f 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/views.py @@ -61,15 +61,15 @@ def view_page(request): @view_config(route_name='add_page', renderer='templates/edit.pt', permission='edit') def add_page(request): - name = request.matchdict['pagename'] + pagename = request.matchdict['pagename'] if 'form.submitted' in request.params: session = DBSession() body = request.params['body'] - page = Page(name, body) + page = Page(pagename, body) session.add(page) return HTTPFound(location = request.route_url('view_page', - pagename=name)) - save_url = request.route_url('add_page', pagename=name) + pagename=pagename)) + save_url = request.route_url('add_page', pagename=pagename) page = Page('', '') return dict(page=page, save_url=save_url, logged_in=authenticated_userid(request)) @@ -77,17 +77,17 @@ def add_page(request): @view_config(route_name='edit_page', renderer='templates/edit.pt', permission='edit') def edit_page(request): - name = request.matchdict['pagename'] + pagename = request.matchdict['pagename'] session = DBSession() - page = session.query(Page).filter_by(name=name).one() + page = session.query(Page).filter_by(name=pagename).one() if 'form.submitted' in request.params: page.data = request.params['body'] session.add(page) return HTTPFound(location = request.route_url('view_page', - pagename=name)) + pagename=pagename)) return dict( page=page, - save_url = request.route_url('edit_page', pagename=name), + save_url = request.route_url('edit_page', pagename=pagename), logged_in=authenticated_userid(request), ) diff --git a/docs/tutorials/wiki2/src/views/tutorial/views.py b/docs/tutorials/wiki2/src/views/tutorial/views.py index c2a94a96b..5a9c75a61 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/views.py +++ b/docs/tutorials/wiki2/src/views/tutorial/views.py @@ -44,27 +44,27 @@ def view_page(request): @view_config(route_name='add_page', renderer='templates/edit.pt') def add_page(request): - name = request.matchdict['pagename'] + pagename = request.matchdict['pagename'] if 'form.submitted' in request.params: body = request.params['body'] - page = Page(name, body) + page = Page(pagename, body) DBSession.add(page) return HTTPFound(location = request.route_url('view_page', - pagename=name)) - save_url = request.route_url('add_page', pagename=name) + pagename=pagename)) + save_url = request.route_url('add_page', pagename=pagename) page = Page('', '') return dict(page=page, save_url=save_url) @view_config(route_name='edit_page', renderer='templates/edit.pt') def edit_page(request): - name = request.matchdict['pagename'] - page = DBSession.query(Page).filter_by(name=name).one() + pagename = request.matchdict['pagename'] + page = DBSession.query(Page).filter_by(name=pagename).one() if 'form.submitted' in request.params: page.data = request.params['body'] DBSession.add(page) return HTTPFound(location = request.route_url('view_page', - pagename=name)) + pagename=pagename)) return dict( page=page, - save_url = request.route_url('edit_page', pagename=name), + save_url = request.route_url('edit_page', pagename=pagename), ) diff --git a/pyramid/chameleon_text.py b/pyramid/chameleon_text.py index 3484b7973..21dc85528 100644 --- a/pyramid/chameleon_text.py +++ b/pyramid/chameleon_text.py @@ -2,18 +2,7 @@ import sys from zope.interface import implementer -from pyramid.compat import reraise - -try: - from chameleon.zpt.template import PageTextTemplateFile - # prevent pyflakes complaining about a redefinition below - PageTextTemplateFile -except ImportError: # pragma: no cover - exc_class, exc, tb = sys.exc_info() - # Chameleon doesn't work on non-CPython platforms - class PageTextTemplateFile(object): - def __init__(self, *arg, **kw): - reraise(ImportError, exc, tb) +from chameleon.zpt.template import PageTextTemplateFile from pyramid.interfaces import ITemplateRenderer diff --git a/pyramid/chameleon_zpt.py b/pyramid/chameleon_zpt.py index d9f4337fa..862e996b9 100644 --- a/pyramid/chameleon_zpt.py +++ b/pyramid/chameleon_zpt.py @@ -2,20 +2,9 @@ import sys from zope.interface import implementer -from pyramid.compat import reraise - -try: - from chameleon.zpt.template import PageTemplateFile - PageTemplateFile # prevent pyflakes complaining about a redefinition below -except ImportError: # pragma: no cover - exc_class, exc, tb = sys.exc_info() - # Chameleon doesn't work on non-CPython platforms - class PageTemplateFile(object): - def __init__(self, *arg, **kw): - reraise(ImportError, exc, tb) +from chameleon.zpt.template import PageTemplateFile from pyramid.interfaces import ITemplateRenderer - from pyramid.decorator import reify from pyramid import renderers diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py index c5d453654..e46519bf5 100644 --- a/pyramid/config/factories.py +++ b/pyramid/config/factories.py @@ -184,7 +184,7 @@ class FactoriesConfiguratorMixin(object): .. warning:: - This method has been deprecated as of Pyramid 1.4. + This method has been docs-deprecated as of Pyramid 1.4. :meth:`pyramid.config.Configurator.add_request_method` should be used instead. diff --git a/pyramid/decorator.py b/pyramid/decorator.py index 98d7b36b5..82d2b1280 100644 --- a/pyramid/decorator.py +++ b/pyramid/decorator.py @@ -1,9 +1,31 @@ class reify(object): + """ Use as a class method decorator. It operates almost exactly like the + Python ``@property`` decorator, but it puts the result of the method it + decorates into the instance dict after the first call, effectively + replacing the function it decorates with an instance variable. It is, in + Python parlance, a non-data descriptor. An example: - """ Put the result of a method which uses this (non-data) - descriptor decorator in the instance dict after the first call, - effectively replacing the decorator with an instance variable.""" + .. code-block:: python + class Foo(object): + @reify + def jammy(self): + print 'jammy called' + return 1 + + And usage of Foo: + + .. code-block:: text + + >>> f = Foo() + >>> v = f.jammy + 'jammy called' + >>> print v + 1 + >>> f.jammy + 1 + >>> # jammy func not called the second time; it replaced itself with 1 + """ def __init__(self, wrapped): self.wrapped = wrapped try: diff --git a/pyramid/mako_templating.py b/pyramid/mako_templating.py index 2b09e8d45..5d09cad01 100644 --- a/pyramid/mako_templating.py +++ b/pyramid/mako_templating.py @@ -95,7 +95,9 @@ class MakoRendererFactoryHelper(object): r'(?:\#(?P<defname>[\w_]+))?' r'(\.(?P<ext>.*))' ) - asset, defname, ext = p.match(info.name).group('asset', 'defname', 'ext') + asset, defname, ext = p.match(info.name).group( + 'asset', 'defname', 'ext' + ) path = '%s.%s' % (asset, ext) registry = info.registry settings = info.settings @@ -154,12 +156,9 @@ class MakoRendererFactoryHelper(object): preprocessor=preprocessor ) - registry_lock.acquire() - try: + with registry_lock: registry.registerUtility(lookup, IMakoLookup, name=settings_prefix) - finally: - registry_lock.release() return MakoLookupTemplateRenderer(path, defname, lookup) diff --git a/pyramid/renderers.py b/pyramid/renderers.py index fe9df33d1..3252c2c93 100644 --- a/pyramid/renderers.py +++ b/pyramid/renderers.py @@ -429,12 +429,9 @@ class ChameleonRendererLookup(object): if renderer is None: renderer = self.impl(spec, self, macro=None) # cache the template - try: - self.lock.acquire() + with self.lock: registry.registerUtility(renderer, ITemplateRenderer, name=spec) - finally: - self.lock.release() else: # spec is a package:relpath asset spec renderer = registry.queryUtility(ITemplateRenderer, name=spec) @@ -445,7 +442,8 @@ class ChameleonRendererLookup(object): r'(\.(?P<ext>.*))' ) asset, macro, ext = p.match(spec).group( - 'asset', 'defname', 'ext') + 'asset', 'defname', 'ext' + ) spec = '%s.%s' % (asset, ext) try: package_name, filename = spec.split(':', 1) @@ -463,12 +461,9 @@ class ChameleonRendererLookup(object): settings = info.settings if not settings.get('reload_assets'): # cache the template - self.lock.acquire() - try: + with self.lock: registry.registerUtility(renderer, ITemplateRenderer, name=spec) - finally: - self.lock.release() return renderer @@ -479,11 +474,8 @@ def template_renderer_factory(info, impl, lock=registry_lock): lookup = registry.queryUtility(IChameleonLookup, name=info.type) if lookup is None: lookup = ChameleonRendererLookup(impl, registry) - lock.acquire() - try: + with lock: registry.registerUtility(lookup, IChameleonLookup, name=info.type) - finally: - lock.release() return lookup(info) @implementer(IRendererInfo) diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py index ea2a4ae09..9fbf0729a 100644 --- a/pyramid/scripts/pserve.py +++ b/pyramid/scripts/pserve.py @@ -583,12 +583,8 @@ class LazyWriter(object): def open(self): if self.fileobj is None: - self.lock.acquire() - try: - if self.fileobj is None: - self.fileobj = open(self.filename, self.mode) - finally: - self.lock.release() + with self.lock: + self.fileobj = open(self.filename, self.mode) return self.fileobj def close(self): diff --git a/pyramid/testing.py b/pyramid/testing.py index 750effb83..e091bac4f 100644 --- a/pyramid/testing.py +++ b/pyramid/testing.py @@ -293,7 +293,7 @@ class DummyRequest(DeprecatedRequestMethodsMixin, URLMethodsMixin, request. For example, by default, the DummyRequest ``GET`` and ``POST`` attributes are of type ``dict``, unlike a normal Request's GET and POST, which are of type ``MultiDict``. If your code uses the features of - MultiDict, you should either use a"real" :class:`pyramid.request.Request` + MultiDict, you should either use a real :class:`pyramid.request.Request` or adapt your DummyRequest by replacing the attributes with ``MultiDict`` instances. diff --git a/pyramid/tests/test_chameleon_text.py b/pyramid/tests/test_chameleon_text.py index 297e96554..d9f20f241 100644 --- a/pyramid/tests/test_chameleon_text.py +++ b/pyramid/tests/test_chameleon_text.py @@ -2,7 +2,6 @@ import sys import unittest from pyramid.compat import binary_type -from pyramid.testing import skip_on from pyramid import testing class Base(object): @@ -50,7 +49,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase): from pyramid.interfaces import ITemplateRenderer verifyClass(ITemplateRenderer, self._getTargetClass()) - @skip_on('java') def test_template_reified(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -59,7 +57,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template, instance.__dict__['template']) - @skip_on('java') def test_template_with_ichameleon_translate(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -68,7 +65,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.translate, lookup.translate) - @skip_on('java') def test_template_with_debug_templates(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -78,7 +74,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.debug, True) - @skip_on('java') def test_template_with_reload_templates(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -88,7 +83,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.auto_reload, True) - @skip_on('java') def test_template_without_reload_templates(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -98,7 +92,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.auto_reload, False) - @skip_on('java') def test_call(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -107,14 +100,12 @@ class TextTemplateRendererTests(Base, unittest.TestCase): self.assertTrue(isinstance(result, binary_type)) self.assertEqual(result, b'Hello.\n') - @skip_on('java') def test_call_with_nondict_value(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) self.assertRaises(ValueError, instance, None, {}) - @skip_on('java') def test_call_nonminimal(self): nonminimal = self._getTemplatePath('nonminimal.txt') lookup = DummyLookup() @@ -123,7 +114,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase): self.assertTrue(isinstance(result, binary_type)) self.assertEqual(result, b'Hello, Chris!\n') - @skip_on('java') def test_implementation(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() diff --git a/pyramid/tests/test_chameleon_zpt.py b/pyramid/tests/test_chameleon_zpt.py index 5d197dac4..37538e83e 100644 --- a/pyramid/tests/test_chameleon_zpt.py +++ b/pyramid/tests/test_chameleon_zpt.py @@ -1,7 +1,6 @@ import sys import unittest -from pyramid.testing import skip_on from pyramid import testing from pyramid.compat import text_type @@ -50,7 +49,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): from pyramid.interfaces import ITemplateRenderer verifyClass(ITemplateRenderer, self._getTargetClass()) - @skip_on('java') def test_call(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -60,7 +58,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): self.assertEqual(result.rstrip('\n'), '<div xmlns="http://www.w3.org/1999/xhtml">\n</div>') - @skip_on('java') def test_template_reified(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -69,7 +66,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template, instance.__dict__['template']) - @skip_on('java') def test_template_with_ichameleon_translate(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -78,7 +74,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.translate, lookup.translate) - @skip_on('java') def test_template_with_debug_templates(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -88,7 +83,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.debug, True) - @skip_on('java') def test_template_without_debug_templates(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -98,7 +92,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.debug, False) - @skip_on('java') def test_template_with_reload_templates(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -108,7 +101,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.auto_reload, True) - @skip_on('java') def test_template_without_reload_templates(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -118,14 +110,12 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.auto_reload, False) - @skip_on('java') def test_call_with_nondict_value(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) self.assertRaises(ValueError, instance, None, {}) - @skip_on('java') def test_implementation(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() |
