Search code examples
flaskurl-routingwerkzeug

How do I sort Flask/Werkzeug routes in the order in which they will be matched?


I have a function that I'm using to list routes for my Flask site and would like to be sure it is sorting them in the order in which they will be matched by Flask/Werkzeug.

Currently I have

def routes(verbose, wide, nostatic):
    """List routes supported by the application"""

    for rule in sorted(app.url_map.iter_rules()):
        if nostatic and rule.endpoint == 'static':
            continue

        if verbose:
            fmt = "{:45s} {:30s} {:30s}" if wide else "{:35s} {:25s} {:25s}"
            line = fmt.format(rule, rule.endpoint, ','.join(rule.methods))
        else:
            fmt = "{:45s}" if wide else "{:35s}"
            line = fmt.format(rule)

        print(line)

but this just seems to sort the routes in the order I've defined them in my code.

How do I sort Flask/Werkzeug routes in the order in which they will be matched?


Another approach: Specifying a host and a path and seeing what rule applies.

    urls = app.url_map.bind(host)
    try:
        m = urls.match(match, "GET")
        z = '{}({})'.format(m[0], ','.join(["{}='{}'".format(arg, m[1][arg]) for arg in m[1]] +
                                           ["host='{}'".format(host)]))
        return z
    except NotFound:
        return

Solution

  • On each request Flask/Werkzeug reordered self.map.update() rules if it need with route weights (see my another answer URL routing conflicts for static files in Flask dev server).

    You can found that Map.update method update _rules (see https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/routing.py#L1313).

    So you can do the same thing:

    sorted(current_app.url_map._rules, key=lambda x: x.match_compare_key())
    

    Or if all routes already loaded and any request done (called Map.build or Map.match) just use app.url_map._rules - it will be sorted. In request app.url_map._rules already sorted, because Map.match was called before dispatcher (match rule with dispatcher).

    You also can found that Map.update called in Map.iter_rules, so you can just use this method current_app.url_map.iter_rules() (see https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/routing.py#L1161).