Source code for persidict.overlapping_multi_dict
"""Container for multiple PersiDict instances with different serialization formats.
This module provides OverlappingMultiDict, which creates and manages multiple
PersiDict sub-dictionaries that share common parameters but use different
serialization_format values. Each sub-dictionary is exposed as an attribute named after
its serialization_format, enabling organized storage of different data formats in the
same logical location.
"""
from __future__ import annotations
from typing import Any
from .persi_dict import PersiDict
[docs]
class OverlappingMultiDict:
"""Container for multiple PersiDict instances, differing only by serialization_format.
This class instantiates several sub-dictionaries (PersiDict subclasses) that
share common parameters but differ by their serialization_format. Each sub-dictionary is
exposed as an attribute whose name equals the serialization_format (e.g., obj.json, obj.csv).
All sub-dictionaries typically point to the same underlying base directory or
bucket and differ only in how items are materialized by serialization format.
Attributes:
dict_type: A subclass of PersiDict used to create each
sub-dictionary.
shared_subdicts_params: Parameters applied to every
created sub-dictionary (e.g., base_dir, bucket, append_only,
digest_len).
individual_subdicts_params: Mapping from
serialization_format (attribute name) to a dict of parameters that are specific
to that sub-dictionary. These override or extend shared_subdicts_params
for the given serialization_format.
subdicts_names: The list of serialization_format names (i.e., attribute
names) created.
Raises:
TypeError: If pickling is attempted or item access is used on the
OverlappingMultiDict itself rather than its sub-dicts.
"""
def __init__(self, *,
dict_type: type[PersiDict],
shared_subdicts_params: dict[str, Any],
**individual_subdicts_params: dict[str, Any]) -> None:
"""Initialize the container and create sub-dictionaries.
Args:
dict_type: A subclass of PersiDict that will be
instantiated for each serialization_format provided via individual_subdicts_params.
shared_subdicts_params: Parameters shared by all
sub-dicts (e.g., base_dir, bucket).
**individual_subdicts_params: Keyword
arguments where each key is a serialization_format (also the attribute name
to be created) and each value is a dict of parameters specific to
that sub-dict. These are merged with shared_subdicts_params when
constructing the sub-dict. The resulting dict also receives
serialization_format=<key>.
Raises:
TypeError: If dict_type is not a PersiDict subclass, or if
shared_subdicts_params is not a dict, or if any individual
parameter set is not a dict.
"""
if not issubclass(dict_type, PersiDict):
raise TypeError("dict_type must be a subclass of PersiDict")
if not isinstance(shared_subdicts_params, dict):
raise TypeError("shared_subdicts_params must be a dict")
self.dict_type = dict_type
self.shared_subdicts_params = shared_subdicts_params
self.individual_subdicts_params = individual_subdicts_params
self.subdicts_names = list(individual_subdicts_params.keys())
for subdict_name in individual_subdicts_params:
if not isinstance(individual_subdicts_params[subdict_name], dict):
raise TypeError(
f"Params for subdict {subdict_name!r} must be a dict")
self.__dict__[subdict_name] = dict_type(
**{**shared_subdicts_params,
**individual_subdicts_params[subdict_name],
"serialization_format": subdict_name})
def __getstate__(self):
"""Prevent pickling.
Raises:
TypeError: Always raised; this object is not pickleable.
"""
raise TypeError("OverlappingMultiDict cannot be pickled.")
def __setstate__(self, state):
"""Prevent unpickling.
Args:
state: The state dictionary that would be used for unpickling (ignored).
Raises:
TypeError: Always raised; this object is not pickleable.
"""
raise TypeError("OverlappingMultiDict cannot be pickled.")
def __getitem__(self, key):
"""Disallow item access on the container itself.
Suggest accessing items through the sub-dictionaries exposed as
attributes (e.g., obj.json[key]).
Args:
key: The key that would be accessed (ignored).
Raises:
TypeError: Always raised to indicate an unsupported operation.
"""
raise TypeError(
"OverlappingMultiDict does not support item access by key. "
"Individual items should be accessed through nested dicts, "
f"which are available via attributes {self.subdicts_names}")
def __setitem__(self, key, value):
"""Disallow item assignment on the container itself.
Args:
key: The key that would be assigned (ignored).
value: The value that would be assigned (ignored).
Raises:
TypeError: Always raised to indicate an unsupported operation.
"""
raise TypeError(
"OverlappingMultiDict does not support item assignment by key. "
"Individual items should be accessed through nested dicts, "
f"which are available via attributes {self.subdicts_names}")
def __delitem__(self, key):
"""Disallow item deletion on the container itself.
Args:
key: The key that would be deleted (ignored).
Raises:
TypeError: Always raised to indicate an unsupported operation.
"""
raise TypeError(
"OverlappingMultiDict does not support item deletion by key. "
"Individual items can be deleted through nested dicts, "
f"which are available via attributes {self.subdicts_names}")