Convert one AST body node into NodeInfo records.
(self, node: ast.AST)
| 348 | return self.nodes |
| 349 | |
| 350 | def _extract_node_info(self, node: ast.AST) -> List[NodeInfo]: |
| 351 | """Convert one AST body node into NodeInfo records.""" |
| 352 | results: List[NodeInfo] = [] |
| 353 | |
| 354 | if isinstance(node, (ast.ClassDef,)): |
| 355 | bases = [self._name_of(b) for b in node.bases] |
| 356 | info = NodeInfo( |
| 357 | name=node.name, |
| 358 | kind="class", |
| 359 | lineno=node.lineno, |
| 360 | end_lineno=node.end_lineno or node.lineno, |
| 361 | bases=bases, |
| 362 | ) |
| 363 | info.source_hash = self._hash_lines(info.lineno, info.end_lineno) |
| 364 | results.append(info) |
| 365 | |
| 366 | elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): |
| 367 | info = NodeInfo( |
| 368 | name=node.name, |
| 369 | kind="function", |
| 370 | lineno=node.lineno, |
| 371 | end_lineno=node.end_lineno or node.lineno, |
| 372 | ) |
| 373 | info.source_hash = self._hash_lines(info.lineno, info.end_lineno) |
| 374 | results.append(info) |
| 375 | |
| 376 | elif isinstance(node, ast.Assign): |
| 377 | # Capture top-level assignments (constants, config dicts, etc.) |
| 378 | for target in node.targets: |
| 379 | name = self._name_of(target) |
| 380 | if name: |
| 381 | end = node.end_lineno or node.lineno |
| 382 | kind = "constant" if re.match(r"^[A-Z][A-Z0-9_]{2,}$", name) else "assignment" |
| 383 | info = NodeInfo( |
| 384 | name=name, |
| 385 | kind=kind, |
| 386 | lineno=node.lineno, |
| 387 | end_lineno=end, |
| 388 | ) |
| 389 | info.source_hash = self._hash_lines(info.lineno, info.end_lineno) |
| 390 | results.append(info) |
| 391 | |
| 392 | elif isinstance(node, ast.AnnAssign): |
| 393 | name = self._name_of(node.target) |
| 394 | if name: |
| 395 | end = node.end_lineno or node.lineno |
| 396 | kind = "constant" if re.match(r"^[A-Z][A-Z0-9_]{2,}$", name) else "assignment" |
| 397 | info = NodeInfo( |
| 398 | name=name, |
| 399 | kind=kind, |
| 400 | lineno=node.lineno, |
| 401 | end_lineno=end, |
| 402 | ) |
| 403 | info.source_hash = self._hash_lines(info.lineno, info.end_lineno) |
| 404 | results.append(info) |
| 405 | |
| 406 | elif isinstance(node, (ast.Import, ast.ImportFrom)): |
| 407 | # Track the first import block as a special node |
no test coverage detected