main
 1from __future__ import annotations
 2
 3from abc import ABC, abstractmethod
 4from typing import Generic, TypeVar, Iterable, cast
 5from typing_extensions import override
 6
 7T = TypeVar("T")
 8
 9
10class LazyProxy(Generic[T], ABC):
11    """Implements data methods to pretend that an instance is another instance.
12
13    This includes forwarding attribute access and other methods.
14    """
15
16    # Note: we have to special case proxies that themselves return proxies
17    # to support using a proxy as a catch-all for any random access, e.g. `proxy.foo.bar.baz`
18
19    def __getattr__(self, attr: str) -> object:
20        proxied = self.__get_proxied__()
21        if isinstance(proxied, LazyProxy):
22            return proxied  # pyright: ignore
23        return getattr(proxied, attr)
24
25    @override
26    def __repr__(self) -> str:
27        proxied = self.__get_proxied__()
28        if isinstance(proxied, LazyProxy):
29            return proxied.__class__.__name__
30        return repr(self.__get_proxied__())
31
32    @override
33    def __str__(self) -> str:
34        proxied = self.__get_proxied__()
35        if isinstance(proxied, LazyProxy):
36            return proxied.__class__.__name__
37        return str(proxied)
38
39    @override
40    def __dir__(self) -> Iterable[str]:
41        proxied = self.__get_proxied__()
42        if isinstance(proxied, LazyProxy):
43            return []
44        return proxied.__dir__()
45
46    @property  # type: ignore
47    @override
48    def __class__(self) -> type:  # pyright: ignore
49        try:
50            proxied = self.__get_proxied__()
51        except Exception:
52            return type(self)
53        if issubclass(type(proxied), LazyProxy):
54            return type(proxied)
55        return proxied.__class__
56
57    def __get_proxied__(self) -> T:
58        return self.__load__()
59
60    def __as_proxied__(self) -> T:
61        """Helper method that returns the current proxy, typed as the loaded object"""
62        return cast(T, self)
63
64    @abstractmethod
65    def __load__(self) -> T: ...