Skip to content

Commit

Permalink
add InstancePropertyHelper and apply_request_extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
mmerickel committed Feb 17, 2015
1 parent cd298a4 commit 04cc91a
Show file tree
Hide file tree
Showing 11 changed files with 321 additions and 104 deletions.
1 change: 1 addition & 0 deletions docs/api/request.rst
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,4 @@
that used as ``request.GET``, ``request.POST``, and ``request.params``),
see :class:`pyramid.interfaces.IMultiDict`.

.. autofunction:: apply_request_extensions(request)
4 changes: 2 additions & 2 deletions pyramid/config/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

from pyramid.util import (
action_method,
InstancePropertyMixin,
get_callable_name,
InstancePropertyHelper,
)


Expand Down Expand Up @@ -174,7 +174,7 @@ def add_request_method(self,

property = property or reify
if property:
name, callable = InstancePropertyMixin._make_property(
name, callable = InstancePropertyHelper.make_property(
callable, name=name, reify=reify)
elif name is None:
name = callable.__name__
Expand Down
3 changes: 1 addition & 2 deletions pyramid/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,8 +591,7 @@ def __call__(request):
class IRequestFactory(Interface):
""" A utility which generates a request """
def __call__(environ):
""" Return an object implementing IRequest, e.g. an instance
of ``pyramid.request.Request``"""
""" Return an instance of ``pyramid.request.Request``"""

This comment has been minimized.

Copy link
@digitalresistor

digitalresistor Feb 17, 2015

Member

Is there a particular reason why the IRequestFactory() can't return an object implementing IRequest? Do we not want to support custom request objects?


def blank(path):
""" Return an empty request object (see
Expand Down
26 changes: 25 additions & 1 deletion pyramid/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from pyramid.interfaces import (
IRequest,
IRequestExtensions,
IResponse,
ISessionFactory,
)
Expand All @@ -16,6 +17,7 @@
text_,
bytes_,
native_,
iteritems_,
)

from pyramid.decorator import reify
Expand All @@ -26,7 +28,10 @@
AuthorizationAPIMixin,
)
from pyramid.url import URLMethodsMixin
from pyramid.util import InstancePropertyMixin
from pyramid.util import (
InstancePropertyHelper,
InstancePropertyMixin,
)

class TemplateContext(object):
pass
Expand Down Expand Up @@ -307,3 +312,22 @@ def call_app_with_subpath_as_path_info(request, app):
new_request.environ['PATH_INFO'] = new_path_info

return new_request.get_response(app)

def apply_request_extensions(request, extensions=None):
"""Apply request extensions (methods and properties) to an instance of
:class:`pyramid.interfaces.IRequest`. This method is dependent on the
``request`` containing a properly initialized registry.
After invoking this method, the ``request`` should have the methods
and properties that were defined using
:meth:`pyramid.config.Configurator.add_request_method`.
"""
if extensions is None:
extensions = request.registry.queryUtility(IRequestExtensions)
if extensions is not None:
for name, fn in iteritems_(extensions.methods):
method = fn.__get__(request, request.__class__)
setattr(request, name, method)

InstancePropertyHelper.apply_properties(
request, extensions.descriptors)
3 changes: 2 additions & 1 deletion pyramid/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from pyramid.exceptions import PredicateMismatch
from pyramid.httpexceptions import HTTPNotFound
from pyramid.request import Request
from pyramid.request import apply_request_extensions
from pyramid.threadlocal import manager

from pyramid.traversal import (
Expand Down Expand Up @@ -213,7 +214,7 @@ def invoke_subrequest(self, request, use_tweens=False):
try:
extensions = self.request_extensions
if extensions is not None:
request._set_extensions(extensions)
apply_request_extensions(request, extensions=extensions)
response = handle_request(request)

if request.response_callbacks:
Expand Down
8 changes: 3 additions & 5 deletions pyramid/scripting.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from pyramid.config import global_registries
from pyramid.exceptions import ConfigurationError
from pyramid.request import Request

from pyramid.interfaces import (
IRequestExtensions,
IRequestFactory,
IRootFactory,
)
from pyramid.request import Request
from pyramid.request import apply_request_extensions

from pyramid.threadlocal import manager as threadlocal_manager
from pyramid.traversal import DefaultRootFactory
Expand Down Expand Up @@ -77,9 +77,7 @@ def prepare(request=None, registry=None):
request.registry = registry
threadlocals = {'registry':registry, 'request':request}
threadlocal_manager.push(threadlocals)
extensions = registry.queryUtility(IRequestExtensions)
if extensions is not None:
request._set_extensions(extensions)
apply_request_extensions(request)
def closer():
threadlocal_manager.pop()
root_factory = registry.queryUtility(IRootFactory,
Expand Down
45 changes: 44 additions & 1 deletion pyramid/tests/test_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,50 @@ def test_subpath_path_info_and_script_name_have_utf8(self):
self.assertEqual(request.environ['SCRIPT_NAME'], '/' + encoded)
self.assertEqual(request.environ['PATH_INFO'], '/' + encoded)

class DummyRequest:
class Test_apply_request_extensions(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()

def tearDown(self):
testing.tearDown()

def _callFUT(self, request, extensions=None):
from pyramid.request import apply_request_extensions
return apply_request_extensions(request, extensions=extensions)

def test_it_with_registry(self):
from pyramid.interfaces import IRequestExtensions
extensions = Dummy()
extensions.methods = {'foo': lambda x, y: y}
extensions.descriptors = {'bar': property(lambda x: 'bar')}
self.config.registry.registerUtility(extensions, IRequestExtensions)
request = DummyRequest()
request.registry = self.config.registry
self._callFUT(request)
self.assertEqual(request.bar, 'bar')
self.assertEqual(request.foo('abc'), 'abc')

def test_it_override_extensions(self):
from pyramid.interfaces import IRequestExtensions
ignore = Dummy()
ignore.methods = {'x': lambda x, y, z: 'asdf'}
ignore.descriptors = {'bar': property(lambda x: 'asdf')}
self.config.registry.registerUtility(ignore, IRequestExtensions)
request = DummyRequest()
request.registry = self.config.registry

extensions = Dummy()
extensions.methods = {'foo': lambda x, y: y}
extensions.descriptors = {'bar': property(lambda x: 'bar')}
self._callFUT(request, extensions=extensions)
self.assertRaises(AttributeError, lambda: request.x)
self.assertEqual(request.bar, 'bar')
self.assertEqual(request.foo('abc'), 'abc')

class Dummy(object):
pass

class DummyRequest(object):
def __init__(self, environ=None):
if environ is None:
environ = {}
Expand Down
8 changes: 5 additions & 3 deletions pyramid/tests/test_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,18 +317,20 @@ def test_call_with_request_extensions(self):
from pyramid.interfaces import IRequestExtensions
from pyramid.interfaces import IRequest
from pyramid.request import Request
from pyramid.util import InstancePropertyHelper
context = DummyContext()
self._registerTraverserFactory(context)
class Extensions(object):
def __init__(self):
self.methods = {}
self.descriptors = {}
extensions = Extensions()
L = []
ext_method = lambda r: 'bar'
name, fn = InstancePropertyHelper.make_property(ext_method, name='foo')
extensions.descriptors[name] = fn
request = Request.blank('/')
request.request_iface = IRequest
request.registry = self.registry
request._set_extensions = lambda *x: L.extend(x)
def request_factory(environ):
return request
self.registry.registerUtility(extensions, IRequestExtensions)
Expand All @@ -342,7 +344,7 @@ def request_factory(environ):
router.request_factory = request_factory
start_response = DummyStartResponse()
router(environ, start_response)
self.assertEqual(L, [extensions])
self.assertEqual(view.request.foo, 'bar')

def test_call_view_registered_nonspecific_default_path(self):
from pyramid.interfaces import IViewClassifier
Expand Down
16 changes: 11 additions & 5 deletions pyramid/tests/test_scripting.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,15 @@ def test_it_with_request_context_already_set(self):
self.assertEqual(request.context, context)

def test_it_with_extensions(self):
exts = Dummy()
from pyramid.util import InstancePropertyHelper
exts = DummyExtensions()
ext_method = lambda r: 'bar'
name, fn = InstancePropertyHelper.make_property(ext_method, 'foo')
exts.descriptors[name] = fn
request = DummyRequest({})
registry = request.registry = self._makeRegistry([exts, DummyFactory])
info = self._callFUT(request=request, registry=registry)
self.assertEqual(request.extensions, exts)
self.assertEqual(request.foo, 'bar')
root, closer = info['root'], info['closer']
closer()

Expand Down Expand Up @@ -199,11 +203,13 @@ def push(self, item):
def pop(self):
self.popped.append(True)

class DummyRequest:
class DummyRequest(object):
matchdict = None
matched_route = None
def __init__(self, environ):
self.environ = environ

def _set_extensions(self, exts):
self.extensions = exts
class DummyExtensions:
def __init__(self):
self.descriptors = {}
self.methods = {}
Loading

0 comments on commit 04cc91a

Please sign in to comment.