summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Petrello <lists@ryanpetrello.com>2014-11-20 19:41:33 -0500
committerRyan Petrello <lists@ryanpetrello.com>2014-11-20 21:40:35 -0500
commit9dbd23b33ecdeae3d93498b764952aeb0fd6560e (patch)
treebfcc56036c47aef7cbfbbf9d3f6b76ed3b504a6d
parent0fe902734d5f17d5f224e31850c5670e8a03e1ec (diff)
downloadpecan-9dbd23b33ecdeae3d93498b764952aeb0fd6560e.tar.gz
Improve detection of infinite recursion for PecanHook and pypy.
Fixes bug 1394344 Change-Id: I1c33598c4187e92f48691a8b6ef0663b09ce7240
-rw-r--r--pecan/hooks.py19
-rw-r--r--pecan/tests/test_hooks.py13
2 files changed, 29 insertions, 3 deletions
diff --git a/pecan/hooks.py b/pecan/hooks.py
index f1f7073..0b666d3 100644
--- a/pecan/hooks.py
+++ b/pecan/hooks.py
@@ -2,6 +2,7 @@ import types
import sys
from inspect import getmembers
+import six
from webob.exc import HTTPFound
from .util import iscontroller, _cfg
@@ -12,8 +13,20 @@ __all__ = [
]
-def walk_controller(root_class, controller, hooks):
- if not isinstance(controller, (int, dict)):
+def walk_controller(root_class, controller, hooks, seen=None):
+ seen = seen or set()
+ if type(controller) not in vars(six.moves.builtins).values():
+ # Avoid recursion loops
+ try:
+ if controller in seen:
+ return
+ seen.add(controller)
+ except TypeError:
+ # If we discover an unhashable item (like a list), it's not
+ # something that we want to traverse because it's not the sort of
+ # thing we would add a hook to
+ return
+
for hook in getattr(controller, '__hooks__', []):
# Append hooks from controller class definition
hooks.add(hook)
@@ -38,7 +51,7 @@ def walk_controller(root_class, controller, hooks):
value.im_class.mro()[1:]))
):
continue
- walk_controller(root_class, value, hooks)
+ walk_controller(root_class, value, hooks, seen)
class HookControllerMeta(type):
diff --git a/pecan/tests/test_hooks.py b/pecan/tests/test_hooks.py
index d3fe05b..a15368e 100644
--- a/pecan/tests/test_hooks.py
+++ b/pecan/tests/test_hooks.py
@@ -1681,6 +1681,19 @@ class TestRestControllerWithHooks(PecanTestCase):
def get_all(self):
return 'Hello, World!'
+ @staticmethod
+ def static(cls):
+ return 'static'
+
+ @property
+ def foo(self):
+ return 'bar'
+
+ def testing123(self):
+ return 'bar'
+
+ unhashable = [1, 'two', 3]
+
app = TestApp(
make_app(
RootController()