Return Promise for lazy functions.#689
Conversation
There was a problem hiding this comment.
I think that Promise is a new thing, isn't it? Looks like that latest 3.2.x does not have this primitive: https://github.com/django/django/tree/3.2.6/django/utils
There was a problem hiding this comment.
Moreover, even in latest master it is just a plain class with no methods or anything: https://github.com/django/django/blob/main/django/utils/functional.py#L68
There was a problem hiding this comment.
Hmm.. Promise has been there for quite a while: django/django@e4e28d9. But you are right that Promise is just a plain class. Since the proxy method knows about the resultclasses, I guess creating generics like Promise[str] would be better?
There was a problem hiding this comment.
Maybe we can add these methods to Promise as well? https://github.com/django/django/blob/3.2.6/django/utils/functional.py#L84-L191
|
Hi, I have updated the PR. I think it is probably not necessary to make |
|
There is still a problem with this. |
This comment was marked as resolved.
This comment was marked as resolved.
0a8e100 to
71a6b57
Compare
|
I believe that this implementation should be good enough for another review. @sobolevn could you take a look at this? I ditched the generic approach and reimplement this with a simpler plugin handling the use case for Looks like the CI is still failing for another unrelated issue. Will look into this in a separate PR probably. Otherwise, I think we are good to go. |
The return value of the lazy translation functions is a proxied `Promise` object. https://github.com/django/django/blob/3.2.6/django/utils/translation/__init__.py#L135-L221. Signed-off-by: Zixuan James Li <p359101898@gmail.com>
https://docs.djangoproject.com/en/4.0/releases/4.0/#features-removed-in-4-0. Signed-off-by: Zixuan James Li <p359101898@gmail.com>
Although there is nothing defined in `Promise` itself, the only instances of `Promise` are created by the `lazy` function, with magic methods defined on it. https://github.com/django/django/blob/3.2.6/django/utils/functional.py#L84-L191. Signed-off-by: Zixuan James Li <p359101898@gmail.com>
This allows the user to access methods defined on lazy strings while still letting mypy be aware of that they are not instances of `str`. The definitions for some of the magic methods are pulled from typeshed. We need those definitions in the stubs so that `_StrPromise` objects will work properly with operators, as refining operator types is tricky with the mypy plugins API. The rest of the methods will be covered by an attribute hook. Signed-off-by: Zixuan James Li <p359101898@gmail.com>
This implements an attribute hook that provides type information for methods that are available on `builtins.str` for `_StrPromise` except the supported operators. This allows us to avoid copying stubs from the builtins for all supported methods on `str`. Signed-off-by: Zixuan James Li <p359101898@gmail.com>
One intended usage of lazystr is to postpone the translation of the error message of a validation error. It is possible that we pass a Promise (specifically _StrPromise) and only evaluate it when a ValidationError is raised. Signed-off-by: Zixuan James Li <p359101898@gmail.com>
|
@sobolevn Hi! The PR has been rebased and the CI is passing. I think it is ready for review. |
|
|
||
| str_info = helpers.lookup_fully_qualified_typeinfo(helpers.get_typechecker_api(ctx), f"builtins.str") | ||
| assert str_info is not None | ||
| method = str_info.get(method_name) |
There was a problem hiding this comment.
I think that you should use analyze_member_access here from mypy/checkmember.py
Signed-off-by: Zixuan James Li <p359101898@gmail.com>
sobolevn
left a comment
There was a problem hiding this comment.
Let's try it out! Thank you!
|
This PR causes errors when passing error: Argument "verbose_name" to "PositiveBigIntegerField" has incompatible type
"_StrPromise"; expected "Optional[str]" [arg-type]
null=True, blank=True, verbose_name=_("number")(where |
|
Please, open a new issue. I am not releasing new version yet, we have time to fix it. |
|
@PIG208 was there a reason django-stubs/django-stubs/utils/functional.pyi Lines 31 to 46 in fa972f1 Is a subclass of I'm raising this question here instead of creating a separate issue since I figured I'd ask about the intent before completely suggesting this should change (maybe there was a reason I am not aware of). It seems like @sobolevn not sure if you have any thoughts or would prefer this to be a separate issue. |
|
@terencehonles Thanks for bringing this up!
The point of having We did not make |
|
See also #1139 (comment) for concrete examples of what would go wrong if we incorrectly told mypy that |
|
ok, thanks for the explanation.
A I had believed that Django's Promise implementation should be a more or less drop in replacement for the object it's promising since I thought it's blockingly coerced into the object when it's used, but it sounds like there's been some discussion of this and I will check out the comment @andersk pointed to 👍 |
Django uses a proxy method for lazy translation functions that actually returns instances of
Promiseinstead ofstr. To be accurate, we should change the signatures for these functions.Related issues
gettext_lazyshould return a Promise object #688gettext_lazyshould return a Promise object #688