From d986123332806aadc43b1daab396ff5200ec10ce Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 6 Aug 2012 23:56:24 -0400 Subject: move stuff from config.util to registry so it can be a set of API (which are now documented), resolve deferred discriminators in introspectable.register so that a directive can depend on a deferred discriminator, put head-adding code in predicate instead of in add_view itself --- docs/api/registry.rst | 14 ++++++++++++ pyramid/config/__init__.py | 8 ++----- pyramid/config/predicates.py | 6 ++++- pyramid/config/routes.py | 2 +- pyramid/config/util.py | 12 ++-------- pyramid/config/views.py | 18 +++++++-------- pyramid/registry.py | 33 ++++++++++++++++++++++++++++ pyramid/tests/test_config/test_predicates.py | 4 ++++ pyramid/util.py | 1 + 9 files changed, 70 insertions(+), 28 deletions(-) diff --git a/docs/api/registry.rst b/docs/api/registry.rst index e62e2ba6f..1d5d52248 100644 --- a/docs/api/registry.rst +++ b/docs/api/registry.rst @@ -38,3 +38,17 @@ This class is new as of :app:`Pyramid` 1.3. +.. autoclass:: Deferred + + This class is new as of :app:`Pyramid` 1.4. + +.. autofunction:: undefer + + This function is new as of :app:`Pyramid` 1.4. + +.. autoclass:: predvalseq + + This class is new as of :app:`Pyramid` 1.4. + + + diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py index 7e6649c14..a45dca255 100644 --- a/pyramid/config/__init__.py +++ b/pyramid/config/__init__.py @@ -45,6 +45,8 @@ from pyramid.registry import ( Introspectable, Introspector, Registry, + Deferred, + undefer, ) from pyramid.router import Router @@ -72,7 +74,6 @@ from pyramid.config.tweens import TweensConfiguratorMixin from pyramid.config.util import ( action_method, ActionInfo, - Deferred, ) from pyramid.config.views import ViewsConfiguratorMixin from pyramid.config.zca import ZCAConfiguratorMixin @@ -1067,11 +1068,6 @@ class ActionState(object): if clear: del self.actions[:] -def undefer(v): - if isinstance(v, Deferred): - v = v.resolve() - return v - # this function is licensed under the ZPL (stolen from Zope) def resolveConflicts(actions): """Resolve conflicting actions diff --git a/pyramid/config/predicates.py b/pyramid/config/predicates.py index 311d47860..9e0ee28c1 100644 --- a/pyramid/config/predicates.py +++ b/pyramid/config/predicates.py @@ -29,7 +29,11 @@ class XHRPredicate(object): class RequestMethodPredicate(object): def __init__(self, val, config): - self.val = as_sorted_tuple(val) + 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)) diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py index d8ce4804c..8f7f3612b 100644 --- a/pyramid/config/routes.py +++ b/pyramid/config/routes.py @@ -10,6 +10,7 @@ from pyramid.interfaces import ( ) from pyramid.exceptions import ConfigurationError +from pyramid.registry import predvalseq from pyramid.request import route_request_iface from pyramid.urldispatch import RoutesMapper @@ -17,7 +18,6 @@ from pyramid.config.util import ( action_method, as_sorted_tuple, PredicateList, - predvalseq, ) from pyramid.config import predicates diff --git a/pyramid/config/util.py b/pyramid/config/util.py index ce4a4a728..cabcab649 100644 --- a/pyramid/config/util.py +++ b/pyramid/config/util.py @@ -11,6 +11,8 @@ from pyramid.compat import ( from pyramid.exceptions import ConfigurationError +from pyramid.registry import predvalseq + from hashlib import md5 MAX_ORDER = 1 << 30 @@ -294,13 +296,3 @@ class PredicateList(object): order = (MAX_ORDER - score) / (len(preds) + 1) return order, preds, phash.hexdigest() -class predvalseq(tuple): - pass - -class Deferred(object): - def __init__(self, func): - self.func = func - - def resolve(self): - return self.func() - diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 4491272d3..6fb598847 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -55,6 +55,11 @@ from pyramid.httpexceptions import ( HTTPNotFound, ) +from pyramid.registry import ( + predvalseq, + Deferred, + ) + from pyramid.security import NO_PERMISSION_REQUIRED from pyramid.static import static_view from pyramid.threadlocal import get_current_registry @@ -64,18 +69,17 @@ from pyramid.view import ( AppendSlashNotFoundViewFactory, ) -from pyramid.util import object_description +from pyramid.util import ( + object_description, + ) from pyramid.config import predicates from pyramid.config.util import ( - Deferred, DEFAULT_PHASH, MAX_ORDER, action_method, - as_sorted_tuple, PredicateList, - predvalseq, ) urljoin = urlparse.urljoin @@ -1029,12 +1033,6 @@ class ViewsConfiguratorMixin(object): raise ConfigurationError( 'request_type must be an interface, not %s' % request_type) - if request_method is not None: - request_method = as_sorted_tuple(request_method) - if 'GET' in request_method and 'HEAD' not in request_method: - # GET implies HEAD too - request_method = as_sorted_tuple(request_method + ('HEAD',)) - if context is None: context = for_ diff --git a/pyramid/registry.py b/pyramid/registry.py index f0f9c83ea..606251a8d 100644 --- a/pyramid/registry.py +++ b/pyramid/registry.py @@ -191,14 +191,20 @@ class Introspectable(dict): def unrelate(self, category_name, discriminator): self._relations.append((False, category_name, discriminator)) + def _assert_resolved(self): + assert undefer(self.discriminator) is self.discriminator + @property def discriminator_hash(self): + self._assert_resolved() return hash(self.discriminator) def __hash__(self): + self._assert_resolved() return hash((self.category_name,) + (self.discriminator,)) def __repr__(self): + self._assert_resolved() return '<%s category %r, discriminator %r>' % (self.__class__.__name__, self.category_name, self.discriminator) @@ -209,9 +215,11 @@ class Introspectable(dict): __bool__ = __nonzero__ # py3 def register(self, introspector, action_info): + self.discriminator = undefer(self.discriminator) self.action_info = action_info introspector.add(self) for relate, category_name, discriminator in self._relations: + discriminator = undefer(discriminator) if relate: method = introspector.relate else: @@ -221,4 +229,29 @@ class Introspectable(dict): (category_name, discriminator) ) +class Deferred(object): + """ Can be used by a third-party configuration extender to wrap a + :term:`discriminator` during configuration if an immediately hashable + discriminator cannot be computed because it relies on unresolved values. + The function should accept no arguments and should return a hashable + discriminator.""" + def __init__(self, func): + self.func = func + + def resolve(self): + return self.func() + +def undefer(v): + """ Function which accepts an object and returns it unless it is a + :class:`pyramid.registry.Deferred` instance. If it is an instance of + that class, its ``resolve`` method is called, and the result of the + method is returned.""" + if isinstance(v, Deferred): + v = v.resolve() + return v + +class predvalseq(tuple): + """ A subtype of tuple used to represent a sequence of predicate values """ + pass + global_registry = Registry('global') diff --git a/pyramid/tests/test_config/test_predicates.py b/pyramid/tests/test_config/test_predicates.py index 94e613715..e33a31458 100644 --- a/pyramid/tests/test_config/test_predicates.py +++ b/pyramid/tests/test_config/test_predicates.py @@ -33,6 +33,10 @@ class TestRequestMethodPredicate(unittest.TestCase): def _makeOne(self, val): from pyramid.config.predicates import RequestMethodPredicate return RequestMethodPredicate(val, None) + + def test_ctor_get_but_no_head(self): + inst = self._makeOne('GET') + self.assertEqual(inst.val, ('GET', 'HEAD')) def test___call___true_single(self): inst = self._makeOne('GET') diff --git a/pyramid/util.py b/pyramid/util.py index 7d5c97814..dabd84695 100644 --- a/pyramid/util.py +++ b/pyramid/util.py @@ -280,3 +280,4 @@ def shortrepr(object, closer): if len(r) > 100: r = r[:100] + ' ... %s' % closer return r + -- cgit v1.2.3