Skip to content

Commit

Permalink
blueprint name may not contain a dot
Browse files Browse the repository at this point in the history
  • Loading branch information
davidism committed May 13, 2021
1 parent d8c37f4 commit 7c52614
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 85 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Expand Up @@ -15,6 +15,9 @@ Unreleased
- Fix some types that weren't available in Python 3.6.0. :issue:`4040`
- Improve typing for ``send_file``, ``send_from_directory``, and
``get_send_file_max_age``. :issue:`4044`, :pr:`4026`
- Show an error when a blueprint name contains a dot. The ``.`` has
special meaning, it is used to separate (nested) blueprint names and
the endpoint name. :issue:`4041`


Version 2.0.0
Expand Down
16 changes: 10 additions & 6 deletions src/flask/blueprints.py
Expand Up @@ -188,6 +188,10 @@ def __init__(
template_folder=template_folder,
root_path=root_path,
)

if "." in name:
raise ValueError("'name' may not contain a dot '.' character.")

self.name = name
self.url_prefix = url_prefix
self.subdomain = subdomain
Expand Down Expand Up @@ -360,12 +364,12 @@ def add_url_rule(
"""Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
the :func:`url_for` function is prefixed with the name of the blueprint.
"""
if endpoint:
assert "." not in endpoint, "Blueprint endpoints should not contain dots"
if view_func and hasattr(view_func, "__name__"):
assert (
"." not in view_func.__name__
), "Blueprint view function name should not contain dots"
if endpoint and "." in endpoint:
raise ValueError("'endpoint' may not contain a dot '.' character.")

if view_func and hasattr(view_func, "__name__") and "." in view_func.__name__:
raise ValueError("'view_func' name may not contain a dot '.' character.")

self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))

def app_template_filter(self, name: t.Optional[str] = None) -> t.Callable:
Expand Down
6 changes: 3 additions & 3 deletions tests/test_basic.py
Expand Up @@ -1631,7 +1631,7 @@ def something_else():


def test_inject_blueprint_url_defaults(app):
bp = flask.Blueprint("foo.bar.baz", __name__, template_folder="template")
bp = flask.Blueprint("foo", __name__, template_folder="template")

@bp.url_defaults
def bp_defaults(endpoint, values):
Expand All @@ -1644,12 +1644,12 @@ def view(page):
app.register_blueprint(bp)

values = dict()
app.inject_url_defaults("foo.bar.baz.view", values)
app.inject_url_defaults("foo.view", values)
expected = dict(page="login")
assert values == expected

with app.test_request_context("/somepage"):
url = flask.url_for("foo.bar.baz.view")
url = flask.url_for("foo.view")
expected = "/login"
assert url == expected

Expand Down
88 changes: 12 additions & 76 deletions tests/test_blueprints.py
@@ -1,5 +1,3 @@
import functools

import pytest
from jinja2 import TemplateNotFound
from werkzeug.http import parse_cache_control_header
Expand Down Expand Up @@ -253,28 +251,9 @@ def test_templates_list(test_apps):
assert templates == ["admin/index.html", "frontend/index.html"]


def test_dotted_names(app, client):
frontend = flask.Blueprint("myapp.frontend", __name__)
backend = flask.Blueprint("myapp.backend", __name__)

@frontend.route("/fe")
def frontend_index():
return flask.url_for("myapp.backend.backend_index")

@frontend.route("/fe2")
def frontend_page2():
return flask.url_for(".frontend_index")

@backend.route("/be")
def backend_index():
return flask.url_for("myapp.frontend.frontend_index")

app.register_blueprint(frontend)
app.register_blueprint(backend)

assert client.get("/fe").data.strip() == b"/be"
assert client.get("/fe2").data.strip() == b"/fe"
assert client.get("/be").data.strip() == b"/fe"
def test_dotted_name_not_allowed(app, client):
with pytest.raises(ValueError):
flask.Blueprint("app.ui", __name__)


def test_dotted_names_from_app(app, client):
Expand Down Expand Up @@ -343,62 +322,19 @@ def index():
def test_route_decorator_custom_endpoint_with_dots(app, client):
bp = flask.Blueprint("bp", __name__)

@bp.route("/foo")
def foo():
return flask.request.endpoint

try:

@bp.route("/bar", endpoint="bar.bar")
def foo_bar():
return flask.request.endpoint

except AssertionError:
pass
else:
raise AssertionError("expected AssertionError not raised")

try:

@bp.route("/bar/123", endpoint="bar.123")
def foo_bar_foo():
return flask.request.endpoint

except AssertionError:
pass
else:
raise AssertionError("expected AssertionError not raised")

def foo_foo_foo():
pass

pytest.raises(
AssertionError,
lambda: bp.add_url_rule("/bar/123", endpoint="bar.123", view_func=foo_foo_foo),
)

pytest.raises(
AssertionError, bp.route("/bar/123", endpoint="bar.123"), lambda: None
)

foo_foo_foo.__name__ = "bar.123"
with pytest.raises(ValueError):
bp.route("/", endpoint="a.b")(lambda: "")

pytest.raises(
AssertionError, lambda: bp.add_url_rule("/bar/123", view_func=foo_foo_foo)
)
with pytest.raises(ValueError):
bp.add_url_rule("/", endpoint="a.b")

bp.add_url_rule(
"/bar/456", endpoint="foofoofoo", view_func=functools.partial(foo_foo_foo)
)
def view():
return ""

app.register_blueprint(bp, url_prefix="/py")
view.__name__ = "a.b"

assert client.get("/py/foo").data == b"bp.foo"
# The rule's didn't actually made it through
rv = client.get("/py/bar")
assert rv.status_code == 404
rv = client.get("/py/bar/123")
assert rv.status_code == 404
with pytest.raises(ValueError):
bp.add_url_rule("/", view_func=view)


def test_endpoint_decorator(app, client):
Expand Down

0 comments on commit 7c52614

Please sign in to comment.