Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add alias support to field() in attrs plugin
  • Loading branch information
sobolevn committed Dec 4, 2023
commit 0dd02557f5db11641eacfe4c0fdbc0b60e44ab32
23 changes: 20 additions & 3 deletions mypy/plugins/attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class Attribute:
def __init__(
self,
name: str,
alias: str | None,
info: TypeInfo,
has_default: bool,
init: bool,
Expand All @@ -114,6 +115,7 @@ def __init__(
init_type: Type | None,
) -> None:
self.name = name
self.alias = alias
self.info = info
self.has_default = has_default
self.init = init
Expand Down Expand Up @@ -171,12 +173,14 @@ def argument(self, ctx: mypy.plugin.ClassDefContext) -> Argument:
arg_kind = ARG_OPT if self.has_default else ARG_POS

# Attrs removes leading underscores when creating the __init__ arguments.
return Argument(Var(self.name.lstrip("_"), init_type), init_type, None, arg_kind)
name = self.alias or self.name.lstrip("_")
return Argument(Var(name, init_type), init_type, None, arg_kind)

def serialize(self) -> JsonDict:
"""Serialize this object so it can be saved and restored."""
return {
"name": self.name,
"alias": self.alias,
"has_default": self.has_default,
"init": self.init,
"kw_only": self.kw_only,
Expand Down Expand Up @@ -205,6 +209,7 @@ def deserialize(

return Attribute(
data["name"],
data["alias"],
info,
data["has_default"],
data["init"],
Expand Down Expand Up @@ -498,6 +503,7 @@ def _attributes_from_assignment(
or if auto_attribs is enabled also like this:
x: type
x: type = default_value
x: type = attr.ib(...)
"""
for lvalue in stmt.lvalues:
lvalues, rvalues = _parse_assignments(lvalue, stmt)
Expand Down Expand Up @@ -564,7 +570,7 @@ def _attribute_from_auto_attrib(
has_rhs = not isinstance(rvalue, TempNode)
sym = ctx.cls.info.names.get(name)
init_type = sym.type if sym else None
return Attribute(name, ctx.cls.info, has_rhs, True, kw_only, None, stmt, init_type)
return Attribute(name, None, ctx.cls.info, has_rhs, True, kw_only, None, stmt, init_type)


def _attribute_from_attrib_maker(
Expand Down Expand Up @@ -628,9 +634,20 @@ def _attribute_from_attrib_maker(
converter = convert
converter_info = _parse_converter(ctx, converter)

# Custom alias might be defined:
alias = None
alias_expr = _get_argument(rvalue, "alias")
if alias_expr:
alias = ctx.api.parse_str_literal(alias_expr)
if alias is None:
ctx.api.fail(
'"alias" argument to attrs field must be a string literal',
rvalue,
code=LITERAL_REQ,
)
name = unmangle(lhs.name)
return Attribute(
name, ctx.cls.info, attr_has_default, init, kw_only, converter_info, stmt, init_type
name, alias, ctx.cls.info, attr_has_default, init, kw_only, converter_info, stmt, init_type
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One day we'll make it kw-only :)

)


Expand Down
25 changes: 25 additions & 0 deletions test-data/unit/check-plugin-attrs.test
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,31 @@ C()
C(_x=42) # E: Unexpected keyword argument "_x" for "C"
[builtins fixtures/list.pyi]

[case testAttrsAliasForInit]
from attrs import define, field

@define
class C1:
_x: int = field(alias="x1")

c1 = C1(x1=42)
reveal_type(c1._x) # N: Revealed type is "builtins.int"
c1.x1 # E: "C1" has no attribute "x1"
C1(_x=42) # E: Unexpected keyword argument "_x" for "C1"

alias = "x2"
@define
class C2:
_x: int = field(alias=alias) # E: "alias" argument to attrs field must be a string literal

@define
class C3:
_x: int = field(alias="_x")

c3 = C3(_x=1)
reveal_type(c3._x) # N: Revealed type is "builtins.int"
[builtins fixtures/plugin_attrs.pyi]

[case testAttrsAutoMustBeAll]
import attr
@attr.s(auto_attribs=True)
Expand Down
4 changes: 4 additions & 0 deletions test-data/unit/lib-stub/attrs/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def field(
eq: Optional[bool] = ...,
order: Optional[bool] = ...,
on_setattr: Optional[_OnSetAttrArgType] = ...,
alias: Optional[str] = ...,
) -> Any: ...

# This form catches an explicit None or no default and infers the type from the
Expand All @@ -98,6 +99,7 @@ def field(
eq: Optional[bool] = ...,
order: Optional[bool] = ...,
on_setattr: Optional[object] = ...,
alias: Optional[str] = ...,
) -> _T: ...

# This form catches an explicit default argument.
Expand All @@ -116,6 +118,7 @@ def field(
eq: Optional[bool] = ...,
order: Optional[bool] = ...,
on_setattr: Optional[object] = ...,
alias: Optional[str] = ...,
) -> _T: ...

# This form covers type=non-Type: e.g. forward references (str), Any
Expand All @@ -134,6 +137,7 @@ def field(
eq: Optional[bool] = ...,
order: Optional[bool] = ...,
on_setattr: Optional[object] = ...,
alias: Optional[str] = ...,
) -> Any: ...

def evolve(inst: _T, **changes: Any) -> _T: ...
Expand Down