Skip to content

Commit e021f65

Browse files
authored
Replace **flags with explicit kwargs on Library.filter (#3360)
is_safe, needs_autoescape, and expects_localtime are the only flags Django's Library.filter consumes, so type them explicitly.
1 parent be244f8 commit e021f65

2 files changed

Lines changed: 45 additions & 3 deletions

File tree

django-stubs/template/library.pyi

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,35 @@ class Library:
2424
def tag(self, name: str | None = None, compile_function: None = None) -> Callable[[_C], _C]: ...
2525
def tag_function(self, func: _C) -> _C: ...
2626
@overload
27-
def filter(self, name: _C, filter_func: None = None, **flags: Any) -> _C: ...
27+
def filter(
28+
self,
29+
name: _C,
30+
filter_func: None = None,
31+
*,
32+
is_safe: bool = False,
33+
needs_autoescape: bool = False,
34+
expects_localtime: bool = False,
35+
) -> _C: ...
2836
@overload
29-
def filter(self, name: str | None, filter_func: _C, **flags: Any) -> _C: ...
37+
def filter(
38+
self,
39+
name: str | None,
40+
filter_func: _C,
41+
*,
42+
is_safe: bool = False,
43+
needs_autoescape: bool = False,
44+
expects_localtime: bool = False,
45+
) -> _C: ...
3046
@overload
31-
def filter(self, name: str | None = None, filter_func: None = None, **flags: Any) -> Callable[[_C], _C]: ...
47+
def filter(
48+
self,
49+
name: str | None = None,
50+
filter_func: None = None,
51+
*,
52+
is_safe: bool = False,
53+
needs_autoescape: bool = False,
54+
expects_localtime: bool = False,
55+
) -> Callable[[_C], _C]: ...
3256
def filter_function(self, func: _C, **flags: Any) -> _C: ...
3357
@overload
3458
def simple_tag(self, func: _C, takes_context: bool | None = None, name: str | None = None) -> _C: ...

tests/assert_type/template/test_library.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,24 @@ def lower2(value: str) -> str:
2929
assert_type(lower2("test"), str)
3030

3131

32+
# register.filter with all flags
33+
@register.filter(name="plain", is_safe=True, needs_autoescape=False, expects_localtime=False)
34+
def plain_filter(value: str) -> str:
35+
return value
36+
37+
38+
assert_type(plain_filter("x"), str)
39+
40+
41+
# Negative: non-bool values for flag kwargs are rejected
42+
register.filter("bad_is_safe", is_safe="yes") # type: ignore[call-overload] # pyright: ignore[reportCallIssue,reportArgumentType] # pyrefly: ignore[no-matching-overload] # ty: ignore[no-matching-overload]
43+
register.filter("bad_needs_autoescape", needs_autoescape="no") # type: ignore[call-overload] # pyright: ignore[reportCallIssue,reportArgumentType] # pyrefly: ignore[no-matching-overload] # ty: ignore[no-matching-overload]
44+
register.filter("bad_expects_localtime", expects_localtime=None) # type: ignore[call-overload] # pyright: ignore[reportCallIssue,reportArgumentType] # pyrefly: ignore[no-matching-overload] # ty: ignore[no-matching-overload]
45+
46+
# Negative: unknown kwargs are rejected
47+
register.filter("unknown_flag", safe=True) # type: ignore[call-overload] # pyright: ignore[reportCallIssue] # pyrefly: ignore[no-matching-overload] # ty: ignore[no-matching-overload]
48+
49+
3250
# register.simple_tag (bare)
3351
@register.simple_tag
3452
def current_time(format_string: str) -> str:

0 commit comments

Comments
 (0)