Skip to content

Commit

Permalink
Make from_string() and get_template() return backend-specific tem…
Browse files Browse the repository at this point in the history
…plate (#2400)

* Annotate `context` argument of `make_context()`

The implementation of `make_context()` explicitly rejects everything
other than `dict` or `None` by raising `TypeError`.

* Drop `Context` from allowed types of template render method

[DEP 182](https://github.com/django/deps/blob/main/final/0182-multiple-template-engines.rst#backends-api)
states: "If `context` is provided, it must be a `dict`.".

While the `Jinja2` and `TemplateStrings` backends support arbitrary
mappings, `DjangoTemplates` calls `make_context()`, which only
supports `dict` (and `None`).

* Do not require template render method to return a `SafeString`

Only the Django Template Language backend outputs `SafeString`.

* Provide arguments types for Jinja2 template render method

All of the argument types are Django types, so there is no need to
avoid annotating them. Only the Jinja2 native template type is
unknown to us.

* Allow non-string values in dummy template render context

The implementation of the `render()` method uses the standard library
method `string.Template.safe_substitute()` to do the actual formatting,
which accepts other value types than `str` and simply calls `__str__()`
on them.

The `safe_substitute()` method is annotated in typeshed with `object`
as the value type for the mapping. However, as `_EngineTemplate` uses
`Any` instead, I went with that.

* Make it explicit that the dummy template class adheres to the protocol

* Make `from_string()` and `get_template()` return backend-specific template

* Revert "Make it explicit that the dummy template class adheres to the protocol"

This reverts commit 509e368.

Inheriting from both `string.Template` and `typing.Protocol` causes
mypy to report a metaclass conflict on Python 3.8.
  • Loading branch information
mthuurne authored Oct 12, 2024
1 parent 1d30114 commit 1d3b071
Show file tree
Hide file tree
Showing 5 changed files with 12 additions and 7 deletions.
6 changes: 2 additions & 4 deletions django-stubs/template/backends/base.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ from collections.abc import Iterator, Mapping
from typing import Any, Protocol, type_check_only

from django.http.request import HttpRequest
from django.template.base import Context
from django.utils.functional import cached_property
from django.utils.safestring import SafeString

class BaseEngine:
name: str
Expand All @@ -23,6 +21,6 @@ class BaseEngine:
class _EngineTemplate(Protocol):
def render(
self,
context: Context | dict[str, Any] | None = ...,
context: dict[str, Any] | None = ...,
request: HttpRequest | None = ...,
) -> SafeString: ...
) -> str: ...
2 changes: 2 additions & 0 deletions django-stubs/template/backends/django.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ from .base import BaseEngine, _EngineTemplate
class DjangoTemplates(BaseEngine):
engine: Engine
def __init__(self, params: dict[str, Any]) -> None: ...
def from_string(self, template_code: str) -> Template: ...
def get_template(self, template_name: str) -> Template: ...
def get_templatetag_libraries(self, custom_libraries: dict[str, str]) -> dict[str, str]: ...

def copy_exception(exc: TemplateDoesNotExist, backend: DjangoTemplates | None = None) -> TemplateDoesNotExist: ...
Expand Down
4 changes: 3 additions & 1 deletion django-stubs/template/backends/dummy.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ from .base import BaseEngine

class TemplateStrings(BaseEngine):
def __init__(self, params: dict[str, dict[Any, Any] | list[Any] | bool | str]) -> None: ...
def from_string(self, template_code: str) -> Template: ...
def get_template(self, template_name: str) -> Template: ...

class Template(string.Template):
template: str
def render(self, context: dict[str, str] | None = ..., request: HttpRequest | None = ...) -> str: ...
def render(self, context: dict[str, Any] | None = ..., request: HttpRequest | None = ...) -> str: ...
5 changes: 4 additions & 1 deletion django-stubs/template/backends/jinja2.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ from collections.abc import Callable
from typing import Any

from _typeshed import Incomplete
from django.http.request import HttpRequest
from django.template.exceptions import TemplateSyntaxError
from django.utils.functional import cached_property

Expand All @@ -11,6 +12,8 @@ class Jinja2(BaseEngine):
env: Any
context_processors: list[str]
def __init__(self, params: dict[str, Any]) -> None: ...
def from_string(self, template_code: str) -> Template: ...
def get_template(self, template_name: str) -> Template: ...
@cached_property
def template_context_processors(self) -> list[Callable]: ...

Expand All @@ -24,6 +27,6 @@ class Template:
backend: Jinja2
origin: Origin
def __init__(self, template: Incomplete, backend: Jinja2) -> None: ...
def render(self, context: Incomplete | None = ..., request: Incomplete | None = ...) -> Incomplete: ...
def render(self, context: dict[str, Any] | None = ..., request: HttpRequest | None = ...) -> str: ...

def get_exception_info(exception: TemplateSyntaxError) -> dict[str, Any]: ...
2 changes: 1 addition & 1 deletion django-stubs/template/context.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,4 @@ class RequestContext(Context):
def bind_template(self, template: Template) -> Iterator[None]: ...
def new(self, values: _ContextValues | None = None) -> RequestContext: ...

def make_context(context: Any, request: HttpRequest | None = None, **kwargs: Any) -> Context: ...
def make_context(context: dict[str, Any] | None, request: HttpRequest | None = None, **kwargs: Any) -> Context: ...

0 comments on commit 1d3b071

Please sign in to comment.