Skip to content

Commit 042aa94

Browse files
committed
wip
1 parent 0665ce9 commit 042aa94

File tree

4 files changed

+51
-1
lines changed

4 files changed

+51
-1
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

+10
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
AttributeContext,
1010
ClassDefContext,
1111
FunctionContext,
12+
FunctionSigContext,
1213
MethodContext,
1314
MethodSigContext,
1415
Plugin,
@@ -45,6 +46,15 @@ def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type]
4546
return singledispatch.create_singledispatch_function_callback
4647
return None
4748

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

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: ...

0 commit comments

Comments
 (0)