Source code for runtimepy.channel.environment.file

"""
A channel-environment extension for loading and saving files.
"""

# built-in
from typing import Optional as _Optional
from typing import TypeVar as _TypeVar
from typing import cast as _cast

# third-party
from vcorelib.io import ARBITER as _ARBITER
from vcorelib.io.types import JsonObject as _JsonObject
from vcorelib.io.types import JsonValue as _JsonValue
from vcorelib.paths import Pathlike as _Pathlike
from vcorelib.paths import normalize as _normalize

# internal
from runtimepy.channel.environment.base import (
    BaseChannelEnvironment as _BaseChannelEnvironment,
)
from runtimepy.channel.environment.base import ValueMap as _ValueMap
from runtimepy.channel.registry import ChannelRegistry as _ChannelRegistry
from runtimepy.enum.registry import EnumRegistry as _EnumRegistry
from runtimepy.mapping import NameToKey as _NameToKey
from runtimepy.primitives.field.manager import (
    ENUMS_KEY,
    FIELDS_KEY,
    NAMES_KEY,
    VALUES_KEY,
)
from runtimepy.primitives.field.manager import (
    fields_from_dict as _fields_from_dict,
)
from runtimepy.primitives.field.manager import (
    fields_from_file as _fields_from_file,
)

T = _TypeVar("T", bound="FileChannelEnvironment")
CHANNELS_KEY = "channels"
CHANNELS_FILE = f"{CHANNELS_KEY}.json"
ENUMS_FILE = f"{ENUMS_KEY}.json"
VALUES_FILE = f"{VALUES_KEY}.json"
FIELDS_FILE = f"{FIELDS_KEY}.json"
NAMES_FILE = f"{NAMES_KEY}.json"
VIEWS_KEY = "views"
# VIEWS_FILE = f"{VIEWS_KEY}.json"


[docs] class FileChannelEnvironment(_BaseChannelEnvironment): """A class integrating file-system operations with channel environments."""
[docs] def export_json(self, resolve_enum: bool = True) -> dict[str, _JsonObject]: """Get this channel environment as a single dictionary.""" # Only allow exporting finalized environments. self.finalize(strict=False) return { CHANNELS_KEY: self.channels.asdict(), ENUMS_KEY: self.enums.asdict(), FIELDS_KEY: self.fields.asdict(), NAMES_KEY: { "id": self.id, CHANNELS_KEY: _cast(_JsonValue, self.channels.names.asdict()), ENUMS_KEY: _cast(_JsonValue, self.enums.names.asdict()), }, VALUES_KEY: _cast( _JsonObject, self.values(resolve_enum=resolve_enum) ), VIEWS_KEY: _cast(_JsonObject, self.views), }
[docs] def export( self, channels: _Pathlike = CHANNELS_FILE, enums: _Pathlike = ENUMS_FILE, values: _Pathlike = VALUES_FILE, fields: _Pathlike = FIELDS_FILE, names: _Pathlike = NAMES_FILE, resolve_enum: bool = True, **kwargs, ) -> None: """Write channel and enum registries to disk.""" # Only allow exporting finalized environments. self.finalize(strict=False) self.channels.encode(channels, **kwargs) self.enums.encode(enums, **kwargs) self.fields.encode(fields, **kwargs) # Keep track of name-to-identifier mappings for all such mappings. _ARBITER.encode( names, _cast( _JsonObject, { CHANNELS_KEY: self.channels.names.asdict(), ENUMS_KEY: self.enums.names.asdict(), "id": self.id, }, ), **kwargs, ) _ARBITER.encode( values, _cast(_JsonObject, self.values(resolve_enum=resolve_enum)), **kwargs, )
[docs] def export_directory( self, path: _Pathlike, resolve_enum: bool = True, **kwargs ) -> None: """Export this channel environment to a directory.""" path = _normalize(path) path.mkdir(parents=True, exist_ok=True) self.export( channels=path.joinpath(CHANNELS_FILE), enums=path.joinpath(ENUMS_FILE), values=path.joinpath(VALUES_FILE), fields=path.joinpath(FIELDS_FILE), names=path.joinpath(NAMES_FILE), resolve_enum=resolve_enum, **kwargs, )
[docs] @classmethod def load_json( cls: type[T], data: dict[str, _JsonObject], finalize: bool = True ) -> T: """Load a channel environment from JSON data.""" chan_reg = _ChannelRegistry.create(data[CHANNELS_KEY]) enum_reg = _EnumRegistry.create(data[ENUMS_KEY]) # Handle name data. chan_reg.names.load_name_to_key( _cast(_NameToKey[int], data[NAMES_KEY][CHANNELS_KEY]) ) enum_reg.names.load_name_to_key( _cast(_NameToKey[int], data[NAMES_KEY][ENUMS_KEY]) ) result = cls( channels=chan_reg, enums=enum_reg, values=_cast(_Optional[_ValueMap], data.get(VALUES_KEY)), fields=_fields_from_dict(data[FIELDS_KEY]), views=_cast(dict[str, str], data.get(VIEWS_KEY, {})), identity=data[NAMES_KEY]["id"], # type: ignore ) # Typically, externally loaded environments should be final at load # time. if finalize: result.finalize() return result
[docs] @classmethod def load( cls: type[T], channels: _Pathlike = CHANNELS_FILE, enums: _Pathlike = ENUMS_FILE, values: _Pathlike = VALUES_FILE, fields: _Pathlike = FIELDS_FILE, names: _Pathlike = NAMES_FILE, finalize: bool = True, ) -> T: """Load a channel environment from a pair of files.""" value_map: _Optional[_ValueMap] = None # Load the value map if it's present. values = _normalize(values) if values.is_file(): value_map = _cast( _ValueMap, _ARBITER.decode(values, require_success=True).data ) chan_reg = _ChannelRegistry.decode(channels) enum_reg = _EnumRegistry.decode(enums) # Load name-to-identifier mapping data and initialize (or update) # name registries. name_data = _ARBITER.decode(names, require_success=True).data chan_reg.names.load_name_to_key( _cast(_NameToKey[int], name_data[CHANNELS_KEY]) ) enum_reg.names.load_name_to_key( _cast(_NameToKey[int], name_data[ENUMS_KEY]) ) result = cls( channels=chan_reg, enums=enum_reg, values=value_map, fields=_fields_from_file(fields), identity=name_data["id"], # type: ignore ) # Typically, externally loaded environments should be final at load # time. if finalize: result.finalize() return result
[docs] @classmethod def load_directory( cls: type[T], path: _Pathlike, finalize: bool = True ) -> T: """Load a channel environment from a directory.""" path = _normalize(path, require=True) assert path.is_dir(), f"'{path}' is not a directory!" return cls.load( channels=path.joinpath(CHANNELS_FILE), enums=path.joinpath(ENUMS_FILE), values=path.joinpath(VALUES_FILE), fields=path.joinpath(FIELDS_FILE), names=path.joinpath(NAMES_FILE), finalize=finalize, )