"""unsprawl.loaders.universal.
UniversalLoader is the public facade that dispatches to a country adapter.
Anti-circular protocol
----------------------
Loaders may import adapters, but `unsprawl.core` must never import loaders.
"""
from __future__ import annotations
import importlib
from dataclasses import dataclass
from unsprawl.core.regions import RegionNode
from unsprawl.core.schemas import Asset
[docs]
@dataclass(frozen=True)
class UniversalLoader:
"""Dynamic dispatcher that loads Assets for a given Region node."""
[docs]
def load(self, region_node: RegionNode) -> list[Asset]:
"""Load assets for a region.
Parameters
----------
region_node:
A `Region.*` node from `unsprawl.core.regions`.
Returns
-------
list[Asset]
The normalized assets for this region.
"""
code = region_node.CODE
country = code.split("-")[0]
module_name = f"unsprawl.adapters.{country.lower()}"
module = importlib.import_module(module_name)
# Convention: adapter class name is '<CC>Adapter' (e.g., SGAdapter)
adapter_class_name = f"{country.upper()}Adapter"
adapter_cls = getattr(module, adapter_class_name)
adapter = adapter_cls()
from typing import cast as _cast
return _cast(list[Asset], adapter.fetch(code))