From bcdf0ad017168408060b6faab156636ced4c94f7 Mon Sep 17 00:00:00 2001 From: Alex Oleshkevich Date: Sun, 16 Mar 2025 13:18:00 +0100 Subject: [PATCH] fix IndexError in TemplateResponse (#2909) --- starlette/templating.py | 6 +- tests/test_templates.py | 121 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/starlette/templating.py b/starlette/templating.py index 6b01aac9..f764858b 100644 --- a/starlette/templating.py +++ b/starlette/templating.py @@ -168,9 +168,9 @@ class Jinja2Templates: name = args[0] context = args[1] if len(args) > 1 else kwargs.get("context", {}) status_code = args[2] if len(args) > 2 else kwargs.get("status_code", 200) - headers = args[2] if len(args) > 2 else kwargs.get("headers") - media_type = args[3] if len(args) > 3 else kwargs.get("media_type") - background = args[4] if len(args) > 4 else kwargs.get("background") + headers = args[3] if len(args) > 3 else kwargs.get("headers") + media_type = args[4] if len(args) > 4 else kwargs.get("media_type") + background = args[5] if len(args) > 5 else kwargs.get("background") if "request" not in context: raise ValueError('context must include a "request" key') diff --git a/tests/test_templates.py b/tests/test_templates.py index 6b2080c1..e182cb82 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -279,6 +279,127 @@ def test_templates_warns_when_first_argument_isnot_request( spy.assert_called() +class TestTemplatesArgsOnly: + # MAINTAINERS: remove after 1.0 + def test_name_and_context(self, tmpdir: Path, test_client_factory: TestClientFactory) -> None: + path = os.path.join(tmpdir, "index.html") + with open(path, "w") as file: + file.write("value: {{ a }}") + templates = Jinja2Templates(directory=str(tmpdir)) + + def page(request: Request) -> Response: + return templates.TemplateResponse( + "index.html", + {"a": "b", "request": request}, + ) + + app = Starlette(routes=[Route("/", page)]) + client = test_client_factory(app) + with pytest.warns(DeprecationWarning): + response = client.get("/") + + assert response.text == "value: b" # context was rendered + assert response.status_code == 200 + + def test_status_code(self, tmpdir: Path, test_client_factory: TestClientFactory) -> None: + path = os.path.join(tmpdir, "index.html") + with open(path, "w") as file: + file.write("value: {{ a }}") + templates = Jinja2Templates(directory=str(tmpdir)) + + def page(request: Request) -> Response: + return templates.TemplateResponse( + "index.html", + {"a": "b", "request": request}, + 201, + ) + + app = Starlette(routes=[Route("/", page)]) + client = test_client_factory(app) + with pytest.warns(DeprecationWarning): + response = client.get("/") + + assert response.text == "value: b" # context was rendered + assert response.status_code == 201 + + def test_headers(self, tmpdir: Path, test_client_factory: TestClientFactory) -> None: + path = os.path.join(tmpdir, "index.html") + with open(path, "w") as file: + file.write("value: {{ a }}") + templates = Jinja2Templates(directory=str(tmpdir)) + + def page(request: Request) -> Response: + return templates.TemplateResponse( + "index.html", + {"a": "b", "request": request}, + 201, + {"x-key": "value"}, + ) + + app = Starlette(routes=[Route("/", page)]) + client = test_client_factory(app) + with pytest.warns(DeprecationWarning): + response = client.get("/") + + assert response.text == "value: b" # context was rendered + assert response.status_code == 201 + assert response.headers["x-key"] == "value" + + def test_media_type(self, tmpdir: Path, test_client_factory: TestClientFactory) -> None: + path = os.path.join(tmpdir, "index.html") + with open(path, "w") as file: + file.write("value: {{ a }}") + templates = Jinja2Templates(directory=str(tmpdir)) + + def page(request: Request) -> Response: + return templates.TemplateResponse( + "index.html", + {"a": "b", "request": request}, + 201, + {"x-key": "value"}, + "text/plain", + ) + + app = Starlette(routes=[Route("/", page)]) + client = test_client_factory(app) + with pytest.warns(DeprecationWarning): + response = client.get("/") + + assert response.text == "value: b" # context was rendered + assert response.status_code == 201 + assert response.headers["x-key"] == "value" + assert response.headers["content-type"] == "text/plain; charset=utf-8" + + def test_all_args(self, tmpdir: Path, test_client_factory: TestClientFactory) -> None: + path = os.path.join(tmpdir, "index.html") + with open(path, "w") as file: + file.write("value: {{ a }}") + templates = Jinja2Templates(directory=str(tmpdir)) + + spy = mock.MagicMock() + + def page(request: Request) -> Response: + return templates.TemplateResponse( + "index.html", + {"a": "b", "request": request}, + 201, + {"x-key": "value"}, + "text/plain", + BackgroundTask(func=spy), + ) + + app = Starlette(routes=[Route("/", page)]) + client = test_client_factory(app) + with pytest.warns(DeprecationWarning): + response = client.get("/") + + assert response.text == "value: b" # context was rendered + assert response.status_code == 201 + assert response.headers["x-key"] == "value" + assert response.headers["content-type"] == "text/plain; charset=utf-8" + spy.assert_called() + + def test_templates_when_first_argument_is_request(tmpdir: Path, test_client_factory: TestClientFactory) -> None: # MAINTAINERS: remove after 1.0 path = os.path.join(tmpdir, "index.html") -- 2.47.2