Skip to content

Commit e47698c

Browse files
committed
wip
1 parent 0665ce9 commit e47698c

File tree

5 files changed

+53
-2
lines changed

5 files changed

+53
-2
lines changed

mypy/plugins/attrs.py

+16
Original file line numberDiff line numberDiff line change
@@ -883,3 +883,19 @@ def add_method(
883883
"""
884884
self_type = self_type if self_type is not None else self.self_type
885885
add_method(self.ctx, method_name, args, ret_type, self_type, tvd)
886+
887+
888+
def evolve_callback(ctx: mypy.plugin.FunctionSigContext) -> FunctionLike:
889+
"""Callback to provide an accurate signature for attrs.evolve."""
890+
md = ctx.args[0][0].node.type.type.metadata
891+
892+
args = {
893+
md_attribute['name']: ctx.api.named_generic_type(md_attribute['init_type'], args=[])
894+
for md_attribute in md.get('attrs', {}).get('attributes', [])
895+
}
896+
897+
return ctx.default_signature.copy_modified(
898+
arg_kinds=ctx.default_signature.arg_kinds[:1] + [ARG_NAMED_OPT] * len(args),
899+
arg_names=ctx.default_signature.arg_names[:1] + list(args.keys()),
900+
arg_types=ctx.default_signature.arg_types[:1] + list(args.values()),
901+
)

mypy/plugins/default.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
MethodSigContext,
1414
Plugin,
1515
)
16+
from mypy.plugin import FunctionSigContext
17+
from mypy.plugins import attrs
1618
from mypy.plugins.common import try_getting_str_literals
1719
from mypy.subtypes import is_subtype
1820
from mypy.typeops import is_literal_type_like, make_simplified_union
@@ -45,10 +47,18 @@ def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type]
4547
return singledispatch.create_singledispatch_function_callback
4648
return None
4749

50+
def get_function_signature_hook(
51+
self, fullname: str
52+
) -> Callable[[FunctionSigContext], FunctionLike] | None:
53+
if fullname == "attr.evolve":
54+
return attrs.evolve_callback
55+
return None
56+
57+
4858
def get_method_signature_hook(
4959
self, fullname: str
5060
) -> Callable[[MethodSigContext], FunctionLike] | None:
51-
from mypy.plugins import ctypes, singledispatch
61+
from mypy.plugins import attrs, ctypes, singledispatch
5262

5363
if fullname == "typing.Mapping.get":
5464
return typed_dict_get_signature_callback

test-data/unit/check-attr.test

+23-1
Original file line numberDiff line numberDiff line change
@@ -1866,4 +1866,26 @@ reveal_type(D) # N: Revealed type is "def (a: builtins.int, b: builtins.str) ->
18661866
D(1, "").a = 2 # E: Cannot assign to final attribute "a"
18671867
D(1, "").b = "2" # E: Cannot assign to final attribute "b"
18681868

1869-
[builtins fixtures/property.pyi]
1869+
[builtins fixtures/property.pyi]
1870+
1871+
[case testEvolve]
1872+
import attr
1873+
1874+
@attr.s(auto_attribs=True)
1875+
class C:
1876+
name: str
1877+
1878+
c = C(name='foo')
1879+
attr.evolve(c)
1880+
attr.evolve(c, name='bar')
1881+
attr.evolve(
1882+
c,
1883+
name=42, # type: ignore[arg-type]
1884+
)
1885+
attr.evolve(
1886+
c,
1887+
age=42, # type: ignore[call-arg]
1888+
)
1889+
1890+
[builtins fixtures/dict.pyi]
1891+
[typing fixtures/typing-medium.pyi]

test-data/unit/lib-stub/attr/__init__.pyi

+2
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,5 @@ def field(
244244
order: Optional[bool] = ...,
245245
on_setattr: Optional[object] = ...,
246246
) -> Any: ...
247+
248+
def evolve(inst: _T, **changes: Any) -> _T: ...

test-data/unit/lib-stub/attrs/__init__.pyi

+1
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,4 @@ def field(
126126
order: Optional[bool] = ...,
127127
on_setattr: Optional[object] = ...,
128128
) -> Any: ...
129+

0 commit comments

Comments
 (0)