Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ikonst committed Jan 26, 2023
1 parent 0665ce9 commit e47698c
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 2 deletions.
16 changes: 16 additions & 0 deletions mypy/plugins/attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -883,3 +883,19 @@ def add_method(
"""
self_type = self_type if self_type is not None else self.self_type
add_method(self.ctx, method_name, args, ret_type, self_type, tvd)


def evolve_callback(ctx: mypy.plugin.FunctionSigContext) -> FunctionLike:
"""Callback to provide an accurate signature for attrs.evolve."""
md = ctx.args[0][0].node.type.type.metadata

args = {
md_attribute['name']: ctx.api.named_generic_type(md_attribute['init_type'], args=[])
for md_attribute in md.get('attrs', {}).get('attributes', [])
}

return ctx.default_signature.copy_modified(
arg_kinds=ctx.default_signature.arg_kinds[:1] + [ARG_NAMED_OPT] * len(args),
arg_names=ctx.default_signature.arg_names[:1] + list(args.keys()),
arg_types=ctx.default_signature.arg_types[:1] + list(args.values()),
)
12 changes: 11 additions & 1 deletion mypy/plugins/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
MethodSigContext,
Plugin,
)
from mypy.plugin import FunctionSigContext
from mypy.plugins import attrs
from mypy.plugins.common import try_getting_str_literals
from mypy.subtypes import is_subtype
from mypy.typeops import is_literal_type_like, make_simplified_union
Expand Down Expand Up @@ -45,10 +47,18 @@ def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type]
return singledispatch.create_singledispatch_function_callback
return None

def get_function_signature_hook(
self, fullname: str
) -> Callable[[FunctionSigContext], FunctionLike] | None:
if fullname == "attr.evolve":
return attrs.evolve_callback
return None


def get_method_signature_hook(
self, fullname: str
) -> Callable[[MethodSigContext], FunctionLike] | None:
from mypy.plugins import ctypes, singledispatch
from mypy.plugins import attrs, ctypes, singledispatch

if fullname == "typing.Mapping.get":
return typed_dict_get_signature_callback
Expand Down
24 changes: 23 additions & 1 deletion test-data/unit/check-attr.test
Original file line number Diff line number Diff line change
Expand Up @@ -1866,4 +1866,26 @@ reveal_type(D) # N: Revealed type is "def (a: builtins.int, b: builtins.str) ->
D(1, "").a = 2 # E: Cannot assign to final attribute "a"
D(1, "").b = "2" # E: Cannot assign to final attribute "b"

[builtins fixtures/property.pyi]
[builtins fixtures/property.pyi]

[case testEvolve]
import attr

@attr.s(auto_attribs=True)
class C:
name: str

c = C(name='foo')
attr.evolve(c)
attr.evolve(c, name='bar')
attr.evolve(
c,
name=42, # type: ignore[arg-type]
)
attr.evolve(
c,
age=42, # type: ignore[call-arg]
)

[builtins fixtures/dict.pyi]
[typing fixtures/typing-medium.pyi]
2 changes: 2 additions & 0 deletions test-data/unit/lib-stub/attr/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,5 @@ def field(
order: Optional[bool] = ...,
on_setattr: Optional[object] = ...,
) -> Any: ...

def evolve(inst: _T, **changes: Any) -> _T: ...
1 change: 1 addition & 0 deletions test-data/unit/lib-stub/attrs/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,4 @@ def field(
order: Optional[bool] = ...,
on_setattr: Optional[object] = ...,
) -> Any: ...

0 comments on commit e47698c

Please sign in to comment.