From 9dbd23b33ecdeae3d93498b764952aeb0fd6560e Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Thu, 20 Nov 2014 19:41:33 -0500 Subject: [PATCH] Improve detection of infinite recursion for PecanHook and pypy. Fixes bug 1394344 Change-Id: I1c33598c4187e92f48691a8b6ef0663b09ce7240 --- pecan/hooks.py | 19 ++++++++++++++++--- pecan/tests/test_hooks.py | 13 +++++++++++++ 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()