summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-09-28 04:48:39 -0400
committerChris McDonough <chrism@plope.com>2011-09-28 04:48:39 -0400
commit311a894e32936a1087cb44543183da67d8c7fdec (patch)
tree98ae57cf94166e698351ab4f16d0a83236765687
parentbbc4f46ccf6b3e9c2235f0fd60622a0deff6b30e (diff)
parentdd5a91eb937369d06f3fc438c817e046fc81f891 (diff)
downloadpyramid-311a894e32936a1087cb44543183da67d8c7fdec.tar.gz
pyramid-311a894e32936a1087cb44543183da67d8c7fdec.tar.bz2
pyramid-311a894e32936a1087cb44543183da67d8c7fdec.zip
Merge branch 'token-reissue' of https://github.com/wichert/pyramid into wichert-token-reissue
-rw-r--r--CHANGES.txt481
-rw-r--r--HISTORY.txt467
-rw-r--r--pyramid/asset.py4
-rw-r--r--pyramid/authentication.py61
-rw-r--r--pyramid/authorization.py5
-rw-r--r--pyramid/chameleon_text.py8
-rw-r--r--pyramid/chameleon_zpt.py8
-rw-r--r--pyramid/compat.py218
-rw-r--r--pyramid/config/__init__.py27
-rw-r--r--pyramid/config/assets.py4
-rw-r--r--pyramid/config/settings.py4
-rw-r--r--pyramid/config/testing.py4
-rw-r--r--pyramid/config/tweens.py21
-rw-r--r--pyramid/config/util.py44
-rw-r--r--pyramid/config/views.py33
-rw-r--r--pyramid/encode.py86
-rw-r--r--pyramid/events.py14
-rw-r--r--pyramid/exceptions.py11
-rw-r--r--pyramid/fixers/fix_bfg_imports.py1
-rw-r--r--pyramid/httpexceptions.py23
-rw-r--r--pyramid/i18n.py26
-rw-r--r--pyramid/interfaces.py26
-rw-r--r--pyramid/mako_templating.py18
-rw-r--r--pyramid/paster.py35
-rw-r--r--pyramid/registry.py9
-rw-r--r--pyramid/renderers.py14
-rw-r--r--pyramid/request.py21
-rw-r--r--pyramid/resource.py2
-rw-r--r--pyramid/response.py6
-rw-r--r--pyramid/router.py4
-rw-r--r--pyramid/scaffolds/__init__.py17
-rw-r--r--pyramid/scaffolds/tests.py6
-rw-r--r--pyramid/security.py3
-rw-r--r--pyramid/session.py37
-rw-r--r--pyramid/settings.py5
-rw-r--r--pyramid/static.py15
-rw-r--r--pyramid/testing.py59
-rw-r--r--pyramid/tests/fixtures/helloworld.mak4
-rw-r--r--pyramid/tests/fixtures/helloworld.mako4
-rw-r--r--pyramid/tests/pkgs/exceptionviewapp/views.py2
-rw-r--r--pyramid/tests/pkgs/fixtureapp/__init__.py2
-rw-r--r--pyramid/tests/pkgs/forbiddenapp/__init__.py3
-rw-r--r--pyramid/tests/pkgs/permbugapp/__init__.py4
-rw-r--r--pyramid/tests/pkgs/wsgiapp2app/__init__.py2
-rw-r--r--pyramid/tests/test_authentication.py49
-rw-r--r--pyramid/tests/test_chameleon_text.py19
-rw-r--r--pyramid/tests/test_chameleon_zpt.py11
-rw-r--r--pyramid/tests/test_config/__init__.py4
-rw-r--r--pyramid/tests/test_config/test_adapters.py24
-rw-r--r--pyramid/tests/test_config/test_assets.py44
-rw-r--r--pyramid/tests/test_config/test_i18n.py2
-rw-r--r--pyramid/tests/test_config/test_init.py64
-rw-r--r--pyramid/tests/test_config/test_testing.py10
-rw-r--r--pyramid/tests/test_config/test_views.py63
-rw-r--r--pyramid/tests/test_docs.py3
-rw-r--r--pyramid/tests/test_encode.py36
-rw-r--r--pyramid/tests/test_httpexceptions.py59
-rw-r--r--pyramid/tests/test_i18n.py18
-rw-r--r--pyramid/tests/test_integration.py195
-rw-r--r--pyramid/tests/test_location.py4
-rw-r--r--pyramid/tests/test_mako_templating.py46
-rw-r--r--pyramid/tests/test_paster.py37
-rw-r--r--pyramid/tests/test_registry.py6
-rw-r--r--pyramid/tests/test_renderers.py35
-rw-r--r--pyramid/tests/test_request.py52
-rw-r--r--pyramid/tests/test_router.py47
-rw-r--r--pyramid/tests/test_scaffolds.py2
-rw-r--r--pyramid/tests/test_session.py20
-rw-r--r--pyramid/tests/test_settings.py2
-rw-r--r--pyramid/tests/test_static.py42
-rw-r--r--pyramid/tests/test_testing.py174
-rw-r--r--pyramid/tests/test_traversal.py162
-rw-r--r--pyramid/tests/test_url.py62
-rw-r--r--pyramid/tests/test_urldispatch.py43
-rw-r--r--pyramid/tests/test_util.py19
-rw-r--r--pyramid/tests/test_view.py8
-rw-r--r--pyramid/traversal.py208
-rw-r--r--pyramid/tweens.py2
-rw-r--r--pyramid/url.py9
-rw-r--r--pyramid/urldispatch.py39
-rw-r--r--pyramid/util.py7
-rw-r--r--pyramid/view.py3
-rw-r--r--setup.py55
-rw-r--r--tox.ini16
84 files changed, 2039 insertions, 1510 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index e90c5e127..047db6472 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 5cabb0111..956f07362 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 730969a4a..4bf0d7bf4 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 e2014b9a1..ed422b044 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 @@ class CallbackAuthenticationPolicy(object):
)
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 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy):
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 @@ class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy):
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 @@ class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
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 @@ class AuthTicket(object):
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 @@ class AuthTktCookieHelper(object):
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 @@ class AuthTktCookieHelper(object):
user_data = 'userid_type:%s' % encoding
for token in tokens:
+ if isinstance(token, text_type):
+ try:
+ token = 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 @@ class AuthTktCookieHelper(object):
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 ac8f195f2..b1ef10033 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 Allow
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 676985853..872d3b920 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 @@ except ImportError: # pragma: no cover
# 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 @@ from pyramid.path import caller_package
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 ca96d9356..aa6f89e07 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 @@ except ImportError: # pragma: no cover
# 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 @@ from pyramid import renderers
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 7d723715e..e686be27d 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 ImportError: # pragma: no cover
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 9effeed17..8085bbc79 100644
--- a/pyramid/config/__init__.py
+++ b/pyramid/config/__init__.py
@@ -13,6 +13,10 @@ from pyramid.interfaces import IExceptionResponse
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.util import action_method
from pyramid.config.views import ViewsConfiguratorMixin
from pyramid.config.zca import ZCAConfiguratorMixin
+empty = text_('')
+
ConfigurationError = ConfigurationError # pyflakes
class Configurator(
@@ -284,7 +290,7 @@ class Configurator(
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 @@ class Configurator(
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 @@ class Configurator(
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 @@ class Configurator(
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 @@ class ActionState(object):
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 1b5254072..08cc6dc38 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 @@ class OverrideProvider(pkg_resources.DefaultProvider):
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 6e636bf58..565a6699c 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 @@ class SettingsConfiguratorMixin(object):
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 0f709f634..36729acdf 100644
--- a/pyramid/config/testing.py
+++ b/pyramid/config/testing.py
@@ -6,7 +6,7 @@ from pyramid.interfaces import IAuthenticationPolicy
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 @@ class TestingConfiguratorMixin(object):
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 2704b89c1..3c7ee384f 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 @@ class TweensConfiguratorMixin(object):
@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 @@ class TweensConfiguratorMixin(object):
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 @@ class TweensConfiguratorMixin(object):
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 @@ class CyclicDependencyError(Exception):
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 @@ class Tweens(object):
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 @@ class Tweens(object):
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 1e54213ac..0336b103d 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 make_predicates(xhr=None, request_method=None, path_info=None,
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 make_predicates(xhr=None, request_method=None, path_info=None,
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 make_predicates(xhr=None, request_method=None, path_info=None,
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 make_predicates(xhr=None, request_method=None, path_info=None,
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 make_predicates(xhr=None, request_method=None, path_info=None,
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 make_predicates(xhr=None, request_method=None, path_info=None,
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 make_predicates(xhr=None, request_method=None, path_info=None,
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 make_predicates(xhr=None, request_method=None, path_info=None,
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 make_predicates(xhr=None, request_method=None, path_info=None,
# 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 make_predicates(xhr=None, request_method=None, path_info=None,
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 make_predicates(xhr=None, request_method=None, path_info=None,
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 3da41861d..179d4065c 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 IViewMapperFactory
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 action_method
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 @@ class ViewDeriver(object):
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 @@ class DefaultViewMapper(object):
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 @@ class ViewsConfiguratorMixin(object):
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 @@ class ViewsConfiguratorMixin(object):
'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 @@ class ViewsConfiguratorMixin(object):
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 @@ class ViewsConfiguratorMixin(object):
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 @@ class ViewsConfiguratorMixin(object):
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 @@ class StaticURLInfo(object):
# 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 826e6a662..a259d1414 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 9473d6330..a495d9c29 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 @@ class subscriber(object):
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 @@ class BeforeRender(dict):
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 cafdb93f0..ff598fe2d 100644
--- a/pyramid/exceptions.py
+++ b/pyramid/exceptions.py
@@ -4,6 +4,8 @@ from pyramid.httpexceptions import HTTPForbidden
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 @@ class ConfigurationConflictError(ConfigurationError):
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 8fd32a797..d9a4a6dfb 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 0887b0eec..4dbca7021 100644
--- a/pyramid/httpexceptions.py
+++ b/pyramid/httpexceptions.py
@@ -122,31 +122,36 @@ field. Reflecting this, these subclasses have one additional keyword argument:
``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 @@ ${body}''')
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 f16aeb378..889227130 100644
--- a/pyramid/i18n.py
+++ b/pyramid/i18n.py
@@ -9,6 +9,7 @@ from translationstring import TranslationStringFactory # API
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 @@ class Translations(gettext.GNUTranslations, object):
# 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 @@ class Translations(gettext.GNUTranslations, object):
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 @@ class Translations(gettext.GNUTranslations, object):
"""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 @@ class Translations(gettext.GNUTranslations, object):
"""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 d90f56b75..f08bd5fbb 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 @@ class IDict(Interface):
""" 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 c79de7217..29be339f2 100644
--- a/pyramid/mako_templating.py
+++ b/pyramid/mako_templating.py
@@ -2,11 +2,12 @@ import os
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 @@ class MakoRenderingException(Exception):
__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 bdf7df109..c9c356a92 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 @@ class PShellCommand(PCommand):
config.read(filename)
try:
items = config.items('pshell')
- except ConfigParser.NoSectionError:
+ except configparser.NoSectionError:
return
resolver = DottedNameResolver(None)
@@ -301,8 +310,8 @@ class PRoutesCommand(PCommand):
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 @@ class PViewsCommand(PCommand):
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 @@ class PViewsCommand(PCommand):
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 @@ class PTweensCommand(PCommand):
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 6b287e4f1..ac706595e 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 @@ class Registry(Components, dict):
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 2efe0f123..073ce444d 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -2,7 +2,7 @@ import os
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,8 @@ from pyramid.interfaces import IRendererInfo
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.decorator import reify
from pyramid.events import BeforeRender
from pyramid.path import caller_package
@@ -145,7 +147,7 @@ def json_renderer_factory(info):
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 +227,8 @@ class JSONP(object):
# utility functions, not API
+@implementer(IChameleonLookup)
class ChameleonRendererLookup(object):
- implements(IChameleonLookup)
def __init__(self, impl, registry):
self.impl = impl
self.registry = registry
@@ -348,8 +350,8 @@ deprecated(
'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 +439,8 @@ class RendererHelper(object):
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 9704a42a4..eae83da6f 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 ISessionFactory
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 @@ class DeprecatedRequestMethodsMixin(object):
@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 @@ class CallbackMethodsMixin(object):
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 @@ class Request(BaseRequest, DeprecatedRequestMethodsMixin, URLMethodsMixin,
@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 5e8f3c968..986c75e37 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 db53de0c3..b0c965296 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 746cf88cf..fb309eb03 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 DefaultRootFactory
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 a49bbd9f6..673f22e21 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 @@ class PyramidTemplate(Template):
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 fe5d6957a..7eb838e2f 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 tempfile
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 5aed7b5fa..a552b613a 100644
--- a/pyramid/security.py
+++ b/pyramid/security.py
@@ -5,6 +5,7 @@ from pyramid.interfaces import IAuthorizationPolicy
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 bfa80ff10..a59f9c628 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 hmac
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 UnencryptedCookieSessionFactoryConfig(
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 UnencryptedCookieSessionFactoryConfig(
# 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 3c376c4a9..de91042eb 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 17a74a16a..50a8b918b 100644
--- a/pyramid/static.py
+++ b/pyramid/static.py
@@ -15,11 +15,14 @@ from pkg_resources import resource_isdir
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 @@ class _FileIter(object):
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 @@ class static_view(object):
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 07f523868..b24a6cac7 100644
--- a/pyramid/testing.py
+++ b/pyramid/testing.py
@@ -3,7 +3,7 @@ import os
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 IView
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
@@ -125,9 +128,7 @@ def registerEventListener(event_iface=None):
event will be appended to the list. You can then compare the
values in the list to expected event notifications. This method
is useful when testing code that wants to call
- :meth:`pyramid.registry.Registry.notify`,
- :func:`zope.component.event.dispatch` or
- :func:`zope.component.event.objectEventNotify`.
+ :meth:`pyramid.registry.Registry.notify`.
The default value of ``event_iface`` (``None``) implies a
subscriber registered for *any* kind of event.
@@ -590,8 +591,8 @@ class DummyResource:
DummyModel = DummyResource # b/w compat (forever)
+@implementer(ISession)
class DummySession(dict):
- implements(ISession)
created = None
new = True
def changed(self):
@@ -621,6 +622,7 @@ class DummySession(dict):
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 +651,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'
@@ -717,6 +718,8 @@ class DummyRequest(DeprecatedRequestMethodsMixin, URLMethodsMixin,
f = self.registry.queryUtility(IResponseFactory, default=Response)
return f()
+have_zca = True
+
def setUp(registry=None, request=None, hook_zca=True, autocommit=True,
settings=None):
"""
@@ -801,7 +804,12 @@ 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()
+ global have_zca
+ try:
+ have_zca and hook_zca and config.hook_zca()
+ except ImportError:
+ # pragma: no cover (dont choke on not being able to import z.component)
+ have_zca = False
config.begin(request=request)
return config
@@ -816,12 +824,13 @@ def tearDown(unhook_zca=True):
argument ``hook_zca=True``. If :mod:`zope.component` cannot be
imported, ignore the argument.
"""
- if unhook_zca:
+ global have_zca
+ if unhook_zca and have_zca:
try:
from zope.component import getSiteManager
getSiteManager.reset()
except ImportError: # pragma: no cover
- pass
+ have_zca = False
info = manager.pop()
manager.clear()
if info is not None:
@@ -889,21 +898,25 @@ class MockTemplate(object):
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 efcf791e8..25283a50d 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 efcf791e8..25283a50d 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 1432618cf..33b97671e 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 c74747bfd..27063aae2 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 7001b87f5..888dc9317 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 10a244f3b..330d983ab 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 0880556ef..e2024198e 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 ff96ae471..6b8393fc2 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 @@ class TestAuthTktCookieHelper(unittest.TestCase):
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 @@ class TestAuthTktCookieHelper(unittest.TestCase):
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 @@ class TestAuthTktCookieHelper(unittest.TestCase):
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
@@ -612,6 +616,7 @@ class TestAuthTktCookieHelper(unittest.TestCase):
now = time.time()
helper.auth_tkt.timestamp = now
helper.now = now + 1
+ helper.auth_tkt.tokens = (u'a', )
request = self._makeRequest('bogus')
result = helper.identify(request)
self.assertTrue(result)
@@ -822,14 +827,16 @@ class TestAuthTktCookieHelper(unittest.TestCase):
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 +850,7 @@ class TestAuthTktCookieHelper(unittest.TestCase):
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 +861,16 @@ class TestAuthTktCookieHelper(unittest.TestCase):
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 +908,18 @@ class TestAuthTktCookieHelper(unittest.TestCase):
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_unicode_but_ascii_token(self):
helper = self._makeOne('secret')
request = self._makeRequest()
+ la = text_(b'foo', 'utf-8')
+ helper.remember(request, 'other', tokens=(la,))
+
+ 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 +1102,6 @@ class TestSessionAuthenticationPolicy(unittest.TestCase):
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 213f25f51..8d23c8753 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 @@ class TextTemplateRendererTests(Base, unittest.TestCase):
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 @@ class TextTemplateRendererTests(Base, unittest.TestCase):
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 @@ class TextTemplateRendererTests(Base, unittest.TestCase):
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 @@ class RenderTemplateTests(Base, unittest.TestCase):
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 @@ class RenderTemplateToResponseTests(Base, unittest.TestCase):
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 84eaedcf4..1a8e6767e 100644
--- a/pyramid/tests/test_chameleon_zpt.py
+++ b/pyramid/tests/test_chameleon_zpt.py
@@ -2,6 +2,7 @@ import unittest
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 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
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'),
'<div xmlns="http://www.w3.org/1999/xhtml">\n</div>')
@@ -126,7 +127,7 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
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'),
'<div xmlns="http://www.w3.org/1999/xhtml">\n</div>')
@@ -140,7 +141,7 @@ class RenderTemplateTests(Base, unittest.TestCase):
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'),
'<div xmlns="http://www.w3.org/1999/xhtml">\n</div>')
@@ -155,8 +156,8 @@ class RenderTemplateToResponseTests(Base, unittest.TestCase):
result = self._callFUT(minimal)
from webob import Response
self.assertTrue(isinstance(result, Response))
- self.assertEqual(result.app_iter[0].rstrip('\n'),
- '<div xmlns="http://www.w3.org/1999/xhtml">\n</div>')
+ self.assertEqual(result.app_iter[0].rstrip(b'\n'),
+ b'<div xmlns="http://www.w3.org/1999/xhtml">\n</div>')
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 2f9f516ae..5b40a8c09 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 @@ includeme = dummy_include
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 29c099e0e..84b7119cf 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 @@ class AdaptersConfiguratorMixinTests(unittest.TestCase):
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 @@ class AdaptersConfiguratorMixinTests(unittest.TestCase):
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 @@ class AdaptersConfiguratorMixinTests(unittest.TestCase):
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 AdaptersConfiguratorMixinTests(unittest.TestCase):
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 322c854f5..627eefba7 100644
--- a/pyramid/tests/test_config/test_assets.py
+++ b/pyramid/tests/test_config/test_assets.py
@@ -163,9 +163,9 @@ class TestOverrideProvider(unittest.TestCase):
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 @@ class TestOverrideProvider(unittest.TestCase):
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 @@ class TestOverrideProvider(unittest.TestCase):
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 @@ class TestOverrideProvider(unittest.TestCase):
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 @@ class TestOverrideProvider(unittest.TestCase):
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 @@ class TestPackageOverrides(unittest.TestCase):
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 @@ class TestPackageOverrides(unittest.TestCase):
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 @@ class DummyPackage:
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 03b410445..25cb88cc3 100644
--- a/pyramid/tests/test_config/test_i18n.py
+++ b/pyramid/tests/test_config/test_i18n.py
@@ -92,7 +92,7 @@ class TestI18NConfiguratorMixin(unittest.TestCase):
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 1dccd00e1..ca1508295 100644
--- a/pyramid/tests/test_config/test_init.py
+++ b/pyramid/tests/test_config/test_init.py
@@ -2,6 +2,10 @@ import unittest
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 dummy_extend2
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ pyramid.tests.test_config.dummy_include2""",
"""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 @@ pyramid.tests.test_config.dummy_include2""",
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 @@ pyramid.tests.test_config.dummy_include2""",
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 @@ pyramid.tests.test_config.dummy_include2""",
finally:
getSiteManager.reset()
+ @skip_on('py3')
def test_unhook_zca(self):
from zope.component import getSiteManager
def foo():
@@ -987,7 +989,7 @@ pyramid.tests.test_config.dummy_include2""",
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 @@ pyramid.tests.test_config.dummy_include2""",
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 @@ pyramid.tests.test_config.dummy_include2""",
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 @@ pyramid.tests.test_config.dummy_include2""",
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 @@ pyramid.tests.test_config.dummy_include2""",
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 @@ class TestConfiguratorDeprecatedFeatures(unittest.TestCase):
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 @@ class TestConfiguratorDeprecatedFeatures(unittest.TestCase):
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 @@ class TestConfiguratorDeprecatedFeatures(unittest.TestCase):
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 @@ class TestConfiguratorDeprecatedFeatures(unittest.TestCase):
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 @@ class TestConfigurator_add_directive(unittest.TestCase):
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 @@ class TestConfigurator_add_directive(unittest.TestCase):
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 @@ class TestConfigurator_add_directive(unittest.TestCase):
'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 @@ class DummyThreadLocalManager(object):
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 @@ class DummyRegistry(object):
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 494a2b099..6c048b46d 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 @@ class TestingConfiguratorMixinTests(unittest.TestCase):
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 @@ class TestingConfiguratorMixinTests(unittest.TestCase):
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 f4e69f4e1..0813eecdb 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 IDummy
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 @@ class TestViewsConfigurationMixin(unittest.TestCase):
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 @@ class TestViewsConfigurationMixin(unittest.TestCase):
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 @@ class TestViewsConfigurationMixin(unittest.TestCase):
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 @@ class TestViewsConfigurationMixin(unittest.TestCase):
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 @@ class TestViewsConfigurationMixin(unittest.TestCase):
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 @@ class TestViewsConfigurationMixin(unittest.TestCase):
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 @@ class TestViewsConfigurationMixin(unittest.TestCase):
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 @@ class TestViewsConfigurationMixin(unittest.TestCase):
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 @@ class TestViewsConfigurationMixin(unittest.TestCase):
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
@@ -1428,6 +1429,7 @@ class TestViewsConfigurationMixin(unittest.TestCase):
request.subpath = ('minimal.pt', )
result = wrapped(None, request)
self.assertEqual(result.status, '200 OK')
+ self.assertTrue(result.body.startswith(b'<div'))
def test_add_static_view_package_relative(self):
from pyramid.interfaces import IStaticURLInfo
@@ -1545,7 +1547,7 @@ class TestViewsConfigurationMixin(unittest.TestCase):
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 +1568,7 @@ class TestViewsConfigurationMixin(unittest.TestCase):
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 +2119,7 @@ class TestViewDeriver(unittest.TestCase):
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 +2285,7 @@ class TestViewDeriver(unittest.TestCase):
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 +2539,7 @@ class TestViewDeriver(unittest.TestCase):
request.url = 'url'
try:
result(None, request)
- except HTTPForbidden, e:
+ except HTTPForbidden as e:
self.assertEqual(e.message,
'Unauthorized: <lambda> failed permission check')
else: # pragma: no cover
@@ -2559,7 +2561,7 @@ class TestViewDeriver(unittest.TestCase):
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 +2579,7 @@ class TestViewDeriver(unittest.TestCase):
request.method = 'POST'
try:
result(None, None)
- except PredicateMismatch, e:
+ except PredicateMismatch as e:
self.assertEqual(e.detail, 'predicate mismatch for view <lambda>')
else: # pragma: no cover
raise AssertionError
@@ -2593,7 +2595,7 @@ class TestViewDeriver(unittest.TestCase):
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 +2664,7 @@ class TestViewDeriver(unittest.TestCase):
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 +2675,7 @@ class TestViewDeriver(unittest.TestCase):
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 +3254,12 @@ class Test_preserve_view_attrs(unittest.TestCase):
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 +3465,11 @@ class DummyRequest:
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 +3515,10 @@ class DummyConfig:
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 bf06c7ca6..eba95b210 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 @@ if 0:
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 27965aea9..ccc8f16e3 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 @@ class UrlEncodeTests(unittest.TestCase):
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 @@ class URLQuoteTests(unittest.TestCase):
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 9c7b38e27..927d27733 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 @@ class Test__no_escape(unittest.TestCase):
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 @@ class TestWSGIHTTPException(unittest.TestCase):
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 @@ class TestWSGIHTTPException(unittest.TestCase):
environ['HTTP_ACCEPT'] = 'text/html'
start_response = DummyStartResponse()
body = list(exc(environ, start_response))[0]
- self.assertTrue(body.startswith('<html'))
- self.assertTrue('200 OK' in body)
- self.assertTrue('explanation' in body)
- self.assertTrue('detail' in body)
+ self.assertTrue(body.startswith(b'<html'))
+ self.assertTrue(b'200 OK' in body)
+ self.assertTrue(b'explanation' in body)
+ self.assertTrue(b'detail' in body)
def test_ctor_with_body_sets_default_app_iter_text(self):
cls = self._getTargetSubclass()
@@ -153,7 +156,7 @@ class TestWSGIHTTPException(unittest.TestCase):
environ = _makeEnviron()
start_response = DummyStartResponse()
body = list(exc(environ, start_response))[0]
- self.assertEqual(body, '200 OK\n\nexplanation\n\n\ndetail\n\n')
+ self.assertEqual(body, b'200 OK\n\nexplanation\n\n\ndetail\n\n')
def test__str__detail(self):
exc = self._makeOne()
@@ -198,7 +201,7 @@ class TestWSGIHTTPException(unittest.TestCase):
environ = _makeEnviron()
start_response = DummyStartResponse()
body = list(exc(environ, start_response))[0]
- self.assertEqual(body, '200 OK\n\nexplanation\n\n\n\n\n')
+ self.assertEqual(body, b'200 OK\n\nexplanation\n\n\n\n\n')
def test__default_app_iter_with_comment_plain(self):
cls = self._getTargetSubclass()
@@ -206,7 +209,7 @@ class TestWSGIHTTPException(unittest.TestCase):
environ = _makeEnviron()
start_response = DummyStartResponse()
body = list(exc(environ, start_response))[0]
- self.assertEqual(body, '200 OK\n\nexplanation\n\n\n\ncomment\n')
+ self.assertEqual(body, b'200 OK\n\nexplanation\n\n\n\ncomment\n')
def test__default_app_iter_no_comment_html(self):
cls = self._getTargetSubclass()
@@ -214,7 +217,7 @@ class TestWSGIHTTPException(unittest.TestCase):
environ = _makeEnviron()
start_response = DummyStartResponse()
body = list(exc(environ, start_response))[0]
- self.assertFalse('<!-- ' in body)
+ self.assertFalse(b'<!-- ' in body)
def test__default_app_iter_with_comment_html(self):
cls = self._getTargetSubclass()
@@ -223,7 +226,7 @@ class TestWSGIHTTPException(unittest.TestCase):
environ['HTTP_ACCEPT'] = '*/*'
start_response = DummyStartResponse()
body = list(exc(environ, start_response))[0]
- self.assertTrue('<!-- comment &amp; comment -->' in body)
+ self.assertTrue(b'<!-- comment &amp; comment -->' in body)
def test__default_app_iter_with_comment_html2(self):
cls = self._getTargetSubclass()
@@ -232,7 +235,7 @@ class TestWSGIHTTPException(unittest.TestCase):
environ['HTTP_ACCEPT'] = 'text/html'
start_response = DummyStartResponse()
body = list(exc(environ, start_response))[0]
- self.assertTrue('<!-- comment &amp; comment -->' in body)
+ self.assertTrue(b'<!-- comment &amp; comment -->' in body)
def test_custom_body_template(self):
cls = self._getTargetSubclass()
@@ -240,7 +243,7 @@ class TestWSGIHTTPException(unittest.TestCase):
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 @@ class TestWSGIHTTPException(unittest.TestCase):
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 @@ class TestRenderAllExceptionsWithoutArguments(unittest.TestCase):
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 @@ class Test_HTTPMove(unittest.TestCase):
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 @@ class TestHTTPMethodNotAllowed(unittest.TestCase):
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 008cf525a..bd4998b10 100644
--- a/pyramid/tests/test_i18n.py
+++ b/pyramid/tests/test_i18n.py
@@ -50,9 +50,9 @@ class TestLocalizer(unittest.TestCase):
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 @@ class TestTranslations(unittest.TestCase):
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 @@ class TestTranslations(unittest.TestCase):
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 1fa1cbbcf..0c17b88ce 100644
--- a/pyramid/tests/test_integration.py
+++ b/pyramid/tests/test_integration.py
@@ -6,6 +6,8 @@ import unittest
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 @@ class IntegrationBase(object):
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 @@ class TestStaticAppBase(IntegrationBase):
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 @@ class TestStaticAppNoSubpath(unittest.TestCase):
'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 @@ class TestStaticPermApp(IntegrationBase, unittest.TestCase):
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 @@ class TestStaticPermApp(IntegrationBase, unittest.TestCase):
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 @@ class TestHybridApp(IntegrationBase, unittest.TestCase):
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 "
- "'<default deny>' in ACL "
- "'<No ACL found on any object in resource "
- "lineage>' on context"))
+ result.startswith(b"ACLDenied permission 'private' via ACE "
+ b"'<default deny>' in ACL "
+ b"'<No ACL found on any object in resource "
+ b"lineage>' 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 @@ class TestViewDecoratorApp(IntegrationBase, unittest.TestCase):
# 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 @@ class TestConflictApp(unittest.TestCase):
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 @@ class TestConflictApp(unittest.TestCase):
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 @@ class TestConflictApp(unittest.TestCase):
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 @@ class TestConflictApp(unittest.TestCase):
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 @@ class ImperativeIncludeConfigurationTest(unittest.TestCase):
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 @@ class SelfScanAppTest(unittest.TestCase):
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 @@ class WSGIApp2AppTest(unittest.TestCase):
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 @@ if os.name != 'java': # uses chameleon
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 3d30c9954..e1f47f4ab 100644
--- a/pyramid/tests/test_location.py
+++ b/pyramid/tests/test_location.py
@@ -34,7 +34,7 @@ class TestLineage(unittest.TestCase):
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 162f774f5..074d28b85 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 @@ class MakoLookupTemplateRendererTests(Base, unittest.TestCase):
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 @@ class MakoLookupTemplateRendererTests(Base, unittest.TestCase):
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 @@ class MakoLookupTemplateRendererTests(Base, unittest.TestCase):
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 @@ class MakoLookupTemplateRendererTests(Base, unittest.TestCase):
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 @@ class TestIntegration(unittest.TestCase):
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 @@ class TestIntegration(unittest.TestCase):
def test_template_default_escaping(self):
from pyramid.renderers import render
- result = render('nonminimal.mak', {'name':'<b>fred</b>'}).replace('\r','')
- self.assertEqual(result, u'Hello, &lt;b&gt;fred&lt;/b&gt;!\n')
+ result = render('nonminimal.mak',
+ {'name':'<b>fred</b>'}).replace('\r','')
+ self.assertEqual(result, text_('Hello, &lt;b&gt;fred&lt;/b&gt;!\n'))
class TestPkgResourceTemplateLookup(unittest.TestCase):
def _makeOne(self, **kw):
@@ -448,7 +454,7 @@ class DummyLookup(object):
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 995e05d46..a697b5691 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 @@ class TestPShellCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 TestPViewsCommand(unittest.TestCase):
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 TestPViewsCommand(unittest.TestCase):
(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 @@ class TestPViewsCommand(unittest.TestCase):
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 TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestGetApp(unittest.TestCase):
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 @@ class TestBootstrap(unittest.TestCase):
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 @@ class DummyConfigParser(object):
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 6a20eaa5d..c3104bd31 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 48b1bed65..1054dcb1c 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -2,6 +2,7 @@ import unittest
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 @@ class Test_string_renderer_factory(unittest.TestCase):
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 @@ class Test_string_renderer_factory(unittest.TestCase):
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 @@ class TestRendererHelper(unittest.TestCase):
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 @@ class TestRendererHelper(unittest.TestCase):
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 @@ class TestRendererHelper(unittest.TestCase):
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 @@ class TestRendererHelper(unittest.TestCase):
('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 @@ class TestRendererHelper(unittest.TestCase):
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 @@ class TestRendererHelper(unittest.TestCase):
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 @@ class TestRendererHelper(unittest.TestCase):
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 @@ class Test_render_to_response(unittest.TestCase):
'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 @@ class Test_render_to_response(unittest.TestCase):
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 @@ class Test_render_to_response(unittest.TestCase):
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 066aa9207..5a11acd07 100644
--- a/pyramid/tests/test_request.py
+++ b/pyramid/tests/test_request.py
@@ -1,6 +1,12 @@
import unittest
from pyramid import testing
+from pyramid.compat import PY3
+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 +56,7 @@ class TestRequest(unittest.TestCase):
}
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 +172,7 @@ class TestRequest(unittest.TestCase):
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 +190,7 @@ class TestRequest(unittest.TestCase):
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,22 +241,28 @@ class TestRequest(unittest.TestCase):
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')
+ inp = text_(
+ b'/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf',
+ 'utf-8'
+ )
+ if PY3: # pragma: no cover
+ body = bytes(json.dumps({'a':inp}), 'utf-16')
+ else:
+ body = json.dumps({'a':inp}).decode('utf-8').encode('utf-16')
request.body = body
- self.assertEqual(request.json_body, {'a':la})
+ request.content_type = 'application/json; charset=utf-16'
+ self.assertEqual(request.json_body, {'a':inp})
def test_json_body_GET_request(self):
request = self._makeOne({'REQUEST_METHOD':'GET'})
@@ -322,17 +334,17 @@ class TestRequestDeprecatedMethods(unittest.TestCase):
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 +382,8 @@ class TestRequestDeprecatedMethods(unittest.TestCase):
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 +509,16 @@ class Test_call_app_with_subpath_as_path_info(unittest.TestCase):
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 a98018500..eb9b7285d 100644
--- a/pyramid/tests/test_router.py
+++ b/pyramid/tests/test_router.py
@@ -177,7 +177,7 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 TestRouter(unittest.TestCase):
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 TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 TestRouter(unittest.TestCase):
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 TestRouter(unittest.TestCase):
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 @@ class DummyStartResponse:
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 @@ class DummyLogger:
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 ed2c5a993..265e20c3b 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 5c6454a38..6d75c7950 100644
--- a/pyramid/tests/test_session.py
+++ b/pyramid/tests/test_session.py
@@ -263,21 +263,15 @@ class Test_manage_accessed(unittest.TestCase):
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 5cc4ce561..d02b3cd3e 100644
--- a/pyramid/tests/test_settings.py
+++ b/pyramid/tests/test_settings.py
@@ -87,7 +87,7 @@ class Test_aslist(unittest.TestCase):
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 6dc38fc57..9fdf29637 100644
--- a/pyramid/tests/test_static.py
+++ b/pyramid/tests/test_static.py
@@ -37,14 +37,14 @@ class Test_static_view_use_subpath_False(unittest.TestCase):
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('<html>static</html>' in response.body)
+ self.assertTrue(b'<html>static</html>' in response.body)
def test_oob_singledot(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -52,7 +52,7 @@ class Test_static_view_use_subpath_False(unittest.TestCase):
context = DummyContext()
response = inst(context, request)
self.assertEqual(response.status, '200 OK')
- self.assertTrue('<html>static</html>' in response.body)
+ self.assertTrue(b'<html>static</html>' in response.body)
def test_oob_emptyelement(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -60,7 +60,7 @@ class Test_static_view_use_subpath_False(unittest.TestCase):
context = DummyContext()
response = inst(context, request)
self.assertEqual(response.status, '200 OK')
- self.assertTrue('<html>static</html>' in response.body)
+ self.assertTrue(b'<html>static</html>' in response.body)
def test_oob_dotdotslash(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -99,21 +99,21 @@ class Test_static_view_use_subpath_False(unittest.TestCase):
request = self._makeRequest({'PATH_INFO':'/subdir/'})
context = DummyContext()
response = inst(context, request)
- self.assertTrue('<html>subdir</html>' in response.body)
+ self.assertTrue(b'<html>subdir</html>' 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('<html>static</html>' in response.body)
+ self.assertTrue(b'<html>static</html>' 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('<html>static</html>' in response.body)
+ self.assertTrue(b'<html>static</html>' 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 @@ class Test_static_view_use_subpath_False(unittest.TestCase):
request = self._makeRequest({'PATH_INFO':'/index.html'})
context = DummyContext()
response = inst(context, request)
- self.assertTrue('<html>static</html>' in response.body)
+ self.assertTrue(b'<html>static</html>' in response.body)
self.assertEqual(len(response.headerlist), 3)
header_names = [ x[0] for x in response.headerlist ]
header_names.sort()
@@ -143,8 +143,11 @@ class Test_static_view_use_subpath_False(unittest.TestCase):
response = inst(context, request)
start_response = DummyStartResponse()
app_iter = response(request.environ, start_response)
- self.assertEqual(start_response.status, '304 Not Modified')
- self.assertEqual(list(app_iter), [])
+ try:
+ self.assertEqual(start_response.status, '304 Not Modified')
+ self.assertEqual(list(app_iter), [])
+ finally:
+ app_iter.close()
def test_not_found(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -192,7 +195,7 @@ class Test_static_view_use_subpath_True(unittest.TestCase):
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 +203,7 @@ class Test_static_view_use_subpath_True(unittest.TestCase):
request.subpath = ()
context = DummyContext()
response = inst(context, request)
- self.assertTrue('<html>static</html>' in response.body)
+ self.assertTrue(b'<html>static</html>' in response.body)
def test_oob_singledot(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -258,7 +261,7 @@ class Test_static_view_use_subpath_True(unittest.TestCase):
request.subpath = ('subdir',)
context = DummyContext()
response = inst(context, request)
- self.assertTrue('<html>subdir</html>' in response.body)
+ self.assertTrue(b'<html>subdir</html>' in response.body)
def test_resource_is_file(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
@@ -266,7 +269,7 @@ class Test_static_view_use_subpath_True(unittest.TestCase):
request.subpath = ('index.html',)
context = DummyContext()
response = inst(context, request)
- self.assertTrue('<html>static</html>' in response.body)
+ self.assertTrue(b'<html>static</html>' 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 +277,7 @@ class Test_static_view_use_subpath_True(unittest.TestCase):
request.subpath = ('index.html',)
context = DummyContext()
response = inst(context, request)
- self.assertTrue('<html>static</html>' in response.body)
+ self.assertTrue(b'<html>static</html>' in response.body)
self.assertEqual(len(response.headerlist), 5)
header_names = [ x[0] for x in response.headerlist ]
header_names.sort()
@@ -289,7 +292,7 @@ class Test_static_view_use_subpath_True(unittest.TestCase):
request.subpath = ('index.html',)
context = DummyContext()
response = inst(context, request)
- self.assertTrue('<html>static</html>' in response.body)
+ self.assertTrue(b'<html>static</html>' in response.body)
self.assertEqual(len(response.headerlist), 3)
header_names = [ x[0] for x in response.headerlist ]
header_names.sort()
@@ -306,8 +309,11 @@ class Test_static_view_use_subpath_True(unittest.TestCase):
response = inst(context, request)
start_response = DummyStartResponse()
app_iter = response(request.environ, start_response)
- self.assertEqual(start_response.status, '304 Not Modified')
- self.assertEqual(list(app_iter), [])
+ try:
+ self.assertEqual(start_response.status, '304 Not Modified')
+ self.assertEqual(list(app_iter), [])
+ finally:
+ app_iter.close()
def test_not_found(self):
inst = self._makeOne('pyramid.tests:fixtures/static')
diff --git a/pyramid/tests/test_testing.py b/pyramid/tests/test_testing.py
index 41a4788ec..05ef36fe9 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 @@ class Test_registerDummySecurityPolicy(TestBase):
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 @@ class Test_registerResources(TestBase):
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 @@ class Test_registerView(TestBase):
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 @@ class Test_registerView(TestBase):
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 @@ class Test_registerView(TestBase):
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 Test_registerAdapter(TestBase):
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 TestDummyResource(unittest.TestCase):
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 @@ class Test_setUp(unittest.TestCase):
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 @@ class Test_tearDown(unittest.TestCase):
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 @@ class Test_tearDown(unittest.TestCase):
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 @@ class TestDummySession(unittest.TestCase):
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 95caf21be..72192b23b 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 @@ class ResourceTreeTraverserTests(unittest.TestCase):
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 @@ class ResourceTreeTraverserTests(unittest.TestCase):
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 @@ class ResourceTreeTraverserTests(unittest.TestCase):
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 @@ class ResourceTreeTraverserTests(unittest.TestCase):
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 @@ class ResourceTreeTraverserTests(unittest.TestCase):
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 @@ class ResourceTreeTraverserTests(unittest.TestCase):
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 @@ class ResourceTreeTraverserTests(unittest.TestCase):
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 @@ class FindResourceTests(unittest.TestCase):
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 @@ class QuotePathSegmentTests(unittest.TestCase):
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 @@ class QuotePathSegmentTests(unittest.TestCase):
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 @@ class TraversalContextURLTests(unittest.TestCase):
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 @@ class TraverseTests(unittest.TestCase):
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 @@ class DummyContext(object):
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 8b95374fb..4c39d8e9c 100644
--- a/pyramid/tests/test_url.py
+++ b/pyramid/tests/test_url.py
@@ -2,6 +2,8 @@ import unittest
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 @@ class TestURLMethodsMixin(unittest.TestCase):
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 @@ class TestURLMethodsMixin(unittest.TestCase):
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 @@ class TestURLMethodsMixin(unittest.TestCase):
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 @@ class TestURLMethodsMixin(unittest.TestCase):
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 @@ class TestURLMethodsMixin(unittest.TestCase):
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 @@ class TestURLMethodsMixin(unittest.TestCase):
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 @@ class TestURLMethodsMixin(unittest.TestCase):
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 @@ class TestURLMethodsMixin(unittest.TestCase):
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 @@ class TestURLMethodsMixin(unittest.TestCase):
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 @@ class TestURLMethodsMixin(unittest.TestCase):
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 3c92e87be..be823b045 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 @@ class RoutesMapperTests(unittest.TestCase):
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 @@ class TestCompileRoute(unittest.TestCase):
'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 @@ class TestCompileRoute(unittest.TestCase):
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 @@ class TestCompileRouteFunctional(unittest.TestCase):
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 @@ class TestCompileRouteFunctional(unittest.TestCase):
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 @@ class TestCompileRouteFunctional(unittest.TestCase):
'/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 @@ class TestCompileRouteFunctional(unittest.TestCase):
'/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 247b61dad..7c870bc45 100644
--- a/pyramid/tests/test_util.py
+++ b/pyramid/tests/test_util.py
@@ -1,4 +1,5 @@
import unittest
+from pyramid.compat import PY3
class TestDottedNameResolver(unittest.TestCase):
def _makeOne(self, package=None):
@@ -9,11 +10,19 @@ class TestDottedNameResolver(unittest.TestCase):
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
+ def test_zope_dottedname_style_resolve_builtin(self):
+ typ = self._makeOne()
+ if PY3:
+ result = typ._zope_dottedname_style('builtins.str')
+ else:
+ result = typ._zope_dottedname_style('__builtin__.str')
+ self.assertEqual(result, str)
+
def test_zope_dottedname_style_resolve_absolute(self):
typ = self._makeOne()
result = typ._zope_dottedname_style(
@@ -190,7 +199,7 @@ class Test_WeakOrderedSet(unittest.TestCase):
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 +210,8 @@ class Test_WeakOrderedSet(unittest.TestCase):
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 +221,7 @@ class Test_WeakOrderedSet(unittest.TestCase):
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 7f66a7563..29e468cd2 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 @@ class TestViewConfigDecorator(unittest.TestCase):
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 @@ class DummyRequest:
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 bf2606529..ee6b5fb7a 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 IRequestFactory
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'/<unprintable unicode>' (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'/<unprintable unicode>' (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 @@ class ResourceTreeTraverser(object):
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 @@ class ResourceTreeTraverser(object):
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 @@ class ResourceTreeTraverser(object):
# 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 @@ class ResourceTreeTraverser(object):
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 b15204e9d..65d7c3919 100644
--- a/pyramid/tweens.py
+++ b/pyramid/tweens.py
@@ -15,7 +15,7 @@ def excview_tween_factory(handler, registry):
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 c2ff43ddd..7a7dd3b4c 100644
--- a/pyramid/url.py
+++ b/pyramid/url.py
@@ -8,6 +8,8 @@ from pyramid.interfaces import IContextURL
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 @@ class URLMethodsMixin(object):
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 @@ class URLMethodsMixin(object):
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 73318193c..662615845 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 @@ class Route(object):
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 _compile_route(route):
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 _compile_route(route):
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 c0e7640c4..a43b50aef 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -2,6 +2,7 @@ import pkg_resources
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 @@ class DottedNameResolver(object):
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 @@ class DottedNameResolver(object):
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 6046408f6..581e42185 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -7,6 +7,7 @@ from pyramid.interfaces import IRoutesMapper
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 82f447103..aa8045a9a 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 @@ except IOError:
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 '
@@ -66,6 +69,10 @@ setup(name='pyramid',
classifiers=[
"Intended Audience :: Developers",
"Programming Language :: Python",
+ "Programming Language :: Python :: 2.6",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.2",
"Framework :: Pylons",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: WSGI",
diff --git a/tox.ini b/tox.ini
index 40711a5f2..a422b2eb6 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