diff options
| -rw-r--r-- | CHANGES.txt | 748 | ||||
| -rw-r--r-- | CONTRIBUTORS.txt | 4 | ||||
| -rw-r--r-- | HISTORY.txt | 733 | ||||
| -rw-r--r-- | TODO.txt | 3 | ||||
| -rw-r--r-- | docs/api/renderers.rst | 4 | ||||
| -rw-r--r-- | docs/conf.py | 2 | ||||
| -rw-r--r-- | docs/narr/renderers.rst | 63 | ||||
| -rw-r--r-- | pyramid/config/views.py | 6 | ||||
| -rw-r--r-- | pyramid/mako_templating.py | 14 | ||||
| -rw-r--r-- | pyramid/renderers.py | 128 | ||||
| -rw-r--r-- | pyramid/security.py | 2 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_views.py | 10 | ||||
| -rw-r--r-- | pyramid/tests/test_mako_templating.py | 13 | ||||
| -rw-r--r-- | pyramid/tests/test_renderers.py | 47 | ||||
| -rw-r--r-- | pyramid/tests/test_security.py | 3 | ||||
| -rw-r--r-- | setup.py | 2 |
16 files changed, 1013 insertions, 769 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 7fd8b7a64..f0fef62ea 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,740 +4,24 @@ Next release Bug Fixes --------- -- Add ``REMOTE_ADDR`` to the ``prequest`` WSGI environ dict for benefit of - the debug toolbar, which effectively requires it to be present to work - properly. - -1.3 (2012-03-21) -================ - -Bug Fixes ---------- - -- When ``pyramid.wsgi.wsgiapp2`` calls the downstream WSGI app, the app's - environ will no longer have (deprecated and potentially misleading) - ``bfg.routes.matchdict`` or ``bfg.routes.route`` keys in it. A symptom of - this bug would be a ``wsgiapp2``-wrapped Pyramid app finding the wrong view - because it mistakenly detects that a route was matched when, in fact, it - was not. - -- The fix for issue https://github.com/Pylons/pyramid/issues/461 (which made - it possible for instance methods to be used as view callables) introduced a - backwards incompatibility when methods that declared only a request - argument were used. See https://github.com/Pylons/pyramid/issues/503 - -1.3b3 (2012-03-17) -================== - -Bug Fixes ---------- - -- ``config.add_view(<aninstancemethod>)`` raised AttributeError involving - ``__text__``. See https://github.com/Pylons/pyramid/issues/461 - -- Remove references to do-nothing ``pyramid.debug_templates`` setting in all - Pyramid-provided ``.ini`` files. This setting previously told Chameleon to - render better exceptions; now Chameleon always renders nice exceptions - regardless of the value of this setting. - -Scaffolds ---------- - -- The ``alchemy`` scaffold now shows an informative error message in the - browser if the person creating the project forgets to run the - initialization script. - -- The ``alchemy`` scaffold initialization script is now called - ``initialize_<projectname>_db`` instead of ``populate_<projectname>``. - -Documentation -------------- - -- Wiki tutorials improved due to collaboration at PyCon US 2012 sprints. - -1.3b2 (2012-03-02) -================== - -Bug Fixes ---------- - -- The method ``pyramid.request.Request.partial_application_url`` is no longer - in the API docs. It was meant to be a private method; its publication in - the documentation as an API method was a mistake, and it has been renamed - to something private. - -- When a static view was registered using an absolute filesystem path on - Windows, the ``request.static_url`` function did not work to generate URLs - to its resources. Symptom: "No static URL definition matching - c:\\foo\\bar\\baz". - -- Make all tests pass on Windows XP. - -- Bug in ACL authentication checking on Python 3: the ``permits`` and - ``principals_allowed_by_permission`` method of - ``pyramid.authorization.ACLAuthenticationPolicy`` could return an - inappropriate ``True`` value when a permission on an ACL was a string - rather than a sequence, and then only if the ACL permission string was a - substring of the ``permission`` value passed to the function. - - This bug effects no Pyramid deployment under Python 2; it is a bug that - exists only in deployments running on Python 3. It has existed since - Pyramid 1.3a1. - - This bug was due to the presence of an ``__iter__`` attribute on strings - under Python 3 which is not present under strings in Python 2. - -1.3b1 (2012-02-26) -================== - -Bug Fixes ---------- - -- ``pyramid.config.Configurator.with_package`` didn't work if the - Configurator was an old-style ``pyramid.configuration.Configurator`` - instance. - -- Pyramid authorization policies did not show up in the introspector. - -Deprecations ------------- - -- All references to the ``tmpl_context`` request variable were removed from - the docs. Its existence in Pyramid is confusing for people who were never - Pylons users. It was added as a porting convenience for Pylons users in - Pyramid 1.0, but it never caught on because the Pyramid rendering system is - a lot different than Pylons' was, and alternate ways exist to do what it - was designed to offer in Pylons. It will continue to exist "forever" but - it will not be recommended or mentioned in the docs. - -1.3a9 (2012-02-22) -================== +- Forward port from 1.3 branch: When no authentication policy was configured, + a call to ``pyramid.security.effective_principals`` would unconditionally + return the empty list. This was incorrect, it should have unconditionally + returned ``[Everyone]``, and now does. Features -------- -- Add an ``introspection`` boolean to the Configurator constructor. If this - is ``True``, actions registered using the Configurator will be registered - with the introspector. If it is ``False``, they won't. The default is - ``True``. Setting it to ``False`` during action processing will prevent - introspection for any following registration statements, and setting it to - ``True`` will start them up again. This addition is to service a - requirement that the debug toolbar's own views and methods not show up in - the introspector. - -- New API: ``pyramid.config.Configurator.add_notfound_view``. This is a - wrapper for ``pyramid.Config.configurator.add_view`` which provides easy - append_slash support and does the right thing about permissions. It should - be preferred over calling ``add_view`` directly with - ``context=HTTPNotFound`` as was previously recommended. - -- New API: ``pyramid.view.notfound_view_config``. This is a decorator - constructor like ``pyramid.view.view_config`` that calls - ``pyramid.config.Configurator.add_notfound_view`` when scanned. It should - be preferred over using ``pyramid.view.view_config`` with - ``context=HTTPNotFound`` as was previously recommended. - -- New API: ``pyramid.config.Configurator.add_forbidden_view``. This is a - wrapper for ``pyramid.Config.configurator.add_view`` which does the right - thing about permissions. It should be preferred over calling ``add_view`` - directly with ``context=HTTPForbidden`` as was previously recommended. - -- New API: ``pyramid.view.forbidden_view_config``. This is a decorator - constructor like ``pyramid.view.view_config`` that calls - ``pyramid.config.Configurator.add_forbidden_view`` when scanned. It should - be preferred over using ``pyramid.view.view_config`` with - ``context=HTTPForbidden`` as was previously recommended. - -- New APIs: ``pyramid.response.FileResponse`` and - ``pyramid.response.FileIter``, for usage in views that must serve files - "manually". - -Backwards Incompatibilities ---------------------------- - -- Remove ``pyramid.config.Configurator.with_context`` class method. It was - never an API, it is only used by ``pyramid_zcml`` and its functionality has - been moved to that package's latest release. This means that you'll need - to use the 0.9.2 or later release of ``pyramid_zcml`` with this release of - Pyramid. - -- The ``introspector`` argument to the ``pyramid.config.Configurator`` - constructor API has been removed. It has been replaced by the boolean - ``introspection`` flag. - -- The ``pyramid.registry.noop_introspector`` API object has been removed. - -- The older deprecated ``set_notfound_view`` Configurator method is now an - alias for the new ``add_notfound_view`` Configurator method. Likewise, the - older deprecated ``set_forbidden_view`` is now an alias for the new - ``add_forbidden_view``. This has the following impact: the ``context`` sent - to views with a ``(context, request)`` call signature registered via the - ``set_notfound_view`` or ``set_forbidden_view`` will now be an exception - object instead of the actual resource context found. Use - ``request.context`` to get the actual resource context. It's also - recommended to disuse ``set_notfound_view`` in favor of - ``add_notfound_view``, and disuse ``set_forbidden_view`` in favor of - ``add_forbidden_view`` despite the aliasing. - -Deprecations ------------- - -- The API documentation for ``pyramid.view.append_slash_notfound_view`` and - ``pyramid.view.AppendSlashNotFoundViewFactory`` was removed. These names - still exist and are still importable, but they are no longer APIs. Use - ``pyramid.config.Configurator.add_notfound_view(append_slash=True)`` or - ``pyramid.view.notfound_view_config(append_slash=True)`` to get the same - behavior. - -- The ``set_forbidden_view`` and ``set_notfound_view`` methods of the - Configurator were removed from the documentation. They have been - deprecated since Pyramid 1.1. - -Bug Fixes ---------- - -- The static file response object used by ``config.add_static_view`` opened - the static file twice, when it only needed to open it once. - -- The AppendSlashNotFoundViewFactory used request.path to match routes. This - was wrong because request.path contains the script name, and this would - cause it to fail in circumstances where the script name was not empty. It - should have used request.path_info, and now does. - -Documentation -------------- - -- Updated the "Creating a Not Found View" section of the "Hooks" chapter, - replacing explanations of registering a view using ``add_view`` or - ``view_config`` with ones using ``add_notfound_view`` or - ``notfound_view_config``. - -- Updated the "Creating a Not Forbidden View" section of the "Hooks" chapter, - replacing explanations of registering a view using ``add_view`` or - ``view_config`` with ones using ``add_forbidden_view`` or - ``forbidden_view_config``. - -- Updated the "Redirecting to Slash-Appended Routes" section of the "URL - Dispatch" chapter, replacing explanations of registering a view using - ``add_view`` or ``view_config`` with ones using ``add_notfound_view`` or - ``notfound_view_config`` - -- Updated all tutorials to use ``pyramid.view.forbidden_view_config`` rather - than ``pyramid.view.view_config`` with an HTTPForbidden context. - -1.3a8 (2012-02-19) -================== - -Features --------- - -- The ``scan`` method of a ``Configurator`` can be passed an ``ignore`` - argument, which can be a string, a callable, or a list consisting of - strings and/or callables. This feature allows submodules, subpackages, and - global objects from being scanned. See - http://readthedocs.org/docs/venusian/en/latest/#ignore-scan-argument for - more information about how to use the ``ignore`` argument to ``scan``. - -- Better error messages when a view callable returns a value that cannot be - converted to a response (for example, when a view callable returns a - dictionary without a renderer defined, or doesn't return any value at all). - The error message now contains information about the view callable itself - as well as the result of calling it. - -- Better error message when a .pyc-only module is ``config.include`` -ed. - This is not permitted due to error reporting requirements, and a better - error message is shown when it is attempted. Previously it would fail with - something like "AttributeError: 'NoneType' object has no attribute - 'rfind'". - -- Add ``pyramid.config.Configurator.add_traverser`` API method. See the - Hooks narrative documentation section entitled "Changing the Traverser" for - more information. This is not a new feature, it just provides an API for - adding a traverser without needing to use the ZCA API. - -- Add ``pyramid.config.Configurator.add_resource_url_adapter`` API method. - See the Hooks narrative documentation section entitled "Changing How - pyramid.request.Request.resource_url Generates a URL" for more information. - This is not a new feature, it just provides an API for adding a resource - url adapter without needing to use the ZCA API. - -- The system value ``req`` is now supplied to renderers as an alias for - ``request``. This means that you can now, for example, in a template, do - ``req.route_url(...)`` instead of ``request.route_url(...)``. This is - purely a change to reduce the amount of typing required to use request - methods and attributes from within templates. The value ``request`` is - still available too, this is just an alternative. - -- A new interface was added: ``pyramid.interfaces.IResourceURL``. An adapter - implementing its interface can be used to override resource URL generation - when ``request.resource_url`` is called. This interface replaces the - now-deprecated ``pyramid.interfaces.IContextURL`` interface. - -- The dictionary passed to a resource's ``__resource_url__`` method (see - "Overriding Resource URL Generation" in the "Resources" chapter) now - contains an ``app_url`` key, representing the application URL generated - during ``request.resource_url``. It represents a potentially customized - URL prefix, containing potentially custom scheme, host and port information - passed by the user to ``request.resource_url``. It should be used instead - of ``request.application_url`` where necessary. - -- The ``request.resource_url`` API now accepts these arguments: ``app_url``, - ``scheme``, ``host``, and ``port``. The app_url argument can be used to - replace the URL prefix wholesale during url generation. The ``scheme``, - ``host``, and ``port`` arguments can be used to replace the respective - default values of ``request.application_url`` partially. - -- A new API named ``request.resource_path`` now exists. It works like - ``request.resource_url`` but produces a relative URL rather than an - absolute one. - -- The ``request.route_url`` API now accepts these arguments: ``_app_url``, - ``_scheme``, ``_host``, and ``_port``. The ``_app_url`` argument can be - used to replace the URL prefix wholesale during url generation. The - ``_scheme``, ``_host``, and ``_port`` arguments can be used to replace the - respective default values of ``request.application_url`` partially. - -Backwards Incompatibilities ---------------------------- - -- The ``pyramid.interfaces.IContextURL`` interface has been deprecated. - People have been instructed to use this to register a resource url adapter - in the "Hooks" chapter to use to influence ``request.resource_url`` URL - generation for resources found via custom traversers since Pyramid 1.0. - - The interface still exists and registering such an adapter still works, but - this interface will be removed from the software after a few major Pyramid - releases. You should replace it with an equivalent - ``pyramid.interfaces.IResourceURL`` adapter, registered using the new - ``pyramid.config.Configurator.add_resource_url_adapter`` API. A - deprecation warning is now emitted when a - ``pyramid.interfaces.IContextURL`` adapter is found when - ``request.resource_url`` is called. - -Documentation -------------- - -- Don't create a ``session`` instance in SQLA Wiki tutorial, use raw - ``DBSession`` instead (this is more common in real SQLA apps). - -Scaffolding ------------ - -- Put ``pyramid.includes`` targets within ini files in scaffolds on separate - lines in order to be able to tell people to comment out only the - ``pyramid_debugtoolbar`` line when they want to disable the toolbar. - -Dependencies ------------- - -- Depend on ``venusian`` >= 1.0a3 to provide scan ``ignore`` support. - -Internal --------- - -- Create a "MakoRendererFactoryHelper" that provides customizable settings - key prefixes. Allows settings prefixes other than "mako." to be used to - create different factories that don't use the global mako settings. This - will be useful for the debug toolbar, which can currently be sabotaged by - someone using custom mako configuration settings. - -1.3a7 (2012-02-07) -================== - -Features --------- - -- More informative error message when a ``config.include`` cannot find an - ``includeme``. See https://github.com/Pylons/pyramid/pull/392. - -- Internal: catch unhashable discriminators early (raise an error instead of - allowing them to find their way into resolveConflicts). - -- The `match_param` view predicate now accepts a string or a tuple. - This replaces the broken behavior of accepting a dict. See - https://github.com/Pylons/pyramid/issues/425 for more information. - -Bug Fixes ---------- - -- The process will now restart when ``pserve`` is used with the ``--reload`` - flag when the ``development.ini`` file (or any other .ini file in use) is - changed. See https://github.com/Pylons/pyramid/issues/377 and - https://github.com/Pylons/pyramid/pull/411 - -- The ``prequest`` script would fail when used against URLs which did not - return HTML or text. See https://github.com/Pylons/pyramid/issues/381 - -Backwards Incompatibilities ---------------------------- - -- The `match_param` view predicate no longer accepts a dict. This will - have no negative affect because the implementation was broken for - dict-based arguments. - -Documentation -------------- - -- Add a traversal hello world example to the narrative docs. - -1.3a6 (2012-01-20) -================== - -Features --------- - -- New API: ``pyramid.config.Configurator.set_request_property``. Add lazy - property descriptors to a request without changing the request factory. - This method provides conflict detection and is the suggested way to add - properties to a request. - -- Responses generated by Pyramid's ``static_view`` now use - a ``wsgi.file_wrapper`` (see - http://www.python.org/dev/peps/pep-0333/#optional-platform-specific-file-handling) - when one is provided by the web server. - -Bug Fixes ---------- - -- Views registered with an ``accept`` could not be overridden correctly with - a different view that had the same predicate arguments. See - https://github.com/Pylons/pyramid/pull/404 for more information. - -- When using a dotted name for a ``view`` argument to - ``Configurator.add_view`` that pointed to a class with a ``view_defaults`` - decorator, the view defaults would not be applied. See - https://github.com/Pylons/pyramid/issues/396 . - -- Static URL paths were URL-quoted twice. See - https://github.com/Pylons/pyramid/issues/407 . - -1.3a5 (2012-01-09) -================== - -Bug Fixes ---------- - -- The ``pyramid.view.view_defaults`` decorator did not work properly when - more than one view relied on the defaults being different for configuration - conflict resolution. See https://github.com/Pylons/pyramid/issues/394. - -Backwards Incompatibilities ---------------------------- - -- The ``path_info`` route and view predicates now match against - ``request.upath_info`` (Unicode) rather than ``request.path_info`` - (indeterminate value based on Python 3 vs. Python 2). This has to be done - to normalize matching on Python 2 and Python 3. - -1.3a4 (2012-01-05) -================== - -Features --------- - -- New API: ``pyramid.request.Request.set_property``. Add lazy property - descriptors to a request without changing the request factory. New - properties may be reified, effectively caching the value for the lifetime - of the instance. Common use-cases for this would be to get a database - connection for the request or identify the current user. - -- Use the ``waitress`` WSGI server instead of ``wsgiref`` in scaffolding. - -Bug Fixes ---------- - -- The documentation of ``pyramid.events.subscriber`` indicated that using it - as a decorator with no arguments like this:: - - @subscriber() - def somefunc(event): - pass - - Would register ``somefunc`` to receive all events sent via the registry, - but this was untrue. Instead, it would receive no events at all. This has - now been fixed and the code matches the documentation. See also - https://github.com/Pylons/pyramid/issues/386 - -- Literal portions of route patterns were not URL-quoted when ``route_url`` - or ``route_path`` was used to generate a URL or path. - -- The result of ``route_path`` or ``route_url`` might have been ``unicode`` - or ``str`` depending on the input. It is now guaranteed to always be - ``str``. - -- URL matching when the pattern contained non-ASCII characters in literal - parts was indeterminate. Now the pattern supplied to ``add_route`` is - assumed to be either: a ``unicode`` value, or a ``str`` value that contains - only ASCII characters. If you now want to match the path info from a URL - that contains high order characters, you can pass the Unicode - representation of the decoded path portion in the pattern. - -- When using a ``traverse=`` route predicate, traversal would fail with a - URLDecodeError if there were any high-order characters in the traversal - pattern or in the matched dynamic segments. - -- Using a dynamic segment named ``traverse`` in a route pattern like this:: - - config.add_route('trav_route', 'traversal/{traverse:.*}') - - Would cause a ``UnicodeDecodeError`` when the route was matched and the - matched portion of the URL contained any high-order characters. See - https://github.com/Pylons/pyramid/issues/385 . - -- When using a ``*traverse`` stararg in a route pattern, a URL that matched - that possessed a ``@@`` in its name (signifying a view name) would be - inappropriately quoted by the traversal machinery during traversal, - resulting in the view not being found properly. See - https://github.com/Pylons/pyramid/issues/382 and - https://github.com/Pylons/pyramid/issues/375 . - -Backwards Incompatibilities ---------------------------- - -- String values passed to ``route_url`` or ``route_path`` that are meant to - replace "remainder" matches will now be URL-quoted except for embedded - slashes. For example:: - - config.add_route('remain', '/foo*remainder') - request.route_path('remain', remainder='abc / def') - # -> '/foo/abc%20/%20def' - - Previously string values passed as remainder replacements were tacked on - untouched, without any URL-quoting. But this doesn't really work logically - if the value passed is Unicode (raw unicode cannot be placed in a URL or in - a path) and it is inconsistent with the rest of the URL generation - machinery if the value is a string (it won't be quoted unless by the - caller). - - Some folks will have been relying on the older behavior to tack on query - string elements and anchor portions of the URL; sorry, you'll need to - change your code to use the ``_query`` and/or ``_anchor`` arguments to - ``route_path`` or ``route_url`` to do this now. - -- If you pass a bytestring that contains non-ASCII characters to - ``add_route`` as a pattern, it will now fail at startup time. Use Unicode - instead. - -1.3a3 (2011-12-21) -================== - -Features --------- - -- Added a ``prequest`` script (along the lines of ``paster request``). It is - documented in the "Command-Line Pyramid" chapter in the section entitled - "Invoking a Request". - -- Add undocumented ``__discriminator__`` API to derived view callables. - e.g. ``adapters.lookup(...).__discriminator__(context, request)``. It will - be used by superdynamic systems that require the discriminator to be used - for introspection after manual view lookup. - -Bug Fixes ---------- - -- Normalized exit values and ``-h`` output for all ``p*`` scripts - (``pviews``, ``proutes``, etc). - -Documentation -------------- - -- Added a section named "Making Your Script into a Console Script" in the - "Command-Line Pyramid" chapter. - -- Removed the "Running Pyramid on Google App Engine" tutorial from the main - docs. It survives on in the Cookbook - (http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/gae.html). - Rationale: it provides the correct info for the Python 2.5 version of GAE - only, and this version of Pyramid does not support Python 2.5. - -1.3a2 (2011-12-14) -================== - -Features --------- - -- New API: ``pyramid.view.view_defaults``. If you use a class as a view, you - can use the new ``view_defaults`` class decorator on the class to provide - defaults to the view configuration information used by every - ``@view_config`` decorator that decorates a method of that class. It also - works against view configurations involving a class made imperatively. - -- Added a backwards compatibility knob to ``pcreate`` to emulate ``paster - create`` handling for the ``--list-templates`` option. - -- Changed scaffolding machinery around a bit to make it easier for people who - want to have extension scaffolds that can work across Pyramid 1.0.X, 1.1.X, - 1.2.X and 1.3.X. See the new "Creating Pyramid Scaffolds" chapter in the - narrative documentation for more info. - -Documentation -------------- - -- Added documentation to "View Configuration" narrative documentation chapter - about ``view_defaults`` class decorator. - -- Added API docs for ``view_defaults`` class decorator. - -- Added an API docs chapter for ``pyramid.scaffolds``. - -- Added a narrative docs chapter named "Creating Pyramid Scaffolds". - -Backwards Incompatibilities ---------------------------- - -- The ``template_renderer`` method of ``pyramid.scaffolds.PyramidScaffold`` - was renamed to ``render_template``. If you were overriding it, you're a - bad person, because it wasn't an API before now. But we're nice so we're - letting you know. - -1.3a1 (2011-12-09) -================== - -Features --------- - -- Python 3.2 compatibility. - -- New ``pyramid.compat`` module and API documentation which provides Python - 2/3 straddling support for Pyramid add-ons and development environments. - -- A ``mako.directories`` setting is no longer required to use Mako templates - Rationale: Mako template renderers can be specified using an absolute asset - spec. An entire application can be written with such asset specs, - requiring no ordered lookup path. - -- ``bpython`` interpreter compatibility in ``pshell``. See the "Command-Line - Pyramid" narrative docs chapter for more information. - -- Added ``get_appsettings`` API function to the ``pyramid.paster`` module. - This function returns the settings defined within an ``[app:...]`` section - in a PasteDeploy ini file. - -- Added ``setup_logging`` API function to the ``pyramid.paster`` module. - This function sets up Python logging according to the logging configuration - in a PasteDeploy ini file. - -- Configuration conflict reporting is reported in a more understandable way - ("Line 11 in file..." vs. a repr of a tuple of similar info). - -- A configuration introspection system was added; see the narrative - documentation chapter entitled "Pyramid Configuration Introspection" for - more information. New APIs: ``pyramid.registry.Introspectable``, - ``pyramid.config.Configurator.introspector``, - ``pyramid.config.Configurator.introspectable``, - ``pyramid.registry.Registry.introspector``. - -- Allow extra keyword arguments to be passed to the - ``pyramid.config.Configurator.action`` method. - -- New APIs: ``pyramid.path.AssetResolver`` and - ``pyramid.path.DottedNameResolver``. The former can be used to resolve - asset specifications, the latter can be used to resolve dotted names to - modules or packages. - -Bug Fixes ---------- - -- Make test suite pass on 32-bit systems; closes #286. closes #306. - See also https://github.com/Pylons/pyramid/issues/286 - -- The ``pryamid.view.view_config`` decorator did not accept a ``match_params`` - predicate argument. See https://github.com/Pylons/pyramid/pull/308 - -- The AuthTktCookieHelper could potentially generate Unicode headers - inappropriately when the ``tokens`` argument to remember was used. See - https://github.com/Pylons/pyramid/pull/314. - -- The AuthTktAuthenticationPolicy did not use a timing-attack-aware string - comparator. See https://github.com/Pylons/pyramid/pull/320 for more info. - -- The DummySession in ``pyramid.testing`` now generates a new CSRF token if - one doesn't yet exist. - -- ``request.static_url`` now generates URL-quoted URLs when fed a ``path`` - argument which contains characters that are unsuitable for URLs. See - https://github.com/Pylons/pyramid/issues/349 for more info. - -- Prevent a scaffold rendering from being named ``site`` (conflicts with - Python internal site.py). - -- Support for using instances as targets of the ``pyramid.wsgi.wsgiapp`` and - ``pryramid.wsgi.wsgiapp2`` functions. - See https://github.com/Pylons/pyramid/pull/370 for more info. - -Backwards Incompatibilities ---------------------------- - -- Pyramid no longer runs on Python 2.5 (which includes the most recent - release of Jython and the Python 2.5 version of GAE as of this writing). - -- The ``paster`` command is no longer the documented way to create projects, - start the server, or run debugging commands. To create projects from - scaffolds, ``paster create`` is replaced by the ``pcreate`` console script. - To serve up a project, ``paster serve`` is replaced by the ``pserve`` - console script. New console scripts named ``pshell``, ``pviews``, - ``proutes``, and ``ptweens`` do what their ``paster <commandname>`` - equivalents used to do. Rationale: the Paste and PasteScript packages do - not run under Python 3. - -- The default WSGI server run as the result of ``pserve`` from newly rendered - scaffolding is now the ``wsgiref`` WSGI server instead of the - ``paste.httpserver`` server. Rationale: Rationale: the Paste and - PasteScript packages do not run under Python 3. - -- The ``pshell`` command (see "paster pshell") no longer accepts a - ``--disable-ipython`` command-line argument. Instead, it accepts a ``-p`` - or ``--python-shell`` argument, which can be any of the values ``python``, - ``ipython`` or ``bpython``. - -- Removed the ``pyramid.renderers.renderer_from_name`` function. It has been - deprecated since Pyramid 1.0, and was never an API. - -- To use ZCML with versions of Pyramid >= 1.3, you will need ``pyramid_zcml`` - version >= 0.8 and ``zope.configuration`` version >= 3.8.0. The - ``pyramid_zcml`` package version 0.8 is backwards compatible all the way to - Pyramid 1.0, so you won't be warned if you have older versions installed - and upgrade Pyramid "in-place"; it may simply break instead. - -Dependencies ------------- - -- Pyramid no longer depends on the ``zope.component`` package, except as a - testing dependency. - -- Pyramid now depends on a zope.interface>=3.8.0, WebOb>=1.2dev, - repoze.lru>=0.4, zope.deprecation>=3.5.0, translationstring>=0.4 (for - Python 3 compatibility purposes). It also, as a testing dependency, - depends on WebTest>=1.3.1 for the same reason. - -- Pyramid no longer depends on the Paste or PasteScript packages. - -Documentation -------------- - -- The SQLAlchemy Wiki tutorial has been updated. It now uses - ``@view_config`` decorators and an explicit database population script. - -- Minor updates to the ZODB Wiki tutorial. - -- A narrative documentation chapter named "Extending Pyramid Configuration" - was added; it describes how to add a new directive, and how use the - ``pyramid.config.Configurator.action`` method within custom directives. It - also describes how to add introspectable objects. - -- A narrative documentation chapter named "Pyramid Configuration - Introspection" was added. It describes how to query the introspection - system. - -Scaffolds ---------- - -- Rendered scaffolds have now been changed to be more relocatable (fewer - mentions of the package name within files in the package). - -- The ``routesalchemy`` scaffold has been renamed ``alchemy``, replacing the - older (traversal-based) ``alchemy`` scaffold (which has been retired). - -- The ``starter`` scaffold now uses URL dispatch by default. +- Custom objects can be made easily JSON-serializable in Pyramid by defining + a ``__json__`` method on the object's class. This method should return + values natively serializable by ``json.dumps`` (such as ints, lists, + dictionaries, strings, and so forth). +- As of this release, the ``request_method`` predicate, when used, will also + imply that ``HEAD`` is implied when you use ``GET``. For example, using + ``@view_config(request_method='GET')`` is equivalent to using + ``@view_config(request_method='HEAD')``. Using + ``@view_config(request_method=('GET', 'POST')`` is equivalent to using + ``@view_config(request_method=('GET', 'HEAD', 'POST')``. This is because + HEAD is a variant of GET that omits the body, and WebOb has special support + to return an empty body when a HEAD is used. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index a402d49e6..365e3e455 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -166,3 +166,7 @@ Contributors - Paul M. Winkler, 2012/02/22 - Martijn Pieters, 2012/03/02 + +- Steve Piercy, 2012/03/27 + +- Wayne Witzel III, 2012/03/27 diff --git a/HISTORY.txt b/HISTORY.txt index f6cf8fa87..f10cfa3ab 100644 --- a/HISTORY.txt +++ b/HISTORY.txt @@ -1,3 +1,736 @@ +1.3 (2012-03-21) +================ + +Bug Fixes +--------- + +- When ``pyramid.wsgi.wsgiapp2`` calls the downstream WSGI app, the app's + environ will no longer have (deprecated and potentially misleading) + ``bfg.routes.matchdict`` or ``bfg.routes.route`` keys in it. A symptom of + this bug would be a ``wsgiapp2``-wrapped Pyramid app finding the wrong view + because it mistakenly detects that a route was matched when, in fact, it + was not. + +- The fix for issue https://github.com/Pylons/pyramid/issues/461 (which made + it possible for instance methods to be used as view callables) introduced a + backwards incompatibility when methods that declared only a request + argument were used. See https://github.com/Pylons/pyramid/issues/503 + +1.3b3 (2012-03-17) +================== + +Bug Fixes +--------- + +- ``config.add_view(<aninstancemethod>)`` raised AttributeError involving + ``__text__``. See https://github.com/Pylons/pyramid/issues/461 + +- Remove references to do-nothing ``pyramid.debug_templates`` setting in all + Pyramid-provided ``.ini`` files. This setting previously told Chameleon to + render better exceptions; now Chameleon always renders nice exceptions + regardless of the value of this setting. + +Scaffolds +--------- + +- The ``alchemy`` scaffold now shows an informative error message in the + browser if the person creating the project forgets to run the + initialization script. + +- The ``alchemy`` scaffold initialization script is now called + ``initialize_<projectname>_db`` instead of ``populate_<projectname>``. + +Documentation +------------- + +- Wiki tutorials improved due to collaboration at PyCon US 2012 sprints. + +1.3b2 (2012-03-02) +================== + +Bug Fixes +--------- + +- The method ``pyramid.request.Request.partial_application_url`` is no longer + in the API docs. It was meant to be a private method; its publication in + the documentation as an API method was a mistake, and it has been renamed + to something private. + +- When a static view was registered using an absolute filesystem path on + Windows, the ``request.static_url`` function did not work to generate URLs + to its resources. Symptom: "No static URL definition matching + c:\\foo\\bar\\baz". + +- Make all tests pass on Windows XP. + +- Bug in ACL authentication checking on Python 3: the ``permits`` and + ``principals_allowed_by_permission`` method of + ``pyramid.authorization.ACLAuthenticationPolicy`` could return an + inappropriate ``True`` value when a permission on an ACL was a string + rather than a sequence, and then only if the ACL permission string was a + substring of the ``permission`` value passed to the function. + + This bug effects no Pyramid deployment under Python 2; it is a bug that + exists only in deployments running on Python 3. It has existed since + Pyramid 1.3a1. + + This bug was due to the presence of an ``__iter__`` attribute on strings + under Python 3 which is not present under strings in Python 2. + +1.3b1 (2012-02-26) +================== + +Bug Fixes +--------- + +- ``pyramid.config.Configurator.with_package`` didn't work if the + Configurator was an old-style ``pyramid.configuration.Configurator`` + instance. + +- Pyramid authorization policies did not show up in the introspector. + +Deprecations +------------ + +- All references to the ``tmpl_context`` request variable were removed from + the docs. Its existence in Pyramid is confusing for people who were never + Pylons users. It was added as a porting convenience for Pylons users in + Pyramid 1.0, but it never caught on because the Pyramid rendering system is + a lot different than Pylons' was, and alternate ways exist to do what it + was designed to offer in Pylons. It will continue to exist "forever" but + it will not be recommended or mentioned in the docs. + +1.3a9 (2012-02-22) +================== + +Features +-------- + +- Add an ``introspection`` boolean to the Configurator constructor. If this + is ``True``, actions registered using the Configurator will be registered + with the introspector. If it is ``False``, they won't. The default is + ``True``. Setting it to ``False`` during action processing will prevent + introspection for any following registration statements, and setting it to + ``True`` will start them up again. This addition is to service a + requirement that the debug toolbar's own views and methods not show up in + the introspector. + +- New API: ``pyramid.config.Configurator.add_notfound_view``. This is a + wrapper for ``pyramid.Config.configurator.add_view`` which provides easy + append_slash support and does the right thing about permissions. It should + be preferred over calling ``add_view`` directly with + ``context=HTTPNotFound`` as was previously recommended. + +- New API: ``pyramid.view.notfound_view_config``. This is a decorator + constructor like ``pyramid.view.view_config`` that calls + ``pyramid.config.Configurator.add_notfound_view`` when scanned. It should + be preferred over using ``pyramid.view.view_config`` with + ``context=HTTPNotFound`` as was previously recommended. + +- New API: ``pyramid.config.Configurator.add_forbidden_view``. This is a + wrapper for ``pyramid.Config.configurator.add_view`` which does the right + thing about permissions. It should be preferred over calling ``add_view`` + directly with ``context=HTTPForbidden`` as was previously recommended. + +- New API: ``pyramid.view.forbidden_view_config``. This is a decorator + constructor like ``pyramid.view.view_config`` that calls + ``pyramid.config.Configurator.add_forbidden_view`` when scanned. It should + be preferred over using ``pyramid.view.view_config`` with + ``context=HTTPForbidden`` as was previously recommended. + +- New APIs: ``pyramid.response.FileResponse`` and + ``pyramid.response.FileIter``, for usage in views that must serve files + "manually". + +Backwards Incompatibilities +--------------------------- + +- Remove ``pyramid.config.Configurator.with_context`` class method. It was + never an API, it is only used by ``pyramid_zcml`` and its functionality has + been moved to that package's latest release. This means that you'll need + to use the 0.9.2 or later release of ``pyramid_zcml`` with this release of + Pyramid. + +- The ``introspector`` argument to the ``pyramid.config.Configurator`` + constructor API has been removed. It has been replaced by the boolean + ``introspection`` flag. + +- The ``pyramid.registry.noop_introspector`` API object has been removed. + +- The older deprecated ``set_notfound_view`` Configurator method is now an + alias for the new ``add_notfound_view`` Configurator method. Likewise, the + older deprecated ``set_forbidden_view`` is now an alias for the new + ``add_forbidden_view``. This has the following impact: the ``context`` sent + to views with a ``(context, request)`` call signature registered via the + ``set_notfound_view`` or ``set_forbidden_view`` will now be an exception + object instead of the actual resource context found. Use + ``request.context`` to get the actual resource context. It's also + recommended to disuse ``set_notfound_view`` in favor of + ``add_notfound_view``, and disuse ``set_forbidden_view`` in favor of + ``add_forbidden_view`` despite the aliasing. + +Deprecations +------------ + +- The API documentation for ``pyramid.view.append_slash_notfound_view`` and + ``pyramid.view.AppendSlashNotFoundViewFactory`` was removed. These names + still exist and are still importable, but they are no longer APIs. Use + ``pyramid.config.Configurator.add_notfound_view(append_slash=True)`` or + ``pyramid.view.notfound_view_config(append_slash=True)`` to get the same + behavior. + +- The ``set_forbidden_view`` and ``set_notfound_view`` methods of the + Configurator were removed from the documentation. They have been + deprecated since Pyramid 1.1. + +Bug Fixes +--------- + +- The static file response object used by ``config.add_static_view`` opened + the static file twice, when it only needed to open it once. + +- The AppendSlashNotFoundViewFactory used request.path to match routes. This + was wrong because request.path contains the script name, and this would + cause it to fail in circumstances where the script name was not empty. It + should have used request.path_info, and now does. + +Documentation +------------- + +- Updated the "Creating a Not Found View" section of the "Hooks" chapter, + replacing explanations of registering a view using ``add_view`` or + ``view_config`` with ones using ``add_notfound_view`` or + ``notfound_view_config``. + +- Updated the "Creating a Not Forbidden View" section of the "Hooks" chapter, + replacing explanations of registering a view using ``add_view`` or + ``view_config`` with ones using ``add_forbidden_view`` or + ``forbidden_view_config``. + +- Updated the "Redirecting to Slash-Appended Routes" section of the "URL + Dispatch" chapter, replacing explanations of registering a view using + ``add_view`` or ``view_config`` with ones using ``add_notfound_view`` or + ``notfound_view_config`` + +- Updated all tutorials to use ``pyramid.view.forbidden_view_config`` rather + than ``pyramid.view.view_config`` with an HTTPForbidden context. + +1.3a8 (2012-02-19) +================== + +Features +-------- + +- The ``scan`` method of a ``Configurator`` can be passed an ``ignore`` + argument, which can be a string, a callable, or a list consisting of + strings and/or callables. This feature allows submodules, subpackages, and + global objects from being scanned. See + http://readthedocs.org/docs/venusian/en/latest/#ignore-scan-argument for + more information about how to use the ``ignore`` argument to ``scan``. + +- Better error messages when a view callable returns a value that cannot be + converted to a response (for example, when a view callable returns a + dictionary without a renderer defined, or doesn't return any value at all). + The error message now contains information about the view callable itself + as well as the result of calling it. + +- Better error message when a .pyc-only module is ``config.include`` -ed. + This is not permitted due to error reporting requirements, and a better + error message is shown when it is attempted. Previously it would fail with + something like "AttributeError: 'NoneType' object has no attribute + 'rfind'". + +- Add ``pyramid.config.Configurator.add_traverser`` API method. See the + Hooks narrative documentation section entitled "Changing the Traverser" for + more information. This is not a new feature, it just provides an API for + adding a traverser without needing to use the ZCA API. + +- Add ``pyramid.config.Configurator.add_resource_url_adapter`` API method. + See the Hooks narrative documentation section entitled "Changing How + pyramid.request.Request.resource_url Generates a URL" for more information. + This is not a new feature, it just provides an API for adding a resource + url adapter without needing to use the ZCA API. + +- The system value ``req`` is now supplied to renderers as an alias for + ``request``. This means that you can now, for example, in a template, do + ``req.route_url(...)`` instead of ``request.route_url(...)``. This is + purely a change to reduce the amount of typing required to use request + methods and attributes from within templates. The value ``request`` is + still available too, this is just an alternative. + +- A new interface was added: ``pyramid.interfaces.IResourceURL``. An adapter + implementing its interface can be used to override resource URL generation + when ``request.resource_url`` is called. This interface replaces the + now-deprecated ``pyramid.interfaces.IContextURL`` interface. + +- The dictionary passed to a resource's ``__resource_url__`` method (see + "Overriding Resource URL Generation" in the "Resources" chapter) now + contains an ``app_url`` key, representing the application URL generated + during ``request.resource_url``. It represents a potentially customized + URL prefix, containing potentially custom scheme, host and port information + passed by the user to ``request.resource_url``. It should be used instead + of ``request.application_url`` where necessary. + +- The ``request.resource_url`` API now accepts these arguments: ``app_url``, + ``scheme``, ``host``, and ``port``. The app_url argument can be used to + replace the URL prefix wholesale during url generation. The ``scheme``, + ``host``, and ``port`` arguments can be used to replace the respective + default values of ``request.application_url`` partially. + +- A new API named ``request.resource_path`` now exists. It works like + ``request.resource_url`` but produces a relative URL rather than an + absolute one. + +- The ``request.route_url`` API now accepts these arguments: ``_app_url``, + ``_scheme``, ``_host``, and ``_port``. The ``_app_url`` argument can be + used to replace the URL prefix wholesale during url generation. The + ``_scheme``, ``_host``, and ``_port`` arguments can be used to replace the + respective default values of ``request.application_url`` partially. + +Backwards Incompatibilities +--------------------------- + +- The ``pyramid.interfaces.IContextURL`` interface has been deprecated. + People have been instructed to use this to register a resource url adapter + in the "Hooks" chapter to use to influence ``request.resource_url`` URL + generation for resources found via custom traversers since Pyramid 1.0. + + The interface still exists and registering such an adapter still works, but + this interface will be removed from the software after a few major Pyramid + releases. You should replace it with an equivalent + ``pyramid.interfaces.IResourceURL`` adapter, registered using the new + ``pyramid.config.Configurator.add_resource_url_adapter`` API. A + deprecation warning is now emitted when a + ``pyramid.interfaces.IContextURL`` adapter is found when + ``request.resource_url`` is called. + +Documentation +------------- + +- Don't create a ``session`` instance in SQLA Wiki tutorial, use raw + ``DBSession`` instead (this is more common in real SQLA apps). + +Scaffolding +----------- + +- Put ``pyramid.includes`` targets within ini files in scaffolds on separate + lines in order to be able to tell people to comment out only the + ``pyramid_debugtoolbar`` line when they want to disable the toolbar. + +Dependencies +------------ + +- Depend on ``venusian`` >= 1.0a3 to provide scan ``ignore`` support. + +Internal +-------- + +- Create a "MakoRendererFactoryHelper" that provides customizable settings + key prefixes. Allows settings prefixes other than "mako." to be used to + create different factories that don't use the global mako settings. This + will be useful for the debug toolbar, which can currently be sabotaged by + someone using custom mako configuration settings. + +1.3a7 (2012-02-07) +================== + +Features +-------- + +- More informative error message when a ``config.include`` cannot find an + ``includeme``. See https://github.com/Pylons/pyramid/pull/392. + +- Internal: catch unhashable discriminators early (raise an error instead of + allowing them to find their way into resolveConflicts). + +- The `match_param` view predicate now accepts a string or a tuple. + This replaces the broken behavior of accepting a dict. See + https://github.com/Pylons/pyramid/issues/425 for more information. + +Bug Fixes +--------- + +- The process will now restart when ``pserve`` is used with the ``--reload`` + flag when the ``development.ini`` file (or any other .ini file in use) is + changed. See https://github.com/Pylons/pyramid/issues/377 and + https://github.com/Pylons/pyramid/pull/411 + +- The ``prequest`` script would fail when used against URLs which did not + return HTML or text. See https://github.com/Pylons/pyramid/issues/381 + +Backwards Incompatibilities +--------------------------- + +- The `match_param` view predicate no longer accepts a dict. This will + have no negative affect because the implementation was broken for + dict-based arguments. + +Documentation +------------- + +- Add a traversal hello world example to the narrative docs. + +1.3a6 (2012-01-20) +================== + +Features +-------- + +- New API: ``pyramid.config.Configurator.set_request_property``. Add lazy + property descriptors to a request without changing the request factory. + This method provides conflict detection and is the suggested way to add + properties to a request. + +- Responses generated by Pyramid's ``static_view`` now use + a ``wsgi.file_wrapper`` (see + http://www.python.org/dev/peps/pep-0333/#optional-platform-specific-file-handling) + when one is provided by the web server. + +Bug Fixes +--------- + +- Views registered with an ``accept`` could not be overridden correctly with + a different view that had the same predicate arguments. See + https://github.com/Pylons/pyramid/pull/404 for more information. + +- When using a dotted name for a ``view`` argument to + ``Configurator.add_view`` that pointed to a class with a ``view_defaults`` + decorator, the view defaults would not be applied. See + https://github.com/Pylons/pyramid/issues/396 . + +- Static URL paths were URL-quoted twice. See + https://github.com/Pylons/pyramid/issues/407 . + +1.3a5 (2012-01-09) +================== + +Bug Fixes +--------- + +- The ``pyramid.view.view_defaults`` decorator did not work properly when + more than one view relied on the defaults being different for configuration + conflict resolution. See https://github.com/Pylons/pyramid/issues/394. + +Backwards Incompatibilities +--------------------------- + +- The ``path_info`` route and view predicates now match against + ``request.upath_info`` (Unicode) rather than ``request.path_info`` + (indeterminate value based on Python 3 vs. Python 2). This has to be done + to normalize matching on Python 2 and Python 3. + +1.3a4 (2012-01-05) +================== + +Features +-------- + +- New API: ``pyramid.request.Request.set_property``. Add lazy property + descriptors to a request without changing the request factory. New + properties may be reified, effectively caching the value for the lifetime + of the instance. Common use-cases for this would be to get a database + connection for the request or identify the current user. + +- Use the ``waitress`` WSGI server instead of ``wsgiref`` in scaffolding. + +Bug Fixes +--------- + +- The documentation of ``pyramid.events.subscriber`` indicated that using it + as a decorator with no arguments like this:: + + @subscriber() + def somefunc(event): + pass + + Would register ``somefunc`` to receive all events sent via the registry, + but this was untrue. Instead, it would receive no events at all. This has + now been fixed and the code matches the documentation. See also + https://github.com/Pylons/pyramid/issues/386 + +- Literal portions of route patterns were not URL-quoted when ``route_url`` + or ``route_path`` was used to generate a URL or path. + +- The result of ``route_path`` or ``route_url`` might have been ``unicode`` + or ``str`` depending on the input. It is now guaranteed to always be + ``str``. + +- URL matching when the pattern contained non-ASCII characters in literal + parts was indeterminate. Now the pattern supplied to ``add_route`` is + assumed to be either: a ``unicode`` value, or a ``str`` value that contains + only ASCII characters. If you now want to match the path info from a URL + that contains high order characters, you can pass the Unicode + representation of the decoded path portion in the pattern. + +- When using a ``traverse=`` route predicate, traversal would fail with a + URLDecodeError if there were any high-order characters in the traversal + pattern or in the matched dynamic segments. + +- Using a dynamic segment named ``traverse`` in a route pattern like this:: + + config.add_route('trav_route', 'traversal/{traverse:.*}') + + Would cause a ``UnicodeDecodeError`` when the route was matched and the + matched portion of the URL contained any high-order characters. See + https://github.com/Pylons/pyramid/issues/385 . + +- When using a ``*traverse`` stararg in a route pattern, a URL that matched + that possessed a ``@@`` in its name (signifying a view name) would be + inappropriately quoted by the traversal machinery during traversal, + resulting in the view not being found properly. See + https://github.com/Pylons/pyramid/issues/382 and + https://github.com/Pylons/pyramid/issues/375 . + +Backwards Incompatibilities +--------------------------- + +- String values passed to ``route_url`` or ``route_path`` that are meant to + replace "remainder" matches will now be URL-quoted except for embedded + slashes. For example:: + + config.add_route('remain', '/foo*remainder') + request.route_path('remain', remainder='abc / def') + # -> '/foo/abc%20/%20def' + + Previously string values passed as remainder replacements were tacked on + untouched, without any URL-quoting. But this doesn't really work logically + if the value passed is Unicode (raw unicode cannot be placed in a URL or in + a path) and it is inconsistent with the rest of the URL generation + machinery if the value is a string (it won't be quoted unless by the + caller). + + Some folks will have been relying on the older behavior to tack on query + string elements and anchor portions of the URL; sorry, you'll need to + change your code to use the ``_query`` and/or ``_anchor`` arguments to + ``route_path`` or ``route_url`` to do this now. + +- If you pass a bytestring that contains non-ASCII characters to + ``add_route`` as a pattern, it will now fail at startup time. Use Unicode + instead. + +1.3a3 (2011-12-21) +================== + +Features +-------- + +- Added a ``prequest`` script (along the lines of ``paster request``). It is + documented in the "Command-Line Pyramid" chapter in the section entitled + "Invoking a Request". + +- Add undocumented ``__discriminator__`` API to derived view callables. + e.g. ``adapters.lookup(...).__discriminator__(context, request)``. It will + be used by superdynamic systems that require the discriminator to be used + for introspection after manual view lookup. + +Bug Fixes +--------- + +- Normalized exit values and ``-h`` output for all ``p*`` scripts + (``pviews``, ``proutes``, etc). + +Documentation +------------- + +- Added a section named "Making Your Script into a Console Script" in the + "Command-Line Pyramid" chapter. + +- Removed the "Running Pyramid on Google App Engine" tutorial from the main + docs. It survives on in the Cookbook + (http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/gae.html). + Rationale: it provides the correct info for the Python 2.5 version of GAE + only, and this version of Pyramid does not support Python 2.5. + +1.3a2 (2011-12-14) +================== + +Features +-------- + +- New API: ``pyramid.view.view_defaults``. If you use a class as a view, you + can use the new ``view_defaults`` class decorator on the class to provide + defaults to the view configuration information used by every + ``@view_config`` decorator that decorates a method of that class. It also + works against view configurations involving a class made imperatively. + +- Added a backwards compatibility knob to ``pcreate`` to emulate ``paster + create`` handling for the ``--list-templates`` option. + +- Changed scaffolding machinery around a bit to make it easier for people who + want to have extension scaffolds that can work across Pyramid 1.0.X, 1.1.X, + 1.2.X and 1.3.X. See the new "Creating Pyramid Scaffolds" chapter in the + narrative documentation for more info. + +Documentation +------------- + +- Added documentation to "View Configuration" narrative documentation chapter + about ``view_defaults`` class decorator. + +- Added API docs for ``view_defaults`` class decorator. + +- Added an API docs chapter for ``pyramid.scaffolds``. + +- Added a narrative docs chapter named "Creating Pyramid Scaffolds". + +Backwards Incompatibilities +--------------------------- + +- The ``template_renderer`` method of ``pyramid.scaffolds.PyramidScaffold`` + was renamed to ``render_template``. If you were overriding it, you're a + bad person, because it wasn't an API before now. But we're nice so we're + letting you know. + +1.3a1 (2011-12-09) +================== + +Features +-------- + +- Python 3.2 compatibility. + +- New ``pyramid.compat`` module and API documentation which provides Python + 2/3 straddling support for Pyramid add-ons and development environments. + +- A ``mako.directories`` setting is no longer required to use Mako templates + Rationale: Mako template renderers can be specified using an absolute asset + spec. An entire application can be written with such asset specs, + requiring no ordered lookup path. + +- ``bpython`` interpreter compatibility in ``pshell``. See the "Command-Line + Pyramid" narrative docs chapter for more information. + +- Added ``get_appsettings`` API function to the ``pyramid.paster`` module. + This function returns the settings defined within an ``[app:...]`` section + in a PasteDeploy ini file. + +- Added ``setup_logging`` API function to the ``pyramid.paster`` module. + This function sets up Python logging according to the logging configuration + in a PasteDeploy ini file. + +- Configuration conflict reporting is reported in a more understandable way + ("Line 11 in file..." vs. a repr of a tuple of similar info). + +- A configuration introspection system was added; see the narrative + documentation chapter entitled "Pyramid Configuration Introspection" for + more information. New APIs: ``pyramid.registry.Introspectable``, + ``pyramid.config.Configurator.introspector``, + ``pyramid.config.Configurator.introspectable``, + ``pyramid.registry.Registry.introspector``. + +- Allow extra keyword arguments to be passed to the + ``pyramid.config.Configurator.action`` method. + +- New APIs: ``pyramid.path.AssetResolver`` and + ``pyramid.path.DottedNameResolver``. The former can be used to resolve + asset specifications, the latter can be used to resolve dotted names to + modules or packages. + +Bug Fixes +--------- + +- Make test suite pass on 32-bit systems; closes #286. closes #306. + See also https://github.com/Pylons/pyramid/issues/286 + +- The ``pryamid.view.view_config`` decorator did not accept a ``match_params`` + predicate argument. See https://github.com/Pylons/pyramid/pull/308 + +- The AuthTktCookieHelper could potentially generate Unicode headers + inappropriately when the ``tokens`` argument to remember was used. See + https://github.com/Pylons/pyramid/pull/314. + +- The AuthTktAuthenticationPolicy did not use a timing-attack-aware string + comparator. See https://github.com/Pylons/pyramid/pull/320 for more info. + +- The DummySession in ``pyramid.testing`` now generates a new CSRF token if + one doesn't yet exist. + +- ``request.static_url`` now generates URL-quoted URLs when fed a ``path`` + argument which contains characters that are unsuitable for URLs. See + https://github.com/Pylons/pyramid/issues/349 for more info. + +- Prevent a scaffold rendering from being named ``site`` (conflicts with + Python internal site.py). + +- Support for using instances as targets of the ``pyramid.wsgi.wsgiapp`` and + ``pryramid.wsgi.wsgiapp2`` functions. + See https://github.com/Pylons/pyramid/pull/370 for more info. + +Backwards Incompatibilities +--------------------------- + +- Pyramid no longer runs on Python 2.5 (which includes the most recent + release of Jython and the Python 2.5 version of GAE as of this writing). + +- The ``paster`` command is no longer the documented way to create projects, + start the server, or run debugging commands. To create projects from + scaffolds, ``paster create`` is replaced by the ``pcreate`` console script. + To serve up a project, ``paster serve`` is replaced by the ``pserve`` + console script. New console scripts named ``pshell``, ``pviews``, + ``proutes``, and ``ptweens`` do what their ``paster <commandname>`` + equivalents used to do. Rationale: the Paste and PasteScript packages do + not run under Python 3. + +- The default WSGI server run as the result of ``pserve`` from newly rendered + scaffolding is now the ``wsgiref`` WSGI server instead of the + ``paste.httpserver`` server. Rationale: Rationale: the Paste and + PasteScript packages do not run under Python 3. + +- The ``pshell`` command (see "paster pshell") no longer accepts a + ``--disable-ipython`` command-line argument. Instead, it accepts a ``-p`` + or ``--python-shell`` argument, which can be any of the values ``python``, + ``ipython`` or ``bpython``. + +- Removed the ``pyramid.renderers.renderer_from_name`` function. It has been + deprecated since Pyramid 1.0, and was never an API. + +- To use ZCML with versions of Pyramid >= 1.3, you will need ``pyramid_zcml`` + version >= 0.8 and ``zope.configuration`` version >= 3.8.0. The + ``pyramid_zcml`` package version 0.8 is backwards compatible all the way to + Pyramid 1.0, so you won't be warned if you have older versions installed + and upgrade Pyramid "in-place"; it may simply break instead. + +Dependencies +------------ + +- Pyramid no longer depends on the ``zope.component`` package, except as a + testing dependency. + +- Pyramid now depends on a zope.interface>=3.8.0, WebOb>=1.2dev, + repoze.lru>=0.4, zope.deprecation>=3.5.0, translationstring>=0.4 (for + Python 3 compatibility purposes). It also, as a testing dependency, + depends on WebTest>=1.3.1 for the same reason. + +- Pyramid no longer depends on the Paste or PasteScript packages. + +Documentation +------------- + +- The SQLAlchemy Wiki tutorial has been updated. It now uses + ``@view_config`` decorators and an explicit database population script. + +- Minor updates to the ZODB Wiki tutorial. + +- A narrative documentation chapter named "Extending Pyramid Configuration" + was added; it describes how to add a new directive, and how use the + ``pyramid.config.Configurator.action`` method within custom directives. It + also describes how to add introspectable objects. + +- A narrative documentation chapter named "Pyramid Configuration + Introspection" was added. It describes how to query the introspection + system. + +Scaffolds +--------- + +- Rendered scaffolds have now been changed to be more relocatable (fewer + mentions of the package name within files in the package). + +- The ``routesalchemy`` scaffold has been renamed ``alchemy``, replacing the + older (traversal-based) ``alchemy`` scaffold (which has been retired). + +- The ``starter`` scaffold now uses URL dispatch by default. + 1.2 (2011-09-12) ================ @@ -139,3 +139,6 @@ Probably Bad Ideas - Have ``remember`` and ``forget`` actually set headers on the response using a response callback (and return the empty list)? + +- http://pythonguy.wordpress.com/2011/06/22/dynamic-variables-revisited/ + instead of thread locals diff --git a/docs/api/renderers.rst b/docs/api/renderers.rst index 312aa0b31..ab182365e 100644 --- a/docs/api/renderers.rst +++ b/docs/api/renderers.rst @@ -11,8 +11,12 @@ .. autofunction:: render_to_response +.. autoclass:: JSON + .. autoclass:: JSONP +.. autoclass:: ObjectJSONEncoder + .. attribute:: null_renderer An object that can be used in advanced integration cases as input to the diff --git a/docs/conf.py b/docs/conf.py index 5254561ba..ffbf15808 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -80,7 +80,7 @@ copyright = '%s, Agendaless Consulting' % datetime.datetime.now().year # other places throughout the built documents. # # The short X.Y version. -version = '1.3' +version = '1.4dev' # The full version, including alpha/beta/rc tags. release = version diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst index 76035cbdf..34bee3c7f 100644 --- a/docs/narr/renderers.rst +++ b/docs/narr/renderers.rst @@ -177,8 +177,8 @@ using the API of the ``request.response`` attribute. See .. index:: pair: renderer; JSON -``json``: JSON Renderer -~~~~~~~~~~~~~~~~~~~~~~~ +JSON Renderer +~~~~~~~~~~~~~ The ``json`` renderer renders view callable results to :term:`JSON`. It passes the return value through the ``json.dumps`` standard library function, @@ -207,7 +207,13 @@ representing the JSON serialization of the return value: '{"content": "Hello!"}' The return value needn't be a dictionary, but the return value must contain -values serializable by :func:`json.dumps`. +values serializable by ``json.dumps``. + +.. note:: + + Extra arguments can be passed to ``json.dumps`` by overriding the default + ``json`` renderer. See :class:`pyramid.renderers.JSON` and + :ref:`adding_and_overriding_renderers` for more information. You can configure a view to use the JSON renderer by naming ``json`` as the ``renderer`` argument of a view configuration, e.g. by using @@ -221,18 +227,61 @@ You can configure a view to use the JSON renderer by naming ``json`` as the context='myproject.resources.Hello', renderer='json') - Views which use the JSON renderer can vary non-body response attributes by using the api of the ``request.response`` attribute. See :ref:`request_response_attr`. +.. _json_serializing_custom_objects: + +Serializing Custom Objects +++++++++++++++++++++++++++ + +Custom objects can be made easily JSON-serializable in Pyramid by defining a +``__json__`` method on the object's class. This method should return values +natively serializable by ``json.dumps`` (such as ints, lists, dictionaries, +strings, and so forth). + +.. code-block:: python + :linenos: + + from pyramid.view import view_config + + class MyObject(object): + def __init__(self, x): + self.x = x + + def __json__(self): + return {'x':self.x} + + @view_config(renderer='json') + def objects(request): + return [MyObject(1), MyObject(2)] + + # the JSON value returned by ``objects`` will be: + # [{"x": 1}, {"x": 2}] + +.. note:: + + Honoring the ``__json__`` method of custom objects is a feature new in + Pyramid 1.4. + +.. warning:: + + The machinery which performs the ``__json__`` method-calling magic is in + the :class:`pyramid.renderers.ObjectJSONEncoder` class. This class will + be used for encoding any non-basic Python object when you use the default + ```json`` or ``jsonp`` renderers. But if you later define your own custom + JSON renderer and pass it a "cls" argument signifying a different encoder, + the encoder you pass will override Pyramid's use of + :class:`pyramid.renderers.ObjectJSONEncoder`. + .. index:: pair: renderer; JSONP .. _jsonp_renderer: JSONP Renderer --------------- +~~~~~~~~~~~~~~ .. note:: This feature is new in Pyramid 1.1. @@ -297,6 +346,10 @@ The string ``callback=?`` above in the the ``url`` param to the JQuery a JSONP request; the ``callback`` parameter will be automatically filled in for you and used. +The same custom-object serialization scheme defined used for a "normal" JSON +renderer in :ref:`json_serializing_custom_objects` can be used when passing +values to a JSONP renderer too. + .. index:: pair: renderer; chameleon diff --git a/pyramid/config/views.py b/pyramid/config/views.py index a79d20d7d..ad4df28d8 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -865,7 +865,8 @@ class ViewsConfiguratorMixin(object): declaration with this argument ensures that the view will only be called when the request's ``method`` attribute (aka the ``REQUEST_METHOD`` of the WSGI environment) string matches a - supplied value. + supplied value. Note that use of ``GET`` also implies that the + view will respond to ``HEAD`` as of Pyramid 1.4. .. note:: The ability to pass a tuple of items as ``request_method`` is new as of Pyramid 1.2. Previous @@ -996,6 +997,9 @@ class ViewsConfiguratorMixin(object): if request_method is not None: request_method = as_sorted_tuple(request_method) + if 'GET' in request_method and 'HEAD' not in request_method: + # GET implies HEAD too + request_method = as_sorted_tuple(request_method + ('HEAD',)) order, predicates, phash = make_predicates(xhr=xhr, request_method=request_method, path_info=path_info, diff --git a/pyramid/mako_templating.py b/pyramid/mako_templating.py index b2db28ba7..208e54bf5 100644 --- a/pyramid/mako_templating.py +++ b/pyramid/mako_templating.py @@ -33,7 +33,8 @@ class PkgResourceTemplateLookup(TemplateLookup): """Called from within a Mako template, avoids adjusting the uri if it looks like an asset specification""" # Don't adjust asset spec names - if ':' in uri: + isabs = os.path.isabs(uri) + if (not isabs) and (':' in uri): return uri return TemplateLookup.adjust_uri(self, uri, relativeto) @@ -48,16 +49,21 @@ class PkgResourceTemplateLookup(TemplateLookup): """ isabs = os.path.isabs(uri) if (not isabs) and (':' in uri): + # Windows can't cope with colons in filenames, so we replace the + # colon with a dollar sign in the filename mako uses to actually + # store the generated python code in the mako module_directory or + # in the temporary location of mako's modules + adjusted = uri.replace(':', '$') try: if self.filesystem_checks: - return self._check(uri, self._collection[uri]) + return self._check(adjusted, self._collection[adjusted]) else: - return self._collection[uri] + return self._collection[adjusted] except KeyError: pname, path = resolve_asset_spec(uri) srcfile = abspath_from_asset_spec(path, pname) if os.path.isfile(srcfile): - return self._load(srcfile, uri) + return self._load(srcfile, adjusted) raise exceptions.TopLevelLookupException( "Can not locate template for uri %r" % uri) return TemplateLookup.get_template(self, uri) diff --git a/pyramid/renderers.py b/pyramid/renderers.py index 14941c61a..0adadf726 100644 --- a/pyramid/renderers.py +++ b/pyramid/renderers.py @@ -144,17 +144,6 @@ def get_renderer(renderer_name, package=None): # concrete renderer factory implementations (also API) -def json_renderer_factory(info): - def _render(value, system): - request = system.get('request') - if request is not None: - response = request.response - ct = response.content_type - if ct == response.default_content_type: - response.content_type = 'application/json' - return json.dumps(value) - return _render - def string_renderer_factory(info): def _render(value, system): if not isinstance(value, string_types): @@ -168,7 +157,101 @@ def string_renderer_factory(info): return value return _render -class JSONP(object): +class ObjectJSONEncoder(json.JSONEncoder): + """ The default JSON object encoder (a subclass of json.Encoder) used by + :class:`pyramid.renderers.JSON` and :class:`pyramid.renderers.JSONP`. It + is used when an object returned from a view and presented to a JSON-based + renderer is not a builtin Python type otherwise serializable to JSON. + + This ``json.Encoder`` subclass overrides the ``json.Encoder.default`` + method. The overridden method looks for a ``__json__`` attribute on the + object it is passed. If it's found, the encoder will assume it's + callable, and will call it with no arguments to obtain a value. The + overridden ``default`` method will then return that value (which must be + a JSON-serializable basic Python type). + + If the object passed to the overridden ``default`` method has no + ``__json__`` attribute, the ``json.JSONEncoder.default`` method is called + with the object that it was passed (which will end up raising a + :exc:`TypeError`, as it would with any other unserializable type). + + This class will be used only when you set a JSON or JSONP + renderer and you do not define your own custom encoder class. + + .. note:: This feature is new in Pyramid 1.4. + """ + + def default(self, obj): + if hasattr(obj, '__json__'): + return obj.__json__() + return json.JSONEncoder.default(self, obj) + +class JSON(object): + """ Renderer that returns a JSON-encoded string. + + Configure a custom JSON renderer using the + :meth:`pyramid.config.Configurator.add_renderer` API at application + startup time: + + .. code-block:: python + + from pyramid.config import Configurator + + config = Configurator() + config.add_renderer('myjson', JSON(indent=4, cls=MyJSONEncoder)) + + Once this renderer is registered via + :meth:`~pyramid.config.Configurator.add_renderer` as above, you can use + ``myjson`` as the ``renderer=`` parameter to ``@view_config`` or + :meth:`pyramid.config.Configurator.add_view``: + + .. code-block:: python + + from pyramid.view import view_config + + @view_config(renderer='myjson') + def myview(request): + return {'greeting':'Hello world'} + + .. note:: + + This feature is new in Pyramid 1.4. Prior to 1.4 there was + no public API for supplying options to the underlying + :func:`json.dumps` without defining a custom renderer. + + """ + + def __init__(self, **kw): + """ Any keyword arguments will be forwarded to + :func:`json.dumps`. + """ + self.kw = kw + + def __call__(self, info): + """ Returns a plain JSON-encoded string with content-type + ``application/json``. The content-type may be overridden by + setting ``request.response.content_type``.""" + def _render(value, system): + request = system.get('request') + if request is not None: + response = request.response + ct = response.content_type + if ct == response.default_content_type: + response.content_type = 'application/json' + return self.value_to_json(value) + return _render + + def value_to_json(self, value): + """ Convert a Python object to a JSON string. + + By default, this uses the :func:`json.dumps` from the stdlib.""" + if not self.kw.get('cls'): + self.kw['cls'] = ObjectJSONEncoder + return json.dumps(value, **self.kw) + +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. @@ -184,6 +267,20 @@ class JSONP(object): config = Configurator() config.add_renderer('jsonp', JSONP(param_name='callback')) + The class also accepts arbitrary keyword arguments; all keyword arguments + except ``param_name`` are passed to the ``json.dumps`` function as + keyword arguments: + + .. code-block:: python + + from pyramid.config import Configurator + + config = Configurator() + config.add_renderer('jsonp', JSONP(param_name='callback', indent=4)) + + .. note:: The ability of this class to accept a ``**kw`` in its + constructor is new as of Pyramid 1.4. + Once this renderer is registered via :meth:`~pyramid.config.Configurator.add_renderer` as above, you can use ``jsonp`` as the ``renderer=`` parameter to ``@view_config`` or @@ -210,9 +307,10 @@ class JSONP(object): See also: :ref:`jsonp_renderer`. """ - - def __init__(self, param_name='callback'): + + def __init__(self, param_name='callback', **kw): self.param_name = param_name + JSON.__init__(self, **kw) def __call__(self, info): """ Returns JSONP-encoded string with content-type @@ -221,7 +319,7 @@ class JSONP(object): plain-JSON encoded string with content-type ``application/json``""" def _render(value, system): request = system['request'] - val = json.dumps(value) + val = self.value_to_json(value) callback = request.GET.get(self.param_name) if callback is None: ct = 'application/json' diff --git a/pyramid/security.py b/pyramid/security.py index f29edd678..4b929241e 100644 --- a/pyramid/security.py +++ b/pyramid/security.py @@ -100,7 +100,7 @@ def effective_principals(request): policy = reg.queryUtility(IAuthenticationPolicy) if policy is None: - return [] + return [Everyone] return policy.effective_principals(request) def principals_allowed_by_permission(context, permission): diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py index 98fe3d1cd..9b46f83c9 100644 --- a/pyramid/tests/test_config/test_views.py +++ b/pyramid/tests/test_config/test_views.py @@ -1146,6 +1146,16 @@ class TestViewsConfigurationMixin(unittest.TestCase): request.method = 'GET' self._assertNotFound(wrapper, None, request) + def test_add_view_with_request_method_get_implies_head(self): + from pyramid.renderers import null_renderer + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_method='GET', renderer=null_renderer) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'HEAD' + self.assertEqual(wrapper(None, request), 'OK') + def test_add_view_with_request_param_noval_true(self): from pyramid.renderers import null_renderer view = lambda *arg: 'OK' diff --git a/pyramid/tests/test_mako_templating.py b/pyramid/tests/test_mako_templating.py index 550c95312..fbb04273b 100644 --- a/pyramid/tests/test_mako_templating.py +++ b/pyramid/tests/test_mako_templating.py @@ -1,7 +1,11 @@ ## come on python gimme some of that sweet, sweet -*- coding: utf-8 -*- +import shutil +import tempfile import unittest + from pyramid import testing + from pyramid.compat import ( text_, text_type, @@ -466,6 +470,15 @@ class TestPkgResourceTemplateLookup(unittest.TestCase): result = inst.get_template('pyramid.tests:fixtures/helloworld.mak') self.assertFalse(result is None) + def test_get_template_asset_spec_with_module_dir(self): + tmpdir = tempfile.mkdtemp() + try: + inst = self._makeOne(module_directory=tmpdir) + result = inst.get_template('pyramid.tests:fixtures/helloworld.mak') + self.assertFalse(result is None) + finally: + shutil.rmtree(tmpdir, ignore_errors=True) + def test_get_template_asset_spec_missing(self): from mako.exceptions import TopLevelLookupException fixturedir = self.get_fixturedir() diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py index b32e68e25..f03c7acda 100644 --- a/pyramid/tests/test_renderers.py +++ b/pyramid/tests/test_renderers.py @@ -340,35 +340,66 @@ class TestChameleonRendererLookup(unittest.TestCase): self.assertNotEqual(reg.queryUtility(ITemplateRenderer, name=spec), None) -class Test_json_renderer_factory(unittest.TestCase): +class TestJSON(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() - - def _callFUT(self, name): - from pyramid.renderers import json_renderer_factory - return json_renderer_factory(name) + + def _makeOne(self, **kw): + from pyramid.renderers import JSON + return JSON(**kw) def test_it(self): - renderer = self._callFUT(None) + renderer = self._makeOne()(None) result = renderer({'a':1}, {}) self.assertEqual(result, '{"a": 1}') def test_with_request_content_type_notset(self): request = testing.DummyRequest() - renderer = self._callFUT(None) + renderer = self._makeOne()(None) renderer({'a':1}, {'request':request}) self.assertEqual(request.response.content_type, 'application/json') def test_with_request_content_type_set(self): request = testing.DummyRequest() request.response.content_type = 'text/mishmash' - renderer = self._callFUT(None) + renderer = self._makeOne()(None) renderer({'a':1}, {'request':request}) self.assertEqual(request.response.content_type, 'text/mishmash') + def test_with_custom_encoder(self): + from datetime import datetime + from json import JSONEncoder + class MyEncoder(JSONEncoder): + def default(self, obj): + return obj.isoformat() + now = datetime.utcnow() + renderer = self._makeOne(cls=MyEncoder)(None) + result = renderer({'a':now}, {}) + self.assertEqual(result, '{"a": "%s"}' % now.isoformat()) + + def test_with_object_encoder(self): + class MyObject(object): + def __init__(self, x): + self.x = x + def __json__(self): + return {'x': self.x} + + objects = [MyObject(1), MyObject(2)] + renderer = self._makeOne()(None) + result = renderer(objects, {}) + self.assertEqual(result, '[{"x": 1}, {"x": 2}]') + + def test_with_object_encoder_no___json__(self): + class MyObject(object): + def __init__(self, x): + self.x = x + objects = [MyObject(1), MyObject(2)] + renderer = self._makeOne()(None) + self.assertRaises(TypeError, renderer, objects, {}) + class Test_string_renderer_factory(unittest.TestCase): def _callFUT(self, name): from pyramid.renderers import string_renderer_factory diff --git a/pyramid/tests/test_security.py b/pyramid/tests/test_security.py index 86149d554..ba9538b01 100644 --- a/pyramid/tests/test_security.py +++ b/pyramid/tests/test_security.py @@ -266,9 +266,10 @@ class TestEffectivePrincipals(unittest.TestCase): return effective_principals(request) def test_no_authentication_policy(self): + from pyramid.security import Everyone request = _makeRequest() result = self._callFUT(request) - self.assertEqual(result, []) + self.assertEqual(result, [Everyone]) def test_with_authentication_policy(self): request = _makeRequest() @@ -64,7 +64,7 @@ if not PY3: testing_extras = tests_require + ['nose', 'coverage'] setup(name='pyramid', - version='1.3', + version='1.4dev', description=('The Pyramid web application development framework, a ' 'Pylons project'), long_description=README + '\n\n' + CHANGES, |
