Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/astroid/brain/brain_dataclasses.py: 31%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

360 statements  

1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html 

2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE 

3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt 

4 

5""" 

6Astroid hook for the dataclasses library. 

7 

8Support built-in dataclasses, pydantic.dataclasses, and marshmallow_dataclass-annotated 

9dataclasses. References: 

10- https://docs.python.org/3/library/dataclasses.html 

11- https://pydantic-docs.helpmanual.io/usage/dataclasses/ 

12- https://lovasoa.github.io/marshmallow_dataclass/ 

13""" 

14 

15from __future__ import annotations 

16 

17from collections.abc import Iterator 

18from typing import Literal 

19 

20from astroid import bases, context, nodes 

21from astroid.brain.helpers import is_class_var 

22from astroid.builder import parse 

23from astroid.const import PY313_PLUS 

24from astroid.exceptions import ( 

25 AstroidSyntaxError, 

26 InferenceError, 

27 MroError, 

28 UseInferenceDefault, 

29) 

30from astroid.inference_tip import inference_tip 

31from astroid.manager import AstroidManager 

32from astroid.typing import InferenceResult 

33from astroid.util import Uninferable, UninferableBase, safe_infer 

34 

35_FieldDefaultReturn = ( 

36 None 

37 | tuple[Literal["default"], nodes.NodeNG] 

38 | tuple[Literal["default_factory"], nodes.Call] 

39) 

40 

41DATACLASSES_DECORATORS = frozenset(("dataclass",)) 

42FIELD_NAME = "field" 

43DATACLASS_MODULES = frozenset( 

44 ("dataclasses", "marshmallow_dataclass", "pydantic.dataclasses") 

45) 

46DEFAULT_FACTORY = "_HAS_DEFAULT_FACTORY" # based on typing.py 

47 

48 

49def is_decorated_with_dataclass( 

50 node: nodes.ClassDef, decorator_names: frozenset[str] = DATACLASSES_DECORATORS 

51) -> bool: 

52 """Return True if a decorated node has a `dataclass` decorator applied.""" 

53 if not (isinstance(node, nodes.ClassDef) and node.decorators): 

54 return False 

55 

56 return any( 

57 _looks_like_dataclass_decorator(decorator_attribute, decorator_names) 

58 for decorator_attribute in node.decorators.nodes 

59 ) 

60 

61 

62def dataclass_transform(node: nodes.ClassDef) -> nodes.ClassDef | None: 

63 """Rewrite a dataclass to be easily understood by pylint.""" 

64 node.is_dataclass = True 

65 

66 for assign_node in _get_dataclass_attributes(node): 

67 name = assign_node.target.name 

68 

69 rhs_node = nodes.Unknown( 

70 lineno=assign_node.lineno, 

71 col_offset=assign_node.col_offset, 

72 parent=assign_node, 

73 ) 

74 rhs_node = AstroidManager().visit_transforms(rhs_node) 

75 node.instance_attrs[name] = [rhs_node] 

76 

77 if not _check_generate_dataclass_init(node): 

78 return None 

79 

80 kw_only_decorated = False 

81 if node.decorators.nodes: 

82 for decorator in node.decorators.nodes: 

83 if not isinstance(decorator, nodes.Call): 

84 kw_only_decorated = False 

85 break 

86 for keyword in decorator.keywords: 

87 if keyword.arg == "kw_only": 

88 kw_only_decorated = keyword.value.bool_value() is True 

89 

90 init_str = _generate_dataclass_init( 

91 node, 

92 list(_get_dataclass_attributes(node, init=True)), 

93 kw_only_decorated, 

94 ) 

95 

96 try: 

97 init_node = parse(init_str)["__init__"] 

98 except AstroidSyntaxError: 

99 pass 

100 else: 

101 init_node.parent = node 

102 init_node.lineno, init_node.col_offset = None, None 

103 node.locals["__init__"] = [init_node] 

104 

105 root = node.root() 

106 if DEFAULT_FACTORY in init_str and DEFAULT_FACTORY not in root.locals: 

107 new_assign = parse(f"{DEFAULT_FACTORY} = object()").body[0] 

108 new_assign.parent = root 

109 root.locals[DEFAULT_FACTORY] = [new_assign.targets[0]] 

110 return node 

111 

112 

113def _get_dataclass_attributes( 

114 node: nodes.ClassDef, init: bool = False 

115) -> Iterator[nodes.AnnAssign]: 

116 """Yield the AnnAssign nodes of dataclass attributes for the node. 

117 

118 If init is True, also include InitVars. 

119 """ 

120 for assign_node in node.body: 

121 if not ( 

122 isinstance(assign_node, nodes.AnnAssign) 

123 and isinstance(assign_node.target, nodes.AssignName) 

124 ): 

125 continue 

126 

127 # Annotation is never None 

128 if is_class_var(assign_node.annotation): # type: ignore[arg-type] 

129 continue 

130 

131 if _is_keyword_only_sentinel(assign_node.annotation): 

132 continue 

133 

134 # Annotation is never None 

135 if not init and _is_init_var(assign_node.annotation): # type: ignore[arg-type] 

136 continue 

137 

138 yield assign_node 

139 

140 

141def _check_generate_dataclass_init(node: nodes.ClassDef) -> bool: 

142 """Return True if we should generate an __init__ method for node. 

143 

144 This is True when: 

145 - node doesn't define its own __init__ method 

146 - the dataclass decorator was called *without* the keyword argument init=False 

147 """ 

148 if "__init__" in node.locals: 

149 return False 

150 

151 found = None 

152 

153 for decorator_attribute in node.decorators.nodes: 

154 if not isinstance(decorator_attribute, nodes.Call): 

155 continue 

156 

157 if _looks_like_dataclass_decorator(decorator_attribute): 

158 found = decorator_attribute 

159 

160 if found is None: 

161 return True 

162 

163 # Check for keyword arguments of the form init=False 

164 return not any( 

165 keyword.arg == "init" 

166 and keyword.value.bool_value() is False # type: ignore[union-attr] # value is never None 

167 for keyword in found.keywords 

168 ) 

169 

170 

171def _find_arguments_from_base_classes( 

172 node: nodes.ClassDef, 

173) -> tuple[ 

174 dict[str, tuple[str | None, str | None]], dict[str, tuple[str | None, str | None]] 

175]: 

176 """Iterate through all bases and get their typing and defaults.""" 

177 pos_only_store: dict[str, tuple[str | None, str | None]] = {} 

178 kw_only_store: dict[str, tuple[str | None, str | None]] = {} 

179 # See TODO down below 

180 # all_have_defaults = True 

181 

182 try: 

183 mro = node.mro() 

184 except MroError: 

185 return pos_only_store, kw_only_store 

186 

187 for base in reversed(mro): 

188 if not base.is_dataclass: 

189 continue 

190 try: 

191 base_init = base.locals["__init__"][0] 

192 except KeyError: 

193 continue 

194 

195 # A base can bind "__init__" to something that is not a function, for 

196 # example by annotating it as a field: "__init__: int". There are no 

197 # arguments to inherit from such a base. 

198 if not isinstance(base_init, nodes.FunctionDef): 

199 continue 

200 

201 pos_only, kw_only = base_init.args._get_arguments_data() 

202 for posarg, data in pos_only.items(): 

203 # if data[1] is None: 

204 # if all_have_defaults and pos_only_store: 

205 # # TODO: This should return an Uninferable as this would raise 

206 # # a TypeError at runtime. However, transforms can't return 

207 # # Uninferables currently. 

208 # pass 

209 # all_have_defaults = False 

210 pos_only_store[posarg] = data 

211 

212 for kwarg, data in kw_only.items(): 

213 kw_only_store[kwarg] = data 

214 return pos_only_store, kw_only_store 

215 

216 

217def _parse_arguments_into_strings( 

218 pos_only_store: dict[str, tuple[str | None, str | None]], 

219 kw_only_store: dict[str, tuple[str | None, str | None]], 

220) -> tuple[str, str]: 

221 """Parse positional and keyword arguments into strings for an __init__ method.""" 

222 pos_only, kw_only = "", "" 

223 for pos_arg, data in pos_only_store.items(): 

224 pos_only += pos_arg 

225 if data[0]: 

226 pos_only += ": " + data[0] 

227 if data[1]: 

228 pos_only += " = " + data[1] 

229 pos_only += ", " 

230 for kw_arg, data in kw_only_store.items(): 

231 kw_only += kw_arg 

232 if data[0]: 

233 kw_only += ": " + data[0] 

234 if data[1]: 

235 kw_only += " = " + data[1] 

236 kw_only += ", " 

237 

238 return pos_only, kw_only 

239 

240 

241def _get_previous_field_default(node: nodes.ClassDef, name: str) -> nodes.NodeNG | None: 

242 """Get the default value of a previously defined field.""" 

243 try: 

244 mro = node.mro() 

245 except MroError: 

246 return None 

247 

248 for base in reversed(mro): 

249 if not base.is_dataclass: 

250 continue 

251 if name in base.locals: 

252 for assign in base.locals[name]: 

253 if ( 

254 isinstance(assign.parent, nodes.AnnAssign) 

255 and assign.parent.value 

256 and isinstance(assign.parent.value, nodes.Call) 

257 and _looks_like_dataclass_field_call(assign.parent.value) 

258 ): 

259 default = _get_field_default(assign.parent.value) 

260 if default: 

261 return default[1] 

262 return None 

263 

264 

265def _generate_dataclass_init( 

266 node: nodes.ClassDef, assigns: list[nodes.AnnAssign], kw_only_decorated: bool 

267) -> str: 

268 """Return an init method for a dataclass given the targets.""" 

269 # pylint: disable = too-many-locals, too-many-branches, too-many-statements 

270 

271 params: list[str] = [] 

272 kw_only_params: list[str] = [] 

273 assignments: list[str] = [] 

274 

275 prev_pos_only_store, prev_kw_only_store = _find_arguments_from_base_classes(node) 

276 

277 for assign in assigns: 

278 name, annotation, value = assign.target.name, assign.annotation, assign.value 

279 

280 # Check whether this assign is overriden by a property assignment 

281 property_node: nodes.FunctionDef | None = None 

282 for additional_assign in node.locals[name]: 

283 if not isinstance(additional_assign, nodes.FunctionDef): 

284 continue 

285 if not additional_assign.decorators: 

286 continue 

287 if "builtins.property" in additional_assign.decoratornames(): 

288 property_node = additional_assign 

289 break 

290 

291 is_field = isinstance(value, nodes.Call) and _looks_like_dataclass_field_call( 

292 value, check_scope=False 

293 ) 

294 

295 if is_field: 

296 # Skip any fields that have `init=False` 

297 if any( 

298 keyword.arg == "init" and (keyword.value.bool_value() is False) 

299 for keyword in value.keywords # type: ignore[union-attr] # value is never None 

300 ): 

301 # Also remove the name from the previous arguments to be inserted later 

302 prev_pos_only_store.pop(name, None) 

303 prev_kw_only_store.pop(name, None) 

304 continue 

305 

306 if _is_init_var(annotation): # type: ignore[arg-type] # annotation is never None 

307 init_var = True 

308 if isinstance(annotation, nodes.Subscript): 

309 annotation = annotation.slice 

310 else: 

311 # Cannot determine type annotation for parameter from InitVar 

312 annotation = None 

313 assignment_str = "" 

314 else: 

315 init_var = False 

316 assignment_str = f"self.{name} = {name}" 

317 

318 ann_str, default_str = None, None 

319 if annotation is not None: 

320 ann_str = annotation.as_string() 

321 

322 if value: 

323 if is_field: 

324 result = _get_field_default(value) # type: ignore[arg-type] 

325 if result: 

326 default_type, default_node = result 

327 if default_type == "default": 

328 default_str = default_node.as_string() 

329 elif default_type == "default_factory": 

330 default_str = DEFAULT_FACTORY 

331 assignment_str = ( 

332 f"self.{name} = {default_node.as_string()} " 

333 f"if {name} is {DEFAULT_FACTORY} else {name}" 

334 ) 

335 else: 

336 default_str = value.as_string() 

337 elif property_node: 

338 # We set the result of the property call as default 

339 # This hides the fact that this would normally be a 'property object' 

340 # But we can't represent those as string 

341 try: 

342 # Call str to make sure also Uninferable gets stringified 

343 default_str = str( 

344 next(property_node.infer_call_result(None)).as_string() 

345 ) 

346 except (InferenceError, StopIteration): 

347 pass 

348 else: 

349 # Even with `init=False` the default value still can be propogated to 

350 # later assignments. Creating weird signatures like: 

351 # (self, a: str = 1) -> None 

352 previous_default = _get_previous_field_default(node, name) 

353 if previous_default: 

354 default_str = previous_default.as_string() 

355 

356 # Construct the param string to add to the init if necessary 

357 param_str = name 

358 if ann_str is not None: 

359 param_str += f": {ann_str}" 

360 if default_str is not None: 

361 param_str += f" = {default_str}" 

362 

363 # If the field is a kw_only field, we need to add it to the kw_only_params 

364 # This overwrites whether or not the class is kw_only decorated 

365 if is_field: 

366 kw_only = [k for k in value.keywords if k.arg == "kw_only"] # type: ignore[union-attr] 

367 if kw_only: 

368 if kw_only[0].value.bool_value() is True: 

369 kw_only_params.append(param_str) 

370 else: 

371 params.append(param_str) 

372 continue 

373 # If kw_only decorated, we need to add all parameters to the kw_only_params 

374 if kw_only_decorated: 

375 if name in prev_kw_only_store: 

376 prev_kw_only_store[name] = (ann_str, default_str) 

377 else: 

378 kw_only_params.append(param_str) 

379 else: 

380 # If the name was previously seen, overwrite that data 

381 # pylint: disable-next=else-if-used 

382 if name in prev_pos_only_store: 

383 prev_pos_only_store[name] = (ann_str, default_str) 

384 elif name in prev_kw_only_store: 

385 params = [name, *params] 

386 prev_kw_only_store.pop(name) 

387 else: 

388 params.append(param_str) 

389 

390 if not init_var: 

391 assignments.append(assignment_str) 

392 

393 prev_pos_only, prev_kw_only = _parse_arguments_into_strings( 

394 prev_pos_only_store, prev_kw_only_store 

395 ) 

396 

397 # Construct the new init method parameter string 

398 # First we do the positional only parameters, making sure to add the 

399 # self parameter and the comma to allow adding keyword only parameters 

400 params_string = "" if "self" in prev_pos_only else "self, " 

401 params_string += prev_pos_only + ", ".join(params) 

402 if not params_string.endswith(", "): 

403 params_string += ", " 

404 

405 # Then we add the keyword only parameters 

406 if prev_kw_only or kw_only_params: 

407 params_string += "*, " 

408 params_string += f"{prev_kw_only}{', '.join(kw_only_params)}" 

409 

410 assignments_string = "\n ".join(assignments) if assignments else "pass" 

411 return f"def __init__({params_string}) -> None:\n {assignments_string}" 

412 

413 

414def infer_dataclass_attribute( 

415 node: nodes.Unknown, ctx: context.InferenceContext | None = None 

416) -> Iterator[InferenceResult]: 

417 """Inference tip for an Unknown node that was dynamically generated to 

418 represent a dataclass attribute. 

419 

420 In the case that a default value is provided, that is inferred first. 

421 Then, an Instance of the annotated class is yielded. 

422 """ 

423 assign = node.parent 

424 if not isinstance(assign, nodes.AnnAssign): 

425 yield Uninferable 

426 return 

427 

428 annotation, value = assign.annotation, assign.value 

429 if value is not None: 

430 yield from value.infer(context=ctx) 

431 if annotation is not None: 

432 yield from _infer_instance_from_annotation(annotation, ctx=ctx) 

433 else: 

434 yield Uninferable 

435 

436 

437def infer_dataclass_field_call( 

438 node: nodes.Call, ctx: context.InferenceContext | None = None 

439) -> Iterator[InferenceResult]: 

440 """Inference tip for dataclass field calls.""" 

441 if not isinstance(node.parent, (nodes.AnnAssign, nodes.Assign)): 

442 raise UseInferenceDefault 

443 result = _get_field_default(node) 

444 if not result: 

445 yield Uninferable 

446 else: 

447 default_type, default = result 

448 if default_type == "default": 

449 yield from default.infer(context=ctx) 

450 else: 

451 new_call = parse(default.as_string()).body[0].value 

452 new_call.parent = node.parent 

453 yield from new_call.infer(context=ctx) 

454 

455 

456def _looks_like_dataclass_decorator( 

457 node: nodes.NodeNG, decorator_names: frozenset[str] = DATACLASSES_DECORATORS 

458) -> bool: 

459 """Return True if node looks like a dataclass decorator. 

460 

461 Uses inference to lookup the value of the node, and if that fails, 

462 matches against specific names. 

463 """ 

464 if isinstance(node, nodes.Call): # decorator with arguments 

465 node = node.func 

466 try: 

467 inferred = next(node.infer()) 

468 except (InferenceError, StopIteration): 

469 inferred = Uninferable 

470 

471 if isinstance(inferred, UninferableBase): 

472 if isinstance(node, nodes.Name): 

473 return node.name in decorator_names 

474 if isinstance(node, nodes.Attribute): 

475 return node.attrname in decorator_names 

476 

477 return False 

478 

479 return ( 

480 isinstance(inferred, nodes.FunctionDef) 

481 and inferred.name in decorator_names 

482 and inferred.root().name in DATACLASS_MODULES 

483 ) 

484 

485 

486def _looks_like_dataclass_attribute(node: nodes.Unknown) -> bool: 

487 """Return True if node was dynamically generated as the child of an AnnAssign 

488 statement. 

489 """ 

490 parent = node.parent 

491 if not parent: 

492 return False 

493 

494 scope = parent.scope() 

495 return ( 

496 isinstance(parent, nodes.AnnAssign) 

497 and isinstance(scope, nodes.ClassDef) 

498 and is_decorated_with_dataclass(scope) 

499 ) 

500 

501 

502def _looks_like_dataclass_field_call( 

503 node: nodes.Call, check_scope: bool = True 

504) -> bool: 

505 """Return True if node is calling dataclasses field or Field 

506 from an AnnAssign statement directly in the body of a ClassDef. 

507 

508 If check_scope is False, skips checking the statement and body. 

509 """ 

510 if check_scope: 

511 stmt = node.statement() 

512 scope = stmt.scope() 

513 if not ( 

514 isinstance(stmt, nodes.AnnAssign) 

515 and stmt.value is not None 

516 and isinstance(scope, nodes.ClassDef) 

517 and is_decorated_with_dataclass(scope) 

518 ): 

519 return False 

520 

521 try: 

522 inferred = next(node.func.infer()) 

523 except (InferenceError, StopIteration): 

524 return False 

525 

526 if not isinstance(inferred, nodes.FunctionDef): 

527 return False 

528 

529 return inferred.name == FIELD_NAME and inferred.root().name in DATACLASS_MODULES 

530 

531 

532def _looks_like_dataclasses(node: nodes.Module) -> bool: 

533 return node.qname() == "dataclasses" 

534 

535 

536def _looks_like_dataclasses_replace(node: nodes.Call) -> bool: 

537 """Return True if node calls dataclasses.replace. 

538 

539 Matches both ``dataclasses.replace(...)`` and the bare-name form 

540 ``from dataclasses import replace; replace(...)``. 

541 """ 

542 func: nodes.NodeNG = node.func 

543 if isinstance(func, nodes.Attribute) and func.attrname == "replace": 

544 target = safe_infer(func.expr) 

545 if isinstance(target, nodes.Module): 

546 return _looks_like_dataclasses(target) 

547 elif isinstance(func, nodes.Name) and func.name == "replace": 

548 target = safe_infer(func) 

549 if isinstance(target, nodes.FunctionDef): 

550 return target.root().name == "dataclasses" 

551 return False 

552 

553 

554def infer_dataclasses_replace( 

555 node: nodes.Call, ctx: context.InferenceContext | None = None 

556) -> Iterator[InferenceResult]: 

557 """Infer ``dataclasses.replace(obj, ...)`` as an instance of obj's type. 

558 

559 Bypasses the stdlib body of ``replace()`` / ``_replace()``, which trips 

560 over subscripted generic bases in the metaclass-resolution chain. 

561 """ 

562 if not node.args: 

563 raise UseInferenceDefault 

564 inferred_obj = safe_infer(node.args[0], context=ctx) 

565 if isinstance(inferred_obj, UninferableBase) or inferred_obj is None: 

566 yield Uninferable 

567 return 

568 if isinstance(inferred_obj, bases.Instance): 

569 yield inferred_obj._proxied.instantiate_class() 

570 return 

571 if isinstance(inferred_obj, nodes.ClassDef): 

572 # replace() must be called on a dataclass instance; passing the class 

573 # itself raises TypeError at runtime, so there is nothing to infer. 

574 yield Uninferable 

575 return 

576 raise UseInferenceDefault 

577 

578 

579def _resolve_private_replace_to_public(node: nodes.Module) -> None: 

580 """In python/cpython@6f3c138, a _replace() method was extracted from 

581 replace(), and this indirection made replace() uninferable.""" 

582 if "_replace" in node.locals: 

583 node.locals["replace"] = node.locals["_replace"] 

584 

585 

586def _get_field_default(field_call: nodes.Call) -> _FieldDefaultReturn: 

587 """Return a the default value of a field call, and the corresponding keyword 

588 argument name. 

589 

590 field(default=...) results in the ... node 

591 field(default_factory=...) results in a Call node with func ... and no arguments 

592 

593 If neither or both arguments are present, return ("", None) instead, 

594 indicating that there is not a valid default value. 

595 """ 

596 default, default_factory = None, None 

597 for keyword in field_call.keywords: 

598 if keyword.arg == "default": 

599 default = keyword.value 

600 elif keyword.arg == "default_factory": 

601 default_factory = keyword.value 

602 

603 if default is not None and default_factory is None: 

604 return "default", default 

605 

606 if default is None and default_factory is not None: 

607 new_call = nodes.Call( 

608 lineno=field_call.lineno, 

609 col_offset=field_call.col_offset, 

610 parent=field_call.parent, 

611 end_lineno=field_call.end_lineno, 

612 end_col_offset=field_call.end_col_offset, 

613 ) 

614 new_call.postinit(func=default_factory, args=[], keywords=[]) 

615 return "default_factory", new_call 

616 

617 return None 

618 

619 

620def _is_keyword_only_sentinel(node: nodes.NodeNG) -> bool: 

621 """Return True if node is the KW_ONLY sentinel.""" 

622 inferred = safe_infer(node) 

623 if not isinstance(inferred, bases.Instance): 

624 return False 

625 if inferred.qname() == "dataclasses._KW_ONLY_TYPE": 

626 return True 

627 if inferred.qname() != "builtins.sentinel": 

628 return False 

629 if isinstance(node, nodes.Name): 

630 _, assignments = node.lookup(node.name) 

631 return any( 

632 isinstance(assignment, nodes.ImportFrom) 

633 and assignment.modname == "dataclasses" 

634 and any(imported == "KW_ONLY" for imported, _ in assignment.names) 

635 for assignment in assignments 

636 ) 

637 if isinstance(node, nodes.Attribute) and node.attrname == "KW_ONLY": 

638 inferred_expr = safe_infer(node.expr) 

639 return ( 

640 isinstance(inferred_expr, nodes.Module) 

641 and inferred_expr.qname() == "dataclasses" 

642 ) 

643 return False 

644 

645 

646def _is_init_var(node: nodes.NodeNG) -> bool: 

647 """Return True if node is an InitVar, with or without subscripting.""" 

648 try: 

649 inferred = next(node.infer()) 

650 except (InferenceError, StopIteration): 

651 return False 

652 

653 return getattr(inferred, "name", "") == "InitVar" 

654 

655 

656# Allowed typing classes for which we support inferring instances 

657_INFERABLE_TYPING_TYPES = frozenset( 

658 ( 

659 "Dict", 

660 "FrozenSet", 

661 "List", 

662 "Set", 

663 "Tuple", 

664 ) 

665) 

666 

667 

668def _infer_instance_from_annotation( 

669 node: nodes.NodeNG, ctx: context.InferenceContext | None = None 

670) -> Iterator[UninferableBase | bases.Instance]: 

671 """Infer an instance corresponding to the type annotation represented by node. 

672 

673 Currently has limited support for the typing module. 

674 """ 

675 klass = None 

676 try: 

677 klass = next(node.infer(context=ctx)) 

678 except (InferenceError, StopIteration): 

679 yield Uninferable 

680 if not isinstance(klass, nodes.ClassDef): 

681 yield Uninferable 

682 elif klass.root().name in { 

683 "typing", 

684 "_collections_abc", 

685 "", 

686 }: # "" because of synthetic nodes in brain_typing.py 

687 if klass.name in _INFERABLE_TYPING_TYPES: 

688 yield klass.instantiate_class() 

689 else: 

690 yield Uninferable 

691 else: 

692 yield klass.instantiate_class() 

693 

694 

695def register(manager: AstroidManager) -> None: 

696 if PY313_PLUS: 

697 manager.register_transform( 

698 nodes.Module, 

699 _resolve_private_replace_to_public, 

700 _looks_like_dataclasses, 

701 ) 

702 

703 manager.register_transform( 

704 nodes.ClassDef, dataclass_transform, is_decorated_with_dataclass 

705 ) 

706 

707 manager.register_transform( 

708 nodes.Call, 

709 inference_tip(infer_dataclass_field_call, raise_on_overwrite=True), 

710 _looks_like_dataclass_field_call, 

711 ) 

712 

713 manager.register_transform( 

714 nodes.Unknown, 

715 inference_tip(infer_dataclass_attribute, raise_on_overwrite=True), 

716 _looks_like_dataclass_attribute, 

717 ) 

718 

719 manager.register_transform( 

720 nodes.Call, 

721 inference_tip(infer_dataclasses_replace, raise_on_overwrite=True), 

722 _looks_like_dataclasses_replace, 

723 )