Find a symbol using it fully qualified name. The algorithm has two steps: first we try splitting the name on '.' to find the module, then iteratively look for each next chunk after a '.' (e.g. for nested classes). This function should *not* be used to find a module. Those should be
(
name: str, modules: dict[str, MypyFile], *, raise_on_missing: bool = False
)
| 11 | |
| 12 | |
| 13 | def lookup_fully_qualified( |
| 14 | name: str, modules: dict[str, MypyFile], *, raise_on_missing: bool = False |
| 15 | ) -> SymbolTableNode | None: |
| 16 | """Find a symbol using it fully qualified name. |
| 17 | |
| 18 | The algorithm has two steps: first we try splitting the name on '.' to find |
| 19 | the module, then iteratively look for each next chunk after a '.' (e.g. for |
| 20 | nested classes). |
| 21 | |
| 22 | This function should *not* be used to find a module. Those should be looked |
| 23 | in the modules dictionary. |
| 24 | """ |
| 25 | # 1. Exclude the names of ad hoc instance intersections from step 2. |
| 26 | i = name.find("<subclass ") |
| 27 | head = name if i == -1 else name[:i] |
| 28 | rest = [] |
| 29 | # 2. Find a module tree in modules dictionary. |
| 30 | while True: |
| 31 | if "." not in head: |
| 32 | if raise_on_missing: |
| 33 | assert "." in head, f"Cannot find module for {name}" |
| 34 | return None |
| 35 | # TODO: this logic is not correct as it confuses a submodule and a local symbol. |
| 36 | # A potential solution may be to use format like pkg.mod:Cls.method for fullname, |
| 37 | # but this is a relatively big change. |
| 38 | head, tail = head.rsplit(".", maxsplit=1) |
| 39 | rest.append(tail) |
| 40 | mod = modules.get(head) |
| 41 | if mod is not None: |
| 42 | break |
| 43 | names = mod.names |
| 44 | # 3. Find the symbol in the module tree. |
| 45 | if not rest: |
| 46 | # Looks like a module, don't use this to avoid confusions. |
| 47 | if raise_on_missing: |
| 48 | assert rest, f"Cannot find {name}, got a module symbol" |
| 49 | return None |
| 50 | if i != -1: |
| 51 | rest[0] += name[i:] |
| 52 | while True: |
| 53 | key = rest.pop() |
| 54 | if key not in names: |
| 55 | if raise_on_missing: |
| 56 | assert key in names, f"Cannot find component {key!r} for {name!r}" |
| 57 | return None |
| 58 | stnode = names[key] |
| 59 | if not rest: |
| 60 | return stnode |
| 61 | node = stnode.node |
| 62 | # In fine-grained mode, could be a cross-reference to a deleted module |
| 63 | # or a Var made up for a missing module. |
| 64 | if not isinstance(node, TypeInfo): |
| 65 | if raise_on_missing: |
| 66 | assert node, f"Cannot find {name}" |
| 67 | return None |
| 68 | names = node.names |
| 69 | |
| 70 |
no test coverage detected
searching dependent graphs…