Python source code parser to detect location of functions, classes and methods.
| 512 | |
| 513 | |
| 514 | class DefinitionFinder(TokenProcessor): |
| 515 | """Python source code parser to detect location of functions, |
| 516 | classes and methods. |
| 517 | """ |
| 518 | |
| 519 | def __init__(self, lines: list[str]) -> None: |
| 520 | super().__init__(lines) |
| 521 | self.decorator: Token | None = None |
| 522 | self.context: list[str] = [] |
| 523 | self.indents: list[tuple[str, str | None, int | None]] = [] |
| 524 | self.definitions: dict[str, tuple[str, int, int]] = {} |
| 525 | |
| 526 | def add_definition(self, name: str, entry: tuple[str, int, int]) -> None: |
| 527 | """Add a location of definition.""" |
| 528 | if self.indents and self.indents[-1][0] == entry[0] == 'def': |
| 529 | # ignore definition of inner function |
| 530 | pass |
| 531 | else: |
| 532 | self.definitions[name] = entry |
| 533 | |
| 534 | def parse(self) -> None: |
| 535 | """Parse the code to obtain location of definitions.""" |
| 536 | while True: |
| 537 | token = self.fetch_token() |
| 538 | if token is None: |
| 539 | break |
| 540 | if token == COMMENT: |
| 541 | pass |
| 542 | elif token == [OP, '@'] and ( |
| 543 | self.previous is None |
| 544 | or self.previous.match(NEWLINE, NL, INDENT, DEDENT) |
| 545 | ): |
| 546 | if self.decorator is None: |
| 547 | self.decorator = token |
| 548 | elif token.match([NAME, 'class']): |
| 549 | self.parse_definition('class') |
| 550 | elif token.match([NAME, 'def']): |
| 551 | self.parse_definition('def') |
| 552 | elif token == INDENT: |
| 553 | self.indents.append(('other', None, None)) |
| 554 | elif token == DEDENT: |
| 555 | self.finalize_block() |
| 556 | |
| 557 | def parse_definition(self, typ: str) -> None: |
| 558 | """Parse AST of definition.""" |
| 559 | name = self.fetch_token() |
| 560 | self.context.append(name.value) # type: ignore[union-attr] |
| 561 | funcname = '.'.join(self.context) |
| 562 | |
| 563 | if self.decorator: |
| 564 | start_pos = self.decorator.start[0] |
| 565 | self.decorator = None |
| 566 | else: |
| 567 | start_pos = name.start[0] # type: ignore[union-attr] |
| 568 | |
| 569 | self.fetch_until([OP, ':']) |
| 570 | if self.fetch_token().match(COMMENT, NEWLINE): # type: ignore[union-attr] |
| 571 | self.fetch_until(INDENT) |