diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1e6fa9d..740f39f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,8 +1,6 @@ name: Tests on: - push: - branches: [ "main", "dev" ] pull_request: branches: [ "*" ] diff --git a/README.md b/README.md index f9b2698..b28b4f3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ IOKit is a Python library that offers a suite of utilities for managing a wide range of input/output operations. Central to its design is the concept of a `State`, where each state signifies a unit of data that can be loaded, saved, or transformed. Each state corresponds to a valid file state, represented as a bytes-like object. -IOKit abstracts and unifies serialization and deserialization operations from various libraries into a single, cohesive interface. This allows for direct manipulation of the file's state in memory, eliminating the need for disk interaction. Consequently, it facilitates the (de)serialization of data in multiple formats, such as `json`, `txt`, `tar`, `gzip`, among others. This abstraction not only simplifies data handling but also enhances efficiency by reducing disk I/O operations. +IOKit abstracts and unifies serialization and deserialization operations from various libraries into a single, cohesive interface. This allows for direct manipulation of the file's state in memory, eliminating the need for disk interaction. Consequently, it facilitates the (de)serialization of data in multiple formats, such as `json`, `yaml`, `txt`, `tar`, `gzip`, among others. This abstraction not only simplifies data handling but also enhances efficiency by reducing disk I/O operations. ## Installation @@ -48,6 +48,22 @@ single.json (16B) {'key': 'value'} ``` +### YAML + +```python +from iokit import Yaml + +data = {"key": "value"} +state = Yaml(data, name="single") +print(state) +print(state.load()) +``` + +```plain-text +single.yaml (11B) +{'key': 'value'} +``` + ### GZip Compression ```python diff --git a/pyproject.toml b/pyproject.toml index ab2f451..487cf24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "humanize>=4.9.0", "typing-extensions>=4.8.0", "pytz>=2024.1", + "PyYAML>=6.0.1", ] [tool.setuptools.dynamic] diff --git a/src/iokit/__init__.py b/src/iokit/__init__.py index 7d6dccb..0afe476 100644 --- a/src/iokit/__init__.py +++ b/src/iokit/__init__.py @@ -5,6 +5,7 @@ "Jsonl", "Tar", "Txt", + "Yaml", "State", "filter_states", "find_state", @@ -12,8 +13,8 @@ "save_file", "save_temp", ] -__version__ = "0.1.2" +__version__ = "0.1.3" -from .extensions import Dat, Gzip, Json, Jsonl, Tar, Txt +from .extensions import Dat, Gzip, Json, Jsonl, Tar, Txt, Yaml from .state import State, filter_states, find_state from .storage import load_file, save_file, save_temp diff --git a/src/iokit/extensions/__init__.py b/src/iokit/extensions/__init__.py index 8401702..672aaa3 100644 --- a/src/iokit/extensions/__init__.py +++ b/src/iokit/extensions/__init__.py @@ -5,6 +5,7 @@ "Jsonl", "Tar", "Txt", + "Yaml", ] from .dat import Dat @@ -13,3 +14,4 @@ from .jsonl import Jsonl from .tar import Tar from .txt import Txt +from .yaml import Yaml diff --git a/src/iokit/extensions/yaml.py b/src/iokit/extensions/yaml.py new file mode 100644 index 0000000..493013d --- /dev/null +++ b/src/iokit/extensions/yaml.py @@ -0,0 +1,18 @@ +__all__ = [ + "Yaml", +] + +from typing import Any + +import yaml + +from iokit.state import State + + +class Yaml(State, suffix="yaml"): + def __init__(self, data: Any, **kwargs: Any): + data = yaml.safe_dump(data).encode("utf-8") + super().__init__(data=data, **kwargs) + + def load(self) -> Any: + return yaml.safe_load(self.data) diff --git a/tests/test_yaml.py b/tests/test_yaml.py new file mode 100644 index 0000000..dbc39f9 --- /dev/null +++ b/tests/test_yaml.py @@ -0,0 +1,40 @@ +from iokit.extensions.yaml import Yaml + + +def test_yaml_empty() -> None: + state = Yaml([], name="empty") + assert state.name == "empty.yaml" + assert state.size == 3 + assert state.data.getvalue() == b"[]\n" + assert not state.load() + + +def test_yaml_single() -> None: + state = Yaml({"key": "value"}, name="single") + assert state.name == "single.yaml" + assert state.load() == {"key": "value"} + + +def test_yaml_multiple() -> None: + state = Yaml({"first": 1, "second": 2}, name="multiple") + assert state.name == "multiple.yaml" + assert state.load() == {"first": 1, "second": 2} + assert state.size == 19 + + +def test_yaml_different() -> None: + data = { + "list": [1, 2, 3], + "tuple": (4, 5, 6), + "dict": {"a": 1, "b": 2}, + "str": "hello", + "int": 42, + } + state = Yaml(data, name="different") + assert state.name == "different.yaml" + loaded = state.load() + assert all(v1 == v2 for v1, v2 in zip(loaded["list"], [1, 2, 3])) + assert all(v1 == v2 for v1, v2 in zip(loaded["tuple"], (4, 5, 6))) + assert loaded["dict"] == {"a": 1, "b": 2} + assert loaded["str"] == "hello" + assert loaded["int"] == 42