diff --git a/CHANGES.txt b/CHANGES.txt
index e90c5e127b..047db6472b 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -4,481 +4,26 @@ Next release
Features
--------
+- Python 3.2 compatibility (except for Paste scaffolding and paster commands,
+ which do not work, because Paste has not been ported to Python 3 yet).
+
- Lone instance methods can now be treated as view callables (see
https://github.com/Pylons/pyramid/pull/283).
-Dependencies
-------------
-
-- Pyramid no longer depends on the zope.component package, except as a
- testing dependency.
-
-- Pyramid now depends on a ``zope.interface`` version greater than or equal
- to 3.8.0.
-
-1.2 (2011-09-12)
-================
-
-Features
---------
-
-- Route pattern replacement marker names can now begin with an underscore.
- See https://github.com/Pylons/pyramid/issues/276.
-
-1.2b3 (2011-09-11)
-==================
-
-Bug Fixes
----------
-
-- The route prefix was not taken into account when a static view was added in
- an "include". See https://github.com/Pylons/pyramid/issues/266 .
-
-1.2b2 (2011-09-08)
-==================
-
-Bug Fixes
----------
-
-- The 1.2b1 tarball was a brownbag (particularly for Windows users) because
- it contained filenames with stray quotation marks in inappropriate places.
- We depend on ``setuptools-git`` to produce release tarballs, and when it
- was run to produce the 1.2b1 tarball, it didn't yet cope well with files
- present in git repositories with high-order characters in their filenames.
-
-Documentation
--------------
-
-- Minor tweaks to the "Introduction" narrative chapter example app and
- wording.
-
-1.2b1 (2011-09-08)
-==================
-
-Bug Fixes
----------
-
-- Sometimes falling back from territory translations (``de_DE``) to language
- translations (``de``) would not work properly when using a localizer. See
- https://github.com/Pylons/pyramid/issues/263
-
-- The static file serving machinery could not serve files that started with a
- ``.`` (dot) character.
-
-- Static files with high-order (super-ASCII) characters in their names could
- not be served by a static view. The static file serving machinery
- inappropriately URL-quoted path segments in filenames when asking for files
- from the filesystem.
-
-- Within ``pyramid.traversal.traversal_path`` , canonicalize URL segments
- from UTF-8 to Unicode before checking whether a segment matches literally
- one of ``.``, the empty string, or ``..`` in case there's some sneaky way
- someone might tunnel those strings via UTF-8 that don't match the literals
- before decoded.
-
-Documentation
--------------
-
-- Added a "What Makes Pyramid Unique" section to the Introduction narrative
- chapter.
-
-1.2a6 (2011-09-06)
-==================
-
-Bug Fixes
----------
-
-- AuthTktAuthenticationPolicy with a ``reissue_time`` interfered with logout.
- See https://github.com/Pylons/pyramid/issues/262.
-
-Internal
---------
-
-- Internalize code previously depended upon as imports from the
- ``paste.auth`` module (futureproof).
-
-- Replaced use of ``paste.urlparser.StaticURLParser`` with a derivative of
- Chris Rossi's "happy" static file serving code (futureproof).
-
-- Fixed test suite; on some systems tests would fail due to indeterminate
- test run ordering and a double-push-single-pop of a shared test variable.
-
-Behavior Differences
---------------------
-
-- An ETag header is no longer set when serving a static file. A
- Last-Modified header is set instead.
-
-- Static file serving no longer supports the ``wsgi.file_wrapper`` extension.
-
-- Instead of returning a ``403 Forbidden`` error when a static file is served
- that cannot be accessed by the Pyramid process' user due to file
- permissions, an IOError (or similar) will be raised.
-
-Scaffolds
----------
-
-- All scaffolds now send the ``cache_max_age`` parameter to the
- ``add_static_view`` method.
-
-1.2a5 (2011-09-04)
-==================
-
-Bug Fixes
----------
-
-- The ``route_prefix`` of a configurator was not properly taken into account
- when registering routes in certain circumstances. See
- https://github.com/Pylons/pyramid/issues/260
-
-Dependencies
-------------
-
-- The ``zope.configuration`` package is no longer a dependency.
-
-1.2a4 (2011-09-02)
-==================
-
-Features
---------
-
-- Support an ``onerror`` keyword argument to
- ``pyramid.config.Configurator.scan()``. This onerror keyword argument is
- passed to ``venusian.Scanner.scan()`` to influence error behavior when
- an exception is raised during scanning.
-
-- The ``request_method`` predicate argument to
- ``pyramid.config.Configurator.add_view`` and
- ``pyramid.config.Configurator.add_route`` is now permitted to be a tuple of
- HTTP method names. Previously it was restricted to being a string
- representing a single HTTP method name.
-
-- Undeprecated ``pyramid.traversal.find_model``,
- ``pyramid.traversal.model_path``, ``pyramid.traversal.model_path_tuple``,
- and ``pyramid.url.model_url``, which were all deprecated in Pyramid 1.0.
- There's just not much cost to keeping them around forever as aliases to
- their renamed ``resource_*`` prefixed functions.
-
-- Undeprecated ``pyramid.view.bfg_view``, which was deprecated in Pyramid
- 1.0. This is a low-cost alias to ``pyramid.view.view_config`` which we'll
- just keep around forever.
-
-Dependencies
-------------
-
-- Pyramid now requires Venusian 1.0a1 or better to support the ``onerror``
- keyword argument to ``pyramid.config.Configurator.scan``.
-
-1.2a3 (2011-08-29)
-==================
-
-Bug Fixes
----------
-
-- Pyramid did not properly generate static URLs using
- ``pyramid.url.static_url`` when passed a caller-package relative path due
- to a refactoring done in 1.2a1.
-
-- The ``settings`` object emitted a deprecation warning any time
- ``__getattr__`` was called upon it. However, there are legitimate
- situations in which ``__getattr__`` is called on arbitrary objects
- (e.g. ``hasattr``). Now, the ``settings`` object only emits the warning
- upon successful lookup.
-
-Internal
---------
-
-- Use ``config.with_package`` in view_config decorator rather than
- manufacturing a new renderer helper (cleanup).
-
-1.2a2 (2011-08-27)
-==================
-
-Bug Fixes
----------
-
-- When a ``renderers=`` argument is not specified to the Configurator
- constructor, eagerly register and commit the default renderer set. This
- permits the overriding of the default renderers, which was broken in 1.2a1
- without a commit directly after Configurator construction.
-
-- Mako rendering exceptions had the wrong value for an error message.
-
-- An include could not set a root factory successfully because the
- Configurator constructor unconditionally registered one that would be
- treated as if it were "the word of the user".
-
-Features
---------
-
-- A session factory can now be passed in using the dotted name syntax.
-
-1.2a1 (2011-08-24)
-==================
-
-Features
---------
-
-- The ``[pshell]`` section in an ini configuration file now treats a
- ``setup`` key as a dotted name that points to a callable that is passed the
- bootstrap environment. It can mutate the environment as necessary for
- great justice.
-
-- A new configuration setting named ``pyramid.includes`` is now available.
- It is described in the "Environment Variables and ``.ini`` Files Settings"
- narrative documentation chapter.
-
-- Added a ``route_prefix`` argument to the
- ``pyramid.config.Configurator.include`` method. This argument allows you
- to compose URL dispatch applications together. See the section entitled
- "Using a Route Prefix to Compose Applications" in the "URL Dispatch"
- narrative documentation chapter.
-
-- Added a ``pyramid.security.NO_PERMISSION_REQUIRED`` constant for use in
- ``permission=`` statements to view configuration. This constant has a
- value of the string ``__no_permission_required__``. This string value was
- previously referred to in documentation; now the documentation uses the
- constant.
-
-- Added a decorator-based way to configure a response adapter:
- ``pyramid.response.response_adapter``. This decorator has the same use as
- ``pyramid.config.Configurator.add_response_adapter`` but it's declarative.
-
-- The ``pyramid.events.BeforeRender`` event now has an attribute named
- ``rendering_val``. This can be used to introspect the value returned by a
- view in a BeforeRender subscriber.
-
-- New configurator directive: ``pyramid.config.Configurator.add_tween``.
- This directive adds a "tween". A "tween" is used to wrap the Pyramid
- router's primary request handling function. This is a feature may be used
- by Pyramid framework extensions, to provide, for example, view timing
- support and as a convenient place to hang bookkeeping code.
-
- Tweens are further described in the narrative docs section in the Hooks
- chapter, named "Registering Tweens".
-
-- New paster command ``paster ptweens``, which prints the current "tween"
- configuration for an application. See the section entitled "Displaying
- Tweens" in the Command-Line Pyramid chapter of the narrative documentation
- for more info.
-
-- The Pyramid debug logger now uses the standard logging configuration
- (usually set up by Paste as part of startup). This means that output from
- e.g. ``debug_notfound``, ``debug_authorization``, etc. will go to the
- normal logging channels. The logger name of the debug logger will be the
- package name of the *caller* of the Configurator's constructor.
-
-- A new attribute is available on request objects: ``exc_info``. Its value
- will be ``None`` until an exception is caught by the Pyramid router, after
- which it will be the result of ``sys.exc_info()``.
-
-- ``pyramid.testing.DummyRequest`` now implements the
- ``add_finished_callback`` and ``add_response_callback`` methods.
-
-- New methods of the ``pyramid.config.Configurator`` class:
- ``set_authentication_policy`` and ``set_authorization_policy``. These are
- meant to be consumed mostly by add-on authors.
-
-- New Configurator method: ``set_root_factory``.
-
-- Pyramid no longer eagerly commits some default configuration statements at
- Configurator construction time, which permits values passed in as
- constructor arguments (e.g. ``authentication_policy`` and
- ``authorization_policy``) to override the same settings obtained via an
- "include".
-
-- Better Mako rendering exceptions via
- ``pyramid.mako_templating.MakoRenderingException``
-
-- New request methods: ``current_route_url``, ``current_route_path``, and
- ``static_path``.
-
-- New functions in ``pyramid.url``: ``current_route_path`` and
- ``static_path``.
-
-- The ``pyramid.request.Request.static_url`` API (and its brethren
- ``pyramid.request.Request.static_path``, ``pyramid.url.static_url``, and
- ``pyramid.url.static_path``) now accept an asbolute filename as a "path"
- argument. This will generate a URL to an asset as long as the filename is
- in a directory which was previously registered as a static view.
- Previously, trying to generate a URL to an asset using an absolute file
- path would raise a ValueError.
-
-- The ``RemoteUserAuthenticationPolicy ``, ``AuthTktAuthenticationPolicy``,
- and ``SessionAuthenticationPolicy`` constructors now accept an additional
- keyword argument named ``debug``. By default, this keyword argument is
- ``False``. When it is ``True``, debug information will be sent to the
- Pyramid debug logger (usually on stderr) when the ``authenticated_userid``
- or ``effective_principals`` method is called on any of these policies. The
- output produced can be useful when trying to diagnose
- authentication-related problems.
-
-- New view predicate: ``match_param``. Example: a view added via
- ``config.add_view(aview, match_param='action=edit')`` will be called only
- when the ``request.matchdict`` has a value inside it named ``action`` with
- a value of ``edit``.
-
-Internal
---------
-
-- The Pyramid "exception view" machinery is now implemented as a "tween"
- (``pyramid.tweens.excview_tween_factory``).
-
-- WSGIHTTPException (HTTPFound, HTTPNotFound, etc) now has a new API named
- "prepare" which renders the body and content type when it is provided with
- a WSGI environ. Required for debug toolbar.
-
-- Once ``__call__`` or ``prepare`` is called on a WSGIHTTPException, the body
- will be set, and subsequent calls to ``__call__`` will always return the
- same body. Delete the body attribute to rerender the exception body.
-
-- Previously the ``pyramid.events.BeforeRender`` event *wrapped* a dictionary
- (it addressed it as its ``_system`` attribute). Now it *is* a dictionary
- (it inherits from ``dict``), and it's the value that is passed to templates
- as a top-level dictionary.
-
-- The ``route_url``, ``route_path``, ``resource_url``, ``static_url``, and
- ``current_route_url`` functions in the ``pyramid.url`` package now delegate
- to a method on the request they've been passed, instead of the other way
- around. The pyramid.request.Request object now inherits from a mixin named
- pyramid.url.URLMethodsMixin to make this possible, and all url/path
- generation logic is embedded in this mixin.
-
-- Refactor ``pyramid.config`` into a package.
-
-- Removed the ``_set_security_policies`` method of the Configurator.
-
-- Moved the ``StaticURLInfo`` class from ``pyramid.static`` to
- ``pyramid.config.views``.
-
-- Move the ``Settings`` class from ``pyramid.settings`` to
- ``pyramid.config.settings``.
-
-- Move the ``OverrideProvider``, ``PackageOverrides``, ``DirectoryOverride``,
- and ``FileOverride`` classes from ``pyramid.asset`` to
- ``pyramid.config.assets``.
-
-Deprecations
-------------
-
-- All Pyramid-related deployment settings (e.g. ``debug_all``,
- ``debug_notfound``) are now meant to be prefixed with the prefix
- ``pyramid.``. For example: ``debug_all`` -> ``pyramid.debug_all``. The
- old non-prefixed settings will continue to work indefinitely but supplying
- them may eventually print a deprecation warning. All scaffolds and
- tutorials have been changed to use prefixed settings.
-
-- The ``settings`` dictionary now raises a deprecation warning when you
- attempt to access its values via ``__getattr__`` instead of
- via ``__getitem__``.
-
Backwards Incompatibilities
---------------------------
-- If a string is passed as the ``debug_logger`` parameter to a Configurator,
- that string is considered to be the name of a global Python logger rather
- than a dotted name to an instance of a logger.
-
-- The ``pyramid.config.Configurator.include`` method now accepts only a
- single ``callable`` argument (a sequence of callables used to be
- permitted). If you are passing more than one ``callable`` to
- ``pyramid.config.Configurator.include``, it will break. You now must now
- instead make a separate call to the method for each callable. This change
- was introduced to support the ``route_prefix`` feature of include.
-
-- It may be necessary to more strictly order configuration route and view
- statements when using an "autocommitting" Configurator. In the past, it
- was possible to add a view which named a route name before adding a route
- with that name when you used an autocommitting configurator. For example::
-
- config = Configurator(autocommit=True)
- config.add_view('my.pkg.someview', route_name='foo')
- config.add_route('foo', '/foo')
-
- The above will raise an exception when the view attempts to add itself.
- Now you must add the route before adding the view::
-
- config = Configurator(autocommit=True)
- config.add_route('foo', '/foo')
- config.add_view('my.pkg.someview', route_name='foo')
-
- This won't effect "normal" users, only people who have legacy BFG codebases
- that used an autommitting configurator and possibly tests that use the
- configurator API (the configurator returned by ``pyramid.testing.setUp`` is
- an autocommitting configurator). The right way to get around this is to
- use a non-autocommitting configurator (the default), which does not have
- these directive ordering requirements.
-
-- The ``pyramid.config.Configurator.add_route`` directive no longer returns a
- route object. This change was required to make route vs. view
- configuration processing work properly.
-
-Documentation
--------------
-
-- Narrative and API documentation which used the ``route_url``,
- ``route_path``, ``resource_url``, ``static_url``, and ``current_route_url``
- functions in the ``pyramid.url`` package have now been changed to use
- eponymous methods of the request instead.
-
-- Added a section entitled "Using a Route Prefix to Compose Applications" to
- the "URL Dispatch" narrative documentation chapter.
-
-- Added a new module to the API docs: ``pyramid.tweens``.
+- Pyramid no longer runs on Python 2.5 (which includes the most recent
+ release of Jython, and the current version of GAE).
-- Added a "Registering Tweens" section to the "Hooks" narrative chapter.
-
-- Added a "Displaying Tweens" section to the "Command-Line Pyramid" narrative
- chapter.
-
-- Added documentation for the ``pyramid.tweens`` and ``pyramid.includes``
- configuration settings to the "Environment Variables and ``.ini`` Files
- Settings" chapter.
-
-- Added a Logging chapter to the narrative docs (based on the Pylons logging
- docs, thanks Phil).
-
-- Added a Paste chapter to the narrative docs (moved content from the Project
- chapter).
-
-- Added the ``pyramid.interfaces.IDict`` interface representing the methods
- of a dictionary, for documentation purposes only (IMultiDict and
- IBeforeRender inherit from it).
-
-- All tutorials now use - The ``route_url``, ``route_path``,
- ``resource_url``, ``static_url``, and ``current_route_url`` methods of the
- request rather than the function variants imported from ``pyramid.url``.
-
-- The ZODB wiki tutorial now uses the ``pyramid_zodbconn`` package rather
- than the ``repoze.zodbconn`` package to provide ZODB integration.
-
-Dependency Changes
-------------------
-
-- Pyramid now relies on PasteScript >= 1.7.4. This version contains a
- feature important for allowing flexible logging configuration.
-
-Scaffolds
-----------
-
-- All scaffolds now use the ``pyramid_tm`` package rather than the
- ``repoze.tm2`` middleware to manage transaction management.
-
-- The ZODB scaffold now uses the ``pyramid_zodbconn`` package rather than the
- ``repoze.zodbconn`` package to provide ZODB integration.
-
-- All scaffolds now use the ``pyramid_debugtoolbar`` package rather than the
- ``WebError`` package to provide interactive debugging features.
-
-- Projects created via a scaffold no longer depend on the ``WebError``
- package at all; configuration in the ``production.ini`` file which used to
- require its ``error_catcher`` middleware has been removed. Configuring
- error catching / email sending is now the domain of the ``pyramid_exclog``
- package (see https://docs.pylonsproject.org/projects/pyramid_exclog/dev/).
+Dependencies
+------------
-Bug Fixes
----------
+- Pyramid no longer depends on the zope.component package, except as a
+ testing dependency.
-- Fixed an issue with the default renderer not working at certain times. See
- https://github.com/Pylons/pyramid/issues/249
+- 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.
diff --git a/HISTORY.txt b/HISTORY.txt
index 5cabb01114..956f07362d 100644
--- a/HISTORY.txt
+++ b/HISTORY.txt
@@ -1,3 +1,470 @@
+1.2 (2011-09-12)
+================
+
+Features
+--------
+
+- Route pattern replacement marker names can now begin with an underscore.
+ See https://github.com/Pylons/pyramid/issues/276.
+
+1.2b3 (2011-09-11)
+==================
+
+Bug Fixes
+---------
+
+- The route prefix was not taken into account when a static view was added in
+ an "include". See https://github.com/Pylons/pyramid/issues/266 .
+
+1.2b2 (2011-09-08)
+==================
+
+Bug Fixes
+---------
+
+- The 1.2b1 tarball was a brownbag (particularly for Windows users) because
+ it contained filenames with stray quotation marks in inappropriate places.
+ We depend on ``setuptools-git`` to produce release tarballs, and when it
+ was run to produce the 1.2b1 tarball, it didn't yet cope well with files
+ present in git repositories with high-order characters in their filenames.
+
+Documentation
+-------------
+
+- Minor tweaks to the "Introduction" narrative chapter example app and
+ wording.
+
+1.2b1 (2011-09-08)
+==================
+
+Bug Fixes
+---------
+
+- Sometimes falling back from territory translations (``de_DE``) to language
+ translations (``de``) would not work properly when using a localizer. See
+ https://github.com/Pylons/pyramid/issues/263
+
+- The static file serving machinery could not serve files that started with a
+ ``.`` (dot) character.
+
+- Static files with high-order (super-ASCII) characters in their names could
+ not be served by a static view. The static file serving machinery
+ inappropriately URL-quoted path segments in filenames when asking for files
+ from the filesystem.
+
+- Within ``pyramid.traversal.traversal_path`` , canonicalize URL segments
+ from UTF-8 to Unicode before checking whether a segment matches literally
+ one of ``.``, the empty string, or ``..`` in case there's some sneaky way
+ someone might tunnel those strings via UTF-8 that don't match the literals
+ before decoded.
+
+Documentation
+-------------
+
+- Added a "What Makes Pyramid Unique" section to the Introduction narrative
+ chapter.
+
+1.2a6 (2011-09-06)
+==================
+
+Bug Fixes
+---------
+
+- AuthTktAuthenticationPolicy with a ``reissue_time`` interfered with logout.
+ See https://github.com/Pylons/pyramid/issues/262.
+
+Internal
+--------
+
+- Internalize code previously depended upon as imports from the
+ ``paste.auth`` module (futureproof).
+
+- Replaced use of ``paste.urlparser.StaticURLParser`` with a derivative of
+ Chris Rossi's "happy" static file serving code (futureproof).
+
+- Fixed test suite; on some systems tests would fail due to indeterminate
+ test run ordering and a double-push-single-pop of a shared test variable.
+
+Behavior Differences
+--------------------
+
+- An ETag header is no longer set when serving a static file. A
+ Last-Modified header is set instead.
+
+- Static file serving no longer supports the ``wsgi.file_wrapper`` extension.
+
+- Instead of returning a ``403 Forbidden`` error when a static file is served
+ that cannot be accessed by the Pyramid process' user due to file
+ permissions, an IOError (or similar) will be raised.
+
+Scaffolds
+---------
+
+- All scaffolds now send the ``cache_max_age`` parameter to the
+ ``add_static_view`` method.
+
+1.2a5 (2011-09-04)
+==================
+
+Bug Fixes
+---------
+
+- The ``route_prefix`` of a configurator was not properly taken into account
+ when registering routes in certain circumstances. See
+ https://github.com/Pylons/pyramid/issues/260
+
+Dependencies
+------------
+
+- The ``zope.configuration`` package is no longer a dependency.
+
+1.2a4 (2011-09-02)
+==================
+
+Features
+--------
+
+- Support an ``onerror`` keyword argument to
+ ``pyramid.config.Configurator.scan()``. This onerror keyword argument is
+ passed to ``venusian.Scanner.scan()`` to influence error behavior when
+ an exception is raised during scanning.
+
+- The ``request_method`` predicate argument to
+ ``pyramid.config.Configurator.add_view`` and
+ ``pyramid.config.Configurator.add_route`` is now permitted to be a tuple of
+ HTTP method names. Previously it was restricted to being a string
+ representing a single HTTP method name.
+
+- Undeprecated ``pyramid.traversal.find_model``,
+ ``pyramid.traversal.model_path``, ``pyramid.traversal.model_path_tuple``,
+ and ``pyramid.url.model_url``, which were all deprecated in Pyramid 1.0.
+ There's just not much cost to keeping them around forever as aliases to
+ their renamed ``resource_*`` prefixed functions.
+
+- Undeprecated ``pyramid.view.bfg_view``, which was deprecated in Pyramid
+ 1.0. This is a low-cost alias to ``pyramid.view.view_config`` which we'll
+ just keep around forever.
+
+Dependencies
+------------
+
+- Pyramid now requires Venusian 1.0a1 or better to support the ``onerror``
+ keyword argument to ``pyramid.config.Configurator.scan``.
+
+1.2a3 (2011-08-29)
+==================
+
+Bug Fixes
+---------
+
+- Pyramid did not properly generate static URLs using
+ ``pyramid.url.static_url`` when passed a caller-package relative path due
+ to a refactoring done in 1.2a1.
+
+- The ``settings`` object emitted a deprecation warning any time
+ ``__getattr__`` was called upon it. However, there are legitimate
+ situations in which ``__getattr__`` is called on arbitrary objects
+ (e.g. ``hasattr``). Now, the ``settings`` object only emits the warning
+ upon successful lookup.
+
+Internal
+--------
+
+- Use ``config.with_package`` in view_config decorator rather than
+ manufacturing a new renderer helper (cleanup).
+
+1.2a2 (2011-08-27)
+==================
+
+Bug Fixes
+---------
+
+- When a ``renderers=`` argument is not specified to the Configurator
+ constructor, eagerly register and commit the default renderer set. This
+ permits the overriding of the default renderers, which was broken in 1.2a1
+ without a commit directly after Configurator construction.
+
+- Mako rendering exceptions had the wrong value for an error message.
+
+- An include could not set a root factory successfully because the
+ Configurator constructor unconditionally registered one that would be
+ treated as if it were "the word of the user".
+
+Features
+--------
+
+- A session factory can now be passed in using the dotted name syntax.
+
+1.2a1 (2011-08-24)
+==================
+
+Features
+--------
+
+- The ``[pshell]`` section in an ini configuration file now treats a
+ ``setup`` key as a dotted name that points to a callable that is passed the
+ bootstrap environment. It can mutate the environment as necessary for
+ great justice.
+
+- A new configuration setting named ``pyramid.includes`` is now available.
+ It is described in the "Environment Variables and ``.ini`` Files Settings"
+ narrative documentation chapter.
+
+- Added a ``route_prefix`` argument to the
+ ``pyramid.config.Configurator.include`` method. This argument allows you
+ to compose URL dispatch applications together. See the section entitled
+ "Using a Route Prefix to Compose Applications" in the "URL Dispatch"
+ narrative documentation chapter.
+
+- Added a ``pyramid.security.NO_PERMISSION_REQUIRED`` constant for use in
+ ``permission=`` statements to view configuration. This constant has a
+ value of the string ``__no_permission_required__``. This string value was
+ previously referred to in documentation; now the documentation uses the
+ constant.
+
+- Added a decorator-based way to configure a response adapter:
+ ``pyramid.response.response_adapter``. This decorator has the same use as
+ ``pyramid.config.Configurator.add_response_adapter`` but it's declarative.
+
+- The ``pyramid.events.BeforeRender`` event now has an attribute named
+ ``rendering_val``. This can be used to introspect the value returned by a
+ view in a BeforeRender subscriber.
+
+- New configurator directive: ``pyramid.config.Configurator.add_tween``.
+ This directive adds a "tween". A "tween" is used to wrap the Pyramid
+ router's primary request handling function. This is a feature may be used
+ by Pyramid framework extensions, to provide, for example, view timing
+ support and as a convenient place to hang bookkeeping code.
+
+ Tweens are further described in the narrative docs section in the Hooks
+ chapter, named "Registering Tweens".
+
+- New paster command ``paster ptweens``, which prints the current "tween"
+ configuration for an application. See the section entitled "Displaying
+ Tweens" in the Command-Line Pyramid chapter of the narrative documentation
+ for more info.
+
+- The Pyramid debug logger now uses the standard logging configuration
+ (usually set up by Paste as part of startup). This means that output from
+ e.g. ``debug_notfound``, ``debug_authorization``, etc. will go to the
+ normal logging channels. The logger name of the debug logger will be the
+ package name of the *caller* of the Configurator's constructor.
+
+- A new attribute is available on request objects: ``exc_info``. Its value
+ will be ``None`` until an exception is caught by the Pyramid router, after
+ which it will be the result of ``sys.exc_info()``.
+
+- ``pyramid.testing.DummyRequest`` now implements the
+ ``add_finished_callback`` and ``add_response_callback`` methods.
+
+- New methods of the ``pyramid.config.Configurator`` class:
+ ``set_authentication_policy`` and ``set_authorization_policy``. These are
+ meant to be consumed mostly by add-on authors.
+
+- New Configurator method: ``set_root_factory``.
+
+- Pyramid no longer eagerly commits some default configuration statements at
+ Configurator construction time, which permits values passed in as
+ constructor arguments (e.g. ``authentication_policy`` and
+ ``authorization_policy``) to override the same settings obtained via an
+ "include".
+
+- Better Mako rendering exceptions via
+ ``pyramid.mako_templating.MakoRenderingException``
+
+- New request methods: ``current_route_url``, ``current_route_path``, and
+ ``static_path``.
+
+- New functions in ``pyramid.url``: ``current_route_path`` and
+ ``static_path``.
+
+- The ``pyramid.request.Request.static_url`` API (and its brethren
+ ``pyramid.request.Request.static_path``, ``pyramid.url.static_url``, and
+ ``pyramid.url.static_path``) now accept an asbolute filename as a "path"
+ argument. This will generate a URL to an asset as long as the filename is
+ in a directory which was previously registered as a static view.
+ Previously, trying to generate a URL to an asset using an absolute file
+ path would raise a ValueError.
+
+- The ``RemoteUserAuthenticationPolicy ``, ``AuthTktAuthenticationPolicy``,
+ and ``SessionAuthenticationPolicy`` constructors now accept an additional
+ keyword argument named ``debug``. By default, this keyword argument is
+ ``False``. When it is ``True``, debug information will be sent to the
+ Pyramid debug logger (usually on stderr) when the ``authenticated_userid``
+ or ``effective_principals`` method is called on any of these policies. The
+ output produced can be useful when trying to diagnose
+ authentication-related problems.
+
+- New view predicate: ``match_param``. Example: a view added via
+ ``config.add_view(aview, match_param='action=edit')`` will be called only
+ when the ``request.matchdict`` has a value inside it named ``action`` with
+ a value of ``edit``.
+
+Internal
+--------
+
+- The Pyramid "exception view" machinery is now implemented as a "tween"
+ (``pyramid.tweens.excview_tween_factory``).
+
+- WSGIHTTPException (HTTPFound, HTTPNotFound, etc) now has a new API named
+ "prepare" which renders the body and content type when it is provided with
+ a WSGI environ. Required for debug toolbar.
+
+- Once ``__call__`` or ``prepare`` is called on a WSGIHTTPException, the body
+ will be set, and subsequent calls to ``__call__`` will always return the
+ same body. Delete the body attribute to rerender the exception body.
+
+- Previously the ``pyramid.events.BeforeRender`` event *wrapped* a dictionary
+ (it addressed it as its ``_system`` attribute). Now it *is* a dictionary
+ (it inherits from ``dict``), and it's the value that is passed to templates
+ as a top-level dictionary.
+
+- The ``route_url``, ``route_path``, ``resource_url``, ``static_url``, and
+ ``current_route_url`` functions in the ``pyramid.url`` package now delegate
+ to a method on the request they've been passed, instead of the other way
+ around. The pyramid.request.Request object now inherits from a mixin named
+ pyramid.url.URLMethodsMixin to make this possible, and all url/path
+ generation logic is embedded in this mixin.
+
+- Refactor ``pyramid.config`` into a package.
+
+- Removed the ``_set_security_policies`` method of the Configurator.
+
+- Moved the ``StaticURLInfo`` class from ``pyramid.static`` to
+ ``pyramid.config.views``.
+
+- Move the ``Settings`` class from ``pyramid.settings`` to
+ ``pyramid.config.settings``.
+
+- Move the ``OverrideProvider``, ``PackageOverrides``, ``DirectoryOverride``,
+ and ``FileOverride`` classes from ``pyramid.asset`` to
+ ``pyramid.config.assets``.
+
+Deprecations
+------------
+
+- All Pyramid-related deployment settings (e.g. ``debug_all``,
+ ``debug_notfound``) are now meant to be prefixed with the prefix
+ ``pyramid.``. For example: ``debug_all`` -> ``pyramid.debug_all``. The
+ old non-prefixed settings will continue to work indefinitely but supplying
+ them may eventually print a deprecation warning. All scaffolds and
+ tutorials have been changed to use prefixed settings.
+
+- The ``settings`` dictionary now raises a deprecation warning when you
+ attempt to access its values via ``__getattr__`` instead of
+ via ``__getitem__``.
+
+Backwards Incompatibilities
+---------------------------
+
+- If a string is passed as the ``debug_logger`` parameter to a Configurator,
+ that string is considered to be the name of a global Python logger rather
+ than a dotted name to an instance of a logger.
+
+- The ``pyramid.config.Configurator.include`` method now accepts only a
+ single ``callable`` argument (a sequence of callables used to be
+ permitted). If you are passing more than one ``callable`` to
+ ``pyramid.config.Configurator.include``, it will break. You now must now
+ instead make a separate call to the method for each callable. This change
+ was introduced to support the ``route_prefix`` feature of include.
+
+- It may be necessary to more strictly order configuration route and view
+ statements when using an "autocommitting" Configurator. In the past, it
+ was possible to add a view which named a route name before adding a route
+ with that name when you used an autocommitting configurator. For example::
+
+ config = Configurator(autocommit=True)
+ config.add_view('my.pkg.someview', route_name='foo')
+ config.add_route('foo', '/foo')
+
+ The above will raise an exception when the view attempts to add itself.
+ Now you must add the route before adding the view::
+
+ config = Configurator(autocommit=True)
+ config.add_route('foo', '/foo')
+ config.add_view('my.pkg.someview', route_name='foo')
+
+ This won't effect "normal" users, only people who have legacy BFG codebases
+ that used an autommitting configurator and possibly tests that use the
+ configurator API (the configurator returned by ``pyramid.testing.setUp`` is
+ an autocommitting configurator). The right way to get around this is to
+ use a non-autocommitting configurator (the default), which does not have
+ these directive ordering requirements.
+
+- The ``pyramid.config.Configurator.add_route`` directive no longer returns a
+ route object. This change was required to make route vs. view
+ configuration processing work properly.
+
+Documentation
+-------------
+
+- Narrative and API documentation which used the ``route_url``,
+ ``route_path``, ``resource_url``, ``static_url``, and ``current_route_url``
+ functions in the ``pyramid.url`` package have now been changed to use
+ eponymous methods of the request instead.
+
+- Added a section entitled "Using a Route Prefix to Compose Applications" to
+ the "URL Dispatch" narrative documentation chapter.
+
+- Added a new module to the API docs: ``pyramid.tweens``.
+
+- Added a "Registering Tweens" section to the "Hooks" narrative chapter.
+
+- Added a "Displaying Tweens" section to the "Command-Line Pyramid" narrative
+ chapter.
+
+- Added documentation for the ``pyramid.tweens`` and ``pyramid.includes``
+ configuration settings to the "Environment Variables and ``.ini`` Files
+ Settings" chapter.
+
+- Added a Logging chapter to the narrative docs (based on the Pylons logging
+ docs, thanks Phil).
+
+- Added a Paste chapter to the narrative docs (moved content from the Project
+ chapter).
+
+- Added the ``pyramid.interfaces.IDict`` interface representing the methods
+ of a dictionary, for documentation purposes only (IMultiDict and
+ IBeforeRender inherit from it).
+
+- All tutorials now use - The ``route_url``, ``route_path``,
+ ``resource_url``, ``static_url``, and ``current_route_url`` methods of the
+ request rather than the function variants imported from ``pyramid.url``.
+
+- The ZODB wiki tutorial now uses the ``pyramid_zodbconn`` package rather
+ than the ``repoze.zodbconn`` package to provide ZODB integration.
+
+Dependency Changes
+------------------
+
+- Pyramid now relies on PasteScript >= 1.7.4. This version contains a
+ feature important for allowing flexible logging configuration.
+
+Scaffolds
+----------
+
+- All scaffolds now use the ``pyramid_tm`` package rather than the
+ ``repoze.tm2`` middleware to manage transaction management.
+
+- The ZODB scaffold now uses the ``pyramid_zodbconn`` package rather than the
+ ``repoze.zodbconn`` package to provide ZODB integration.
+
+- All scaffolds now use the ``pyramid_debugtoolbar`` package rather than the
+ ``WebError`` package to provide interactive debugging features.
+
+- Projects created via a scaffold no longer depend on the ``WebError``
+ package at all; configuration in the ``production.ini`` file which used to
+ require its ``error_catcher`` middleware has been removed. Configuring
+ error catching / email sending is now the domain of the ``pyramid_exclog``
+ package (see https://docs.pylonsproject.org/projects/pyramid_exclog/dev/).
+
+Bug Fixes
+---------
+
+- Fixed an issue with the default renderer not working at certain times. See
+ https://github.com/Pylons/pyramid/issues/249
+
+
1.1 (2011-07-22)
================
diff --git a/pyramid/asset.py b/pyramid/asset.py
index 730969a4a6..4bf0d7bf4b 100644
--- a/pyramid/asset.py
+++ b/pyramid/asset.py
@@ -1,11 +1,13 @@
import os
import pkg_resources
+from pyramid.compat import string_types
+
from pyramid.path import package_path
from pyramid.path import package_name
def resolve_asset_spec(spec, pname='__main__'):
- if pname and not isinstance(pname, basestring):
+ if pname and not isinstance(pname, string_types):
pname = pname.__name__ # as package
if os.path.isabs(spec):
return None, spec
diff --git a/pyramid/authentication.py b/pyramid/authentication.py
index e2014b9a18..e38c984b3e 100644
--- a/pyramid/authentication.py
+++ b/pyramid/authentication.py
@@ -1,12 +1,19 @@
from codecs import utf_8_decode
from codecs import utf_8_encode
from hashlib import md5
+import base64
import datetime
import re
import time as time_mod
-import urllib
-from zope.interface import implements
+from zope.interface import implementer
+
+from pyramid.compat import long
+from pyramid.compat import text_type
+from pyramid.compat import binary_type
+from pyramid.compat import url_unquote
+from pyramid.compat import url_quote
+from pyramid.compat import bytes_
from pyramid.interfaces import IAuthenticationPolicy
from pyramid.interfaces import IDebugLogger
@@ -105,6 +112,7 @@ def effective_principals(self, request):
)
return effective_principals
+@implementer(IAuthenticationPolicy)
class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy):
""" A :app:`Pyramid` :term:`authentication policy` which
obtains data from the :mod:`repoze.who` 1.X WSGI 'API' (the
@@ -129,7 +137,6 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy):
Objects of this class implement the interface described by
:class:`pyramid.interfaces.IAuthenticationPolicy`.
"""
- implements(IAuthenticationPolicy)
def __init__(self, identifier_name='auth_tkt', callback=None):
self.identifier_name = identifier_name
@@ -193,6 +200,7 @@ def forget(self, request):
identity = self._get_identity(request)
return identifier.forget(request.environ, identity)
+@implementer(IAuthenticationPolicy)
class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy):
""" A :app:`Pyramid` :term:`authentication policy` which
obtains data from the ``REMOTE_USER`` WSGI environment variable.
@@ -222,7 +230,6 @@ class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy):
Objects of this class implement the interface described by
:class:`pyramid.interfaces.IAuthenticationPolicy`.
"""
- implements(IAuthenticationPolicy)
def __init__(self, environ_key='REMOTE_USER', callback=None, debug=False):
self.environ_key = environ_key
@@ -238,6 +245,7 @@ def remember(self, request, principal, **kw):
def forget(self, request):
return []
+@implementer(IAuthenticationPolicy)
class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
""" A :app:`Pyramid` :term:`authentication policy` which
obtains data from an :class:`paste.auth.auth_tkt` cookie.
@@ -340,7 +348,6 @@ class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
Objects of this class implement the interface described by
:class:`pyramid.interfaces.IAuthenticationPolicy`.
"""
- implements(IAuthenticationPolicy)
def __init__(self,
secret,
callback=None,
@@ -383,10 +390,10 @@ def forget(self, request):
return self.cookie.forget(request)
def b64encode(v):
- return v.encode('base64').strip().replace('\n', '')
+ return base64.b64encode(bytes_(v)).strip().replace(b'\n', b'')
def b64decode(v):
- return v.decode('base64')
+ return base64.b64decode(bytes_(v))
# this class licensed under the MIT license (stolen from Paste)
class AuthTicket(object):
@@ -440,7 +447,7 @@ def digest(self):
def cookie_value(self):
v = '%s%08x%s!' % (self.digest(), int(self.time),
- urllib.quote(self.userid))
+ url_quote(self.userid))
if self.tokens:
v += self.tokens + '!'
v += self.user_data
@@ -469,13 +476,13 @@ def parse_ticket(secret, ticket, ip):
digest = ticket[:32]
try:
timestamp = int(ticket[32:40], 16)
- except ValueError, e:
+ except ValueError as e:
raise BadTicket('Timestamp is not a hex integer: %s' % e)
try:
userid, data = ticket[40:].split('!', 1)
except ValueError:
raise BadTicket('userid is not followed by !')
- userid = urllib.unquote(userid)
+ userid = url_unquote(userid)
if '!' in data:
tokens, user_data = data.split('!', 1)
else: # pragma: no cover (never generated)
@@ -496,14 +503,14 @@ def parse_ticket(secret, ticket, ip):
# this function licensed under the MIT license (stolen from Paste)
def calculate_digest(ip, timestamp, secret, userid, tokens, user_data):
- secret = maybe_encode(secret)
- userid = maybe_encode(userid)
- tokens = maybe_encode(tokens)
- user_data = maybe_encode(user_data)
+ secret = bytes_(secret, 'utf-8')
+ userid = bytes_(userid, 'utf-8')
+ tokens = bytes_(tokens, 'utf-8')
+ user_data = bytes_(user_data, 'utf-8')
digest0 = md5(
- encode_ip_timestamp(ip, timestamp) + secret + userid + '\0'
- + tokens + '\0' + user_data).hexdigest()
- digest = md5(digest0 + secret).hexdigest()
+ encode_ip_timestamp(ip, timestamp) + secret + userid + b'\0'
+ + tokens + b'\0' + user_data).hexdigest()
+ digest = md5(bytes_(digest0) + secret).hexdigest()
return digest
# this function licensed under the MIT license (stolen from Paste)
@@ -515,12 +522,7 @@ def encode_ip_timestamp(ip, timestamp):
(t & 0xff00) >> 8,
t & 0xff)
ts_chars = ''.join(map(chr, ts))
- return ip_chars + ts_chars
-
-def maybe_encode(s, encoding='utf8'):
- if isinstance(s, unicode):
- s = s.encode(encoding)
- return s
+ return bytes_(ip_chars + ts_chars)
EXPIRE = object()
@@ -546,8 +548,8 @@ class AuthTktCookieHelper(object):
userid_type_encoders = {
int: ('int', str),
long: ('int', str),
- unicode: ('b64unicode', lambda x: b64encode(utf_8_encode(x)[0])),
- str: ('b64str', lambda x: b64encode(x)),
+ text_type: ('b64unicode', lambda x: b64encode(utf_8_encode(x)[0])),
+ binary_type: ('b64str', lambda x: b64encode(x)),
}
def __init__(self, secret, cookie_name='auth_tkt', secure=False,
@@ -658,7 +660,7 @@ def identify(self, request):
if reissue and not hasattr(request, '_authtkt_reissued'):
if ( (now - timestamp) > self.reissue_time ):
# work around https://github.com/Pylons/pyramid/issues#issue/108
- tokens = filter(None, tokens)
+ tokens = list(filter(None, tokens))
headers = self.remember(request, userid, max_age=self.max_age,
tokens=tokens)
def reissue_authtkt(request, response):
@@ -725,6 +727,11 @@ def remember(self, request, userid, max_age=None, tokens=()):
user_data = 'userid_type:%s' % encoding
for token in tokens:
+ if isinstance(token, text_type):
+ try:
+ token.encode('ascii')
+ except UnicodeEncodeError:
+ raise ValueError("Invalid token %r" % (token,))
if not (isinstance(token, str) and VALID_TOKEN.match(token)):
raise ValueError("Invalid token %r" % (token,))
@@ -743,6 +750,7 @@ def remember(self, request, userid, max_age=None, tokens=()):
cookie_value = ticket.cookie_value()
return self._get_cookies(environ, cookie_value, max_age)
+@implementer(IAuthenticationPolicy)
class SessionAuthenticationPolicy(CallbackAuthenticationPolicy):
""" A :app:`Pyramid` authentication policy which gets its data from the
configured :term:`session`. For this authentication policy to work, you
@@ -772,7 +780,6 @@ class SessionAuthenticationPolicy(CallbackAuthenticationPolicy):
or IRC channels when asking for support.
"""
- implements(IAuthenticationPolicy)
def __init__(self, prefix='auth.', callback=None, debug=False):
self.callback = callback
diff --git a/pyramid/authorization.py b/pyramid/authorization.py
index ac8f195f2d..b1ef10033a 100644
--- a/pyramid/authorization.py
+++ b/pyramid/authorization.py
@@ -1,4 +1,4 @@
-from zope.interface import implements
+from zope.interface import implementer
from pyramid.interfaces import IAuthorizationPolicy
@@ -9,6 +9,7 @@
from pyramid.security import Deny
from pyramid.security import Everyone
+@implementer(IAuthorizationPolicy)
class ACLAuthorizationPolicy(object):
""" An :term:`authorization policy` which consults an :term:`ACL`
object attached to a :term:`context` to determine authorization
@@ -60,8 +61,6 @@ class ACLAuthorizationPolicy(object):
:class:`pyramid.interfaces.IAuthorizationPolicy` interface.
"""
- implements(IAuthorizationPolicy)
-
def permits(self, context, principals, permission):
""" Return an instance of
:class:`pyramid.security.ACLAllowed` instance if the policy
diff --git a/pyramid/chameleon_text.py b/pyramid/chameleon_text.py
index 676985853d..872d3b920a 100644
--- a/pyramid/chameleon_text.py
+++ b/pyramid/chameleon_text.py
@@ -1,7 +1,9 @@
import sys
from zope.deprecation import deprecated
-from zope.interface import implements
+from zope.interface import implementer
+
+from pyramid.compat import reraise
try:
from chameleon.zpt.template import PageTextTemplateFile
@@ -12,7 +14,7 @@
# Chameleon doesn't work on non-CPython platforms
class PageTextTemplateFile(object):
def __init__(self, *arg, **kw):
- raise ImportError, exc, tb
+ reraise(ImportError, exc, tb)
from pyramid.interfaces import ITemplateRenderer
@@ -23,8 +25,8 @@ def __init__(self, *arg, **kw):
def renderer_factory(info):
return renderers.template_renderer_factory(info, TextTemplateRenderer)
+@implementer(ITemplateRenderer)
class TextTemplateRenderer(object):
- implements(ITemplateRenderer)
def __init__(self, path, lookup):
self.path = path
self.lookup = lookup
diff --git a/pyramid/chameleon_zpt.py b/pyramid/chameleon_zpt.py
index ca96d93560..aa6f89e07c 100644
--- a/pyramid/chameleon_zpt.py
+++ b/pyramid/chameleon_zpt.py
@@ -1,7 +1,9 @@
import sys
from zope.deprecation import deprecated
-from zope.interface import implements
+from zope.interface import implementer
+
+from pyramid.compat import reraise
try:
from chameleon.zpt.template import PageTemplateFile
@@ -11,7 +13,7 @@
# Chameleon doesn't work on non-CPython platforms
class PageTemplateFile(object):
def __init__(self, *arg, **kw):
- raise ImportError, exc, tb
+ reraise(ImportError, exc, tb)
from pyramid.interfaces import ITemplateRenderer
@@ -22,8 +24,8 @@ def __init__(self, *arg, **kw):
def renderer_factory(info):
return renderers.template_renderer_factory(info, ZPTTemplateRenderer)
+@implementer(ITemplateRenderer)
class ZPTTemplateRenderer(object):
- implements(ITemplateRenderer)
def __init__(self, path, lookup):
self.path = path
self.lookup = lookup
diff --git a/pyramid/compat.py b/pyramid/compat.py
index 7d723715e8..e686be27dc 100644
--- a/pyramid/compat.py
+++ b/pyramid/compat.py
@@ -1,3 +1,18 @@
+import sys
+import types
+
+try: # pragma: no cover
+ import __pypy__
+ PYPY = True
+except: # pragma: no cover
+ __pypy__ = None
+ PYPY = False
+
+try:
+ import cPickle as pickle
+except ImportError: # pragma: no cover
+ import pickle
+
try:
import json
except ImportError: # pragma: no cover
@@ -6,3 +21,206 @@
except NotImplementedError:
from django.utils import simplejson as json # GAE
+# True if we are running on Python 3.
+PY3 = sys.version_info[0] == 3
+
+if PY3: # pragma: no cover
+ string_types = str,
+ integer_types = int,
+ class_types = type,
+ text_type = str
+ binary_type = bytes
+ long = int
+else:
+ string_types = basestring,
+ integer_types = (int, long)
+ class_types = (type, types.ClassType)
+ text_type = unicode
+ binary_type = str
+ long = long
+
+def text_(s, encoding='latin-1', errors='strict'):
+ if isinstance(s, binary_type):
+ return s.decode(encoding, errors)
+ return s # pragma: no cover
+
+def bytes_(s, encoding='latin-1', errors='strict'):
+ if isinstance(s, text_type):
+ return s.encode(encoding, errors)
+ return s
+
+if PY3: # pragma: no cover
+ def ascii_native_(s):
+ if isinstance(s, text_type):
+ s = s.encode('ascii')
+ return str(s, 'ascii', 'strict')
+else:
+ def ascii_native_(s):
+ if isinstance(s, text_type):
+ s = s.encode('ascii')
+ return str(s)
+
+if PY3: # pragma: no cover
+ def native_(s, encoding='latin-1', errors='strict'):
+ if isinstance(s, text_type):
+ return s
+ return str(s, encoding, errors)
+else:
+ def native_(s, encoding='latin-1', errors='strict'):
+ if isinstance(s, text_type):
+ return s.encode(encoding, errors)
+ return str(s)
+
+if PY3: # pragma: no cover
+ from urllib import parse
+ urlparse = parse
+ from urllib.parse import quote as url_quote
+ from urllib.parse import quote_plus as url_quote_plus
+ from urllib.parse import unquote as url_unquote
+ from urllib.parse import urlencode as url_encode
+ from urllib.request import urlopen as url_open
+ url_unquote_text = url_unquote
+ url_unquote_native = url_unquote
+else:
+ import urlparse
+ from urllib import quote as url_quote
+ from urllib import quote_plus as url_quote_plus
+ from urllib import unquote as url_unquote
+ from urllib import urlencode as url_encode
+ from urllib2 import urlopen as url_open
+ def url_unquote_text(v, encoding='utf-8', errors='replace'):
+ v = url_unquote(v)
+ return v.decode(encoding, errors)
+ def url_unquote_native(v, encoding='utf-8', errors='replace'):
+ return native_(url_unquote_text(v, encoding, errors))
+
+
+if PY3: # pragma: no cover
+ import builtins
+ exec_ = getattr(builtins, "exec")
+
+
+ def reraise(tp, value, tb=None):
+ if value.__traceback__ is not tb:
+ raise value.with_traceback(tb)
+ raise value
+
+
+ print_ = getattr(builtins, "print")
+ del builtins
+
+else: # pragma: no cover
+ def exec_(code, globs=None, locs=None):
+ """Execute code in a namespace."""
+ if globs is None:
+ frame = sys._getframe(1)
+ globs = frame.f_globals
+ if locs is None:
+ locs = frame.f_locals
+ del frame
+ elif locs is None:
+ locs = globs
+ exec("""exec code in globs, locs""")
+
+
+ exec_("""def reraise(tp, value, tb=None):
+ raise tp, value, tb
+""")
+
+
+ def print_(*args, **kwargs):
+ """The new-style print function."""
+ fp = kwargs.pop("file", sys.stdout)
+ if fp is None:
+ return
+ def write(data):
+ if not isinstance(data, basestring):
+ data = str(data)
+ fp.write(data)
+ want_unicode = False
+ sep = kwargs.pop("sep", None)
+ if sep is not None:
+ if isinstance(sep, unicode):
+ want_unicode = True
+ elif not isinstance(sep, str):
+ raise TypeError("sep must be None or a string")
+ end = kwargs.pop("end", None)
+ if end is not None:
+ if isinstance(end, unicode):
+ want_unicode = True
+ elif not isinstance(end, str):
+ raise TypeError("end must be None or a string")
+ if kwargs:
+ raise TypeError("invalid keyword arguments to print()")
+ if not want_unicode:
+ for arg in args:
+ if isinstance(arg, unicode):
+ want_unicode = True
+ break
+ if want_unicode:
+ newline = unicode("\n")
+ space = unicode(" ")
+ else:
+ newline = "\n"
+ space = " "
+ if sep is None:
+ sep = space
+ if end is None:
+ end = newline
+ for i, arg in enumerate(args):
+ if i:
+ write(sep)
+ write(arg)
+ write(end)
+
+if PY3: # pragma: no cover
+ def iteritems_(d):
+ return d.items()
+ def itervalues_(d):
+ return d.values()
+ def iterkeys_(d):
+ return d.keys()
+else:
+ def iteritems_(d):
+ return d.iteritems()
+ def itervalues_(d):
+ return d.itervalues()
+ def iterkeys_(d):
+ return d.iterkeys()
+
+
+if PY3: # pragma: no cover
+ def map_(*arg):
+ return list(map(*arg))
+else:
+ map_ = map
+
+if PY3: # pragma: no cover
+ def is_nonstr_iter(v):
+ if isinstance(v, str):
+ return False
+ return hasattr(v, '__iter__')
+else:
+ def is_nonstr_iter(v):
+ return hasattr(v, '__iter__')
+
+if PY3: # pragma: no cover
+ im_func = '__func__'
+else:
+ im_func = 'im_func'
+
+try: # pragma: no cover
+ import configparser
+except ImportError: # pragma: no cover
+ import ConfigParser
+ configparser = ConfigParser
+
+try:
+ from Cookie import SimpleCookie
+except ImportError: # pragma: no cover
+ from http.cookies import SimpleCookie
+
+if PY3: # pragma: no cover
+ from html import escape
+else:
+ from cgi import escape
diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py
index 9effeed171..8085bbc79c 100644
--- a/pyramid/config/__init__.py
+++ b/pyramid/config/__init__.py
@@ -13,6 +13,10 @@
from pyramid.asset import resolve_asset_spec
from pyramid.authorization import ACLAuthorizationPolicy
+from pyramid.compat import text_
+from pyramid.compat import reraise
+from pyramid.compat import string_types
+from pyramid.compat import PY3
from pyramid.events import ApplicationCreated
from pyramid.exceptions import ConfigurationConflictError
from pyramid.exceptions import ConfigurationError
@@ -42,6 +46,8 @@
from pyramid.config.views import ViewsConfiguratorMixin
from pyramid.config.zca import ZCAConfiguratorMixin
+empty = text_('')
+
ConfigurationError = ConfigurationError # pyflakes
class Configurator(
@@ -284,7 +290,7 @@ def setup_registry(self, settings=None, root_factory=None,
self._set_settings(settings)
self._register_response_adapters()
- if isinstance(debug_logger, basestring):
+ if isinstance(debug_logger, string_types):
debug_logger = logging.getLogger(debug_logger)
if debug_logger is None:
@@ -402,7 +408,7 @@ def queryAdapterOrSelf(object, interface, default=None):
if not hasattr(_registry, 'registerSelfAdapter'):
def registerSelfAdapter(required=None, provided=None,
- name=u'', info=u'', event=True):
+ name=empty, info=empty, event=True):
return _registry.registerAdapter(lambda x: x,
required=required,
provided=provided, name=name,
@@ -655,7 +661,10 @@ def __getattr__(self, name):
c, action_wrap = c
if action_wrap:
c = action_method(c)
- m = types.MethodType(c, self, self.__class__)
+ if PY3: # pragma: no cover
+ m = types.MethodType(c, self)
+ else:
+ m = types.MethodType(c, self, self.__class__)
return m
@classmethod
@@ -710,7 +719,7 @@ def absolute_asset_spec(self, relative_spec):
when generating an absolute asset specification. If the
provided ``relative_spec`` argument is already absolute, or if
the ``relative_spec`` is not a string, it is simply returned."""
- if not isinstance(relative_spec, basestring):
+ if not isinstance(relative_spec, string_types):
return relative_spec
return self._make_spec(relative_spec)
@@ -908,7 +917,9 @@ def execute_actions(self, clear=True):
except:
t, v, tb = sys.exc_info()
try:
- raise ConfigurationExecutionError(t, v, info), None, tb
+ reraise(ConfigurationExecutionError,
+ ConfigurationExecutionError(t, v, info),
+ tb)
finally:
del t, v, tb
finally:
@@ -994,7 +1005,11 @@ def resolveConflicts(actions):
# We need to sort the actions by the paths so that the shortest
# path with a given prefix comes first:
- dups.sort()
+ def allbutfunc(stupid):
+ # f me with a shovel, py3 cant cope with sorting when the
+ # callable function is in the list
+ return stupid[0:2] + stupid[3:]
+ dups.sort(key=allbutfunc)
(basepath, i, callable, args, kw, baseinfo) = dups[0]
output.append(
(i, discriminator, callable, args, kw, basepath, baseinfo)
diff --git a/pyramid/config/assets.py b/pyramid/config/assets.py
index 1b52540723..08cc6dc38d 100644
--- a/pyramid/config/assets.py
+++ b/pyramid/config/assets.py
@@ -1,7 +1,7 @@
import pkg_resources
import sys
-from zope.interface import implements
+from zope.interface import implementer
from pyramid.interfaces import IPackageOverrides
@@ -80,8 +80,8 @@ def resource_listdir(self, resource_name):
return pkg_resources.DefaultProvider.resource_listdir(
self, resource_name)
+@implementer(IPackageOverrides)
class PackageOverrides:
- implements(IPackageOverrides)
# pkg_resources arg in kw args below for testing
def __init__(self, package, pkg_resources=pkg_resources):
if hasattr(package, '__loader__') and not isinstance(package.__loader__,
diff --git a/pyramid/config/settings.py b/pyramid/config/settings.py
index 6e636bf583..565a6699c2 100644
--- a/pyramid/config/settings.py
+++ b/pyramid/config/settings.py
@@ -1,7 +1,7 @@
import os
import warnings
-from zope.interface import implements
+from zope.interface import implementer
from pyramid.interfaces import ISettings
@@ -54,12 +54,12 @@ def get_settings(self):
return self.registry.settings
+@implementer(ISettings)
class Settings(dict):
""" Deployment settings. Update application settings (usually
from PasteDeploy keywords) with framework-specific key/value pairs
(e.g. find ``PYRAMID_DEBUG_AUTHORIZATION`` in os.environ and jam into
keyword args)."""
- implements(ISettings)
# _environ_ is dep inj for testing
def __init__(self, d=None, _environ_=os.environ, **kw):
if d is None:
diff --git a/pyramid/config/testing.py b/pyramid/config/testing.py
index 0f709f6340..36729acdf3 100644
--- a/pyramid/config/testing.py
+++ b/pyramid/config/testing.py
@@ -6,7 +6,7 @@
from pyramid.interfaces import IRendererFactory
from pyramid.renderers import RendererHelper
-from pyramid.traversal import traversal_path
+from pyramid.traversal import traversal_path_info
from pyramid.config.util import action_method
@@ -66,7 +66,7 @@ def __init__(self, context):
def __call__(self, request):
path = request.environ['PATH_INFO']
ob = resources[path]
- traversed = traversal_path(path)
+ traversed = traversal_path_info(path)
return {'context':ob, 'view_name':'','subpath':(),
'traversed':traversed, 'virtual_root':ob,
'virtual_root_path':(), 'root':ob}
diff --git a/pyramid/config/tweens.py b/pyramid/config/tweens.py
index 2704b89c13..3c7ee384f3 100644
--- a/pyramid/config/tweens.py
+++ b/pyramid/config/tweens.py
@@ -1,7 +1,10 @@
-from zope.interface import implements
+from zope.interface import implementer
from pyramid.interfaces import ITweens
+from pyramid.compat import string_types
+from pyramid.compat import is_nonstr_iter
+from pyramid.compat import string_types
from pyramid.exceptions import ConfigurationError
from pyramid.tweens import excview_tween_factory
from pyramid.tweens import MAIN, INGRESS, EXCVIEW
@@ -96,7 +99,7 @@ def add_tween(self, tween_factory, under=None, over=None):
@action_method
def _add_tween(self, tween_factory, under=None, over=None, explicit=False):
- if not isinstance(tween_factory, basestring):
+ if not isinstance(tween_factory, string_types):
raise ConfigurationError(
'The "tween_factory" argument to add_tween must be a '
'dotted name to a globally importable object, not %r' %
@@ -110,7 +113,7 @@ def _add_tween(self, tween_factory, under=None, over=None, explicit=False):
tween_factory = self.maybe_dotted(tween_factory)
def is_string_or_iterable(v):
- if isinstance(v, basestring):
+ if isinstance(v, string_types):
return True
if hasattr(v, '__iter__'):
return True
@@ -121,10 +124,10 @@ def is_string_or_iterable(v):
raise ConfigurationError(
'"%s" must be a string or iterable, not %s' % (t, p))
- if over is INGRESS or hasattr(over, '__iter__') and INGRESS in over:
+ if over is INGRESS or is_nonstr_iter(over) and INGRESS in over:
raise ConfigurationError('%s cannot be over INGRESS' % name)
- if under is MAIN or hasattr(under, '__iter__') and MAIN in under:
+ if under is MAIN or is_nonstr_iter(under) and MAIN in under:
raise ConfigurationError('%s cannot be under MAIN' % name)
registry = self.registry
@@ -157,8 +160,8 @@ def __str__(self):
msg = 'Implicit tween ordering cycle:' + '; '.join(L)
return msg
+@implementer(ITweens)
class Tweens(object):
- implements(ITweens)
def __init__(self):
self.explicit = []
self.names = []
@@ -176,12 +179,12 @@ def add_implicit(self, name, factory, under=None, over=None):
if under is None and over is None:
under = INGRESS
if under is not None:
- if not hasattr(under, '__iter__'):
+ if not is_nonstr_iter(under):
under = (under,)
self.order += [(u, name) for u in under]
self.req_under.add(name)
if over is not None:
- if not hasattr(over, '__iter__'):
+ if not is_nonstr_iter(over): #hasattr(over, '__iter__'):
over = (over,)
self.order += [(name, o) for o in over]
self.req_over.add(name)
@@ -197,7 +200,7 @@ def implicit(self):
order.append((a, b))
def add_node(node):
- if not graph.has_key(node):
+ if not node in graph:
roots.append(node)
graph[node] = [0] # 0 = number of arcs coming into this node
diff --git a/pyramid/config/util.py b/pyramid/config/util.py
index 1e54213ac8..0336b103d9 100644
--- a/pyramid/config/util.py
+++ b/pyramid/config/util.py
@@ -1,9 +1,12 @@
import re
import traceback
+from pyramid.compat import string_types
+from pyramid.compat import bytes_
+from pyramid.compat import is_nonstr_iter
from pyramid.exceptions import ConfigurationError
from pyramid.traversal import find_interface
-from pyramid.traversal import traversal_path
+from pyramid.traversal import traversal_path_info
from hashlib import md5
@@ -93,10 +96,10 @@ def xhr_predicate(context, request):
xhr_predicate.__text__ = "xhr = True"
weights.append(1 << 1)
predicates.append(xhr_predicate)
- h.update('xhr:%r' % bool(xhr))
+ h.update(bytes_('xhr:%r' % bool(xhr)))
if request_method is not None:
- if not hasattr(request_method, '__iter__'):
+ if not is_nonstr_iter(request_method):
request_method = (request_method,)
request_method = sorted(request_method)
def request_method_predicate(context, request):
@@ -106,20 +109,20 @@ def request_method_predicate(context, request):
weights.append(1 << 2)
predicates.append(request_method_predicate)
for m in request_method:
- h.update('request_method:%r' % m)
+ h.update(bytes_('request_method:%r' % m))
if path_info is not None:
try:
path_info_val = re.compile(path_info)
- except re.error, why:
- raise ConfigurationError(why[0])
+ except re.error as why:
+ raise ConfigurationError(why.args[0])
def path_info_predicate(context, request):
return path_info_val.match(request.path_info) is not None
text = "path_info = %s"
path_info_predicate.__text__ = text % path_info
weights.append(1 << 3)
predicates.append(path_info_predicate)
- h.update('path_info:%r' % path_info)
+ h.update(bytes_('path_info:%r' % path_info))
if request_param is not None:
request_param_val = None
@@ -136,7 +139,8 @@ def request_param_predicate(context, request):
request_param_predicate.__text__ = text
weights.append(1 << 4)
predicates.append(request_param_predicate)
- h.update('request_param:%r=%r' % (request_param, request_param_val))
+ h.update(
+ bytes_('request_param:%r=%r' % (request_param, request_param_val)))
if header is not None:
header_name = header
@@ -145,8 +149,8 @@ def request_param_predicate(context, request):
header_name, header_val = header.split(':', 1)
try:
header_val = re.compile(header_val)
- except re.error, why:
- raise ConfigurationError(why[0])
+ except re.error as why:
+ raise ConfigurationError(why.args[0])
if header_val is None:
text = "header %s" % header_name
else:
@@ -161,7 +165,7 @@ def header_predicate(context, request):
header_predicate.__text__ = text
weights.append(1 << 5)
predicates.append(header_predicate)
- h.update('header:%r=%r' % (header_name, header_val))
+ h.update(bytes_('header:%r=%r' % (header_name, header_val)))
if accept is not None:
def accept_predicate(context, request):
@@ -169,7 +173,7 @@ def accept_predicate(context, request):
accept_predicate.__text__ = "accept = %s" % accept
weights.append(1 << 6)
predicates.append(accept_predicate)
- h.update('accept:%r' % accept)
+ h.update(bytes_('accept:%r' % accept))
if containment is not None:
def containment_predicate(context, request):
@@ -177,7 +181,7 @@ def containment_predicate(context, request):
containment_predicate.__text__ = "containment = %s" % containment
weights.append(1 << 7)
predicates.append(containment_predicate)
- h.update('containment:%r' % hash(containment))
+ h.update(bytes_('containment:%r' % hash(containment)))
if request_type is not None:
def request_type_predicate(context, request):
@@ -186,22 +190,22 @@ def request_type_predicate(context, request):
request_type_predicate.__text__ = text % request_type
weights.append(1 << 8)
predicates.append(request_type_predicate)
- h.update('request_type:%r' % hash(request_type))
+ h.update(bytes_('request_type:%r' % hash(request_type)))
if match_param is not None:
- if isinstance(match_param, basestring):
+ if isinstance(match_param, string_types):
match_param, match_param_val = match_param.split('=', 1)
match_param = {match_param: match_param_val}
text = "match_param %s" % match_param
def match_param_predicate(context, request):
- for k, v in match_param.iteritems():
+ for k, v in match_param.items():
if request.matchdict.get(k) != v:
return False
return True
match_param_predicate.__text__ = text
weights.append(1 << 9)
predicates.append(match_param_predicate)
- h.update('match_param:%r' % match_param)
+ h.update(bytes_('match_param:%r' % match_param))
if custom:
for num, predicate in enumerate(custom):
@@ -222,7 +226,7 @@ def match_param_predicate(context, request):
# functions for custom predicates, so that the hash output
# of predicate instances which are "logically the same"
# may compare equal.
- h.update('custom%s:%r' % (num, hash(predicate)))
+ h.update(bytes_('custom%s:%r' % (num, hash(predicate))))
weights.append(1 << 10)
if traverse is not None:
@@ -237,7 +241,7 @@ def traverse_predicate(context, request):
return True
m = context['match']
tvalue = tgenerate(m)
- m['traverse'] = traversal_path(tvalue)
+ m['traverse'] = traversal_path_info(tvalue)
return True
# This isn't actually a predicate, it's just a infodict
# modifier that injects ``traverse`` into the matchdict. As a
@@ -254,7 +258,7 @@ def traverse_predicate(context, request):
return order, predicates, phash
def as_sorted_tuple(val):
- if not hasattr(val, '__iter__'):
+ if not is_nonstr_iter(val):
val = (val,)
val = tuple(sorted(val))
return val
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index 3da41861da..179d4065cc 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -1,11 +1,9 @@
import inspect
-from urlparse import urljoin
-from urlparse import urlparse
from zope.interface import Interface
from zope.interface import classProvides
from zope.interface import implementedBy
-from zope.interface import implements
+from zope.interface import implementer
from zope.interface.interfaces import IInterface
from pyramid.interfaces import IAuthenticationPolicy
@@ -28,6 +26,9 @@
from pyramid.interfaces import PHASE1_CONFIG
from pyramid import renderers
+from pyramid.compat import string_types
+from pyramid.compat import urlparse
+from pyramid.compat import im_func
from pyramid.exceptions import ConfigurationError
from pyramid.exceptions import PredicateMismatch
from pyramid.httpexceptions import HTTPForbidden
@@ -43,6 +44,9 @@
from pyramid.config.util import as_sorted_tuple
from pyramid.config.util import make_predicates
+urljoin = urlparse.urljoin
+url_parse = urlparse.urlparse
+
def wraps_view(wrapper):
def inner(self, view):
wrapper_view = wrapper(self, view)
@@ -333,9 +337,9 @@ def decorated_view(self, view):
return view
return decorator(view)
+@implementer(IViewMapper)
class DefaultViewMapper(object):
classProvides(IViewMapperFactory)
- implements(IViewMapper)
def __init__(self, **kw):
self.attr = kw.get('attr')
@@ -414,6 +418,7 @@ def _attr_view(context, request):
return _attr_view
def requestonly(view, attr=None):
+ ismethod = False
if attr is None:
attr = '__call__'
if inspect.isroutine(view):
@@ -423,6 +428,7 @@ def requestonly(view, attr=None):
fn = view.__init__
except AttributeError:
return False
+ ismethod = hasattr(fn, '__call__')
else:
try:
fn = getattr(view, attr)
@@ -436,7 +442,8 @@ def requestonly(view, attr=None):
args = argspec[0]
- if inspect.ismethod(fn):
+ if hasattr(fn, im_func) or ismethod:
+ # it's an instance method (or unbound method on py2)
if not args:
return False
args = args[1:]
@@ -456,8 +463,8 @@ def requestonly(view, attr=None):
return False
+@implementer(IMultiView)
class MultiView(object):
- implements(IMultiView)
def __init__(self, name):
self.name = name
@@ -910,7 +917,7 @@ def view(context, request):
if not IInterface.providedBy(r_context):
r_context = implementedBy(r_context)
- if isinstance(renderer, basestring):
+ if isinstance(renderer, string_types):
renderer = renderers.RendererHelper(
name=renderer, package=self.package,
registry = self.registry)
@@ -1057,7 +1064,7 @@ def regclosure():
'view', context, name, request_type, IView, containment,
request_param, request_method, route_name, attr,
xhr, accept, header, path_info, match_param]
- discriminator.extend(sorted(custom_predicates))
+ discriminator.extend(sorted([hash(x) for x in custom_predicates]))
discriminator = tuple(discriminator)
self.action(discriminator, register)
@@ -1147,7 +1154,7 @@ def _derive_view(self, view, permission=None, predicates=(),
mapper=None, http_cache=None):
view = self.maybe_dotted(view)
mapper = self.maybe_dotted(mapper)
- if isinstance(renderer, basestring):
+ if isinstance(renderer, string_types):
renderer = renderers.RendererHelper(
name=renderer, package=self.package,
registry = self.registry)
@@ -1206,7 +1213,7 @@ def set_forbidden_view(self, view=None, attr=None, renderer=None,
The ``wrapper`` argument should be the name of another view
which will wrap this view when rendered (see the ``add_view``
method's ``wrapper`` argument for a description)."""
- if isinstance(renderer, basestring):
+ if isinstance(renderer, string_types):
renderer = renderers.RendererHelper(
name=renderer, package=self.package,
registry = self.registry)
@@ -1248,7 +1255,7 @@ def set_notfound_view(self, view=None, attr=None, renderer=None,
which will wrap this view when rendered (see the ``add_view``
method's ``wrapper`` argument for a description).
"""
- if isinstance(renderer, basestring):
+ if isinstance(renderer, string_types):
renderer = renderers.RendererHelper(
name=renderer, package=self.package,
registry=self.registry)
@@ -1404,8 +1411,8 @@ def isexception(o):
)
+@implementer(IStaticURLInfo)
class StaticURLInfo(object):
- implements(IStaticURLInfo)
def _get_registrations(self, registry):
try:
@@ -1449,7 +1456,7 @@ def add(self, config, name, spec, **extra):
# make sure it ends with a slash
name = name + '/'
- if urlparse(name)[0]:
+ if url_parse(name)[0]:
# it's a URL
# url, spec, route_name
url = name
diff --git a/pyramid/encode.py b/pyramid/encode.py
index 826e6a6620..a259d1414f 100644
--- a/pyramid/encode.py
+++ b/pyramid/encode.py
@@ -1,58 +1,11 @@
-import re
+from pyramid.compat import text_type
+from pyramid.compat import binary_type
+from pyramid.compat import is_nonstr_iter
+from pyramid.compat import url_quote as _url_quote
+from pyramid.compat import url_quote_plus as quote_plus # bw compat api (dnr)
-always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- 'abcdefghijklmnopqrstuvwxyz'
- '0123456789' '_.-')
-_safemaps = {}
-_must_quote = {}
-
-def url_quote(s, safe=''):
- """quote('abc def') -> 'abc%20def'
-
- Each part of a URL, e.g. the path info, the query, etc., has a
- different set of reserved characters that must be quoted.
-
- RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax lists
- the following reserved characters::
-
- reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
- "$" | ","
-
- Each of these characters is reserved in some component of a URL,
- but not necessarily in all of them.
-
- Unlike the default version of this function in the Python stdlib, by
- default, the url_quote function is intended for quoting individual path
- segments instead of an already composed path that might have ``/``
- characters in it. Thus, it *will* encode any ``/`` character it finds in a
- string unless ``/`` is marked as 'safe'. It is also slightly faster than
- the stdlib version.
- """
- cachekey = (safe, always_safe)
- try:
- safe_map = _safemaps[cachekey]
- if not _must_quote[cachekey].search(s):
- return s
- except KeyError:
- safe += always_safe
- _must_quote[cachekey] = re.compile(r'[^%s]' % safe)
- safe_map = {}
- for i in range(256):
- c = chr(i)
- if c in safe:
- safe_map[c] = c
- else:
- safe_map[c] = '%%%02X' % i
- _safemaps[cachekey] = safe_map
- res = map(safe_map.__getitem__, s)
- return ''.join(res)
-
-def quote_plus(s, safe=''):
- """ Version of stdlib quote_plus which uses faster url_quote """
- if ' ' in s:
- s = url_quote(s, safe + ' ')
- return s.replace(' ', '+')
- return url_quote(s, safe)
+def url_quote(s, safe=''): # bw compat api
+ return _url_quote(s, safe=safe)
def urlencode(query, doseq=True):
"""
@@ -88,21 +41,26 @@ def urlencode(query, doseq=True):
prefix = ''
for (k, v) in query:
- if k.__class__ is unicode:
- k = k.encode('utf-8')
- k = quote_plus(str(k))
- if hasattr(v, '__iter__'):
+ k = _enc(k)
+
+ if is_nonstr_iter(v):
for x in v:
- if x.__class__ is unicode:
- x = x.encode('utf-8')
- x = quote_plus(str(x))
+ x = _enc(x)
result += '%s%s=%s' % (prefix, k, x)
prefix = '&'
else:
- if v.__class__ is unicode:
- v = v.encode('utf-8')
- v = quote_plus(str(v))
+ v = _enc(v)
result += '%s%s=%s' % (prefix, k, v)
+
prefix = '&'
return result
+
+def _enc(val):
+ cls = val.__class__
+ if cls is text_type:
+ val = val.encode('utf-8')
+ elif cls is not binary_type:
+ val = str(val).encode('utf-8')
+ return quote_plus(val)
+
diff --git a/pyramid/events.py b/pyramid/events.py
index 9473d6330f..a495d9c292 100644
--- a/pyramid/events.py
+++ b/pyramid/events.py
@@ -1,6 +1,6 @@
import venusian
-from zope.interface import implements
+from zope.interface import implementer
from pyramid.interfaces import IContextFound
from pyramid.interfaces import INewRequest
@@ -71,16 +71,17 @@ def __call__(self, wrapped):
self.venusian.attach(wrapped, self.register, category='pyramid')
return wrapped
+@implementer(INewRequest)
class NewRequest(object):
""" An instance of this class is emitted as an :term:`event`
whenever :app:`Pyramid` begins to process a new request. The
even instance has an attribute, ``request``, which is a
:term:`request` object. This event class implements the
:class:`pyramid.interfaces.INewRequest` interface."""
- implements(INewRequest)
def __init__(self, request):
self.request = request
+@implementer(INewResponse)
class NewResponse(object):
""" An instance of this class is emitted as an :term:`event`
whenever any :app:`Pyramid` :term:`view` or :term:`exception
@@ -112,11 +113,11 @@ class NewResponse(object):
almost purely for symmetry with the
:class:`pyramid.interfaces.INewRequest` event.
"""
- implements(INewResponse)
def __init__(self, request, response):
self.request = request
self.response = response
+@implementer(IContextFound)
class ContextFound(object):
""" An instance of this class is emitted as an :term:`event` after
the :app:`Pyramid` :term:`router` finds a :term:`context`
@@ -137,13 +138,13 @@ class ContextFound(object):
As of :app:`Pyramid` 1.0, for backwards compatibility purposes, this
event may also be imported as :class:`pyramid.events.AfterTraversal`.
"""
- implements(IContextFound)
def __init__(self, request):
self.request = request
AfterTraversal = ContextFound # b/c as of 1.0
-class ApplicationCreated(object):
+@implementer(IApplicationCreated)
+class ApplicationCreated(object):
""" An instance of this class is emitted as an :term:`event` when
the :meth:`pyramid.config.Configurator.make_wsgi_app` is
called. The instance has an attribute, ``app``, which is an
@@ -157,13 +158,13 @@ class ApplicationCreated(object):
:class:`pyramid.events.WSGIApplicationCreatedEvent`. This was the name
of the event class before :app:`Pyramid` 1.0.
"""
- implements(IApplicationCreated)
def __init__(self, app):
self.app = app
self.object = app
WSGIApplicationCreatedEvent = ApplicationCreated # b/c (as of 1.0)
+@implementer(IBeforeRender)
class BeforeRender(dict):
"""
Subscribers to this event may introspect the and modify the set of
@@ -201,7 +202,6 @@ def add_global(event):
See also :class:`pyramid.interfaces.IBeforeRender`.
"""
- implements(IBeforeRender)
def __init__(self, system, rendering_val=None):
dict.__init__(self, system)
self.rendering_val = rendering_val
diff --git a/pyramid/exceptions.py b/pyramid/exceptions.py
index cafdb93f06..ff598fe2d5 100644
--- a/pyramid/exceptions.py
+++ b/pyramid/exceptions.py
@@ -4,6 +4,8 @@
NotFound = HTTPNotFound # bw compat
Forbidden = HTTPForbidden # bw compat
+CR = '\n'
+
class PredicateMismatch(HTTPNotFound):
"""
Internal exception (not an API) raised by multiviews when no
@@ -37,15 +39,14 @@ def __init__(self, conflicts):
def __str__(self):
r = ["Conflicting configuration actions"]
- items = self._conflicts.items()
- items.sort()
+ items = sorted(self._conflicts.items())
for discriminator, infos in items:
r.append(" For: %s" % (discriminator, ))
for info in infos:
- for line in unicode(info).rstrip().split(u'\n'):
- r.append(u" "+line)
+ for line in str(info).rstrip().split(CR):
+ r.append(" "+line)
- return "\n".join(r)
+ return CR.join(r)
class ConfigurationExecutionError(ConfigurationError):
diff --git a/pyramid/fixers/fix_bfg_imports.py b/pyramid/fixers/fix_bfg_imports.py
index 8fd32a797b..d9a4a6dfbd 100644
--- a/pyramid/fixers/fix_bfg_imports.py
+++ b/pyramid/fixers/fix_bfg_imports.py
@@ -199,6 +199,7 @@ def fix_zcml(path):
newf.write(newt)
newf.flush()
newf.close()
+ text.close()
for dir in dirs:
if dir.startswith('.'):
diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py
index 0887b0eec4..4dbca7021d 100644
--- a/pyramid/httpexceptions.py
+++ b/pyramid/httpexceptions.py
@@ -122,31 +122,36 @@
``location``, which indicates the location to which to redirect.
"""
-import types
from string import Template
-from zope.interface import implements
+from zope.interface import implementer
from webob import html_escape as _html_escape
from pyramid.interfaces import IExceptionResponse
from pyramid.response import Response
+from pyramid.compat import class_types
+from pyramid.compat import text_type
+from pyramid.compat import binary_type
+from pyramid.compat import text_
def _no_escape(value):
if value is None:
return ''
- if not isinstance(value, basestring):
+ if not isinstance(value, text_type):
if hasattr(value, '__unicode__'):
- value = unicode(value)
+ value = value.__unicode__()
+ if isinstance(value, binary_type):
+ value = text_(value, 'utf-8')
else:
- value = str(value)
+ value = text_type(value)
return value
class HTTPException(Exception): # bw compat
""" Base class for all :term:`exception response` objects."""
+@implementer(IExceptionResponse)
class WSGIHTTPException(Response, HTTPException):
- implements(IExceptionResponse)
## You should set in subclasses:
# code = 200
@@ -259,7 +264,7 @@ def prepare(self, environ):
args[k.lower()] = escape(v)
body = body_tmpl.substitute(args)
page = page_template.substitute(status=self.status, body=body)
- if isinstance(page, unicode):
+ if isinstance(page, text_type):
page = page.encode(self.charset)
self.app_iter = [page]
self.body = page
@@ -1016,8 +1021,8 @@ def default_exceptionresponse_view(context, request):
status_map={}
code = None
-for name, value in globals().items():
- if (isinstance(value, (type, types.ClassType)) and
+for name, value in list(globals().items()):
+ if (isinstance(value, class_types) and
issubclass(value, HTTPException)
and not name.startswith('_')):
code = getattr(value, 'code', None)
diff --git a/pyramid/i18n.py b/pyramid/i18n.py
index f16aeb3789..889227130f 100644
--- a/pyramid/i18n.py
+++ b/pyramid/i18n.py
@@ -9,6 +9,7 @@
TranslationString = TranslationString # PyFlakes
TranslationStringFactory = TranslationStringFactory # PyFlakes
+from pyramid.compat import PY3
from pyramid.interfaces import ILocalizer
from pyramid.interfaces import ITranslationDirectories
from pyramid.interfaces import ILocaleNegotiator
@@ -180,10 +181,10 @@ def make_localizer(current_locale_name, translation_directories):
mopath = os.path.realpath(os.path.join(messages_dir,
mofile))
if mofile.endswith('.mo') and os.path.isfile(mopath):
- mofp = open(mopath, 'rb')
- domain = mofile[:-3]
- dtrans = Translations(mofp, domain)
- translations.add(dtrans)
+ with open(mopath, 'rb') as mofp:
+ domain = mofile[:-3]
+ dtrans = Translations(mofp, domain)
+ translations.add(dtrans)
return Localizer(locale_name=current_locale_name,
translations=translations)
@@ -231,7 +232,7 @@ def __init__(self, fileobj=None, domain=DEFAULT_DOMAIN):
# this domain; see https://github.com/Pylons/pyramid/issues/235
self.plural = lambda n: int(n != 1)
gettext.GNUTranslations.__init__(self, fp=fileobj)
- self.files = filter(None, [getattr(fileobj, 'name', None)])
+ self.files = list(filter(None, [getattr(fileobj, 'name', None)]))
self.domain = domain
self._domains = {}
@@ -257,7 +258,8 @@ def load(cls, dirname=None, locales=None, domain=DEFAULT_DOMAIN):
filename = gettext.find(domain, dirname, locales)
if not filename:
return gettext.NullTranslations()
- return cls(fileobj=open(filename, 'rb'), domain=domain)
+ with open(filename, 'rb') as fp:
+ return cls(fileobj=fp, domain=domain)
def __repr__(self):
return '<%s: "%s">' % (type(self).__name__,
@@ -327,7 +329,10 @@ def dugettext(self, domain, message):
"""Like ``ugettext()``, but look the message up in the specified
domain.
"""
- return self._domains.get(domain, self).ugettext(message)
+ if PY3: # pragma: no cover
+ return self._domains.get(domain, self).gettext(message)
+ else: # pragma: no cover
+ return self._domains.get(domain, self).ugettext(message)
def dngettext(self, domain, singular, plural, num):
"""Like ``ngettext()``, but look the message up in the specified
@@ -345,5 +350,10 @@ def dungettext(self, domain, singular, plural, num):
"""Like ``ungettext()`` but look the message up in the specified
domain.
"""
- return self._domains.get(domain, self).ungettext(singular, plural, num)
+ if PY3: # pragma: no cover
+ return self._domains.get(domain, self).ngettext(
+ singular, plural, num)
+ else: # pragma: no cover
+ return self._domains.get(domain, self).ungettext(
+ singular, plural, num)
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index d90f56b75d..f08bd5fbb6 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -1,6 +1,8 @@
from zope.interface import Attribute
from zope.interface import Interface
+from pyramid.compat import PY3
+
# public API interfaces
class IContextFound(Interface):
@@ -46,7 +48,7 @@ class IApplicationCreated(Interface):
versions before 1.0, this interface can also be imported as
:class:`pyramid.interfaces.IWSGIApplicationCreatedEvent`.
"""
- app = Attribute(u"Created application")
+ app = Attribute("Created application")
IWSGIApplicationCreatedEvent = IApplicationCreated # b /c
@@ -296,25 +298,27 @@ def get(k, default=None):
""" Return the value for key ``k`` from the renderer dictionary, or
the default if no such value exists."""
- has_key = __contains__
-
def items():
""" Return a list of [(k,v)] pairs from the dictionary """
- def iteritems():
- """ Return an iterator of (k,v) pairs from the dictionary """
-
def keys():
""" Return a list of keys from the dictionary """
- def iterkeys():
- """ Return an iterator of keys from the dictionary """
-
def values():
""" Return a list of values from the dictionary """
- def itervalues():
- """ Return an iterator of values from the dictionary """
+ if not PY3:
+
+ def iterkeys():
+ """ Return an iterator of keys from the dictionary """
+
+ def iteritems():
+ """ Return an iterator of (k,v) pairs from the dictionary """
+
+ def itervalues():
+ """ Return an iterator of values from the dictionary """
+
+ has_key = __contains__
def pop(k, default=None):
""" Pop the key k from the dictionary and return its value. If k
diff --git a/pyramid/mako_templating.py b/pyramid/mako_templating.py
index c79de72173..29be339f23 100644
--- a/pyramid/mako_templating.py
+++ b/pyramid/mako_templating.py
@@ -2,11 +2,12 @@
import sys
import threading
-from zope.interface import implements
+from zope.interface import implementer
from zope.interface import Interface
from pyramid.asset import resolve_asset_spec
from pyramid.asset import abspath_from_asset_spec
+from pyramid.compat import is_nonstr_iter
from pyramid.exceptions import ConfigurationError
from pyramid.interfaces import ITemplateRenderer
from pyramid.settings import asbool
@@ -74,8 +75,8 @@ def renderer_factory(info):
if directories is None:
raise ConfigurationError(
'Mako template used without a ``mako.directories`` setting')
- if not hasattr(directories, '__iter__'):
- directories = filter(None, directories.splitlines())
+ if not is_nonstr_iter(directories):
+ directories = list(filter(None, directories.splitlines()))
directories = [ abspath_from_asset_spec(d) for d in directories ]
if module_directory is not None:
module_directory = abspath_from_asset_spec(module_directory)
@@ -83,11 +84,12 @@ def renderer_factory(info):
dotted = DottedNameResolver(info.package)
error_handler = dotted.maybe_resolve(error_handler)
if default_filters is not None:
- if not hasattr(default_filters, '__iter__'):
- default_filters = filter(None, default_filters.splitlines())
+ if not is_nonstr_iter(default_filters):
+ default_filters = list(filter(
+ None, default_filters.splitlines()))
if imports is not None:
- if not hasattr(imports, '__iter__'):
- imports = filter(None, imports.splitlines())
+ if not is_nonstr_iter(imports):
+ imports = list(filter(None, imports.splitlines()))
strict_undefined = asbool(strict_undefined)
if preprocessor is not None:
dotted = DottedNameResolver(info.package)
@@ -120,8 +122,8 @@ def __repr__(self):
__str__ = __repr__
+@implementer(ITemplateRenderer)
class MakoLookupTemplateRenderer(object):
- implements(ITemplateRenderer)
def __init__(self, path, lookup):
self.path = path
self.lookup = lookup
diff --git a/pyramid/paster.py b/pyramid/paster.py
index bdf7df1090..c9c356a92f 100644
--- a/pyramid/paster.py
+++ b/pyramid/paster.py
@@ -1,19 +1,28 @@
-import ConfigParser
import os
import sys
from code import interact
import zope.deprecation
-from paste.deploy import loadapp
-from paste.script.command import Command
+try:
+ from paste.deploy import loadapp
+except ImportError: # pragma: no cover
+ def loadapp(*arg, **kw):
+ raise NotImplementedError
+
+try:
+ from paste.script.command import Command
+except ImportError: # pragma: no cover
+ class Command:
+ pass
from pyramid.interfaces import IMultiView
from pyramid.interfaces import ITweens
+from pyramid.compat import print_
+from pyramid.compat import configparser
from pyramid.scripting import prepare
from pyramid.util import DottedNameResolver
-
from pyramid.tweens import MAIN
from pyramid.tweens import INGRESS
@@ -138,7 +147,7 @@ class PShellCommand(PCommand):
"option will override the 'setup' key in the "
"[pshell] ini section."))
- ConfigParser = ConfigParser.ConfigParser # testing
+ ConfigParser = configparser.ConfigParser # testing
loaded_objects = {}
object_help = {}
@@ -149,7 +158,7 @@ def pshell_file_config(self, filename):
config.read(filename)
try:
items = config.items('pshell')
- except ConfigParser.NoSectionError:
+ except configparser.NoSectionError:
return
resolver = DottedNameResolver(None)
@@ -301,8 +310,8 @@ def _get_mapper(self, registry):
return config.get_routes_mapper()
def out(self, msg): # pragma: no cover
- print msg
-
+ print_(msg)
+
def command(self):
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IViewClassifier
@@ -360,8 +369,8 @@ class PViewsCommand(PCommand):
parser = Command.standard_parser(simulate=True)
def out(self, msg): # pragma: no cover
- print msg
-
+ print_(msg)
+
def _find_multi_routes(self, mapper, request):
infos = []
path = request.environ['PATH_INFO']
@@ -380,7 +389,7 @@ def _find_view(self, url, registry):
configuration` within the application registry; return the view.
"""
from zope.interface import providedBy
- from zope.interface import implements
+ from zope.interface import implementer
from pyramid.interfaces import IRequest
from pyramid.interfaces import IRootFactory
from pyramid.interfaces import IRouteRequest
@@ -401,8 +410,8 @@ def _find_view(self, url, registry):
adapters = registry.adapters
request = None
+ @implementer(IMultiView)
class RoutesMultiView(object):
- implements(IMultiView)
def __init__(self, infos, context_iface, root_factory, request):
self.views = []
@@ -606,7 +615,7 @@ def _get_tweens(self, registry):
return config.registry.queryUtility(ITweens)
def out(self, msg): # pragma: no cover
- print msg
+ print_(msg)
def show_chain(self, chain):
fmt = '%-10s %-65s'
diff --git a/pyramid/registry.py b/pyramid/registry.py
index 6b287e4f19..ac706595ef 100644
--- a/pyramid/registry.py
+++ b/pyramid/registry.py
@@ -1,7 +1,10 @@
-from zope.component.registry import Components
+from zope.interface.registry import Components
+from pyramid.compat import text_
from pyramid.interfaces import ISettings
+empty = text_('')
+
class Registry(Components, dict):
""" A registry object is an :term:`application registry`. It is used by
the framework itself to perform mappings of URLs to view callables, as
@@ -34,8 +37,8 @@ def registerSubscriptionAdapter(self, *arg, **kw):
self.has_listeners = True
return result
- def registerSelfAdapter(self, required=None, provided=None, name=u'',
- info=u'', event=True):
+ def registerSelfAdapter(self, required=None, provided=None, name=empty,
+ info=empty, event=True):
# registerAdapter analogue which always returns the object itself
# when required is matched
return self.registerAdapter(lambda x: x, required=required,
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index 2efe0f1231..88cb869a50 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -2,7 +2,7 @@
import pkg_resources
import threading
-from zope.interface import implements
+from zope.interface import implementer
from zope.deprecation import deprecated
from pyramid.interfaces import IChameleonLookup
@@ -15,6 +15,9 @@
from pyramid.asset import asset_spec_from_abspath
from pyramid.compat import json
+from pyramid.compat import string_types
+from pyramid.compat import text_type
+from pyramid.compat import native_
from pyramid.decorator import reify
from pyramid.events import BeforeRender
from pyramid.path import caller_package
@@ -145,7 +148,7 @@ def _render(value, system):
def string_renderer_factory(info):
def _render(value, system):
- if not isinstance(value, basestring):
+ if not isinstance(value, string_types):
value = str(value)
request = system.get('request')
if request is not None:
@@ -225,8 +228,8 @@ def _render(value, system):
# utility functions, not API
+@implementer(IChameleonLookup)
class ChameleonRendererLookup(object):
- implements(IChameleonLookup)
def __init__(self, impl, registry):
self.impl = impl
self.registry = registry
@@ -348,8 +351,8 @@ def renderer_from_name(path, package=None):
'the next major release. To replace it, use the '
'``pyramid.renderers.get_renderer`` API instead. ')
+@implementer(IRendererInfo)
class RendererHelper(object):
- implements(IRendererInfo)
def __init__(self, name=None, package=None, registry=None):
if name and '.' in name:
rtype = os.path.splitext(name)[1]
@@ -437,8 +440,8 @@ def _make_response(self, result, request):
if result is None:
result = ''
- if isinstance(result, unicode):
- response.unicode_body = result
+ if isinstance(result, text_type):
+ response.text = result
else:
response.body = result
diff --git a/pyramid/request.py b/pyramid/request.py
index 9704a42a4d..eae83da6f3 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -1,6 +1,6 @@
from zope.deprecation import deprecate
from zope.deprecation.deprecation import deprecated
-from zope.interface import implements
+from zope.interface import implementer
from zope.interface.interface import InterfaceClass
from webob import BaseRequest
@@ -11,6 +11,10 @@
from pyramid.interfaces import IResponseFactory
from pyramid.compat import json
+from pyramid.compat import iterkeys_, itervalues_, iteritems_
+from pyramid.compat import text_
+from pyramid.compat import bytes_
+from pyramid.compat import native_
from pyramid.exceptions import ConfigurationError
from pyramid.decorator import reify
from pyramid.response import Response
@@ -64,15 +68,15 @@ def items(self):
@deprecate(dictlike)
def iteritems(self):
- return self.environ.iteritems()
+ return iteritems_(self.environ)
@deprecate(dictlike)
def iterkeys(self):
- return self.environ.iterkeys()
+ return iterkeys_(self.environ)
@deprecate(dictlike)
def itervalues(self):
- return self.environ.itervalues()
+ return itervalues_(self.environ)
@deprecate(dictlike)
def keys(self):
@@ -283,6 +287,7 @@ def _process_finished_callbacks(self):
callback = callbacks.pop(0)
callback(self)
+@implementer(IRequest)
class Request(BaseRequest, DeprecatedRequestMethodsMixin, URLMethodsMixin,
CallbackMethodsMixin):
"""
@@ -305,7 +310,6 @@ class Request(BaseRequest, DeprecatedRequestMethodsMixin, URLMethodsMixin,
release of this :app:`Pyramid` version. See
http://pythonpaste.org/webob/ for further information.
"""
- implements(IRequest)
exception = None
exc_info = None
matchdict = None
@@ -360,7 +364,7 @@ def is_response(self, ob):
@property
def json_body(self):
- return json.loads(self.body, encoding=self.charset)
+ return json.loads(text_(self.body, self.charset))
def route_request_iface(name, bases=()):
# zope.interface treats the __name__ as the __doc__ and changes __name__
@@ -404,7 +408,8 @@ def call_app_with_subpath_as_path_info(request, app):
new_script_name = ''
# compute new_path_info
- new_path_info = '/' + '/'.join([x.encode('utf-8') for x in subpath])
+ new_path_info = '/' + '/'.join([native_(x.encode('utf-8'), 'latin-1')
+ for x in subpath])
if new_path_info != '/': # don't want a sole double-slash
if path_info != '/': # if orig path_info is '/', we're already done
@@ -422,7 +427,7 @@ def call_app_with_subpath_as_path_info(request, app):
break
el = workback.pop()
if el:
- tmp.insert(0, el.decode('utf-8'))
+ tmp.insert(0, text_(bytes_(el, 'latin-1'), 'utf-8'))
# strip all trailing slashes from workback to avoid appending undue slashes
# to end of script_name
diff --git a/pyramid/resource.py b/pyramid/resource.py
index 5e8f3c9686..986c75e370 100644
--- a/pyramid/resource.py
+++ b/pyramid/resource.py
@@ -1,5 +1,5 @@
""" Backwards compatibility shim module (forever). """
-from asset import * # b/w compat
+from pyramid.asset import * # b/w compat
resolve_resource_spec = resolve_asset_spec
resource_spec_from_abspath = asset_spec_from_abspath
abspath_from_resource_spec = abspath_from_asset_spec
diff --git a/pyramid/response.py b/pyramid/response.py
index db53de0c36..b0c9652964 100644
--- a/pyramid/response.py
+++ b/pyramid/response.py
@@ -1,12 +1,12 @@
import venusian
from webob import Response as _Response
-from zope.interface import implements
+from zope.interface import implementer
from pyramid.interfaces import IResponse
+@implementer(IResponse)
class Response(_Response):
- implements(IResponse)
-
+ pass
class response_adapter(object):
""" Decorator activated via a :term:`scan` which treats the function
diff --git a/pyramid/router.py b/pyramid/router.py
index 746cf88cf5..fb309eb03b 100644
--- a/pyramid/router.py
+++ b/pyramid/router.py
@@ -1,4 +1,4 @@
-from zope.interface import implements
+from zope.interface import implementer
from zope.interface import providedBy
from pyramid.interfaces import IDebugLogger
@@ -23,8 +23,8 @@
from pyramid.traversal import ResourceTreeTraverser
from pyramid.tweens import excview_tween_factory
+@implementer(IRouter)
class Router(object):
- implements(IRouter)
debug_notfound = False
debug_routematch = False
diff --git a/pyramid/scaffolds/__init__.py b/pyramid/scaffolds/__init__.py
index a49bbd9f66..673f22e21e 100644
--- a/pyramid/scaffolds/__init__.py
+++ b/pyramid/scaffolds/__init__.py
@@ -1,7 +1,18 @@
import os
-from paste.script.templates import Template
-from paste.util.template import paste_script_template_renderer
+from pyramid.compat import print_
+
+try:
+ from paste.script.templates import Template
+except ImportError: # pragma: no cover
+ class Template:
+ pass
+
+try:
+ from paste.util.template import paste_script_template_renderer
+except ImportError: # pragma: no cover
+ def paste_script_template_renderer():
+ pass
class PyramidTemplate(Template):
def pre(self, command, output_dir, vars):
@@ -18,7 +29,7 @@ def post(self, command, output_dir, vars):
return Template.post(self, command, output_dir, vars)
def out(self, msg): # pragma: no cover (replaceable testing hook)
- print msg
+ print_(msg)
class StarterProjectTemplate(PyramidTemplate):
_template_dir = 'starter'
diff --git a/pyramid/scaffolds/tests.py b/pyramid/scaffolds/tests.py
index fe5d6957aa..7eb838e2f5 100644
--- a/pyramid/scaffolds/tests.py
+++ b/pyramid/scaffolds/tests.py
@@ -1,5 +1,4 @@
import sys
-import httplib
import os
import pkg_resources
import shutil
@@ -8,6 +7,11 @@
import time
import signal
+try:
+ import httplib
+except ImportError: # pragma: no cover
+ import http.client as httplib
+
class TemplateTest(object):
def make_venv(self, directory): # pragma: no cover
import virtualenv
diff --git a/pyramid/security.py b/pyramid/security.py
index 5aed7b5faf..a552b613ab 100644
--- a/pyramid/security.py
+++ b/pyramid/security.py
@@ -5,6 +5,7 @@
from pyramid.interfaces import ISecuredView
from pyramid.interfaces import IViewClassifier
+from pyramid.compat import map_
from pyramid.threadlocal import get_current_registry
Everyone = 'system.Everyone'
@@ -134,7 +135,7 @@ def view_execution_permitted(context, request, name=''):
reg = request.registry
except AttributeError:
reg = get_current_registry() # b/c
- provides = [IViewClassifier] + map(providedBy, (request, context))
+ provides = [IViewClassifier] + map_(providedBy, (request, context))
view = reg.adapters.lookup(provides, ISecuredView, name=name)
if view is None:
return Allowed(
diff --git a/pyramid/session.py b/pyramid/session.py
index bfa80ff102..a59f9c6289 100644
--- a/pyramid/session.py
+++ b/pyramid/session.py
@@ -1,9 +1,3 @@
-
-try:
- import cPickle as pickle
-except ImportError: # pragma: no cover
- import pickle
-
from hashlib import sha1
import base64
import binascii
@@ -11,8 +5,13 @@
import time
import os
-from zope.interface import implements
+from zope.interface import implementer
+from pyramid.compat import pickle
+from pyramid.compat import PY3
+from pyramid.compat import text_
+from pyramid.compat import bytes_
+from pyramid.compat import native_
from pyramid.interfaces import ISession
def manage_accessed(wrapped):
@@ -88,9 +87,9 @@ def UnencryptedCookieSessionFactoryConfig(
"""
+ @implementer(ISession)
class UnencryptedCookieSessionFactory(dict):
""" Dictionary-like session object """
- implements(ISession)
# configuration parameters
_cookie_name = cookie_name
@@ -144,16 +143,18 @@ def invalidate(self):
get = manage_accessed(dict.get)
__getitem__ = manage_accessed(dict.__getitem__)
items = manage_accessed(dict.items)
- iteritems = manage_accessed(dict.iteritems)
values = manage_accessed(dict.values)
- itervalues = manage_accessed(dict.itervalues)
keys = manage_accessed(dict.keys)
- iterkeys = manage_accessed(dict.iterkeys)
__contains__ = manage_accessed(dict.__contains__)
- has_key = manage_accessed(dict.has_key)
__len__ = manage_accessed(dict.__len__)
__iter__ = manage_accessed(dict.__iter__)
+ if not PY3:
+ iteritems = manage_accessed(dict.iteritems)
+ itervalues = manage_accessed(dict.itervalues)
+ iterkeys = manage_accessed(dict.iterkeys)
+ has_key = manage_accessed(dict.has_key)
+
# modifying dictionary methods
clear = manage_accessed(dict.clear)
update = manage_accessed(dict.update)
@@ -183,7 +184,7 @@ def peek_flash(self, queue=''):
# CSRF API methods
@manage_accessed
def new_csrf_token(self):
- token = os.urandom(20).encode('hex')
+ token = text_(binascii.hexlify(os.urandom(20)))
self['_csrft_'] = token
return token
@@ -235,8 +236,8 @@ def signed_serialize(data, secret):
response.set_cookie('signed_cookie', cookieval)
"""
pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
- sig = hmac.new(secret, pickled, sha1).hexdigest()
- return sig + base64.standard_b64encode(pickled)
+ sig = hmac.new(bytes_(secret), pickled, sha1).hexdigest()
+ return sig + native_(base64.b64encode(pickled))
def signed_deserialize(serialized, secret, hmac=hmac):
""" Deserialize the value returned from ``signed_serialize``. If
@@ -254,12 +255,12 @@ def signed_deserialize(serialized, secret, hmac=hmac):
# hmac parameterized only for unit tests
try:
input_sig, pickled = (serialized[:40],
- base64.standard_b64decode(serialized[40:]))
- except (binascii.Error, TypeError), e:
+ base64.b64decode(bytes_(serialized[40:])))
+ except (binascii.Error, TypeError) as e:
# Badly formed data can make base64 die
raise ValueError('Badly formed base64 data: %s' % e)
- sig = hmac.new(secret, pickled, sha1).hexdigest()
+ sig = hmac.new(bytes_(secret), pickled, sha1).hexdigest()
if len(sig) != len(input_sig):
raise ValueError('Wrong signature length')
diff --git a/pyramid/settings.py b/pyramid/settings.py
index 3c376c4a91..de91042eb9 100644
--- a/pyramid/settings.py
+++ b/pyramid/settings.py
@@ -1,6 +1,7 @@
from zope.deprecation import deprecated
from pyramid.threadlocal import get_current_registry
+from pyramid.compat import string_types
def get_settings():
"""
@@ -39,9 +40,9 @@ def asbool(s):
return s.lower() in ('t', 'true', 'y', 'yes', 'on', '1')
def aslist_cronly(value):
- if isinstance(value, basestring):
+ if isinstance(value, string_types):
value = filter(None, [x.strip() for x in value.splitlines()])
- return value
+ return list(value)
def aslist(value):
values = aslist_cronly(value)
diff --git a/pyramid/static.py b/pyramid/static.py
index 17a74a16a3..50a8b918b5 100644
--- a/pyramid/static.py
+++ b/pyramid/static.py
@@ -15,11 +15,14 @@
from repoze.lru import lru_cache
from pyramid.asset import resolve_asset_spec
+from pyramid.compat import text_
from pyramid.httpexceptions import HTTPNotFound
from pyramid.httpexceptions import HTTPMovedPermanently
from pyramid.path import caller_package
from pyramid.response import Response
-from pyramid.traversal import traversal_path
+from pyramid.traversal import traversal_path_info
+
+slash = text_('/')
def init_mimetypes(mimetypes):
# this is a function so it can be unittested
@@ -72,6 +75,8 @@ def next(self):
raise StopIteration
return data
+ __next__ = next # py3
+
def close(self):
self.file.close()
@@ -107,8 +112,8 @@ class static_view(object):
``PATH_INFO`` when calling the underlying WSGI application which actually
serves the static files. If it is ``True``, the static application will
consider ``request.subpath`` as ``PATH_INFO`` input. If it is ``False``,
- the static application will consider request.path_info as ``PATH_INFO``
- input. By default, this is ``False``.
+ the static application will consider request.environ[``PATH_INFO``] as
+ ``PATH_INFO`` input. By default, this is ``False``.
.. note::
@@ -139,7 +144,7 @@ def __call__(self, context, request):
if self.use_subpath:
path_tuple = request.subpath
else:
- path_tuple = traversal_path(request.path_info)
+ path_tuple = traversal_path_info(request.environ['PATH_INFO'])
path = _secure_path(path_tuple)
@@ -194,6 +199,6 @@ def _secure_path(path_tuple):
return None
if any([_contains_slash(item) for item in path_tuple]):
return None
- encoded = u'/'.join(path_tuple) # will be unicode
+ encoded = slash.join(path_tuple) # will be unicode
return encoded
diff --git a/pyramid/testing.py b/pyramid/testing.py
index 07f5238685..89cd39a1bf 100644
--- a/pyramid/testing.py
+++ b/pyramid/testing.py
@@ -3,7 +3,7 @@
from zope.deprecation import deprecated
-from zope.interface import implements
+from zope.interface import implementer
from zope.interface import Interface
from zope.interface import alsoProvides
@@ -14,6 +14,9 @@
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import ISession
+from pyramid.compat import PY3
+from pyramid.compat import PYPY
+from pyramid.compat import class_types
from pyramid.config import Configurator
from pyramid.decorator import reify
from pyramid.httpexceptions import HTTPForbidden
@@ -28,6 +31,13 @@
from pyramid.request import CallbackMethodsMixin
from pyramid.url import URLMethodsMixin
+try:
+ import zope.component
+ zope.component
+ have_zca = True
+except ImportError: # pragma: no cover
+ have_zca = False
+
_marker = object()
def registerDummySecurityPolicy(userid=None, groupids=(), permissive=True):
@@ -590,8 +600,8 @@ def clone(self, __name__=_marker, __parent__=_marker, **kw):
DummyModel = DummyResource # b/w compat (forever)
+@implementer(ISession)
class DummySession(dict):
- implements(ISession)
created = None
new = True
def changed(self):
@@ -621,6 +631,7 @@ def new_csrf_token(self):
def get_csrf_token(self):
return self.get('_csrft_', None)
+@implementer(IRequest)
class DummyRequest(DeprecatedRequestMethodsMixin, URLMethodsMixin,
CallbackMethodsMixin):
""" A DummyRequest object (incompletely) imitates a :term:`request` object.
@@ -649,7 +660,6 @@ class DummyRequest(DeprecatedRequestMethodsMixin, URLMethodsMixin,
a Request, use the :class:`pyramid.request.Request` class itself rather
than this class while writing tests.
"""
- implements(IRequest)
method = 'GET'
application_url = 'http://example.com'
host = 'example.com:80'
@@ -801,7 +811,7 @@ def setUp(registry=None, request=None, hook_zca=True, autocommit=True,
# any existing renderer factory lookup system.
config.add_renderer(name, renderer)
config.commit()
- hook_zca and config.hook_zca()
+ have_zca and hook_zca and config.hook_zca()
config.begin(request=request)
return config
@@ -889,21 +899,25 @@ def __call__(self, *arg, **kw):
return self.response
def skip_on(*platforms):
+ skip = False
+ for platform in platforms:
+ if skip_on.os_name.startswith(platform):
+ skip = True
+ if platform == 'pypy' and PYPY: # pragma: no cover
+ skip = True
+ if platform == 'py3' and PY3: # pragma: no cover
+ skip = True
def decorator(func):
- def wrapper(*args, **kw):
- for platform in platforms:
- if skip_on.os_name.startswith(platform):
- return
- if platform == 'pypy' and skip_on.pypy: # pragma: no cover
+ if isinstance(func, class_types):
+ if skip: return None
+ else: return func
+ else:
+ def wrapper(*args, **kw):
+ if skip:
return
- return func(*args, **kw)
- wrapper.__name__ = func.__name__
- wrapper.__doc__ = func.__doc__
- return wrapper
+ return func(*args, **kw)
+ wrapper.__name__ = func.__name__
+ wrapper.__doc__ = func.__doc__
+ return wrapper
return decorator
skip_on.os_name = os.name # for testing
-try: # pragma: no cover
- import __pypy__
- skip_on.pypy = True
-except ImportError:
- skip_on.pypy = False
diff --git a/pyramid/tests/fixtures/helloworld.mak b/pyramid/tests/fixtures/helloworld.mak
index efcf791e83..25283a50dc 100644
--- a/pyramid/tests/fixtures/helloworld.mak
+++ b/pyramid/tests/fixtures/helloworld.mak
@@ -1,3 +1,3 @@
## -*- coding: utf-8 -*-
-<% a, b = 'foo', u'föö' %>
-Hello ${u'föö'}
+<%!from pyramid.compat import text_%><% a, b = 'foo', text_('föö', 'utf-8') %>
+Hello ${text_('föö', 'utf-8')}
diff --git a/pyramid/tests/fixtures/helloworld.mako b/pyramid/tests/fixtures/helloworld.mako
index efcf791e83..25283a50dc 100644
--- a/pyramid/tests/fixtures/helloworld.mako
+++ b/pyramid/tests/fixtures/helloworld.mako
@@ -1,3 +1,3 @@
## -*- coding: utf-8 -*-
-<% a, b = 'foo', u'föö' %>
-Hello ${u'föö'}
+<%!from pyramid.compat import text_%><% a, b = 'foo', text_('föö', 'utf-8') %>
+Hello ${text_('föö', 'utf-8')}
diff --git a/pyramid/tests/pkgs/exceptionviewapp/views.py b/pyramid/tests/pkgs/exceptionviewapp/views.py
index 1432618cfe..33b97671ee 100644
--- a/pyramid/tests/pkgs/exceptionviewapp/views.py
+++ b/pyramid/tests/pkgs/exceptionviewapp/views.py
@@ -1,5 +1,5 @@
from webob import Response
-from models import AnException
+from .models import AnException
def no(request):
return Response('no')
diff --git a/pyramid/tests/pkgs/fixtureapp/__init__.py b/pyramid/tests/pkgs/fixtureapp/__init__.py
index c74747bfd4..27063aae29 100644
--- a/pyramid/tests/pkgs/fixtureapp/__init__.py
+++ b/pyramid/tests/pkgs/fixtureapp/__init__.py
@@ -5,7 +5,7 @@ def includeme(config):
config.add_view('.views.erroneous_view', name='error.html')
config.add_view('.views.fixture_view', name='dummyskin.html',
request_type='.views.IDummy')
- from models import fixture, IFixture
+ from .models import fixture, IFixture
config.registry.registerUtility(fixture, IFixture)
config.add_view('.views.fixture_view', name='another.html')
diff --git a/pyramid/tests/pkgs/forbiddenapp/__init__.py b/pyramid/tests/pkgs/forbiddenapp/__init__.py
index 7001b87f59..888dc93173 100644
--- a/pyramid/tests/pkgs/forbiddenapp/__init__.py
+++ b/pyramid/tests/pkgs/forbiddenapp/__init__.py
@@ -1,5 +1,6 @@
from webob import Response
from pyramid.httpexceptions import HTTPForbidden
+from pyramid.compat import bytes_
def x_view(request): # pragma: no cover
return Response('this is private!')
@@ -9,7 +10,7 @@ def forbidden_view(context, request):
result = context.result
message = msg + '\n' + str(result)
resp = HTTPForbidden()
- resp.body = message
+ resp.body = bytes_(message)
return resp
def includeme(config):
diff --git a/pyramid/tests/pkgs/permbugapp/__init__.py b/pyramid/tests/pkgs/permbugapp/__init__.py
index 10a244f3bd..330d983ab3 100644
--- a/pyramid/tests/pkgs/permbugapp/__init__.py
+++ b/pyramid/tests/pkgs/permbugapp/__init__.py
@@ -1,6 +1,6 @@
-from cgi import escape
+from pyramid.compat import escape
from pyramid.security import view_execution_permitted
-from webob import Response
+from pyramid.response import Response
def x_view(request): # pragma: no cover
return Response('this is private!')
diff --git a/pyramid/tests/pkgs/wsgiapp2app/__init__.py b/pyramid/tests/pkgs/wsgiapp2app/__init__.py
index 0880556efb..e2024198ec 100644
--- a/pyramid/tests/pkgs/wsgiapp2app/__init__.py
+++ b/pyramid/tests/pkgs/wsgiapp2app/__init__.py
@@ -8,7 +8,7 @@ def hello(environ, start_response):
assert environ['SCRIPT_NAME'] == '/hello'
response_headers = [('Content-Type', 'text/plain')]
start_response('200 OK', response_headers)
- return ['Hello!']
+ return [b'Hello!']
def main():
from pyramid.config import Configurator
diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py
index ff96ae4710..b612c57897 100644
--- a/pyramid/tests/test_authentication.py
+++ b/pyramid/tests/test_authentication.py
@@ -1,5 +1,7 @@
import unittest
from pyramid import testing
+from pyramid.compat import text_
+from pyramid.compat import bytes_
class TestCallbackAuthenticationPolicyDebugging(unittest.TestCase):
def setUp(self):
@@ -462,7 +464,7 @@ def _parseHeader(self, header):
return cookie
def _parseCookie(self, cookie):
- from Cookie import SimpleCookie
+ from pyramid.compat import SimpleCookie
cookies = SimpleCookie()
cookies.load(cookie)
return cookies.get('auth_tkt')
@@ -562,14 +564,15 @@ def test_identify_good_cookie_unknown_useridtype(self):
self.assertEqual(environ['AUTH_TYPE'],'cookie')
def test_identify_good_cookie_b64str_useridtype(self):
+ from base64 import b64encode
helper = self._makeOne('secret', include_ip=False)
- helper.auth_tkt.userid = 'encoded'.encode('base64').strip()
+ helper.auth_tkt.userid = b64encode(b'encoded').strip()
helper.auth_tkt.user_data = 'userid_type:b64str'
request = self._makeRequest('ticket')
result = helper.identify(request)
self.assertEqual(len(result), 4)
self.assertEqual(result['tokens'], ())
- self.assertEqual(result['userid'], 'encoded')
+ self.assertEqual(result['userid'], b'encoded')
self.assertEqual(result['userdata'], 'userid_type:b64str')
self.assertEqual(result['timestamp'], 0)
environ = request.environ
@@ -578,14 +581,15 @@ def test_identify_good_cookie_b64str_useridtype(self):
self.assertEqual(environ['AUTH_TYPE'],'cookie')
def test_identify_good_cookie_b64unicode_useridtype(self):
+ from base64 import b64encode
helper = self._makeOne('secret', include_ip=False)
- helper.auth_tkt.userid = '\xc3\xa9ncoded'.encode('base64').strip()
+ helper.auth_tkt.userid = b64encode(b'\xc3\xa9ncoded').strip()
helper.auth_tkt.user_data = 'userid_type:b64unicode'
request = self._makeRequest('ticket')
result = helper.identify(request)
self.assertEqual(len(result), 4)
self.assertEqual(result['tokens'], ())
- self.assertEqual(result['userid'], unicode('\xc3\xa9ncoded', 'utf-8'))
+ self.assertEqual(result['userid'], text_(b'\xc3\xa9ncoded', 'utf-8'))
self.assertEqual(result['userdata'], 'userid_type:b64unicode')
self.assertEqual(result['timestamp'], 0)
environ = request.environ
@@ -822,14 +826,16 @@ def test_remember_domain_has_port(self):
self.assertTrue(result[1][1].endswith('; Path=/; Domain=example.com'))
self.assertTrue(result[1][1].startswith('auth_tkt='))
- def test_remember_string_userid(self):
+ def test_remember_binary_userid(self):
+ import base64
helper = self._makeOne('secret')
request = self._makeRequest()
- result = helper.remember(request, 'userid')
+ result = helper.remember(request, b'userid')
values = self._parseHeaders(result)
self.assertEqual(len(result), 3)
val = self._cookieValue(values[0])
- self.assertEqual(val['userid'], 'userid'.encode('base64').strip())
+ self.assertEqual(val['userid'],
+ bytes_(base64.b64encode(b'userid').strip()))
self.assertEqual(val['user_data'], 'userid_type:b64str')
def test_remember_int_userid(self):
@@ -843,6 +849,7 @@ def test_remember_int_userid(self):
self.assertEqual(val['user_data'], 'userid_type:int')
def test_remember_long_userid(self):
+ from pyramid.compat import long
helper = self._makeOne('secret')
request = self._makeRequest()
result = helper.remember(request, long(1))
@@ -853,15 +860,16 @@ def test_remember_long_userid(self):
self.assertEqual(val['user_data'], 'userid_type:int')
def test_remember_unicode_userid(self):
+ import base64
helper = self._makeOne('secret')
request = self._makeRequest()
- userid = unicode('\xc2\xa9', 'utf-8')
+ userid = text_(b'\xc2\xa9', 'utf-8')
result = helper.remember(request, userid)
values = self._parseHeaders(result)
self.assertEqual(len(result), 3)
val = self._cookieValue(values[0])
self.assertEqual(val['userid'],
- userid.encode('utf-8').encode('base64').strip())
+ base64.b64encode(userid.encode('utf-8')))
self.assertEqual(val['user_data'], 'userid_type:b64unicode')
def test_remember_insane_userid(self):
@@ -899,11 +907,12 @@ def test_remember_tokens(self):
self.assertEqual(result[2][0], 'Set-Cookie')
self.assertTrue("'tokens': ('foo', 'bar')" in result[2][1])
- def test_remember_non_string_token(self):
+ def test_remember_nonascii_token(self):
helper = self._makeOne('secret')
request = self._makeRequest()
+ la = text_(b'La Pe\xc3\xb1a', 'utf-8')
self.assertRaises(ValueError, helper.remember, request, 'other',
- tokens=(u'foo',))
+ tokens=(la,))
def test_remember_invalid_token_format(self):
helper = self._makeOne('secret')
@@ -1086,15 +1095,6 @@ def test_forget_no_identity(self):
self.assertEqual(request.session.get('userid'), None)
self.assertEqual(result, [])
-class Test_maybe_encode(unittest.TestCase):
- def _callFUT(self, s, encoding='utf-8'):
- from pyramid.authentication import maybe_encode
- return maybe_encode(s, encoding)
-
- def test_unicode(self):
- result = self._callFUT(u'abc')
- self.assertEqual(result, 'abc')
-
class DummyContext:
pass
diff --git a/pyramid/tests/test_chameleon_text.py b/pyramid/tests/test_chameleon_text.py
index 213f25f516..8d23c8753c 100644
--- a/pyramid/tests/test_chameleon_text.py
+++ b/pyramid/tests/test_chameleon_text.py
@@ -1,5 +1,6 @@
import unittest
+from pyramid.compat import binary_type
from pyramid.testing import skip_on
from pyramid import testing
@@ -98,8 +99,8 @@ def test_call(self):
lookup = DummyLookup()
instance = self._makeOne(minimal, lookup)
result = instance({}, {})
- self.assertTrue(isinstance(result, str))
- self.assertEqual(result, 'Hello.\n')
+ self.assertTrue(isinstance(result, binary_type))
+ self.assertEqual(result, b'Hello.\n')
@skip_on('java')
def test_call_with_nondict_value(self):
@@ -114,8 +115,8 @@ def test_call_nonminimal(self):
lookup = DummyLookup()
instance = self._makeOne(nonminimal, lookup)
result = instance({'name':'Chris'}, {})
- self.assertTrue(isinstance(result, str))
- self.assertEqual(result, 'Hello, Chris!\n')
+ self.assertTrue(isinstance(result, binary_type))
+ self.assertEqual(result, b'Hello, Chris!\n')
@skip_on('java')
def test_implementation(self):
@@ -123,8 +124,8 @@ def test_implementation(self):
lookup = DummyLookup()
instance = self._makeOne(minimal, lookup)
result = instance.implementation()()
- self.assertTrue(isinstance(result, str))
- self.assertEqual(result, 'Hello.\n')
+ self.assertTrue(isinstance(result, binary_type))
+ self.assertEqual(result, b'Hello.\n')
class RenderTemplateTests(Base, unittest.TestCase):
def _callFUT(self, name, **kw):
@@ -135,8 +136,8 @@ def _callFUT(self, name, **kw):
def test_it(self):
minimal = self._getTemplatePath('minimal.txt')
result = self._callFUT(minimal)
- self.assertTrue(isinstance(result, str))
- self.assertEqual(result, 'Hello.\n')
+ self.assertTrue(isinstance(result, binary_type))
+ self.assertEqual(result, b'Hello.\n')
class RenderTemplateToResponseTests(Base, unittest.TestCase):
def _callFUT(self, name, **kw):
@@ -149,7 +150,7 @@ def test_minimal(self):
result = self._callFUT(minimal)
from webob import Response
self.assertTrue(isinstance(result, Response))
- self.assertEqual(result.app_iter, ['Hello.\n'])
+ self.assertEqual(result.app_iter, [b'Hello.\n'])
self.assertEqual(result.status, '200 OK')
self.assertEqual(len(result.headerlist), 2)
diff --git a/pyramid/tests/test_chameleon_zpt.py b/pyramid/tests/test_chameleon_zpt.py
index 84eaedcf49..1a8e6767e5 100644
--- a/pyramid/tests/test_chameleon_zpt.py
+++ b/pyramid/tests/test_chameleon_zpt.py
@@ -2,6 +2,7 @@
from pyramid.testing import skip_on
from pyramid import testing
+from pyramid.compat import text_type
class Base(object):
def setUp(self):
@@ -51,7 +52,7 @@ def test_call(self):
lookup = DummyLookup()
instance = self._makeOne(minimal, lookup)
result = instance({}, {})
- self.assertTrue(isinstance(result, unicode))
+ self.assertTrue(isinstance(result, text_type))
self.assertEqual(result.rstrip('\n'),
'
\n
')
@@ -126,7 +127,7 @@ def test_implementation(self):
lookup = DummyLookup()
instance = self._makeOne(minimal, lookup)
result = instance.implementation()()
- self.assertTrue(isinstance(result, unicode))
+ self.assertTrue(isinstance(result, text_type))
self.assertEqual(result.rstrip('\n'),
'\n
')
@@ -140,7 +141,7 @@ def _callFUT(self, name, **kw):
def test_it(self):
minimal = self._getTemplatePath('minimal.pt')
result = self._callFUT(minimal)
- self.assertTrue(isinstance(result, unicode))
+ self.assertTrue(isinstance(result, text_type))
self.assertEqual(result.rstrip('\n'),
'\n
')
@@ -155,8 +156,8 @@ def test_it(self):
result = self._callFUT(minimal)
from webob import Response
self.assertTrue(isinstance(result, Response))
- self.assertEqual(result.app_iter[0].rstrip('\n'),
- '\n
')
+ self.assertEqual(result.app_iter[0].rstrip(b'\n'),
+ b'\n
')
self.assertEqual(result.status, '200 OK')
self.assertEqual(len(result.headerlist), 2)
diff --git a/pyramid/tests/test_config/__init__.py b/pyramid/tests/test_config/__init__.py
index 2f9f516ae2..5b40a8c094 100644
--- a/pyramid/tests/test_config/__init__.py
+++ b/pyramid/tests/test_config/__init__.py
@@ -1,6 +1,6 @@
# package
-from zope.interface import implements
+from zope.interface import implementer
from zope.interface import Interface
class IFactory(Interface):
@@ -23,8 +23,8 @@ def dummy_include2(config):
class DummyContext:
pass
+@implementer(IFactory)
class DummyFactory(object):
- implements(IFactory)
def __call__(self):
""" """
diff --git a/pyramid/tests/test_config/test_adapters.py b/pyramid/tests/test_config/test_adapters.py
index 29c099e0e6..84b7119cf3 100644
--- a/pyramid/tests/test_config/test_adapters.py
+++ b/pyramid/tests/test_config/test_adapters.py
@@ -1,5 +1,6 @@
import unittest
+from pyramid.compat import PY3
from pyramid.tests.test_config import IDummy
class AdaptersConfiguratorMixinTests(unittest.TestCase):
@@ -9,12 +10,13 @@ def _makeOne(self, *arg, **kw):
return config
def test_add_subscriber_defaults(self):
- from zope.interface import implements
+ from zope.interface import implementer
from zope.interface import Interface
class IEvent(Interface):
pass
+ @implementer(IEvent)
class Event:
- implements(IEvent)
+ pass
L = []
def subscriber(event):
L.append(event)
@@ -28,12 +30,13 @@ def subscriber(event):
self.assertEqual(len(L), 2)
def test_add_subscriber_iface_specified(self):
- from zope.interface import implements
+ from zope.interface import implementer
from zope.interface import Interface
class IEvent(Interface):
pass
+ @implementer(IEvent)
class Event:
- implements(IEvent)
+ pass
L = []
def subscriber(event):
L.append(event)
@@ -59,13 +62,13 @@ def test_add_subscriber_dottednames(self):
self.assertEqual(handler.required, (INewRequest,))
def test_add_object_event_subscriber(self):
- from zope.interface import implements
+ from zope.interface import implementer
from zope.interface import Interface
class IEvent(Interface):
pass
+ @implementer(IEvent)
class Event:
object = 'foo'
- implements(IEvent)
event = Event()
L = []
def subscriber(object, event):
@@ -101,8 +104,11 @@ class Adapter(object):
def test_add_response_adapter_dottednames(self):
from pyramid.interfaces import IResponse
config = self._makeOne(autocommit=True)
- config.add_response_adapter('pyramid.response.Response',
- 'types.StringType')
+ if PY3: # pragma: no cover
+ str_name = 'builtins.str'
+ else:
+ str_name = '__builtin__.str'
+ config.add_response_adapter('pyramid.response.Response', str_name)
result = config.registry.queryAdapter('foo', IResponse)
- self.assertTrue(result.body, 'foo')
+ self.assertTrue(result.body, b'foo')
diff --git a/pyramid/tests/test_config/test_assets.py b/pyramid/tests/test_config/test_assets.py
index 322c854f54..627eefba7e 100644
--- a/pyramid/tests/test_config/test_assets.py
+++ b/pyramid/tests/test_config/test_assets.py
@@ -163,9 +163,9 @@ def test_get_resource_stream_no_overrides(self):
import pyramid.tests.test_config
provider = self._makeOne(pyramid.tests.test_config)
here = os.path.dirname(os.path.abspath(__file__))
- expected = open(os.path.join(here, resource_name)).read()
- result = provider.get_resource_stream(None, resource_name)
- self.assertEqual(result.read().replace('\r', ''), expected)
+ expected = read_(os.path.join(here, resource_name))
+ with provider.get_resource_stream(None, resource_name) as result:
+ self.assertEqual(result.read().replace(b'\r', b''), expected)
def test_get_resource_string_no_overrides(self):
import os
@@ -173,9 +173,9 @@ def test_get_resource_string_no_overrides(self):
import pyramid.tests.test_config
provider = self._makeOne(pyramid.tests.test_config)
here = os.path.dirname(os.path.abspath(__file__))
- expected = open(os.path.join(here, resource_name)).read()
+ expected = read_(os.path.join(here, resource_name))
result = provider.get_resource_string(None, resource_name)
- self.assertEqual(result.replace('\r', ''), expected)
+ self.assertEqual(result.replace(b'\r', b''), expected)
def test_has_resource_no_overrides(self):
resource_name = 'test_assets.py'
@@ -221,9 +221,9 @@ def test_get_resource_stream_override_returns_None(self):
import pyramid.tests.test_config
provider = self._makeOne(pyramid.tests.test_config)
here = os.path.dirname(os.path.abspath(__file__))
- expected = open(os.path.join(here, resource_name)).read()
- result = provider.get_resource_stream(None, resource_name)
- self.assertEqual(result.read(), expected)
+ expected = read_(os.path.join(here, resource_name))
+ with provider.get_resource_stream(None, resource_name) as result:
+ self.assertEqual(result.read(), expected)
def test_get_resource_string_override_returns_None(self):
overrides = DummyOverrides(None)
@@ -233,7 +233,7 @@ def test_get_resource_string_override_returns_None(self):
import pyramid.tests.test_config
provider = self._makeOne(pyramid.tests.test_config)
here = os.path.dirname(os.path.abspath(__file__))
- expected = open(os.path.join(here, resource_name)).read()
+ expected = read_(os.path.join(here, resource_name))
result = provider.get_resource_string(None, resource_name)
self.assertEqual(result, expected)
@@ -273,12 +273,13 @@ def test_get_resource_filename_override_returns_value(self):
self.assertEqual(result, 'value')
def test_get_resource_stream_override_returns_value(self):
- overrides = DummyOverrides('value')
+ from io import BytesIO
+ overrides = DummyOverrides(BytesIO(b'value'))
import pyramid.tests.test_config
self._registerOverrides(overrides)
provider = self._makeOne(pyramid.tests.test_config)
- result = provider.get_resource_stream(None, 'test_assets.py')
- self.assertEqual(result, 'value')
+ with provider.get_resource_stream(None, 'test_assets.py') as stream:
+ self.assertEqual(stream.getvalue(), b'value')
def test_get_resource_string_override_returns_value(self):
overrides = DummyOverrides('value')
@@ -419,9 +420,10 @@ def test_get_stream(self):
po = self._makeOne(package)
po.overrides= overrides
here = os.path.dirname(os.path.abspath(__file__))
- expected = open(os.path.join(here, 'test_assets.py')).read()
- self.assertEqual(po.get_stream('whatever').read().replace('\r', ''),
- expected)
+ expected = read_(os.path.join(here, 'test_assets.py'))
+ with po.get_stream('whatever') as stream:
+ self.assertEqual(stream.read().replace(b'\r', b''),
+ expected)
def test_get_stream_file_doesnt_exist(self):
overrides = [ DummyOverride(None), DummyOverride(
@@ -439,8 +441,9 @@ def test_get_string(self):
po = self._makeOne(package)
po.overrides= overrides
here = os.path.dirname(os.path.abspath(__file__))
- expected = open(os.path.join(here, 'test_assets.py')).read()
- self.assertEqual(po.get_string('whatever').replace('\r', ''), expected)
+ expected = read_(os.path.join(here, 'test_assets.py'))
+ self.assertEqual(po.get_string('whatever').replace(b'\r', b''),
+ expected)
def test_get_string_file_doesnt_exist(self):
overrides = [ DummyOverride(None), DummyOverride(
@@ -581,9 +584,14 @@ def __init__(self, name):
class DummyUnderOverride:
def __call__(self, package, path, override_package, override_prefix,
- _info=u''):
+ _info=''):
self.package = package
self.path = path
self.override_package = override_package
self.override_prefix = override_prefix
+def read_(src):
+ with open(src, 'rb') as f:
+ contents = f.read()
+ return contents
+
diff --git a/pyramid/tests/test_config/test_i18n.py b/pyramid/tests/test_config/test_i18n.py
index 03b410445c..25cb88cc37 100644
--- a/pyramid/tests/test_config/test_i18n.py
+++ b/pyramid/tests/test_config/test_i18n.py
@@ -92,7 +92,7 @@ def test_add_translation_dirs_registers_chameleon_translate(self):
try:
config.add_translation_dirs('pyramid.tests.pkgs.localeapp:locale')
translate = config.registry.getUtility(IChameleonTranslate)
- self.assertEqual(translate('Approve'), u'Approve')
+ self.assertEqual(translate('Approve'), 'Approve')
finally:
manager.pop()
diff --git a/pyramid/tests/test_config/test_init.py b/pyramid/tests/test_config/test_init.py
index 1dccd00e10..ca15082950 100644
--- a/pyramid/tests/test_config/test_init.py
+++ b/pyramid/tests/test_config/test_init.py
@@ -2,6 +2,10 @@
import os
+from pyramid.compat import PYPY
+from pyramid.compat import im_func
+from pyramid.testing import skip_on
+
from pyramid.tests.test_config import dummy_tween_factory
from pyramid.tests.test_config import dummy_include
from pyramid.tests.test_config import dummy_extend
@@ -9,14 +13,8 @@
from pyramid.tests.test_config import IDummy
from pyramid.tests.test_config import DummyContext
-try:
- import __pypy__
-except:
- __pypy__ = None
-
from pyramid.exceptions import ConfigurationExecutionError
from pyramid.exceptions import ConfigurationConflictError
-from pyramid.exceptions import ConfigurationError
class ConfiguratorTests(unittest.TestCase):
def _makeOne(self, *arg, **kw):
@@ -70,7 +68,7 @@ def test_ctor_no_registry(self):
config.commit()
self.assertTrue(config.registry.getUtility(IRendererFactory, 'json'))
self.assertTrue(config.registry.getUtility(IRendererFactory, 'string'))
- if not __pypy__:
+ if not PYPY:
self.assertTrue(config.registry.getUtility(IRendererFactory, '.pt'))
self.assertTrue(config.registry.getUtility(IRendererFactory,'.txt'))
self.assertTrue(config.registry.getUtility(IRendererFactory, '.mak'))
@@ -309,10 +307,12 @@ def test__fix_registry_notify(self):
def test__fix_registry_queryAdapterOrSelf(self):
from zope.interface import Interface
+ from zope.interface import implementer
class IFoo(Interface):
pass
+ @implementer(IFoo)
class Foo(object):
- implements(IFoo)
+ pass
class Bar(object):
pass
adaptation = ()
@@ -333,7 +333,7 @@ def test__fix_registry_registerSelfAdapter(self):
args, kw = reg.adapters[0]
self.assertEqual(args[0]('abc'), 'abc')
self.assertEqual(kw,
- {'info': u'', 'provided': 'provided',
+ {'info': '', 'provided': 'provided',
'required': 'required', 'name': 'abc', 'event': True})
def test_setup_registry_calls_fix_registry(self):
@@ -605,8 +605,8 @@ def test_setup_registry_includes(self):
pyramid.tests.test_config.dummy_include2""",
}
config.setup_registry(settings=settings)
- self.assert_(reg.included)
- self.assert_(reg.also_included)
+ self.assertTrue(reg.included)
+ self.assertTrue(reg.also_included)
def test_setup_registry_includes_spaces(self):
from pyramid.registry import Registry
@@ -617,8 +617,8 @@ def test_setup_registry_includes_spaces(self):
"""pyramid.tests.test_config.dummy_include pyramid.tests.test_config.dummy_include2""",
}
config.setup_registry(settings=settings)
- self.assert_(reg.included)
- self.assert_(reg.also_included)
+ self.assertTrue(reg.included)
+ self.assertTrue(reg.also_included)
def test_setup_registry_tweens(self):
from pyramid.interfaces import ITweens
@@ -917,7 +917,7 @@ def test_scan_integration_conflict(self):
c.scan(selfscan)
try:
c.commit()
- except ConfigurationConflictError, why:
+ except ConfigurationConflictError as why:
def scanconflicts(e):
conflicts = e._conflicts.values()
for conflict in conflicts:
@@ -929,6 +929,7 @@ def scanconflicts(e):
self.assertTrue("@view_config(name='two', renderer='string')" in
which)
+ @skip_on('py3')
def test_hook_zca(self):
from zope.component import getSiteManager
def foo():
@@ -942,6 +943,7 @@ def foo():
finally:
getSiteManager.reset()
+ @skip_on('py3')
def test_unhook_zca(self):
from zope.component import getSiteManager
def foo():
@@ -987,7 +989,7 @@ def includeme2(config):
config.include(includeme2)
try:
config.commit()
- except ConfigurationConflictError, why:
+ except ConfigurationConflictError as why:
c1, c2 = _conflictFunctions(why)
self.assertEqual(c1, 'includeme1')
self.assertEqual(c2, 'includeme2')
@@ -1031,7 +1033,7 @@ def view2(request): pass
config.set_notfound_view(view2)
try:
config.commit()
- except ConfigurationConflictError, why:
+ except ConfigurationConflictError as why:
c1, c2 = _conflictFunctions(why)
self.assertEqual(c1, 'test_conflict_set_notfound_view')
self.assertEqual(c2, 'test_conflict_set_notfound_view')
@@ -1046,7 +1048,7 @@ def view2(request): pass
config.set_forbidden_view(view2)
try:
config.commit()
- except ConfigurationConflictError, why:
+ except ConfigurationConflictError as why:
c1, c2 = _conflictFunctions(why)
self.assertEqual(c1, 'test_conflict_set_forbidden_view')
self.assertEqual(c2, 'test_conflict_set_forbidden_view')
@@ -1069,7 +1071,7 @@ def foo(config): pass
directives = {'foo':(foo, True)}
config.registry._directives = directives
foo_meth = config.foo
- self.assertTrue(foo_meth.im_func.__docobj__ is foo)
+ self.assertTrue(getattr(foo_meth, im_func).__docobj__ is foo)
def test___getattr__matches_no_action_wrap(self):
config = self._makeOne()
@@ -1077,7 +1079,7 @@ def foo(config): pass
directives = {'foo':(foo, False)}
config.registry._directives = directives
foo_meth = config.foo
- self.assertTrue(foo_meth.im_func is foo)
+ self.assertTrue(getattr(foo_meth, im_func) is foo)
class TestConfiguratorDeprecatedFeatures(unittest.TestCase):
def setUp(self):
@@ -1118,9 +1120,9 @@ def _getViewCallable(self, config, ctx_iface=None, request_iface=None,
def _registerRenderer(self, config, name='.txt'):
from pyramid.interfaces import IRendererFactory
from pyramid.interfaces import ITemplateRenderer
- from zope.interface import implements
+ from zope.interface import implementer
+ @implementer(ITemplateRenderer)
class Renderer:
- implements(ITemplateRenderer)
def __init__(self, info):
self.__class__.info = info
def __call__(self, *arg):
@@ -1220,7 +1222,7 @@ def test_add_route_with_view_renderer(self):
request_type = self._getRouteRequestIface(config, 'name')
wrapper = self._getViewCallable(config, None, request_type)
self._assertRoute(config, 'name', 'path')
- self.assertEqual(wrapper(None, None).body, 'Hello!')
+ self.assertEqual(wrapper(None, None).body, b'Hello!')
def test_add_route_with_view_attr(self):
from pyramid.renderers import null_renderer
@@ -1248,7 +1250,7 @@ def test_add_route_with_view_renderer_alias(self):
request_type = self._getRouteRequestIface(config, 'name')
wrapper = self._getViewCallable(config, None, request_type)
self._assertRoute(config, 'name', 'path')
- self.assertEqual(wrapper(None, None).body, 'Hello!')
+ self.assertEqual(wrapper(None, None).body, b'Hello!')
def test_add_route_with_view_permission(self):
from pyramid.interfaces import IAuthenticationPolicy
@@ -1286,7 +1288,7 @@ def view2(request): pass
config.add_route('a', '/a', view=view2)
try:
config.commit()
- except ConfigurationConflictError, why:
+ except ConfigurationConflictError as why:
c1, c2, c3, c4, c5, c6 = _conflictFunctions(why)
self.assertEqual(c1, 'test_conflict_route_with_view')
self.assertEqual(c2, 'test_conflict_route_with_view')
@@ -1308,7 +1310,7 @@ def test_extend_with_dotted_name(self):
config = self.config
config.add_directive(
'dummy_extend', 'pyramid.tests.test_config.dummy_extend')
- self.assert_(hasattr(config, 'dummy_extend'))
+ self.assertTrue(hasattr(config, 'dummy_extend'))
config.dummy_extend('discrim')
after = config.action_state
self.assertEqual(
@@ -1321,7 +1323,7 @@ def test_extend_with_python_callable(self):
config = self.config
config.add_directive(
'dummy_extend', dummy_extend)
- self.assert_(hasattr(config, 'dummy_extend'))
+ self.assertTrue(hasattr(config, 'dummy_extend'))
config.dummy_extend('discrim')
after = config.action_state
self.assertEqual(
@@ -1335,7 +1337,7 @@ def test_extend_same_name_doesnt_conflict(self):
'dummy_extend', dummy_extend)
config.add_directive(
'dummy_extend', dummy_extend2)
- self.assert_(hasattr(config, 'dummy_extend'))
+ self.assertTrue(hasattr(config, 'dummy_extend'))
config.dummy_extend('discrim')
after = config.action_state
self.assertEqual(
@@ -1530,9 +1532,10 @@ def push(self, d):
def pop(self):
self.popped = True
-from zope.interface import implements
+from zope.interface import implementer
+@implementer(IDummy)
class DummyEvent:
- implements(IDummy)
+ pass
class DummyRegistry(object):
def __init__(self, adaptation=None):
@@ -1550,8 +1553,9 @@ def queryAdapter(self, *arg, **kw):
return self.adaptation
from pyramid.interfaces import IResponse
+@implementer(IResponse)
class DummyResponse(object):
- implements(IResponse)
+ pass
from zope.interface import Interface
class IOther(Interface):
diff --git a/pyramid/tests/test_config/test_testing.py b/pyramid/tests/test_config/test_testing.py
index 494a2b099f..6c048b46d5 100644
--- a/pyramid/tests/test_config/test_testing.py
+++ b/pyramid/tests/test_config/test_testing.py
@@ -1,5 +1,6 @@
import unittest
+from pyramid.compat import text_
from pyramid.tests.test_config import IDummy
class TestingConfiguratorMixinTests(unittest.TestCase):
@@ -35,14 +36,14 @@ def test_testing_resources(self):
self.assertEqual(result['context'], ob1)
self.assertEqual(result['view_name'], '')
self.assertEqual(result['subpath'], ())
- self.assertEqual(result['traversed'], (u'ob1',))
+ self.assertEqual(result['traversed'], (text_('ob1'),))
self.assertEqual(result['virtual_root'], ob1)
self.assertEqual(result['virtual_root_path'], ())
result = adapter(DummyRequest({'PATH_INFO':'/ob2'}))
self.assertEqual(result['context'], ob2)
self.assertEqual(result['view_name'], '')
self.assertEqual(result['subpath'], ())
- self.assertEqual(result['traversed'], (u'ob2',))
+ self.assertEqual(result['traversed'], (text_('ob2'),))
self.assertEqual(result['virtual_root'], ob2)
self.assertEqual(result['virtual_root_path'], ())
self.assertRaises(KeyError, adapter, DummyRequest({'PATH_INFO':'/ob3'}))
@@ -166,9 +167,10 @@ def test_testing_add_template(self):
renderer.assert_(bar=2)
renderer.assert_(request=request)
-from zope.interface import implements
+from zope.interface import implementer
+@implementer(IDummy)
class DummyEvent:
- implements(IDummy)
+ pass
class DummyRequest:
subpath = ()
diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py
index f4e69f4e10..1f7ea05c2a 100644
--- a/pyramid/tests/test_config/test_views.py
+++ b/pyramid/tests/test_config/test_views.py
@@ -5,6 +5,7 @@
from pyramid.tests.test_config import dummy_view
+from pyramid.compat import im_func
from pyramid.exceptions import ConfigurationError
from pyramid.exceptions import ConfigurationExecutionError
from pyramid.exceptions import ConfigurationConflictError
@@ -37,9 +38,9 @@ def _getViewCallable(self, config, ctx_iface=None, request_iface=None,
def _registerRenderer(self, config, name='.txt'):
from pyramid.interfaces import IRendererFactory
from pyramid.interfaces import ITemplateRenderer
- from zope.interface import implements
+ from zope.interface import implementer
+ @implementer(ITemplateRenderer)
class Renderer:
- implements(ITemplateRenderer)
def __init__(self, info):
self.__class__.info = info
def __call__(self, *arg):
@@ -103,7 +104,7 @@ def test_add_view_view_callable_None_with_renderer(self):
self._registerRenderer(config, name='dummy')
config.add_view(renderer='dummy')
view = self._getViewCallable(config)
- self.assertTrue('Hello!' in view(None, None).body)
+ self.assertTrue(b'Hello!' in view(None, None).body)
def test_add_view_wrapped_view_is_decorated(self):
def view(request): # request-only wrapper
@@ -352,7 +353,7 @@ def test_add_view_same_phash_overrides_existing_single_view(self):
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IMultiView
phash = md5()
- phash.update('xhr:True')
+ phash.update(b'xhr:True')
view = lambda *arg: 'NOT OK'
view.__phash__ = phash.hexdigest()
config = self._makeOne(autocommit=True)
@@ -376,7 +377,7 @@ def test_add_view_exc_same_phash_overrides_existing_single_view(self):
from pyramid.interfaces import IExceptionViewClassifier
from pyramid.interfaces import IMultiView
phash = md5()
- phash.update('xhr:True')
+ phash.update(b'xhr:True')
view = lambda *arg: 'NOT OK'
view.__phash__ = phash.hexdigest()
config = self._makeOne(autocommit=True)
@@ -891,7 +892,7 @@ def __call__(self):
wrapper = self._getViewCallable(config)
request = self._makeRequest(config)
result = wrapper(None, request)
- self.assertEqual(result.body, 'Hello!')
+ self.assertEqual(result.body, b'Hello!')
settings = config.registry.queryUtility(ISettings)
result = renderer.info
self.assertEqual(result.registry, config.registry)
@@ -919,7 +920,7 @@ def __call__(self, *arg, **kw):
wrapper = self._getViewCallable(config)
request = self._makeRequest(config)
result = wrapper(None, request)
- self.assertEqual(result.body, 'moo')
+ self.assertEqual(result.body, b'moo')
def test_add_view_with_template_renderer_no_callable(self):
from pyramid.tests import test_config
@@ -931,7 +932,7 @@ def test_add_view_with_template_renderer_no_callable(self):
wrapper = self._getViewCallable(config)
request = self._makeRequest(config)
result = wrapper(None, request)
- self.assertEqual(result.body, 'Hello!')
+ self.assertEqual(result.body, b'Hello!')
settings = config.registry.queryUtility(ISettings)
result = renderer.info
self.assertEqual(result.registry, config.registry)
@@ -1392,7 +1393,7 @@ def view(request):
return 'OK'
result = config.derive_view(view)
self.assertFalse(result is view)
- self.assertEqual(result(None, None).body, 'moo')
+ self.assertEqual(result(None, None).body, b'moo')
def test_derive_view_with_default_renderer_with_explicit_renderer(self):
class moo(object): pass
@@ -1410,7 +1411,7 @@ def view(request):
result = config.derive_view(view, renderer='foo')
self.assertFalse(result is view)
request = self._makeRequest(config)
- self.assertEqual(result(None, request).body, 'foo')
+ self.assertEqual(result(None, request).body, b'foo')
def test_add_static_view_here_no_utility_registered(self):
from pyramid.renderers import null_renderer
@@ -1545,7 +1546,7 @@ def test_set_notfound_view_with_renderer(self):
result = view(None, request)
finally:
config.end()
- self.assertTrue('div' in result.body)
+ self.assertTrue(b'div' in result.body)
@testing.skip_on('java')
def test_set_forbidden_view_with_renderer(self):
@@ -1566,7 +1567,7 @@ def test_set_forbidden_view_with_renderer(self):
result = view(None, request)
finally:
config.end()
- self.assertTrue('div' in result.body)
+ self.assertTrue(b'div' in result.body)
def test_set_view_mapper(self):
from pyramid.interfaces import IViewMapperFactory
@@ -2117,7 +2118,7 @@ def view(request):
request = self._makeRequest()
request.override_renderer = 'moo'
context = testing.DummyResource()
- self.assertEqual(result(context, request).body, 'moo')
+ self.assertEqual(result(context, request).body, b'moo')
def test_requestonly_function_with_renderer_request_has_view(self):
response = DummyResponse()
@@ -2283,7 +2284,7 @@ def __call__(self, request):
self.assertFalse(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
- self.assertTrue('instance' in result.__name__)
+ self.assertTrue('test_views' in result.__name__)
self.assertFalse(hasattr(result, '__call_permissive__'))
self.assertEqual(result(None, None), response)
@@ -2537,7 +2538,7 @@ def test_secured_view_raises_forbidden_no_name(self):
request.url = 'url'
try:
result(None, request)
- except HTTPForbidden, e:
+ except HTTPForbidden as e:
self.assertEqual(e.message,
'Unauthorized: failed permission check')
else: # pragma: no cover
@@ -2559,7 +2560,7 @@ def myview(request): pass
request.url = 'url'
try:
result(None, request)
- except HTTPForbidden, e:
+ except HTTPForbidden as e:
self.assertEqual(e.message,
'Unauthorized: myview failed permission check')
else: # pragma: no cover
@@ -2577,7 +2578,7 @@ def predicate1(context, request):
request.method = 'POST'
try:
result(None, None)
- except PredicateMismatch, e:
+ except PredicateMismatch as e:
self.assertEqual(e.detail, 'predicate mismatch for view ')
else: # pragma: no cover
raise AssertionError
@@ -2593,7 +2594,7 @@ def predicate1(context, request):
request.method = 'POST'
try:
result(None, None)
- except PredicateMismatch, e:
+ except PredicateMismatch as e:
self.assertEqual(e.detail, 'predicate mismatch for view myview')
else: # pragma: no cover
raise AssertionError
@@ -2662,7 +2663,7 @@ def outer_view(context, request):
self.assertEqual(request.wrapped_body, inner_response.body)
self.assertEqual(request.wrapped_view.__original_view__,
inner_view)
- return Response('outer ' + request.wrapped_body)
+ return Response(b'outer ' + request.wrapped_body)
self.config.registry.registerAdapter(
outer_view, (IViewClassifier, None, None), IView, 'owrap')
deriver = self._makeOne(viewname='inner',
@@ -2673,7 +2674,7 @@ def outer_view(context, request):
self.assertEqual(inner_view.__doc__, result.__doc__)
request = self._makeRequest()
response = result(None, request)
- self.assertEqual(response.body, 'outer OK')
+ self.assertEqual(response.body, b'outer OK')
def test_with_wrapper_viewname_notfound(self):
from pyramid.response import Response
@@ -3252,12 +3253,12 @@ def __permitted__(self, context, request):
self.assertTrue(view1.__doc__ is view2.__doc__)
self.assertTrue(view1.__module__ is view2.__module__)
self.assertTrue(view1.__name__ is view2.__name__)
- self.assertTrue(view1.__call_permissive__.im_func is
- view2.__call_permissive__.im_func)
- self.assertTrue(view1.__permitted__.im_func is
- view2.__permitted__.im_func)
- self.assertTrue(view1.__predicated__.im_func is
- view2.__predicated__.im_func)
+ self.assertTrue(getattr(view1.__call_permissive__, im_func) is
+ getattr(view2.__call_permissive__, im_func))
+ self.assertTrue(getattr(view1.__permitted__, im_func) is
+ getattr(view2.__permitted__, im_func))
+ self.assertTrue(getattr(view1.__predicated__, im_func) is
+ getattr(view2.__predicated__, im_func))
class TestStaticURLInfo(unittest.TestCase):
@@ -3463,10 +3464,11 @@ def __init__(self, environ=None):
class DummyContext:
pass
-from zope.interface import implements
+from zope.interface import implementer
from pyramid.interfaces import IResponse
+@implementer(IResponse)
class DummyResponse(object):
- implements(IResponse)
+ pass
class DummyAccept(object):
def __init__(self, *matches):
@@ -3512,10 +3514,10 @@ def add_view(self, *args, **kw):
def action(self, discriminator, callable):
callable()
-from zope.interface import implements
+from zope.interface import implementer
from pyramid.interfaces import IMultiView
+@implementer(IMultiView)
class DummyMultiView:
- implements(IMultiView)
def __init__(self):
self.views = []
self.name = 'name'
diff --git a/pyramid/tests/test_docs.py b/pyramid/tests/test_docs.py
index bf06c7ca67..eba95b2104 100644
--- a/pyramid/tests/test_docs.py
+++ b/pyramid/tests/test_docs.py
@@ -1,4 +1,5 @@
import unittest
+from pyramid.compat import print_
if 0:
# no released version of manuel actually works with :lineno:
@@ -31,5 +32,5 @@ def test_docs(cls):
if filename.endswith('.rst'):
docs.append(os.path.join(root, filename))
- print path
+ print_(path)
return manuel.testing.TestSuite(m, *docs)
diff --git a/pyramid/tests/test_encode.py b/pyramid/tests/test_encode.py
index 27965aea97..ccc8f16e3d 100644
--- a/pyramid/tests/test_encode.py
+++ b/pyramid/tests/test_encode.py
@@ -1,4 +1,6 @@
import unittest
+from pyramid.compat import text_
+from pyramid.compat import native_
class UrlEncodeTests(unittest.TestCase):
def _callFUT(self, query, doseq=False):
@@ -10,17 +12,17 @@ def test_ascii_only(self):
self.assertEqual(result, 'a=1&b=2')
def test_unicode_key(self):
- la = unicode('LaPe\xc3\xb1a', 'utf-8')
+ la = text_(b'LaPe\xc3\xb1a', 'utf-8')
result = self._callFUT([(la, 1), ('b',2)])
self.assertEqual(result, 'LaPe%C3%B1a=1&b=2')
def test_unicode_val_single(self):
- la = unicode('LaPe\xc3\xb1a', 'utf-8')
+ la = text_(b'LaPe\xc3\xb1a', 'utf-8')
result = self._callFUT([('a', la), ('b',2)])
self.assertEqual(result, 'a=LaPe%C3%B1a&b=2')
def test_unicode_val_multiple(self):
- la = [unicode('LaPe\xc3\xb1a', 'utf-8')] * 2
+ la = [text_(b'LaPe\xc3\xb1a', 'utf-8')] * 2
result = self._callFUT([('a', la), ('b',2)], doseq=True)
self.assertEqual(result, 'a=LaPe%C3%B1a&a=LaPe%C3%B1a&b=2')
@@ -42,29 +44,17 @@ def _callFUT(self, val, safe=''):
from pyramid.encode import url_quote
return url_quote(val, safe)
- def test_it_default(self):
- la = 'La/Pe\xc3\xb1a'
+ def test_it_bytes(self):
+ la = b'La/Pe\xc3\xb1a'
result = self._callFUT(la)
self.assertEqual(result, 'La%2FPe%C3%B1a')
- def test_it_with_safe(self):
- la = 'La/Pe\xc3\xb1a'
- result = self._callFUT(la, '/')
- self.assertEqual(result, 'La/Pe%C3%B1a')
-
-class TestQuotePlus(unittest.TestCase):
- def _callFUT(self, val, safe=''):
- from pyramid.encode import quote_plus
- return quote_plus(val, safe)
-
- def test_it_default(self):
- la = 'La Pe\xc3\xb1a'
+ def test_it_native(self):
+ la = native_(b'La/Pe\xc3\xb1a', 'utf-8')
result = self._callFUT(la)
- self.assertEqual(result, 'La+Pe%C3%B1a')
-
+ self.assertEqual(result, 'La%2FPe%C3%B1a')
+
def test_it_with_safe(self):
- la = 'La /Pe\xc3\xb1a'
+ la = b'La/Pe\xc3\xb1a'
result = self._callFUT(la, '/')
- self.assertEqual(result, 'La+/Pe%C3%B1a')
-
-
+ self.assertEqual(result, 'La/Pe%C3%B1a')
diff --git a/pyramid/tests/test_httpexceptions.py b/pyramid/tests/test_httpexceptions.py
index 9c7b38e272..927d277331 100644
--- a/pyramid/tests/test_httpexceptions.py
+++ b/pyramid/tests/test_httpexceptions.py
@@ -1,5 +1,8 @@
import unittest
+from pyramid.compat import bytes_
+from pyramid.compat import text_
+
class Test_exception_response(unittest.TestCase):
def _callFUT(self, *arg, **kw):
from pyramid.httpexceptions import exception_response
@@ -48,9 +51,9 @@ def test_not_basestring(self):
def test_unicode(self):
class DummyUnicodeObject(object):
def __unicode__(self):
- return u'42'
+ return text_('42')
duo = DummyUnicodeObject()
- self.assertEqual(self._callFUT(duo), u'42')
+ self.assertEqual(self._callFUT(duo), text_('42'))
class TestWSGIHTTPException(unittest.TestCase):
def _getTargetClass(self):
@@ -124,16 +127,16 @@ def test_ctor_with_empty_body(self):
self.assertEqual(exc.content_length, None)
def test_ctor_with_body_doesnt_set_default_app_iter(self):
- exc = self._makeOne(body='123')
- self.assertEqual(exc.app_iter, ['123'])
+ exc = self._makeOne(body=b'123')
+ self.assertEqual(exc.app_iter, [b'123'])
def test_ctor_with_unicode_body_doesnt_set_default_app_iter(self):
- exc = self._makeOne(unicode_body=u'123')
- self.assertEqual(exc.app_iter, ['123'])
+ exc = self._makeOne(unicode_body=text_('123'))
+ self.assertEqual(exc.app_iter, [b'123'])
def test_ctor_with_app_iter_doesnt_set_default_app_iter(self):
- exc = self._makeOne(app_iter=['123'])
- self.assertEqual(exc.app_iter, ['123'])
+ exc = self._makeOne(app_iter=[b'123'])
+ self.assertEqual(exc.app_iter, [b'123'])
def test_ctor_with_body_sets_default_app_iter_html(self):
cls = self._getTargetSubclass()
@@ -142,10 +145,10 @@ def test_ctor_with_body_sets_default_app_iter_html(self):
environ['HTTP_ACCEPT'] = 'text/html'
start_response = DummyStartResponse()
body = list(exc(environ, start_response))[0]
- self.assertTrue(body.startswith('' in body)
+ self.assertTrue(b'' in body)
def test__default_app_iter_with_comment_html2(self):
cls = self._getTargetSubclass()
@@ -232,7 +235,7 @@ def test__default_app_iter_with_comment_html2(self):
environ['HTTP_ACCEPT'] = 'text/html'
start_response = DummyStartResponse()
body = list(exc(environ, start_response))[0]
- self.assertTrue('' in body)
+ self.assertTrue(b'' in body)
def test_custom_body_template(self):
cls = self._getTargetSubclass()
@@ -240,7 +243,7 @@ def test_custom_body_template(self):
environ = _makeEnviron()
start_response = DummyStartResponse()
body = list(exc(environ, start_response))[0]
- self.assertEqual(body, '200 OK\n\nGET')
+ self.assertEqual(body, b'200 OK\n\nGET')
def test_custom_body_template_with_custom_variable_doesnt_choke(self):
cls = self._getTargetSubclass()
@@ -251,16 +254,16 @@ def __str__(self): raise ValueError
environ['gardentheory.user'] = Choke()
start_response = DummyStartResponse()
body = list(exc(environ, start_response))[0]
- self.assertEqual(body, '200 OK\n\nGET')
+ self.assertEqual(body, b'200 OK\n\nGET')
def test_body_template_unicode(self):
cls = self._getTargetSubclass()
- la = unicode('/La Pe\xc3\xb1a', 'utf-8')
+ la = text_(b'/La Pe\xc3\xb1a', 'utf-8')
environ = _makeEnviron(unicodeval=la)
exc = cls(body_template='${unicodeval}')
start_response = DummyStartResponse()
body = list(exc(environ, start_response))[0]
- self.assertEqual(body, '200 OK\n\n/La Pe\xc3\xb1a')
+ self.assertEqual(body, b'200 OK\n\n/La Pe\xc3\xb1a')
class TestRenderAllExceptionsWithoutArguments(unittest.TestCase):
def _doit(self, content_type):
@@ -274,9 +277,9 @@ def _doit(self, content_type):
exc.content_type = content_type
result = list(exc(environ, start_response))[0]
if exc.empty_body:
- self.assertEqual(result, '')
+ self.assertEqual(result, b'')
else:
- self.assertTrue(exc.status in result)
+ self.assertTrue(bytes_(exc.status) in result)
L.append(result)
self.assertEqual(len(L), len(status_map))
@@ -309,8 +312,8 @@ def test_it_call_with_default_body_tmpl(self):
start_response = DummyStartResponse()
app_iter = exc(environ, start_response)
self.assertEqual(app_iter[0],
- ('None None\n\nThe resource has been moved to foo; '
- 'you should be redirected automatically.\n\n'))
+ (b'None None\n\nThe resource has been moved to foo; '
+ b'you should be redirected automatically.\n\n'))
class TestHTTPForbidden(unittest.TestCase):
def _makeOne(self, *arg, **kw):
@@ -336,8 +339,8 @@ def test_it_with_default_body_tmpl(self):
start_response = DummyStartResponse()
app_iter = exc(environ, start_response)
self.assertEqual(app_iter[0],
- ('405 Method Not Allowed\n\nThe method GET is not '
- 'allowed for this resource. \n\n\n'))
+ (b'405 Method Not Allowed\n\nThe method GET is not '
+ b'allowed for this resource. \n\n\n'))
class DummyRequest(object):
diff --git a/pyramid/tests/test_i18n.py b/pyramid/tests/test_i18n.py
index 008cf525a0..bd4998b106 100644
--- a/pyramid/tests/test_i18n.py
+++ b/pyramid/tests/test_i18n.py
@@ -50,9 +50,9 @@ def test_translate(self):
def test_pluralize(self):
translations = DummyTranslations()
localizer = self._makeOne(None, translations)
- self.assertEqual(localizer.pluralize('singular', 'plural', 1,
- domain='1', mapping={}),
- 'singular')
+ result = localizer.pluralize('singular', 'plural', 1,
+ domain='1', mapping={})
+ self.assertEqual(result, 'singular')
self.assertTrue(localizer.pluralizer)
def test_pluralize_pluralizer_already_added(self):
@@ -434,8 +434,8 @@ def test_dgettext(self):
def test_ldgettext(self):
t = self._makeOne()
- self.assertEqual(t.ldgettext('messages', 'foo'), 'Voh')
- self.assertEqual(t.ldgettext('messages1', 'foo'), 'VohD')
+ self.assertEqual(t.ldgettext('messages', 'foo'), b'Voh')
+ self.assertEqual(t.ldgettext('messages1', 'foo'), b'VohD')
def test_dugettext(self):
t = self._makeOne()
@@ -449,8 +449,8 @@ def test_dngettext(self):
def test_ldngettext(self):
t = self._makeOne()
- self.assertEqual(t.ldngettext('messages', 'foo1', 'foos1', 1), 'Voh1')
- self.assertEqual(t.ldngettext('messages1', 'foo1', 'foos1', 1), 'VohD1')
+ self.assertEqual(t.ldngettext('messages', 'foo1', 'foos1', 1), b'Voh1')
+ self.assertEqual(t.ldngettext('messages1', 'foo1', 'foos1', 1),b'VohD1')
def test_dungettext(self):
t = self._makeOne()
@@ -476,5 +476,9 @@ class DummyTranslations(object):
def ugettext(self, text):
return text
+ gettext = ugettext
+
def ungettext(self, singular, plural, n):
return singular
+
+ ngettext = ungettext
diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py
index 1fa1cbbcf3..0c17b88cec 100644
--- a/pyramid/tests/test_integration.py
+++ b/pyramid/tests/test_integration.py
@@ -6,6 +6,8 @@
from pyramid.wsgi import wsgiapp
from pyramid.view import view_config
from pyramid.static import static_view
+from pyramid.compat import text_
+from pyramid.compat import url_quote
from zope.interface import Interface
@@ -62,45 +64,42 @@ def tearDown(self):
here = os.path.dirname(__file__)
class TestStaticAppBase(IntegrationBase):
- def _assertBody(self, body, filename):
- self.assertEqual(
- body.replace('\r', ''),
- open(filename, 'r').read()
- )
-
def test_basic(self):
res = self.testapp.get('/minimal.pt', status=200)
- self._assertBody(res.body, os.path.join(here, 'fixtures/minimal.pt'))
+ _assertBody(res.body, os.path.join(here, 'fixtures/minimal.pt'))
def test_hidden(self):
res = self.testapp.get('/static/.hiddenfile', status=200)
- self._assertBody(
- res.body,
- os.path.join(here, 'fixtures/static/.hiddenfile')
- )
+ _assertBody(res.body, os.path.join(here, 'fixtures/static/.hiddenfile'))
def test_highchars_in_pathelement(self):
- res = self.testapp.get('/static/héhé/index.html', status=200)
- self._assertBody(
- res.body, os.path.join(here, u'fixtures/static/héhé/index.html')
+ url = url_quote('/static/héhé/index.html')
+ res = self.testapp.get(url, status=200)
+ _assertBody(
+ res.body,
+ os.path.join(here,
+ text_('fixtures/static/héhé/index.html', 'utf-8'))
)
def test_highchars_in_filename(self):
- res = self.testapp.get('/static/héhé.html', status=200)
- self._assertBody(
- res.body, os.path.join(here, u'fixtures/static/héhé.html')
+ url = url_quote('/static/héhé.html')
+ res = self.testapp.get(url, status=200)
+ _assertBody(
+ res.body,
+ os.path.join(here,
+ text_('fixtures/static/héhé.html', 'utf-8'))
)
def test_not_modified(self):
self.testapp.extra_environ = {
'HTTP_IF_MODIFIED_SINCE':httpdate(pow(2, 32)-1)}
res = self.testapp.get('/minimal.pt', status=304)
- self.assertEqual(res.body, '')
+ self.assertEqual(res.body, b'')
def test_file_in_subdir(self):
fn = os.path.join(here, 'fixtures/static/index.html')
res = self.testapp.get('/static/index.html', status=200)
- self._assertBody(res.body, fn)
+ _assertBody(res.body, fn)
def test_directory_noslash_redir(self):
res = self.testapp.get('/static', status=301)
@@ -120,30 +119,30 @@ def test_directory_noslash_redir_with_scriptname(self):
def test_directory_withslash(self):
fn = os.path.join(here, 'fixtures/static/index.html')
res = self.testapp.get('/static/', status=200)
- self._assertBody(res.body, fn)
+ _assertBody(res.body, fn)
def test_range_inclusive(self):
self.testapp.extra_environ = {'HTTP_RANGE':'bytes=1-2'}
res = self.testapp.get('/static/index.html', status=206)
- self.assertEqual(res.body, 'ht')
+ self.assertEqual(res.body, b'ht')
def test_range_tilend(self):
self.testapp.extra_environ = {'HTTP_RANGE':'bytes=-5'}
res = self.testapp.get('/static/index.html', status=206)
- self.assertEqual(res.body, 'tml>\n')
+ self.assertEqual(res.body, b'tml>\n')
def test_range_notbytes(self):
self.testapp.extra_environ = {'HTTP_RANGE':'kHz=-5'}
res = self.testapp.get('/static/index.html', status=200)
- self._assertBody(res.body,
- os.path.join(here, 'fixtures/static/index.html'))
+ _assertBody(res.body,
+ os.path.join(here, 'fixtures/static/index.html'))
def test_range_multiple(self):
res = self.testapp.get('/static/index.html',
[('HTTP_RANGE', 'bytes=10-11,11-12')],
status=200)
- self._assertBody(res.body,
- os.path.join(here, 'fixtures/static/index.html'))
+ _assertBody(res.body,
+ os.path.join(here, 'fixtures/static/index.html'))
def test_range_oob(self):
self.testapp.extra_environ = {'HTTP_RANGE':'bytes=1000-1002'}
@@ -171,7 +170,7 @@ class TestStaticAppNoSubpath(unittest.TestCase):
staticapp = static_view(os.path.join(here, 'fixtures'), use_subpath=False)
def _makeRequest(self, extra):
from pyramid.request import Request
- from StringIO import StringIO
+ from io import BytesIO
kw = {'PATH_INFO':'',
'SCRIPT_NAME':'',
'SERVER_NAME':'localhost',
@@ -179,59 +178,48 @@ def _makeRequest(self, extra):
'REQUEST_METHOD':'GET',
'wsgi.version':(1,0),
'wsgi.url_scheme':'http',
- 'wsgi.input':StringIO()}
+ 'wsgi.input':BytesIO()}
kw.update(extra)
request = Request(kw)
return request
- def _assertBody(self, body, filename):
- self.assertEqual(
- body.replace('\r', ''),
- open(filename, 'r').read()
- )
-
def test_basic(self):
request = self._makeRequest({'PATH_INFO':'/minimal.pt'})
context = DummyContext()
result = self.staticapp(context, request)
self.assertEqual(result.status, '200 OK')
- self._assertBody(result.body, os.path.join(here, 'fixtures/minimal.pt'))
+ _assertBody(result.body, os.path.join(here, 'fixtures/minimal.pt'))
class TestStaticAppWithRoutePrefix(IntegrationBase, unittest.TestCase):
package = 'pyramid.tests.pkgs.static_routeprefix'
- def _assertBody(self, body, filename):
- self.assertEqual(
- body.replace('\r', ''),
- open(filename, 'r').read()
- )
def test_includelevel1(self):
res = self.testapp.get('/static/minimal.pt', status=200)
- self._assertBody(res.body,
- os.path.join(here, 'fixtures/minimal.pt'))
+ _assertBody(res.body,
+ os.path.join(here, 'fixtures/minimal.pt'))
def test_includelevel2(self):
res = self.testapp.get('/prefix/static/index.html', status=200)
- self._assertBody(res.body,
- os.path.join(here, 'fixtures/static/index.html'))
+ _assertBody(res.body,
+ os.path.join(here, 'fixtures/static/index.html'))
class TestFixtureApp(IntegrationBase, unittest.TestCase):
package = 'pyramid.tests.pkgs.fixtureapp'
def test_another(self):
res = self.testapp.get('/another.html', status=200)
- self.assertEqual(res.body, 'fixture')
+ self.assertEqual(res.body, b'fixture')
def test_root(self):
res = self.testapp.get('/', status=200)
- self.assertEqual(res.body, 'fixture')
+ self.assertEqual(res.body, b'fixture')
def test_dummyskin(self):
self.testapp.get('/dummyskin.html', status=404)
def test_error(self):
res = self.testapp.get('/error.html', status=200)
- self.assertEqual(res.body, 'supressed')
+ self.assertEqual(res.body, b'supressed')
def test_protected(self):
self.testapp.get('/protected.html', status=403)
@@ -242,8 +230,8 @@ class TestStaticPermApp(IntegrationBase, unittest.TestCase):
def test_allowed(self):
result = self.testapp.get('/allowed/index.html', status=200)
self.assertEqual(
- result.body.replace('\r', ''),
- open(os.path.join(here, 'fixtures/static/index.html'), 'r').read())
+ result.body.replace(b'\r', b''),
+ read_(os.path.join(here, 'fixtures/static/index.html')))
def test_denied_via_acl_global_root_factory(self):
self.testapp.extra_environ = {'REMOTE_USER':'bob'}
@@ -253,8 +241,8 @@ def test_allowed_via_acl_global_root_factory(self):
self.testapp.extra_environ = {'REMOTE_USER':'fred'}
result = self.testapp.get('/protected/index.html', status=200)
self.assertEqual(
- result.body.replace('\r', ''),
- open(os.path.join(here, 'fixtures/static/index.html'), 'r').read())
+ result.body.replace(b'\r', b''),
+ read_(os.path.join(here, 'fixtures/static/index.html')))
def test_denied_via_acl_local_root_factory(self):
self.testapp.extra_environ = {'REMOTE_USER':'fred'}
@@ -264,8 +252,8 @@ def test_allowed_via_acl_local_root_factory(self):
self.testapp.extra_environ = {'REMOTE_USER':'bob'}
result = self.testapp.get('/factory_protected/index.html', status=200)
self.assertEqual(
- result.body.replace('\r', ''),
- open(os.path.join(here, 'fixtures/static/index.html'), 'r').read())
+ result.body.replace(b'\r', b''),
+ read_(os.path.join(here, 'fixtures/static/index.html')))
class TestCCBug(IntegrationBase, unittest.TestCase):
# "unordered" as reported in IRC by author of
@@ -273,11 +261,11 @@ class TestCCBug(IntegrationBase, unittest.TestCase):
package = 'pyramid.tests.pkgs.ccbugapp'
def test_rdf(self):
res = self.testapp.get('/licenses/1/v1/rdf', status=200)
- self.assertEqual(res.body, 'rdf')
+ self.assertEqual(res.body, b'rdf')
def test_juri(self):
res = self.testapp.get('/licenses/1/v1/juri', status=200)
- self.assertEqual(res.body, 'juri')
+ self.assertEqual(res.body, b'juri')
class TestHybridApp(IntegrationBase, unittest.TestCase):
# make sure views registered for a route "win" over views registered
@@ -286,19 +274,19 @@ class TestHybridApp(IntegrationBase, unittest.TestCase):
package = 'pyramid.tests.pkgs.hybridapp'
def test_root(self):
res = self.testapp.get('/', status=200)
- self.assertEqual(res.body, 'global')
+ self.assertEqual(res.body, b'global')
def test_abc(self):
res = self.testapp.get('/abc', status=200)
- self.assertEqual(res.body, 'route')
+ self.assertEqual(res.body, b'route')
def test_def(self):
res = self.testapp.get('/def', status=200)
- self.assertEqual(res.body, 'route2')
+ self.assertEqual(res.body, b'route2')
def test_ghi(self):
res = self.testapp.get('/ghi', status=200)
- self.assertEqual(res.body, 'global')
+ self.assertEqual(res.body, b'global')
def test_jkl(self):
self.testapp.get('/jkl', status=404)
@@ -308,41 +296,41 @@ def test_mno(self):
def test_pqr_global2(self):
res = self.testapp.get('/pqr/global2', status=200)
- self.assertEqual(res.body, 'global2')
+ self.assertEqual(res.body, b'global2')
def test_error(self):
res = self.testapp.get('/error', status=200)
- self.assertEqual(res.body, 'supressed')
+ self.assertEqual(res.body, b'supressed')
def test_error2(self):
res = self.testapp.get('/error2', status=200)
- self.assertEqual(res.body, 'supressed2')
+ self.assertEqual(res.body, b'supressed2')
def test_error_sub(self):
res = self.testapp.get('/error_sub', status=200)
- self.assertEqual(res.body, 'supressed2')
+ self.assertEqual(res.body, b'supressed2')
class TestRestBugApp(IntegrationBase, unittest.TestCase):
# test bug reported by delijati 2010/2/3 (http://pastebin.com/d4cc15515)
package = 'pyramid.tests.pkgs.restbugapp'
def test_it(self):
res = self.testapp.get('/pet', status=200)
- self.assertEqual(res.body, 'gotten')
+ self.assertEqual(res.body, b'gotten')
class TestForbiddenAppHasResult(IntegrationBase, unittest.TestCase):
# test that forbidden exception has ACLDenied result attached
package = 'pyramid.tests.pkgs.forbiddenapp'
def test_it(self):
res = self.testapp.get('/x', status=403)
- message, result = [x.strip() for x in res.body.split('\n')]
- self.assertTrue(message.endswith('failed permission check'))
+ message, result = [x.strip() for x in res.body.split(b'\n')]
+ self.assertTrue(message.endswith(b'failed permission check'))
self.assertTrue(
- result.startswith("ACLDenied permission 'private' via ACE "
- "'' in ACL "
- "'' on context"))
+ result.startswith(b"ACLDenied permission 'private' via ACE "
+ b"'' in ACL "
+ b"'' on context"))
self.assertTrue(
- result.endswith("for principals ['system.Everyone']"))
+ result.endswith(b"for principals ['system.Everyone']"))
class TestViewDecoratorApp(IntegrationBase, unittest.TestCase):
package = 'pyramid.tests.pkgs.viewdecoratorapp'
@@ -357,20 +345,20 @@ def test_first(self):
# we use mako here instead of chameleon because it works on Jython
self._configure_mako()
res = self.testapp.get('/first', status=200)
- self.assertTrue('OK' in res.body)
+ self.assertTrue(b'OK' in res.body)
def test_second(self):
# we use mako here instead of chameleon because it works on Jython
self._configure_mako()
res = self.testapp.get('/second', status=200)
- self.assertTrue('OK2' in res.body)
+ self.assertTrue(b'OK2' in res.body)
class TestViewPermissionBug(IntegrationBase, unittest.TestCase):
# view_execution_permitted bug as reported by Shane at http://lists.repoze.org/pipermail/repoze-dev/2010-October/003603.html
package = 'pyramid.tests.pkgs.permbugapp'
def test_test(self):
res = self.testapp.get('/test', status=200)
- self.assertTrue('ACLDenied' in res.body)
+ self.assertTrue(b'ACLDenied' in res.body)
def test_x(self):
self.testapp.get('/x', status=403)
@@ -380,15 +368,15 @@ class TestDefaultViewPermissionBug(IntegrationBase, unittest.TestCase):
package = 'pyramid.tests.pkgs.defpermbugapp'
def test_x(self):
res = self.testapp.get('/x', status=403)
- self.assertTrue('failed permission check' in res.body)
+ self.assertTrue(b'failed permission check' in res.body)
def test_y(self):
res = self.testapp.get('/y', status=403)
- self.assertTrue('failed permission check' in res.body)
+ self.assertTrue(b'failed permission check' in res.body)
def test_z(self):
res = self.testapp.get('/z', status=200)
- self.assertTrue('public' in res.body)
+ self.assertTrue(b'public' in res.body)
from pyramid.tests.pkgs.exceptionviewapp.models import \
AnException, NotAnException
@@ -400,31 +388,31 @@ class TestExceptionViewsApp(IntegrationBase, unittest.TestCase):
root_factory = lambda *arg: excroot
def test_root(self):
res = self.testapp.get('/', status=200)
- self.assertTrue('maybe' in res.body)
+ self.assertTrue(b'maybe' in res.body)
def test_notanexception(self):
res = self.testapp.get('/notanexception', status=200)
- self.assertTrue('no' in res.body)
+ self.assertTrue(b'no' in res.body)
def test_anexception(self):
res = self.testapp.get('/anexception', status=200)
- self.assertTrue('yes' in res.body)
+ self.assertTrue(b'yes' in res.body)
def test_route_raise_exception(self):
res = self.testapp.get('/route_raise_exception', status=200)
- self.assertTrue('yes' in res.body)
+ self.assertTrue(b'yes' in res.body)
def test_route_raise_exception2(self):
res = self.testapp.get('/route_raise_exception2', status=200)
- self.assertTrue('yes' in res.body)
+ self.assertTrue(b'yes' in res.body)
def test_route_raise_exception3(self):
res = self.testapp.get('/route_raise_exception3', status=200)
- self.assertTrue('whoa' in res.body)
+ self.assertTrue(b'whoa' in res.body)
def test_route_raise_exception4(self):
res = self.testapp.get('/route_raise_exception4', status=200)
- self.assertTrue('whoa' in res.body)
+ self.assertTrue(b'whoa' in res.body)
class TestConflictApp(unittest.TestCase):
package = 'pyramid.tests.pkgs.conflictapp'
@@ -440,9 +428,9 @@ def test_autoresolved_view(self):
from webtest import TestApp
self.testapp = TestApp(app)
res = self.testapp.get('/')
- self.assertTrue('a view' in res.body)
+ self.assertTrue(b'a view' in res.body)
res = self.testapp.get('/route')
- self.assertTrue('route view' in res.body)
+ self.assertTrue(b'route view' in res.body)
def test_overridden_autoresolved_view(self):
from pyramid.response import Response
@@ -455,7 +443,7 @@ def thisview(request):
from webtest import TestApp
self.testapp = TestApp(app)
res = self.testapp.get('/')
- self.assertTrue('this view' in res.body)
+ self.assertTrue(b'this view' in res.body)
def test_overridden_route_view(self):
from pyramid.response import Response
@@ -468,7 +456,7 @@ def thisview(request):
from webtest import TestApp
self.testapp = TestApp(app)
res = self.testapp.get('/route')
- self.assertTrue('this view' in res.body)
+ self.assertTrue(b'this view' in res.body)
def test_nonoverridden_authorization_policy(self):
config = self._makeConfig()
@@ -477,7 +465,7 @@ def test_nonoverridden_authorization_policy(self):
from webtest import TestApp
self.testapp = TestApp(app)
res = self.testapp.get('/protected', status=403)
- self.assertTrue('403 Forbidden' in res)
+ self.assertTrue(b'403 Forbidden' in res.body)
def test_overridden_authorization_policy(self):
config = self._makeConfig()
@@ -507,15 +495,15 @@ def tearDown(self):
def test_root(self):
res = self.testapp.get('/', status=200)
- self.assertTrue('root' in res.body)
+ self.assertTrue(b'root' in res.body)
def test_two(self):
res = self.testapp.get('/two', status=200)
- self.assertTrue('two' in res.body)
+ self.assertTrue(b'two' in res.body)
def test_three(self):
res = self.testapp.get('/three', status=200)
- self.assertTrue('three' in res.body)
+ self.assertTrue(b'three' in res.body)
class SelfScanAppTest(unittest.TestCase):
def setUp(self):
@@ -531,11 +519,11 @@ def tearDown(self):
def test_root(self):
res = self.testapp.get('/', status=200)
- self.assertTrue('root' in res.body)
+ self.assertTrue(b'root' in res.body)
def test_two(self):
res = self.testapp.get('/two', status=200)
- self.assertTrue('two' in res.body)
+ self.assertTrue(b'two' in res.body)
class WSGIApp2AppTest(unittest.TestCase):
def setUp(self):
@@ -551,18 +539,18 @@ def tearDown(self):
def test_hello(self):
res = self.testapp.get('/hello', status=200)
- self.assertTrue('Hello' in res.body)
+ self.assertTrue(b'Hello' in res.body)
if os.name != 'java': # uses chameleon
class RendererScanAppTest(IntegrationBase, unittest.TestCase):
package = 'pyramid.tests.pkgs.rendererscanapp'
def test_root(self):
res = self.testapp.get('/one', status=200)
- self.assertTrue('One!' in res.body)
+ self.assertTrue(b'One!' in res.body)
def test_two(self):
res = self.testapp.get('/two', status=200)
- self.assertTrue('Two!' in res.body)
+ self.assertTrue(b'Two!' in res.body)
def test_rescan(self):
self.config.scan('pyramid.tests.pkgs.rendererscanapp')
@@ -570,9 +558,9 @@ def test_rescan(self):
from webtest import TestApp
testapp = TestApp(app)
res = testapp.get('/one', status=200)
- self.assertTrue('One!' in res.body)
+ self.assertTrue(b'One!' in res.body)
res = testapp.get('/two', status=200)
- self.assertTrue('Two!' in res.body)
+ self.assertTrue(b'Two!' in res.body)
class DummyContext(object):
pass
@@ -588,3 +576,12 @@ def httpdate(ts):
import datetime
ts = datetime.datetime.utcfromtimestamp(ts)
return ts.strftime("%a, %d %b %Y %H:%M:%S GMT")
+
+def read_(filename):
+ with open(filename, 'rb') as fp:
+ val = fp.read()
+ return val
+
+def _assertBody(body, filename):
+ assert(body.replace(b'\r', b'') == read_(filename))
+
diff --git a/pyramid/tests/test_location.py b/pyramid/tests/test_location.py
index 3d30c9954e..e1f47f4ab3 100644
--- a/pyramid/tests/test_location.py
+++ b/pyramid/tests/test_location.py
@@ -34,7 +34,7 @@ def test_lineage(self):
self.assertEqual(result, [o1])
from pyramid.interfaces import ILocation
-from zope.interface import implements
+from zope.interface import implementer
+@implementer(ILocation)
class Location(object):
- implements(ILocation)
__name__ = __parent__ = None
diff --git a/pyramid/tests/test_mako_templating.py b/pyramid/tests/test_mako_templating.py
index 162f774f53..074d28b85e 100644
--- a/pyramid/tests/test_mako_templating.py
+++ b/pyramid/tests/test_mako_templating.py
@@ -2,6 +2,8 @@
import unittest
from pyramid import testing
+from pyramid.compat import text_
+from pyramid.compat import text_type
class Base(object):
def setUp(self):
@@ -275,16 +277,16 @@ def test_call(self):
lookup = DummyLookup()
instance = self._makeOne('path', lookup)
result = instance({}, {'system':1})
- self.assertTrue(isinstance(result, unicode))
- self.assertEqual(result, u'result')
+ self.assertTrue(isinstance(result, text_type))
+ self.assertEqual(result, text_('result'))
def test_call_with_system_context(self):
# lame
lookup = DummyLookup()
instance = self._makeOne('path', lookup)
result = instance({}, {'context':1})
- self.assertTrue(isinstance(result, unicode))
- self.assertEqual(result, u'result')
+ self.assertTrue(isinstance(result, text_type))
+ self.assertEqual(result, text_('result'))
self.assertEqual(lookup.values, {'_context':1})
def test_call_with_tuple_value(self):
@@ -292,7 +294,7 @@ def test_call_with_tuple_value(self):
instance = self._makeOne('path', lookup)
result = instance(('fub', {}), {'context':1})
self.assertEqual(lookup.deffed, 'fub')
- self.assertEqual(result, u'result')
+ self.assertEqual(result, text_('result'))
self.assertEqual(lookup.values, {'_context':1})
def test_call_with_nondict_value(self):
@@ -306,7 +308,7 @@ def test_call_render_raises(self):
instance = self._makeOne('path', lookup)
try:
instance({}, {})
- except MakoRenderingException, e:
+ except MakoRenderingException as e:
self.assertTrue('NotImplementedError' in e.text)
else: # pragma: no cover
raise AssertionError
@@ -315,8 +317,8 @@ def test_implementation(self):
lookup = DummyLookup()
instance = self._makeOne('path', lookup)
result = instance.implementation().render_unicode()
- self.assertTrue(isinstance(result, unicode))
- self.assertEqual(result, u'result')
+ self.assertTrue(isinstance(result, text_type))
+ self.assertEqual(result, text_('result'))
class TestIntegration(unittest.TestCase):
def setUp(self):
@@ -333,45 +335,48 @@ def tearDown(self):
def test_render(self):
from pyramid.renderers import render
result = render('helloworld.mak', {'a':1}).replace('\r','')
- self.assertEqual(result, u'\nHello föö\n')
+ self.assertEqual(result, text_('\nHello föö\n', 'utf-8'))
def test_render_from_fs(self):
from pyramid.renderers import render
self.config.add_settings({'reload_templates': True})
result = render('helloworld.mak', {'a':1}).replace('\r','')
- self.assertEqual(result, u'\nHello föö\n')
+ self.assertEqual(result, text_('\nHello föö\n', 'utf-8'))
def test_render_inheritance(self):
from pyramid.renderers import render
result = render('helloinherit.mak', {}).replace('\r','')
- self.assertEqual(result, u'Layout\nHello World!\n')
+ self.assertEqual(result, text_('Layout\nHello World!\n'))
def test_render_inheritance_pkg_spec(self):
from pyramid.renderers import render
result = render('hello_inherit_pkg.mak', {}).replace('\r','')
- self.assertEqual(result, u'Layout\nHello World!\n')
+ self.assertEqual(result, text_('Layout\nHello World!\n'))
def test_render_to_response(self):
from pyramid.renderers import render_to_response
result = render_to_response('helloworld.mak', {'a':1})
- self.assertEqual(result.ubody.replace('\r',''), u'\nHello föö\n')
+ self.assertEqual(result.ubody.replace('\r',''),
+ text_('\nHello föö\n', 'utf-8'))
def test_render_to_response_pkg_spec(self):
from pyramid.renderers import render_to_response
result = render_to_response('pyramid.tests:fixtures/helloworld.mak',
{'a':1})
- self.assertEqual(result.ubody.replace('\r', ''), u'\nHello föö\n')
+ self.assertEqual(result.ubody.replace('\r', ''),
+ text_('\nHello föö\n', 'utf-8'))
def test_render_with_abs_path(self):
from pyramid.renderers import render
result = render('/helloworld.mak', {'a':1}).replace('\r','')
- self.assertEqual(result, u'\nHello föö\n')
+ self.assertEqual(result, text_('\nHello föö\n', 'utf-8'))
def test_get_renderer(self):
from pyramid.renderers import get_renderer
result = get_renderer('helloworld.mak')
- self.assertEqual(result.implementation().render_unicode().replace('\r',''),
- u'\nHello föö\n')
+ self.assertEqual(
+ result.implementation().render_unicode().replace('\r',''),
+ text_('\nHello föö\n', 'utf-8'))
def test_template_not_found(self):
from pyramid.renderers import render
@@ -381,8 +386,9 @@ def test_template_not_found(self):
def test_template_default_escaping(self):
from pyramid.renderers import render
- result = render('nonminimal.mak', {'name':'fred'}).replace('\r','')
- self.assertEqual(result, u'Hello, <b>fred</b>!\n')
+ result = render('nonminimal.mak',
+ {'name':'fred'}).replace('\r','')
+ self.assertEqual(result, text_('Hello, <b>fred</b>!\n'))
class TestPkgResourceTemplateLookup(unittest.TestCase):
def _makeOne(self, **kw):
@@ -448,7 +454,7 @@ def render_unicode(self, **values):
if self.exc:
raise self.exc
self.values = values
- return u'result'
+ return text_('result')
class DummyRendererInfo(object):
def __init__(self, kw):
diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py
index 995e05d463..a697b56917 100644
--- a/pyramid/tests/test_paster.py
+++ b/pyramid/tests/test_paster.py
@@ -1,5 +1,7 @@
import unittest
+from pyramid.testing import skip_on
+@skip_on('py3')
class TestPShellCommand(unittest.TestCase):
def _getTargetClass(self):
from pyramid.paster import PShellCommand
@@ -246,6 +248,7 @@ def test_command_custom_section_override(self):
self.assertTrue(self.bootstrap.closer.called)
self.assertTrue(shell.help)
+@skip_on('py3')
class TestPRoutesCommand(unittest.TestCase):
def _getTargetClass(self):
from pyramid.paster import PRoutesCommand
@@ -372,6 +375,7 @@ def test__get_mapper(self):
result = command._get_mapper(registry)
self.assertEqual(result.__class__, RoutesMapper)
+@skip_on('py3')
class TestPViewsCommand(unittest.TestCase):
def _getTargetClass(self):
from pyramid.paster import PViewsCommand
@@ -397,7 +401,7 @@ def test__find_view_no_match(self):
self.assertEqual(result, None)
def test__find_view_no_match_multiview_registered(self):
- from zope.interface import implements
+ from zope.interface import implementer
from zope.interface import providedBy
from pyramid.interfaces import IRequest
from pyramid.interfaces import IViewClassifier
@@ -405,8 +409,9 @@ def test__find_view_no_match_multiview_registered(self):
from pyramid.traversal import DefaultRootFactory
from pyramid.registry import Registry
registry = Registry()
+ @implementer(IMultiView)
class View1(object):
- implements(IMultiView)
+ pass
request = DummyRequest({'PATH_INFO':'/a'})
root = DefaultRootFactory(request)
root_iface = providedBy(root)
@@ -439,7 +444,7 @@ def view1(): pass
self.assertEqual(result, view1)
def test__find_view_traversal_multiview(self):
- from zope.interface import implements
+ from zope.interface import implementer
from zope.interface import providedBy
from pyramid.interfaces import IRequest
from pyramid.interfaces import IViewClassifier
@@ -447,8 +452,9 @@ def test__find_view_traversal_multiview(self):
from pyramid.traversal import DefaultRootFactory
from pyramid.registry import Registry
registry = Registry()
+ @implementer(IMultiView)
class View1(object):
- implements(IMultiView)
+ pass
request = DummyRequest({'PATH_INFO':'/a'})
root = DefaultRootFactory(request)
root_iface = providedBy(root)
@@ -463,7 +469,7 @@ class View1(object):
def test__find_view_route_no_multiview(self):
from zope.interface import Interface
- from zope.interface import implements
+ from zope.interface import implementer
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IView
@@ -478,8 +484,8 @@ class IMyRoute(Interface):
(IViewClassifier, IMyRoute, IMyRoot),
IView, '')
registry.registerUtility(IMyRoute, IRouteRequest, name='a')
+ @implementer(IMyRoot)
class Factory(object):
- implements(IMyRoot)
def __init__(self, request):
pass
routes = [DummyRoute('a', '/a', factory=Factory, matchdict={}),
@@ -491,7 +497,7 @@ def __init__(self, request):
def test__find_view_route_multiview_no_view_registered(self):
from zope.interface import Interface
- from zope.interface import implements
+ from zope.interface import implementer
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IMultiView
from pyramid.interfaces import IRootFactory
@@ -507,8 +513,8 @@ class IMyRoute2(Interface):
pass
registry.registerUtility(IMyRoute1, IRouteRequest, name='a')
registry.registerUtility(IMyRoute2, IRouteRequest, name='b')
+ @implementer(IMyRoot)
class Factory(object):
- implements(IMyRoot)
def __init__(self, request):
pass
registry.registerUtility(Factory, IRootFactory)
@@ -521,7 +527,7 @@ def __init__(self, request):
def test__find_view_route_multiview(self):
from zope.interface import Interface
- from zope.interface import implements
+ from zope.interface import implementer
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IView
@@ -545,8 +551,8 @@ class IMyRoute2(Interface):
IView, '')
registry.registerUtility(IMyRoute1, IRouteRequest, name='a')
registry.registerUtility(IMyRoute2, IRouteRequest, name='b')
+ @implementer(IMyRoot)
class Factory(object):
- implements(IMyRoot)
def __init__(self, request):
pass
registry.registerUtility(Factory, IRootFactory)
@@ -822,6 +828,7 @@ def predicate(): pass
self.assertEqual(L[8], ' pyramid.tests.test_paster.view.call')
self.assertEqual(L[9], ' view predicates (predicate = x)')
+@skip_on('py3')
class TestGetApp(unittest.TestCase):
def _callFUT(self, config_file, section_name, loadapp):
from pyramid.paster import get_app
@@ -857,6 +864,7 @@ def test_it_with_hash_and_name_override(self):
self.assertEqual(loadapp.relative_to, os.getcwd())
self.assertEqual(result, app)
+@skip_on('py3')
class TestBootstrap(unittest.TestCase):
def _callFUT(self, config_uri, request=None):
from pyramid.paster import bootstrap
@@ -896,6 +904,7 @@ def test_it_request_with_registry(self):
self.assertEqual(result['root'], self.root)
self.assert_('closer' in result)
+@skip_on('py3')
class TestPTweensCommand(unittest.TestCase):
def _getTargetClass(self):
from pyramid.paster import PTweensCommand
@@ -1042,10 +1051,10 @@ class DummyView(object):
def __init__(self, **attrs):
self.__request_attrs__ = attrs
+from zope.interface import implementer
+from pyramid.interfaces import IMultiView
+@implementer(IMultiView)
class DummyMultiView(object):
- from zope.interface import implements
- from pyramid.interfaces import IMultiView
- implements(IMultiView)
def __init__(self, *views, **attrs):
self.views = [(None, view, None) for view in views]
@@ -1062,7 +1071,7 @@ def items(self, section):
self.section = section
if self.result is None:
from ConfigParser import NoSectionError
- raise NoSectionError, section
+ raise NoSectionError(section)
return self.result
class DummyConfigParserFactory(object):
diff --git a/pyramid/tests/test_registry.py b/pyramid/tests/test_registry.py
index 6a20eaa5de..c3104bd319 100644
--- a/pyramid/tests/test_registry.py
+++ b/pyramid/tests/test_registry.py
@@ -48,9 +48,11 @@ class DummyModule:
__file__ = ''
from zope.interface import Interface
-from zope.interface import implements
+from zope.interface import implementer
class IDummyEvent(Interface):
pass
+@implementer(IDummyEvent)
class DummyEvent(object):
- implements(IDummyEvent)
+ pass
+
diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py
index 48b1bed654..1054dcb1ca 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -2,6 +2,7 @@
from pyramid.testing import cleanUp
from pyramid import testing
+from pyramid.compat import text_
class TestTemplateRendererFactory(unittest.TestCase):
def setUp(self):
@@ -433,7 +434,7 @@ def _callFUT(self, name):
def test_it_unicode(self):
renderer = self._callFUT(None)
- value = unicode('La Pe\xc3\xb1a', 'utf-8')
+ value = text_('La Pe\xc3\xb1a', 'utf-8')
result = renderer(value, {})
self.assertEqual(result, value)
@@ -452,14 +453,14 @@ def test_it_other(self):
def test_with_request_content_type_notset(self):
request = testing.DummyRequest()
renderer = self._callFUT(None)
- renderer(None, {'request':request})
+ renderer('', {'request':request})
self.assertEqual(request.response.content_type, 'text/plain')
def test_with_request_content_type_set(self):
request = testing.DummyRequest()
request.response.content_type = 'text/mishmash'
renderer = self._callFUT(None)
- renderer(None, {'request':request})
+ renderer('', {'request':request})
self.assertEqual(request.response.content_type, 'text/mishmash')
@@ -593,22 +594,22 @@ def test__make_response_request_is_None(self):
request = None
helper = self._makeOne('loo.foo')
response = helper._make_response('abc', request)
- self.assertEqual(response.body, 'abc')
+ self.assertEqual(response.body, b'abc')
def test__make_response_request_is_None_response_factory_exists(self):
self._registerResponseFactory()
request = None
helper = self._makeOne('loo.foo')
- response = helper._make_response('abc', request)
+ response = helper._make_response(b'abc', request)
self.assertEqual(response.__class__.__name__, 'ResponseFactory')
- self.assertEqual(response.body, 'abc')
+ self.assertEqual(response.body, b'abc')
def test__make_response_result_is_unicode(self):
from pyramid.response import Response
request = testing.DummyRequest()
request.response = Response()
helper = self._makeOne('loo.foo')
- la = unicode('/La Pe\xc3\xb1a', 'utf-8')
+ la = text_('/La Pe\xc3\xb1a', 'utf-8')
response = helper._make_response(la, request)
self.assertEqual(response.body, la.encode('utf-8'))
@@ -617,7 +618,7 @@ def test__make_response_result_is_str(self):
request = testing.DummyRequest()
request.response = Response()
helper = self._makeOne('loo.foo')
- la = unicode('/La Pe\xc3\xb1a', 'utf-8')
+ la = text_('/La Pe\xc3\xb1a', 'utf-8')
response = helper._make_response(la.encode('utf-8'), request)
self.assertEqual(response.body, la.encode('utf-8'))
@@ -630,7 +631,7 @@ def test__make_response_with_content_type(self):
helper = self._makeOne('loo.foo')
response = helper._make_response('abc', request)
self.assertEqual(response.content_type, 'text/nonsense')
- self.assertEqual(response.body, 'abc')
+ self.assertEqual(response.body, b'abc')
def test__make_response_with_headerlist(self):
from pyramid.response import Response
@@ -645,7 +646,7 @@ def test__make_response_with_headerlist(self):
('Content-Length', '3'),
('a', '1'),
('b', '2')])
- self.assertEqual(response.body, 'abc')
+ self.assertEqual(response.body, b'abc')
def test__make_response_with_status(self):
from pyramid.response import Response
@@ -656,7 +657,7 @@ def test__make_response_with_status(self):
helper = self._makeOne('loo.foo')
response = helper._make_response('abc', request)
self.assertEqual(response.status, '406 You Lose')
- self.assertEqual(response.body, 'abc')
+ self.assertEqual(response.body, b'abc')
def test__make_response_with_charset(self):
from pyramid.response import Response
@@ -686,9 +687,9 @@ def __init__(self):
self.config.registry.registerUtility(ResponseFactory, IResponseFactory)
request = testing.DummyRequest()
helper = self._makeOne('loo.foo')
- response = helper._make_response('abc', request)
+ response = helper._make_response(b'abc', request)
self.assertEqual(response.__class__, ResponseFactory)
- self.assertEqual(response.body, 'abc')
+ self.assertEqual(response.body, b'abc')
def test__make_response_with_real_request(self):
# functional
@@ -699,7 +700,7 @@ def test__make_response_with_real_request(self):
helper = self._makeOne('loo.foo')
response = helper._make_response('abc', request)
self.assertEqual(response.status, '406 You Lose')
- self.assertEqual(response.body, 'abc')
+ self.assertEqual(response.body, b'abc')
def test_clone_noargs(self):
helper = self._makeOne('name', 'package', 'registry')
@@ -811,7 +812,7 @@ def test_it_no_request(self):
'pyramid.tests:abc/def.pt')
renderer.string_response = 'abc'
response = self._callFUT('abc/def.pt', dict(a=1))
- self.assertEqual(response.body, 'abc')
+ self.assertEqual(response.body, b'abc')
renderer.assert_(a=1)
renderer.assert_(request=None)
@@ -822,7 +823,7 @@ def test_it_with_request(self):
request = testing.DummyRequest()
response = self._callFUT('abc/def.pt',
dict(a=1), request=request)
- self.assertEqual(response.body, 'abc')
+ self.assertEqual(response.body, b'abc')
renderer.assert_(a=1)
renderer.assert_(request=request)
@@ -834,7 +835,7 @@ def test_it_with_package(self):
request = testing.DummyRequest()
response = self._callFUT('abc/def.pt', dict(a=1), request=request,
package=pyramid.tests)
- self.assertEqual(response.body, 'abc')
+ self.assertEqual(response.body, b'abc')
renderer.assert_(a=1)
renderer.assert_(request=request)
diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py
index 066aa92075..9d48d3b6e8 100644
--- a/pyramid/tests/test_request.py
+++ b/pyramid/tests/test_request.py
@@ -1,6 +1,11 @@
import unittest
from pyramid import testing
+from pyramid.compat import text_
+from pyramid.compat import bytes_
+from pyramid.compat import native_
+from pyramid.compat import iteritems_, iterkeys_, itervalues_
+
class TestRequest(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
@@ -50,7 +55,7 @@ def test_params_decoded_from_utf_8_by_default(self):
}
request = self._makeOne(environ)
request.charset = None
- self.assertEqual(request.GET['la'], u'La Pe\xf1a')
+ self.assertEqual(request.GET['la'], text_(b'La Pe\xf1a'))
def test_class_implements(self):
from pyramid.interfaces import IRequest
@@ -166,7 +171,7 @@ def test_route_url(self):
self.config.registry.registerUtility(mapper, IRoutesMapper)
result = inst.route_url('flub', 'extra1', 'extra2',
a=1, b=2, c=3, _query={'a':1},
- _anchor=u"foo")
+ _anchor=text_("foo"))
self.assertEqual(result,
'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo')
@@ -184,7 +189,7 @@ def test_route_path(self):
self.config.registry.registerUtility(mapper, IRoutesMapper)
result = inst.route_path('flub', 'extra1', 'extra2',
a=1, b=2, c=3, _query={'a':1},
- _anchor=u"foo")
+ _anchor=text_("foo"))
self.assertEqual(result, '/1/2/3/extra1/extra2?a=1#foo')
def test_static_url(self):
@@ -235,20 +240,20 @@ def adapter(ob):
def test_json_body_invalid_json(self):
request = self._makeOne({'REQUEST_METHOD':'POST'})
- request.body = '{'
+ request.body = b'{'
self.assertRaises(ValueError, getattr, request, 'json_body')
def test_json_body_valid_json(self):
request = self._makeOne({'REQUEST_METHOD':'POST'})
- request.body = '{"a":1}'
+ request.body = b'{"a":1}'
self.assertEqual(request.json_body, {'a':1})
def test_json_body_alternate_charset(self):
from pyramid.compat import json
request = self._makeOne({'REQUEST_METHOD':'POST'})
request.charset = 'latin-1'
- la = unicode('La Pe\xc3\xb1a', 'utf-8')
- body = json.dumps({'a':la}, encoding='latin-1')
+ la = text_(b'La Pe\xc3\xb1a', 'utf-8')
+ body = bytes_(json.dumps({'a':la}), 'latin-1')
request.body = body
self.assertEqual(request.json_body, {'a':la})
@@ -322,17 +327,17 @@ def test_items(self):
def test_iteritems(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
- self.assertEqual(list(inst.iteritems()), list(environ.iteritems()))
+ self.assertEqual(list(inst.iteritems()), list(iteritems_(environ)))
def test_iterkeys(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
- self.assertEqual(list(inst.iterkeys()), list(environ.iterkeys()))
+ self.assertEqual(list(inst.iterkeys()), list(iterkeys_(environ)))
def test_itervalues(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
- self.assertEqual(list(inst.itervalues()), list(environ.itervalues()))
+ self.assertEqual(list(inst.itervalues()), list(itervalues_(environ)))
def test_keys(self):
environ = {'zooma':1}
@@ -370,8 +375,8 @@ def test_update(self):
def test_values(self):
environ = {'zooma':1}
inst = self._makeOne(environ)
- result = inst.values()
- self.assertEqual(result, environ.values())
+ result = list(inst.values())
+ self.assertEqual(result, list(environ.values()))
def test_response_content_type(self):
inst = self._makeOne()
@@ -497,14 +502,16 @@ def test_it_with_extra_slashes_in_path_info(self):
self.assertEqual(request.environ['PATH_INFO'], '/hello/')
def test_subpath_path_info_and_script_name_have_utf8(self):
- la = 'La Pe\xc3\xb1a'
- request = DummyRequest({'PATH_INFO':'/'+la, 'SCRIPT_NAME':'/'+la})
- request.subpath = (unicode(la, 'utf-8'), )
+ encoded = native_(text_(b'La Pe\xc3\xb1a'))
+ decoded = text_(bytes_(encoded), 'utf-8')
+ request = DummyRequest({'PATH_INFO':'/' + encoded,
+ 'SCRIPT_NAME':'/' + encoded})
+ request.subpath = (decoded, )
response = self._callFUT(request, 'app')
self.assertTrue(request.copied)
self.assertEqual(response, 'app')
- self.assertEqual(request.environ['SCRIPT_NAME'], '/' + la)
- self.assertEqual(request.environ['PATH_INFO'], '/' + la)
+ self.assertEqual(request.environ['SCRIPT_NAME'], '/' + encoded)
+ self.assertEqual(request.environ['PATH_INFO'], '/' + encoded)
class DummyRequest:
def __init__(self, environ=None):
diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py
index a980185008..eb9b7285d4 100644
--- a/pyramid/tests/test_router.py
+++ b/pyramid/tests/test_router.py
@@ -177,7 +177,7 @@ def make_response(s):
return Response(s)
router.registry.registerAdapter(make_response, (str,), IResponse)
app_iter = router(environ, start_response)
- self.assertEqual(app_iter, ['abc'])
+ self.assertEqual(app_iter, [b'abc'])
self.assertEqual(start_response.status, '200 OK')
self.assertEqual(environ['handled'], ['two', 'one'])
@@ -188,8 +188,8 @@ def test_call_traverser_default(self):
router = self._makeOne()
start_response = DummyStartResponse()
why = exc_raised(HTTPNotFound, router, environ, start_response)
- self.assertTrue('/' in why[0], why)
- self.assertFalse('debug_notfound' in why[0])
+ self.assertTrue('/' in why.args[0], why)
+ self.assertFalse('debug_notfound' in why.args[0])
self.assertEqual(len(logger.messages), 0)
def test_traverser_raises_notfound_class(self):
@@ -209,7 +209,7 @@ def test_traverser_raises_notfound_instance(self):
router = self._makeOne()
start_response = DummyStartResponse()
why = exc_raised(HTTPNotFound, router, environ, start_response)
- self.assertTrue('foo' in why[0], why)
+ self.assertTrue('foo' in why.args[0], why)
def test_traverser_raises_forbidden_class(self):
from pyramid.httpexceptions import HTTPForbidden
@@ -229,7 +229,7 @@ def test_traverser_raises_forbidden_instance(self):
router = self._makeOne()
start_response = DummyStartResponse()
why = exc_raised(HTTPForbidden, router, environ, start_response)
- self.assertTrue('foo' in why[0], why)
+ self.assertTrue('foo' in why.args[0], why)
def test_call_no_view_registered_no_isettings(self):
from pyramid.httpexceptions import HTTPNotFound
@@ -240,8 +240,8 @@ def test_call_no_view_registered_no_isettings(self):
router = self._makeOne()
start_response = DummyStartResponse()
why = exc_raised(HTTPNotFound, router, environ, start_response)
- self.assertTrue('/' in why[0], why)
- self.assertFalse('debug_notfound' in why[0])
+ self.assertTrue('/' in why.args[0], why)
+ self.assertFalse('debug_notfound' in why.args[0])
self.assertEqual(len(logger.messages), 0)
def test_call_no_view_registered_debug_notfound_false(self):
@@ -254,8 +254,8 @@ def test_call_no_view_registered_debug_notfound_false(self):
router = self._makeOne()
start_response = DummyStartResponse()
why = exc_raised(HTTPNotFound, router, environ, start_response)
- self.assertTrue('/' in why[0], why)
- self.assertFalse('debug_notfound' in why[0])
+ self.assertTrue('/' in why.args[0], why)
+ self.assertFalse('debug_notfound' in why.args[0])
self.assertEqual(len(logger.messages), 0)
def test_call_no_view_registered_debug_notfound_true(self):
@@ -269,15 +269,15 @@ def test_call_no_view_registered_debug_notfound_true(self):
start_response = DummyStartResponse()
why = exc_raised(HTTPNotFound, router, environ, start_response)
self.assertTrue(
- "debug_notfound of url http://localhost:8080/; " in why[0])
- self.assertTrue("view_name: '', subpath: []" in why[0])
- self.assertTrue('http://localhost:8080' in why[0], why)
+ "debug_notfound of url http://localhost:8080/; " in why.args[0])
+ self.assertTrue("view_name: '', subpath: []" in why.args[0])
+ self.assertTrue('http://localhost:8080' in why.args[0], why)
self.assertEqual(len(logger.messages), 1)
message = logger.messages[0]
self.assertTrue('of url http://localhost:8080' in message)
self.assertTrue("path_info: " in message)
- self.assertTrue('DummyContext instance at' in message)
+ self.assertTrue('DummyContext' in message)
self.assertTrue("view_name: ''" in message)
self.assertTrue("subpath: []" in message)
@@ -309,7 +309,7 @@ def make_response(s):
return Response(s)
router.registry.registerAdapter(make_response, (str,), IResponse)
app_iter = router(environ, start_response)
- self.assertEqual(app_iter, ['abc'])
+ self.assertEqual(app_iter, [b'abc'])
self.assertEqual(start_response.status, '200 OK')
def test_call_view_registered_nonspecific_default_path(self):
@@ -427,7 +427,7 @@ class IContext(Interface):
router = self._makeOne()
start_response = DummyStartResponse()
why = exc_raised(HTTPForbidden, router, environ, start_response)
- self.assertEqual(why[0], 'unauthorized')
+ self.assertEqual(why.args[0], 'unauthorized')
def test_call_view_raises_notfound(self):
from zope.interface import Interface
@@ -447,7 +447,7 @@ class IContext(Interface):
router = self._makeOne()
start_response = DummyStartResponse()
why = exc_raised(HTTPNotFound, router, environ, start_response)
- self.assertEqual(why[0], 'notfound')
+ self.assertEqual(why.args[0], 'notfound')
def test_call_view_raises_response_cleared(self):
from zope.interface import Interface
@@ -465,7 +465,7 @@ def view(context, request):
raise KeyError
def exc_view(context, request):
self.assertFalse(hasattr(request.response, 'a'))
- request.response.body = 'OK'
+ request.response.body = b'OK'
return request.response
environ = self._makeEnviron()
self._registerView(view, '', IViewClassifier, IRequest, IContext)
@@ -474,7 +474,7 @@ def exc_view(context, request):
router = self._makeOne()
start_response = DummyStartResponse()
itera = router(environ, start_response)
- self.assertEqual(itera, ['OK'])
+ self.assertEqual(itera, [b'OK'])
def test_call_request_has_response_callbacks(self):
from zope.interface import Interface
@@ -650,7 +650,6 @@ def factory(request):
self.assertEqual(environ['bfg.routes.route'].name, 'foo')
self.assertEqual(request.matchdict, matchdict)
self.assertEqual(request.matched_route.name, 'foo')
-
self.assertEqual(len(logger.messages), 1)
self.assertTrue(
logger.messages[0].startswith(
@@ -735,7 +734,7 @@ class IContext(Interface):
router = self._makeOne()
start_response = DummyStartResponse()
why = exc_raised(HTTPNotFound, router, environ, start_response)
- self.assertTrue('from root factory' in why[0])
+ self.assertTrue('from root factory' in why.args[0])
def test_root_factory_raises_forbidden(self):
from pyramid.interfaces import IRootFactory
@@ -753,7 +752,7 @@ class IContext(Interface):
router = self._makeOne()
start_response = DummyStartResponse()
why = exc_raised(HTTPForbidden, router, environ, start_response)
- self.assertTrue('from root factory' in why[0])
+ self.assertTrue('from root factory' in why.args[0])
def test_root_factory_exception_propagating(self):
from pyramid.interfaces import IRootFactory
@@ -1162,10 +1161,10 @@ def __call__(self, status, headers):
self.headers = headers
from pyramid.interfaces import IResponse
-from zope.interface import implements
+from zope.interface import implementer
+@implementer(IResponse)
class DummyResponse(object):
- implements(IResponse)
headerlist = ()
app_iter = ()
environ = None
@@ -1202,7 +1201,7 @@ def info(self, msg):
def exc_raised(exc, func, *arg, **kw):
try:
func(*arg, **kw)
- except exc, e:
+ except exc as e:
return e
else:
raise AssertionError('%s not raised' % exc) # pragma: no cover
diff --git a/pyramid/tests/test_scaffolds.py b/pyramid/tests/test_scaffolds.py
index ed2c5a9936..265e20c3b5 100644
--- a/pyramid/tests/test_scaffolds.py
+++ b/pyramid/tests/test_scaffolds.py
@@ -1,5 +1,7 @@
import unittest
+from pyramid.testing import skip_on
+@skip_on('py3')
class TestPyramidTemplate(unittest.TestCase):
def _getTargetClass(self):
from pyramid.scaffolds import PyramidTemplate
diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py
index 5c6454a385..6d75c7950e 100644
--- a/pyramid/tests/test_session.py
+++ b/pyramid/tests/test_session.py
@@ -263,21 +263,15 @@ def test_cookie_is_set(self):
self.assertEqual(session.response, response)
def serialize(data, secret):
- try:
- from hashlib import sha1
- except ImportError: # pragma: no cover
- import sha as sha1
-
- try:
- import cPickle as pickle
- except ImportError: # pragma: no cover
- import pickle
-
import hmac
import base64
- pickled = pickle.dumps('123', pickle.HIGHEST_PROTOCOL)
- sig = hmac.new(secret, pickled, sha1).hexdigest()
- return sig + base64.standard_b64encode(pickled)
+ from hashlib import sha1
+ from pyramid.compat import bytes_
+ from pyramid.compat import native_
+ from pyramid.compat import pickle
+ pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
+ sig = hmac.new(bytes_(secret), pickled, sha1).hexdigest()
+ return sig + native_(base64.b64encode(pickled))
class Test_signed_serialize(unittest.TestCase):
def _callFUT(self, data, secret):
diff --git a/pyramid/tests/test_settings.py b/pyramid/tests/test_settings.py
index 5cc4ce5619..d02b3cd3e8 100644
--- a/pyramid/tests/test_settings.py
+++ b/pyramid/tests/test_settings.py
@@ -87,7 +87,7 @@ def _callFUT(self, val):
def test_with_list(self):
result = self._callFUT(['abc', 'def'])
- self.assertEqual(result, ['abc', 'def'])
+ self.assertEqual(list(result), ['abc', 'def'])
def test_with_string(self):
result = self._callFUT('abc def')
diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py
index 6dc38fc570..427b03c803 100644
--- a/pyramid/tests/test_static.py
+++ b/pyramid/tests/test_static.py
@@ -37,14 +37,14 @@ def test_call_adds_slash_path_info_empty(self):
response = inst(context, request)
response.prepare(request.environ)
self.assertEqual(response.status, '301 Moved Permanently')
- self.assertTrue('http://example.com:6543/' in response.body)
+ self.assertTrue(b'http://example.com:6543/' in response.body)
def test_path_info_slash_means_index_html(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
request = self._makeRequest()
context = DummyContext()
response = inst(context, request)
- self.assertTrue('static' in response.body)
+ self.assertTrue(b'static' in response.body)
def test_oob_singledot(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -52,7 +52,7 @@ def test_oob_singledot(self):
context = DummyContext()
response = inst(context, request)
self.assertEqual(response.status, '200 OK')
- self.assertTrue('static' in response.body)
+ self.assertTrue(b'static' in response.body)
def test_oob_emptyelement(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -60,7 +60,7 @@ def test_oob_emptyelement(self):
context = DummyContext()
response = inst(context, request)
self.assertEqual(response.status, '200 OK')
- self.assertTrue('static' in response.body)
+ self.assertTrue(b'static' in response.body)
def test_oob_dotdotslash(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -99,21 +99,21 @@ def test_resource_isdir(self):
request = self._makeRequest({'PATH_INFO':'/subdir/'})
context = DummyContext()
response = inst(context, request)
- self.assertTrue('subdir' in response.body)
+ self.assertTrue(b'subdir' in response.body)
def test_resource_is_file(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
request = self._makeRequest({'PATH_INFO':'/index.html'})
context = DummyContext()
response = inst(context, request)
- self.assertTrue('static' in response.body)
+ self.assertTrue(b'static' in response.body)
def test_resource_is_file_with_cache_max_age(self):
inst = self._makeOne('pyramid.tests:fixtures/static', cache_max_age=600)
request = self._makeRequest({'PATH_INFO':'/index.html'})
context = DummyContext()
response = inst(context, request)
- self.assertTrue('static' in response.body)
+ self.assertTrue(b'static' in response.body)
self.assertEqual(len(response.headerlist), 5)
header_names = [ x[0] for x in response.headerlist ]
header_names.sort()
@@ -127,7 +127,7 @@ def test_resource_is_file_with_no_cache_max_age(self):
request = self._makeRequest({'PATH_INFO':'/index.html'})
context = DummyContext()
response = inst(context, request)
- self.assertTrue('static' in response.body)
+ self.assertTrue(b'static' in response.body)
self.assertEqual(len(response.headerlist), 3)
header_names = [ x[0] for x in response.headerlist ]
header_names.sort()
@@ -192,7 +192,7 @@ def test_call_adds_slash_path_info_empty(self):
response = inst(context, request)
response.prepare(request.environ)
self.assertEqual(response.status, '301 Moved Permanently')
- self.assertTrue('http://example.com:6543/' in response.body)
+ self.assertTrue(b'http://example.com:6543/' in response.body)
def test_path_info_slash_means_index_html(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -200,7 +200,7 @@ def test_path_info_slash_means_index_html(self):
request.subpath = ()
context = DummyContext()
response = inst(context, request)
- self.assertTrue('static' in response.body)
+ self.assertTrue(b'static' in response.body)
def test_oob_singledot(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -258,7 +258,7 @@ def test_resource_isdir(self):
request.subpath = ('subdir',)
context = DummyContext()
response = inst(context, request)
- self.assertTrue('subdir' in response.body)
+ self.assertTrue(b'subdir' in response.body)
def test_resource_is_file(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -266,7 +266,7 @@ def test_resource_is_file(self):
request.subpath = ('index.html',)
context = DummyContext()
response = inst(context, request)
- self.assertTrue('static' in response.body)
+ self.assertTrue(b'static' in response.body)
def test_resource_is_file_with_cache_max_age(self):
inst = self._makeOne('pyramid.tests:fixtures/static', cache_max_age=600)
@@ -274,7 +274,7 @@ def test_resource_is_file_with_cache_max_age(self):
request.subpath = ('index.html',)
context = DummyContext()
response = inst(context, request)
- self.assertTrue('static' in response.body)
+ self.assertTrue(b'static' in response.body)
self.assertEqual(len(response.headerlist), 5)
header_names = [ x[0] for x in response.headerlist ]
header_names.sort()
@@ -289,7 +289,7 @@ def test_resource_is_file_with_no_cache_max_age(self):
request.subpath = ('index.html',)
context = DummyContext()
response = inst(context, request)
- self.assertTrue('static' in response.body)
+ self.assertTrue(b'static' in response.body)
self.assertEqual(len(response.headerlist), 3)
header_names = [ x[0] for x in response.headerlist ]
header_names.sort()
diff --git a/pyramid/tests/test_testing.py b/pyramid/tests/test_testing.py
index 41a4788ec2..05ef36fe9f 100644
--- a/pyramid/tests/test_testing.py
+++ b/pyramid/tests/test_testing.py
@@ -1,5 +1,5 @@
-
import unittest
+from pyramid.compat import text_
class TestBase(unittest.TestCase):
def setUp(self):
@@ -35,8 +35,10 @@ def test_registerDummySecurityPolicy(self):
class Test_registerResources(TestBase):
def test_it(self):
- ob1 = object()
- ob2 = object()
+ class Dummy:
+ pass
+ ob1 = Dummy()
+ ob2 = Dummy()
resources = {'/ob1':ob1, '/ob2':ob2}
from pyramid import testing
testing.registerResources(resources)
@@ -46,14 +48,14 @@ def test_it(self):
self.assertEqual(result['context'], ob1)
self.assertEqual(result['view_name'], '')
self.assertEqual(result['subpath'], ())
- self.assertEqual(result['traversed'], (u'ob1',))
+ self.assertEqual(result['traversed'], (text_('ob1'),))
self.assertEqual(result['virtual_root'], ob1)
self.assertEqual(result['virtual_root_path'], ())
result = adapter(DummyRequest({'PATH_INFO':'/ob2'}))
self.assertEqual(result['context'], ob2)
self.assertEqual(result['view_name'], '')
self.assertEqual(result['subpath'], ())
- self.assertEqual(result['traversed'], (u'ob2',))
+ self.assertEqual(result['traversed'], (text_('ob2'),))
self.assertEqual(result['virtual_root'], ob2)
self.assertEqual(result['virtual_root_path'], ())
self.assertRaises(KeyError, adapter, DummyRequest({'PATH_INFO':'/ob3'}))
@@ -132,7 +134,7 @@ def test_registerView_withresult(self):
request = DummyRequest()
request.registry = self.registry
response = render_view_to_response(None, request, 'moo.html')
- self.assertEqual(response.body, 'yo')
+ self.assertEqual(response.body, b'yo')
def test_registerView_custom(self):
from pyramid import testing
@@ -146,7 +148,7 @@ def view(context, request):
request = DummyRequest()
request.registry = self.registry
response = render_view_to_response(None, request, 'moo.html')
- self.assertEqual(response.body, '123')
+ self.assertEqual(response.body, b'123')
def test_registerView_with_permission_denying(self):
from pyramid import testing
@@ -188,7 +190,7 @@ def view(context, request):
request = DummyRequest()
request.registry = self.registry
result = render_view_to_response(None, request, 'moo.html')
- self.assertEqual(result.app_iter, ['123'])
+ self.assertEqual(result.app_iter, [b'123'])
class Test_registerAdapter(TestBase):
@@ -222,12 +224,12 @@ class for_(Interface):
class Test_registerUtility(TestBase):
def test_registerUtility(self):
- from zope.interface import implements
+ from zope.interface import implementer
from zope.interface import Interface
class iface(Interface):
pass
+ @implementer(iface)
class impl:
- implements(iface)
def __call__(self):
return 'foo'
utility = impl()
@@ -380,9 +382,10 @@ class Dummy:
resource = self._makeOne()
resource['abc'] = Dummy()
resource['def'] = Dummy()
- self.assertEqual(resource.values(), resource.subs.values())
- self.assertEqual(resource.items(), resource.subs.items())
- self.assertEqual(resource.keys(), resource.subs.keys())
+ L = list
+ self.assertEqual(L(resource.values()), L(resource.subs.values()))
+ self.assertEqual(L(resource.items()), L(resource.subs.items()))
+ self.assertEqual(L(resource.keys()), L(resource.subs.keys()))
self.assertEqual(len(resource), 2)
def test_nonzero(self):
@@ -586,87 +589,73 @@ def _callFUT(self, **kw):
from pyramid.testing import setUp
return setUp(**kw)
+ def tearDown(self):
+ from pyramid.threadlocal import manager
+ manager.clear()
+ getSiteManager = self._getSM()
+ if getSiteManager is not None:
+ getSiteManager.reset()
+
+ def _getSM(self):
+ try:
+ from zope.component import getSiteManager
+ except ImportError: # pragma: no cover
+ getSiteManager = None
+ return getSiteManager
+
+ def _assertSMHook(self, hook):
+ getSiteManager = self._getSM()
+ if getSiteManager is not None:
+ result = getSiteManager.sethook(None)
+ self.assertEqual(result, hook)
+
def test_it_defaults(self):
from pyramid.threadlocal import manager
from pyramid.threadlocal import get_current_registry
from pyramid.registry import Registry
- from zope.component import getSiteManager
old = True
manager.push(old)
- try:
- config = self._callFUT()
- current = manager.get()
- self.assertFalse(current is old)
- self.assertEqual(config.registry, current['registry'])
- self.assertEqual(current['registry'].__class__, Registry)
- self.assertEqual(current['request'], None)
- finally:
- result = getSiteManager.sethook(None)
- self.assertEqual(result, get_current_registry)
- getSiteManager.reset()
- manager.clear()
+ config = self._callFUT()
+ current = manager.get()
+ self.assertFalse(current is old)
+ self.assertEqual(config.registry, current['registry'])
+ self.assertEqual(current['registry'].__class__, Registry)
+ self.assertEqual(current['request'], None)
+ self._assertSMHook(get_current_registry)
def test_it_with_registry(self):
from pyramid.registry import Registry
- from zope.component import getSiteManager
from pyramid.threadlocal import manager
registry = Registry()
- try:
- self._callFUT(registry=registry)
- current = manager.get()
- self.assertEqual(current['registry'], registry)
- finally:
- getSiteManager.reset()
- manager.clear()
+ self._callFUT(registry=registry)
+ current = manager.get()
+ self.assertEqual(current['registry'], registry)
def test_it_with_request(self):
- from zope.component import getSiteManager
from pyramid.threadlocal import manager
request = object()
- try:
- self._callFUT(request=request)
- current = manager.get()
- self.assertEqual(current['request'], request)
- finally:
- getSiteManager.reset()
- manager.clear()
+ self._callFUT(request=request)
+ current = manager.get()
+ self.assertEqual(current['request'], request)
def test_it_with_hook_zca_false(self):
- from zope.component import getSiteManager
- from pyramid.threadlocal import manager
from pyramid.registry import Registry
registry = Registry()
- try:
- self._callFUT(registry=registry, hook_zca=False)
+ self._callFUT(registry=registry, hook_zca=False)
+ getSiteManager = self._getSM()
+ if getSiteManager is not None:
sm = getSiteManager()
self.assertFalse(sm is registry)
- finally:
- getSiteManager.reset()
- manager.clear()
def test_it_with_settings_passed_explicit_registry(self):
- from zope.component import getSiteManager
- from pyramid.threadlocal import manager
from pyramid.registry import Registry
registry = Registry()
- try:
- self._callFUT(registry=registry, hook_zca=False,
- settings=dict(a=1))
- self.assertEqual(registry.settings['a'], 1)
- finally:
- getSiteManager.reset()
- manager.clear()
+ self._callFUT(registry=registry, hook_zca=False, settings=dict(a=1))
+ self.assertEqual(registry.settings['a'], 1)
def test_it_with_settings_passed_implicit_registry(self):
- from zope.component import getSiteManager
- from pyramid.threadlocal import manager
- try:
- config = self._callFUT(hook_zca=False,
- settings=dict(a=1))
- self.assertEqual(config.registry.settings['a'], 1)
- finally:
- getSiteManager.reset()
- manager.clear()
+ config = self._callFUT(hook_zca=False, settings=dict(a=1))
+ self.assertEqual(config.registry.settings['a'], 1)
class Test_cleanUp(Test_setUp):
def _callFUT(self, *arg, **kw):
@@ -678,24 +667,48 @@ def _callFUT(self, **kw):
from pyramid.testing import tearDown
return tearDown(**kw)
+ def tearDown(self):
+ from pyramid.threadlocal import manager
+ manager.clear()
+ getSiteManager = self._getSM()
+ if getSiteManager is not None:
+ getSiteManager.reset()
+
+ def _getSM(self):
+ try:
+ from zope.component import getSiteManager
+ except ImportError: # pragma: no cover
+ getSiteManager = None
+ return getSiteManager
+
+ def _assertSMHook(self, hook):
+ getSiteManager = self._getSM()
+ if getSiteManager is not None:
+ result = getSiteManager.sethook(None)
+ self.assertEqual(result, hook)
+
+ def _setSMHook(self, hook):
+ getSiteManager = self._getSM()
+ if getSiteManager is not None:
+ getSiteManager.sethook(hook)
+
def test_defaults(self):
from pyramid.threadlocal import manager
- from zope.component import getSiteManager
registry = DummyRegistry()
old = {'registry':registry}
hook = lambda *arg: None
try:
- getSiteManager.sethook(hook)
+ self._setSMHook(hook)
manager.push(old)
self._callFUT()
current = manager.get()
self.assertNotEqual(current, old)
self.assertEqual(registry.inited, 2)
finally:
- result = getSiteManager.sethook(None)
- self.assertNotEqual(result, hook)
- getSiteManager.reset()
- manager.clear()
+ getSiteManager = self._getSM()
+ if getSiteManager is not None:
+ result = getSiteManager.sethook(None)
+ self.assertNotEqual(result, hook)
def test_registry_cannot_be_inited(self):
from pyramid.threadlocal import manager
@@ -714,17 +727,12 @@ def raiseit(name):
manager.clear()
def test_unhook_zc_false(self):
- from pyramid.threadlocal import manager
- from zope.component import getSiteManager
hook = lambda *arg: None
try:
- getSiteManager.sethook(hook)
+ self._setSMHook(hook)
self._callFUT(unhook_zca=False)
finally:
- result = getSiteManager.sethook(None)
- self.assertEqual(result, hook)
- getSiteManager.reset()
- manager.clear()
+ self._assertSMHook(hook)
class TestDummyRendererFactory(unittest.TestCase):
def _makeOne(self, name, factory):
@@ -888,13 +896,15 @@ def test_get_csrf_token(self):
from zope.interface import Interface
-from zope.interface import implements
+from zope.interface import implementer
class IDummy(Interface):
pass
+@implementer(IDummy)
class DummyEvent:
- implements(IDummy)
+ pass
+
class DummyRequest:
application_url = 'http://example.com'
diff --git a/pyramid/tests/test_traversal.py b/pyramid/tests/test_traversal.py
index 95caf21be2..72192b23b1 100644
--- a/pyramid/tests/test_traversal.py
+++ b/pyramid/tests/test_traversal.py
@@ -1,69 +1,91 @@
import unittest
from pyramid.testing import cleanUp
+from pyramid.compat import text_
+from pyramid.compat import native_
+from pyramid.compat import text_type
+from pyramid.compat import url_quote
class TraversalPathTests(unittest.TestCase):
def _callFUT(self, path):
from pyramid.traversal import traversal_path
return traversal_path(path)
+ def test_utf8(self):
+ la = b'La Pe\xc3\xb1a'
+ encoded = url_quote(la)
+ decoded = text_(la, 'utf-8')
+ path = '/'.join([encoded, encoded])
+ result = self._callFUT(path)
+ self.assertEqual(result, (decoded, decoded))
+
+ def test_utf16(self):
+ from pyramid.exceptions import URLDecodeError
+ la = text_(b'La Pe\xc3\xb1a', 'utf-8').encode('utf-16')
+ encoded = url_quote(la)
+ path = '/'.join([encoded, encoded])
+ self.assertRaises(URLDecodeError, self._callFUT, path)
+
+ def test_unicode_highorder_chars(self):
+ path = text_('/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF')
+ self.assertEqual(self._callFUT(path),
+ (text_('\u6d41\u884c\u8d8b\u52bf', 'unicode_escape'),))
+
+ def test_element_urllquoted(self):
+ self.assertEqual(self._callFUT('/foo/space%20thing/bar'),
+ (text_('foo'), text_('space thing'), text_('bar')))
+
+ def test_unicode_undecodeable_to_ascii(self):
+ path = text_(b'/La Pe\xc3\xb1a', 'utf-8')
+ self.assertRaises(UnicodeEncodeError, self._callFUT, path)
+
+class TraversalPathInfoTests(unittest.TestCase):
+ def _callFUT(self, path):
+ from pyramid.traversal import traversal_path_info
+ return traversal_path_info(path)
+
def test_path_startswith_endswith(self):
- self.assertEqual(self._callFUT('/foo/'), (u'foo',))
+ self.assertEqual(self._callFUT('/foo/'), (text_('foo'),))
def test_empty_elements(self):
- self.assertEqual(self._callFUT('foo///'), (u'foo',))
+ self.assertEqual(self._callFUT('foo///'), (text_('foo'),))
def test_onedot(self):
- self.assertEqual(self._callFUT('foo/./bar'), (u'foo', u'bar'))
+ self.assertEqual(self._callFUT('foo/./bar'),
+ (text_('foo'), text_('bar')))
def test_twodots(self):
- self.assertEqual(self._callFUT('foo/../bar'), (u'bar',))
+ self.assertEqual(self._callFUT('foo/../bar'), (text_('bar'),))
def test_twodots_at_start(self):
- self.assertEqual(self._callFUT('../../bar'), (u'bar',))
-
- def test_element_urllquoted(self):
- self.assertEqual(self._callFUT('/foo/space%20thing/bar'),
- (u'foo', u'space thing', u'bar'))
+ self.assertEqual(self._callFUT('../../bar'), (text_('bar'),))
def test_segments_are_unicode(self):
result = self._callFUT('/foo/bar')
- self.assertEqual(type(result[0]), unicode)
- self.assertEqual(type(result[1]), unicode)
+ self.assertEqual(type(result[0]), text_type)
+ self.assertEqual(type(result[1]), text_type)
def test_same_value_returned_if_cached(self):
result1 = self._callFUT('/foo/bar')
result2 = self._callFUT('/foo/bar')
- self.assertEqual(result1, (u'foo', u'bar'))
- self.assertEqual(result2, (u'foo', u'bar'))
-
- def test_utf8(self):
- import urllib
- la = 'La Pe\xc3\xb1a'
- encoded = urllib.quote(la)
- decoded = unicode(la, 'utf-8')
- path = '/'.join([encoded, encoded])
- self.assertEqual(self._callFUT(path), (decoded, decoded))
-
- def test_utf16(self):
- from pyramid.exceptions import URLDecodeError
- import urllib
- la = unicode('La Pe\xc3\xb1a', 'utf-8').encode('utf-16')
- encoded = urllib.quote(la)
- path = '/'.join([encoded, encoded])
- self.assertRaises(URLDecodeError, self._callFUT, path)
-
- def test_unicode_highorder_chars(self):
- path = u'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF'
- self.assertEqual(self._callFUT(path), (u'\u6d41\u884c\u8d8b\u52bf',))
+ self.assertEqual(result1, (text_('foo'), text_('bar')))
+ self.assertEqual(result2, (text_('foo'), text_('bar')))
def test_unicode_simple(self):
- path = u'/abc'
- self.assertEqual(self._callFUT(path), (u'abc',))
+ path = text_('/abc')
+ self.assertEqual(self._callFUT(path), (text_('abc'),))
- def test_unicode_undecodeable_to_ascii(self):
- path = unicode('/La Pe\xc3\xb1a', 'utf-8')
- self.assertRaises(UnicodeEncodeError, self._callFUT, path)
+ def test_highorder(self):
+ la = b'La Pe\xc3\xb1a'
+ latin1 = native_(la)
+ result = self._callFUT(latin1)
+ self.assertEqual(result, (text_(la, 'utf-8'),))
+
+ def test_highorder_undecodeable(self):
+ from pyramid.exceptions import URLDecodeError
+ la = text_(b'La Pe\xc3\xb1a', 'utf-8')
+ notlatin1 = native_(la)
+ self.assertRaises(URLDecodeError, self._callFUT, notlatin1)
class ResourceTreeTraverserTests(unittest.TestCase):
def setUp(self):
@@ -146,7 +168,7 @@ def test_call_withconn_getitem_withpath_nosubpath(self):
self.assertEqual(result['context'], foo)
self.assertEqual(result['view_name'], 'bar')
self.assertEqual(result['subpath'], ())
- self.assertEqual(result['traversed'], (u'foo',))
+ self.assertEqual(result['traversed'], (text_('foo'),))
self.assertEqual(result['root'], root)
self.assertEqual(result['virtual_root'], root)
self.assertEqual(result['virtual_root_path'], ())
@@ -161,7 +183,7 @@ def test_call_withconn_getitem_withpath_withsubpath(self):
self.assertEqual(result['context'], foo)
self.assertEqual(result['view_name'], 'bar')
self.assertEqual(result['subpath'], ('baz', 'buz'))
- self.assertEqual(result['traversed'], (u'foo',))
+ self.assertEqual(result['traversed'], (text_('foo'),))
self.assertEqual(result['root'], root)
self.assertEqual(result['virtual_root'], root)
self.assertEqual(result['virtual_root_path'], ())
@@ -194,10 +216,12 @@ def test_call_with_vh_root(self):
self.assertEqual(result['context'], baz)
self.assertEqual(result['view_name'], '')
self.assertEqual(result['subpath'], ())
- self.assertEqual(result['traversed'], (u'foo', u'bar', u'baz'))
+ self.assertEqual(result['traversed'],
+ (text_('foo'), text_('bar'), text_('baz')))
self.assertEqual(result['root'], root)
self.assertEqual(result['virtual_root'], bar)
- self.assertEqual(result['virtual_root_path'], (u'foo', u'bar'))
+ self.assertEqual(result['virtual_root_path'],
+ (text_('foo'), text_('bar')))
def test_call_with_vh_root2(self):
environ = self._getEnviron(PATH_INFO='/bar/baz',
@@ -212,10 +236,11 @@ def test_call_with_vh_root2(self):
self.assertEqual(result['context'], baz)
self.assertEqual(result['view_name'], '')
self.assertEqual(result['subpath'], ())
- self.assertEqual(result['traversed'], (u'foo', u'bar', u'baz'))
+ self.assertEqual(result['traversed'],
+ (text_('foo'), text_('bar'), text_('baz')))
self.assertEqual(result['root'], root)
self.assertEqual(result['virtual_root'], foo)
- self.assertEqual(result['virtual_root_path'], (u'foo',))
+ self.assertEqual(result['virtual_root_path'], (text_('foo'),))
def test_call_with_vh_root3(self):
environ = self._getEnviron(PATH_INFO='/foo/bar/baz',
@@ -230,7 +255,8 @@ def test_call_with_vh_root3(self):
self.assertEqual(result['context'], baz)
self.assertEqual(result['view_name'], '')
self.assertEqual(result['subpath'], ())
- self.assertEqual(result['traversed'], (u'foo', u'bar', u'baz'))
+ self.assertEqual(result['traversed'],
+ (text_('foo'), text_('bar'), text_('baz')))
self.assertEqual(result['root'], root)
self.assertEqual(result['virtual_root'], root)
self.assertEqual(result['virtual_root_path'], ())
@@ -248,10 +274,12 @@ def test_call_with_vh_root4(self):
self.assertEqual(result['context'], baz)
self.assertEqual(result['view_name'], '')
self.assertEqual(result['subpath'], ())
- self.assertEqual(result['traversed'], (u'foo', u'bar', u'baz'))
+ self.assertEqual(result['traversed'],
+ (text_('foo'), text_('bar'), text_('baz')))
self.assertEqual(result['root'], root)
self.assertEqual(result['virtual_root'], baz)
- self.assertEqual(result['virtual_root_path'], (u'foo', u'bar', u'baz'))
+ self.assertEqual(result['virtual_root_path'],
+ (text_('foo'), text_('bar'), text_('baz')))
def test_call_with_vh_root_path_root(self):
policy = self._makeOne(None)
@@ -268,23 +296,23 @@ def test_call_with_vh_root_path_root(self):
self.assertEqual(result['virtual_root_path'], ())
def test_non_utf8_path_segment_unicode_path_segments_fails(self):
+ from pyramid.exceptions import URLDecodeError
foo = DummyContext()
root = DummyContext(foo)
policy = self._makeOne(root)
- segment = unicode('LaPe\xc3\xb1a', 'utf-8').encode('utf-16')
+ segment = native_(text_(b'LaPe\xc3\xb1a', 'utf-8'), 'utf-16')
environ = self._getEnviron(PATH_INFO='/%s' % segment)
request = DummyRequest(environ)
- from pyramid.exceptions import URLDecodeError
self.assertRaises(URLDecodeError, policy, request)
def test_non_utf8_path_segment_settings_unicode_path_segments_fails(self):
+ from pyramid.exceptions import URLDecodeError
foo = DummyContext()
root = DummyContext(foo)
policy = self._makeOne(root)
- segment = unicode('LaPe\xc3\xb1a', 'utf-8').encode('utf-16')
+ segment = native_(text_(b'LaPe\xc3\xb1a', 'utf-8'), 'utf-16')
environ = self._getEnviron(PATH_INFO='/%s' % segment)
request = DummyRequest(environ)
- from pyramid.exceptions import URLDecodeError
self.assertRaises(URLDecodeError, policy, request)
def test_withroute_nothingfancy(self):
@@ -592,13 +620,16 @@ def test_absolute_unicode_found(self):
unprintable = DummyContext()
root = DummyContext(unprintable)
unprintable.__parent__ = root
- unprintable.__name__ = unicode(
- '/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf', 'utf-8')
+ unprintable.__name__ = text_(
+ b'/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf', 'utf-8')
root.__parent__ = None
root.__name__ = None
traverser = ResourceTreeTraverser
self._registerTraverser(traverser)
- result = self._callFUT(root, u'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF')
+ result = self._callFUT(
+ root,
+ text_(b'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF')
+ )
self.assertEqual(result, unprintable)
class ResourcePathTests(unittest.TestCase):
@@ -744,7 +775,7 @@ def _callFUT(self, s):
return quote_path_segment(s)
def test_unicode(self):
- la = unicode('/La Pe\xc3\xb1a', 'utf-8')
+ la = text_(b'/La Pe\xc3\xb1a', 'utf-8')
result = self._callFUT(la)
self.assertEqual(result, '%2FLa%20Pe%C3%B1a')
@@ -759,8 +790,9 @@ def test_int(self):
self.assertEqual(result, '12345')
def test_long(self):
+ from pyramid.compat import long
import sys
- s = long(sys.maxint + 1)
+ s = long(sys.maxsize + 1)
result = self._callFUT(s)
expected = str(s)
self.assertEqual(result, expected)
@@ -833,15 +865,16 @@ def test_call_unicode_mixed_with_bytes_in_resource_names(self):
root.__name__ = None
one = DummyContext()
one.__parent__ = root
- one.__name__ = unicode('La Pe\xc3\xb1a', 'utf-8')
+ one.__name__ = text_(b'La Pe\xc3\xb1a', 'utf-8')
two = DummyContext()
two.__parent__ = one
- two.__name__ = 'La Pe\xc3\xb1a'
+ two.__name__ = b'La Pe\xc3\xb1a'
request = DummyRequest()
context_url = self._makeOne(two, request)
result = context_url()
- self.assertEqual(result,
- 'http://example.com:5432/La%20Pe%C3%B1a/La%20Pe%C3%B1a/')
+ self.assertEqual(
+ result,
+ 'http://example.com:5432/La%20Pe%C3%B1a/La%20Pe%C3%B1a/')
def test_call_with_virtual_root_path(self):
from pyramid.interfaces import VH_ROOT_KEY
@@ -1036,6 +1069,13 @@ def test_self_string_found(self):
self._callFUT(resource, '')
self.assertEqual(resource.request.environ['PATH_INFO'], '')
+ def test_self_unicode_found(self):
+ resource = DummyContext()
+ traverser = make_traverser({'context':resource, 'view_name':''})
+ self._registerTraverser(traverser)
+ self._callFUT(resource, text_(''))
+ self.assertEqual(resource.request.environ['PATH_INFO'], '')
+
def test_self_tuple_found(self):
resource = DummyContext()
traverser = make_traverser({'context':resource, 'view_name':''})
@@ -1168,7 +1208,7 @@ def __init__(self, next=None, name=None):
def __getitem__(self, name):
if self.next is None:
- raise KeyError, name
+ raise KeyError(name)
return self.next
def __repr__(self):
diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py
index 8b95374fba..4c39d8e9ce 100644
--- a/pyramid/tests/test_url.py
+++ b/pyramid/tests/test_url.py
@@ -2,6 +2,8 @@
from pyramid.testing import setUp
from pyramid.testing import tearDown
+from pyramid.compat import text_
+from pyramid.compat import native_
class TestURLMethodsMixin(unittest.TestCase):
def setUp(self):
@@ -48,7 +50,7 @@ def test_resource_url_extra_args(self):
def test_resource_url_unicode_in_element_names(self):
request = self._makeOne()
self._registerContextURL(request.registry)
- uc = unicode('La Pe\xc3\xb1a', 'utf-8')
+ uc = text_(b'La Pe\xc3\xb1a', 'utf-8')
context = DummyContext()
result = request.resource_url(context, uc)
self.assertEqual(result,
@@ -73,7 +75,7 @@ def test_resource_url_with_query_dict(self):
request = self._makeOne()
self._registerContextURL(request.registry)
context = DummyContext()
- uc = unicode('La Pe\xc3\xb1a', 'utf-8')
+ uc = text_(b'La Pe\xc3\xb1a', 'utf-8')
result = request.resource_url(context, 'a', query={'a':uc})
self.assertEqual(result,
'http://example.com/context/a?a=La+Pe%C3%B1a')
@@ -82,9 +84,9 @@ def test_resource_url_with_query_seq(self):
request = self._makeOne()
self._registerContextURL(request.registry)
context = DummyContext()
- uc = unicode('La Pe\xc3\xb1a', 'utf-8')
+ uc = text_(b'La Pe\xc3\xb1a', 'utf-8')
result = request.resource_url(context, 'a', query=[('a', 'hi there'),
- ('b', uc)])
+ ('b', uc)])
self.assertEqual(result,
'http://example.com/context/a?a=hi+there&b=La+Pe%C3%B1a')
@@ -117,10 +119,15 @@ def test_resource_url_anchor_is_encoded_utf8_if_unicode(self):
request = self._makeOne()
self._registerContextURL(request.registry)
context = DummyContext()
- uc = unicode('La Pe\xc3\xb1a', 'utf-8')
+ uc = text_(b'La Pe\xc3\xb1a', 'utf-8')
result = request.resource_url(context, anchor=uc)
- self.assertEqual(result,
- 'http://example.com/context/#La Pe\xc3\xb1a')
+ self.assertEqual(
+ result,
+ native_(
+ text_(b'http://example.com/context/#La Pe\xc3\xb1a',
+ 'utf-8'),
+ 'utf-8')
+ )
def test_resource_url_anchor_is_not_urlencoded(self):
request = self._makeOne()
@@ -172,28 +179,42 @@ def test_route_url_no_elements(self):
mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3'))
request.registry.registerUtility(mapper, IRoutesMapper)
result = request.route_url('flub', a=1, b=2, c=3, _query={'a':1},
- _anchor=u"foo")
+ _anchor=text_(b"foo"))
self.assertEqual(result,
'http://example.com:5432/1/2/3?a=1#foo')
- def test_route_url_with_anchor_string(self):
+ def test_route_url_with_anchor_binary(self):
from pyramid.interfaces import IRoutesMapper
request = self._makeOne()
mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3'))
request.registry.registerUtility(mapper, IRoutesMapper)
- result = request.route_url('flub', _anchor="La Pe\xc3\xb1a")
- self.assertEqual(result,
- 'http://example.com:5432/1/2/3#La Pe\xc3\xb1a')
+ result = request.route_url('flub', _anchor=b"La Pe\xc3\xb1a")
+
+ self.assertEqual(
+ result,
+ native_(
+ text_(
+ b'http://example.com:5432/1/2/3#La Pe\xc3\xb1a',
+ 'utf-8'),
+ 'utf-8')
+ )
def test_route_url_with_anchor_unicode(self):
from pyramid.interfaces import IRoutesMapper
request = self._makeOne()
mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3'))
request.registry.registerUtility(mapper, IRoutesMapper)
- anchor = unicode('La Pe\xc3\xb1a', 'utf-8')
+ anchor = text_(b'La Pe\xc3\xb1a', 'utf-8')
result = request.route_url('flub', _anchor=anchor)
- self.assertEqual(result,
- 'http://example.com:5432/1/2/3#La Pe\xc3\xb1a')
+
+ self.assertEqual(
+ result,
+ native_(
+ text_(
+ b'http://example.com:5432/1/2/3#La Pe\xc3\xb1a',
+ 'utf-8'),
+ 'utf-8')
+ )
def test_route_url_with_query(self):
from pyramid.interfaces import IRoutesMapper
@@ -281,7 +302,7 @@ def test_current_route_url_with_elements_query_and_anchor(self):
request.matchdict = {}
request.registry.registerUtility(mapper, IRoutesMapper)
result = request.current_route_url('extra1', 'extra2', _query={'a':1},
- _anchor=u"foo")
+ _anchor=text_(b"foo"))
self.assertEqual(result,
'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo')
@@ -294,7 +315,8 @@ def test_current_route_url_with_route_name(self):
request.matchdict = {}
request.registry.registerUtility(mapper, IRoutesMapper)
result = request.current_route_url('extra1', 'extra2', _query={'a':1},
- _anchor=u"foo", _route_name='bar')
+ _anchor=text_(b"foo"),
+ _route_name='bar')
self.assertEqual(result,
'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo')
@@ -308,7 +330,7 @@ def test_current_route_path(self):
request.script_name = '/script_name'
request.registry.registerUtility(mapper, IRoutesMapper)
result = request.current_route_path('extra1', 'extra2', _query={'a':1},
- _anchor=u"foo")
+ _anchor=text_(b"foo"))
self.assertEqual(result, '/script_name/1/2/3/extra1/extra2?a=1#foo')
def test_route_path_with_elements(self):
@@ -319,7 +341,7 @@ def test_route_path_with_elements(self):
request.script_name = ''
result = request.route_path('flub', 'extra1', 'extra2',
a=1, b=2, c=3, _query={'a':1},
- _anchor=u"foo")
+ _anchor=text_(b"foo"))
self.assertEqual(result, '/1/2/3/extra1/extra2?a=1#foo')
def test_route_path_with_script_name(self):
@@ -330,7 +352,7 @@ def test_route_path_with_script_name(self):
request.registry.registerUtility(mapper, IRoutesMapper)
result = request.route_path('flub', 'extra1', 'extra2',
a=1, b=2, c=3, _query={'a':1},
- _anchor=u"foo")
+ _anchor=text_(b"foo"))
self.assertEqual(result, '/foo/1/2/3/extra1/extra2?a=1#foo')
def test_static_url_staticurlinfo_notfound(self):
diff --git a/pyramid/tests/test_urldispatch.py b/pyramid/tests/test_urldispatch.py
index 3c92e87beb..be823b045c 100644
--- a/pyramid/tests/test_urldispatch.py
+++ b/pyramid/tests/test_urldispatch.py
@@ -1,5 +1,7 @@
import unittest
from pyramid import testing
+from pyramid.compat import text_
+from pyramid.compat import native_
class TestRoute(unittest.TestCase):
def _getTargetClass(self):
@@ -146,7 +148,7 @@ def test___call__route_fails_to_match_with_predicates(self):
def test___call__custom_predicate_gets_info(self):
mapper = self._makeOne()
def pred(info, request):
- self.assertEqual(info['match'], {'action':u'action1'})
+ self.assertEqual(info['match'], {'action':'action1'})
self.assertEqual(info['route'], mapper.routes['foo'])
return True
mapper.connect('foo', 'archives/:action/article1', predicates=[pred])
@@ -269,7 +271,7 @@ def test_with_star(self):
'traverse':('everything', 'else', 'here')})
self.assertEqual(matcher('foo/baz/biz/buz/bar'), None)
self.assertEqual(generator(
- {'baz':1, 'buz':2, 'traverse':u'/a/b'}), '/foo/1/biz/2/bar/a/b')
+ {'baz':1, 'buz':2, 'traverse':'/a/b'}), '/foo/1/biz/2/bar/a/b')
def test_with_bracket_star(self):
matcher, generator = self._callFUT(
@@ -293,7 +295,8 @@ def test_no_beginning_slash(self):
def test_url_decode_error(self):
from pyramid.exceptions import URLDecodeError
matcher, generator = self._callFUT('/:foo')
- self.assertRaises(URLDecodeError, matcher, '/%FF%FE%8B%00')
+ self.assertRaises(URLDecodeError, matcher,
+ native_(b'/\xff\xfe\x8b\x00'))
def test_custom_regex(self):
matcher, generator = self._callFUT('foo/{baz}/biz/{buz:[^/\.]+}.{bar}')
@@ -363,10 +366,13 @@ def test_matcher_functional_newstyle(self):
self.matches('zzz/{x}*traverse', '/zzz/abc/def/g',
{'x':'abc', 'traverse':('def', 'g')})
self.matches('*traverse', '/zzz/abc', {'traverse':('zzz', 'abc')})
- self.matches('*traverse', '/zzz/%20abc', {'traverse':('zzz', ' abc')})
- self.matches('{x}', '/La%20Pe%C3%B1a', {'x':u'La Pe\xf1a'})
- self.matches('*traverse', '/La%20Pe%C3%B1a/x',
- {'traverse':(u'La Pe\xf1a', 'x')})
+ self.matches('*traverse', '/zzz/ abc', {'traverse':('zzz', ' abc')})
+ #'/La%20Pe%C3%B1a'
+ self.matches('{x}', native_(b'/La Pe\xc3\xb1a'),
+ {'x':text_(b'La Pe\xf1a')})
+ # '/La%20Pe%C3%B1a/x'
+ self.matches('*traverse', native_(b'/La Pe\xc3\xb1a/x'),
+ {'traverse':(text_(b'La Pe\xf1a'), 'x')})
self.matches('/foo/{id}.html', '/foo/bar.html', {'id':'bar'})
self.matches('/{num:[0-9]+}/*traverse', '/555/abc/def',
{'num':'555', 'traverse':('abc', 'def')})
@@ -386,10 +392,13 @@ def test_matcher_functional_oldstyle(self):
self.matches('zzz/:x*traverse', '/zzz/abc/def/g',
{'x':'abc', 'traverse':('def', 'g')})
self.matches('*traverse', '/zzz/abc', {'traverse':('zzz', 'abc')})
- self.matches('*traverse', '/zzz/%20abc', {'traverse':('zzz', ' abc')})
- self.matches(':x', '/La%20Pe%C3%B1a', {'x':u'La Pe\xf1a'})
- self.matches('*traverse', '/La%20Pe%C3%B1a/x',
- {'traverse':(u'La Pe\xf1a', 'x')})
+ self.matches('*traverse', '/zzz/ abc', {'traverse':('zzz', ' abc')})
+ #'/La%20Pe%C3%B1a'
+ self.matches(':x', native_(b'/La Pe\xc3\xb1a'),
+ {'x':text_(b'La Pe\xf1a')})
+ # '/La%20Pe%C3%B1a/x'
+ self.matches('*traverse', native_(b'/La Pe\xc3\xb1a/x'),
+ {'traverse':(text_(b'La Pe\xf1a'), 'x')})
self.matches('/foo/:id.html', '/foo/bar.html', {'id':'bar'})
self.matches('/foo/:id_html', '/foo/bar_html', {'id_html':'bar_html'})
self.matches('zzz/:_', '/zzz/abc', {'_':'abc'})
@@ -408,12 +417,12 @@ def test_generator_functional_newstyle(self):
'/zzz/abc')
self.generates('zzz/{x}*traverse', {'x':'abc', 'traverse':'/def/g'},
'/zzz/abc/def/g')
- self.generates('/{x}', {'x':unicode('/La Pe\xc3\xb1a', 'utf-8')},
+ self.generates('/{x}', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8')},
'/%2FLa%20Pe%C3%B1a')
- self.generates('/{x}*y', {'x':unicode('/La Pe\xc3\xb1a', 'utf-8'),
+ self.generates('/{x}*y', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8'),
'y':'/rest/of/path'},
'/%2FLa%20Pe%C3%B1a/rest/of/path')
- self.generates('*traverse', {'traverse':('a', u'La Pe\xf1a')},
+ self.generates('*traverse', {'traverse':('a', text_(b'La Pe\xf1a'))},
'/a/La%20Pe%C3%B1a')
self.generates('/foo/{id}.html', {'id':'bar'}, '/foo/bar.html')
self.generates('/foo/{_}', {'_':'20'}, '/foo/20')
@@ -428,12 +437,12 @@ def test_generator_functional_oldstyle(self):
'/zzz/abc')
self.generates('zzz/:x*traverse', {'x':'abc', 'traverse':'/def/g'},
'/zzz/abc/def/g')
- self.generates('/:x', {'x':unicode('/La Pe\xc3\xb1a', 'utf-8')},
+ self.generates('/:x', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8')},
'/%2FLa%20Pe%C3%B1a')
- self.generates('/:x*y', {'x':unicode('/La Pe\xc3\xb1a', 'utf-8'),
+ self.generates('/:x*y', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8'),
'y':'/rest/of/path'},
'/%2FLa%20Pe%C3%B1a/rest/of/path')
- self.generates('*traverse', {'traverse':('a', u'La Pe\xf1a')},
+ self.generates('*traverse', {'traverse':('a', text_(b'La Pe\xf1a'))},
'/a/La%20Pe%C3%B1a')
self.generates('/foo/:id.html', {'id':'bar'}, '/foo/bar.html')
self.generates('/foo/:_', {'_':'20'}, '/foo/20')
diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py
index 247b61dad5..2883a968c3 100644
--- a/pyramid/tests/test_util.py
+++ b/pyramid/tests/test_util.py
@@ -9,7 +9,7 @@ def config_exc(self, func, *arg, **kw):
from pyramid.exceptions import ConfigurationError
try:
func(*arg, **kw)
- except ConfigurationError, e:
+ except ConfigurationError as e:
return e
else:
raise AssertionError('Invalid not raised') # pragma: no cover
@@ -190,7 +190,7 @@ def test_add_item(self):
reg = Dummy()
wos.add(reg)
self.assertEqual(list(wos), [reg])
- self.assert_(reg in wos)
+ self.assertTrue(reg in wos)
self.assertEqual(wos.last, reg)
def test_add_multiple_items(self):
@@ -201,8 +201,8 @@ def test_add_multiple_items(self):
wos.add(reg2)
self.assertEqual(len(wos), 2)
self.assertEqual(list(wos), [reg1, reg2])
- self.assert_(reg1 in wos)
- self.assert_(reg2 in wos)
+ self.assertTrue(reg1 in wos)
+ self.assertTrue(reg2 in wos)
self.assertEqual(wos.last, reg2)
def test_add_duplicate_items(self):
@@ -212,7 +212,7 @@ def test_add_duplicate_items(self):
wos.add(reg)
self.assertEqual(len(wos), 1)
self.assertEqual(list(wos), [reg])
- self.assert_(reg in wos)
+ self.assertTrue(reg in wos)
self.assertEqual(wos.last, reg)
def test_weakref_removal(self):
diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py
index 7f66a7563e..29e468cd2c 100644
--- a/pyramid/tests/test_view.py
+++ b/pyramid/tests/test_view.py
@@ -1,6 +1,8 @@
import unittest
import sys
+from zope.interface import implementer
+
from pyramid.testing import setUp
from pyramid.testing import tearDown
@@ -406,8 +408,9 @@ def foo(): pass
def test_call_with_renderer_IRendererInfo(self):
import pyramid.tests
from pyramid.interfaces import IRendererInfo
+ @implementer(IRendererInfo)
class DummyRendererHelper(object):
- implements(IRendererInfo)
+ pass
renderer_helper = DummyRendererHelper()
decorator = self._makeOne(renderer=renderer_helper)
venusian = DummyVenusian()
@@ -588,10 +591,9 @@ def __init__(self, environ=None):
self.environ = environ
from pyramid.interfaces import IResponse
-from zope.interface import implements
+@implementer(IResponse)
class DummyResponse(object):
- implements(IResponse)
headerlist = ()
app_iter = ()
status = '200 OK'
diff --git a/pyramid/traversal.py b/pyramid/traversal.py
index bf2606529e..ee6b5fb7ac 100644
--- a/pyramid/traversal.py
+++ b/pyramid/traversal.py
@@ -1,7 +1,6 @@
-import urllib
import warnings
-from zope.interface import implements
+from zope.interface import implementer
from zope.interface.interfaces import IInterface
from repoze.lru import lru_cache
@@ -11,11 +10,22 @@
from pyramid.interfaces import ITraverser
from pyramid.interfaces import VH_ROOT_KEY
+from pyramid.compat import PY3
+from pyramid.compat import native_
+from pyramid.compat import text_
+from pyramid.compat import bytes_
+from pyramid.compat import ascii_native_
+from pyramid.compat import text_type
+from pyramid.compat import binary_type
+from pyramid.compat import url_unquote_native
+from pyramid.compat import is_nonstr_iter
from pyramid.encode import url_quote
from pyramid.exceptions import URLDecodeError
from pyramid.location import lineage
from pyramid.threadlocal import get_current_registry
+empty = text_('')
+
def find_root(resource):
""" Find the root node in the resource tree to which ``resource``
belongs. Note that ``resource`` should be :term:`location`-aware.
@@ -58,6 +68,10 @@ def find_resource(resource, path):
:func:`pyramid.traversal.resource_path` function generates strings
which follow these rules (albeit only absolute ones).
+ Rules for passing *text* (Unicode) as the ``path`` argument are the same
+ as those for a string. In particular, the text may not have any nonascii
+ characters in it.
+
Rules for passing a *tuple* as the ``path`` argument: if the first
element in the path tuple is the empty string (for example ``('',
'a', 'b', 'c')``, the path is considered absolute and the resource tree
@@ -77,6 +91,8 @@ def find_resource(resource, path):
be imported as :func:`pyramid.traversal.find_model`, although doing so
will emit a deprecation warning.
"""
+ if isinstance(path, text_type):
+ path = ascii_native_(path)
D = traverse(resource, path)
view_name = D['view_name']
context = D['context']
@@ -276,7 +292,7 @@ def traverse(resource, path):
and will be URL-decoded.
"""
- if hasattr(path, '__iter__'):
+ if is_nonstr_iter(path):
# the traverser factory expects PATH_INFO to be a string, not
# unicode and it expects path segments to be utf-8 and
# urlencoded (it's the same traverser which accepts PATH_INFO
@@ -294,8 +310,7 @@ def traverse(resource, path):
# step rather than later down the line as the result of calling
# ``traversal_path``).
- if isinstance(path, unicode):
- path = path.encode('ascii')
+ path = ascii_native_(path)
if path and path[0] == '/':
resource = find_root(resource)
@@ -407,26 +422,35 @@ def virtual_root(resource, request):
urlgenerator = TraversalContextURL(resource, request)
return urlgenerator.virtual_root()
-@lru_cache(1000)
def traversal_path(path):
- """ Given a ``PATH_INFO`` string (slash-separated path segments),
- return a tuple representing that path which can be used to
- traverse a resource tree.
-
- The ``PATH_INFO`` is split on slashes, creating a list of
- segments. Each segment is URL-unquoted, and subsequently decoded
- into Unicode. Each segment is assumed to be encoded using the
- UTF-8 encoding (or a subset, such as ASCII); a
- :exc:`pyramid.exceptions.URLDecodeError` is raised if a segment
- cannot be decoded. If a segment name is empty or if it is ``.``,
- it is ignored. If a segment name is ``..``, the previous segment
- is deleted, and the ``..`` is ignored.
+ """ Variant of :func:`pyramid.traversal.traversal_path_info` suitable for
+ decoding paths that are URL-encoded."""
+ path = ascii_native_(path)
+ path = url_unquote_native(path, 'latin-1', 'strict')
+ return traversal_path_info(path)
- If this function is passed a Unicode object instead of a string,
- that Unicode object *must* directly encodeable to ASCII. For
- example, u'/foo' will work but u'/' (a
- Unicode object with characters that cannot be encoded to ascii)
- will not.
+@lru_cache(1000)
+def traversal_path_info(path):
+ """ Given a ``PATH_INFO`` environ value (slash-separated path segments),
+ return a tuple representing that path which can be used to traverse a
+ resource tree.
+
+ ``PATH_INFO`` is assumed to already be URL-decoded. It is encoded to
+ bytes using the Latin-1 encoding; the resulting set of bytes is
+ subsequently decoded to text using the UTF-8 encoding; a
+ :exc:`pyramid.exc.URLDecodeError` is raised if a the URL cannot be
+ decoded.
+
+ The ``PATH_INFO`` is split on slashes, creating a list of segments. Each
+ segment subsequently decoded into Unicode. If a segment name is empty or
+ if it is ``.``, it is ignored. If a segment name is ``..``, the previous
+ segment is deleted, and the ``..`` is ignored.
+
+ If this function is passed a Unicode object instead of a string, that
+ Unicode object *must* directly encodeable to ASCII. For example, u'/foo'
+ will work but u'/' (a Unicode object with characters
+ that cannot be encoded to ascii) will not. A :exc:`UnicodeError` will be
+ raised if the Unicode cannot be encoded directly to ASCII.
Examples:
@@ -474,16 +498,13 @@ def traversal_path(path):
writing their own traversal machinery, as opposed to users writing
applications in :app:`Pyramid`.
"""
- if isinstance(path, unicode):
- path = path.encode('ascii')
+ try:
+ path = bytes_(path, 'latin-1').decode('utf-8')
+ except UnicodeDecodeError as e:
+ raise URLDecodeError(e.encoding, e.object, e.start, e.end, e.reason)
path = path.strip('/')
clean = []
for segment in path.split('/'):
- segment = urllib.unquote(segment)
- try:
- segment = segment.decode('utf-8')
- except UnicodeDecodeError, e:
- raise URLDecodeError(e.encoding, e.object, e.start, e.end, e.reason)
if not segment or segment == '.':
continue
elif segment == '..':
@@ -495,55 +516,82 @@ def traversal_path(path):
_segment_cache = {}
-def quote_path_segment(segment, safe=''):
- """ Return a quoted representation of a 'path segment' (such as
- the string ``__name__`` attribute of a resource) as a string. If the
- ``segment`` passed in is a unicode object, it is converted to a
- UTF-8 string, then it is URL-quoted using Python's
- ``urllib.quote``. If the ``segment`` passed in is a string, it is
- URL-quoted using Python's :mod:`urllib.quote`. If the segment
- passed in is not a string or unicode object, an error will be
- raised. The return value of ``quote_path_segment`` is always a
- string, never Unicode.
-
- You may pass a string of characters that need not be encoded as
- the ``safe`` argument to this function. This corresponds to the
- ``safe`` argument to :mod:`urllib.quote`.
-
- .. note:: The return value for each segment passed to this
- function is cached in a module-scope dictionary for
- speed: the cached version is returned when possible
- rather than recomputing the quoted version. No cache
- emptying is ever done for the lifetime of an
- application, however. If you pass arbitrary
- user-supplied strings to this function (as opposed to
- some bounded set of values from a 'working set' known to
- your application), it may become a memory leak.
- """
- # The bit of this code that deals with ``_segment_cache`` is an
- # optimization: we cache all the computation of URL path segments
- # in this module-scope dictionary with the original string (or
- # unicode value) as the key, so we can look it up later without
- # needing to reencode or re-url-quote it
- try:
- return _segment_cache[(segment, safe)]
- except KeyError:
- if segment.__class__ is unicode: # isinstance slighly slower (~15%)
- result = url_quote(segment.encode('utf-8'), safe)
- else:
- result = url_quote(str(segment), safe)
- # we don't need a lock to mutate _segment_cache, as the below
- # will generate exactly one Python bytecode (STORE_SUBSCR)
- _segment_cache[(segment, safe)] = result
- return result
-
+quote_path_segment_doc = """ \
+Return a quoted representation of a 'path segment' (such as
+the string ``__name__`` attribute of a resource) as a string. If the
+``segment`` passed in is a unicode object, it is converted to a
+UTF-8 string, then it is URL-quoted using Python's
+``urllib.quote``. If the ``segment`` passed in is a string, it is
+URL-quoted using Python's :mod:`urllib.quote`. If the segment
+passed in is not a string or unicode object, an error will be
+raised. The return value of ``quote_path_segment`` is always a
+string, never Unicode.
+
+You may pass a string of characters that need not be encoded as
+the ``safe`` argument to this function. This corresponds to the
+``safe`` argument to :mod:`urllib.quote`.
+
+.. note::
+
+ The return value for each segment passed to this
+ function is cached in a module-scope dictionary for
+ speed: the cached version is returned when possible
+ rather than recomputing the quoted version. No cache
+ emptying is ever done for the lifetime of an
+ application, however. If you pass arbitrary
+ user-supplied strings to this function (as opposed to
+ some bounded set of values from a 'working set' known to
+ your application), it may become a memory leak.
+"""
+
+
+if PY3: # pragma: no cover
+ # special-case on Python 2 for speed? unchecked
+ def quote_path_segment(segment, safe=''):
+ """ %s """ % quote_path_segment_doc
+ # The bit of this code that deals with ``_segment_cache`` is an
+ # optimization: we cache all the computation of URL path segments
+ # in this module-scope dictionary with the original string (or
+ # unicode value) as the key, so we can look it up later without
+ # needing to reencode or re-url-quote it
+ try:
+ return _segment_cache[(segment, safe)]
+ except KeyError:
+ if segment.__class__ not in (text_type, binary_type):
+ segment = str(segment)
+ result = url_quote(native_(segment, 'utf-8'), safe)
+ # we don't need a lock to mutate _segment_cache, as the below
+ # will generate exactly one Python bytecode (STORE_SUBSCR)
+ _segment_cache[(segment, safe)] = result
+ return result
+else:
+ def quote_path_segment(segment, safe=''):
+ """ %s """ % quote_path_segment_doc
+ # The bit of this code that deals with ``_segment_cache`` is an
+ # optimization: we cache all the computation of URL path segments
+ # in this module-scope dictionary with the original string (or
+ # unicode value) as the key, so we can look it up later without
+ # needing to reencode or re-url-quote it
+ try:
+ return _segment_cache[(segment, safe)]
+ except KeyError:
+ if segment.__class__ is text_type: #isinstance slighly slower (~15%)
+ result = url_quote(segment.encode('utf-8'), safe)
+ else:
+ result = url_quote(str(segment), safe)
+ # we don't need a lock to mutate _segment_cache, as the below
+ # will generate exactly one Python bytecode (STORE_SUBSCR)
+ _segment_cache[(segment, safe)] = result
+ return result
+
+
+@implementer(ITraverser)
class ResourceTreeTraverser(object):
""" A resource tree traverser that should be used (for speed) when
every resource in the tree supplies a ``__name__`` and
``__parent__`` attribute (ie. every resource in the tree is
:term:`location` aware) ."""
- implements(ITraverser)
VIEW_SELECTOR = '@@'
@@ -567,14 +615,14 @@ def __call__(self, request):
matchdict = environ['bfg.routes.matchdict']
path = matchdict.get('traverse', '/') or '/'
- if hasattr(path, '__iter__'):
+ if is_nonstr_iter(path):
# this is a *traverse stararg (not a {traverse})
path = '/'.join([quote_path_segment(x) for x in path]) or '/'
subpath = matchdict.get('subpath', ())
- if not hasattr(subpath, '__iter__'):
+ if not is_nonstr_iter(subpath):
# this is not a *subpath stararg (just a {subpath})
- subpath = traversal_path(subpath)
+ subpath = traversal_path_info(subpath)
else:
# this request did not match a route
@@ -586,7 +634,7 @@ def __call__(self, request):
if VH_ROOT_KEY in environ:
vroot_path = environ[VH_ROOT_KEY]
- vroot_tuple = traversal_path(vroot_path)
+ vroot_tuple = traversal_path_info(vroot_path)
vpath = vroot_path + path
vroot_idx = len(vroot_tuple) -1
else:
@@ -607,7 +655,7 @@ def __call__(self, request):
# and this hurts readability; apologies
i = 0
view_selector = self.VIEW_SELECTOR
- vpath_tuple = traversal_path(vpath)
+ vpath_tuple = traversal_path_info(vpath)
for segment in vpath_tuple:
if segment[:2] == view_selector:
return {'context':ob,
@@ -643,16 +691,16 @@ def __call__(self, request):
ob = next
i += 1
- return {'context':ob, 'view_name':u'', 'subpath':subpath,
+ return {'context':ob, 'view_name':empty, 'subpath':subpath,
'traversed':vpath_tuple, 'virtual_root':vroot,
'virtual_root_path':vroot_tuple, 'root':root}
ModelGraphTraverser = ResourceTreeTraverser # b/w compat, not API, used in wild
+@implementer(IContextURL)
class TraversalContextURL(object):
""" The IContextURL adapter used to generate URLs for a resource in a
resource tree"""
- implements(IContextURL)
vroot_varname = VH_ROOT_KEY
diff --git a/pyramid/tweens.py b/pyramid/tweens.py
index b15204e9d5..65d7c39194 100644
--- a/pyramid/tweens.py
+++ b/pyramid/tweens.py
@@ -15,7 +15,7 @@ def excview_tween(request):
attrs = request.__dict__
try:
response = handler(request)
- except Exception, exc:
+ except Exception as exc:
# WARNING: do not assign the result of sys.exc_info() to a
# local var here, doing so will cause a leak
attrs['exc_info'] = sys.exc_info()
diff --git a/pyramid/url.py b/pyramid/url.py
index c2ff43ddde..7a7dd3b4cd 100644
--- a/pyramid/url.py
+++ b/pyramid/url.py
@@ -8,6 +8,8 @@
from pyramid.interfaces import IRoutesMapper
from pyramid.interfaces import IStaticURLInfo
+from pyramid.compat import native_
+from pyramid.compat import text_type
from pyramid.encode import urlencode
from pyramid.path import caller_package
from pyramid.threadlocal import get_current_registry
@@ -137,8 +139,7 @@ def route_url(self, route_name, *elements, **kw):
if '_anchor' in kw:
anchor = kw.pop('_anchor')
- if isinstance(anchor, unicode):
- anchor = anchor.encode('utf-8')
+ anchor = native_(anchor, 'utf-8')
anchor = '#' + anchor
if '_app_url' in kw:
@@ -310,8 +311,8 @@ def resource_url(self, resource, *elements, **kw):
if 'anchor' in kw:
anchor = kw['anchor']
- if isinstance(anchor, unicode):
- anchor = anchor.encode('utf-8')
+ if isinstance(anchor, text_type):
+ anchor = native_(anchor, 'utf-8')
anchor = '#' + anchor
if elements:
diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py
index 73318193c5..6626158458 100644
--- a/pyramid/urldispatch.py
+++ b/pyramid/urldispatch.py
@@ -1,19 +1,23 @@
import re
-from urllib import unquote
-from zope.interface import implements
+from zope.interface import implementer
from pyramid.interfaces import IRoutesMapper
from pyramid.interfaces import IRoute
-from pyramid.encode import url_quote
+from pyramid.compat import native_
+from pyramid.compat import bytes_
+from pyramid.compat import text_type
+from pyramid.compat import string_types
+from pyramid.compat import is_nonstr_iter
+from pyramid.compat import url_quote
from pyramid.exceptions import URLDecodeError
-from pyramid.traversal import traversal_path
+from pyramid.traversal import traversal_path_info
from pyramid.traversal import quote_path_segment
_marker = object()
+@implementer(IRoute)
class Route(object):
- implements(IRoute)
def __init__(self, name, pattern, factory=None, predicates=(),
pregenerator=None):
self.pattern = pattern
@@ -24,8 +28,8 @@ def __init__(self, name, pattern, factory=None, predicates=(),
self.predicates = predicates
self.pregenerator = pregenerator
+@implementer(IRoutesMapper)
class RoutesMapper(object):
- implements(IRoutesMapper)
def __init__(self):
self.routelist = []
self.routes = {}
@@ -133,14 +137,14 @@ def matcher(path):
if m is None:
return m
d = {}
- for k, v in m.groupdict().iteritems():
+ for k, v in m.groupdict().items():
if k == star:
- d[k] = traversal_path(v)
+ d[k] = traversal_path_info(v)
else:
- encoded = unquote(v)
try:
- d[k] = encoded.decode('utf-8')
- except UnicodeDecodeError, e:
+ val = bytes_(v).decode('utf-8', 'strict')
+ d[k] = val
+ except UnicodeDecodeError as e:
raise URLDecodeError(
e.encoding, e.object, e.start, e.end, e.reason
)
@@ -153,15 +157,14 @@ def matcher(path):
def generator(dict):
newdict = {}
for k, v in dict.items():
- if isinstance(v, unicode):
- v = v.encode('utf-8')
- if k == star and hasattr(v, '__iter__'):
+ if v.__class__ is text_type:
+ v = native_(v, 'utf-8')
+ if k == star and is_nonstr_iter(v):
v = '/'.join([quote_path_segment(x) for x in v])
elif k != star:
- try:
- v = url_quote(v)
- except TypeError:
- pass
+ if v.__class__ not in string_types:
+ v = str(v)
+ v = url_quote(v, safe='')
newdict[k] = v
return gen % newdict
diff --git a/pyramid/util.py b/pyramid/util.py
index c0e7640c41..a43b50aef3 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -2,6 +2,7 @@
import sys
import weakref
+from pyramid.compat import string_types
from pyramid.exceptions import ConfigurationError
from pyramid.path import package_of
@@ -69,7 +70,7 @@ def __init__(self, package):
self.package_name = None
self.package = None
else:
- if isinstance(package, basestring):
+ if isinstance(package, string_types):
try:
__import__(package)
except ImportError:
@@ -132,12 +133,12 @@ def _zope_dottedname_style(self, value):
return found
def resolve(self, dotted):
- if not isinstance(dotted, basestring):
+ if not isinstance(dotted, string_types):
raise ConfigurationError('%r is not a string' % (dotted,))
return self.maybe_resolve(dotted)
def maybe_resolve(self, dotted):
- if isinstance(dotted, basestring):
+ if isinstance(dotted, string_types):
if ':' in dotted:
return self._pkg_resources_style(dotted)
else:
diff --git a/pyramid/view.py b/pyramid/view.py
index 6046408f65..581e421853 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -7,6 +7,7 @@
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
+from pyramid.compat import map_
from pyramid.httpexceptions import HTTPFound
from pyramid.httpexceptions import default_exceptionresponse_view
from pyramid.path import caller_package
@@ -52,7 +53,7 @@ def render_view_to_response(context, request, name='', secure=True):
disallowed.
If ``secure`` is ``False``, no permission checking is done."""
- provides = [IViewClassifier] + map(providedBy, (request, context))
+ provides = [IViewClassifier] + map_(providedBy, (request, context))
try:
reg = request.registry
except AttributeError:
diff --git a/setup.py b/setup.py
index 82f4471037..08aa1643e3 100644
--- a/setup.py
+++ b/setup.py
@@ -13,11 +13,15 @@
##############################################################################
import os
-import platform
import sys
from setuptools import setup, find_packages
+if sys.version_info[:2] < (2, 6):
+ raise RuntimeError('Requires Python 2.6 or better')
+
+PY3 = sys.version_info[0] == 3
+
here = os.path.abspath(os.path.dirname(__file__))
try:
README = open(os.path.join(here, 'README.rst')).read()
@@ -26,38 +30,37 @@
README = CHANGES = ''
install_requires=[
+ 'setuptools',
'Chameleon >= 1.2.3',
'Mako >= 0.3.6', # strict_undefined
- 'Paste > 1.7', # temp version pin to prevent PyPi install failure :-(
- 'PasteDeploy',
- 'PasteScript >= 1.7.4', # "here" in logging fileConfig
- 'WebOb >= 1.0.2', # no "default_charset"; request.script_name doesnt error
- 'repoze.lru',
- 'setuptools',
- 'zope.component >= 3.6.0', # independent of zope.hookable
- 'zope.interface >= 3.5.1', # 3.5.0 comment: "allow to bootstrap on jython"
- 'zope.deprecation',
+ 'WebOb >= 1.2dev', # response.text / py3 compat
+ 'repoze.lru >= 0.4', # py3 compat
+ 'zope.interface >= 3.8.0', # has zope.interface.registry
+ 'zope.deprecation >= 3.5.0', # py3 compat
'venusian >= 1.0a1', # ``onerror``
- 'translationstring',
+ 'translationstring >= 0.4', # py3 compat
]
-if platform.system() == 'Java':
- tests_require = install_requires + [
- 'WebTest',
- 'virtualenv',
- ]
-else:
- tests_require= install_requires + [
+if not PY3:
+ install_requires.extend([
+ 'Paste > 1.7', # temp version pin to prevent PyPi install failure :-(
+ 'PasteDeploy',
+ 'PasteScript >= 1.7.4', # "here" in logging fileConfig
+ ])
+
+tests_require = install_requires + [
+ 'WebTest >= 1.3.1', # py3 compat
+ 'virtualenv',
+ ]
+
+if not PY3:
+ tests_require.extend([
'Sphinx',
'docutils',
'repoze.sphinx.autointerface',
- 'WebTest',
- 'virtualenv',
- ]
+ 'zope.component>=3.11.0',
+ ])
-if sys.version_info[:2] < (2, 6):
- install_requires.append('simplejson')
-
setup(name='pyramid',
version='1.2',
description=('The Pyramid web application development framework, a '
diff --git a/tox.ini b/tox.ini
index 40711a5f2f..a422b2eb69 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,19 +1,25 @@
[tox]
envlist =
- py25,py26,py27,jython,pypy,cover
+ py26,py27,py32,pypy,cover
[testenv]
commands =
python setup.py test -q
deps =
+ https://github.com/Pylons/webob/zipball/master
+ zope.component
Sphinx
- WebTest
repoze.sphinx.autointerface
+ WebTest
virtualenv
-[testenv:jython]
+[testenv:py32]
commands =
- jython setup.py test -q
+ python setup.py test -q
+deps =
+ https://github.com/Pylons/webob/zipball/master
+ WebTest
+ virtualenv
[testenv:cover]
basepython =
@@ -21,6 +27,8 @@ basepython =
commands =
python setup.py nosetests --with-xunit --with-xcoverage
deps =
+ https://github.com/Pylons/webob/zipball/master
+ zope.component
Sphinx
WebTest
repoze.sphinx.autointerface