Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support passing lazy strings to utils.text functions #1344

Merged
merged 3 commits into from
Jan 26, 2023

Conversation

ds-cbo
Copy link
Contributor

@ds-cbo ds-cbo commented Jan 25, 2023

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.

@intgr intgr changed the title support keep_lazy_text on utils/text.py Support keep_lazy_text on utils/text.py Jan 25, 2023
Copy link
Collaborator

@intgr intgr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is useful.

The current patch may cause some false positives for users when upgrading though. For example this code was previously fine, but after the change:

from django.utils.text import wrap

def takes_str(value: str):
    pass

result = wrap("teststring", 80)
reveal_type(result)  # Revealed type is "Union[builtins.str, django.utils.functional._StrPromise]"
takes_str(result)  # Argument 1 to "takes_str" has incompatible type "Union[str, _StrPromise]"; expected "str"

The solution is to define TypeVar, to teach mypy that result type is dependent on input type.

_StrOrPromiseT = TypeVar("_StrOrPromiseT", bound=_StrOrPromise)

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

Now type checkers know that if you pass in str, the result will also be str.

Looks like it can be useful to define another TypeVar also for the case of _StrOrPromise | None

It looks like get_text_list is a more complex case and may not be worth it, feel free to use regular _StrOrPromise there. It may be possible to make it work with @overload.

@intgr
Copy link
Collaborator

intgr commented Jan 25, 2023

Maybe this is of interest for @mschoettle too.

@intgr intgr self-assigned this Jan 25, 2023
Copy link
Collaborator

@intgr intgr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Waiting for CI to pass.

@intgr intgr changed the title Support keep_lazy_text on utils/text.py Support passing lazy strings to utils.text functions Jan 26, 2023
@intgr intgr merged commit b201f90 into typeddjango:master Jan 26, 2023
@ds-cbo ds-cbo deleted the keep-lazy-text-utils branch January 26, 2023 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants