2 回答
TA贡献1828条经验 获得超3个赞
我正在添加另一个答案,因为现在我有一些最少的 hacky(阅读:我正在使用检查来读取给定函数的源代码,而不是自己读取整个文件),跨模块工作,并且可以重用于任何其他应该始终是最后一个的装饰器。您也不必app.route像在我的其他答案的更新中那样使用不同的语法。
这是如何做到这一点(警告:这是一个非常封闭的开始):
import flask
import inspect
class DecoratorOrderError(TypeError):
pass
def assert_last_decorator(final_decorator):
"""
Converts a decorator so that an exception is raised when it is not the last decorator to be used on a function.
This only works for decorator syntax, not if somebody explicitly uses the decorator, e.g.
final_decorator = some_other_decorator(final_decorator) will still work without an exception.
:param final_decorator: The decorator that should be made final.
:return: The same decorator, but it checks that it is the last one before calling the inner function.
"""
def check_decorator_order(func):
# Use inspect to read the code of the function
code, _ = inspect.getsourcelines(func)
decorators = []
for line in code:
if line.startswith("@"):
decorators.append(line)
else:
break
# Remove the "@", function calls, and any object calls, such as "app.route". We just want the name of the decorator function (e.g. "route")
decorator_names_only = [dec.replace("@", "").split("(")[0].split(".")[-1] for dec in decorators]
is_final_decorator = [final_decorator.__name__ == name for name in decorator_names_only]
num_finals = sum(is_final_decorator)
if num_finals > 1 or (num_finals == 1 and not is_final_decorator[0]):
raise DecoratorOrderError(f"'{final_decorator.__name__}' is not the topmost decorator of function '{func.__name__}'")
return func
def handle_arguments(*args, **kwargs):
# Used to pass the arguments to the final decorator
def handle_function(f):
# Which function should be decorated by the final decorator?
return final_decorator(*args, **kwargs)(check_decorator_order(f))
return handle_function
return handle_arguments
您现在可以app.route使用应用于该函数的该函数替换该app.route函数。这很重要,必须在使用app.route装饰器之前完成,所以我建议在创建应用程序时执行。
app = flask.Flask(__name__)
app.route = assert_last_decorator(app.route)
def require_administrator(func):
@functools.wraps(func)
def has_administrator(*args, **kwargs):
print("Would check admin now")
return func(*args, **kwargs)
return has_administrator
@app.route("/good", methods=["GET"]) # Works
@require_administrator
def test_good():
return "ok"
@require_administrator
@app.route("/bad", methods=["GET"]) # Raises an Exception
def test_bad():
return "not ok"
我相信这几乎是您在问题中想要的。
添加回答
举报