_proxy.py 1.9 KB

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