From d0e1bbf3d0faf72bdd93dae1f193c9ec6607a56e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionu=C8=9B=20Ar=C8=9B=C4=83ri=C8=99i?= Date: Mon, 31 Aug 2015 22:58:00 +0100 Subject: [PATCH 1/3] add working test case for explicit params We will later make it fail, but want to show a nice diff in the next commit. --- test_classy/test_decorators.py | 4 +++- test_classy/view_classes.py | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/test_classy/test_decorators.py b/test_classy/test_decorators.py index c346c72..1d1dd9e 100644 --- a/test_classy/test_decorators.py +++ b/test_classy/test_decorators.py @@ -56,4 +56,6 @@ def test_params_decorator_delete(): eq_(b"Params Decorator Delete 1234", resp.data) - +def test_explicit_params_decorator(): + resp = client.get('/decorated/explicit/foo/bar') + eq_(b"Explicit param foobar", resp.data) diff --git a/test_classy/view_classes.py b/test_classy/view_classes.py index 5ad1e3a..e9a82d4 100644 --- a/test_classy/view_classes.py +++ b/test_classy/view_classes.py @@ -186,6 +186,13 @@ def decorated_function(*args, **kwargs): return decorator +def explicit_params_decorator(f): + @wraps(f) + def wrapper(*args, **kwargs): + return f(*args, **kwargs) + return wrapper + + def recursive_decorator(f): @wraps(f) def decorated_view(*args, **kwargs): @@ -253,6 +260,9 @@ def someval(self, val): def anotherval(self, val): return "Anotherval " + val + @explicit_params_decorator + def explicit(self, arg1, arg2): + return "Explicit param " + arg1 + arg2 class InheritanceView(BasicView): From 05f6d408a242c5e5cd44899522633ed9cdbd3207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionu=C8=9B=20Ar=C8=9B=C4=83ri=C8=99i?= Date: Mon, 31 Aug 2015 22:59:42 +0100 Subject: [PATCH 2/3] change explicit_params_decorator so it uses arg1 Use one of the args explicitly in one of the decorators. This makes sure, that the function which tries to get to the bottom of the decorator chain, doesn't just look for the first function with named parameters. --- test_classy/view_classes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_classy/view_classes.py b/test_classy/view_classes.py index e9a82d4..abb843f 100644 --- a/test_classy/view_classes.py +++ b/test_classy/view_classes.py @@ -188,8 +188,8 @@ def decorated_function(*args, **kwargs): def explicit_params_decorator(f): @wraps(f) - def wrapper(*args, **kwargs): - return f(*args, **kwargs) + def wrapper(self, arg1, *args, **kwargs): + return f(self, arg1, *args, **kwargs) return wrapper From 0f4a793c75157c9ddfab4943f231550725aa058b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionu=C8=9B=20Ar=C8=9B=C4=83ri=C8=99i?= Date: Sat, 20 Feb 2016 17:01:09 +0000 Subject: [PATCH 3/3] Use undecorated lib to get true_argspec The undecorated library provides a more robust way of getting access to the original method without any decorators applied. It also supports decorators with explicitly named parameters (which the existing true_argspec method did not support). --- flask_classy.py | 22 ++-------------------- setup.py | 3 ++- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/flask_classy.py b/flask_classy.py index e0c5ccd..9663593 100644 --- a/flask_classy.py +++ b/flask_classy.py @@ -15,6 +15,7 @@ import inspect from werkzeug.routing import parse_rule from flask import request, Response, make_response +from undecorated import undecorated import re _py2 = sys.version_info[0] == 2 @@ -294,26 +295,7 @@ def get_interesting_members(base_class, cls): def get_true_argspec(method): """Drills through layers of decorators attempting to locate the actual argspec for the method.""" - argspec = inspect.getargspec(method) - args = argspec[0] - if args and args[0] == 'self': - return argspec - if hasattr(method, '__func__'): - method = method.__func__ - if not hasattr(method, '__closure__') or method.__closure__ is None: - raise DecoratorCompatibilityError - - closure = method.__closure__ - for cell in closure: - inner_method = cell.cell_contents - if inner_method is method: - continue - if not inspect.isfunction(inner_method) \ - and not inspect.ismethod(inner_method): - continue - true_argspec = get_true_argspec(inner_method) - if true_argspec: - return true_argspec + return inspect.getargspec(undecorated(method)) class DecoratorCompatibilityError(Exception): diff --git a/setup.py b/setup.py index 347cb45..bb6c60f 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ include_package_data=True, platforms='any', install_requires=[ + 'undecorated', 'Flask>=0.9' ], classifiers=[ @@ -37,4 +38,4 @@ 'Topic :: Software Development :: Libraries :: Python Modules' ], test_suite='test_classy' -) \ No newline at end of file +)