One way to go around this is to cheat the rules sorting algorithm by spoofing the registered rule's match_compare_key()
method. Note that this hack only works with routes that have been registered directly with app.route()
(the Flask object), not with Blueprints. Blueprints' routes are added to the global url Map only upon blueprint's registration on the main app, making it challenging to modify the generated rules.
# an ordinary route
@app.route('/<var1>/<var2>/<var3>')
def some_view(var1, var2, var3):
pass
# let's find the rule that was just generated
rule = app.url_map._rules[-1]
# we create some comparison keys:
# increase probability that the rule will be near or at the top
top_compare_key = False, -100, [(-2, 0)]
# increase probability that the rule will be near or at the bottom
bottom_compare_key = True, 100, [(2, 0)]
# rig rule.match_compare_key() to return the spoofed compare_key
rule.match_compare_key = lambda: top_compare_key
Note that in this case the resulting spoofed function is not bound to the rule object. Therefore upon calling rule.match_compare_key()
, the function does not receive a self
argument. If you want to bind the function properly, do this instead:
spoof = lambda self: top_compare_key
rule.match_compare_key = spoof.__get__(rule, type(rule))
We can generalize the above with a decorator
def weighted_route(*args, **kwargs):
def decorator(view_func):
compare_key = kwargs.pop('compare_key', None)
# register view_func with route
app.route(*args, **kwargs)(view_func)
if compare_key is not None:
rule = app.url_map._rules[-1]
rule.match_compare_key = lambda: compare_key
return view_func
return decorator
# can be used like @app.route(). To weight the rule, just provide
# the `compare_key` param.
@weighted_route('/<var1>/<var2>/<var3>', compare_key=bottom_compare_key)
def some_view(var1, var2, var3):
pass
The same hack implemented as a context manager.
import contextlib
@contextlib.contextmanager
def weighted_route(compare_key=None):
yield
if compare_key is not None:
rule = app.url_map._rules[-1]
rule.match_compare_key = lambda: compare_key
# and to use
with weighted_route(compare_key):
@app.route('/<var1>/<var2>/<var3>')
def some_view(var1, var2, var3):
pass