indie-status-page/venv/lib/python3.11/site-packages/mypy/lookup.py
IndieStatusBot 902133edd3 feat: indie status page MVP -- FastAPI + SQLite
- 8 DB models (services, incidents, monitors, subscribers, etc.)
- Full CRUD API for services, incidents, monitors
- Public status page with live data
- Incident detail page with timeline
- API key authentication
- Uptime monitoring scheduler
- 13 tests passing
- TECHNICAL_DESIGN.md with full spec
2026-04-25 05:00:00 +00:00

68 lines
2.4 KiB
Python

"""
This is a module for various lookup functions:
functions that will find a semantic node by its name.
"""
from __future__ import annotations
from mypy.nodes import MypyFile, SymbolTableNode, TypeInfo
# TODO: gradually move existing lookup functions to this module.
def lookup_fully_qualified(
name: str, modules: dict[str, MypyFile], *, raise_on_missing: bool = False
) -> SymbolTableNode | None:
"""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 looked
in the modules dictionary.
"""
# 1. Exclude the names of ad hoc instance intersections from step 2.
i = name.find("<subclass ")
head = name if i == -1 else name[:i]
rest = []
# 2. Find a module tree in modules dictionary.
while True:
if "." not in head:
if raise_on_missing:
assert "." in head, f"Cannot find module for {name}"
return None
# TODO: this logic is not correct as it confuses a submodule and a local symbol.
# A potential solution may be to use format like pkg.mod:Cls.method for fullname,
# but this is a relatively big change.
head, tail = head.rsplit(".", maxsplit=1)
rest.append(tail)
mod = modules.get(head)
if mod is not None:
break
names = mod.names
# 3. Find the symbol in the module tree.
if not rest:
# Looks like a module, don't use this to avoid confusions.
if raise_on_missing:
assert rest, f"Cannot find {name}, got a module symbol"
return None
if i != -1:
rest[0] += name[i:]
while True:
key = rest.pop()
if key not in names:
if raise_on_missing:
assert key in names, f"Cannot find component {key!r} for {name!r}"
return None
stnode = names[key]
if not rest:
return stnode
node = stnode.node
# In fine-grained mode, could be a cross-reference to a deleted module
# or a Var made up for a missing module.
if not isinstance(node, TypeInfo):
if raise_on_missing:
assert node, f"Cannot find {name}"
return None
names = node.names