Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ jobs:
- name: Check out repository code
uses: actions/checkout@v4

- name: Install uv with cache
uses: astral-sh/setup-uv@v5
with:
version: "0.6.2"
enable-cache: true
cache-dependency-glob: "uv.lock"

- name: Install nox
uses: wntrblm/nox@2025.02.09

Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
*__pycache__/
poetry.lock
dist/
.idea/
.idea/
.venv/
uv.lock
ifdo.egg-info/
build/
40 changes: 31 additions & 9 deletions ifdo/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@

from pydantic import BaseModel, Field, field_validator
from stringcase import spinalcase
from yaml import safe_dump, safe_load
from yaml import safe_dump as yaml_dump, safe_load as yaml_load
from json import dump as json_dump, load as json_load

from ifdo.model import model

Expand Down Expand Up @@ -962,8 +963,8 @@ class iFDO: # noqa: N801
image_set_items (dict[str, list[ImageData]]): A dictionary mapping keys to lists of ImageData objects.

Methods:
load(path: str | Path) -> 'iFDO': Class method to load an iFDO object from a YAML file.
save(path: str | Path) -> None: Instance method to save the iFDO object to a YAML file.
load(path: str | Path) -> 'iFDO': Class method to load an iFDO object from a YAML or JSON file.
save(path: str | Path) -> None: Instance method to save the iFDO object to a YAML or JSON file.

Example:
# Load an existing iFDO from a YAML file
Expand All @@ -983,26 +984,47 @@ class iFDO: # noqa: N801
@classmethod
def load(cls, path: str | Path) -> "iFDO":
"""
Load an iFDO from a YAML file.
Load an iFDO from a YAML or JSON file.

Args:
path: Path to the YAML file.
path: Path to the file. Should have a suffix of `.yaml`, `.yml`, or `.json`.

Returns:
The loaded iFDO object.

Raises:
ValueError: If the file format is not supported.
"""
path = Path(path) # Ensure Path object
with path.open() as f:
d = safe_load(f)
suffix = path.suffix.lower().lstrip(".")
if suffix in ("yaml", "yml"):
d = yaml_load(f)
elif suffix == "json":
d = json_load(f)
else:
raise ValueError("Unsupported file format. Use YAML (.yaml, .yml) or JSON (.json).")

return cls.from_dict(d)

def save(self, path: str | Path) -> None:
"""
Save to a YAML file.
Save to a YAML or JSON file. Should have a suffix of `.yaml`, `.yml`, or `.json`.

Args:
path: Path to the YAML file.
path: Path to the file.

Raises:
ValueError: If the file format is not supported
"""
path = Path(path) # Ensure Path object
with path.open("w") as f:
safe_dump(self.to_dict(), f, sort_keys=False)
suffix = path.suffix.lower().lstrip(".")
if suffix in ("yaml", "yml"):
yaml_dump(self.to_dict(), f, sort_keys=False)
elif suffix == "json":
json_dump(self.to_dict(), f, indent=2)
else:
raise ValueError(
"Unsupported file format. Use YAML (.yaml, .yml) or JSON (.json).",
)
7 changes: 5 additions & 2 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from nox import session
import nox

@session(python=["3.10", "3.11", "3.12", "3.13"])

nox.options.default_venv_backend = "uv"

@nox.session(python=["3.10", "3.11", "3.12", "3.13"])
def tests(session):
session.install("pytest", "jsonschema", ".")
session.run("pytest")
53 changes: 30 additions & 23 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
[tool.poetry]
[project]
name = "ifdo"
version = "1.2.5"
description = "Python iFDO implementation"
readme = "README.md"
authors = [
"Kevin Barnard <kbarnard@mbari.org>",
"Chris Jackett <chris.jackett@csiro.au>",
{name = "Kevin Barnard", email = "kbarnard@mbari.org"},
{name = "Chris Jackett", email = "chris.jackett@csiro.au"},
{name = "Karsten Schimpf", email = "karsten.schimpf@awi.de"},
]
license = {text = "MIT"}
requires-python = ">=3.10"

dependencies = [
"stringcase<2.0.0,>=1.2.0",
"pyyaml<7.0,>=6.0",
"pydantic<3.0.0,>=2.4.2",
]

[dependency-groups]
dev = [
"pre-commit<5.0.0,>=4.0.1",
"ruff<1.0.0,>=0.7.3",
"black<25.0.0,>=24.10.0",
"mypy<2.0.0,>=1.13.0",
"bandit<2.0.0,>=1.7.10",
"types-pyyaml<7.0.0.0,>=6.0.12.20240917",
"pytest<9.0.0,>=8.3.4",
"nox<2026.0.0,>=2025.2.9",
"jsonschema<5.0.0,>=4.23.0"
]
license = "MIT"
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
stringcase = "^1.2.0"
pyyaml = "^6.0"
pydantic = "^2.4.2"
[tool.uv]
package=true

[tool.poetry.group.dev.dependencies]
pre-commit = "^4.0.1"
ruff = "^0.7.3"
black = "^24.10.0"
mypy = "^1.13.0"
bandit = "^1.7.10"
types-pyyaml = "^6.0.12.20240917"
pytest = "^8.3.4"
nox = "^2025.2.9"
jsonschema = "^4.23.0"
[tool.setuptools.packages.find]
include = ["ifdo*"]

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Loading