diff options
| author | J08nY | 2025-02-17 12:08:03 +0100 |
|---|---|---|
| committer | J08nY | 2025-02-17 12:08:03 +0100 |
| commit | 67ccbf3f4792fe39f9c75e0546140930d6dccd40 (patch) | |
| tree | bc5968022987518093bdf8377d8c1843666839f7 /src | |
| parent | c06dc527b4f80bff4266a245482f9bf98eb38a28 (diff) | |
| download | sec-certs-67ccbf3f4792fe39f9c75e0546140930d6dccd40.tar.gz sec-certs-67ccbf3f4792fe39f9c75e0546140930d6dccd40.tar.zst sec-certs-67ccbf3f4792fe39f9c75e0546140930d6dccd40.zip | |
Document the backed status of datasets.
Diffstat (limited to 'src')
| -rw-r--r-- | src/sec_certs/dataset/auxiliary_dataset_handling.py | 7 | ||||
| -rw-r--r-- | src/sec_certs/dataset/dataset.py | 14 | ||||
| -rw-r--r-- | src/sec_certs/dataset/json_path_dataset.py | 7 | ||||
| -rw-r--r-- | src/sec_certs/serialization/json.py | 51 |
4 files changed, 69 insertions, 10 deletions
diff --git a/src/sec_certs/dataset/auxiliary_dataset_handling.py b/src/sec_certs/dataset/auxiliary_dataset_handling.py index dc6993df..cd67f929 100644 --- a/src/sec_certs/dataset/auxiliary_dataset_handling.py +++ b/src/sec_certs/dataset/auxiliary_dataset_handling.py @@ -33,6 +33,13 @@ class AuxiliaryDatasetHandler(ABC): self.aux_datasets_dir = Path(aux_datasets_dir) if aux_datasets_dir is not None else None # type: ignore @property + def is_backed(self) -> bool: + """ + Returns whether the dataset is backed by a directory. + """ + return self.aux_datasets_dir is not None + + @property def root_dir(self) -> Path: if self.RELATIVE_DIR: return self.aux_datasets_dir / Path(self.RELATIVE_DIR) diff --git a/src/sec_certs/dataset/dataset.py b/src/sec_certs/dataset/dataset.py index 70bf3830..7add7b52 100644 --- a/src/sec_certs/dataset/dataset.py +++ b/src/sec_certs/dataset/dataset.py @@ -71,6 +71,13 @@ class Dataset(Generic[CertSubType], ComplexSerializableType, ABC): self._set_local_paths() @property + def is_backed(self) -> bool: + """ + Returns whether the dataset is backed by a directory. + """ + return self.root_dir is not None + + @property def root_dir(self) -> Path: """ Directory that will hold the serialized dataset files. @@ -95,13 +102,6 @@ class Dataset(Generic[CertSubType], ComplexSerializableType, ABC): self._set_local_paths() @property - def is_backed(self) -> bool: - """ - Returns whether the dataset is backed by a directory. - """ - return self.root_dir is not None - - @property @only_backed(throw=False) def web_dir(self) -> Path: """ diff --git a/src/sec_certs/dataset/json_path_dataset.py b/src/sec_certs/dataset/json_path_dataset.py index 8b9212b7..76df73f2 100644 --- a/src/sec_certs/dataset/json_path_dataset.py +++ b/src/sec_certs/dataset/json_path_dataset.py @@ -18,6 +18,13 @@ class JSONPathDataset(ComplexSerializableType, ABC): self.json_path = Path(json_path) if json_path is not None else None @property + def is_backed(self) -> bool: + """ + Returns whether the dataset is backed by a JSON file. + """ + return self.json_path is not None + + @property def json_path(self) -> Path | None: return self._json_path diff --git a/src/sec_certs/serialization/json.py b/src/sec_certs/serialization/json.py index 69822836..c843638c 100644 --- a/src/sec_certs/serialization/json.py +++ b/src/sec_certs/serialization/json.py @@ -3,6 +3,7 @@ from __future__ import annotations import copy import gzip import json +import logging from collections.abc import Callable from datetime import date, datetime from functools import wraps @@ -12,12 +13,24 @@ from typing import Any, TypeVar, cast T = TypeVar("T", bound="ComplexSerializableType") TCallable = TypeVar("TCallable", bound=Callable[..., Any]) +logger = logging.getLogger(__name__) + class SerializationError(Exception): pass class ComplexSerializableType: + """ + A class that can be serialized to json and thus a dictionary. + + Direct inheritance from this class is required for the class to be serializable. + Only the `serialized_attributes` are serialized. If `__slots__` is defined, only those attributes are serialized. + + .. note:: + The `to_dict` and `from_dict` should be overridden if non-trivial types of attributes need to be serialized. + """ + __slots__: tuple[str] def __init__(self, *args, **kwargs): @@ -103,8 +116,14 @@ class ComplexSerializableType: return json.load(handle, cls=CustomJSONDecoder) -# Decorator for serialization def serialize(func: Callable) -> Callable: + """ + Decorator to be used on instance methods of ComplexSerializableType child classes. + The decorated method will be serialized to json after execution. + + Adds the `update_json` keyword argument to the decorated method. If set to False, the json will not be updated. + """ + @wraps(func) def _serialize(*args, **kwargs): if not args or not issubclass(type(args[0]), ComplexSerializableType): @@ -127,6 +146,14 @@ def serialize(func: Callable) -> Callable: def only_backed(throw: bool = True): + """ + Decorator to be used on instance methods of ComplexSerializableType child classes. + The decorated method will only be executed if the `root_dir` attribute is set. + + :param bool throw: if True, will raise ValueError if `root_dir` is not set, defaults to True + Otherwise, just logs a warning and returns None. + """ + def deco(func: TCallable) -> TCallable: @wraps(func) def _only_backed(*args, **kwargs): @@ -134,6 +161,7 @@ def only_backed(throw: bool = True): if throw: raise ValueError(f"Method {func.__name__} can only be called on backed dataset.") else: + logger.warning(f"Method {func.__name__} can only be called on backed dataset.") return None else: return func(*args, **kwargs) @@ -144,6 +172,17 @@ def only_backed(throw: bool = True): def get_class_fullname(obj: Any) -> str: + """ + Returns the full name of the class of the object. + + Example: + >>> get_class_fullname(datetime.now()) + 'datetime.datetime' + + + :param Any obj: object to get the class name from + :return str: full name of the class + """ klass = obj if isinstance(obj, type) else obj.__class__ module = klass.__module__ if module == "builtins": @@ -152,6 +191,10 @@ def get_class_fullname(obj: Any) -> str: class CustomJSONEncoder(json.JSONEncoder): + """ + Custom JSONEncoder. + """ + def default(self, obj): if isinstance(obj, ComplexSerializableType): return {**{"_type": get_class_fullname(obj)}, **obj.to_dict()} @@ -172,8 +215,10 @@ class CustomJSONEncoder(json.JSONEncoder): class CustomJSONDecoder(json.JSONDecoder): """ - Custom JSONDecoder. Any complex object that should be de-serializable must inherit directly from class - ComplexSerializableType (nested inheritance does not currently work (because x.__subclassess__() prints only direct + Custom JSONDecoder. + + Any complex object that should be de-serializable must inherit directly from class + `ComplexSerializableType` (nested inheritance does not currently work (because x.__subclassess__() prints only direct subclasses. Any such class must implement methods to_dict() and from_dict(). These are used to drive serialization. """ |
