attrs.py 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072
  1. """Plugin for supporting the attrs library (http://www.attrs.org)"""
  2. from __future__ import annotations
  3. from collections import defaultdict
  4. from functools import reduce
  5. from typing import Final, Iterable, List, Mapping, cast
  6. from typing_extensions import Literal
  7. import mypy.plugin # To avoid circular imports.
  8. from mypy.applytype import apply_generic_arguments
  9. from mypy.errorcodes import LITERAL_REQ
  10. from mypy.expandtype import expand_type, expand_type_by_instance
  11. from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type
  12. from mypy.meet import meet_types
  13. from mypy.messages import format_type_bare
  14. from mypy.nodes import (
  15. ARG_NAMED,
  16. ARG_NAMED_OPT,
  17. ARG_OPT,
  18. ARG_POS,
  19. MDEF,
  20. Argument,
  21. AssignmentStmt,
  22. CallExpr,
  23. Context,
  24. Decorator,
  25. Expression,
  26. FuncDef,
  27. IndexExpr,
  28. JsonDict,
  29. LambdaExpr,
  30. ListExpr,
  31. MemberExpr,
  32. NameExpr,
  33. OverloadedFuncDef,
  34. PlaceholderNode,
  35. RefExpr,
  36. SymbolTableNode,
  37. TempNode,
  38. TupleExpr,
  39. TypeApplication,
  40. TypeInfo,
  41. TypeVarExpr,
  42. Var,
  43. is_class_var,
  44. )
  45. from mypy.plugin import SemanticAnalyzerPluginInterface
  46. from mypy.plugins.common import (
  47. _get_argument,
  48. _get_bool_argument,
  49. _get_decorator_bool_argument,
  50. add_attribute_to_class,
  51. add_method,
  52. deserialize_and_fixup_type,
  53. )
  54. from mypy.server.trigger import make_wildcard_trigger
  55. from mypy.typeops import get_type_vars, make_simplified_union, map_type_from_supertype
  56. from mypy.types import (
  57. AnyType,
  58. CallableType,
  59. FunctionLike,
  60. Instance,
  61. LiteralType,
  62. NoneType,
  63. Overloaded,
  64. ProperType,
  65. TupleType,
  66. Type,
  67. TypeOfAny,
  68. TypeVarType,
  69. UninhabitedType,
  70. UnionType,
  71. get_proper_type,
  72. )
  73. from mypy.typevars import fill_typevars
  74. from mypy.util import unmangle
  75. # The names of the different functions that create classes or arguments.
  76. attr_class_makers: Final = {"attr.s", "attr.attrs", "attr.attributes"}
  77. attr_dataclass_makers: Final = {"attr.dataclass"}
  78. attr_frozen_makers: Final = {"attr.frozen", "attrs.frozen"}
  79. attr_define_makers: Final = {"attr.define", "attr.mutable", "attrs.define", "attrs.mutable"}
  80. attr_attrib_makers: Final = {"attr.ib", "attr.attrib", "attr.attr", "attr.field", "attrs.field"}
  81. attr_optional_converters: Final = {"attr.converters.optional", "attrs.converters.optional"}
  82. SELF_TVAR_NAME: Final = "_AT"
  83. MAGIC_ATTR_NAME: Final = "__attrs_attrs__"
  84. MAGIC_ATTR_CLS_NAME_TEMPLATE: Final = "__{}_AttrsAttributes__" # The tuple subclass pattern.
  85. ATTRS_INIT_NAME: Final = "__attrs_init__"
  86. class Converter:
  87. """Holds information about a `converter=` argument"""
  88. def __init__(self, init_type: Type | None = None, ret_type: Type | None = None) -> None:
  89. self.init_type = init_type
  90. self.ret_type = ret_type
  91. class Attribute:
  92. """The value of an attr.ib() call."""
  93. def __init__(
  94. self,
  95. name: str,
  96. info: TypeInfo,
  97. has_default: bool,
  98. init: bool,
  99. kw_only: bool,
  100. converter: Converter | None,
  101. context: Context,
  102. init_type: Type | None,
  103. ) -> None:
  104. self.name = name
  105. self.info = info
  106. self.has_default = has_default
  107. self.init = init
  108. self.kw_only = kw_only
  109. self.converter = converter
  110. self.context = context
  111. self.init_type = init_type
  112. def argument(self, ctx: mypy.plugin.ClassDefContext) -> Argument:
  113. """Return this attribute as an argument to __init__."""
  114. assert self.init
  115. init_type: Type | None = None
  116. if self.converter:
  117. if self.converter.init_type:
  118. init_type = self.converter.init_type
  119. if init_type and self.init_type and self.converter.ret_type:
  120. # The converter return type should be the same type as the attribute type.
  121. # Copy type vars from attr type to converter.
  122. converter_vars = get_type_vars(self.converter.ret_type)
  123. init_vars = get_type_vars(self.init_type)
  124. if converter_vars and len(converter_vars) == len(init_vars):
  125. variables = {
  126. binder.id: arg for binder, arg in zip(converter_vars, init_vars)
  127. }
  128. init_type = expand_type(init_type, variables)
  129. else:
  130. ctx.api.fail("Cannot determine __init__ type from converter", self.context)
  131. init_type = AnyType(TypeOfAny.from_error)
  132. else: # There is no converter, the init type is the normal type.
  133. init_type = self.init_type or self.info[self.name].type
  134. unannotated = False
  135. if init_type is None:
  136. unannotated = True
  137. # Convert type not set to Any.
  138. init_type = AnyType(TypeOfAny.unannotated)
  139. else:
  140. proper_type = get_proper_type(init_type)
  141. if isinstance(proper_type, AnyType):
  142. if proper_type.type_of_any == TypeOfAny.unannotated:
  143. unannotated = True
  144. if unannotated and ctx.api.options.disallow_untyped_defs:
  145. # This is a compromise. If you don't have a type here then the
  146. # __init__ will be untyped. But since the __init__ is added it's
  147. # pointing at the decorator. So instead we also show the error in the
  148. # assignment, which is where you would fix the issue.
  149. node = self.info[self.name].node
  150. assert node is not None
  151. ctx.api.msg.need_annotation_for_var(node, self.context)
  152. if self.kw_only:
  153. arg_kind = ARG_NAMED_OPT if self.has_default else ARG_NAMED
  154. else:
  155. arg_kind = ARG_OPT if self.has_default else ARG_POS
  156. # Attrs removes leading underscores when creating the __init__ arguments.
  157. return Argument(Var(self.name.lstrip("_"), init_type), init_type, None, arg_kind)
  158. def serialize(self) -> JsonDict:
  159. """Serialize this object so it can be saved and restored."""
  160. return {
  161. "name": self.name,
  162. "has_default": self.has_default,
  163. "init": self.init,
  164. "kw_only": self.kw_only,
  165. "has_converter": self.converter is not None,
  166. "converter_init_type": self.converter.init_type.serialize()
  167. if self.converter and self.converter.init_type
  168. else None,
  169. "context_line": self.context.line,
  170. "context_column": self.context.column,
  171. "init_type": self.init_type.serialize() if self.init_type else None,
  172. }
  173. @classmethod
  174. def deserialize(
  175. cls, info: TypeInfo, data: JsonDict, api: SemanticAnalyzerPluginInterface
  176. ) -> Attribute:
  177. """Return the Attribute that was serialized."""
  178. raw_init_type = data["init_type"]
  179. init_type = deserialize_and_fixup_type(raw_init_type, api) if raw_init_type else None
  180. raw_converter_init_type = data["converter_init_type"]
  181. converter_init_type = (
  182. deserialize_and_fixup_type(raw_converter_init_type, api)
  183. if raw_converter_init_type
  184. else None
  185. )
  186. return Attribute(
  187. data["name"],
  188. info,
  189. data["has_default"],
  190. data["init"],
  191. data["kw_only"],
  192. Converter(converter_init_type) if data["has_converter"] else None,
  193. Context(line=data["context_line"], column=data["context_column"]),
  194. init_type,
  195. )
  196. def expand_typevar_from_subtype(self, sub_type: TypeInfo) -> None:
  197. """Expands type vars in the context of a subtype when an attribute is inherited
  198. from a generic super type."""
  199. if self.init_type:
  200. self.init_type = map_type_from_supertype(self.init_type, sub_type, self.info)
  201. else:
  202. self.init_type = None
  203. def _determine_eq_order(ctx: mypy.plugin.ClassDefContext) -> bool:
  204. """
  205. Validate the combination of *cmp*, *eq*, and *order*. Derive the effective
  206. value of order.
  207. """
  208. cmp = _get_decorator_optional_bool_argument(ctx, "cmp")
  209. eq = _get_decorator_optional_bool_argument(ctx, "eq")
  210. order = _get_decorator_optional_bool_argument(ctx, "order")
  211. if cmp is not None and any((eq is not None, order is not None)):
  212. ctx.api.fail('Don\'t mix "cmp" with "eq" and "order"', ctx.reason)
  213. # cmp takes precedence due to bw-compatibility.
  214. if cmp is not None:
  215. return cmp
  216. # If left None, equality is on and ordering mirrors equality.
  217. if eq is None:
  218. eq = True
  219. if order is None:
  220. order = eq
  221. if eq is False and order is True:
  222. ctx.api.fail("eq must be True if order is True", ctx.reason)
  223. return order
  224. def _get_decorator_optional_bool_argument(
  225. ctx: mypy.plugin.ClassDefContext, name: str, default: bool | None = None
  226. ) -> bool | None:
  227. """Return the Optional[bool] argument for the decorator.
  228. This handles both @decorator(...) and @decorator.
  229. """
  230. if isinstance(ctx.reason, CallExpr):
  231. attr_value = _get_argument(ctx.reason, name)
  232. if attr_value:
  233. if isinstance(attr_value, NameExpr):
  234. if attr_value.fullname == "builtins.True":
  235. return True
  236. if attr_value.fullname == "builtins.False":
  237. return False
  238. if attr_value.fullname == "builtins.None":
  239. return None
  240. ctx.api.fail(
  241. f'"{name}" argument must be a True, False, or None literal',
  242. ctx.reason,
  243. code=LITERAL_REQ,
  244. )
  245. return default
  246. return default
  247. else:
  248. return default
  249. def attr_tag_callback(ctx: mypy.plugin.ClassDefContext) -> None:
  250. """Record that we have an attrs class in the main semantic analysis pass.
  251. The later pass implemented by attr_class_maker_callback will use this
  252. to detect attrs classes in base classes.
  253. """
  254. # The value is ignored, only the existence matters.
  255. ctx.cls.info.metadata["attrs_tag"] = {}
  256. def attr_class_maker_callback(
  257. ctx: mypy.plugin.ClassDefContext,
  258. auto_attribs_default: bool | None = False,
  259. frozen_default: bool = False,
  260. slots_default: bool = False,
  261. ) -> bool:
  262. """Add necessary dunder methods to classes decorated with attr.s.
  263. attrs is a package that lets you define classes without writing dull boilerplate code.
  264. At a quick glance, the decorator searches the class body for assignments of `attr.ib`s (or
  265. annotated variables if auto_attribs=True), then depending on how the decorator is called,
  266. it will add an __init__ or all the compare methods.
  267. For frozen=True it will turn the attrs into properties.
  268. See https://www.attrs.org/en/stable/how-does-it-work.html for information on how attrs works.
  269. If this returns False, some required metadata was not ready yet and we need another
  270. pass.
  271. """
  272. info = ctx.cls.info
  273. init = _get_decorator_bool_argument(ctx, "init", True)
  274. frozen = _get_frozen(ctx, frozen_default)
  275. order = _determine_eq_order(ctx)
  276. slots = _get_decorator_bool_argument(ctx, "slots", slots_default)
  277. auto_attribs = _get_decorator_optional_bool_argument(ctx, "auto_attribs", auto_attribs_default)
  278. kw_only = _get_decorator_bool_argument(ctx, "kw_only", False)
  279. match_args = _get_decorator_bool_argument(ctx, "match_args", True)
  280. for super_info in ctx.cls.info.mro[1:-1]:
  281. if "attrs_tag" in super_info.metadata and "attrs" not in super_info.metadata:
  282. # Super class is not ready yet. Request another pass.
  283. return False
  284. attributes = _analyze_class(ctx, auto_attribs, kw_only)
  285. # Check if attribute types are ready.
  286. for attr in attributes:
  287. node = info.get(attr.name)
  288. if node is None:
  289. # This name is likely blocked by some semantic analysis error that
  290. # should have been reported already.
  291. _add_empty_metadata(info)
  292. return True
  293. _add_attrs_magic_attribute(ctx, [(attr.name, info[attr.name].type) for attr in attributes])
  294. if slots:
  295. _add_slots(ctx, attributes)
  296. if match_args and ctx.api.options.python_version[:2] >= (3, 10):
  297. # `.__match_args__` is only added for python3.10+, but the argument
  298. # exists for earlier versions as well.
  299. _add_match_args(ctx, attributes)
  300. # Save the attributes so that subclasses can reuse them.
  301. ctx.cls.info.metadata["attrs"] = {
  302. "attributes": [attr.serialize() for attr in attributes],
  303. "frozen": frozen,
  304. }
  305. adder = MethodAdder(ctx)
  306. # If __init__ is not being generated, attrs still generates it as __attrs_init__ instead.
  307. _add_init(ctx, attributes, adder, "__init__" if init else ATTRS_INIT_NAME)
  308. if order:
  309. _add_order(ctx, adder)
  310. if frozen:
  311. _make_frozen(ctx, attributes)
  312. return True
  313. def _get_frozen(ctx: mypy.plugin.ClassDefContext, frozen_default: bool) -> bool:
  314. """Return whether this class is frozen."""
  315. if _get_decorator_bool_argument(ctx, "frozen", frozen_default):
  316. return True
  317. # Subclasses of frozen classes are frozen so check that.
  318. for super_info in ctx.cls.info.mro[1:-1]:
  319. if "attrs" in super_info.metadata and super_info.metadata["attrs"]["frozen"]:
  320. return True
  321. return False
  322. def _analyze_class(
  323. ctx: mypy.plugin.ClassDefContext, auto_attribs: bool | None, kw_only: bool
  324. ) -> list[Attribute]:
  325. """Analyze the class body of an attr maker, its parents, and return the Attributes found.
  326. auto_attribs=True means we'll generate attributes from type annotations also.
  327. auto_attribs=None means we'll detect which mode to use.
  328. kw_only=True means that all attributes created here will be keyword only args in __init__.
  329. """
  330. own_attrs: dict[str, Attribute] = {}
  331. if auto_attribs is None:
  332. auto_attribs = _detect_auto_attribs(ctx)
  333. # Walk the body looking for assignments and decorators.
  334. for stmt in ctx.cls.defs.body:
  335. if isinstance(stmt, AssignmentStmt):
  336. for attr in _attributes_from_assignment(ctx, stmt, auto_attribs, kw_only):
  337. # When attrs are defined twice in the same body we want to use the 2nd definition
  338. # in the 2nd location. So remove it from the OrderedDict.
  339. # Unless it's auto_attribs in which case we want the 2nd definition in the
  340. # 1st location.
  341. if not auto_attribs and attr.name in own_attrs:
  342. del own_attrs[attr.name]
  343. own_attrs[attr.name] = attr
  344. elif isinstance(stmt, Decorator):
  345. _cleanup_decorator(stmt, own_attrs)
  346. for attribute in own_attrs.values():
  347. # Even though these look like class level assignments we want them to look like
  348. # instance level assignments.
  349. if attribute.name in ctx.cls.info.names:
  350. node = ctx.cls.info.names[attribute.name].node
  351. if isinstance(node, PlaceholderNode):
  352. # This node is not ready yet.
  353. continue
  354. assert isinstance(node, Var)
  355. node.is_initialized_in_class = False
  356. # Traverse the MRO and collect attributes from the parents.
  357. taken_attr_names = set(own_attrs)
  358. super_attrs = []
  359. for super_info in ctx.cls.info.mro[1:-1]:
  360. if "attrs" in super_info.metadata:
  361. # Each class depends on the set of attributes in its attrs ancestors.
  362. ctx.api.add_plugin_dependency(make_wildcard_trigger(super_info.fullname))
  363. for data in super_info.metadata["attrs"]["attributes"]:
  364. # Only add an attribute if it hasn't been defined before. This
  365. # allows for overwriting attribute definitions by subclassing.
  366. if data["name"] not in taken_attr_names:
  367. a = Attribute.deserialize(super_info, data, ctx.api)
  368. a.expand_typevar_from_subtype(ctx.cls.info)
  369. super_attrs.append(a)
  370. taken_attr_names.add(a.name)
  371. attributes = super_attrs + list(own_attrs.values())
  372. # Check the init args for correct default-ness. Note: This has to be done after all the
  373. # attributes for all classes have been read, because subclasses can override parents.
  374. last_default = False
  375. for i, attribute in enumerate(attributes):
  376. if not attribute.init:
  377. continue
  378. if attribute.kw_only:
  379. # Keyword-only attributes don't care whether they are default or not.
  380. continue
  381. # If the issue comes from merging different classes, report it
  382. # at the class definition point.
  383. context = attribute.context if i >= len(super_attrs) else ctx.cls
  384. if not attribute.has_default and last_default:
  385. ctx.api.fail("Non-default attributes not allowed after default attributes.", context)
  386. last_default |= attribute.has_default
  387. return attributes
  388. def _add_empty_metadata(info: TypeInfo) -> None:
  389. """Add empty metadata to mark that we've finished processing this class."""
  390. info.metadata["attrs"] = {"attributes": [], "frozen": False}
  391. def _detect_auto_attribs(ctx: mypy.plugin.ClassDefContext) -> bool:
  392. """Return whether auto_attribs should be enabled or disabled.
  393. It's disabled if there are any unannotated attribs()
  394. """
  395. for stmt in ctx.cls.defs.body:
  396. if isinstance(stmt, AssignmentStmt):
  397. for lvalue in stmt.lvalues:
  398. lvalues, rvalues = _parse_assignments(lvalue, stmt)
  399. if len(lvalues) != len(rvalues):
  400. # This means we have some assignment that isn't 1 to 1.
  401. # It can't be an attrib.
  402. continue
  403. for lhs, rvalue in zip(lvalues, rvalues):
  404. # Check if the right hand side is a call to an attribute maker.
  405. if (
  406. isinstance(rvalue, CallExpr)
  407. and isinstance(rvalue.callee, RefExpr)
  408. and rvalue.callee.fullname in attr_attrib_makers
  409. and not stmt.new_syntax
  410. ):
  411. # This means we have an attrib without an annotation and so
  412. # we can't do auto_attribs=True
  413. return False
  414. return True
  415. def _attributes_from_assignment(
  416. ctx: mypy.plugin.ClassDefContext, stmt: AssignmentStmt, auto_attribs: bool, kw_only: bool
  417. ) -> Iterable[Attribute]:
  418. """Return Attribute objects that are created by this assignment.
  419. The assignments can look like this:
  420. x = attr.ib()
  421. x = y = attr.ib()
  422. x, y = attr.ib(), attr.ib()
  423. or if auto_attribs is enabled also like this:
  424. x: type
  425. x: type = default_value
  426. """
  427. for lvalue in stmt.lvalues:
  428. lvalues, rvalues = _parse_assignments(lvalue, stmt)
  429. if len(lvalues) != len(rvalues):
  430. # This means we have some assignment that isn't 1 to 1.
  431. # It can't be an attrib.
  432. continue
  433. for lhs, rvalue in zip(lvalues, rvalues):
  434. # Check if the right hand side is a call to an attribute maker.
  435. if (
  436. isinstance(rvalue, CallExpr)
  437. and isinstance(rvalue.callee, RefExpr)
  438. and rvalue.callee.fullname in attr_attrib_makers
  439. ):
  440. attr = _attribute_from_attrib_maker(ctx, auto_attribs, kw_only, lhs, rvalue, stmt)
  441. if attr:
  442. yield attr
  443. elif auto_attribs and stmt.type and stmt.new_syntax and not is_class_var(lhs):
  444. yield _attribute_from_auto_attrib(ctx, kw_only, lhs, rvalue, stmt)
  445. def _cleanup_decorator(stmt: Decorator, attr_map: dict[str, Attribute]) -> None:
  446. """Handle decorators in class bodies.
  447. `x.default` will set a default value on x
  448. `x.validator` and `x.default` will get removed to avoid throwing a type error.
  449. """
  450. remove_me = []
  451. for func_decorator in stmt.decorators:
  452. if (
  453. isinstance(func_decorator, MemberExpr)
  454. and isinstance(func_decorator.expr, NameExpr)
  455. and func_decorator.expr.name in attr_map
  456. ):
  457. if func_decorator.name == "default":
  458. attr_map[func_decorator.expr.name].has_default = True
  459. if func_decorator.name in ("default", "validator"):
  460. # These are decorators on the attrib object that only exist during
  461. # class creation time. In order to not trigger a type error later we
  462. # just remove them. This might leave us with a Decorator with no
  463. # decorators (Emperor's new clothes?)
  464. # TODO: It would be nice to type-check these rather than remove them.
  465. # default should be Callable[[], T]
  466. # validator should be Callable[[Any, 'Attribute', T], Any]
  467. # where T is the type of the attribute.
  468. remove_me.append(func_decorator)
  469. for dec in remove_me:
  470. stmt.decorators.remove(dec)
  471. def _attribute_from_auto_attrib(
  472. ctx: mypy.plugin.ClassDefContext,
  473. kw_only: bool,
  474. lhs: NameExpr,
  475. rvalue: Expression,
  476. stmt: AssignmentStmt,
  477. ) -> Attribute:
  478. """Return an Attribute for a new type assignment."""
  479. name = unmangle(lhs.name)
  480. # `x: int` (without equal sign) assigns rvalue to TempNode(AnyType())
  481. has_rhs = not isinstance(rvalue, TempNode)
  482. sym = ctx.cls.info.names.get(name)
  483. init_type = sym.type if sym else None
  484. return Attribute(name, ctx.cls.info, has_rhs, True, kw_only, None, stmt, init_type)
  485. def _attribute_from_attrib_maker(
  486. ctx: mypy.plugin.ClassDefContext,
  487. auto_attribs: bool,
  488. kw_only: bool,
  489. lhs: NameExpr,
  490. rvalue: CallExpr,
  491. stmt: AssignmentStmt,
  492. ) -> Attribute | None:
  493. """Return an Attribute from the assignment or None if you can't make one."""
  494. if auto_attribs and not stmt.new_syntax:
  495. # auto_attribs requires an annotation on *every* attr.ib.
  496. assert lhs.node is not None
  497. ctx.api.msg.need_annotation_for_var(lhs.node, stmt)
  498. return None
  499. if len(stmt.lvalues) > 1:
  500. ctx.api.fail("Too many names for one attribute", stmt)
  501. return None
  502. # This is the type that belongs in the __init__ method for this attrib.
  503. init_type = stmt.type
  504. # Read all the arguments from the call.
  505. init = _get_bool_argument(ctx, rvalue, "init", True)
  506. # Note: If the class decorator says kw_only=True the attribute is ignored.
  507. # See https://github.com/python-attrs/attrs/issues/481 for explanation.
  508. kw_only |= _get_bool_argument(ctx, rvalue, "kw_only", False)
  509. # TODO: Check for attr.NOTHING
  510. attr_has_default = bool(_get_argument(rvalue, "default"))
  511. attr_has_factory = bool(_get_argument(rvalue, "factory"))
  512. if attr_has_default and attr_has_factory:
  513. ctx.api.fail('Can\'t pass both "default" and "factory".', rvalue)
  514. elif attr_has_factory:
  515. attr_has_default = True
  516. # If the type isn't set through annotation but is passed through `type=` use that.
  517. type_arg = _get_argument(rvalue, "type")
  518. if type_arg and not init_type:
  519. try:
  520. un_type = expr_to_unanalyzed_type(type_arg, ctx.api.options, ctx.api.is_stub_file)
  521. except TypeTranslationError:
  522. ctx.api.fail("Invalid argument to type", type_arg)
  523. else:
  524. init_type = ctx.api.anal_type(un_type)
  525. if init_type and isinstance(lhs.node, Var) and not lhs.node.type:
  526. # If there is no annotation, add one.
  527. lhs.node.type = init_type
  528. lhs.is_inferred_def = False
  529. # Note: convert is deprecated but works the same as converter.
  530. converter = _get_argument(rvalue, "converter")
  531. convert = _get_argument(rvalue, "convert")
  532. if convert and converter:
  533. ctx.api.fail('Can\'t pass both "convert" and "converter".', rvalue)
  534. elif convert:
  535. ctx.api.fail("convert is deprecated, use converter", rvalue)
  536. converter = convert
  537. converter_info = _parse_converter(ctx, converter)
  538. name = unmangle(lhs.name)
  539. return Attribute(
  540. name, ctx.cls.info, attr_has_default, init, kw_only, converter_info, stmt, init_type
  541. )
  542. def _parse_converter(
  543. ctx: mypy.plugin.ClassDefContext, converter_expr: Expression | None
  544. ) -> Converter | None:
  545. """Return the Converter object from an Expression."""
  546. # TODO: Support complex converters, e.g. lambdas, calls, etc.
  547. if not converter_expr:
  548. return None
  549. converter_info = Converter()
  550. if (
  551. isinstance(converter_expr, CallExpr)
  552. and isinstance(converter_expr.callee, RefExpr)
  553. and converter_expr.callee.fullname in attr_optional_converters
  554. and converter_expr.args
  555. and converter_expr.args[0]
  556. ):
  557. # Special handling for attr.converters.optional(type)
  558. # We extract the type and add make the init_args Optional in Attribute.argument
  559. converter_expr = converter_expr.args[0]
  560. is_attr_converters_optional = True
  561. else:
  562. is_attr_converters_optional = False
  563. converter_type: Type | None = None
  564. if isinstance(converter_expr, RefExpr) and converter_expr.node:
  565. if isinstance(converter_expr.node, FuncDef):
  566. if converter_expr.node.type and isinstance(converter_expr.node.type, FunctionLike):
  567. converter_type = converter_expr.node.type
  568. else: # The converter is an unannotated function.
  569. converter_info.init_type = AnyType(TypeOfAny.unannotated)
  570. return converter_info
  571. elif isinstance(converter_expr.node, OverloadedFuncDef) and is_valid_overloaded_converter(
  572. converter_expr.node
  573. ):
  574. converter_type = converter_expr.node.type
  575. elif isinstance(converter_expr.node, TypeInfo):
  576. from mypy.checkmember import type_object_type # To avoid import cycle.
  577. converter_type = type_object_type(converter_expr.node, ctx.api.named_type)
  578. elif (
  579. isinstance(converter_expr, IndexExpr)
  580. and isinstance(converter_expr.analyzed, TypeApplication)
  581. and isinstance(converter_expr.base, RefExpr)
  582. and isinstance(converter_expr.base.node, TypeInfo)
  583. ):
  584. # The converter is a generic type.
  585. from mypy.checkmember import type_object_type # To avoid import cycle.
  586. converter_type = type_object_type(converter_expr.base.node, ctx.api.named_type)
  587. if isinstance(converter_type, CallableType):
  588. converter_type = apply_generic_arguments(
  589. converter_type,
  590. converter_expr.analyzed.types,
  591. ctx.api.msg.incompatible_typevar_value,
  592. converter_type,
  593. )
  594. else:
  595. converter_type = None
  596. if isinstance(converter_expr, LambdaExpr):
  597. # TODO: should we send a fail if converter_expr.min_args > 1?
  598. converter_info.init_type = AnyType(TypeOfAny.unannotated)
  599. return converter_info
  600. if not converter_type:
  601. # Signal that we have an unsupported converter.
  602. ctx.api.fail(
  603. "Unsupported converter, only named functions, types and lambdas are currently "
  604. "supported",
  605. converter_expr,
  606. )
  607. converter_info.init_type = AnyType(TypeOfAny.from_error)
  608. return converter_info
  609. converter_type = get_proper_type(converter_type)
  610. if isinstance(converter_type, CallableType) and converter_type.arg_types:
  611. converter_info.init_type = converter_type.arg_types[0]
  612. if not is_attr_converters_optional:
  613. converter_info.ret_type = converter_type.ret_type
  614. elif isinstance(converter_type, Overloaded):
  615. types: list[Type] = []
  616. for item in converter_type.items:
  617. # Walk the overloads looking for methods that can accept one argument.
  618. num_arg_types = len(item.arg_types)
  619. if not num_arg_types:
  620. continue
  621. if num_arg_types > 1 and any(kind == ARG_POS for kind in item.arg_kinds[1:]):
  622. continue
  623. types.append(item.arg_types[0])
  624. # Make a union of all the valid types.
  625. if types:
  626. converter_info.init_type = make_simplified_union(types)
  627. if is_attr_converters_optional and converter_info.init_type:
  628. # If the converter was attr.converter.optional(type) then add None to
  629. # the allowed init_type.
  630. converter_info.init_type = UnionType.make_union([converter_info.init_type, NoneType()])
  631. return converter_info
  632. def is_valid_overloaded_converter(defn: OverloadedFuncDef) -> bool:
  633. return all(
  634. (not isinstance(item, Decorator) or isinstance(item.func.type, FunctionLike))
  635. for item in defn.items
  636. )
  637. def _parse_assignments(
  638. lvalue: Expression, stmt: AssignmentStmt
  639. ) -> tuple[list[NameExpr], list[Expression]]:
  640. """Convert a possibly complex assignment expression into lists of lvalues and rvalues."""
  641. lvalues: list[NameExpr] = []
  642. rvalues: list[Expression] = []
  643. if isinstance(lvalue, (TupleExpr, ListExpr)):
  644. if all(isinstance(item, NameExpr) for item in lvalue.items):
  645. lvalues = cast(List[NameExpr], lvalue.items)
  646. if isinstance(stmt.rvalue, (TupleExpr, ListExpr)):
  647. rvalues = stmt.rvalue.items
  648. elif isinstance(lvalue, NameExpr):
  649. lvalues = [lvalue]
  650. rvalues = [stmt.rvalue]
  651. return lvalues, rvalues
  652. def _add_order(ctx: mypy.plugin.ClassDefContext, adder: MethodAdder) -> None:
  653. """Generate all the ordering methods for this class."""
  654. bool_type = ctx.api.named_type("builtins.bool")
  655. object_type = ctx.api.named_type("builtins.object")
  656. # Make the types be:
  657. # AT = TypeVar('AT')
  658. # def __lt__(self: AT, other: AT) -> bool
  659. # This way comparisons with subclasses will work correctly.
  660. tvd = TypeVarType(
  661. SELF_TVAR_NAME,
  662. ctx.cls.info.fullname + "." + SELF_TVAR_NAME,
  663. id=-1,
  664. values=[],
  665. upper_bound=object_type,
  666. default=AnyType(TypeOfAny.from_omitted_generics),
  667. )
  668. self_tvar_expr = TypeVarExpr(
  669. SELF_TVAR_NAME,
  670. ctx.cls.info.fullname + "." + SELF_TVAR_NAME,
  671. [],
  672. object_type,
  673. AnyType(TypeOfAny.from_omitted_generics),
  674. )
  675. ctx.cls.info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr)
  676. args = [Argument(Var("other", tvd), tvd, None, ARG_POS)]
  677. for method in ["__lt__", "__le__", "__gt__", "__ge__"]:
  678. adder.add_method(method, args, bool_type, self_type=tvd, tvd=tvd)
  679. def _make_frozen(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute]) -> None:
  680. """Turn all the attributes into properties to simulate frozen classes."""
  681. for attribute in attributes:
  682. if attribute.name in ctx.cls.info.names:
  683. # This variable belongs to this class so we can modify it.
  684. node = ctx.cls.info.names[attribute.name].node
  685. if not isinstance(node, Var):
  686. # The superclass attribute was overridden with a non-variable.
  687. # No need to do anything here, override will be verified during
  688. # type checking.
  689. continue
  690. node.is_property = True
  691. else:
  692. # This variable belongs to a super class so create new Var so we
  693. # can modify it.
  694. var = Var(attribute.name, ctx.cls.info[attribute.name].type)
  695. var.info = ctx.cls.info
  696. var._fullname = f"{ctx.cls.info.fullname}.{var.name}"
  697. ctx.cls.info.names[var.name] = SymbolTableNode(MDEF, var)
  698. var.is_property = True
  699. def _add_init(
  700. ctx: mypy.plugin.ClassDefContext,
  701. attributes: list[Attribute],
  702. adder: MethodAdder,
  703. method_name: Literal["__init__", "__attrs_init__"],
  704. ) -> None:
  705. """Generate an __init__ method for the attributes and add it to the class."""
  706. # Convert attributes to arguments with kw_only arguments at the end of
  707. # the argument list
  708. pos_args = []
  709. kw_only_args = []
  710. sym_table = ctx.cls.info.names
  711. for attribute in attributes:
  712. if not attribute.init:
  713. continue
  714. if attribute.kw_only:
  715. kw_only_args.append(attribute.argument(ctx))
  716. else:
  717. pos_args.append(attribute.argument(ctx))
  718. # If the attribute is Final, present in `__init__` and has
  719. # no default, make sure it doesn't error later.
  720. if not attribute.has_default and attribute.name in sym_table:
  721. sym_node = sym_table[attribute.name].node
  722. if isinstance(sym_node, Var) and sym_node.is_final:
  723. sym_node.final_set_in_init = True
  724. args = pos_args + kw_only_args
  725. if all(
  726. # We use getattr rather than instance checks because the variable.type
  727. # might be wrapped into a Union or some other type, but even non-Any
  728. # types reliably track the fact that the argument was not annotated.
  729. getattr(arg.variable.type, "type_of_any", None) == TypeOfAny.unannotated
  730. for arg in args
  731. ):
  732. # This workaround makes --disallow-incomplete-defs usable with attrs,
  733. # but is definitely suboptimal as a long-term solution.
  734. # See https://github.com/python/mypy/issues/5954 for discussion.
  735. for a in args:
  736. a.variable.type = AnyType(TypeOfAny.implementation_artifact)
  737. a.type_annotation = AnyType(TypeOfAny.implementation_artifact)
  738. adder.add_method(method_name, args, NoneType())
  739. def _add_attrs_magic_attribute(
  740. ctx: mypy.plugin.ClassDefContext, attrs: list[tuple[str, Type | None]]
  741. ) -> None:
  742. any_type = AnyType(TypeOfAny.explicit)
  743. attributes_types: list[Type] = [
  744. ctx.api.named_type_or_none("attr.Attribute", [attr_type or any_type]) or any_type
  745. for _, attr_type in attrs
  746. ]
  747. fallback_type = ctx.api.named_type(
  748. "builtins.tuple", [ctx.api.named_type_or_none("attr.Attribute", [any_type]) or any_type]
  749. )
  750. attr_name = MAGIC_ATTR_CLS_NAME_TEMPLATE.format(ctx.cls.fullname.replace(".", "_"))
  751. ti = ctx.api.basic_new_typeinfo(attr_name, fallback_type, 0)
  752. for (name, _), attr_type in zip(attrs, attributes_types):
  753. var = Var(name, attr_type)
  754. var._fullname = name
  755. var.is_property = True
  756. proper_type = get_proper_type(attr_type)
  757. if isinstance(proper_type, Instance):
  758. var.info = proper_type.type
  759. ti.names[name] = SymbolTableNode(MDEF, var, plugin_generated=True)
  760. attributes_type = Instance(ti, [])
  761. # We need to stash the type of the magic attribute so it can be
  762. # loaded on cached runs.
  763. ctx.cls.info.names[attr_name] = SymbolTableNode(MDEF, ti, plugin_generated=True)
  764. add_attribute_to_class(
  765. ctx.api,
  766. ctx.cls,
  767. MAGIC_ATTR_NAME,
  768. TupleType(attributes_types, fallback=attributes_type),
  769. fullname=f"{ctx.cls.fullname}.{MAGIC_ATTR_NAME}",
  770. override_allow_incompatible=True,
  771. is_classvar=True,
  772. )
  773. def _add_slots(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute]) -> None:
  774. # Unlike `@dataclasses.dataclass`, `__slots__` is rewritten here.
  775. ctx.cls.info.slots = {attr.name for attr in attributes}
  776. # Also, inject `__slots__` attribute to class namespace:
  777. slots_type = TupleType(
  778. [ctx.api.named_type("builtins.str") for _ in attributes],
  779. fallback=ctx.api.named_type("builtins.tuple"),
  780. )
  781. add_attribute_to_class(api=ctx.api, cls=ctx.cls, name="__slots__", typ=slots_type)
  782. def _add_match_args(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute]) -> None:
  783. if (
  784. "__match_args__" not in ctx.cls.info.names
  785. or ctx.cls.info.names["__match_args__"].plugin_generated
  786. ):
  787. str_type = ctx.api.named_type("builtins.str")
  788. match_args = TupleType(
  789. [
  790. str_type.copy_modified(last_known_value=LiteralType(attr.name, fallback=str_type))
  791. for attr in attributes
  792. if not attr.kw_only and attr.init
  793. ],
  794. fallback=ctx.api.named_type("builtins.tuple"),
  795. )
  796. add_attribute_to_class(api=ctx.api, cls=ctx.cls, name="__match_args__", typ=match_args)
  797. class MethodAdder:
  798. """Helper to add methods to a TypeInfo.
  799. ctx: The ClassDefCtx we are using on which we will add methods.
  800. """
  801. # TODO: Combine this with the code build_namedtuple_typeinfo to support both.
  802. def __init__(self, ctx: mypy.plugin.ClassDefContext) -> None:
  803. self.ctx = ctx
  804. self.self_type = fill_typevars(ctx.cls.info)
  805. def add_method(
  806. self,
  807. method_name: str,
  808. args: list[Argument],
  809. ret_type: Type,
  810. self_type: Type | None = None,
  811. tvd: TypeVarType | None = None,
  812. ) -> None:
  813. """Add a method: def <method_name>(self, <args>) -> <ret_type>): ... to info.
  814. self_type: The type to use for the self argument or None to use the inferred self type.
  815. tvd: If the method is generic these should be the type variables.
  816. """
  817. self_type = self_type if self_type is not None else self.self_type
  818. add_method(self.ctx, method_name, args, ret_type, self_type, tvd)
  819. def _get_attrs_init_type(typ: Instance) -> CallableType | None:
  820. """
  821. If `typ` refers to an attrs class, get the type of its initializer method.
  822. """
  823. magic_attr = typ.type.get(MAGIC_ATTR_NAME)
  824. if magic_attr is None or not magic_attr.plugin_generated:
  825. return None
  826. init_method = typ.type.get_method("__init__") or typ.type.get_method(ATTRS_INIT_NAME)
  827. if not isinstance(init_method, FuncDef) or not isinstance(init_method.type, CallableType):
  828. return None
  829. return init_method.type
  830. def _fail_not_attrs_class(ctx: mypy.plugin.FunctionSigContext, t: Type, parent_t: Type) -> None:
  831. t_name = format_type_bare(t, ctx.api.options)
  832. if parent_t is t:
  833. msg = (
  834. f'Argument 1 to "evolve" has a variable type "{t_name}" not bound to an attrs class'
  835. if isinstance(t, TypeVarType)
  836. else f'Argument 1 to "evolve" has incompatible type "{t_name}"; expected an attrs class'
  837. )
  838. else:
  839. pt_name = format_type_bare(parent_t, ctx.api.options)
  840. msg = (
  841. f'Argument 1 to "evolve" has type "{pt_name}" whose item "{t_name}" is not bound to an attrs class'
  842. if isinstance(t, TypeVarType)
  843. else f'Argument 1 to "evolve" has incompatible type "{pt_name}" whose item "{t_name}" is not an attrs class'
  844. )
  845. ctx.api.fail(msg, ctx.context)
  846. def _get_expanded_attr_types(
  847. ctx: mypy.plugin.FunctionSigContext,
  848. typ: ProperType,
  849. display_typ: ProperType,
  850. parent_typ: ProperType,
  851. ) -> list[Mapping[str, Type]] | None:
  852. """
  853. For a given type, determine what attrs classes it can be: for each class, return the field types.
  854. For generic classes, the field types are expanded.
  855. If the type contains Any or a non-attrs type, returns None; in the latter case, also reports an error.
  856. """
  857. if isinstance(typ, AnyType):
  858. return None
  859. elif isinstance(typ, UnionType):
  860. ret: list[Mapping[str, Type]] | None = []
  861. for item in typ.relevant_items():
  862. item = get_proper_type(item)
  863. item_types = _get_expanded_attr_types(ctx, item, item, parent_typ)
  864. if ret is not None and item_types is not None:
  865. ret += item_types
  866. else:
  867. ret = None # but keep iterating to emit all errors
  868. return ret
  869. elif isinstance(typ, TypeVarType):
  870. return _get_expanded_attr_types(
  871. ctx, get_proper_type(typ.upper_bound), display_typ, parent_typ
  872. )
  873. elif isinstance(typ, Instance):
  874. init_func = _get_attrs_init_type(typ)
  875. if init_func is None:
  876. _fail_not_attrs_class(ctx, display_typ, parent_typ)
  877. return None
  878. init_func = expand_type_by_instance(init_func, typ)
  879. # [1:] to skip the self argument of AttrClass.__init__
  880. field_names = cast(List[str], init_func.arg_names[1:])
  881. field_types = init_func.arg_types[1:]
  882. return [dict(zip(field_names, field_types))]
  883. else:
  884. _fail_not_attrs_class(ctx, display_typ, parent_typ)
  885. return None
  886. def _meet_fields(types: list[Mapping[str, Type]]) -> Mapping[str, Type]:
  887. """
  888. "Meet" the fields of a list of attrs classes, i.e. for each field, its new type will be the lower bound.
  889. """
  890. field_to_types = defaultdict(list)
  891. for fields in types:
  892. for name, typ in fields.items():
  893. field_to_types[name].append(typ)
  894. return {
  895. name: get_proper_type(reduce(meet_types, f_types))
  896. if len(f_types) == len(types)
  897. else UninhabitedType()
  898. for name, f_types in field_to_types.items()
  899. }
  900. def evolve_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> CallableType:
  901. """
  902. Generate a signature for the 'attr.evolve' function that's specific to the call site
  903. and dependent on the type of the first argument.
  904. """
  905. if len(ctx.args) != 2:
  906. # Ideally the name and context should be callee's, but we don't have it in FunctionSigContext.
  907. ctx.api.fail(f'"{ctx.default_signature.name}" has unexpected type annotation', ctx.context)
  908. return ctx.default_signature
  909. if len(ctx.args[0]) != 1:
  910. return ctx.default_signature # leave it to the type checker to complain
  911. inst_arg = ctx.args[0][0]
  912. inst_type = get_proper_type(ctx.api.get_expression_type(inst_arg))
  913. inst_type_str = format_type_bare(inst_type, ctx.api.options)
  914. attr_types = _get_expanded_attr_types(ctx, inst_type, inst_type, inst_type)
  915. if attr_types is None:
  916. return ctx.default_signature
  917. fields = _meet_fields(attr_types)
  918. return CallableType(
  919. arg_names=["inst", *fields.keys()],
  920. arg_kinds=[ARG_POS] + [ARG_NAMED_OPT] * len(fields),
  921. arg_types=[inst_type, *fields.values()],
  922. ret_type=inst_type,
  923. fallback=ctx.default_signature.fallback,
  924. name=f"{ctx.default_signature.name} of {inst_type_str}",
  925. )