summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2012-11-21 06:26:40 -0500
committerChris McDonough <chrism@plope.com>2012-11-21 06:26:40 -0500
commit28fc3d575107a95a977a049eb38f55c6c422813a (patch)
tree3731da7c279a7be41e8384ad4d568774dbac144f
parentbc527250b318332e275ecb1f547498e36993c680 (diff)
downloadpyramid-28fc3d575107a95a977a049eb38f55c6c422813a.tar.gz
pyramid-28fc3d575107a95a977a049eb38f55c6c422813a.tar.bz2
pyramid-28fc3d575107a95a977a049eb38f55c6c422813a.zip
- In order to normalize the relationship between event subscribers and
subscriber predicates, we now allow both subscribers and subscriber predicates to accept only a single ``event`` argument even if they've been subscribed for notifications that involve multiple interfaces. Subscribers and subscriber predicates that accept only one argument will receive the first object passed to ``notify``; this is typically (but not always) the event object. The other objects involved in the subscription lookup will be discarded. For instance, if an event is sent by code like this:: registry.notify(event, context) In the past, in order to catch such an event, you were obligated to write and register an event subscriber that mentioned both the event and the context in its argument list:: @subscriber([SomeEvent, SomeContextType]) def subscriber(event, context): pass With the event-only feature you can now write an event subscriber that accepts only ``event`` even if it subscribes to multiple interfaces:: @subscriber([SomeEvent, SomeContextType]) def subscriber(event): # this will work! Note, however, that if the event object is not the first object in the call to ``notify``, you'll run into trouble. For example, if notify is called with the context argument first:: registry.notify(context, event) You won't be able to take advantage of the feature. It will "work", but the object received by your event handler won't be the event object, it will be the context object, which won't be very useful:: @subscriber([SomeContextType, SomeEvent]) def subscriber(event): # bzzt! you'll be getting the context here as ``event``, and it'll # be useless Existing multiple-argument subscribers continue to work without issue, so you should continue use those if your system notifies using multiple interfaces and the first interface is not the event interface. For example:: @subscriber([SomeContextType, SomeEvent]) def subscriber(context, event): # this will still work! The event-only feature makes it possible to use a subscriber predicate that accepts only a request argument within both multiple-interface subscriber registrations and single-interface subscriber registrations. In the past, if you had a subscriber predicate like this:: class RequestPathStartsWith(object): def __init__(self, val, config): self.val = val def text(self): return 'path_startswith = %s' % (self.val,) phash = text def __call__(self, event): return event.request.path.startswith(self.val) If you attempted to use the above predicate to condition a subscription that involved multiple interfaces, it would not work. You had to change it to accept the same arguments as the subscription itself. For example, you might have had to change its ``__call__`` method like so, adding a ``context`` argument:: def __call__(self, event, context): return event.request.path.startswith(self.val) With the event-only feature, you needn't make the change. Instead, you can write all predicates so they only accept ``event`` in their ``__call__`` and they'll be useful across all registrations for subscriptions that use an event as their first argument, even ones which accept more than just ``event``. However, the same caveat applies to predicates as to subscriptions: if you're subscribing to a multi-interface event, and the first interface is not the event interface, the predicate won't work properly. In such a case, you'll need to match the predicate ``__call__`` argument ordering and composition to the ordering of the interfaces. For example:: def __call__(self, context, event): return event.request.path.startswith(self.val) tl;dr: 1) Always use the event as the first argument to a multi-interface subscription and 2) Use only ``event`` in your subscriber and subscriber predicate parameter lists, no matter how many interfaces the subscriber is notified with, as long as the event object is the first argument passed to ``registry.notify``. This will result in the maximum amount of reusability of subscriber predicates.
-rw-r--r--CHANGES.txt98
-rw-r--r--pyramid/config/adapters.py78
-rw-r--r--pyramid/config/util.py50
-rw-r--r--pyramid/config/views.py46
-rw-r--r--pyramid/tests/test_config/test_adapters.py9
-rw-r--r--pyramid/tests/test_config/test_util.py186
-rw-r--r--pyramid/tests/test_config/test_views.py190
7 files changed, 416 insertions, 241 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 5344cb7d1..a625af3f9 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -15,6 +15,104 @@ Features
values in parameterized ``.ini`` file, e.g. ``pshell etc/development.ini
http_port=8080``. See https://github.com/Pylons/pyramid/pull/714
+- In order to normalize the relationship between event subscribers and
+ subscriber predicates, we now allow both subscribers and subscriber
+ predicates to accept only a single ``event`` argument even if they've been
+ subscribed for notifications that involve multiple interfaces. Subscribers
+ and subscriber predicates that accept only one argument will receive the
+ first object passed to ``notify``; this is typically (but not always) the
+ event object. The other objects involved in the subscription lookup will be
+ discarded.
+
+ For instance, if an event is sent by code like this::
+
+ registry.notify(event, context)
+
+ In the past, in order to catch such an event, you were obligated to write and
+ register an event subscriber that mentioned both the event and the context in
+ its argument list::
+
+ @subscriber([SomeEvent, SomeContextType])
+ def subscriber(event, context):
+ pass
+
+ With the event-only feature you can now write an event subscriber that
+ accepts only ``event`` even if it subscribes to multiple interfaces::
+
+ @subscriber([SomeEvent, SomeContextType])
+ def subscriber(event):
+ # this will work!
+
+ Note, however, that if the event object is not the first object in the call
+ to ``notify``, you'll run into trouble. For example, if notify is called
+ with the context argument first::
+
+ registry.notify(context, event)
+
+ You won't be able to take advantage of the feature. It will "work", but the
+ object received by your event handler won't be the event object, it will be
+ the context object, which won't be very useful::
+
+ @subscriber([SomeContextType, SomeEvent])
+ def subscriber(event):
+ # bzzt! you'll be getting the context here as ``event``, and it'll
+ # be useless
+
+ Existing multiple-argument subscribers continue to work without issue, so you
+ should continue use those if your system notifies using multiple interfaces
+ and the first interface is not the event interface. For example::
+
+ @subscriber([SomeContextType, SomeEvent])
+ def subscriber(context, event):
+ # this will still work!
+
+ The event-only feature makes it possible to use a subscriber predicate that
+ accepts only a request argument within both multiple-interface subscriber
+ registrations and single-interface subscriber registrations. In the past, if
+ you had a subscriber predicate like this::
+
+ class RequestPathStartsWith(object):
+ def __init__(self, val, config):
+ self.val = val
+
+ def text(self):
+ return 'path_startswith = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, event):
+ return event.request.path.startswith(self.val)
+
+ If you attempted to use the above predicate to condition a subscription that
+ involved multiple interfaces, it would not work. You had to change it to
+ accept the same arguments as the subscription itself. For example, you might
+ have had to change its ``__call__`` method like so, adding a ``context``
+ argument::
+
+ def __call__(self, event, context):
+ return event.request.path.startswith(self.val)
+
+ With the event-only feature, you needn't make the change. Instead, you can
+ write all predicates so they only accept ``event`` in their ``__call__`` and
+ they'll be useful across all registrations for subscriptions that use an
+ event as their first argument, even ones which accept more than just
+ ``event``. However, the same caveat applies to predicates as to
+ subscriptions: if you're subscribing to a multi-interface event, and the
+ first interface is not the event interface, the predicate won't work
+ properly. In such a case, you'll need to match the predicate ``__call__``
+ argument ordering and composition to the ordering of the interfaces. For
+ example::
+
+ def __call__(self, context, event):
+ return event.request.path.startswith(self.val)
+
+ tl;dr: 1) Always use the event as the first argument to a multi-interface
+ subscription and 2) Use only ``event`` in your subscriber and subscriber
+ predicate parameter lists, no matter how many interfaces the subscriber is
+ notified with, as long as the event object is the first argument passed to
+ ``registry.notify``. This will result in the maximum amount of reusability
+ of subscriber predicates.
+
Bug Fixes
---------
diff --git a/pyramid/config/adapters.py b/pyramid/config/adapters.py
index 12c4de660..dad60660a 100644
--- a/pyramid/config/adapters.py
+++ b/pyramid/config/adapters.py
@@ -10,6 +10,7 @@ from pyramid.interfaces import (
from pyramid.config.util import (
action_method,
+ takes_one_arg,
)
@@ -51,8 +52,22 @@ class AdaptersConfiguratorMixin(object):
def register():
predlist = self.get_predlist('subscriber')
order, preds, phash = predlist.make(self, **predicates)
- intr.update({'phash':phash, 'order':order, 'predicates':preds})
- derived_subscriber = self._derive_subscriber(subscriber, preds)
+
+ derived_predicates = [ self._derive_predicate(p) for p in preds ]
+ derived_subscriber = self._derive_subscriber(
+ subscriber,
+ derived_predicates,
+ )
+
+ intr.update(
+ {'phash':phash,
+ 'order':order,
+ 'predicates':preds,
+ 'derived_predicates':derived_predicates,
+ 'derived_subscriber':derived_subscriber,
+ }
+ )
+
self.registry.registerHandler(derived_subscriber, iface)
intr = self.introspectable(
@@ -68,25 +83,54 @@ class AdaptersConfiguratorMixin(object):
self.action(None, register, introspectables=(intr,))
return subscriber
+ def _derive_predicate(self, predicate):
+ derived_predicate = predicate
+
+ if eventonly(predicate):
+ def derived_predicate(*arg):
+ return predicate(arg[0])
+ # seems pointless to try to fix __doc__, __module__, etc as
+ # predicate will invariably be an instance
+
+ return derived_predicate
+
def _derive_subscriber(self, subscriber, predicates):
+ derived_subscriber = subscriber
+
+ if eventonly(subscriber):
+ def derived_subscriber(*arg):
+ return subscriber(arg[0])
+ if hasattr(subscriber, '__name__'):
+ update_wrapper(derived_subscriber, subscriber)
+
if not predicates:
- return subscriber
+ return derived_subscriber
+
def subscriber_wrapper(*arg):
- # We need to accept *arg and pass it along because zope
- # subscribers are designed poorly. Notification will always call
- # an associated subscriber with all of the objects involved in
- # the subscription lookup, despite the fact that the event sender
- # always has the option to attach those objects to the event
- # object itself (and usually does). It would be much saner if the
- # registry just used extra args passed to notify to do the lookup
- # but only called event subscribers with the actual event object,
- # or if we had been smart enough early on to always wrap
- # subscribers in something that threw away the extra args, but
- # c'est la vie.
+ # We need to accept *arg and pass it along because zope subscribers
+ # are designed awkwardly. Notification via
+ # registry.adapter.subscribers will always call an associated
+ # subscriber with all of the objects involved in the subscription
+ # lookup, despite the fact that the event sender always has the
+ # option to attach those objects to the event object itself, and
+ # almost always does.
+ #
+ # The "eventonly" jazz sprinkled in this function and related
+ # functions allows users to define subscribers and predicates which
+ # accept only an event argument without needing to accept the rest
+ # of the adaptation arguments. Had I been smart enough early on to
+ # use .subscriptions to find the subscriber functions in order to
+ # call them manually with a single "event" argument instead of
+ # relying on .subscribers to both find and call them implicitly
+ # with all args, the eventonly hack would not have been required.
+ # At this point, though, using .subscriptions and manual execution
+ # is not possible without badly breaking backwards compatibility.
if all((predicate(*arg) for predicate in predicates)):
- return subscriber(*arg)
+ return derived_subscriber(*arg)
+
if hasattr(subscriber, '__name__'):
update_wrapper(subscriber_wrapper, subscriber)
+
return subscriber_wrapper
@action_method
@@ -266,7 +310,7 @@ class AdaptersConfiguratorMixin(object):
if resource_iface is None:
resource_iface = Interface
self.registry.registerAdapter(
- adapter,
+ adapter,
(resource_iface, Interface),
IResourceURL,
)
@@ -281,3 +325,5 @@ class AdaptersConfiguratorMixin(object):
intr['resource_iface'] = resource_iface
self.action(discriminator, register, introspectables=(intr,))
+def eventonly(callee):
+ return takes_one_arg(callee, argname='event')
diff --git a/pyramid/config/util.py b/pyramid/config/util.py
index a83e23798..af0dd1641 100644
--- a/pyramid/config/util.py
+++ b/pyramid/config/util.py
@@ -1,10 +1,12 @@
from hashlib import md5
+import inspect
from pyramid.compat import (
bytes_,
is_nonstr_iter,
)
+from pyramid.compat import im_func
from pyramid.exceptions import ConfigurationError
from pyramid.registry import predvalseq
@@ -110,3 +112,51 @@ class PredicateList(object):
order = (MAX_ORDER - score) / (len(preds) + 1)
return order, preds, phash.hexdigest()
+def takes_one_arg(callee, attr=None, argname=None):
+ ismethod = False
+ if attr is None:
+ attr = '__call__'
+ if inspect.isroutine(callee):
+ fn = callee
+ elif inspect.isclass(callee):
+ try:
+ fn = callee.__init__
+ except AttributeError:
+ return False
+ ismethod = hasattr(fn, '__call__')
+ else:
+ try:
+ fn = getattr(callee, attr)
+ except AttributeError:
+ return False
+
+ try:
+ argspec = inspect.getargspec(fn)
+ except TypeError:
+ return False
+
+ args = argspec[0]
+
+ if hasattr(fn, im_func) or ismethod:
+ # it's an instance method (or unbound method on py2)
+ if not args:
+ return False
+ args = args[1:]
+
+ if not args:
+ return False
+
+ if len(args) == 1:
+ return True
+
+ if argname:
+
+ defaults = argspec[3]
+ if defaults is None:
+ defaults = ()
+
+ if args[0] == argname:
+ if len(args) - len(defaults) == 1:
+ return True
+
+ return False
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index 4e5af480d..d1b69566b 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -81,6 +81,7 @@ import pyramid.config.predicates
from pyramid.config.util import (
DEFAULT_PHASH,
MAX_ORDER,
+ takes_one_arg,
)
urljoin = urlparse.urljoin
@@ -503,50 +504,7 @@ class DefaultViewMapper(object):
return _attr_view
def requestonly(view, attr=None):
- ismethod = False
- if attr is None:
- attr = '__call__'
- if inspect.isroutine(view):
- fn = view
- elif inspect.isclass(view):
- try:
- fn = view.__init__
- except AttributeError:
- return False
- ismethod = hasattr(fn, '__call__')
- else:
- try:
- fn = getattr(view, attr)
- except AttributeError:
- return False
-
- try:
- argspec = inspect.getargspec(fn)
- except TypeError:
- return False
-
- args = argspec[0]
-
- if hasattr(fn, im_func) or ismethod:
- # it's an instance method (or unbound method on py2)
- if not args:
- return False
- args = args[1:]
- if not args:
- return False
-
- if len(args) == 1:
- return True
-
- defaults = argspec[3]
- if defaults is None:
- defaults = ()
-
- if args[0] == 'request':
- if len(args) - len(defaults) == 1:
- return True
-
- return False
+ return takes_one_arg(view, attr=attr, argname='request')
@implementer(IMultiView)
class MultiView(object):
diff --git a/pyramid/tests/test_config/test_adapters.py b/pyramid/tests/test_config/test_adapters.py
index d47e012dc..4cbb1bf80 100644
--- a/pyramid/tests/test_config/test_adapters.py
+++ b/pyramid/tests/test_config/test_adapters.py
@@ -331,6 +331,15 @@ class AdaptersConfiguratorMixinTests(unittest.TestCase):
self.assertEqual(intr['adapter'], DummyResourceURL)
self.assertEqual(intr['resource_iface'], DummyIface)
+class Test_eventonly(unittest.TestCase):
+ def _callFUT(self, callee):
+ from pyramid.config.adapters import eventonly
+ return eventonly(callee)
+
+ def test_defaults(self):
+ def acallable(event, a=1, b=2): pass
+ self.assertTrue(self._callFUT(acallable))
+
class DummyTraverser(object):
def __init__(self, root):
self.root = root
diff --git a/pyramid/tests/test_config/test_util.py b/pyramid/tests/test_config/test_util.py
index b32f9c6ef..a984acfd0 100644
--- a/pyramid/tests/test_config/test_util.py
+++ b/pyramid/tests/test_config/test_util.py
@@ -365,6 +365,192 @@ class TestPredicateList(unittest.TestCase):
from pyramid.exceptions import ConfigurationError
self.assertRaises(ConfigurationError, self._callFUT, unknown=1)
+class Test_takes_one_arg(unittest.TestCase):
+ def _callFUT(self, view, attr=None, argname=None):
+ from pyramid.config.util import takes_one_arg
+ return takes_one_arg(view, attr=attr, argname=argname)
+
+ def test_requestonly_newstyle_class_no_init(self):
+ class foo(object):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_requestonly_newstyle_class_init_toomanyargs(self):
+ class foo(object):
+ def __init__(self, context, request):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_requestonly_newstyle_class_init_onearg_named_request(self):
+ class foo(object):
+ def __init__(self, request):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_newstyle_class_init_onearg_named_somethingelse(self):
+ class foo(object):
+ def __init__(self, req):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_newstyle_class_init_defaultargs_firstname_not_request(self):
+ class foo(object):
+ def __init__(self, context, request=None):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_newstyle_class_init_defaultargs_firstname_request(self):
+ class foo(object):
+ def __init__(self, request, foo=1, bar=2):
+ """ """
+ self.assertTrue(self._callFUT(foo, argname='request'))
+
+ def test_newstyle_class_init_firstname_request_with_secondname(self):
+ class foo(object):
+ def __init__(self, request, two):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_newstyle_class_init_noargs(self):
+ class foo(object):
+ def __init__():
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_oldstyle_class_no_init(self):
+ class foo:
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_oldstyle_class_init_toomanyargs(self):
+ class foo:
+ def __init__(self, context, request):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_oldstyle_class_init_onearg_named_request(self):
+ class foo:
+ def __init__(self, request):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_oldstyle_class_init_onearg_named_somethingelse(self):
+ class foo:
+ def __init__(self, req):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_oldstyle_class_init_defaultargs_firstname_not_request(self):
+ class foo:
+ def __init__(self, context, request=None):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_oldstyle_class_init_defaultargs_firstname_request(self):
+ class foo:
+ def __init__(self, request, foo=1, bar=2):
+ """ """
+ self.assertTrue(self._callFUT(foo, argname='request'), True)
+
+ def test_oldstyle_class_init_noargs(self):
+ class foo:
+ def __init__():
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_function_toomanyargs(self):
+ def foo(context, request):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_function_with_attr_false(self):
+ def bar(context, request):
+ """ """
+ def foo(context, request):
+ """ """
+ foo.bar = bar
+ self.assertFalse(self._callFUT(foo, 'bar'))
+
+ def test_function_with_attr_true(self):
+ def bar(context, request):
+ """ """
+ def foo(request):
+ """ """
+ foo.bar = bar
+ self.assertTrue(self._callFUT(foo, 'bar'))
+
+ def test_function_onearg_named_request(self):
+ def foo(request):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_function_onearg_named_somethingelse(self):
+ def foo(req):
+ """ """
+ self.assertTrue(self._callFUT(foo))
+
+ def test_function_defaultargs_firstname_not_request(self):
+ def foo(context, request=None):
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_function_defaultargs_firstname_request(self):
+ def foo(request, foo=1, bar=2):
+ """ """
+ self.assertTrue(self._callFUT(foo, argname='request'))
+
+ def test_function_noargs(self):
+ def foo():
+ """ """
+ self.assertFalse(self._callFUT(foo))
+
+ def test_instance_toomanyargs(self):
+ class Foo:
+ def __call__(self, context, request):
+ """ """
+ foo = Foo()
+ self.assertFalse(self._callFUT(foo))
+
+ def test_instance_defaultargs_onearg_named_request(self):
+ class Foo:
+ def __call__(self, request):
+ """ """
+ foo = Foo()
+ self.assertTrue(self._callFUT(foo))
+
+ def test_instance_defaultargs_onearg_named_somethingelse(self):
+ class Foo:
+ def __call__(self, req):
+ """ """
+ foo = Foo()
+ self.assertTrue(self._callFUT(foo))
+
+ def test_instance_defaultargs_firstname_not_request(self):
+ class Foo:
+ def __call__(self, context, request=None):
+ """ """
+ foo = Foo()
+ self.assertFalse(self._callFUT(foo))
+
+ def test_instance_defaultargs_firstname_request(self):
+ class Foo:
+ def __call__(self, request, foo=1, bar=2):
+ """ """
+ foo = Foo()
+ self.assertTrue(self._callFUT(foo, argname='request'), True)
+
+ def test_instance_nocall(self):
+ class Foo: pass
+ foo = Foo()
+ self.assertFalse(self._callFUT(foo))
+
+ def test_method_onearg_named_request(self):
+ class Foo:
+ def method(self, request):
+ """ """
+ foo = Foo()
+ self.assertTrue(self._callFUT(foo.method))
+
class DummyCustomPredicate(object):
def __init__(self):
diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py
index bb4c5d519..4cebdce8a 100644
--- a/pyramid/tests/test_config/test_views.py
+++ b/pyramid/tests/test_config/test_views.py
@@ -1896,192 +1896,20 @@ class TestViewsConfigurationMixin(unittest.TestCase):
from pyramid.tests import test_config
self.assertEqual(result, test_config)
-
class Test_requestonly(unittest.TestCase):
def _callFUT(self, view, attr=None):
from pyramid.config.views import requestonly
- return requestonly(view, attr)
-
- def test_requestonly_newstyle_class_no_init(self):
- class foo(object):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_requestonly_newstyle_class_init_toomanyargs(self):
- class foo(object):
- def __init__(self, context, request):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_requestonly_newstyle_class_init_onearg_named_request(self):
- class foo(object):
- def __init__(self, request):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_newstyle_class_init_onearg_named_somethingelse(self):
- class foo(object):
- def __init__(self, req):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_newstyle_class_init_defaultargs_firstname_not_request(self):
- class foo(object):
- def __init__(self, context, request=None):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_newstyle_class_init_defaultargs_firstname_request(self):
- class foo(object):
- def __init__(self, request, foo=1, bar=2):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_newstyle_class_init_firstname_request_with_secondname(self):
- class foo(object):
- def __init__(self, request, two):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_newstyle_class_init_noargs(self):
- class foo(object):
- def __init__():
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_oldstyle_class_no_init(self):
- class foo:
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_oldstyle_class_init_toomanyargs(self):
- class foo:
- def __init__(self, context, request):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_oldstyle_class_init_onearg_named_request(self):
- class foo:
- def __init__(self, request):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_oldstyle_class_init_onearg_named_somethingelse(self):
- class foo:
- def __init__(self, req):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_oldstyle_class_init_defaultargs_firstname_not_request(self):
- class foo:
- def __init__(self, context, request=None):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_oldstyle_class_init_defaultargs_firstname_request(self):
- class foo:
- def __init__(self, request, foo=1, bar=2):
- """ """
- self.assertTrue(self._callFUT(foo), True)
-
- def test_oldstyle_class_init_noargs(self):
- class foo:
- def __init__():
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_function_toomanyargs(self):
- def foo(context, request):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_function_with_attr_false(self):
- def bar(context, request):
- """ """
- def foo(context, request):
- """ """
- foo.bar = bar
- self.assertFalse(self._callFUT(foo, 'bar'))
-
- def test_function_with_attr_true(self):
- def bar(context, request):
- """ """
- def foo(request):
- """ """
- foo.bar = bar
- self.assertTrue(self._callFUT(foo, 'bar'))
-
- def test_function_onearg_named_request(self):
- def foo(request):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_function_onearg_named_somethingelse(self):
- def foo(req):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_function_defaultargs_firstname_not_request(self):
- def foo(context, request=None):
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_function_defaultargs_firstname_request(self):
- def foo(request, foo=1, bar=2):
- """ """
- self.assertTrue(self._callFUT(foo))
-
- def test_function_noargs(self):
- def foo():
- """ """
- self.assertFalse(self._callFUT(foo))
-
- def test_instance_toomanyargs(self):
- class Foo:
- def __call__(self, context, request):
- """ """
- foo = Foo()
- self.assertFalse(self._callFUT(foo))
+ return requestonly(view, attr=attr)
- def test_instance_defaultargs_onearg_named_request(self):
- class Foo:
- def __call__(self, request):
- """ """
- foo = Foo()
- self.assertTrue(self._callFUT(foo))
-
- def test_instance_defaultargs_onearg_named_somethingelse(self):
- class Foo:
- def __call__(self, req):
- """ """
- foo = Foo()
- self.assertTrue(self._callFUT(foo))
-
- def test_instance_defaultargs_firstname_not_request(self):
- class Foo:
- def __call__(self, context, request=None):
- """ """
- foo = Foo()
- self.assertFalse(self._callFUT(foo))
-
- def test_instance_defaultargs_firstname_request(self):
- class Foo:
- def __call__(self, request, foo=1, bar=2):
- """ """
- foo = Foo()
- self.assertTrue(self._callFUT(foo), True)
+ def test_defaults(self):
+ def aview(request, a=1, b=2): pass
+ self.assertTrue(self._callFUT(aview))
- def test_instance_nocall(self):
- class Foo: pass
- foo = Foo()
- self.assertFalse(self._callFUT(foo))
-
- def test_method_onearg_named_request(self):
- class Foo:
- def method(self, request):
- """ """
- foo = Foo()
- self.assertTrue(self._callFUT(foo.method))
+ def test_otherattr(self):
+ class AView(object):
+ def __init__(self, request, a=1, b=2): pass
+ def bleh(self): pass
+ self.assertTrue(self._callFUT(AView, 'bleh'))
class Test_isexception(unittest.TestCase):
def _callFUT(self, ob):