1 from __future__ import annotations 2 3 import collections.abc as c 4 import inspect 5 import typing as t 6 from weakref import ref 7 from weakref import WeakMethod 8 9 T = t.TypeVar("T") 10 11 12 class Symbol: 13 """A constant symbol, nicer than ``object()``. Repeated calls return the 14 same instance. 15 16 >>> Symbol('foo') is Symbol('foo') 17 True 18 >>> Symbol('foo') 19 foo 20 """ 21 22 symbols: t.ClassVar[dict[str, Symbol]] = {} 23 24 def __new__(cls, name: str) -> Symbol: 25 if name in cls.symbols: 26 return cls.symbols[name] 27 28 obj = super().__new__(cls) 29 cls.symbols[name] = obj 30 return obj 31 32 def __init__(self, name: str) -> None: 33 self.name = name 34 35 def __repr__(self) -> str: 36 return self.name 37 38 def __getnewargs__(self) -> tuple[t.Any, ...]: 39 return (self.name,) 40 41 42 def make_id(obj: object) -> c.Hashable: 43 """Get a stable identifier for a receiver or sender, to be used as a dict 44 key or in a set. 45 """ 46 if inspect.ismethod(obj): 47 # The id of a bound method is not stable, but the id of the unbound 48 # function and instance are. 49 return id(obj.__func__), id(obj.__self__) 50 51 if isinstance(obj, (str, int)): 52 # Instances with the same value always compare equal and have the same 53 # hash, even if the id may change. 54 return obj 55 56 # Assume other types are not hashable but will always be the same instance. 57 return id(obj) 58 59 60 def make_ref(obj: T, callback: c.Callable[[ref[T]], None] | None = None) -> ref[T]: 61 if inspect.ismethod(obj): 62 return WeakMethod(obj, callback) # type: ignore[arg-type, return-value] 63 64 return ref(obj, callback) 65