Skip to content

Instantly share code, notes, and snippets.

@nerodono
Created March 12, 2024 05:06
Show Gist options
  • Save nerodono/281c81e5faa4744254d2e6e04d55b8cf to your computer and use it in GitHub Desktop.
Save nerodono/281c81e5faa4744254d2e6e04d55b8cf to your computer and use it in GitHub Desktop.
"""Sole purpose of this thing - fix circular imports.
Consider, for example, message:
`slonogram.schemas.message.Message` depends on `slonogram.schemas.chat.Chat`
`Chat` also depends on `Message`, so that we got logically unsolvable circular imports problem.
This is the solution to it.
"""
from typing import Any, Iterable
from importlib import import_module
class _ImportTarget:
__slots__ = ('module', )
def __init__(self, module: str) -> None:
self.module = module
def import_name(self, name: str) -> Any:
return getattr(import_module(self.module), name)
class LazilyImported:
keys: list[str]
data: dict[str, _ImportTarget | Any]
__slots__ = ('keys', 'data')
def __init__(self, items: Iterable[str]) -> None:
keys: list[str] = []
data: dict[str, _ImportTarget | Any] = {}
for item in items:
object_dot = item.rfind('.')
if object_dot == -1:
raise ValueError(f'Module name {item!r} does not contain any dot: import target not known')
module = item[:object_dot]
name = item[object_dot+1:]
data[name] = _ImportTarget(module)
keys.append(name)
self.data = data
self.keys = keys
def __getattr__(self, name: str) -> Any:
value = self.data[name]
if type(value) is _ImportTarget:
imported = value.import_name(name)
self.data[name] = imported
return imported
return value
def __dir__(self) -> list[str]:
return self.keys
__all__ = ["LazilyImported"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment