summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml6
-rw-r--r--appveyor.yml2
-rw-r--r--setup.py9
-rw-r--r--src/pyramid/asset.py4
-rw-r--r--src/pyramid/authentication.py40
-rw-r--r--src/pyramid/authorization.py4
-rw-r--r--src/pyramid/compat.py326
-rw-r--r--src/pyramid/config/__init__.py16
-rw-r--r--src/pyramid/config/actions.py2
-rw-r--r--src/pyramid/config/predicates.py2
-rw-r--r--src/pyramid/config/routes.py4
-rw-r--r--src/pyramid/config/testing.py4
-rw-r--r--src/pyramid/config/tweens.py10
-rw-r--r--src/pyramid/config/views.py34
-rw-r--r--src/pyramid/csrf.py15
-rw-r--r--src/pyramid/encode.py19
-rw-r--r--src/pyramid/httpexceptions.py13
-rw-r--r--src/pyramid/i18n.py15
-rw-r--r--src/pyramid/interfaces.py15
-rw-r--r--src/pyramid/path.py8
-rw-r--r--src/pyramid/predicates.py4
-rw-r--r--src/pyramid/registry.py5
-rw-r--r--src/pyramid/renderers.py6
-rw-r--r--src/pyramid/request.py13
-rw-r--r--src/pyramid/response.py4
-rw-r--r--src/pyramid/scripts/prequest.py4
-rw-r--r--src/pyramid/scripts/proutes.py3
-rw-r--r--src/pyramid/scripts/pserve.py23
-rw-r--r--src/pyramid/scripts/pshell.py3
-rw-r--r--src/pyramid/scripts/pviews.py2
-rw-r--r--src/pyramid/security.py3
-rw-r--r--src/pyramid/session.py12
-rw-r--r--src/pyramid/settings.py4
-rw-r--r--src/pyramid/static.py9
-rw-r--r--src/pyramid/testing.py8
-rw-r--r--src/pyramid/traversal.py169
-rw-r--r--src/pyramid/tweens.py2
-rw-r--r--src/pyramid/url.py5
-rw-r--r--src/pyramid/urldispatch.py54
-rw-r--r--src/pyramid/util.py164
-rw-r--r--src/pyramid/view.py6
-rw-r--r--src/pyramid/viewderivers.py9
-rw-r--r--tests/pkgs/forbiddenapp/__init__.py2
-rw-r--r--tests/pkgs/permbugapp/__init__.py2
-rw-r--r--tests/test_authentication.py17
-rw-r--r--tests/test_compat.py32
-rw-r--r--tests/test_config/test_adapters.py6
-rw-r--r--tests/test_config/test_factories.py9
-rw-r--r--tests/test_config/test_init.py9
-rw-r--r--tests/test_config/test_predicates.py2
-rw-r--r--tests/test_config/test_routes.py2
-rw-r--r--tests/test_config/test_testing.py16
-rw-r--r--tests/test_config/test_views.py33
-rw-r--r--tests/test_encode.py4
-rw-r--r--tests/test_httpexceptions.py10
-rw-r--r--tests/test_integration.py11
-rw-r--r--tests/test_path.py6
-rw-r--r--tests/test_predicates.py2
-rw-r--r--tests/test_renderers.py6
-rw-r--r--tests/test_request.py9
-rw-r--r--tests/test_response.py2
-rw-r--r--tests/test_scripts/dummy.py5
-rw-r--r--tests/test_scripts/test_prequest.py21
-rw-r--r--tests/test_scripts/test_pserve.py5
-rw-r--r--tests/test_scripts/test_pviews.py18
-rw-r--r--tests/test_session.py10
-rw-r--r--tests/test_traversal.py30
-rw-r--r--tests/test_url.py3
-rw-r--r--tests/test_urldispatch.py66
-rw-r--r--tests/test_util.py117
-rw-r--r--tests/test_view.py3
-rw-r--r--tox.ini24
72 files changed, 556 insertions, 986 deletions
diff --git a/.travis.yml b/.travis.yml
index 6efbee21b..c4860d2de 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,20 +4,16 @@ sudo: false
matrix:
include:
- - python: 2.7
- env: TOXENV=py27
- python: 3.4
env: TOXENV=py34
- python: 3.5
env: TOXENV=py35
- python: 3.6
env: TOXENV=py36
- - python: pypy
- env: TOXENV=pypy
- python: pypy3
env: TOXENV=pypy3
- python: 3.6
- env: TOXENV=py2-cover,py3-cover,coverage
+ env: TOXENV=py36-cover,coverage
- python: 3.5
env: TOXENV=docs
- python: 3.6
diff --git a/appveyor.yml b/appveyor.yml
index 8c9d158e1..a9bcd40f1 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,8 +8,6 @@ environment:
TOXENV: "py35"
- PYTHON: "C:\\Python34"
TOXENV: "py34"
- - PYTHON: "C:\\Python27"
- TOXENV: "py27"
cache:
- '%LOCALAPPDATA%\pip\Cache'
diff --git a/setup.py b/setup.py
index 0143764b8..b63f4c182 100644
--- a/setup.py
+++ b/setup.py
@@ -69,7 +69,6 @@ setup(
"Development Status :: 6 - Mature",
"Intended Audience :: Developers",
"Programming Language :: Python",
- "Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
@@ -96,13 +95,9 @@ setup(
package_dir={'': 'src'},
include_package_data=True,
zip_safe=False,
- python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*',
+ python_requires='>=3.4',
install_requires=install_requires,
- extras_require={
- ':python_version<"3.2"': ['repoze.lru >= 0.4'],
- 'testing': testing_extras,
- 'docs': docs_extras,
- },
+ extras_require={'testing': testing_extras, 'docs': docs_extras},
tests_require=tests_require,
test_suite="tests",
entry_points="""\
diff --git a/src/pyramid/asset.py b/src/pyramid/asset.py
index 0d7575a85..a32babe6c 100644
--- a/src/pyramid/asset.py
+++ b/src/pyramid/asset.py
@@ -1,13 +1,11 @@
import os
import pkg_resources
-from pyramid.compat import string_types
-
from pyramid.path import package_path, package_name
def resolve_asset_spec(spec, pname='__main__'):
- if pname and not isinstance(pname, string_types):
+ if pname and not isinstance(pname, str):
pname = pname.__name__ # as package
if os.path.isabs(spec):
return None, spec
diff --git a/src/pyramid/authentication.py b/src/pyramid/authentication.py
index 7cb6b6811..21cfc0c0e 100644
--- a/src/pyramid/authentication.py
+++ b/src/pyramid/authentication.py
@@ -6,28 +6,18 @@ import hashlib
import base64
import re
import time as time_mod
+from urllib.parse import quote, unquote
import warnings
from zope.interface import implementer
from webob.cookies import CookieProfile
-from pyramid.compat import (
- long,
- text_type,
- binary_type,
- url_unquote,
- url_quote,
- bytes_,
- ascii_native_,
- native_,
-)
-
from pyramid.interfaces import IAuthenticationPolicy, IDebugLogger
from pyramid.security import Authenticated, Everyone
-from pyramid.util import strings_differ
+from pyramid.util import strings_differ, bytes_, ascii_, text_
from pyramid.util import SimpleSerializer
VALID_TOKEN = re.compile(r"^[A-Za-z][A-Za-z0-9+_-]*$")
@@ -727,11 +717,7 @@ class AuthTicket(object):
)
def cookie_value(self):
- v = '%s%08x%s!' % (
- self.digest(),
- int(self.time),
- url_quote(self.userid),
- )
+ v = '%s%08x%s!' % (self.digest(), int(self.time), quote(self.userid))
if self.tokens:
v += self.tokens + '!'
v += self.user_data
@@ -759,7 +745,7 @@ def parse_ticket(secret, ticket, ip, hashalg='md5'):
If the ticket cannot be parsed, a ``BadTicket`` exception will be raised
with an explanation.
"""
- ticket = native_(ticket).strip('"')
+ ticket = text_(ticket).strip('"')
digest_size = hashlib.new(hashalg).digest_size * 2
digest = ticket[:digest_size]
try:
@@ -770,7 +756,7 @@ def parse_ticket(secret, ticket, ip, hashalg='md5'):
userid, data = ticket[digest_size + 8 :].split('!', 1)
except ValueError:
raise BadTicket('userid is not followed by !')
- userid = url_unquote(userid)
+ userid = unquote(userid)
if '!' in data:
tokens, user_data = data.split('!', 1)
else: # pragma: no cover (never generated)
@@ -857,9 +843,8 @@ class AuthTktCookieHelper(object):
userid_type_encoders = {
int: ('int', str),
- long: ('int', str),
- text_type: ('b64unicode', lambda x: b64encode(utf_8_encode(x)[0])),
- binary_type: ('b64str', lambda x: b64encode(x)),
+ str: ('b64unicode', lambda x: b64encode(utf_8_encode(x)[0])),
+ bytes: ('b64str', lambda x: b64encode(x)),
}
def __init__(
@@ -879,16 +864,13 @@ class AuthTktCookieHelper(object):
domain=None,
samesite='Lax',
):
-
- serializer = SimpleSerializer()
-
self.cookie_profile = CookieProfile(
cookie_name=cookie_name,
secure=secure,
max_age=max_age,
httponly=http_only,
path=path,
- serializer=serializer,
+ serializer=SimpleSerializer(),
samesite=samesite,
)
@@ -1048,7 +1030,7 @@ class AuthTktCookieHelper(object):
"type provided.".format(type(userid)),
RuntimeWarning,
)
- encoding, encoder = self.userid_type_encoders.get(text_type)
+ encoding, encoder = self.userid_type_encoders.get(str)
userid = str(userid)
userid = encoder(userid)
@@ -1056,9 +1038,9 @@ class AuthTktCookieHelper(object):
new_tokens = []
for token in tokens:
- if isinstance(token, text_type):
+ if isinstance(token, str):
try:
- token = ascii_native_(token)
+ token = ascii_(token)
except UnicodeEncodeError:
raise ValueError("Invalid token %r" % (token,))
if not (isinstance(token, str) and VALID_TOKEN.match(token)):
diff --git a/src/pyramid/authorization.py b/src/pyramid/authorization.py
index 974748765..6056a8d25 100644
--- a/src/pyramid/authorization.py
+++ b/src/pyramid/authorization.py
@@ -4,10 +4,10 @@ from pyramid.interfaces import IAuthorizationPolicy
from pyramid.location import lineage
-from pyramid.compat import is_nonstr_iter
-
from pyramid.security import ACLAllowed, ACLDenied, Allow, Deny, Everyone
+from pyramid.util import is_nonstr_iter
+
@implementer(IAuthorizationPolicy)
class ACLAuthorizationPolicy(object):
diff --git a/src/pyramid/compat.py b/src/pyramid/compat.py
deleted file mode 100644
index 31832c874..000000000
--- a/src/pyramid/compat.py
+++ /dev/null
@@ -1,326 +0,0 @@
-import inspect
-import platform
-import sys
-import types
-
-WIN = platform.system() == 'Windows'
-
-try: # pragma: no cover
- import __pypy__
-
- PYPY = True
-except BaseException: # pragma: no cover
- __pypy__ = None
- PYPY = False
-
-try:
- import cPickle as pickle
-except ImportError: # pragma: no cover
- import pickle
-
-try:
- from functools import lru_cache
-except ImportError:
- from repoze.lru import lru_cache
-
-# PY3 is left as bw-compat but PY2 should be used for most checks.
-PY2 = sys.version_info[0] == 2
-PY3 = sys.version_info[0] == 3
-
-if PY2:
- string_types = (basestring,)
- integer_types = (int, long)
- class_types = (type, types.ClassType)
- text_type = unicode
- binary_type = str
- long = long
-else:
- string_types = (str,)
- integer_types = (int,)
- class_types = (type,)
- text_type = str
- binary_type = bytes
- long = int
-
-
-def text_(s, encoding='latin-1', errors='strict'):
- """ If ``s`` is an instance of ``binary_type``, return
- ``s.decode(encoding, errors)``, otherwise return ``s``"""
- if isinstance(s, binary_type):
- return s.decode(encoding, errors)
- return s
-
-
-def bytes_(s, encoding='latin-1', errors='strict'):
- """ If ``s`` is an instance of ``text_type``, return
- ``s.encode(encoding, errors)``, otherwise return ``s``"""
- if isinstance(s, text_type):
- return s.encode(encoding, errors)
- return s
-
-
-if PY2:
-
- def ascii_native_(s):
- if isinstance(s, text_type):
- s = s.encode('ascii')
- return str(s)
-
-
-else:
-
- def ascii_native_(s):
- if isinstance(s, text_type):
- s = s.encode('ascii')
- return str(s, 'ascii', 'strict')
-
-
-ascii_native_.__doc__ = """
-Python 3: If ``s`` is an instance of ``text_type``, return
-``s.encode('ascii')``, otherwise return ``str(s, 'ascii', 'strict')``
-
-Python 2: If ``s`` is an instance of ``text_type``, return
-``s.encode('ascii')``, otherwise return ``str(s)``
-"""
-
-
-if PY2:
-
- def native_(s, encoding='latin-1', errors='strict'):
- """ If ``s`` is an instance of ``text_type``, return
- ``s.encode(encoding, errors)``, otherwise return ``str(s)``"""
- if isinstance(s, text_type):
- return s.encode(encoding, errors)
- return str(s)
-
-
-else:
-
- def native_(s, encoding='latin-1', errors='strict'):
- """ If ``s`` is an instance of ``text_type``, return
- ``s``, otherwise return ``str(s, encoding, errors)``"""
- if isinstance(s, text_type):
- return s
- return str(s, encoding, errors)
-
-
-native_.__doc__ = """
-Python 3: If ``s`` is an instance of ``text_type``, return ``s``, otherwise
-return ``str(s, encoding, errors)``
-
-Python 2: If ``s`` is an instance of ``text_type``, return
-``s.encode(encoding, errors)``, otherwise return ``str(s)``
-"""
-
-if PY2:
- 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'
- ): # pragma: no cover
- v = url_unquote(v)
- return v.decode(encoding, errors)
-
- def url_unquote_native(
- v, encoding='utf-8', errors='replace'
- ): # pragma: no cover
- return native_(url_unquote_text(v, encoding, errors))
-
-
-else:
- 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
-
-
-if PY2: # 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
-"""
- )
-
-else: # pragma: no cover
- import builtins
-
- exec_ = getattr(builtins, "exec")
-
- def reraise(tp, value, tb=None):
- if value is None:
- value = tp
- if value.__traceback__ is not tb:
- raise value.with_traceback(tb)
- raise value
-
- del builtins
-
-
-if PY2: # pragma: no cover
-
- def iteritems_(d):
- return d.iteritems()
-
- def itervalues_(d):
- return d.itervalues()
-
- def iterkeys_(d):
- return d.iterkeys()
-
-
-else: # pragma: no cover
-
- def iteritems_(d):
- return d.items()
-
- def itervalues_(d):
- return d.values()
-
- def iterkeys_(d):
- return d.keys()
-
-
-if PY2:
- map_ = map
-else:
-
- def map_(*arg):
- return list(map(*arg))
-
-
-if PY2:
-
- def is_nonstr_iter(v):
- return hasattr(v, '__iter__')
-
-
-else:
-
- def is_nonstr_iter(v):
- if isinstance(v, str):
- return False
- return hasattr(v, '__iter__')
-
-
-if PY2:
- im_func = 'im_func'
- im_self = 'im_self'
-else:
- im_func = '__func__'
- im_self = '__self__'
-
-try:
- import configparser
-except ImportError:
- import ConfigParser as configparser
-
-try:
- from http.cookies import SimpleCookie
-except ImportError:
- from Cookie import SimpleCookie
-
-if PY2:
- from cgi import escape
-else:
- from html import escape
-
-if PY2:
- input_ = raw_input
-else:
- input_ = input
-
-if PY2:
- from io import BytesIO as NativeIO
-else:
- from io import StringIO as NativeIO
-
-# "json" is not an API; it's here to support older pyramid_debugtoolbar
-# versions which attempt to import it
-import json
-
-if PY2:
-
- def decode_path_info(path):
- return path.decode('utf-8')
-
-
-else:
- # see PEP 3333 for why we encode WSGI PATH_INFO to latin-1 before
- # decoding it to utf-8
- def decode_path_info(path):
- return path.encode('latin-1').decode('utf-8')
-
-
-if PY2:
- from urlparse import unquote as unquote_to_bytes
-
- def unquote_bytes_to_wsgi(bytestring):
- return unquote_to_bytes(bytestring)
-
-
-else:
- # see PEP 3333 for why we decode the path to latin-1
- from urllib.parse import unquote_to_bytes
-
- def unquote_bytes_to_wsgi(bytestring):
- return unquote_to_bytes(bytestring).decode('latin-1')
-
-
-def is_bound_method(ob):
- return inspect.ismethod(ob) and getattr(ob, im_self, None) is not None
-
-
-# support annotations and keyword-only arguments in PY3
-if PY2:
- from inspect import getargspec
-else:
- from inspect import getfullargspec as getargspec
-
-if PY2:
- from itertools import izip_longest as zip_longest
-else:
- from itertools import zip_longest
-
-
-def is_unbound_method(fn):
- """
- This consistently verifies that the callable is bound to a
- class.
- """
- is_bound = is_bound_method(fn)
-
- if not is_bound and inspect.isroutine(fn):
- spec = getargspec(fn)
- has_self = len(spec.args) > 0 and spec.args[0] == 'self'
-
- if PY2 and inspect.ismethod(fn):
- return True
- elif inspect.isfunction(fn) and has_self:
- return True
-
- return False
diff --git a/src/pyramid/config/__init__.py b/src/pyramid/config/__init__.py
index 475f0d9a2..072b654c4 100644
--- a/src/pyramid/config/__init__.py
+++ b/src/pyramid/config/__init__.py
@@ -19,8 +19,6 @@ from pyramid.asset import resolve_asset_spec
from pyramid.authorization import ACLAuthorizationPolicy
-from pyramid.compat import text_, string_types
-
from pyramid.events import ApplicationCreated
from pyramid.exceptions import ConfigurationError
@@ -37,7 +35,7 @@ from pyramid.settings import aslist
from pyramid.threadlocal import manager
-from pyramid.util import WeakOrderedSet, object_description
+from pyramid.util import WeakOrderedSet, get_callable_name, object_description
from pyramid.config.actions import action_method, ActionState
from pyramid.config.predicates import not_
@@ -59,7 +57,6 @@ from pyramid.config.zca import ZCAConfiguratorMixin
from pyramid.path import DottedNameResolver
-empty = text_('')
_marker = object()
not_ = not_ # api
@@ -367,7 +364,7 @@ class Configurator(
self._set_settings(settings)
- if isinstance(debug_logger, string_types):
+ if isinstance(debug_logger, str):
debug_logger = logging.getLogger(debug_logger)
if debug_logger is None:
@@ -489,11 +486,7 @@ class Configurator(
if not hasattr(_registry, 'registerSelfAdapter'):
def registerSelfAdapter(
- required=None,
- provided=None,
- name=empty,
- info=empty,
- event=True,
+ required=None, provided=None, name='', info='', event=True
):
return _registry.registerAdapter(
lambda x: x,
@@ -705,6 +698,7 @@ class Configurator(
``add_directive`` does not participate in conflict detection, and
later calls to ``add_directive`` will override earlier calls.
"""
+ name = get_callable_name(name)
c = self.maybe_dotted(directive)
if not hasattr(self.registry, '_directives'):
self.registry._directives = {}
@@ -759,7 +753,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, string_types):
+ if not isinstance(relative_spec, str):
return relative_spec
return self._make_spec(relative_spec)
diff --git a/src/pyramid/config/actions.py b/src/pyramid/config/actions.py
index 9c1227d4a..4a526e242 100644
--- a/src/pyramid/config/actions.py
+++ b/src/pyramid/config/actions.py
@@ -5,7 +5,6 @@ import sys
import traceback
from zope.interface import implementer
-from pyramid.compat import reraise
from pyramid.exceptions import (
ConfigurationConflictError,
ConfigurationError,
@@ -14,6 +13,7 @@ from pyramid.exceptions import (
from pyramid.interfaces import IActionInfo
from pyramid.registry import undefer
from pyramid.util import is_nonstr_iter
+from pyramid.util import reraise
class ActionConfiguratorMixin(object):
diff --git a/src/pyramid/config/predicates.py b/src/pyramid/config/predicates.py
index 31e770562..3eb07c17d 100644
--- a/src/pyramid/config/predicates.py
+++ b/src/pyramid/config/predicates.py
@@ -1,12 +1,12 @@
from hashlib import md5
from webob.acceptparse import Accept
-from pyramid.compat import bytes_, is_nonstr_iter
from pyramid.exceptions import ConfigurationError
from pyramid.interfaces import IPredicateList, PHASE1_CONFIG
from pyramid.predicates import Notted
from pyramid.registry import predvalseq
from pyramid.util import TopologicalSorter
+from pyramid.util import is_nonstr_iter, bytes_
MAX_ORDER = 1 << 30
diff --git a/src/pyramid/config/routes.py b/src/pyramid/config/routes.py
index 52540c935..4b26b7481 100644
--- a/src/pyramid/config/routes.py
+++ b/src/pyramid/config/routes.py
@@ -1,7 +1,7 @@
import contextlib
+from urllib.parse import urlparse
import warnings
-from pyramid.compat import urlparse
from pyramid.interfaces import (
IRequest,
IRouteRequest,
@@ -358,7 +358,7 @@ class RoutesConfiguratorMixin(object):
# check for an external route; an external route is one which is
# is a full url (e.g. 'http://example.com/{id}')
- parsed = urlparse.urlparse(pattern)
+ parsed = urlparse(pattern)
external_url = pattern
if parsed.hostname:
diff --git a/src/pyramid/config/testing.py b/src/pyramid/config/testing.py
index bba5054e6..9c998840a 100644
--- a/src/pyramid/config/testing.py
+++ b/src/pyramid/config/testing.py
@@ -9,7 +9,7 @@ from pyramid.interfaces import (
from pyramid.renderers import RendererHelper
-from pyramid.traversal import decode_path_info, split_path_info
+from pyramid.traversal import split_path_info
from pyramid.config.actions import action_method
@@ -95,7 +95,7 @@ class TestingConfiguratorMixin(object):
self.context = context
def __call__(self, request):
- path = decode_path_info(request.environ['PATH_INFO'])
+ path = request.path_info
ob = resources[path]
traversed = split_path_info(path)
return {
diff --git a/src/pyramid/config/tweens.py b/src/pyramid/config/tweens.py
index 7fc786a97..c85639d14 100644
--- a/src/pyramid/config/tweens.py
+++ b/src/pyramid/config/tweens.py
@@ -2,13 +2,15 @@ from zope.interface import implementer
from pyramid.interfaces import ITweens
-from pyramid.compat import string_types, is_nonstr_iter
-
from pyramid.exceptions import ConfigurationError
from pyramid.tweens import MAIN, INGRESS, EXCVIEW
-from pyramid.util import is_string_or_iterable, TopologicalSorter
+from pyramid.util import (
+ is_nonstr_iter,
+ is_string_or_iterable,
+ TopologicalSorter,
+)
from pyramid.config.actions import action_method
@@ -105,7 +107,7 @@ class TweensConfiguratorMixin(object):
@action_method
def _add_tween(self, tween_factory, under=None, over=None, explicit=False):
- if not isinstance(tween_factory, string_types):
+ if not isinstance(tween_factory, str):
raise ConfigurationError(
'The "tween_factory" argument to add_tween must be a '
'dotted name to a globally importable object, not %r'
diff --git a/src/pyramid/config/views.py b/src/pyramid/config/views.py
index 0c4a17376..ac531ecb2 100644
--- a/src/pyramid/config/views.py
+++ b/src/pyramid/config/views.py
@@ -5,6 +5,7 @@ import operator
import os
import warnings
+from urllib.parse import quote, urljoin, urlparse, urlunparse
from webob.acceptparse import Accept
from zope.interface import Interface, implementedBy, implementer
from zope.interface.interfaces import IInterface
@@ -32,13 +33,6 @@ from pyramid.interfaces import (
from pyramid import renderers
from pyramid.asset import resolve_asset_spec
-from pyramid.compat import (
- string_types,
- urlparse,
- url_quote,
- WIN,
- is_nonstr_iter,
-)
from pyramid.decorator import reify
@@ -59,7 +53,12 @@ from pyramid.url import parse_url_overrides
from pyramid.view import AppendSlashNotFoundViewFactory
-from pyramid.util import as_sorted_tuple, TopologicalSorter
+from pyramid.util import (
+ as_sorted_tuple,
+ is_nonstr_iter,
+ TopologicalSorter,
+ WIN,
+)
import pyramid.predicates
import pyramid.viewderivers
@@ -83,9 +82,6 @@ from pyramid.config.predicates import (
sort_accept_offers,
)
-urljoin = urlparse.urljoin
-url_parse = urlparse.urlparse
-
DefaultViewMapper = DefaultViewMapper # bw-compat
preserve_view_attrs = preserve_view_attrs # bw-compat
requestonly = requestonly # bw-compat
@@ -889,7 +885,7 @@ class ViewsConfiguratorMixin(object):
if not IInterface.providedBy(r_context):
r_context = implementedBy(r_context)
- if isinstance(renderer, string_types):
+ if isinstance(renderer, str):
renderer = renderers.RendererHelper(
name=renderer, package=self.package, registry=self.registry
)
@@ -1582,7 +1578,7 @@ class ViewsConfiguratorMixin(object):
):
view = self.maybe_dotted(view)
mapper = self.maybe_dotted(mapper)
- if isinstance(renderer, string_types):
+ if isinstance(renderer, str):
renderer = renderers.RendererHelper(
name=renderer, package=self.package, registry=self.registry
)
@@ -2197,14 +2193,12 @@ class StaticURLInfo(object):
return request.route_url(route_name, **kw)
else:
app_url, qs, anchor = parse_url_overrides(request, kw)
- parsed = url_parse(url)
+ parsed = urlparse(url)
if not parsed.scheme:
- url = urlparse.urlunparse(
- parsed._replace(
- scheme=request.environ['wsgi.url_scheme']
- )
+ url = urlunparse(
+ parsed._replace(scheme=request.scheme)
)
- subpath = url_quote(subpath)
+ subpath = quote(subpath)
result = urljoin(url, subpath)
return result + qs + anchor
@@ -2233,7 +2227,7 @@ class StaticURLInfo(object):
# make sure it ends with a slash
name = name + '/'
- if url_parse(name).netloc:
+ if urlparse(name).netloc:
# it's a URL
# url, spec, route_name
url = name
diff --git a/src/pyramid/csrf.py b/src/pyramid/csrf.py
index fba5d9baa..26c628acc 100644
--- a/src/pyramid/csrf.py
+++ b/src/pyramid/csrf.py
@@ -1,14 +1,20 @@
+from urllib.parse import urlparse
import uuid
from webob.cookies import CookieProfile
from zope.interface import implementer
-from pyramid.compat import bytes_, urlparse, text_
from pyramid.exceptions import BadCSRFOrigin, BadCSRFToken
from pyramid.interfaces import ICSRFStoragePolicy
from pyramid.settings import aslist
-from pyramid.util import SimpleSerializer, is_same_domain, strings_differ
+from pyramid.util import (
+ SimpleSerializer,
+ is_same_domain,
+ strings_differ,
+ bytes_,
+ text_,
+)
@implementer(ICSRFStoragePolicy)
@@ -117,7 +123,6 @@ class CookieCSRFStoragePolicy(object):
path='/',
samesite='Lax',
):
- serializer = SimpleSerializer()
self.cookie_profile = CookieProfile(
cookie_name=cookie_name,
secure=secure,
@@ -125,7 +130,7 @@ class CookieCSRFStoragePolicy(object):
httponly=httponly,
path=path,
domains=[domain],
- serializer=serializer,
+ serializer=SimpleSerializer(),
samesite=samesite,
)
self.cookie_name = cookie_name
@@ -303,7 +308,7 @@ def check_csrf_origin(request, trusted_origins=None, raises=True):
# Parse our origin so we we can extract the required information from
# it.
- originp = urlparse.urlparse(origin)
+ originp = urlparse(origin)
# Ensure that our Referer is also secure.
if originp.scheme != "https":
diff --git a/src/pyramid/encode.py b/src/pyramid/encode.py
index 2cf2247da..11d3a7787 100644
--- a/src/pyramid/encode.py
+++ b/src/pyramid/encode.py
@@ -1,17 +1,14 @@
-from pyramid.compat import (
- text_type,
- binary_type,
- is_nonstr_iter,
- url_quote as _url_quote,
- url_quote_plus as _quote_plus,
-)
+from urllib.parse import quote as _url_quote
+from urllib.parse import quote_plus as _quote_plus
+
+from pyramid.util import is_nonstr_iter
def url_quote(val, safe=''): # bw compat api
cls = val.__class__
- if cls is text_type:
+ if cls is str:
val = val.encode('utf-8')
- elif cls is not binary_type:
+ elif cls is not bytes:
val = str(val).encode('utf-8')
return _url_quote(val, safe=safe)
@@ -19,9 +16,9 @@ def url_quote(val, safe=''): # bw compat api
# bw compat api (dnr)
def quote_plus(val, safe=''):
cls = val.__class__
- if cls is text_type:
+ if cls is str:
val = val.encode('utf-8')
- elif cls is not binary_type:
+ elif cls is not bytes:
val = str(val).encode('utf-8')
return _quote_plus(val, safe=safe)
diff --git a/src/pyramid/httpexceptions.py b/src/pyramid/httpexceptions.py
index 959a45f37..56797dc88 100644
--- a/src/pyramid/httpexceptions.py
+++ b/src/pyramid/httpexceptions.py
@@ -137,22 +137,21 @@ from zope.interface import implementer
from webob import html_escape as _html_escape
from webob.acceptparse import create_accept_header
-from pyramid.compat import class_types, text_type, binary_type, text_
-
from pyramid.interfaces import IExceptionResponse
from pyramid.response import Response
+from pyramid.util import text_
def _no_escape(value):
if value is None:
return ''
- if not isinstance(value, text_type):
+ if not isinstance(value, str):
if hasattr(value, '__unicode__'):
value = value.__unicode__()
- if isinstance(value, binary_type):
+ if isinstance(value, bytes):
value = text_(value, 'utf-8')
else:
- value = text_type(value)
+ value = str(value)
return value
@@ -326,7 +325,7 @@ ${body}'''
args[k.lower()] = escape(v)
body = body_tmpl.substitute(args)
page = page_template.substitute(status=self.status, body=body)
- if isinstance(page, text_type):
+ if isinstance(page, str):
page = page.encode(self.charset if self.charset else 'UTF-8')
self.app_iter = [page]
self.body = page
@@ -1331,7 +1330,7 @@ status_map = {}
code = None
for name, value in list(globals().items()):
if (
- isinstance(value, class_types)
+ isinstance(value, type)
and issubclass(value, HTTPException)
and value not in {HTTPClientError, HTTPServerError}
and not name.startswith('_')
diff --git a/src/pyramid/i18n.py b/src/pyramid/i18n.py
index e99a29aab..45f528852 100644
--- a/src/pyramid/i18n.py
+++ b/src/pyramid/i18n.py
@@ -8,7 +8,6 @@ from translationstring import (
TranslationStringFactory, # API
)
-from pyramid.compat import PY2
from pyramid.decorator import reify
from pyramid.interfaces import (
@@ -353,10 +352,7 @@ class Translations(gettext.GNUTranslations, object):
"""Like ``ugettext()``, but look the message up in the specified
domain.
"""
- if PY2:
- return self._domains.get(domain, self).ugettext(message)
- else:
- return self._domains.get(domain, self).gettext(message)
+ return self._domains.get(domain, self).gettext(message)
def dngettext(self, domain, singular, plural, num):
"""Like ``ngettext()``, but look the message up in the specified
@@ -374,14 +370,7 @@ class Translations(gettext.GNUTranslations, object):
"""Like ``ungettext()`` but look the message up in the specified
domain.
"""
- if PY2:
- return self._domains.get(domain, self).ungettext(
- singular, plural, num
- )
- else:
- return self._domains.get(domain, self).ngettext(
- singular, plural, num
- )
+ return self._domains.get(domain, self).ngettext(singular, plural, num)
class LocalizerRequestMixin(object):
diff --git a/src/pyramid/interfaces.py b/src/pyramid/interfaces.py
index 31bcd7e88..f1e238c6b 100644
--- a/src/pyramid/interfaces.py
+++ b/src/pyramid/interfaces.py
@@ -1,7 +1,5 @@
from zope.interface import Attribute, Interface
-from pyramid.compat import PY2
-
# public API interfaces
@@ -366,19 +364,6 @@ class IDict(Interface):
def values():
""" Return a list of values from the dictionary """
- if PY2:
-
- 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
doesn't exist, and default is provided, return the default. If k
diff --git a/src/pyramid/path.py b/src/pyramid/path.py
index c70be99db..47877ce5d 100644
--- a/src/pyramid/path.py
+++ b/src/pyramid/path.py
@@ -7,8 +7,6 @@ from zope.interface import implementer
from pyramid.interfaces import IAssetDescriptor
-from pyramid.compat import string_types
-
ignore_types = [imp.C_EXTENSION, imp.C_BUILTIN]
init_names = [
'__init__%s' % x[0]
@@ -101,7 +99,7 @@ class Resolver(object):
if package in (None, CALLER_PACKAGE):
self.package = package
else:
- if isinstance(package, string_types):
+ if isinstance(package, str):
try:
__import__(package)
except ImportError:
@@ -307,7 +305,7 @@ class DottedNameResolver(Resolver):
v = r.resolve('xml') # v is the xml module
"""
- if not isinstance(dotted, string_types):
+ if not isinstance(dotted, str):
raise ValueError('%r is not a string' % (dotted,))
package = self.package
if package is CALLER_PACKAGE:
@@ -328,7 +326,7 @@ class DottedNameResolver(Resolver):
v = r.maybe_resolve(xml)
# v is the xml module; no exception raised
"""
- if isinstance(dotted, string_types):
+ if isinstance(dotted, str):
package = self.package
if package is CALLER_PACKAGE:
package = caller_package()
diff --git a/src/pyramid/predicates.py b/src/pyramid/predicates.py
index 280f6c03c..5a1127fb3 100644
--- a/src/pyramid/predicates.py
+++ b/src/pyramid/predicates.py
@@ -2,8 +2,6 @@ import re
from pyramid.exceptions import ConfigurationError
-from pyramid.compat import is_nonstr_iter
-
from pyramid.csrf import check_csrf_token
from pyramid.traversal import (
find_interface,
@@ -12,7 +10,7 @@ from pyramid.traversal import (
)
from pyramid.urldispatch import _compile_route
-from pyramid.util import as_sorted_tuple, object_description
+from pyramid.util import as_sorted_tuple, is_nonstr_iter, object_description
_marker = object()
diff --git a/src/pyramid/registry.py b/src/pyramid/registry.py
index c24125830..7b2547dd7 100644
--- a/src/pyramid/registry.py
+++ b/src/pyramid/registry.py
@@ -4,15 +4,12 @@ import threading
from zope.interface import implementer
from zope.interface.registry import Components
-from pyramid.compat import text_
from pyramid.decorator import reify
from pyramid.interfaces import IIntrospector, IIntrospectable, ISettings
from pyramid.path import CALLER_PACKAGE, caller_package
-empty = text_('')
-
class Registry(Components, dict):
""" A registry object is an :term:`application registry`.
@@ -77,7 +74,7 @@ class Registry(Components, dict):
return result
def registerSelfAdapter(
- self, required=None, provided=None, name=empty, info=empty, event=True
+ self, required=None, provided=None, name='', info='', event=True
):
# registerAdapter analogue which always returns the object itself
# when required is matched
diff --git a/src/pyramid/renderers.py b/src/pyramid/renderers.py
index a8e3ec16f..832369fd4 100644
--- a/src/pyramid/renderers.py
+++ b/src/pyramid/renderers.py
@@ -8,8 +8,6 @@ from zope.interface.registry import Components
from pyramid.interfaces import IJSONAdapter, IRendererFactory, IRendererInfo
-from pyramid.compat import string_types, text_type
-
from pyramid.csrf import get_csrf_token
from pyramid.decorator import reify
@@ -169,7 +167,7 @@ def get_renderer(renderer_name, package=None, registry=None):
def string_renderer_factory(info):
def _render(value, system):
- if not isinstance(value, string_types):
+ if not isinstance(value, str):
value = str(value)
request = system.get('request')
if request is not None:
@@ -485,7 +483,7 @@ class RendererHelper(object):
response = response_factory(request)
if result is not None:
- if isinstance(result, text_type):
+ if isinstance(result, str):
response.text = result
elif isinstance(result, bytes):
response.body = result
diff --git a/src/pyramid/request.py b/src/pyramid/request.py
index 907b4477f..23c00468d 100644
--- a/src/pyramid/request.py
+++ b/src/pyramid/request.py
@@ -13,14 +13,17 @@ from pyramid.interfaces import (
ISessionFactory,
)
-from pyramid.compat import text_, bytes_, native_, iteritems_
-
from pyramid.decorator import reify
from pyramid.i18n import LocalizerRequestMixin
from pyramid.response import Response, _get_response_factory
from pyramid.security import AuthenticationAPIMixin, AuthorizationAPIMixin
from pyramid.url import URLMethodsMixin
-from pyramid.util import InstancePropertyHelper, InstancePropertyMixin
+from pyramid.util import (
+ InstancePropertyHelper,
+ InstancePropertyMixin,
+ text_,
+ bytes_,
+)
from pyramid.view import ViewMethodsMixin
@@ -281,7 +284,7 @@ def call_app_with_subpath_as_path_info(request, app):
# compute new_path_info
new_path_info = '/' + '/'.join(
- [native_(x.encode('utf-8'), 'latin-1') for x in subpath]
+ [text_(x.encode('utf-8'), 'latin-1') for x in subpath]
)
if new_path_info != '/': # don't want a sole double-slash
@@ -328,7 +331,7 @@ def apply_request_extensions(request, extensions=None):
if extensions is None:
extensions = request.registry.queryUtility(IRequestExtensions)
if extensions is not None:
- for name, fn in iteritems_(extensions.methods):
+ for name, fn in extensions.methods.items():
method = fn.__get__(request, request.__class__)
setattr(request, name, method)
diff --git a/src/pyramid/response.py b/src/pyramid/response.py
index 38f9fa1ce..8a2ba8929 100644
--- a/src/pyramid/response.py
+++ b/src/pyramid/response.py
@@ -100,14 +100,12 @@ class FileIter(object):
def __iter__(self):
return self
- def next(self):
+ def __next__(self):
val = self.file.read(self.block_size)
if not val:
raise StopIteration
return val
- __next__ = next # py3
-
def close(self):
self.file.close()
diff --git a/src/pyramid/scripts/prequest.py b/src/pyramid/scripts/prequest.py
index e8f5ff8b3..eb2032419 100644
--- a/src/pyramid/scripts/prequest.py
+++ b/src/pyramid/scripts/prequest.py
@@ -2,8 +2,8 @@ import base64
import argparse
import sys
import textwrap
+from urllib.parse import unquote
-from pyramid.compat import url_unquote
from pyramid.request import Request
from pyramid.scripts.common import get_config_loader
from pyramid.scripts.common import parse_vars
@@ -152,7 +152,7 @@ class PRequestCommand(object):
except ValueError:
qs = ''
- path = url_unquote(path)
+ path = unquote(path)
headers = {}
if self.args.login:
diff --git a/src/pyramid/scripts/proutes.py b/src/pyramid/scripts/proutes.py
index 2bce7d1de..78c2295d5 100644
--- a/src/pyramid/scripts/proutes.py
+++ b/src/pyramid/scripts/proutes.py
@@ -7,7 +7,6 @@ import re
from zope.interface import Interface
from pyramid.paster import bootstrap
-from pyramid.compat import string_types
from pyramid.interfaces import IRouteRequest
from pyramid.config import not_
@@ -188,7 +187,7 @@ def get_route_data(route, registry):
view_request_methods[view_module] = []
view_request_methods_order.append(view_module)
- if isinstance(request_method, string_types):
+ if isinstance(request_method, str):
request_method = (request_method,)
elif isinstance(request_method, not_):
request_method = ('!%s' % request_method.value,)
diff --git a/src/pyramid/scripts/pserve.py b/src/pyramid/scripts/pserve.py
index 581479d65..7d68521a4 100644
--- a/src/pyramid/scripts/pserve.py
+++ b/src/pyramid/scripts/pserve.py
@@ -19,8 +19,6 @@ import webbrowser
import hupper
-from pyramid.compat import PY2
-
from pyramid.scripts.common import get_config_loader
from pyramid.scripts.common import parse_vars
from pyramid.path import AssetResolver
@@ -380,18 +378,15 @@ def cherrypy_server_runner(
server = WSGIServer(bind_addr, app, server_name=server_name, **kwargs)
if ssl_pem is not None:
- if PY2:
- server.ssl_certificate = server.ssl_private_key = ssl_pem
- else:
- # creates wsgiserver.ssl_builtin as side-effect
- try:
- from cheroot.server import get_ssl_adapter_class
- from cheroot.ssl.builtin import BuiltinSSLAdapter
- except ImportError:
- from cherrypy.wsgiserver import get_ssl_adapter_class
- from cherrypy.wsgiserver.ssl_builtin import BuiltinSSLAdapter
- get_ssl_adapter_class()
- server.ssl_adapter = BuiltinSSLAdapter(ssl_pem, ssl_pem)
+ # creates wsgiserver.ssl_builtin as side-effect
+ try:
+ from cheroot.server import get_ssl_adapter_class
+ from cheroot.ssl.builtin import BuiltinSSLAdapter
+ except ImportError:
+ from cherrypy.wsgiserver import get_ssl_adapter_class
+ from cherrypy.wsgiserver.ssl_builtin import BuiltinSSLAdapter
+ get_ssl_adapter_class()
+ server.ssl_adapter = BuiltinSSLAdapter(ssl_pem, ssl_pem)
if protocol_version:
server.protocol = protocol_version
diff --git a/src/pyramid/scripts/pshell.py b/src/pyramid/scripts/pshell.py
index e63114d18..a9f02e408 100644
--- a/src/pyramid/scripts/pshell.py
+++ b/src/pyramid/scripts/pshell.py
@@ -6,7 +6,6 @@ import sys
import textwrap
import pkg_resources
-from pyramid.compat import exec_
from pyramid.util import DottedNameResolver
from pyramid.util import make_contextmanager
from pyramid.paster import bootstrap
@@ -214,7 +213,7 @@ class PShellCommand(object):
if self.pystartup and os.path.isfile(self.pystartup):
with open(self.pystartup, 'rb') as fp:
- exec_(fp.read().decode('utf-8'), env)
+ exec(fp.read().decode('utf-8'), env)
if '__builtins__' in env:
del env['__builtins__']
diff --git a/src/pyramid/scripts/pviews.py b/src/pyramid/scripts/pviews.py
index 891dc4709..d2a4bfa40 100644
--- a/src/pyramid/scripts/pviews.py
+++ b/src/pyramid/scripts/pviews.py
@@ -70,7 +70,7 @@ class PViewsCommand(object):
def _find_multi_routes(self, mapper, request):
infos = []
- path = request.environ['PATH_INFO']
+ path = request.path_info
# find all routes that match path, regardless of predicates
for route in mapper.get_routes():
match = route.match(path)
diff --git a/src/pyramid/security.py b/src/pyramid/security.py
index 08ae295d8..f8743e9a7 100644
--- a/src/pyramid/security.py
+++ b/src/pyramid/security.py
@@ -8,7 +8,6 @@ from pyramid.interfaces import (
IViewClassifier,
)
-from pyramid.compat import map_
from pyramid.threadlocal import get_current_registry
Everyone = 'system.Everyone'
@@ -149,7 +148,7 @@ def view_execution_permitted(context, request, name=''):
"""
reg = _get_registry(request)
- provides = [IViewClassifier] + map_(providedBy, (request, context))
+ provides = [IViewClassifier] + [providedBy(x) for x in (request, context)]
# XXX not sure what to do here about using _find_views or analogue;
# for now let's just keep it as-is
view = reg.adapters.lookup(provides, ISecuredView, name=name)
diff --git a/src/pyramid/session.py b/src/pyramid/session.py
index 68e0c506c..70ac4f55f 100644
--- a/src/pyramid/session.py
+++ b/src/pyramid/session.py
@@ -1,5 +1,6 @@
import binascii
import os
+import pickle
import time
from zope.deprecation import deprecated
@@ -7,11 +8,12 @@ from zope.interface import implementer
from webob.cookies import JSONSerializer, SignedSerializer
-from pyramid.compat import pickle, PY2, text_, bytes_, native_
from pyramid.csrf import check_csrf_origin, check_csrf_token
from pyramid.interfaces import ISession
+from pyramid.util import text_, bytes_
+
def manage_accessed(wrapped):
""" Decorator which causes a cookie to be renewed when an accessor
@@ -255,12 +257,6 @@ def BaseCookieSessionFactory(
__len__ = manage_accessed(dict.__len__)
__iter__ = manage_accessed(dict.__iter__)
- if PY2:
- 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_changed(dict.clear)
update = manage_changed(dict.update)
@@ -309,7 +305,7 @@ def BaseCookieSessionFactory(
exception is not None
): # dont set a cookie during exceptions
return False
- cookieval = native_(
+ cookieval = text_(
serializer.dumps((self.accessed, self.created, dict(self)))
)
if len(cookieval) > 4064:
diff --git a/src/pyramid/settings.py b/src/pyramid/settings.py
index af9433840..d1eb4ff14 100644
--- a/src/pyramid/settings.py
+++ b/src/pyramid/settings.py
@@ -1,5 +1,3 @@
-from pyramid.compat import string_types
-
truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1'))
falsey = frozenset(('f', 'false', 'n', 'no', 'off', '0'))
@@ -17,7 +15,7 @@ def asbool(s):
def aslist_cronly(value):
- if isinstance(value, string_types):
+ if isinstance(value, str):
value = filter(None, [x.strip() for x in value.splitlines()])
return list(value)
diff --git a/src/pyramid/static.py b/src/pyramid/static.py
index 58ad97a46..e3561e93e 100644
--- a/src/pyramid/static.py
+++ b/src/pyramid/static.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+from functools import lru_cache
import json
import os
@@ -8,8 +9,6 @@ from pkg_resources import resource_exists, resource_filename, resource_isdir
from pyramid.asset import abspath_from_asset_spec, resolve_asset_spec
-from pyramid.compat import lru_cache, text_
-
from pyramid.httpexceptions import HTTPNotFound, HTTPMovedPermanently
from pyramid.path import caller_package
@@ -18,8 +17,6 @@ from pyramid.response import _guess_type, FileResponse
from pyramid.traversal import traversal_path_info
-slash = text_('/')
-
class static_view(object):
""" An instance of this class is a callable which can act as a
@@ -91,7 +88,7 @@ class static_view(object):
if self.use_subpath:
path_tuple = request.subpath
else:
- path_tuple = traversal_path_info(request.environ['PATH_INFO'])
+ path_tuple = traversal_path_info(request.path_info)
path = _secure_path(path_tuple)
if path is None:
@@ -160,7 +157,7 @@ def _secure_path(path_tuple):
return None
if any([_contains_slash(item) for item in path_tuple]):
return None
- encoded = slash.join(path_tuple) # will be unicode
+ encoded = '/'.join(path_tuple) # will be unicode
return encoded
diff --git a/src/pyramid/testing.py b/src/pyramid/testing.py
index f700b5a4e..6831ea4e2 100644
--- a/src/pyramid/testing.py
+++ b/src/pyramid/testing.py
@@ -8,8 +8,6 @@ from zope.interface import implementer, alsoProvides
from pyramid.interfaces import IRequest, ISession
-from pyramid.compat import PY3, PYPY, class_types, text_
-
from pyramid.config import Configurator
from pyramid.decorator import reify
from pyramid.path import caller_package
@@ -28,7 +26,7 @@ from pyramid.threadlocal import get_current_registry, manager
from pyramid.i18n import LocalizerRequestMixin
from pyramid.request import CallbackMethodsMixin
from pyramid.url import URLMethodsMixin
-from pyramid.util import InstancePropertyMixin
+from pyramid.util import InstancePropertyMixin, PYPY, text_
from pyramid.view import ViewMethodsMixin
@@ -640,11 +638,9 @@ def skip_on(*platforms): # pragma: no cover
skip = True
if platform == 'pypy' and PYPY:
skip = True
- if platform == 'py3' and PY3:
- skip = True
def decorator(func):
- if isinstance(func, class_types):
+ if isinstance(func, type):
if skip:
return None
else:
diff --git a/src/pyramid/traversal.py b/src/pyramid/traversal.py
index 338b49083..7de4c3f91 100644
--- a/src/pyramid/traversal.py
+++ b/src/pyramid/traversal.py
@@ -1,3 +1,6 @@
+from functools import lru_cache
+from urllib.parse import unquote_to_bytes
+
from zope.interface import implementer
from zope.interface.interfaces import IInterface
@@ -8,29 +11,15 @@ from pyramid.interfaces import (
VH_ROOT_KEY,
)
-from pyramid.compat import (
- PY2,
- native_,
- text_,
- ascii_native_,
- text_type,
- binary_type,
- is_nonstr_iter,
- decode_path_info,
- unquote_bytes_to_wsgi,
- lru_cache,
-)
-
from pyramid.encode import url_quote
from pyramid.exceptions import URLDecodeError
from pyramid.location import lineage
from pyramid.threadlocal import get_current_registry
+from pyramid.util import ascii_, is_nonstr_iter, text_
PATH_SEGMENT_SAFE = "~!$&'()*+,;=:@" # from webob
PATH_SAFE = PATH_SEGMENT_SAFE + "/"
-empty = text_('')
-
def find_root(resource):
""" Find the root node in the resource tree to which ``resource``
@@ -94,8 +83,8 @@ def find_resource(resource, path):
:func:`pyramid.traversal.resource_path_tuple` can always be
resolved by ``find_resource``.
"""
- if isinstance(path, text_type):
- path = ascii_native_(path)
+ if isinstance(path, str):
+ path = ascii_(path)
D = traverse(resource, path)
view_name = D['view_name']
context = D['context']
@@ -312,7 +301,7 @@ def traverse(resource, path):
# step rather than later down the line as the result of calling
# ``traversal_path``).
- path = ascii_native_(path)
+ path = ascii_(path)
if path and path[0] == '/':
resource = find_root(resource)
@@ -446,7 +435,7 @@ def traversal_path(path):
not. A :exc:`UnicodeEncodeError` will be raised if the Unicode cannot be
encoded directly to ASCII.
"""
- if isinstance(path, text_type):
+ if isinstance(path, str):
# must not possess characters outside ascii
path = path.encode('ascii')
# we unquote this path exactly like a PEP 3333 server would
@@ -549,83 +538,63 @@ def split_path_info(path):
return tuple(clean)
+# see PEP 3333 for why we encode to latin-1 then decode to utf-8
+def decode_path_info(path):
+ return path.encode('latin-1').decode('utf-8')
+
+
+# see PEP 3333 for why we decode the path to latin-1
+def unquote_bytes_to_wsgi(bytestring):
+ return unquote_to_bytes(bytestring).decode('latin-1')
+
+
_segment_cache = {}
-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 PY2:
- # special-case on Python 2 for speed? unchecked
- def quote_path_segment(segment, safe=PATH_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
-
-
-else:
-
- def quote_path_segment(segment, safe=PATH_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
-
-
-slash = text_('/')
+
+def quote_path_segment(segment, safe=PATH_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__ not in (str, bytes):
+ segment = str(segment)
+ result = url_quote(text_(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
@implementer(ITraverser)
@@ -647,12 +616,12 @@ class ResourceTreeTraverser(object):
if matchdict is not None:
- path = matchdict.get('traverse', slash) or slash
+ path = matchdict.get('traverse', '/') or '/'
if is_nonstr_iter(path):
# this is a *traverse stararg (not a {traverse})
# routing has already decoded these elements, so we just
# need to join them
- path = '/' + slash.join(path) or slash
+ path = '/' + '/'.join(path) or '/'
subpath = matchdict.get('subpath', ())
if not is_nonstr_iter(subpath):
@@ -666,10 +635,10 @@ class ResourceTreeTraverser(object):
subpath = ()
try:
# empty if mounted under a path in mod_wsgi, for example
- path = request.path_info or slash
+ path = request.path_info or '/'
except KeyError:
# if environ['PATH_INFO'] is just not there
- path = slash
+ path = '/'
except UnicodeDecodeError as e:
raise URLDecodeError(
e.encoding, e.object, e.start, e.end, e.reason
@@ -691,7 +660,7 @@ class ResourceTreeTraverser(object):
root = self.root
ob = vroot = root
- if vpath == slash: # invariant: vpath must not be empty
+ if vpath == '/': # invariant: vpath must not be empty
# prevent a call to traversal_path if we know it's going
# to return the empty tuple
vpath_tuple = ()
@@ -745,7 +714,7 @@ class ResourceTreeTraverser(object):
return {
'context': ob,
- 'view_name': empty,
+ 'view_name': '',
'subpath': subpath,
'traversed': vpath_tuple,
'virtual_root': vroot,
diff --git a/src/pyramid/tweens.py b/src/pyramid/tweens.py
index 839c53b8f..b5660b44b 100644
--- a/src/pyramid/tweens.py
+++ b/src/pyramid/tweens.py
@@ -1,7 +1,7 @@
import sys
-from pyramid.compat import reraise
from pyramid.httpexceptions import HTTPNotFound
+from pyramid.util import reraise
def _error_handler(request, exc):
diff --git a/src/pyramid/url.py b/src/pyramid/url.py
index 00dd13bfe..22551a349 100644
--- a/src/pyramid/url.py
+++ b/src/pyramid/url.py
@@ -1,13 +1,14 @@
""" Utility functions for dealing with URLs in pyramid """
+from functools import lru_cache
import os
from pyramid.interfaces import IResourceURL, IRoutesMapper, IStaticURLInfo
-from pyramid.compat import bytes_, lru_cache, string_types
from pyramid.encode import url_quote, urlencode
from pyramid.path import caller_package
from pyramid.threadlocal import get_current_registry
+from pyramid.util import bytes_
from pyramid.traversal import (
ResourceURL,
@@ -45,7 +46,7 @@ def parse_url_overrides(request, kw):
qs = ''
if query:
- if isinstance(query, string_types):
+ if isinstance(query, str):
qs = '?' + url_quote(query, QUERY_SAFE)
else:
qs = '?' + urlencode(query, doseq=True)
diff --git a/src/pyramid/urldispatch.py b/src/pyramid/urldispatch.py
index de8a69d2a..73b7be9f3 100644
--- a/src/pyramid/urldispatch.py
+++ b/src/pyramid/urldispatch.py
@@ -3,21 +3,12 @@ from zope.interface import implementer
from pyramid.interfaces import IRoutesMapper, IRoute
-from pyramid.compat import (
- PY2,
- native_,
- text_,
- text_type,
- string_types,
- binary_type,
- is_nonstr_iter,
- decode_path_info,
-)
-
from pyramid.exceptions import URLDecodeError
from pyramid.traversal import quote_path_segment, split_path_info, PATH_SAFE
+from pyramid.util import is_nonstr_iter, text_
+
_marker = object()
@@ -82,10 +73,9 @@ class RoutesMapper(object):
return self.routes[name].generate(kw)
def __call__(self, request):
- environ = request.environ
try:
# empty if mounted under a path in mod_wsgi, for example
- path = decode_path_info(environ['PATH_INFO'] or '/')
+ path = request.path_info or '/'
except KeyError:
path = '/'
except UnicodeDecodeError as e:
@@ -127,7 +117,7 @@ def _compile_route(route):
# using the ASCII decoding. We decode it using ASCII because we don't
# want to accept bytestrings with high-order characters in them here as
# we have no idea what the encoding represents.
- if route.__class__ is not text_type:
+ if route.__class__ is not str:
try:
route = text_(route, 'ascii')
except UnicodeDecodeError:
@@ -174,7 +164,7 @@ def _compile_route(route):
name, reg = name.split(':', 1)
else:
reg = '[^/]+'
- gen.append('%%(%s)s' % native_(name)) # native
+ gen.append('%%(%s)s' % name) # native
name = '(?P<%s>%s)' % (name, reg) # unicode
rpat.append(name)
s = pat.pop() # unicode
@@ -189,34 +179,22 @@ def _compile_route(route):
if remainder:
rpat.append('(?P<%s>.*?)' % remainder) # unicode
- gen.append('%%(%s)s' % native_(remainder)) # native
+ gen.append('%%(%s)s' % remainder) # native
pattern = ''.join(rpat) + '$' # unicode
match = re.compile(pattern).match
def matcher(path):
- # This function really wants to consume Unicode patterns natively,
- # but if someone passes us a bytestring, we allow it by converting it
- # to Unicode using the ASCII decoding. We decode it using ASCII
- # because we don't want to accept bytestrings with high-order
- # characters in them here as we have no idea what the encoding
- # represents.
- if path.__class__ is not text_type:
- path = text_(path, 'ascii')
m = match(path)
if m is None:
return None
d = {}
for k, v in m.groupdict().items():
- # k and v will be Unicode 2.6.4 and lower doesnt accept unicode
- # kwargs as **kw, so we explicitly cast the keys to native
- # strings in case someone wants to pass the result as **kw
- nk = native_(k, 'ascii')
if k == remainder:
- d[nk] = split_path_info(v)
+ d[k] = split_path_info(v)
else:
- d[nk] = v
+ d[k] = v
return d
gen = ''.join(gen)
@@ -227,27 +205,21 @@ def _compile_route(route):
def generator(dict):
newdict = {}
for k, v in dict.items():
- if PY2:
- if v.__class__ is text_type:
- # url_quote below needs bytes, not unicode on Py2
- v = v.encode('utf-8')
- else:
- if v.__class__ is binary_type:
- # url_quote below needs a native string, not bytes on Py3
- v = v.decode('utf-8')
+ if v.__class__ is bytes:
+ # url_quote below needs a native string
+ v = v.decode('utf-8')
if k == remainder:
# a stararg argument
if is_nonstr_iter(v):
v = '/'.join([q(x) for x in v]) # native
else:
- if v.__class__ not in string_types:
+ if v.__class__ is not str:
v = str(v)
v = q(v)
else:
- if v.__class__ not in string_types:
+ if v.__class__ is not str:
v = str(v)
- # v may be bytes (py2) or native string (py3)
v = q(v)
# at this point, the value will be a native string
diff --git a/src/pyramid/util.py b/src/pyramid/util.py
index bebf9e7d3..1180fce83 100644
--- a/src/pyramid/util.py
+++ b/src/pyramid/util.py
@@ -1,32 +1,24 @@
from contextlib import contextmanager
import functools
-
-try:
- # py2.7.7+ and py3.3+ have native comparison support
- from hmac import compare_digest
-except ImportError: # pragma: no cover
- compare_digest = None
+from hmac import compare_digest
import inspect
+import platform
import weakref
-from pyramid.exceptions import ConfigurationError, CyclicDependencyError
-
-from pyramid.compat import (
- getargspec,
- im_func,
- is_nonstr_iter,
- integer_types,
- string_types,
- bytes_,
- text_,
- PY2,
- native_,
-)
-
from pyramid.path import DottedNameResolver as _DottedNameResolver
_marker = object()
+WIN = platform.system() == 'Windows'
+
+try: # pragma: no cover
+ import __pypy__
+
+ PYPY = True
+except BaseException: # pragma: no cover
+ __pypy__ = None
+ PYPY = False
+
class DottedNameResolver(_DottedNameResolver):
def __init__(
@@ -35,8 +27,40 @@ class DottedNameResolver(_DottedNameResolver):
_DottedNameResolver.__init__(self, package)
+def text_(s, encoding='latin-1', errors='strict'):
+ """ If ``s`` is an instance of ``bytes``, return
+ ``s.decode(encoding, errors)``, otherwise return ``s``"""
+ if isinstance(s, bytes):
+ return s.decode(encoding, errors)
+ return s
+
+
+def bytes_(s, encoding='latin-1', errors='strict'):
+ """ If ``s`` is an instance of ``str``, return
+ ``s.encode(encoding, errors)``, otherwise return ``s``"""
+ if isinstance(s, str):
+ return s.encode(encoding, errors)
+ return s
+
+
+def ascii_(s):
+ """
+ If ``s`` is an instance of ``str``, return
+ ``s.encode('ascii')``, otherwise return ``str(s, 'ascii', 'strict')``
+ """
+ if isinstance(s, str):
+ s = s.encode('ascii')
+ return str(s, 'ascii', 'strict')
+
+
+def is_nonstr_iter(v):
+ if isinstance(v, str):
+ return False
+ return hasattr(v, '__iter__')
+
+
def is_string_or_iterable(v):
- if isinstance(v, string_types):
+ if isinstance(v, str):
return True
if hasattr(v, '__iter__'):
return True
@@ -330,17 +354,14 @@ def object_description(object):
is a boolean, an integer, a list, a tuple, a set, or ``None``, a
(possibly shortened) string representation is returned.
"""
- if isinstance(object, string_types):
- return text_(object)
- if isinstance(object, integer_types):
- return text_(str(object))
+ if isinstance(object, str):
+ return object
+ if isinstance(object, int):
+ return str(object)
if isinstance(object, (bool, float, type(None))):
- return text_(str(object))
+ return str(object)
if isinstance(object, set):
- if PY2:
- return shortrepr(object, ')')
- else:
- return shortrepr(object, '}')
+ return shortrepr(object, '}')
if isinstance(object, tuple):
return shortrepr(object, ')')
if isinstance(object, list):
@@ -349,26 +370,25 @@ def object_description(object):
return shortrepr(object, '}')
module = inspect.getmodule(object)
if module is None:
- return text_('object %s' % str(object))
+ return 'object %s' % str(object)
modulename = module.__name__
if inspect.ismodule(object):
- return text_('module %s' % modulename)
+ return 'module %s' % modulename
if inspect.ismethod(object):
oself = getattr(object, '__self__', None)
- if oself is None: # pragma: no cover
- oself = getattr(object, 'im_self', None)
- return text_(
- 'method %s of class %s.%s'
- % (object.__name__, modulename, oself.__class__.__name__)
+ return 'method %s of class %s.%s' % (
+ object.__name__,
+ modulename,
+ oself.__class__.__name__,
)
if inspect.isclass(object):
dottedname = '%s.%s' % (modulename, object.__name__)
- return text_('class %s' % dottedname)
+ return 'class %s' % dottedname
if inspect.isfunction(object):
dottedname = '%s.%s' % (modulename, object.__name__)
- return text_('function %s' % dottedname)
- return text_('object %s' % str(object))
+ return 'function %s' % dottedname
+ return 'object %s' % str(object)
def shortrepr(object, closer):
@@ -499,11 +519,17 @@ class TopologicalSorter(object):
has_after.add(b)
if not self.req_before.issubset(has_before):
+ # avoid circular dependency
+ from pyramid.exceptions import ConfigurationError
+
raise ConfigurationError(
'Unsatisfied before dependencies: %s'
% (', '.join(sorted(self.req_before - has_before)))
)
if not self.req_after.issubset(has_after):
+ # avoid circular dependency
+ from pyramid.exceptions import ConfigurationError
+
raise ConfigurationError(
'Unsatisfied after dependencies: %s'
% (', '.join(sorted(self.req_after - has_after)))
@@ -524,6 +550,9 @@ class TopologicalSorter(object):
del graph[root]
if graph:
+ # avoid circular dependency
+ from pyramid.exceptions import CyclicDependencyError
+
# loop in input
cycledeps = {}
for k, v in graph.items():
@@ -545,8 +574,11 @@ def get_callable_name(name):
if it is not.
"""
try:
- return native_(name, 'ascii')
+ return ascii_(name)
except (UnicodeEncodeError, UnicodeDecodeError):
+ # avoid circular dependency
+ from pyramid.exceptions import ConfigurationError
+
msg = (
'`name="%s"` is invalid. `name` must be ascii because it is '
'used on __name__ of the method'
@@ -615,10 +647,7 @@ def takes_one_arg(callee, attr=None, argname=None):
if inspect.isroutine(callee):
fn = callee
elif inspect.isclass(callee):
- try:
- fn = callee.__init__
- except AttributeError:
- return False
+ fn = callee.__init__
ismethod = hasattr(fn, '__call__')
else:
try:
@@ -626,15 +655,11 @@ def takes_one_arg(callee, attr=None, argname=None):
except AttributeError:
return False
- try:
- argspec = getargspec(fn)
- except TypeError:
- return False
-
+ argspec = inspect.getfullargspec(fn)
args = argspec[0]
- if hasattr(fn, im_func) or ismethod:
- # it's an instance method (or unbound method on py2)
+ if hasattr(fn, '__func__') or ismethod:
+ # it's an instance method
if not args:
return False
args = args[1:]
@@ -660,7 +685,40 @@ def takes_one_arg(callee, attr=None, argname=None):
class SimpleSerializer(object):
def loads(self, bstruct):
- return native_(bstruct)
+ return text_(bstruct)
def dumps(self, appstruct):
return bytes_(appstruct)
+
+
+def is_bound_method(ob):
+ return inspect.ismethod(ob) and getattr(ob, '__self__', None) is not None
+
+
+def is_unbound_method(fn):
+ """
+ This consistently verifies that the callable is bound to a
+ class.
+ """
+ is_bound = is_bound_method(fn)
+
+ if not is_bound and inspect.isroutine(fn):
+ spec = inspect.getfullargspec(fn)
+ has_self = len(spec.args) > 0 and spec.args[0] == 'self'
+
+ if inspect.isfunction(fn) and has_self:
+ return True
+
+ return False
+
+
+def reraise(tp, value, tb=None):
+ try:
+ if value is None:
+ value = tp()
+ if value.__traceback__ is not tb:
+ raise value.with_traceback(tb)
+ raise value
+ finally:
+ value = None
+ tb = None
diff --git a/src/pyramid/view.py b/src/pyramid/view.py
index 9f58e72ae..944ad93ea 100644
--- a/src/pyramid/view.py
+++ b/src/pyramid/view.py
@@ -15,9 +15,6 @@ from pyramid.interfaces import (
IExceptionViewClassifier,
)
-from pyramid.compat import decode_path_info
-from pyramid.compat import reraise as reraise_
-
from pyramid.exceptions import ConfigurationError, PredicateMismatch
from pyramid.httpexceptions import (
@@ -29,6 +26,7 @@ from pyramid.httpexceptions import (
from pyramid.threadlocal import get_current_registry, manager
from pyramid.util import hide_attrs
+from pyramid.util import reraise as reraise_
_marker = object()
@@ -305,7 +303,7 @@ class AppendSlashNotFoundViewFactory(object):
self.redirect_class = redirect_class
def __call__(self, context, request):
- path = decode_path_info(request.environ['PATH_INFO'] or '/')
+ path = request.path_info
registry = request.registry
mapper = registry.queryUtility(IRoutesMapper)
if mapper is not None and not path.endswith('/'):
diff --git a/src/pyramid/viewderivers.py b/src/pyramid/viewderivers.py
index fbe0c252c..181cc9e5c 100644
--- a/src/pyramid/viewderivers.py
+++ b/src/pyramid/viewderivers.py
@@ -17,11 +17,14 @@ from pyramid.interfaces import (
IViewMapperFactory,
)
-from pyramid.compat import is_bound_method, is_unbound_method
-
from pyramid.exceptions import ConfigurationError
from pyramid.httpexceptions import HTTPForbidden
-from pyramid.util import object_description, takes_one_arg
+from pyramid.util import (
+ object_description,
+ takes_one_arg,
+ is_bound_method,
+ is_unbound_method,
+)
from pyramid.view import render_view_to_response
from pyramid import renderers
diff --git a/tests/pkgs/forbiddenapp/__init__.py b/tests/pkgs/forbiddenapp/__init__.py
index 9ebf62a9d..31ea4dd52 100644
--- a/tests/pkgs/forbiddenapp/__init__.py
+++ b/tests/pkgs/forbiddenapp/__init__.py
@@ -1,6 +1,6 @@
from webob import Response
from pyramid.httpexceptions import HTTPForbidden
-from pyramid.compat import bytes_
+from pyramid.util import bytes_
def x_view(request): # pragma: no cover
diff --git a/tests/pkgs/permbugapp/__init__.py b/tests/pkgs/permbugapp/__init__.py
index aedd405f8..72b5d9c17 100644
--- a/tests/pkgs/permbugapp/__init__.py
+++ b/tests/pkgs/permbugapp/__init__.py
@@ -1,4 +1,4 @@
-from pyramid.compat import escape
+from html import escape
from pyramid.security import view_execution_permitted
from pyramid.response import Response
diff --git a/tests/test_authentication.py b/tests/test_authentication.py
index fc3e60587..8671eba05 100644
--- a/tests/test_authentication.py
+++ b/tests/test_authentication.py
@@ -1,7 +1,8 @@
+from http.cookies import SimpleCookie
import unittest
import warnings
from pyramid import testing
-from pyramid.compat import text_, bytes_
+from pyramid.util import text_, bytes_
class TestCallbackAuthenticationPolicyDebugging(unittest.TestCase):
@@ -706,8 +707,6 @@ class TestAuthTktCookieHelper(unittest.TestCase):
return cookie
def _parseCookie(self, cookie):
- from pyramid.compat import SimpleCookie
-
cookies = SimpleCookie()
cookies.load(cookie)
return cookies.get('auth_tkt')
@@ -1272,18 +1271,6 @@ class TestAuthTktCookieHelper(unittest.TestCase):
self.assertEqual(val['userid'], '1')
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))
- values = self._parseHeaders(result)
- self.assertEqual(len(result), 3)
- val = self._cookieValue(values[0])
- self.assertEqual(val['userid'], '1')
- self.assertEqual(val['user_data'], 'userid_type:int')
-
def test_remember_unicode_userid(self):
import base64
diff --git a/tests/test_compat.py b/tests/test_compat.py
deleted file mode 100644
index 4a14caedf..000000000
--- a/tests/test_compat.py
+++ /dev/null
@@ -1,32 +0,0 @@
-import unittest
-from pyramid.compat import is_unbound_method
-
-
-class TestUnboundMethods(unittest.TestCase):
- def test_old_style_bound(self):
- self.assertFalse(is_unbound_method(OldStyle().run))
-
- def test_new_style_bound(self):
- self.assertFalse(is_unbound_method(NewStyle().run))
-
- def test_old_style_unbound(self):
- self.assertTrue(is_unbound_method(OldStyle.run))
-
- def test_new_style_unbound(self):
- self.assertTrue(is_unbound_method(NewStyle.run))
-
- def test_normal_func_unbound(self):
- def func(): # pragma: no cover
- return 'OK'
-
- self.assertFalse(is_unbound_method(func))
-
-
-class OldStyle:
- def run(self): # pragma: no cover
- return 'OK'
-
-
-class NewStyle(object):
- def run(self): # pragma: no cover
- return 'OK'
diff --git a/tests/test_config/test_adapters.py b/tests/test_config/test_adapters.py
index d871e8825..60a4f3090 100644
--- a/tests/test_config/test_adapters.py
+++ b/tests/test_config/test_adapters.py
@@ -1,6 +1,5 @@
import unittest
-from pyramid.compat import PY2
from . import IDummy
@@ -270,10 +269,7 @@ class AdaptersConfiguratorMixinTests(unittest.TestCase):
from pyramid.interfaces import IResponse
config = self._makeOne(autocommit=True)
- if PY2:
- str_name = '__builtin__.str'
- else:
- str_name = 'builtins.str'
+ str_name = 'builtins.str'
config.add_response_adapter('pyramid.response.Response', str_name)
result = config.registry.queryAdapter('foo', IResponse)
self.assertTrue(result.body, b'foo')
diff --git a/tests/test_config/test_factories.py b/tests/test_config/test_factories.py
index c03d3f68b..bbc38b6cd 100644
--- a/tests/test_config/test_factories.py
+++ b/tests/test_config/test_factories.py
@@ -160,8 +160,7 @@ class TestFactoriesMixin(unittest.TestCase):
config = self._makeOne(autocommit=True)
self.assertRaises(AttributeError, config.add_request_method)
- def test_add_request_method_with_text_type_name(self):
- from pyramid.compat import text_, PY2
+ def test_add_request_method_with_text_name(self):
from pyramid.exceptions import ConfigurationError
config = self._makeOne(autocommit=True)
@@ -170,11 +169,7 @@ class TestFactoriesMixin(unittest.TestCase):
pass
def get_bad_name():
- if PY2:
- name = text_(b'La Pe\xc3\xb1a', 'utf-8')
- else:
- name = b'La Pe\xc3\xb1a'
-
+ name = b'La Pe\xc3\xb1a'
config.add_request_method(boomshaka, name=name)
self.assertRaises(ConfigurationError, get_bad_name)
diff --git a/tests/test_config/test_init.py b/tests/test_config/test_init.py
index 811672fb3..ce2b042ec 100644
--- a/tests/test_config/test_init.py
+++ b/tests/test_config/test_init.py
@@ -1,9 +1,6 @@
import os
import unittest
-from pyramid.compat import im_func
-from pyramid.testing import skip_on
-
from . import dummy_tween_factory
from . import dummy_include
from . import dummy_extend
@@ -1157,7 +1154,6 @@ test_config.dummy_include2"""
"@view_config(name='two', renderer='string')" in which
)
- @skip_on('py3')
def test_hook_zca(self):
from zope.component import getSiteManager
@@ -1173,7 +1169,6 @@ test_config.dummy_include2"""
finally:
getSiteManager.reset()
- @skip_on('py3')
def test_unhook_zca(self):
from zope.component import getSiteManager
@@ -1208,7 +1203,7 @@ test_config.dummy_include2"""
directives = {'foo': (foo, True)}
config.registry._directives = directives
foo_meth = config.foo
- self.assertTrue(getattr(foo_meth, im_func).__docobj__ is foo)
+ self.assertTrue(getattr(foo_meth, '__func__').__docobj__ is foo)
def test___getattr__matches_no_action_wrap(self):
config = self._makeOne()
@@ -1219,7 +1214,7 @@ test_config.dummy_include2"""
directives = {'foo': (foo, False)}
config.registry._directives = directives
foo_meth = config.foo
- self.assertTrue(getattr(foo_meth, im_func) is foo)
+ self.assertTrue(getattr(foo_meth, '__func__') is foo)
class TestConfigurator_add_directive(unittest.TestCase):
diff --git a/tests/test_config/test_predicates.py b/tests/test_config/test_predicates.py
index 079652b39..c27b41639 100644
--- a/tests/test_config/test_predicates.py
+++ b/tests/test_config/test_predicates.py
@@ -1,6 +1,6 @@
import unittest
-from pyramid.compat import text_
+from pyramid.util import text_
class TestPredicateList(unittest.TestCase):
diff --git a/tests/test_config/test_routes.py b/tests/test_config/test_routes.py
index e6540c343..4ff67cf66 100644
--- a/tests/test_config/test_routes.py
+++ b/tests/test_config/test_routes.py
@@ -2,7 +2,7 @@ import unittest
from . import dummyfactory
from . import DummyContext
-from pyramid.compat import text_
+from pyramid.util import text_
class RoutesConfiguratorMixinTests(unittest.TestCase):
diff --git a/tests/test_config/test_testing.py b/tests/test_config/test_testing.py
index ede31e1b6..0fb73d268 100644
--- a/tests/test_config/test_testing.py
+++ b/tests/test_config/test_testing.py
@@ -1,8 +1,8 @@
import unittest
from zope.interface import implementer
-from pyramid.compat import text_
from pyramid.security import AuthenticationAPIMixin, AuthorizationAPIMixin
+from pyramid.util import text_
from . import IDummy
@@ -69,23 +69,27 @@ class TestingConfiguratorMixinTests(unittest.TestCase):
config = self._makeOne(autocommit=True)
config.testing_resources(resources)
adapter = config.registry.getAdapter(None, ITraverser)
- result = adapter(DummyRequest({'PATH_INFO': '/ob1'}))
+ request = DummyRequest()
+ request.path_info = '/ob1'
+ result = adapter(request)
self.assertEqual(result['context'], ob1)
self.assertEqual(result['view_name'], '')
self.assertEqual(result['subpath'], ())
self.assertEqual(result['traversed'], (text_('ob1'),))
self.assertEqual(result['virtual_root'], ob1)
self.assertEqual(result['virtual_root_path'], ())
- result = adapter(DummyRequest({'PATH_INFO': '/ob2'}))
+ request = DummyRequest()
+ request.path_info = '/ob2'
+ result = adapter(request)
self.assertEqual(result['context'], ob2)
self.assertEqual(result['view_name'], '')
self.assertEqual(result['subpath'], ())
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'})
- )
+ request = DummyRequest()
+ request.path_info = '/ob3'
+ self.assertRaises(KeyError, adapter, request)
try:
config.begin()
self.assertEqual(find_resource(None, '/ob1'), ob1)
diff --git a/tests/test_config/test_views.py b/tests/test_config/test_views.py
index b72b9b36a..685b81a0f 100644
--- a/tests/test_config/test_views.py
+++ b/tests/test_config/test_views.py
@@ -3,11 +3,11 @@ import unittest
from zope.interface import implementer
from pyramid import testing
-from pyramid.compat import im_func, text_
from pyramid.exceptions import ConfigurationError
from pyramid.exceptions import ConfigurationExecutionError
from pyramid.exceptions import ConfigurationConflictError
from pyramid.interfaces import IResponse, IRequest, IMultiView
+from pyramid.util import text_
from . import IDummy
from . import dummy_view
@@ -1357,6 +1357,7 @@ class TestViewsConfigurationMixin(unittest.TestCase):
request_method='POST',
)
request = self._makeRequest(config)
+ request.path_info = '/'
request.method = 'POST'
request.params = {}
router = Router(config.registry)
@@ -1412,6 +1413,7 @@ class TestViewsConfigurationMixin(unittest.TestCase):
request_method='POST',
)
request = self._makeRequest(config)
+ request.path_info = '/'
request.method = 'POST'
request.params = {}
router = Router(config.registry)
@@ -2722,7 +2724,7 @@ class TestViewsConfigurationMixin(unittest.TestCase):
view, renderer=null_renderer, append_slash=True
)
request = self._makeRequest(config)
- request.environ['PATH_INFO'] = '/foo'
+ request.path_info = '/foo'
request.query_string = 'a=1&b=2'
request.path = '/scriptname/foo'
view = self._getViewCallable(
@@ -2751,7 +2753,7 @@ class TestViewsConfigurationMixin(unittest.TestCase):
view, renderer=null_renderer, append_slash=HTTPMovedPermanently
)
request = self._makeRequest(config)
- request.environ['PATH_INFO'] = '/foo'
+ request.path_info = '/foo'
request.query_string = 'a=1&b=2'
request.path = '/scriptname/foo'
view = self._getViewCallable(
@@ -2795,15 +2797,6 @@ class TestViewsConfigurationMixin(unittest.TestCase):
request = self._makeRequest(config)
self.assertRaises(PredicateMismatch, wrapper, context, request)
- # Since Python 3 has to be all cool and fancy and different...
- def _assertBody(self, response, value):
- from pyramid.compat import text_type
-
- if isinstance(value, text_type): # pragma: no cover
- self.assertEqual(response.text, value)
- else: # pragma: no cover
- self.assertEqual(response.body, value)
-
def test_add_notfound_view_with_renderer(self):
from zope.interface import implementedBy
from pyramid.interfaces import IRequest
@@ -2820,7 +2813,7 @@ class TestViewsConfigurationMixin(unittest.TestCase):
request_iface=IRequest,
)
result = view(None, request)
- self._assertBody(result, '{}')
+ self.assertEqual(result.text, '{}')
def test_add_forbidden_view_with_renderer(self):
from zope.interface import implementedBy
@@ -2838,7 +2831,7 @@ class TestViewsConfigurationMixin(unittest.TestCase):
request_iface=IRequest,
)
result = view(None, request)
- self._assertBody(result, '{}')
+ self.assertEqual(result.text, '{}')
def test_set_view_mapper(self):
from pyramid.interfaces import IViewMapperFactory
@@ -3732,16 +3725,16 @@ class Test_preserve_view_attrs(unittest.TestCase):
self.assertTrue(view1.__module__ is view2.__module__)
self.assertTrue(view1.__name__ is view2.__name__)
self.assertTrue(
- getattr(view1.__call_permissive__, im_func)
- is getattr(view2.__call_permissive__, im_func)
+ getattr(view1.__call_permissive__, '__func__')
+ is getattr(view2.__call_permissive__, '__func__')
)
self.assertTrue(
- getattr(view1.__permitted__, im_func)
- is getattr(view2.__permitted__, im_func)
+ getattr(view1.__permitted__, '__func__')
+ is getattr(view2.__permitted__, '__func__')
)
self.assertTrue(
- getattr(view1.__predicated__, im_func)
- is getattr(view2.__predicated__, im_func)
+ getattr(view1.__predicated__, '__func__')
+ is getattr(view2.__predicated__, '__func__')
)
diff --git a/tests/test_encode.py b/tests/test_encode.py
index f70050cac..4df08d509 100644
--- a/tests/test_encode.py
+++ b/tests/test_encode.py
@@ -1,5 +1,5 @@
import unittest
-from pyramid.compat import text_, native_
+from pyramid.util import text_
class UrlEncodeTests(unittest.TestCase):
@@ -74,7 +74,7 @@ class URLQuoteTests(unittest.TestCase):
self.assertEqual(result, 'La%2FPe%C3%B1a')
def test_it_native(self):
- la = native_(b'La/Pe\xc3\xb1a', 'utf-8')
+ la = text_(b'La/Pe\xc3\xb1a', 'utf-8')
result = self._callFUT(la)
self.assertEqual(result, 'La%2FPe%C3%B1a')
diff --git a/tests/test_httpexceptions.py b/tests/test_httpexceptions.py
index 4c13e096d..5decfc39c 100644
--- a/tests/test_httpexceptions.py
+++ b/tests/test_httpexceptions.py
@@ -1,6 +1,6 @@
import unittest
-from pyramid.compat import bytes_, string_types, text_
+from pyramid.util import bytes_, text_
class Test_exception_response(unittest.TestCase):
@@ -67,6 +67,12 @@ class Test__no_escape(unittest.TestCase):
def test_not_basestring(self):
self.assertEqual(self._callFUT(42), '42')
+ def test_bytes(self):
+ self.assertEqual(
+ self._callFUT(b'/La Pe\xc3\xb1a/{x}'),
+ b'/La Pe\xc3\xb1a/{x}'.decode('utf-8'),
+ )
+
def test_unicode(self):
class DummyUnicodeObject(object):
def __unicode__(self):
@@ -406,7 +412,7 @@ class TestHTTPException(unittest.TestCase):
def test_allow_detail_non_str(self):
exc = self._makeOne(detail={'error': 'This is a test'})
- self.assertIsInstance(exc.__str__(), string_types)
+ self.assertIsInstance(exc.__str__(), str)
class TestRenderAllExceptionsWithoutArguments(unittest.TestCase):
diff --git a/tests/test_integration.py b/tests/test_integration.py
index d57a7cf6e..d1f65274b 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -4,14 +4,15 @@ import gc
import locale
import os
import unittest
+from urllib.parse import quote
from webtest import TestApp
from zope.interface import Interface
-from pyramid.wsgi import wsgiapp
-from pyramid.view import view_config
from pyramid.static import static_view
from pyramid.testing import skip_on
-from pyramid.compat import text_, url_quote
+from pyramid.util import text_
+from pyramid.view import view_config
+from pyramid.wsgi import wsgiapp
from .pkgs.exceptionviewapp.models import AnException, NotAnException
@@ -108,7 +109,7 @@ class StaticAppBase(IntegrationBase):
os.makedirs(pathdir)
with open(path, 'wb') as fp:
fp.write(body)
- url = url_quote('/static/héhé/index.html')
+ url = quote('/static/héhé/index.html')
res = self.testapp.get(url, status=200)
self.assertEqual(res.body, body)
finally:
@@ -123,7 +124,7 @@ class StaticAppBase(IntegrationBase):
with open(path, 'wb') as fp:
fp.write(body)
try:
- url = url_quote('/static/héhé.html')
+ url = quote('/static/héhé.html')
res = self.testapp.get(url, status=200)
self.assertEqual(res.body, body)
finally:
diff --git a/tests/test_path.py b/tests/test_path.py
index 626bb1139..da7cd64e1 100644
--- a/tests/test_path.py
+++ b/tests/test_path.py
@@ -1,6 +1,5 @@
import unittest
import os
-from pyramid.compat import PY2
here = os.path.abspath(os.path.dirname(__file__))
@@ -429,10 +428,7 @@ class TestDottedNameResolver(unittest.TestCase):
def test_zope_dottedname_style_resolve_builtin(self):
typ = self._makeOne()
- if PY2:
- result = typ._zope_dottedname_style('__builtin__.str', None)
- else:
- result = typ._zope_dottedname_style('builtins.str', None)
+ result = typ._zope_dottedname_style('builtins.str', None)
self.assertEqual(result, str)
def test_zope_dottedname_style_resolve_absolute(self):
diff --git a/tests/test_predicates.py b/tests/test_predicates.py
index c072b4229..a99651a8f 100644
--- a/tests/test_predicates.py
+++ b/tests/test_predicates.py
@@ -2,7 +2,7 @@ import unittest
from pyramid import testing
-from pyramid.compat import text_
+from pyramid.util import text_
class TestXHRPredicate(unittest.TestCase):
diff --git a/tests/test_renderers.py b/tests/test_renderers.py
index 0eacfa996..db8b3b4f2 100644
--- a/tests/test_renderers.py
+++ b/tests/test_renderers.py
@@ -1,8 +1,8 @@
import unittest
-from pyramid.testing import cleanUp
from pyramid import testing
-from pyramid.compat import text_
+from pyramid.testing import cleanUp
+from pyramid.util import text_
class TestJSON(unittest.TestCase):
@@ -774,7 +774,7 @@ class DummyResponse:
body = b''
# compat for renderer that will set unicode on py3
- def _set_text(self, val): # pragma: no cover
+ def _set_text(self, val):
self.body = val.encode('utf8')
text = property(fset=_set_text)
diff --git a/tests/test_request.py b/tests/test_request.py
index dcac501aa..484d86e01 100644
--- a/tests/test_request.py
+++ b/tests/test_request.py
@@ -1,8 +1,8 @@
import unittest
from pyramid import testing
-from pyramid.compat import PY2, text_, bytes_, native_
from pyramid.security import AuthenticationAPIMixin, AuthorizationAPIMixin
+from pyramid.util import text_, bytes_
class TestRequest(unittest.TestCase):
@@ -352,10 +352,7 @@ class TestRequest(unittest.TestCase):
inp = text_(
b'/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf', 'utf-8'
)
- if PY2:
- body = json.dumps({'a': inp}).decode('utf-8').encode('utf-16')
- else:
- body = bytes(json.dumps({'a': inp}), 'utf-16')
+ body = bytes(json.dumps({'a': inp}), 'utf-16')
request.body = body
request.content_type = 'application/json; charset=utf-16'
self.assertEqual(request.json_body, {'a': inp})
@@ -481,7 +478,7 @@ 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):
- encoded = native_(text_(b'La Pe\xc3\xb1a'))
+ encoded = text_(b'La Pe\xc3\xb1a')
decoded = text_(bytes_(encoded), 'utf-8')
request = DummyRequest(
{'PATH_INFO': '/' + encoded, 'SCRIPT_NAME': '/' + encoded}
diff --git a/tests/test_response.py b/tests/test_response.py
index 5231e47f0..18d4335ad 100644
--- a/tests/test_response.py
+++ b/tests/test_response.py
@@ -73,9 +73,9 @@ class TestFileResponse(unittest.TestCase):
# function returns Unicode for the content_type, unlike any previous
# version of Python. See https://github.com/Pylons/pyramid/issues/1360
# for more information.
- from pyramid.compat import text_
import mimetypes as old_mimetypes
from pyramid import response
+ from pyramid.util import text_
class FakeMimetypesModule(object):
def guess_type(self, *arg, **kw):
diff --git a/tests/test_scripts/dummy.py b/tests/test_scripts/dummy.py
index 8e340f645..bb3475d39 100644
--- a/tests/test_scripts/dummy.py
+++ b/tests/test_scripts/dummy.py
@@ -81,8 +81,11 @@ class DummyRoute(object):
class DummyRequest:
application_url = 'http://example.com:5432'
script_name = ''
+ path_info = '/'
- def __init__(self, environ):
+ def __init__(self, environ=None):
+ if environ is None:
+ environ = {}
self.environ = environ
self.matchdict = {}
diff --git a/tests/test_scripts/test_prequest.py b/tests/test_scripts/test_prequest.py
index 1521172bc..aadde719a 100644
--- a/tests/test_scripts/test_prequest.py
+++ b/tests/test_scripts/test_prequest.py
@@ -1,3 +1,4 @@
+from io import StringIO
import unittest
from . import dummy
@@ -134,13 +135,11 @@ class TestPRequestCommand(unittest.TestCase):
self.assertEqual(self._out, ['abc'])
def test_command_method_post(self):
- from pyramid.compat import NativeIO
-
command = self._makeOne(
['', '--method=POST', 'development.ini', '/'],
[('Content-Type', 'text/html; charset=UTF-8')],
)
- stdin = NativeIO()
+ stdin = StringIO()
command.stdin = stdin
command.run()
self.assertEqual(self._environ['REQUEST_METHOD'], 'POST')
@@ -150,13 +149,11 @@ class TestPRequestCommand(unittest.TestCase):
self.assertEqual(self._out, ['abc'])
def test_command_method_put(self):
- from pyramid.compat import NativeIO
-
command = self._makeOne(
['', '--method=PUT', 'development.ini', '/'],
[('Content-Type', 'text/html; charset=UTF-8')],
)
- stdin = NativeIO()
+ stdin = StringIO()
command.stdin = stdin
command.run()
self.assertEqual(self._environ['REQUEST_METHOD'], 'PUT')
@@ -166,13 +163,11 @@ class TestPRequestCommand(unittest.TestCase):
self.assertEqual(self._out, ['abc'])
def test_command_method_patch(self):
- from pyramid.compat import NativeIO
-
command = self._makeOne(
['', '--method=PATCH', 'development.ini', '/'],
[('Content-Type', 'text/html; charset=UTF-8')],
)
- stdin = NativeIO()
+ stdin = StringIO()
command.stdin = stdin
command.run()
self.assertEqual(self._environ['REQUEST_METHOD'], 'PATCH')
@@ -182,13 +177,11 @@ class TestPRequestCommand(unittest.TestCase):
self.assertEqual(self._out, ['abc'])
def test_command_method_propfind(self):
- from pyramid.compat import NativeIO
-
command = self._makeOne(
['', '--method=PROPFIND', 'development.ini', '/'],
[('Content-Type', 'text/html; charset=UTF-8')],
)
- stdin = NativeIO()
+ stdin = StringIO()
command.stdin = stdin
command.run()
self.assertEqual(self._environ['REQUEST_METHOD'], 'PROPFIND')
@@ -196,13 +189,11 @@ class TestPRequestCommand(unittest.TestCase):
self.assertEqual(self._out, ['abc'])
def test_command_method_options(self):
- from pyramid.compat import NativeIO
-
command = self._makeOne(
['', '--method=OPTIONS', 'development.ini', '/'],
[('Content-Type', 'text/html; charset=UTF-8')],
)
- stdin = NativeIO()
+ stdin = StringIO()
command.stdin = stdin
command.run()
self.assertEqual(self._environ['REQUEST_METHOD'], 'OPTIONS')
diff --git a/tests/test_scripts/test_pserve.py b/tests/test_scripts/test_pserve.py
index b85f4ddb7..a573f2e5b 100644
--- a/tests/test_scripts/test_pserve.py
+++ b/tests/test_scripts/test_pserve.py
@@ -1,3 +1,4 @@
+from io import StringIO
import os
import unittest
from . import dummy
@@ -8,9 +9,7 @@ here = os.path.abspath(os.path.dirname(__file__))
class TestPServeCommand(unittest.TestCase):
def setUp(self):
- from pyramid.compat import NativeIO
-
- self.out_ = NativeIO()
+ self.out_ = StringIO()
def out(self, msg):
self.out_.write(msg)
diff --git a/tests/test_scripts/test_pviews.py b/tests/test_scripts/test_pviews.py
index 0b26a9cf3..c8d29113f 100644
--- a/tests/test_scripts/test_pviews.py
+++ b/tests/test_scripts/test_pviews.py
@@ -53,7 +53,8 @@ class TestPViewsCommand(unittest.TestCase):
class View1(object):
pass
- request = dummy.DummyRequest({'PATH_INFO': '/a'})
+ request = dummy.DummyRequest()
+ request.path_info = '/a'
root = DefaultRootFactory(request)
root_iface = providedBy(root)
registry.registerAdapter(
@@ -78,7 +79,8 @@ class TestPViewsCommand(unittest.TestCase):
def view1(): # pragma: no cover
pass
- request = dummy.DummyRequest({'PATH_INFO': '/a'})
+ request = dummy.DummyRequest()
+ request.path_info = '/a'
root = DefaultRootFactory(request)
root_iface = providedBy(root)
registry.registerAdapter(
@@ -105,7 +107,8 @@ class TestPViewsCommand(unittest.TestCase):
class View1(object):
pass
- request = dummy.DummyRequest({'PATH_INFO': '/a'})
+ request = dummy.DummyRequest()
+ request.path_info = '/a'
root = DefaultRootFactory(request)
root_iface = providedBy(root)
view = View1()
@@ -267,7 +270,8 @@ class TestPViewsCommand(unittest.TestCase):
dummy.DummyRoute('b', '/a', factory=factory, matchdict={}),
]
mapper = dummy.DummyMapper(*routes)
- request = dummy.DummyRequest({'PATH_INFO': '/a'})
+ request = dummy.DummyRequest()
+ request.path_info = '/a'
result = command._find_multi_routes(mapper, request)
self.assertEqual(
result,
@@ -288,7 +292,8 @@ class TestPViewsCommand(unittest.TestCase):
dummy.DummyRoute('b', '/a', factory=factory, matchdict={}),
]
mapper = dummy.DummyMapper(*routes)
- request = dummy.DummyRequest({'PATH_INFO': '/a'})
+ request = dummy.DummyRequest()
+ request.path_info = '/a'
result = command._find_multi_routes(mapper, request)
self.assertEqual(result, [{'match': {}, 'route': routes[1]}])
@@ -303,7 +308,8 @@ class TestPViewsCommand(unittest.TestCase):
dummy.DummyRoute('b', '/a', factory=factory),
]
mapper = dummy.DummyMapper(*routes)
- request = dummy.DummyRequest({'PATH_INFO': '/a'})
+ request = dummy.DummyRequest()
+ request.path_info = '/a'
result = command._find_multi_routes(mapper, request)
self.assertEqual(result, [])
diff --git a/tests/test_session.py b/tests/test_session.py
index 5e2a1ff55..8e5e82bb2 100644
--- a/tests/test_session.py
+++ b/tests/test_session.py
@@ -1,8 +1,8 @@
import base64
import json
+import pickle
import unittest
from pyramid import testing
-from pyramid.compat import pickle
class SharedCookieSessionTests(object):
@@ -607,13 +607,7 @@ class DummySerializer(object):
return base64.b64encode(json.dumps(value).encode('utf-8'))
def loads(self, value):
- try:
- return json.loads(base64.b64decode(value).decode('utf-8'))
-
- # base64.b64decode raises a TypeError on py2 instead of a ValueError
- # and a ValueError is required for the session to handle it properly
- except TypeError:
- raise ValueError
+ return json.loads(base64.b64decode(value).decode('utf-8'))
class DummySessionFactory(dict):
diff --git a/tests/test_traversal.py b/tests/test_traversal.py
index 61e480cbc..de712a6e8 100644
--- a/tests/test_traversal.py
+++ b/tests/test_traversal.py
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
import unittest
+from urllib.parse import quote
from pyramid.testing import cleanUp
-from pyramid.compat import text_, native_, text_type, url_quote, PY2
+from pyramid.util import text_
class TraversalPathTests(unittest.TestCase):
@@ -14,7 +15,7 @@ class TraversalPathTests(unittest.TestCase):
def test_utf8(self):
la = b'La Pe\xc3\xb1a'
- encoded = url_quote(la)
+ encoded = quote(la)
decoded = text_(la, 'utf-8')
path = '/'.join([encoded, encoded])
result = self._callFUT(path)
@@ -24,7 +25,7 @@ class TraversalPathTests(unittest.TestCase):
from pyramid.exceptions import URLDecodeError
la = text_(b'La Pe\xc3\xb1a', 'utf-8').encode('utf-16')
- encoded = url_quote(la)
+ encoded = quote(la)
path = '/'.join([encoded, encoded])
self.assertRaises(URLDecodeError, self._callFUT, path)
@@ -71,8 +72,8 @@ class TraversalPathInfoTests(unittest.TestCase):
def test_segments_are_unicode(self):
result = self._callFUT('/foo/bar')
- self.assertEqual(type(result[0]), text_type)
- self.assertEqual(type(result[1]), text_type)
+ self.assertEqual(type(result[0]), str)
+ self.assertEqual(type(result[1]), str)
def test_same_value_returned_if_cached(self):
result1 = self._callFUT('/foo/bar')
@@ -86,15 +87,14 @@ class TraversalPathInfoTests(unittest.TestCase):
def test_highorder(self):
la = b'La Pe\xc3\xb1a'
- latin1 = native_(la)
+ latin1 = text_(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)
+ notlatin1 = text_(b'La Pe\xc3\xb1a', 'utf-8')
self.assertRaises(URLDecodeError, self._callFUT, notlatin1)
@@ -346,10 +346,7 @@ class ResourceTreeTraverserTests(unittest.TestCase):
foo = DummyContext(bar, path)
root = DummyContext(foo, 'root')
policy = self._makeOne(root)
- if PY2:
- vhm_root = b'/Qu\xc3\xa9bec'
- else:
- vhm_root = b'/Qu\xc3\xa9bec'.decode('latin-1')
+ vhm_root = b'/Qu\xc3\xa9bec'.decode('latin-1')
environ = self._getEnviron(HTTP_X_VHM_ROOT=vhm_root)
request = DummyRequest(environ, path_info=text_('/bar'))
result = policy(request)
@@ -873,15 +870,6 @@ class QuotePathSegmentTests(unittest.TestCase):
result = self._callFUT(s)
self.assertEqual(result, '12345')
- def test_long(self):
- from pyramid.compat import long
- import sys
-
- s = long(sys.maxsize + 1)
- result = self._callFUT(s)
- expected = str(s)
- self.assertEqual(result, expected)
-
def test_other(self):
class Foo(object):
def __str__(self):
diff --git a/tests/test_url.py b/tests/test_url.py
index 94a0a61c9..648f48d53 100644
--- a/tests/test_url.py
+++ b/tests/test_url.py
@@ -3,7 +3,7 @@ import unittest
from pyramid import testing
-from pyramid.compat import text_, WIN
+from pyramid.util import WIN, text_
class TestURLMethodsMixin(unittest.TestCase):
@@ -25,6 +25,7 @@ class TestURLMethodsMixin(unittest.TestCase):
def __init__(self, environ):
self.environ = environ
+ self.scheme = environ.get('wsgi.url_scheme', 'http')
request = Request(environ)
request.registry = self.config.registry
diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py
index 772250e89..5d77042ae 100644
--- a/tests/test_urldispatch.py
+++ b/tests/test_urldispatch.py
@@ -1,6 +1,6 @@
import unittest
from pyramid import testing
-from pyramid.compat import text_, PY2
+from pyramid.util import text_
class TestRoute(unittest.TestCase):
@@ -59,9 +59,7 @@ class RoutesMapperTests(unittest.TestCase):
def _getRequest(self, **kw):
from pyramid.threadlocal import get_current_registry
- environ = {'SERVER_NAME': 'localhost', 'wsgi.url_scheme': 'http'}
- environ.update(kw)
- request = DummyRequest(environ)
+ request = DummyRequest(**kw)
reg = get_current_registry()
request.registry = reg
return request
@@ -83,7 +81,7 @@ class RoutesMapperTests(unittest.TestCase):
def test_no_route_matches(self):
mapper = self._makeOne()
- request = self._getRequest(PATH_INFO='/')
+ request = self._getRequest(path_info='/')
result = mapper(request)
self.assertEqual(result['match'], None)
self.assertEqual(result['route'], None)
@@ -130,19 +128,39 @@ class RoutesMapperTests(unittest.TestCase):
def test___call__pathinfo_cant_be_decoded(self):
from pyramid.exceptions import URLDecodeError
+ from pyramid.threadlocal import get_current_registry
+
+ class DummyRequest:
+ @property
+ def path_info(self):
+ return b'\xff\xfe\xe6\x00'.decode('utf-8')
mapper = self._makeOne()
- if PY2:
- path_info = b'\xff\xfe\xe6\x00'
- else:
- path_info = b'\xff\xfe\xe6\x00'.decode('latin-1')
- request = self._getRequest(PATH_INFO=path_info)
+ request = DummyRequest()
+ request.registry = get_current_registry()
self.assertRaises(URLDecodeError, mapper, request)
+ def test___call__pathinfo_KeyError(self):
+ from pyramid.threadlocal import get_current_registry
+
+ class DummyRequest:
+ @property
+ def path_info(self):
+ # if the PATH_INFO is missing from the environ
+ raise KeyError
+
+ mapper = self._makeOne()
+ mapper.connect('root', '')
+ request = DummyRequest()
+ request.registry = get_current_registry()
+ result = mapper(request)
+ self.assertEqual(result['route'], mapper.routes['root'])
+ self.assertEqual(result['match'], {})
+
def test___call__route_matches(self):
mapper = self._makeOne()
mapper.connect('foo', 'archives/:action/:article')
- request = self._getRequest(PATH_INFO='/archives/action1/article1')
+ request = self._getRequest(path_info='/archives/action1/article1')
result = mapper(request)
self.assertEqual(result['route'], mapper.routes['foo'])
self.assertEqual(result['match']['action'], 'action1')
@@ -153,7 +171,7 @@ class RoutesMapperTests(unittest.TestCase):
mapper.connect(
'foo', 'archives/:action/:article', predicates=[lambda *arg: True]
)
- request = self._getRequest(PATH_INFO='/archives/action1/article1')
+ request = self._getRequest(path_info='/archives/action1/article1')
result = mapper(request)
self.assertEqual(result['route'], mapper.routes['foo'])
self.assertEqual(result['match']['action'], 'action1')
@@ -167,7 +185,7 @@ class RoutesMapperTests(unittest.TestCase):
predicates=[lambda *arg: True, lambda *arg: False],
)
mapper.connect('bar', 'archives/:action/:article')
- request = self._getRequest(PATH_INFO='/archives/action1/article1')
+ request = self._getRequest(path_info='/archives/action1/article1')
result = mapper(request)
self.assertEqual(result['route'], mapper.routes['bar'])
self.assertEqual(result['match']['action'], 'action1')
@@ -182,7 +200,7 @@ class RoutesMapperTests(unittest.TestCase):
return True
mapper.connect('foo', 'archives/:action/article1', predicates=[pred])
- request = self._getRequest(PATH_INFO='/archives/action1/article1')
+ request = self._getRequest(path_info='/archives/action1/article1')
mapper(request)
def test_cc_bug(self):
@@ -194,13 +212,13 @@ class RoutesMapperTests(unittest.TestCase):
'juri', 'licenses/:license_code/:license_version/:jurisdiction'
)
- request = self._getRequest(PATH_INFO='/licenses/1/v2/rdf')
+ request = self._getRequest(path_info='/licenses/1/v2/rdf')
result = mapper(request)
self.assertEqual(result['route'], mapper.routes['rdf'])
self.assertEqual(result['match']['license_code'], '1')
self.assertEqual(result['match']['license_version'], 'v2')
- request = self._getRequest(PATH_INFO='/licenses/1/v2/usa')
+ request = self._getRequest(path_info='/licenses/1/v2/usa')
result = mapper(request)
self.assertEqual(result['route'], mapper.routes['juri'])
self.assertEqual(result['match']['license_code'], '1')
@@ -210,7 +228,7 @@ class RoutesMapperTests(unittest.TestCase):
def test___call__root_route_matches(self):
mapper = self._makeOne()
mapper.connect('root', '')
- request = self._getRequest(PATH_INFO='/')
+ request = self._getRequest(path_info='/')
result = mapper(request)
self.assertEqual(result['route'], mapper.routes['root'])
self.assertEqual(result['match'], {})
@@ -218,7 +236,7 @@ class RoutesMapperTests(unittest.TestCase):
def test___call__root_route_matches2(self):
mapper = self._makeOne()
mapper.connect('root', '/')
- request = self._getRequest(PATH_INFO='/')
+ request = self._getRequest(path_info='/')
result = mapper(request)
self.assertEqual(result['route'], mapper.routes['root'])
self.assertEqual(result['match'], {})
@@ -226,7 +244,7 @@ class RoutesMapperTests(unittest.TestCase):
def test___call__root_route_when_path_info_empty(self):
mapper = self._makeOne()
mapper.connect('root', '/')
- request = self._getRequest(PATH_INFO='')
+ request = self._getRequest(path_info='')
result = mapper(request)
self.assertEqual(result['route'], mapper.routes['root'])
self.assertEqual(result['match'], {})
@@ -234,7 +252,7 @@ class RoutesMapperTests(unittest.TestCase):
def test___call__root_route_when_path_info_notempty(self):
mapper = self._makeOne()
mapper.connect('root', '/')
- request = self._getRequest(PATH_INFO='/')
+ request = self._getRequest(path_info='/')
result = mapper(request)
self.assertEqual(result['route'], mapper.routes['root'])
self.assertEqual(result['match'], {})
@@ -242,7 +260,7 @@ class RoutesMapperTests(unittest.TestCase):
def test___call__no_path_info(self):
mapper = self._makeOne()
mapper.connect('root', '/')
- request = self._getRequest()
+ request = self._getRequest(path_info='')
result = mapper(request)
self.assertEqual(result['route'], mapper.routes['root'])
self.assertEqual(result['match'], {})
@@ -646,8 +664,10 @@ class DummyContext(object):
class DummyRequest(object):
- def __init__(self, environ):
- self.environ = environ
+ scheme = 'http'
+
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
class DummyRoute(object):
diff --git a/tests/test_util.py b/tests/test_util.py
index a36655f6f..0f313955b 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -1,5 +1,6 @@
+import sys
import unittest
-from pyramid.compat import PY2, text_, bytes_
+from pyramid.util import text_, bytes_
class Test_InstancePropertyHelper(unittest.TestCase):
@@ -170,14 +171,10 @@ class Test_InstancePropertyHelper(unittest.TestCase):
self.assertEqual(2, foo.y)
def test_make_property_unicode(self):
- from pyramid.compat import text_
from pyramid.exceptions import ConfigurationError
cls = self._getTargetClass()
- if PY2:
- name = text_(b'La Pe\xc3\xb1a', 'utf-8')
- else:
- name = b'La Pe\xc3\xb1a'
+ name = b'La Pe\xc3\xb1a'
def make_bad_name():
cls.make_property(lambda x: 1, name=name, reify=True)
@@ -498,10 +495,7 @@ class Test_object_description(unittest.TestCase):
self.assertEqual(self._callFUT(('a', 'b')), "('a', 'b')")
def test_set(self):
- if PY2:
- self.assertEqual(self._callFUT(set(['a'])), "set(['a'])")
- else:
- self.assertEqual(self._callFUT(set(['a'])), "{'a'}")
+ self.assertEqual(self._callFUT(set(['a'])), "{'a'}")
def test_list(self):
self.assertEqual(self._callFUT(['a']), "['a']")
@@ -839,31 +833,26 @@ class TestSentinel(unittest.TestCase):
class TestCallableName(unittest.TestCase):
- def test_valid_ascii(self):
+ def _callFUT(self, val):
from pyramid.util import get_callable_name
- from pyramid.compat import text_
- if PY2:
- name = text_(b'hello world', 'utf-8')
- else:
- name = b'hello world'
+ return get_callable_name(val)
- self.assertEqual(get_callable_name(name), 'hello world')
+ def test_valid_ascii_bytes(self):
+ name = b'hello world'
+ self.assertEqual(self._callFUT(name), 'hello world')
- def test_invalid_ascii(self):
- from pyramid.util import get_callable_name
- from pyramid.compat import text_
+ def test_valid_ascii_string(self):
from pyramid.exceptions import ConfigurationError
- def get_bad_name():
- if PY2:
- name = text_(b'La Pe\xc3\xb1a', 'utf-8')
- else:
- name = b'La Pe\xc3\xb1a'
+ name = b'La Pe\xc3\xb1a'.decode('utf-8')
+ self.assertRaises(ConfigurationError, self._callFUT, name)
- get_callable_name(name)
+ def test_invalid_ascii(self):
+ from pyramid.exceptions import ConfigurationError
- self.assertRaises(ConfigurationError, get_bad_name)
+ name = b'La Pe\xc3\xb1a'
+ self.assertRaises(ConfigurationError, self._callFUT, name)
class Test_hide_attrs(unittest.TestCase):
@@ -1240,3 +1229,77 @@ class TestSimpleSerializer(unittest.TestCase):
def test_dumps(self):
inst = self._makeOne()
self.assertEqual(inst.dumps('abc'), bytes_('abc'))
+
+
+class TestUnboundMethods(unittest.TestCase):
+ class Dummy(object):
+ def run(self): # pragma: no cover
+ return 'OK'
+
+ def _callFUT(self, val):
+ from pyramid.util import is_unbound_method
+
+ return is_unbound_method(val)
+
+ def test_bound_method(self):
+ self.assertFalse(self._callFUT(self.Dummy().run))
+
+ def test_unbound_method(self):
+ self.assertTrue(self._callFUT(self.Dummy.run))
+
+ def test_normal_func_unbound(self):
+ def func(): # pragma: no cover
+ return 'OK'
+
+ self.assertFalse(self._callFUT(func))
+
+
+class TestReraise(unittest.TestCase):
+ def _callFUT(self, *args):
+ from pyramid.util import reraise
+
+ return reraise(*args)
+
+ def test_it(self):
+ # tests cribbed from six.py
+ def get_next(tb):
+ return tb.tb_next.tb_next.tb_next
+
+ e = Exception('blah')
+ try:
+ raise e
+ except Exception:
+ tp, val, tb = sys.exc_info()
+
+ try:
+ self._callFUT(tp, val, tb)
+ except Exception:
+ tp2, val2, tb2 = sys.exc_info()
+ self.assertIs(tp2, Exception)
+ self.assertIs(val2, e)
+ self.assertIs(get_next(tb2), tb)
+
+ try:
+ self._callFUT(tp, val)
+ except Exception:
+ tp2, val2, tb2 = sys.exc_info()
+ self.assertIs(tp2, Exception)
+ self.assertIs(val2, e)
+ self.assertIsNot(get_next(tb2), tb)
+
+ try:
+ self._callFUT(tp, val, tb2)
+ except Exception:
+ tp2, val2, tb3 = sys.exc_info()
+ self.assertIs(tp2, Exception)
+ self.assertIs(val2, e)
+ self.assertIs(get_next(tb3), tb2)
+
+ try:
+ self._callFUT(tp, None, tb)
+ except Exception:
+ tp2, val2, tb2 = sys.exc_info()
+ self.assertIs(tp2, Exception)
+ self.assertIsNot(val2, val)
+ self.assertIsInstance(val2, Exception)
+ self.assertIs(get_next(tb2), tb)
diff --git a/tests/test_view.py b/tests/test_view.py
index f82480169..de40df1d5 100644
--- a/tests/test_view.py
+++ b/tests/test_view.py
@@ -417,12 +417,11 @@ class RenderViewToIterableTests(BaseTest, unittest.TestCase):
from pyramid.request import Request
from pyramid.config import Configurator
from pyramid.view import render_view
- from webob.compat import text_type
config = Configurator(settings={})
def view(request):
- request.response.text = text_type('<body></body>')
+ request.response.text = '<body></body>'
return request.response
config.add_view(name='test', view=view)
diff --git a/tox.ini b/tox.ini
index 5bf19d2a7..0dae17e0f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,23 +1,19 @@
[tox]
envlist =
lint,
- py27,py34,py35,py36,py37,py38,pypy,pypy3,
- docs,{py2,py3}-cover,coverage,
+ py34,py35,py36,py37,py38,pypy3,
+ docs,py36-cover,coverage,
[testenv]
# Most of these are defaults but if you specify any you can't fall back
# to defaults for others.
basepython =
- py27: python2.7
py34: python3.4
py35: python3.5
py36: python3.6
py37: python3.7
py38: python3.8
- pypy: pypy
pypy3: pypy3
- py2: python2.7
- py3: python3.6
commands =
nosetests --with-xunit --xunit-file=nosetests-{envname}.xml {posargs:}
@@ -59,21 +55,13 @@ extras =
# we separate coverage into its own testenv because a) "last run wins" wrt
# cobertura jenkins reporting and b) pypy and jython can't handle any
# combination of versions of coverage and nosexcover that i can find.
-[testenv:py2-cover]
-commands =
- coverage run {envbindir}/nosetests
- coverage xml -o coverage-py2.xml
-setenv =
- COVERAGE_FILE=.coverage.py2
-extras =
- testing
-
-[testenv:py3-cover]
+[testenv:py36-cover]
+basepython = python3.6
commands =
coverage run {envbindir}/nosetests
- coverage xml -o coverage-py3.xml
+ coverage xml -o coverage-{envname}.xml
setenv =
- COVERAGE_FILE=.coverage.py3
+ COVERAGE_FILE=.coverage.{envname}
extras =
testing