Files
django-stubs/mypy_django_plugin/transformers/functional.py
PIG208 18a055124a Return Promise for lazy functions. (#689)
* Type the return value of lazy translation functions as Promise.

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>

* Mark unicode translation functions for deprecation.

https://docs.djangoproject.com/en/4.0/releases/4.0/#features-removed-in-4-0.

Signed-off-by: Zixuan James Li <p359101898@gmail.com>

* Add proxied functions for Promise.

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>

* Add _StrPromise as a special type for Promise objects for str.

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>

* Implement _StrPromise attribute hook.

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>

* Allow message being a _StrPromise object for RegexValidator.

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>

* Refactor _StrPromise attribtue hook with analyze_member_access.

Signed-off-by: Zixuan James Li <p359101898@gmail.com>

Signed-off-by: Zixuan James Li <p359101898@gmail.com>
2022-08-27 21:08:31 +03:00

36 lines
1.3 KiB
Python

from mypy.checkmember import analyze_member_access
from mypy.errorcodes import ATTR_DEFINED
from mypy.nodes import CallExpr, MemberExpr
from mypy.plugin import AttributeContext
from mypy.types import AnyType, Instance
from mypy.types import Type as MypyType
from mypy.types import TypeOfAny
from mypy_django_plugin.lib import helpers
def resolve_str_promise_attribute(ctx: AttributeContext) -> MypyType:
if isinstance(ctx.context, MemberExpr):
method_name = ctx.context.name
elif isinstance(ctx.context, CallExpr) and isinstance(ctx.context.callee, MemberExpr):
method_name = ctx.context.callee.name
else:
ctx.api.fail(f'Cannot resolve the attribute of "{ctx.type}"', ctx.context, code=ATTR_DEFINED)
return AnyType(TypeOfAny.from_error)
str_info = helpers.lookup_fully_qualified_typeinfo(helpers.get_typechecker_api(ctx), f"builtins.str")
assert str_info is not None
str_type = Instance(str_info, [])
return analyze_member_access(
method_name,
str_type,
ctx.context,
is_lvalue=False,
is_super=False,
# operators are already handled with magic methods defined in the stubs for _StrPromise
is_operator=False,
msg=ctx.api.msg,
original_type=ctx.type,
chk=helpers.get_typechecker_api(ctx),
)