usse/funda-scraper/venv/lib/python3.10/site-packages/mypy/server/objgraph.py

122 lines
3.3 KiB
Python
Raw Normal View History

2023-02-20 22:38:24 +00:00
"""Find all objects reachable from a root object."""
from collections.abc import Iterable
import weakref
import types
from typing import List, Dict, Iterator, Tuple, Mapping
from typing_extensions import Final
method_descriptor_type: Final = type(object.__dir__)
method_wrapper_type: Final = type(object().__ne__)
wrapper_descriptor_type: Final = type(object.__ne__)
FUNCTION_TYPES: Final = (
types.BuiltinFunctionType,
types.FunctionType,
types.MethodType,
method_descriptor_type,
wrapper_descriptor_type,
method_wrapper_type,
)
ATTR_BLACKLIST: Final = {
'__doc__',
'__name__',
'__class__',
'__dict__',
}
# Instances of these types can't have references to other objects
ATOMIC_TYPE_BLACKLIST: Final = {
bool,
int,
float,
str,
type(None),
object,
}
# Don't look at most attributes of these types
COLLECTION_TYPE_BLACKLIST: Final = {
list,
set,
dict,
tuple,
}
# Don't return these objects
TYPE_BLACKLIST: Final = {
weakref.ReferenceType,
}
def isproperty(o: object, attr: str) -> bool:
return isinstance(getattr(type(o), attr, None), property)
def get_edge_candidates(o: object) -> Iterator[Tuple[object, object]]:
# use getattr because mypyc expects dict, not mappingproxy
if '__getattribute__' in getattr(type(o), '__dict__'): # noqa
return
if type(o) not in COLLECTION_TYPE_BLACKLIST:
for attr in dir(o):
try:
if attr not in ATTR_BLACKLIST and hasattr(o, attr) and not isproperty(o, attr):
e = getattr(o, attr)
if not type(e) in ATOMIC_TYPE_BLACKLIST:
yield attr, e
except AssertionError:
pass
if isinstance(o, Mapping):
yield from o.items()
elif isinstance(o, Iterable) and not isinstance(o, str):
for i, e in enumerate(o):
yield i, e
def get_edges(o: object) -> Iterator[Tuple[object, object]]:
for s, e in get_edge_candidates(o):
if (isinstance(e, FUNCTION_TYPES)):
# We don't want to collect methods, but do want to collect values
# in closures and self pointers to other objects
if hasattr(e, '__closure__'):
yield (s, '__closure__'), e.__closure__ # type: ignore
if hasattr(e, '__self__'):
se = e.__self__ # type: ignore
if se is not o and se is not type(o) and hasattr(s, '__self__'):
yield s.__self__, se # type: ignore
else:
if not type(e) in TYPE_BLACKLIST:
yield s, e
def get_reachable_graph(root: object) -> Tuple[Dict[int, object],
Dict[int, Tuple[int, object]]]:
parents = {}
seen = {id(root): root}
worklist = [root]
while worklist:
o = worklist.pop()
for s, e in get_edges(o):
if id(e) in seen:
continue
parents[id(e)] = (id(o), s)
seen[id(e)] = e
worklist.append(e)
return seen, parents
def get_path(o: object,
seen: Dict[int, object],
parents: Dict[int, Tuple[int, object]]) -> List[Tuple[object, object]]:
path = []
while id(o) in parents:
pid, attr = parents[id(o)]
o = seen[pid]
path.append((attr, o))
path.reverse()
return path