summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin <martin.frlin@gmail.com>2016-12-06 12:07:38 +0100
committerMartin <martin.frlin@gmail.com>2016-12-06 12:07:38 +0100
commit33a048d505f690bde6c44914dff154abfc2e790f (patch)
tree52d0f892e5bd769b3942f860cb99bd03f3ec96ed
parent8bb7a4ba3593895a12ded27260ece7c1d673de56 (diff)
parentfedfacf0bd1cbefbe922618d4df93917465f1307 (diff)
downloadpyramid-33a048d505f690bde6c44914dff154abfc2e790f.tar.gz
pyramid-33a048d505f690bde6c44914dff154abfc2e790f.tar.bz2
pyramid-33a048d505f690bde6c44914dff154abfc2e790f.zip
Merge remote-tracking branch 'upstream/master' into issue-2656
-rw-r--r--CONTRIBUTORS.txt2
-rw-r--r--docs/conf.py4
-rw-r--r--pyramid/config/predicates.py303
-rw-r--r--pyramid/config/routes.py10
-rw-r--r--pyramid/config/security.py2
-rw-r--r--pyramid/config/tweens.py2
-rw-r--r--pyramid/config/util.py15
-rw-r--r--pyramid/config/views.py6
-rw-r--r--pyramid/predicates.py300
-rw-r--r--pyramid/tests/test_config/test_util.py11
-rw-r--r--pyramid/tests/test_predicates.py (renamed from pyramid/tests/test_config/test_predicates.py)22
-rw-r--r--pyramid/util.py15
12 files changed, 350 insertions, 342 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 98e243c1f..b4e30e085 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -286,3 +286,5 @@ Contributors
- Keith Yang, 2016/07/22
- Moriyoshi Koizumi, 2016/11/20
+
+- Mikko Ohtamaa, 2016/12/6
diff --git a/docs/conf.py b/docs/conf.py
index c3a7170fc..84fd5cf1b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -191,10 +191,10 @@ latex_documents = [
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
-latex_use_parts = True
+latex_toplevel_sectioning = "section"
# If false, no module index is generated.
-latex_use_modindex = False
+latex_domain_indices = False
## Say, for a moment that you have a twoside document that needs a 3cm
## inner margin to allow for binding and at least two centimetres the
diff --git a/pyramid/config/predicates.py b/pyramid/config/predicates.py
index 0b76bbd70..5d99d6564 100644
--- a/pyramid/config/predicates.py
+++ b/pyramid/config/predicates.py
@@ -1,301 +1,2 @@
-import re
-
-from pyramid.exceptions import ConfigurationError
-
-from pyramid.compat import is_nonstr_iter
-
-from pyramid.traversal import (
- find_interface,
- traversal_path,
- resource_path_tuple
- )
-
-from pyramid.urldispatch import _compile_route
-from pyramid.util import object_description
-from pyramid.session import check_csrf_token
-
-from .util import as_sorted_tuple
-
-_marker = object()
-
-class XHRPredicate(object):
- def __init__(self, val, config):
- self.val = bool(val)
-
- def text(self):
- return 'xhr = %s' % self.val
-
- phash = text
-
- def __call__(self, context, request):
- return bool(request.is_xhr) is self.val
-
-class RequestMethodPredicate(object):
- def __init__(self, val, config):
- request_method = as_sorted_tuple(val)
- if 'GET' in request_method and 'HEAD' not in request_method:
- # GET implies HEAD too
- request_method = as_sorted_tuple(request_method + ('HEAD',))
- self.val = request_method
-
- def text(self):
- return 'request_method = %s' % (','.join(self.val))
-
- phash = text
-
- def __call__(self, context, request):
- return request.method in self.val
-
-class PathInfoPredicate(object):
- def __init__(self, val, config):
- self.orig = val
- try:
- val = re.compile(val)
- except re.error as why:
- raise ConfigurationError(why.args[0])
- self.val = val
-
- def text(self):
- return 'path_info = %s' % (self.orig,)
-
- phash = text
-
- def __call__(self, context, request):
- return self.val.match(request.upath_info) is not None
-
-class RequestParamPredicate(object):
- def __init__(self, val, config):
- val = as_sorted_tuple(val)
- reqs = []
- for p in val:
- k = p
- v = None
- if p.startswith('='):
- if '=' in p[1:]:
- k, v = p[1:].split('=', 1)
- k = '=' + k
- k, v = k.strip(), v.strip()
- elif '=' in p:
- k, v = p.split('=', 1)
- k, v = k.strip(), v.strip()
- reqs.append((k, v))
- self.val = val
- self.reqs = reqs
-
- def text(self):
- return 'request_param %s' % ','.join(
- ['%s=%s' % (x,y) if y else x for x, y in self.reqs]
- )
-
- phash = text
-
- def __call__(self, context, request):
- for k, v in self.reqs:
- actual = request.params.get(k)
- if actual is None:
- return False
- if v is not None and actual != v:
- return False
- return True
-
-class HeaderPredicate(object):
- def __init__(self, val, config):
- name = val
- v = None
- if ':' in name:
- name, val_str = name.split(':', 1)
- try:
- v = re.compile(val_str)
- except re.error as why:
- raise ConfigurationError(why.args[0])
- if v is None:
- self._text = 'header %s' % (name,)
- else:
- self._text = 'header %s=%s' % (name, val_str)
- self.name = name
- self.val = v
-
- def text(self):
- return self._text
-
- phash = text
-
- def __call__(self, context, request):
- if self.val is None:
- return self.name in request.headers
- val = request.headers.get(self.name)
- if val is None:
- return False
- return self.val.match(val) is not None
-
-class AcceptPredicate(object):
- def __init__(self, val, config):
- self.val = val
-
- def text(self):
- return 'accept = %s' % (self.val,)
-
- phash = text
-
- def __call__(self, context, request):
- return self.val in request.accept
-
-class ContainmentPredicate(object):
- def __init__(self, val, config):
- self.val = config.maybe_dotted(val)
-
- def text(self):
- return 'containment = %s' % (self.val,)
-
- phash = text
-
- def __call__(self, context, request):
- ctx = getattr(request, 'context', context)
- return find_interface(ctx, self.val) is not None
-
-class RequestTypePredicate(object):
- def __init__(self, val, config):
- self.val = val
-
- def text(self):
- return 'request_type = %s' % (self.val,)
-
- phash = text
-
- def __call__(self, context, request):
- return self.val.providedBy(request)
-
-class MatchParamPredicate(object):
- def __init__(self, val, config):
- val = as_sorted_tuple(val)
- self.val = val
- reqs = [ p.split('=', 1) for p in val ]
- self.reqs = [ (x.strip(), y.strip()) for x, y in reqs ]
-
- def text(self):
- return 'match_param %s' % ','.join(
- ['%s=%s' % (x,y) for x, y in self.reqs]
- )
-
- phash = text
-
- def __call__(self, context, request):
- if not request.matchdict:
- # might be None
- return False
- for k, v in self.reqs:
- if request.matchdict.get(k) != v:
- return False
- return True
-
-class CustomPredicate(object):
- def __init__(self, func, config):
- self.func = func
-
- def text(self):
- return getattr(
- self.func,
- '__text__',
- 'custom predicate: %s' % object_description(self.func)
- )
-
- def phash(self):
- # using hash() here rather than id() is intentional: we
- # want to allow custom predicates that are part of
- # frameworks to be able to define custom __hash__
- # functions for custom predicates, so that the hash output
- # of predicate instances which are "logically the same"
- # may compare equal.
- return 'custom:%r' % hash(self.func)
-
- def __call__(self, context, request):
- return self.func(context, request)
-
-
-class TraversePredicate(object):
- # Can only be used as a *route* "predicate"; it adds 'traverse' to the
- # matchdict if it's specified in the routing args. This causes the
- # ResourceTreeTraverser to use the resolved traverse pattern as the
- # traversal path.
- def __init__(self, val, config):
- _, self.tgenerate = _compile_route(val)
- self.val = val
-
- def text(self):
- return 'traverse matchdict pseudo-predicate'
-
- def phash(self):
- # This isn't actually a predicate, it's just a infodict modifier that
- # injects ``traverse`` into the matchdict. As a result, we don't
- # need to update the hash.
- return ''
-
- def __call__(self, context, request):
- if 'traverse' in context:
- return True
- m = context['match']
- tvalue = self.tgenerate(m) # tvalue will be urlquoted string
- m['traverse'] = traversal_path(tvalue)
- # This isn't actually a predicate, it's just a infodict modifier that
- # injects ``traverse`` into the matchdict. As a result, we just
- # return True.
- return True
-
-class CheckCSRFTokenPredicate(object):
-
- check_csrf_token = staticmethod(check_csrf_token) # testing
-
- def __init__(self, val, config):
- self.val = val
-
- def text(self):
- return 'check_csrf = %s' % (self.val,)
-
- phash = text
-
- def __call__(self, context, request):
- val = self.val
- if val:
- if val is True:
- val = 'csrf_token'
- return self.check_csrf_token(request, val, raises=False)
- return True
-
-class PhysicalPathPredicate(object):
- def __init__(self, val, config):
- if is_nonstr_iter(val):
- self.val = tuple(val)
- else:
- val = tuple(filter(None, val.split('/')))
- self.val = ('',) + val
-
- def text(self):
- return 'physical_path = %s' % (self.val,)
-
- phash = text
-
- def __call__(self, context, request):
- if getattr(context, '__name__', _marker) is not _marker:
- return resource_path_tuple(context) == self.val
- return False
-
-class EffectivePrincipalsPredicate(object):
- def __init__(self, val, config):
- if is_nonstr_iter(val):
- self.val = set(val)
- else:
- self.val = set((val,))
-
- def text(self):
- return 'effective_principals = %s' % sorted(list(self.val))
-
- phash = text
-
- def __call__(self, context, request):
- req_principals = request.effective_principals
- if is_nonstr_iter(req_principals):
- rpset = set(req_principals)
- if self.val.issubset(rpset):
- return True
- return False
-
+import zope.deprecation
+zope.deprecation.moved('pyramid.predicates', 'Pyramid 2.0')
diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py
index 90d4d47d2..203baa128 100644
--- a/pyramid/config/routes.py
+++ b/pyramid/config/routes.py
@@ -13,12 +13,10 @@ from pyramid.registry import predvalseq
from pyramid.request import route_request_iface
from pyramid.urldispatch import RoutesMapper
-from pyramid.config.util import (
- action_method,
- as_sorted_tuple,
- )
+from pyramid.config.util import action_method
+from pyramid.util import as_sorted_tuple
-import pyramid.config.predicates
+import pyramid.predicates
class RoutesConfiguratorMixin(object):
@action_method
@@ -446,7 +444,7 @@ class RoutesConfiguratorMixin(object):
)
def add_default_route_predicates(self):
- p = pyramid.config.predicates
+ p = pyramid.predicates
for (name, factory) in (
('xhr', p.XHRPredicate),
('request_method', p.RequestMethodPredicate),
diff --git a/pyramid/config/security.py b/pyramid/config/security.py
index 02732c042..33593376b 100644
--- a/pyramid/config/security.py
+++ b/pyramid/config/security.py
@@ -9,9 +9,9 @@ from pyramid.interfaces import (
PHASE2_CONFIG,
)
-from pyramid.config.util import as_sorted_tuple
from pyramid.exceptions import ConfigurationError
from pyramid.util import action_method
+from pyramid.util import as_sorted_tuple
class SecurityConfiguratorMixin(object):
@action_method
diff --git a/pyramid/config/tweens.py b/pyramid/config/tweens.py
index 0aeb01fe3..16712ab16 100644
--- a/pyramid/config/tweens.py
+++ b/pyramid/config/tweens.py
@@ -17,9 +17,9 @@ from pyramid.tweens import (
from pyramid.config.util import (
action_method,
- is_string_or_iterable,
TopologicalSorter,
)
+from pyramid.util import is_string_or_iterable
class TweensConfiguratorMixin(object):
def add_tween(self, tween_factory, under=None, over=None):
diff --git a/pyramid/config/util.py b/pyramid/config/util.py
index 626e8d5fe..67bba9593 100644
--- a/pyramid/config/util.py
+++ b/pyramid/config/util.py
@@ -4,8 +4,7 @@ import inspect
from pyramid.compat import (
bytes_,
getargspec,
- is_nonstr_iter,
- string_types,
+ is_nonstr_iter
)
from pyramid.compat import im_func
@@ -24,18 +23,6 @@ ActionInfo = ActionInfo # support bw compat imports
MAX_ORDER = 1 << 30
DEFAULT_PHASH = md5().hexdigest()
-def is_string_or_iterable(v):
- if isinstance(v, string_types):
- return True
- if hasattr(v, '__iter__'):
- return True
-
-def as_sorted_tuple(val):
- if not is_nonstr_iter(val):
- val = (val,)
- val = tuple(sorted(val))
- return val
-
class not_(object):
"""
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index 6082d8b48..65c9da585 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -70,10 +70,11 @@ import pyramid.util
from pyramid.util import (
viewdefaults,
action_method,
+ as_sorted_tuple,
TopologicalSorter,
)
-import pyramid.config.predicates
+import pyramid.predicates
import pyramid.viewderivers
from pyramid.viewderivers import (
@@ -89,7 +90,6 @@ from pyramid.viewderivers import (
from pyramid.config.util import (
DEFAULT_PHASH,
MAX_ORDER,
- as_sorted_tuple,
)
urljoin = urlparse.urljoin
@@ -1143,7 +1143,7 @@ class ViewsConfiguratorMixin(object):
)
def add_default_view_predicates(self):
- p = pyramid.config.predicates
+ p = pyramid.predicates
for (name, factory) in (
('xhr', p.XHRPredicate),
('request_method', p.RequestMethodPredicate),
diff --git a/pyramid/predicates.py b/pyramid/predicates.py
new file mode 100644
index 000000000..7c3a778ca
--- /dev/null
+++ b/pyramid/predicates.py
@@ -0,0 +1,300 @@
+import re
+
+from pyramid.exceptions import ConfigurationError
+
+from pyramid.compat import is_nonstr_iter
+
+from pyramid.session import check_csrf_token
+from pyramid.traversal import (
+ find_interface,
+ traversal_path,
+ resource_path_tuple
+ )
+
+from pyramid.urldispatch import _compile_route
+from pyramid.util import object_description
+from pyramid.util import as_sorted_tuple
+
+_marker = object()
+
+class XHRPredicate(object):
+ def __init__(self, val, config):
+ self.val = bool(val)
+
+ def text(self):
+ return 'xhr = %s' % self.val
+
+ phash = text
+
+ def __call__(self, context, request):
+ return bool(request.is_xhr) is self.val
+
+class RequestMethodPredicate(object):
+ def __init__(self, val, config):
+ request_method = as_sorted_tuple(val)
+ if 'GET' in request_method and 'HEAD' not in request_method:
+ # GET implies HEAD too
+ request_method = as_sorted_tuple(request_method + ('HEAD',))
+ self.val = request_method
+
+ def text(self):
+ return 'request_method = %s' % (','.join(self.val))
+
+ phash = text
+
+ def __call__(self, context, request):
+ return request.method in self.val
+
+class PathInfoPredicate(object):
+ def __init__(self, val, config):
+ self.orig = val
+ try:
+ val = re.compile(val)
+ except re.error as why:
+ raise ConfigurationError(why.args[0])
+ self.val = val
+
+ def text(self):
+ return 'path_info = %s' % (self.orig,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ return self.val.match(request.upath_info) is not None
+
+class RequestParamPredicate(object):
+ def __init__(self, val, config):
+ val = as_sorted_tuple(val)
+ reqs = []
+ for p in val:
+ k = p
+ v = None
+ if p.startswith('='):
+ if '=' in p[1:]:
+ k, v = p[1:].split('=', 1)
+ k = '=' + k
+ k, v = k.strip(), v.strip()
+ elif '=' in p:
+ k, v = p.split('=', 1)
+ k, v = k.strip(), v.strip()
+ reqs.append((k, v))
+ self.val = val
+ self.reqs = reqs
+
+ def text(self):
+ return 'request_param %s' % ','.join(
+ ['%s=%s' % (x,y) if y else x for x, y in self.reqs]
+ )
+
+ phash = text
+
+ def __call__(self, context, request):
+ for k, v in self.reqs:
+ actual = request.params.get(k)
+ if actual is None:
+ return False
+ if v is not None and actual != v:
+ return False
+ return True
+
+class HeaderPredicate(object):
+ def __init__(self, val, config):
+ name = val
+ v = None
+ if ':' in name:
+ name, val_str = name.split(':', 1)
+ try:
+ v = re.compile(val_str)
+ except re.error as why:
+ raise ConfigurationError(why.args[0])
+ if v is None:
+ self._text = 'header %s' % (name,)
+ else:
+ self._text = 'header %s=%s' % (name, val_str)
+ self.name = name
+ self.val = v
+
+ def text(self):
+ return self._text
+
+ phash = text
+
+ def __call__(self, context, request):
+ if self.val is None:
+ return self.name in request.headers
+ val = request.headers.get(self.name)
+ if val is None:
+ return False
+ return self.val.match(val) is not None
+
+class AcceptPredicate(object):
+ def __init__(self, val, config):
+ self.val = val
+
+ def text(self):
+ return 'accept = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ return self.val in request.accept
+
+class ContainmentPredicate(object):
+ def __init__(self, val, config):
+ self.val = config.maybe_dotted(val)
+
+ def text(self):
+ return 'containment = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ ctx = getattr(request, 'context', context)
+ return find_interface(ctx, self.val) is not None
+
+class RequestTypePredicate(object):
+ def __init__(self, val, config):
+ self.val = val
+
+ def text(self):
+ return 'request_type = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ return self.val.providedBy(request)
+
+class MatchParamPredicate(object):
+ def __init__(self, val, config):
+ val = as_sorted_tuple(val)
+ self.val = val
+ reqs = [ p.split('=', 1) for p in val ]
+ self.reqs = [ (x.strip(), y.strip()) for x, y in reqs ]
+
+ def text(self):
+ return 'match_param %s' % ','.join(
+ ['%s=%s' % (x,y) for x, y in self.reqs]
+ )
+
+ phash = text
+
+ def __call__(self, context, request):
+ if not request.matchdict:
+ # might be None
+ return False
+ for k, v in self.reqs:
+ if request.matchdict.get(k) != v:
+ return False
+ return True
+
+class CustomPredicate(object):
+ def __init__(self, func, config):
+ self.func = func
+
+ def text(self):
+ return getattr(
+ self.func,
+ '__text__',
+ 'custom predicate: %s' % object_description(self.func)
+ )
+
+ def phash(self):
+ # using hash() here rather than id() is intentional: we
+ # want to allow custom predicates that are part of
+ # frameworks to be able to define custom __hash__
+ # functions for custom predicates, so that the hash output
+ # of predicate instances which are "logically the same"
+ # may compare equal.
+ return 'custom:%r' % hash(self.func)
+
+ def __call__(self, context, request):
+ return self.func(context, request)
+
+
+class TraversePredicate(object):
+ # Can only be used as a *route* "predicate"; it adds 'traverse' to the
+ # matchdict if it's specified in the routing args. This causes the
+ # ResourceTreeTraverser to use the resolved traverse pattern as the
+ # traversal path.
+ def __init__(self, val, config):
+ _, self.tgenerate = _compile_route(val)
+ self.val = val
+
+ def text(self):
+ return 'traverse matchdict pseudo-predicate'
+
+ def phash(self):
+ # This isn't actually a predicate, it's just a infodict modifier that
+ # injects ``traverse`` into the matchdict. As a result, we don't
+ # need to update the hash.
+ return ''
+
+ def __call__(self, context, request):
+ if 'traverse' in context:
+ return True
+ m = context['match']
+ tvalue = self.tgenerate(m) # tvalue will be urlquoted string
+ m['traverse'] = traversal_path(tvalue)
+ # This isn't actually a predicate, it's just a infodict modifier that
+ # injects ``traverse`` into the matchdict. As a result, we just
+ # return True.
+ return True
+
+class CheckCSRFTokenPredicate(object):
+
+ check_csrf_token = staticmethod(check_csrf_token) # testing
+
+ def __init__(self, val, config):
+ self.val = val
+
+ def text(self):
+ return 'check_csrf = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ val = self.val
+ if val:
+ if val is True:
+ val = 'csrf_token'
+ return self.check_csrf_token(request, val, raises=False)
+ return True
+
+class PhysicalPathPredicate(object):
+ def __init__(self, val, config):
+ if is_nonstr_iter(val):
+ self.val = tuple(val)
+ else:
+ val = tuple(filter(None, val.split('/')))
+ self.val = ('',) + val
+
+ def text(self):
+ return 'physical_path = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ if getattr(context, '__name__', _marker) is not _marker:
+ return resource_path_tuple(context) == self.val
+ return False
+
+class EffectivePrincipalsPredicate(object):
+ def __init__(self, val, config):
+ if is_nonstr_iter(val):
+ self.val = set(val)
+ else:
+ self.val = set((val,))
+
+ def text(self):
+ return 'effective_principals = %s' % sorted(list(self.val))
+
+ phash = text
+
+ def __call__(self, context, request):
+ req_principals = request.effective_principals
+ if is_nonstr_iter(req_principals):
+ rpset = set(req_principals)
+ if self.val.issubset(rpset):
+ return True
+ return False
+
diff --git a/pyramid/tests/test_config/test_util.py b/pyramid/tests/test_config/test_util.py
index ccf7fa260..398b6fba8 100644
--- a/pyramid/tests/test_config/test_util.py
+++ b/pyramid/tests/test_config/test_util.py
@@ -5,7 +5,7 @@ class TestPredicateList(unittest.TestCase):
def _makeOne(self):
from pyramid.config.util import PredicateList
- from pyramid.config import predicates
+ from pyramid import predicates
inst = PredicateList()
for name, factory in (
('xhr', predicates.XHRPredicate),
@@ -594,6 +594,15 @@ class TestNotted(unittest.TestCase):
self.assertEqual(inst.phash(), '')
self.assertEqual(inst(None, None), True)
+
+class TestDeprecatedPredicates(unittest.TestCase):
+ def test_it(self):
+ import warnings
+ with warnings.catch_warnings(record=True) as w:
+ warnings.filterwarnings('always')
+ from pyramid.config.predicates import XHRPredicate
+ self.assertEqual(len(w), 1)
+
class DummyPredicate(object):
def __init__(self, result):
self.result = result
diff --git a/pyramid/tests/test_config/test_predicates.py b/pyramid/tests/test_predicates.py
index 9cd8f2734..8a002c24e 100644
--- a/pyramid/tests/test_config/test_predicates.py
+++ b/pyramid/tests/test_predicates.py
@@ -6,7 +6,7 @@ from pyramid.compat import text_
class TestXHRPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import XHRPredicate
+ from pyramid.predicates import XHRPredicate
return XHRPredicate(val, None)
def test___call___true(self):
@@ -33,7 +33,7 @@ class TestXHRPredicate(unittest.TestCase):
class TestRequestMethodPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import RequestMethodPredicate
+ from pyramid.predicates import RequestMethodPredicate
return RequestMethodPredicate(val, None)
def test_ctor_get_but_no_head(self):
@@ -71,7 +71,7 @@ class TestRequestMethodPredicate(unittest.TestCase):
class TestPathInfoPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import PathInfoPredicate
+ from pyramid.predicates import PathInfoPredicate
return PathInfoPredicate(val, None)
def test_ctor_compilefail(self):
@@ -102,7 +102,7 @@ class TestPathInfoPredicate(unittest.TestCase):
class TestRequestParamPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import RequestParamPredicate
+ from pyramid.predicates import RequestParamPredicate
return RequestParamPredicate(val, None)
def test___call___true_exists(self):
@@ -174,7 +174,7 @@ class TestRequestParamPredicate(unittest.TestCase):
class TestMatchParamPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import MatchParamPredicate
+ from pyramid.predicates import MatchParamPredicate
return MatchParamPredicate(val, None)
def test___call___true_single(self):
@@ -216,7 +216,7 @@ class TestMatchParamPredicate(unittest.TestCase):
class TestCustomPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import CustomPredicate
+ from pyramid.predicates import CustomPredicate
return CustomPredicate(val, None)
def test___call___true(self):
@@ -255,7 +255,7 @@ class TestCustomPredicate(unittest.TestCase):
class TestTraversePredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import TraversePredicate
+ from pyramid.predicates import TraversePredicate
return TraversePredicate(val, None)
def test___call__traverse_has_remainder_already(self):
@@ -297,7 +297,7 @@ class TestTraversePredicate(unittest.TestCase):
class Test_CheckCSRFTokenPredicate(unittest.TestCase):
def _makeOne(self, val, config):
- from pyramid.config.predicates import CheckCSRFTokenPredicate
+ from pyramid.predicates import CheckCSRFTokenPredicate
return CheckCSRFTokenPredicate(val, config)
def test_text(self):
@@ -340,7 +340,7 @@ class Test_CheckCSRFTokenPredicate(unittest.TestCase):
class TestHeaderPredicate(unittest.TestCase):
def _makeOne(self, val):
- from pyramid.config.predicates import HeaderPredicate
+ from pyramid.predicates import HeaderPredicate
return HeaderPredicate(val, None)
def test___call___true_exists(self):
@@ -404,7 +404,7 @@ class TestHeaderPredicate(unittest.TestCase):
class Test_PhysicalPathPredicate(unittest.TestCase):
def _makeOne(self, val, config):
- from pyramid.config.predicates import PhysicalPathPredicate
+ from pyramid.predicates import PhysicalPathPredicate
return PhysicalPathPredicate(val, config)
def test_text(self):
@@ -468,7 +468,7 @@ class Test_EffectivePrincipalsPredicate(unittest.TestCase):
testing.tearDown()
def _makeOne(self, val, config):
- from pyramid.config.predicates import EffectivePrincipalsPredicate
+ from pyramid.predicates import EffectivePrincipalsPredicate
return EffectivePrincipalsPredicate(val, config)
def test_text(self):
diff --git a/pyramid/util.py b/pyramid/util.py
index d5b3c6d72..3337d410d 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -28,13 +28,24 @@ from pyramid.compat import (
from pyramid.interfaces import IActionInfo
from pyramid.path import DottedNameResolver as _DottedNameResolver
+_marker = object()
+
class DottedNameResolver(_DottedNameResolver):
def __init__(self, package=None): # default to package = None for bw compat
_DottedNameResolver.__init__(self, package)
-_marker = object()
-
+def is_string_or_iterable(v):
+ if isinstance(v, string_types):
+ return True
+ if hasattr(v, '__iter__'):
+ return True
+
+def as_sorted_tuple(val):
+ if not is_nonstr_iter(val):
+ val = (val,)
+ val = tuple(sorted(val))
+ return val
class InstancePropertyHelper(object):
"""A helper object for assigning properties and descriptors to instances.