forked from orbit-oss/flask
show subdomain or host in routes output
This commit is contained in:
parent
182ce3dd15
commit
84c007d34f
3 changed files with 69 additions and 49 deletions
|
|
@ -44,6 +44,8 @@ Unreleased
|
|||
to set the domain, which modern browsers interpret as an exact match rather than
|
||||
a subdomain match. Warnings about ``localhost`` and IP addresses are also removed.
|
||||
:issue:`5051`
|
||||
- The ``routes`` command shows each rule's ``subdomain`` or ``host`` when domain
|
||||
matching is in use. :issue:`5004`
|
||||
|
||||
|
||||
Version 2.2.4
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import sys
|
|||
import traceback
|
||||
import typing as t
|
||||
from functools import update_wrapper
|
||||
from operator import attrgetter
|
||||
from operator import itemgetter
|
||||
|
||||
import click
|
||||
from click.core import ParameterSource
|
||||
|
|
@ -989,49 +989,62 @@ def shell_command() -> None:
|
|||
@click.option(
|
||||
"--sort",
|
||||
"-s",
|
||||
type=click.Choice(("endpoint", "methods", "rule", "match")),
|
||||
type=click.Choice(("endpoint", "methods", "domain", "rule", "match")),
|
||||
default="endpoint",
|
||||
help=(
|
||||
'Method to sort routes by. "match" is the order that Flask will match '
|
||||
"routes when dispatching a request."
|
||||
"Method to sort routes by. 'match' is the order that Flask will match routes"
|
||||
" when dispatching a request."
|
||||
),
|
||||
)
|
||||
@click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.")
|
||||
@with_appcontext
|
||||
def routes_command(sort: str, all_methods: bool) -> None:
|
||||
"""Show all registered routes with endpoints and methods."""
|
||||
|
||||
rules = list(current_app.url_map.iter_rules())
|
||||
|
||||
if not rules:
|
||||
click.echo("No routes were registered.")
|
||||
return
|
||||
|
||||
ignored_methods = set(() if all_methods else ("HEAD", "OPTIONS"))
|
||||
ignored_methods = set() if all_methods else {"HEAD", "OPTIONS"}
|
||||
host_matching = current_app.url_map.host_matching
|
||||
has_domain = any(rule.host if host_matching else rule.subdomain for rule in rules)
|
||||
rows = []
|
||||
|
||||
if sort in ("endpoint", "rule"):
|
||||
rules = sorted(rules, key=attrgetter(sort))
|
||||
elif sort == "methods":
|
||||
rules = sorted(rules, key=lambda rule: sorted(rule.methods)) # type: ignore
|
||||
for rule in rules:
|
||||
row = [
|
||||
rule.endpoint,
|
||||
", ".join(sorted((rule.methods or set()) - ignored_methods)),
|
||||
]
|
||||
|
||||
rule_methods = [
|
||||
", ".join(sorted(rule.methods - ignored_methods)) # type: ignore
|
||||
for rule in rules
|
||||
]
|
||||
if has_domain:
|
||||
row.append((rule.host if host_matching else rule.subdomain) or "")
|
||||
|
||||
headers = ("Endpoint", "Methods", "Rule")
|
||||
widths = (
|
||||
max(len(rule.endpoint) for rule in rules),
|
||||
max(len(methods) for methods in rule_methods),
|
||||
max(len(rule.rule) for rule in rules),
|
||||
)
|
||||
widths = [max(len(h), w) for h, w in zip(headers, widths)]
|
||||
row = "{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}".format(*widths)
|
||||
row.append(rule.rule)
|
||||
rows.append(row)
|
||||
|
||||
click.echo(row.format(*headers).strip())
|
||||
click.echo(row.format(*("-" * width for width in widths)))
|
||||
headers = ["Endpoint", "Methods"]
|
||||
sorts = ["endpoint", "methods"]
|
||||
|
||||
for rule, methods in zip(rules, rule_methods):
|
||||
click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip())
|
||||
if has_domain:
|
||||
headers.append("Host" if host_matching else "Subdomain")
|
||||
sorts.append("domain")
|
||||
|
||||
headers.append("Rule")
|
||||
sorts.append("rule")
|
||||
|
||||
try:
|
||||
rows.sort(key=itemgetter(sorts.index(sort)))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
rows.insert(0, headers)
|
||||
widths = [max(len(row[i]) for row in rows) for i in range(len(headers))]
|
||||
rows.insert(1, ["-" * w for w in widths])
|
||||
template = " ".join(f"{{{i}:<{w}}}" for i, w in enumerate(widths))
|
||||
|
||||
for row in rows:
|
||||
click.echo(template.format(*row))
|
||||
|
||||
|
||||
cli = FlaskGroup(
|
||||
|
|
|
|||
|
|
@ -433,16 +433,12 @@ class TestRoutes:
|
|||
@pytest.fixture
|
||||
def app(self):
|
||||
app = Flask(__name__)
|
||||
app.testing = True
|
||||
|
||||
@app.route("/get_post/<int:x>/<int:y>", methods=["GET", "POST"])
|
||||
def yyy_get_post(x, y):
|
||||
pass
|
||||
|
||||
@app.route("/zzz_post", methods=["POST"])
|
||||
def aaa_post():
|
||||
pass
|
||||
|
||||
app.add_url_rule(
|
||||
"/get_post/<int:x>/<int:y>",
|
||||
methods=["GET", "POST"],
|
||||
endpoint="yyy_get_post",
|
||||
)
|
||||
app.add_url_rule("/zzz_post", methods=["POST"], endpoint="aaa_post")
|
||||
return app
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -450,17 +446,6 @@ class TestRoutes:
|
|||
cli = FlaskGroup(create_app=lambda: app)
|
||||
return partial(runner.invoke, cli)
|
||||
|
||||
@pytest.fixture
|
||||
def invoke_no_routes(self, runner):
|
||||
def create_app():
|
||||
app = Flask(__name__, static_folder=None)
|
||||
app.testing = True
|
||||
|
||||
return app
|
||||
|
||||
cli = FlaskGroup(create_app=create_app)
|
||||
return partial(runner.invoke, cli)
|
||||
|
||||
def expect_order(self, order, output):
|
||||
# skip the header and match the start of each row
|
||||
for expect, line in zip(order, output.splitlines()[2:]):
|
||||
|
|
@ -493,11 +478,31 @@ class TestRoutes:
|
|||
output = invoke(["routes", "--all-methods"]).output
|
||||
assert "GET, HEAD, OPTIONS, POST" in output
|
||||
|
||||
def test_no_routes(self, invoke_no_routes):
|
||||
result = invoke_no_routes(["routes"])
|
||||
def test_no_routes(self, runner):
|
||||
app = Flask(__name__, static_folder=None)
|
||||
cli = FlaskGroup(create_app=lambda: app)
|
||||
result = runner.invoke(cli, ["routes"])
|
||||
assert result.exit_code == 0
|
||||
assert "No routes were registered." in result.output
|
||||
|
||||
def test_subdomain(self, runner):
|
||||
app = Flask(__name__, static_folder=None)
|
||||
app.add_url_rule("/a", subdomain="a", endpoint="a")
|
||||
app.add_url_rule("/b", subdomain="b", endpoint="b")
|
||||
cli = FlaskGroup(create_app=lambda: app)
|
||||
result = runner.invoke(cli, ["routes"])
|
||||
assert result.exit_code == 0
|
||||
assert "Subdomain" in result.output
|
||||
|
||||
def test_host(self, runner):
|
||||
app = Flask(__name__, static_folder=None, host_matching=True)
|
||||
app.add_url_rule("/a", host="a", endpoint="a")
|
||||
app.add_url_rule("/b", host="b", endpoint="b")
|
||||
cli = FlaskGroup(create_app=lambda: app)
|
||||
result = runner.invoke(cli, ["routes"])
|
||||
assert result.exit_code == 0
|
||||
assert "Host" in result.output
|
||||
|
||||
|
||||
def dotenv_not_available():
|
||||
try:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue