semanal_namedtuple.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. """Semantic analysis of named tuple definitions.
  2. This is conceptually part of mypy.semanal.
  3. """
  4. from __future__ import annotations
  5. from contextlib import contextmanager
  6. from typing import Iterator, List, Mapping, cast
  7. from typing_extensions import Final
  8. from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type
  9. from mypy.nodes import (
  10. ARG_NAMED_OPT,
  11. ARG_OPT,
  12. ARG_POS,
  13. MDEF,
  14. Argument,
  15. AssignmentStmt,
  16. Block,
  17. CallExpr,
  18. ClassDef,
  19. Context,
  20. Decorator,
  21. EllipsisExpr,
  22. Expression,
  23. ExpressionStmt,
  24. FuncBase,
  25. FuncDef,
  26. ListExpr,
  27. NamedTupleExpr,
  28. NameExpr,
  29. PassStmt,
  30. RefExpr,
  31. Statement,
  32. StrExpr,
  33. SymbolTable,
  34. SymbolTableNode,
  35. TempNode,
  36. TupleExpr,
  37. TypeInfo,
  38. TypeVarExpr,
  39. Var,
  40. is_StrExpr_list,
  41. )
  42. from mypy.options import Options
  43. from mypy.semanal_shared import (
  44. PRIORITY_FALLBACKS,
  45. SemanticAnalyzerInterface,
  46. calculate_tuple_fallback,
  47. has_placeholder,
  48. set_callable_name,
  49. )
  50. from mypy.types import (
  51. TYPED_NAMEDTUPLE_NAMES,
  52. AnyType,
  53. CallableType,
  54. LiteralType,
  55. TupleType,
  56. Type,
  57. TypeOfAny,
  58. TypeType,
  59. TypeVarLikeType,
  60. TypeVarType,
  61. UnboundType,
  62. has_type_vars,
  63. )
  64. from mypy.util import get_unique_redefinition_name
  65. # Matches "_prohibited" in typing.py, but adds __annotations__, which works at runtime but can't
  66. # easily be supported in a static checker.
  67. NAMEDTUPLE_PROHIBITED_NAMES: Final = (
  68. "__new__",
  69. "__init__",
  70. "__slots__",
  71. "__getnewargs__",
  72. "_fields",
  73. "_field_defaults",
  74. "_field_types",
  75. "_make",
  76. "_replace",
  77. "_asdict",
  78. "_source",
  79. "__annotations__",
  80. )
  81. NAMEDTUP_CLASS_ERROR: Final = (
  82. "Invalid statement in NamedTuple definition; " 'expected "field_name: field_type [= default]"'
  83. )
  84. SELF_TVAR_NAME: Final = "_NT"
  85. class NamedTupleAnalyzer:
  86. def __init__(self, options: Options, api: SemanticAnalyzerInterface) -> None:
  87. self.options = options
  88. self.api = api
  89. def analyze_namedtuple_classdef(
  90. self, defn: ClassDef, is_stub_file: bool, is_func_scope: bool
  91. ) -> tuple[bool, TypeInfo | None]:
  92. """Analyze if given class definition can be a named tuple definition.
  93. Return a tuple where first item indicates whether this can possibly be a named tuple,
  94. and the second item is the corresponding TypeInfo (may be None if not ready and should be
  95. deferred).
  96. """
  97. for base_expr in defn.base_type_exprs:
  98. if isinstance(base_expr, RefExpr):
  99. self.api.accept(base_expr)
  100. if base_expr.fullname in TYPED_NAMEDTUPLE_NAMES:
  101. result = self.check_namedtuple_classdef(defn, is_stub_file)
  102. if result is None:
  103. # This is a valid named tuple, but some types are incomplete.
  104. return True, None
  105. items, types, default_items, statements = result
  106. if is_func_scope and "@" not in defn.name:
  107. defn.name += "@" + str(defn.line)
  108. existing_info = None
  109. if isinstance(defn.analyzed, NamedTupleExpr):
  110. existing_info = defn.analyzed.info
  111. info = self.build_namedtuple_typeinfo(
  112. defn.name, items, types, default_items, defn.line, existing_info
  113. )
  114. defn.analyzed = NamedTupleExpr(info, is_typed=True)
  115. defn.analyzed.line = defn.line
  116. defn.analyzed.column = defn.column
  117. defn.defs.body = statements
  118. # All done: this is a valid named tuple with all types known.
  119. return True, info
  120. # This can't be a valid named tuple.
  121. return False, None
  122. def check_namedtuple_classdef(
  123. self, defn: ClassDef, is_stub_file: bool
  124. ) -> tuple[list[str], list[Type], dict[str, Expression], list[Statement]] | None:
  125. """Parse and validate fields in named tuple class definition.
  126. Return a four tuple:
  127. * field names
  128. * field types
  129. * field default values
  130. * valid statements
  131. or None, if any of the types are not ready.
  132. """
  133. if self.options.python_version < (3, 6) and not is_stub_file:
  134. self.fail("NamedTuple class syntax is only supported in Python 3.6", defn)
  135. return [], [], {}, []
  136. if len(defn.base_type_exprs) > 1:
  137. self.fail("NamedTuple should be a single base", defn)
  138. items: list[str] = []
  139. types: list[Type] = []
  140. default_items: dict[str, Expression] = {}
  141. statements: list[Statement] = []
  142. for stmt in defn.defs.body:
  143. statements.append(stmt)
  144. if not isinstance(stmt, AssignmentStmt):
  145. # Still allow pass or ... (for empty namedtuples).
  146. if isinstance(stmt, PassStmt) or (
  147. isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, EllipsisExpr)
  148. ):
  149. continue
  150. # Also allow methods, including decorated ones.
  151. if isinstance(stmt, (Decorator, FuncBase)):
  152. continue
  153. # And docstrings.
  154. if isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, StrExpr):
  155. continue
  156. statements.pop()
  157. defn.removed_statements.append(stmt)
  158. self.fail(NAMEDTUP_CLASS_ERROR, stmt)
  159. elif len(stmt.lvalues) > 1 or not isinstance(stmt.lvalues[0], NameExpr):
  160. # An assignment, but an invalid one.
  161. statements.pop()
  162. defn.removed_statements.append(stmt)
  163. self.fail(NAMEDTUP_CLASS_ERROR, stmt)
  164. else:
  165. # Append name and type in this case...
  166. name = stmt.lvalues[0].name
  167. items.append(name)
  168. if stmt.type is None:
  169. types.append(AnyType(TypeOfAny.unannotated))
  170. else:
  171. # We never allow recursive types at function scope. Although it is
  172. # possible to support this for named tuples, it is still tricky, and
  173. # it would be inconsistent with type aliases.
  174. analyzed = self.api.anal_type(
  175. stmt.type,
  176. allow_placeholder=not self.options.disable_recursive_aliases
  177. and not self.api.is_func_scope(),
  178. prohibit_self_type="NamedTuple item type",
  179. )
  180. if analyzed is None:
  181. # Something is incomplete. We need to defer this named tuple.
  182. return None
  183. types.append(analyzed)
  184. # ...despite possible minor failures that allow further analyzis.
  185. if name.startswith("_"):
  186. self.fail(
  187. f"NamedTuple field name cannot start with an underscore: {name}", stmt
  188. )
  189. if stmt.type is None or hasattr(stmt, "new_syntax") and not stmt.new_syntax:
  190. self.fail(NAMEDTUP_CLASS_ERROR, stmt)
  191. elif isinstance(stmt.rvalue, TempNode):
  192. # x: int assigns rvalue to TempNode(AnyType())
  193. if default_items:
  194. self.fail(
  195. "Non-default NamedTuple fields cannot follow default fields", stmt
  196. )
  197. else:
  198. default_items[name] = stmt.rvalue
  199. return items, types, default_items, statements
  200. def check_namedtuple(
  201. self, node: Expression, var_name: str | None, is_func_scope: bool
  202. ) -> tuple[str | None, TypeInfo | None, list[TypeVarLikeType]]:
  203. """Check if a call defines a namedtuple.
  204. The optional var_name argument is the name of the variable to
  205. which this is assigned, if any.
  206. Return a tuple of two items:
  207. * Internal name of the named tuple (e.g. the name passed as an argument to namedtuple)
  208. or None if it is not a valid named tuple
  209. * Corresponding TypeInfo, or None if not ready.
  210. If the definition is invalid but looks like a namedtuple,
  211. report errors but return (some) TypeInfo.
  212. """
  213. if not isinstance(node, CallExpr):
  214. return None, None, []
  215. call = node
  216. callee = call.callee
  217. if not isinstance(callee, RefExpr):
  218. return None, None, []
  219. fullname = callee.fullname
  220. if fullname == "collections.namedtuple":
  221. is_typed = False
  222. elif fullname in TYPED_NAMEDTUPLE_NAMES:
  223. is_typed = True
  224. else:
  225. return None, None, []
  226. result = self.parse_namedtuple_args(call, fullname)
  227. if result:
  228. items, types, defaults, typename, tvar_defs, ok = result
  229. else:
  230. # Error. Construct dummy return value.
  231. if var_name:
  232. name = var_name
  233. if is_func_scope:
  234. name += "@" + str(call.line)
  235. else:
  236. name = var_name = "namedtuple@" + str(call.line)
  237. info = self.build_namedtuple_typeinfo(name, [], [], {}, node.line, None)
  238. self.store_namedtuple_info(info, var_name, call, is_typed)
  239. if name != var_name or is_func_scope:
  240. # NOTE: we skip local namespaces since they are not serialized.
  241. self.api.add_symbol_skip_local(name, info)
  242. return var_name, info, []
  243. if not ok:
  244. # This is a valid named tuple but some types are not ready.
  245. return typename, None, []
  246. # We use the variable name as the class name if it exists. If
  247. # it doesn't, we use the name passed as an argument. We prefer
  248. # the variable name because it should be unique inside a
  249. # module, and so we don't need to disambiguate it with a line
  250. # number.
  251. if var_name:
  252. name = var_name
  253. else:
  254. name = typename
  255. if var_name is None or is_func_scope:
  256. # There are two special cases where need to give it a unique name derived
  257. # from the line number:
  258. # * This is a base class expression, since it often matches the class name:
  259. # class NT(NamedTuple('NT', [...])):
  260. # ...
  261. # * This is a local (function or method level) named tuple, since
  262. # two methods of a class can define a named tuple with the same name,
  263. # and they will be stored in the same namespace (see below).
  264. name += "@" + str(call.line)
  265. if defaults:
  266. default_items = {
  267. arg_name: default for arg_name, default in zip(items[-len(defaults) :], defaults)
  268. }
  269. else:
  270. default_items = {}
  271. existing_info = None
  272. if isinstance(node.analyzed, NamedTupleExpr):
  273. existing_info = node.analyzed.info
  274. info = self.build_namedtuple_typeinfo(
  275. name, items, types, default_items, node.line, existing_info
  276. )
  277. # If var_name is not None (i.e. this is not a base class expression), we always
  278. # store the generated TypeInfo under var_name in the current scope, so that
  279. # other definitions can use it.
  280. if var_name:
  281. self.store_namedtuple_info(info, var_name, call, is_typed)
  282. else:
  283. call.analyzed = NamedTupleExpr(info, is_typed=is_typed)
  284. call.analyzed.set_line(call)
  285. # There are three cases where we need to store the generated TypeInfo
  286. # second time (for the purpose of serialization):
  287. # * If there is a name mismatch like One = NamedTuple('Other', [...])
  288. # we also store the info under name 'Other@lineno', this is needed
  289. # because classes are (de)serialized using their actual fullname, not
  290. # the name of l.h.s.
  291. # * If this is a method level named tuple. It can leak from the method
  292. # via assignment to self attribute and therefore needs to be serialized
  293. # (local namespaces are not serialized).
  294. # * If it is a base class expression. It was not stored above, since
  295. # there is no var_name (but it still needs to be serialized
  296. # since it is in MRO of some class).
  297. if name != var_name or is_func_scope:
  298. # NOTE: we skip local namespaces since they are not serialized.
  299. self.api.add_symbol_skip_local(name, info)
  300. return typename, info, tvar_defs
  301. def store_namedtuple_info(
  302. self, info: TypeInfo, name: str, call: CallExpr, is_typed: bool
  303. ) -> None:
  304. self.api.add_symbol(name, info, call)
  305. call.analyzed = NamedTupleExpr(info, is_typed=is_typed)
  306. call.analyzed.set_line(call)
  307. def parse_namedtuple_args(
  308. self, call: CallExpr, fullname: str
  309. ) -> None | (tuple[list[str], list[Type], list[Expression], str, list[TypeVarLikeType], bool]):
  310. """Parse a namedtuple() call into data needed to construct a type.
  311. Returns a 6-tuple:
  312. - List of argument names
  313. - List of argument types
  314. - List of default values
  315. - First argument of namedtuple
  316. - All typevars found in the field definition
  317. - Whether all types are ready.
  318. Return None if the definition didn't typecheck.
  319. """
  320. type_name = "NamedTuple" if fullname in TYPED_NAMEDTUPLE_NAMES else "namedtuple"
  321. # TODO: Share code with check_argument_count in checkexpr.py?
  322. args = call.args
  323. if len(args) < 2:
  324. self.fail(f'Too few arguments for "{type_name}()"', call)
  325. return None
  326. defaults: list[Expression] = []
  327. if len(args) > 2:
  328. # Typed namedtuple doesn't support additional arguments.
  329. if fullname in TYPED_NAMEDTUPLE_NAMES:
  330. self.fail('Too many arguments for "NamedTuple()"', call)
  331. return None
  332. for i, arg_name in enumerate(call.arg_names[2:], 2):
  333. if arg_name == "defaults":
  334. arg = args[i]
  335. # We don't care what the values are, as long as the argument is an iterable
  336. # and we can count how many defaults there are.
  337. if isinstance(arg, (ListExpr, TupleExpr)):
  338. defaults = list(arg.items)
  339. else:
  340. self.fail(
  341. "List or tuple literal expected as the defaults argument to "
  342. "{}()".format(type_name),
  343. arg,
  344. )
  345. break
  346. if call.arg_kinds[:2] != [ARG_POS, ARG_POS]:
  347. self.fail(f'Unexpected arguments to "{type_name}()"', call)
  348. return None
  349. if not isinstance(args[0], StrExpr):
  350. self.fail(f'"{type_name}()" expects a string literal as the first argument', call)
  351. return None
  352. typename = args[0].value
  353. types: list[Type] = []
  354. tvar_defs = []
  355. if not isinstance(args[1], (ListExpr, TupleExpr)):
  356. if fullname == "collections.namedtuple" and isinstance(args[1], StrExpr):
  357. str_expr = args[1]
  358. items = str_expr.value.replace(",", " ").split()
  359. else:
  360. self.fail(
  361. 'List or tuple literal expected as the second argument to "{}()"'.format(
  362. type_name
  363. ),
  364. call,
  365. )
  366. return None
  367. else:
  368. listexpr = args[1]
  369. if fullname == "collections.namedtuple":
  370. # The fields argument contains just names, with implicit Any types.
  371. if not is_StrExpr_list(listexpr.items):
  372. self.fail('String literal expected as "namedtuple()" item', call)
  373. return None
  374. items = [item.value for item in listexpr.items]
  375. else:
  376. type_exprs = [
  377. t.items[1]
  378. for t in listexpr.items
  379. if isinstance(t, TupleExpr) and len(t.items) == 2
  380. ]
  381. tvar_defs = self.api.get_and_bind_all_tvars(type_exprs)
  382. # The fields argument contains (name, type) tuples.
  383. result = self.parse_namedtuple_fields_with_types(listexpr.items, call)
  384. if result is None:
  385. # One of the types is not ready, defer.
  386. return None
  387. items, types, _, ok = result
  388. if not ok:
  389. return [], [], [], typename, [], False
  390. if not types:
  391. types = [AnyType(TypeOfAny.unannotated) for _ in items]
  392. underscore = [item for item in items if item.startswith("_")]
  393. if underscore:
  394. self.fail(
  395. f'"{type_name}()" field names cannot start with an underscore: '
  396. + ", ".join(underscore),
  397. call,
  398. )
  399. if len(defaults) > len(items):
  400. self.fail(f'Too many defaults given in call to "{type_name}()"', call)
  401. defaults = defaults[: len(items)]
  402. return items, types, defaults, typename, tvar_defs, True
  403. def parse_namedtuple_fields_with_types(
  404. self, nodes: list[Expression], context: Context
  405. ) -> tuple[list[str], list[Type], list[Expression], bool] | None:
  406. """Parse typed named tuple fields.
  407. Return (names, types, defaults, whether types are all ready), or None if error occurred.
  408. """
  409. items: list[str] = []
  410. types: list[Type] = []
  411. for item in nodes:
  412. if isinstance(item, TupleExpr):
  413. if len(item.items) != 2:
  414. self.fail('Invalid "NamedTuple()" field definition', item)
  415. return None
  416. name, type_node = item.items
  417. if isinstance(name, StrExpr):
  418. items.append(name.value)
  419. else:
  420. self.fail('Invalid "NamedTuple()" field name', item)
  421. return None
  422. try:
  423. type = expr_to_unanalyzed_type(type_node, self.options, self.api.is_stub_file)
  424. except TypeTranslationError:
  425. self.fail("Invalid field type", type_node)
  426. return None
  427. # We never allow recursive types at function scope.
  428. analyzed = self.api.anal_type(
  429. type,
  430. allow_placeholder=not self.options.disable_recursive_aliases
  431. and not self.api.is_func_scope(),
  432. prohibit_self_type="NamedTuple item type",
  433. )
  434. # Workaround #4987 and avoid introducing a bogus UnboundType
  435. if isinstance(analyzed, UnboundType):
  436. analyzed = AnyType(TypeOfAny.from_error)
  437. # These should be all known, otherwise we would defer in visit_assignment_stmt().
  438. if analyzed is None:
  439. return [], [], [], False
  440. types.append(analyzed)
  441. else:
  442. self.fail('Tuple expected as "NamedTuple()" field', item)
  443. return None
  444. return items, types, [], True
  445. def build_namedtuple_typeinfo(
  446. self,
  447. name: str,
  448. items: list[str],
  449. types: list[Type],
  450. default_items: Mapping[str, Expression],
  451. line: int,
  452. existing_info: TypeInfo | None,
  453. ) -> TypeInfo:
  454. strtype = self.api.named_type("builtins.str")
  455. implicit_any = AnyType(TypeOfAny.special_form)
  456. basetuple_type = self.api.named_type("builtins.tuple", [implicit_any])
  457. dictype = self.api.named_type("builtins.dict", [strtype, implicit_any])
  458. # Actual signature should return OrderedDict[str, Union[types]]
  459. ordereddictype = self.api.named_type("builtins.dict", [strtype, implicit_any])
  460. fallback = self.api.named_type("builtins.tuple", [implicit_any])
  461. # Note: actual signature should accept an invariant version of Iterable[UnionType[types]].
  462. # but it can't be expressed. 'new' and 'len' should be callable types.
  463. iterable_type = self.api.named_type_or_none("typing.Iterable", [implicit_any])
  464. function_type = self.api.named_type("builtins.function")
  465. literals: list[Type] = [LiteralType(item, strtype) for item in items]
  466. match_args_type = TupleType(literals, basetuple_type)
  467. info = existing_info or self.api.basic_new_typeinfo(name, fallback, line)
  468. info.is_named_tuple = True
  469. tuple_base = TupleType(types, fallback)
  470. if info.special_alias and has_placeholder(info.special_alias.target):
  471. self.api.process_placeholder(
  472. None, "NamedTuple item", info, force_progress=tuple_base != info.tuple_type
  473. )
  474. info.update_tuple_type(tuple_base)
  475. info.line = line
  476. # For use by mypyc.
  477. info.metadata["namedtuple"] = {"fields": items.copy()}
  478. # We can't calculate the complete fallback type until after semantic
  479. # analysis, since otherwise base classes might be incomplete. Postpone a
  480. # callback function that patches the fallback.
  481. if not has_placeholder(tuple_base) and not has_type_vars(tuple_base):
  482. self.api.schedule_patch(
  483. PRIORITY_FALLBACKS, lambda: calculate_tuple_fallback(tuple_base)
  484. )
  485. def add_field(
  486. var: Var, is_initialized_in_class: bool = False, is_property: bool = False
  487. ) -> None:
  488. var.info = info
  489. var.is_initialized_in_class = is_initialized_in_class
  490. var.is_property = is_property
  491. var._fullname = f"{info.fullname}.{var.name}"
  492. info.names[var.name] = SymbolTableNode(MDEF, var)
  493. fields = [Var(item, typ) for item, typ in zip(items, types)]
  494. for var in fields:
  495. add_field(var, is_property=True)
  496. # We can't share Vars between fields and method arguments, since they
  497. # have different full names (the latter are normally used as local variables
  498. # in functions, so their full names are set to short names when generated methods
  499. # are analyzed).
  500. vars = [Var(item, typ) for item, typ in zip(items, types)]
  501. tuple_of_strings = TupleType([strtype for _ in items], basetuple_type)
  502. add_field(Var("_fields", tuple_of_strings), is_initialized_in_class=True)
  503. add_field(Var("_field_types", dictype), is_initialized_in_class=True)
  504. add_field(Var("_field_defaults", dictype), is_initialized_in_class=True)
  505. add_field(Var("_source", strtype), is_initialized_in_class=True)
  506. add_field(Var("__annotations__", ordereddictype), is_initialized_in_class=True)
  507. add_field(Var("__doc__", strtype), is_initialized_in_class=True)
  508. if self.options.python_version >= (3, 10):
  509. add_field(Var("__match_args__", match_args_type), is_initialized_in_class=True)
  510. assert info.tuple_type is not None # Set by update_tuple_type() above.
  511. tvd = TypeVarType(
  512. name=SELF_TVAR_NAME,
  513. fullname=info.fullname + "." + SELF_TVAR_NAME,
  514. id=self.api.tvar_scope.new_unique_func_id(),
  515. values=[],
  516. upper_bound=info.tuple_type,
  517. default=AnyType(TypeOfAny.from_omitted_generics),
  518. )
  519. selftype = tvd
  520. def add_method(
  521. funcname: str,
  522. ret: Type,
  523. args: list[Argument],
  524. is_classmethod: bool = False,
  525. is_new: bool = False,
  526. ) -> None:
  527. if is_classmethod or is_new:
  528. first = [Argument(Var("_cls"), TypeType.make_normalized(selftype), None, ARG_POS)]
  529. else:
  530. first = [Argument(Var("_self"), selftype, None, ARG_POS)]
  531. args = first + args
  532. types = [arg.type_annotation for arg in args]
  533. items = [arg.variable.name for arg in args]
  534. arg_kinds = [arg.kind for arg in args]
  535. assert None not in types
  536. signature = CallableType(cast(List[Type], types), arg_kinds, items, ret, function_type)
  537. signature.variables = [tvd]
  538. func = FuncDef(funcname, args, Block([]))
  539. func.info = info
  540. func.is_class = is_classmethod
  541. func.type = set_callable_name(signature, func)
  542. func._fullname = info.fullname + "." + funcname
  543. func.line = line
  544. if is_classmethod:
  545. v = Var(funcname, func.type)
  546. v.is_classmethod = True
  547. v.info = info
  548. v._fullname = func._fullname
  549. func.is_decorated = True
  550. dec = Decorator(func, [NameExpr("classmethod")], v)
  551. dec.line = line
  552. sym = SymbolTableNode(MDEF, dec)
  553. else:
  554. sym = SymbolTableNode(MDEF, func)
  555. sym.plugin_generated = True
  556. info.names[funcname] = sym
  557. add_method(
  558. "_replace",
  559. ret=selftype,
  560. args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT) for var in vars],
  561. )
  562. def make_init_arg(var: Var) -> Argument:
  563. default = default_items.get(var.name, None)
  564. kind = ARG_POS if default is None else ARG_OPT
  565. return Argument(var, var.type, default, kind)
  566. add_method("__new__", ret=selftype, args=[make_init_arg(var) for var in vars], is_new=True)
  567. add_method("_asdict", args=[], ret=ordereddictype)
  568. special_form_any = AnyType(TypeOfAny.special_form)
  569. add_method(
  570. "_make",
  571. ret=selftype,
  572. is_classmethod=True,
  573. args=[
  574. Argument(Var("iterable", iterable_type), iterable_type, None, ARG_POS),
  575. Argument(Var("new"), special_form_any, EllipsisExpr(), ARG_NAMED_OPT),
  576. Argument(Var("len"), special_form_any, EllipsisExpr(), ARG_NAMED_OPT),
  577. ],
  578. )
  579. self_tvar_expr = TypeVarExpr(
  580. SELF_TVAR_NAME,
  581. info.fullname + "." + SELF_TVAR_NAME,
  582. [],
  583. info.tuple_type,
  584. AnyType(TypeOfAny.from_omitted_generics),
  585. )
  586. info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr)
  587. return info
  588. @contextmanager
  589. def save_namedtuple_body(self, named_tuple_info: TypeInfo) -> Iterator[None]:
  590. """Preserve the generated body of class-based named tuple and then restore it.
  591. Temporarily clear the names dict so we don't get errors about duplicate names
  592. that were already set in build_namedtuple_typeinfo (we already added the tuple
  593. field names while generating the TypeInfo, and actual duplicates are
  594. already reported).
  595. """
  596. nt_names = named_tuple_info.names
  597. named_tuple_info.names = SymbolTable()
  598. yield
  599. # Make sure we didn't use illegal names, then reset the names in the typeinfo.
  600. for prohibited in NAMEDTUPLE_PROHIBITED_NAMES:
  601. if prohibited in named_tuple_info.names:
  602. if nt_names.get(prohibited) is named_tuple_info.names[prohibited]:
  603. continue
  604. ctx = named_tuple_info.names[prohibited].node
  605. assert ctx is not None
  606. self.fail(f'Cannot overwrite NamedTuple attribute "{prohibited}"', ctx)
  607. # Restore the names in the original symbol table. This ensures that the symbol
  608. # table contains the field objects created by build_namedtuple_typeinfo. Exclude
  609. # __doc__, which can legally be overwritten by the class.
  610. for key, value in nt_names.items():
  611. if key in named_tuple_info.names:
  612. if key == "__doc__":
  613. continue
  614. sym = named_tuple_info.names[key]
  615. if isinstance(sym.node, (FuncBase, Decorator)) and not sym.plugin_generated:
  616. # Keep user-defined methods as is.
  617. continue
  618. # Keep existing (user-provided) definitions under mangled names, so they
  619. # get semantically analyzed.
  620. r_key = get_unique_redefinition_name(key, named_tuple_info.names)
  621. named_tuple_info.names[r_key] = sym
  622. named_tuple_info.names[key] = value
  623. # Helpers
  624. def fail(self, msg: str, ctx: Context) -> None:
  625. self.api.fail(msg, ctx)