Skip to content

Commit

Permalink
added load, save and new methods to core.FileSet
Browse files Browse the repository at this point in the history
  • Loading branch information
tclose committed Sep 16, 2024
1 parent 76422be commit 3b4957d
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 51 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ repos:
--non-interactive,
]
exclude: tests
additional_dependencies: [pytest, attrs]
additional_dependencies: [pytest, attrs, imageio]
2 changes: 1 addition & 1 deletion extras/fileformats/extras/application/serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def convert_data_serialization(
output_path = out_dir / (
in_file.fspath.stem + (output_format.ext if output_format.ext else "")
)
return output_format.save_new(output_path, dct)
return output_format.new(output_path, dct)


@extra_implementation(DataSerialization.load)
Expand Down
4 changes: 2 additions & 2 deletions extras/fileformats/extras/image/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ def convert_image(
output_format: ty.Type[RasterImage],
out_dir: ty.Optional[Path] = None,
) -> RasterImage:
data_array = in_file.read_data()
data_array = in_file.load()
if out_dir is None:
out_dir = Path(tempfile.mkdtemp())
output_path = out_dir / (
in_file.fspath.stem + (output_format.ext if output_format.ext else "")
)
return output_format.save_new(output_path, data_array)
return output_format.new(output_path, data_array)
8 changes: 4 additions & 4 deletions extras/fileformats/extras/image/readwrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from fileformats.image.raster import RasterImage, DataArrayType


@extra_implementation(RasterImage.read_data)
@extra_implementation(RasterImage.load)
def read_raster_data(image: RasterImage) -> DataArrayType:
return imageio.imread(image.fspath) # type: ignore


@extra_implementation(RasterImage.write_data)
def write_raster_data(image: RasterImage, data_array: DataArrayType) -> None:
imageio.imwrite(image.fspath, data_array)
@extra_implementation(RasterImage.save)
def write_raster_data(image: RasterImage, data: DataArrayType) -> None:
imageio.imwrite(image.fspath, data)
21 changes: 2 additions & 19 deletions fileformats/application/serialization.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import json
import typing as ty
from fileformats.core.typing import Self, TypeAlias
from fileformats.core.typing import TypeAlias
from pathlib import Path
from fileformats.core import extra, DataType, FileSet, extra_implementation
from fileformats.core import DataType, FileSet, extra_implementation
from fileformats.core.mixin import WithClassifiers
from ..generic import File
from fileformats.core.exceptions import FormatMismatchError
Expand Down Expand Up @@ -44,23 +44,6 @@ class DataSerialization(WithClassifiers, File):

iana_mime: ty.Optional[str] = None

@extra
def load(self) -> LoadedSerialization:
"""Load the contents of the file into a dictionary"""
raise NotImplementedError

@extra
def save(self, data: LoadedSerialization) -> None:
"""Serialise a dictionary to a new file"""
raise NotImplementedError

@classmethod
def save_new(cls, fspath: ty.Union[str, Path], data: LoadedSerialization) -> Self:
# We have to use a mock object as the data file hasn't been written yet
mock = cls.mock(fspath)
mock.save(data)
return cls(fspath)


class Xml(DataSerialization):
ext: ty.Optional[str] = ".xml"
Expand Down
6 changes: 5 additions & 1 deletion fileformats/core/extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@ def decorator(implementation: ExtraImplementation) -> ExtraImplementation:

def type_match(a: ty.Union[str, type], b: ty.Union[str, type]) -> bool:
return (
a == b or inspect.isclass(a) and inspect.isclass(b) and issubclass(b, a)
a is ty.Any # type: ignore[comparison-overlap]
or a == b
or inspect.isclass(a)
and inspect.isclass(b)
and issubclass(b, a)
)

mhas_kwargs = msig_args and msig_args[-1].kind == inspect.Parameter.VAR_KEYWORD
Expand Down
46 changes: 46 additions & 0 deletions fileformats/core/fileset.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,52 @@ def __hash__(self) -> int:
def __repr__(self) -> str:
return f"{self.type_name}('" + "', '".join(str(p) for p in self.fspaths) + "')"

@extra
def load(self) -> ty.Any:
"""Load the contents of the file into an object of type that make sense for the
datat type
Returns
-------
Any
the data loaded from the file in an type to the format
"""

@extra
def save(self, data: ty.Any) -> None:
"""Load new contents from a format-specific object
Parameters
----------
data: Any
the data to be saved to the file in a type that matches the one loaded by
the `load` method
"""

@classmethod
def new(cls, fspath: ty.Union[str, Path], data: ty.Any) -> Self:
"""Create a new file-set object with the given data saved to the file
Parameters
----------
fspath: str | Path
the file-system path to save the data to. Additional paths should be
able to be inferred from this path
data: Any
the data to be saved to the file in a type that matches the one loaded by
the `load` method
Returns
-------
FileSet
a new file-set object with the given data saved to the file
"""
# We have to use a mock object as the data file hasn't been written yet so can't
# be validated
mock = cls.mock(fspath)
mock.save(data)
return cls(fspath)

@property
def parent(self) -> Path:
"A common parent directory for all the top-level paths in the file-set"
Expand Down
14 changes: 9 additions & 5 deletions fileformats/core/mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def header(self) -> "fileformats.core.FileSet":
def read_metadata(
self, selected_keys: ty.Optional[ty.Collection[str]] = None
) -> ty.Mapping[str, ty.Any]:
header: ty.Dict[str, ty.Any] = self.header.load() # type: ignore[attr-defined]
header: ty.Dict[str, ty.Any] = self.header.load()
if selected_keys:
header = {k: v for k, v in header.items() if k in selected_keys}
return header
Expand Down Expand Up @@ -227,12 +227,16 @@ def read_metadata(
metadata: ty.Dict[str, ty.Any] = dict(self.primary_type.read_metadata(self, selected_keys=selected_keys)) # type: ignore[arg-type]
for side_car in self.side_cars:
try:
side_car_metadata: ty.Dict[str, ty.Any] = side_car.load() # type: ignore[attr-defined]
side_car_metadata: ty.Dict[str, ty.Any] = side_car.load()
except AttributeError:
continue
else:
side_car_class_name: str = to_mime_format_name(type(side_car).__name__)
metadata[side_car_class_name] = side_car_metadata
if not isinstance(side_car_metadata, dict):
raise TypeError(
f"`load` method of side-car type {type(side_car)} must return a "
f"dictionary, not {type(side_car_metadata)!r}"
)
side_car_class_name: str = to_mime_format_name(type(side_car).__name__)
metadata[side_car_class_name] = side_car_metadata
return metadata

@classproperty
Expand Down
19 changes: 1 addition & 18 deletions fileformats/image/raster.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from pathlib import Path
from fileformats.core.typing import Self, TypeAlias
from fileformats.core.typing import TypeAlias
import typing as ty
from fileformats.core.mixin import WithMagicNumber
from fileformats.core import extra
from fileformats.core.exceptions import FormatMismatchError
from .base import Image

Expand All @@ -21,21 +19,6 @@ class RasterImage(Image):
pass
binary = True

@extra
def read_data(self) -> DataArrayType:
...

@extra
def write_data(self, data_array: DataArrayType) -> None:
...

@classmethod
def save_new(cls, fspath: Path, data_array: DataArrayType) -> Self:
# We have to use a mock object as the data file hasn't been written yet
mock = cls.mock(fspath)
mock.write_data(data_array)
return cls(fspath)


class Bitmap(WithMagicNumber, RasterImage):
ext = ".bmp"
Expand Down

0 comments on commit 3b4957d

Please sign in to comment.