Skip to content

Commit

Permalink
Support passing lazy strings to utils.text functions (#1344)
Browse files Browse the repository at this point in the history
Currently, we get many of the following errors when trying to pass our Django code through mypy:

error: Argument 1 to "capfirst" has incompatible type "_StrPromise"; expected "Optional[str]"  [arg-type]

Looking at the Django source code for capfirst it has been decorated with @keep_lazy_text meaning it also supports _StrPromise next to str.

I've updated the typing of all similar functions to reflect this support.

* Add bound typevar to bind output laziness to input laziness

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
ds-cbo and pre-commit-ci[bot] authored Jan 26, 2023
1 parent 32e5ebc commit b201f90
Showing 1 changed file with 15 additions and 9 deletions.
24 changes: 15 additions & 9 deletions django-stubs/utils/text.pyi
Original file line number Diff line number Diff line change
@@ -1,31 +1,37 @@
from collections.abc import Callable, Iterable, Iterator
from io import BytesIO
from re import Pattern
from typing import TypeVar, overload

from django.db.models.base import Model
from django.utils.functional import SimpleLazyObject
from django.utils.functional import SimpleLazyObject, _StrOrPromise
from django.utils.safestring import SafeString

def capfirst(x: str | None) -> str | None: ...
_StrOrPromiseT = TypeVar("_StrOrPromiseT", bound=_StrOrPromise)

def capfirst(x: _StrOrPromiseT | None) -> _StrOrPromiseT | None: ...

re_words: Pattern[str]
re_chars: Pattern[str]
re_tag: Pattern[str]
re_newlines: Pattern[str]
re_camel_case: Pattern[str]

def wrap(text: str, width: int) -> str: ...
def wrap(text: _StrOrPromiseT, width: int) -> _StrOrPromiseT: ...

class Truncator(SimpleLazyObject):
def __init__(self, text: Model | str) -> None: ...
def add_truncation_text(self, text: str, truncate: str | None = ...) -> str: ...
def chars(self, num: int, truncate: str | None = ..., html: bool = ...) -> str: ...
def words(self, num: int, truncate: str | None = ..., html: bool = ...) -> str: ...

def get_valid_filename(name: str) -> str: ...
def get_valid_filename(name: _StrOrPromiseT) -> _StrOrPromiseT: ...
@overload
def get_text_list(list_: list[str], last_word: str = ...) -> str: ...
def normalize_newlines(text: str) -> str: ...
def phone2numeric(phone: str) -> str: ...
@overload
def get_text_list(list_: list[_StrOrPromise], last_word: _StrOrPromise = ...) -> _StrOrPromise: ...
def normalize_newlines(text: _StrOrPromiseT) -> _StrOrPromiseT: ...
def phone2numeric(phone: _StrOrPromiseT) -> _StrOrPromiseT: ...
def compress_string(s: bytes) -> bytes: ...

class StreamingBuffer(BytesIO):
Expand All @@ -37,9 +43,9 @@ def compress_sequence(sequence: Iterable[bytes]) -> Iterator[bytes]: ...
smart_split_re: Pattern[str]

def smart_split(text: str) -> Iterator[str]: ...
def unescape_entities(text: str) -> str: ...
def unescape_string_literal(s: str) -> str: ...
def slugify(value: str, allow_unicode: bool = ...) -> str: ...
def unescape_entities(text: _StrOrPromiseT) -> _StrOrPromiseT: ...
def unescape_string_literal(s: _StrOrPromiseT) -> _StrOrPromiseT: ...
def slugify(value: _StrOrPromiseT, allow_unicode: bool = ...) -> _StrOrPromiseT: ...
def camel_case_to_spaces(value: str) -> str: ...

format_lazy: Callable[..., str]

0 comments on commit b201f90

Please sign in to comment.