eoio.writers.base module#

eoio.writers.base - base classes for eoio dataset writers

Design goals#

  • Mirror eoio.readers.base so both halves of the library read the same way.

  • Keep the base small: it owns configuration and orchestration, concrete writers own the format.

  • Keep import-time dependencies light (format backends are imported lazily).

Writer contract#

Concrete writers must implement: - write_dataset(obj, path) -> None - get_extension() -> str

and should set: - name - the identifier the writer is registered under

Optionally override: - can_write(obj) -> WriteCapability - to refuse objects the format cannot express - default_write_params / all_write_params - to declare format-specific options

Typical usage#

writer = SomeWriter(write_params={…}) writer.write(ds, “output.nc”)

write() is the public entrypoint. Unlike a reader, which is constructed around a single product path, a writer instance holds only configuration and can be reused across many outputs - which is what write_multi relies on.

Note on history#

This is not a revival of the writer package deleted in 8d1a300. That one patched existing products in place (copy a .SAFE tree, apply per-band corrections); this one serialises datasets. The contracts have nothing in common.

class eoio.writers.base.BaseWriter(write_params: Dict[str, Any] | None = None)[source]#

Bases: ABC

Base class for eoio writers.

This class is intentionally small. It owns: - config merge/validation - a single public entrypoint: write() - the orchestration common to every format: capability check, conformance check,

provenance, attribute encoding

Concrete writers implement write_dataset() and get_extension().

property all_options: Dict[str, Any]#

Return the available options for this writer.

Returns:

Dictionary of available write parameters.

all_write_params: Dict[str, str] = {'create_parents': 'True/False', 'encode_attrs': 'True/False', 'on_nonconformance': 'warn/error/ignore', 'update_history': 'True/False'}#
can_write(obj: Any) → WriteCapability[source]#

Report whether this writer can express obj.

The default accepts any Dataset or DataTree, which is correct for general-purpose formats: anything expressible in xarray is expressible in netCDF or Zarr. Format-specific writers should override this and explain what is missing - a Sentinel-2 SAFE writer needs an S2-shaped dataset, and saying so is more useful than emitting a broken product.

Parameters:

obj – Candidate object.

Returns:

Capability, with a reason when the answer is no.

check_conformance(obj: Dataset | DataTree) → List[str][source]#

Check the object against CF-1.8 / ACDD / the controlled vocabulary.

Placeholder until the conformance checks land (see #205); returning no issues keeps the orchestration honest in the meantime rather than pretending to check.

Parameters:

obj – Object about to be written.

Returns:

Human-readable descriptions of non-conformances; empty when it conforms.

default_write_params: Dict[str, Any] = {'create_parents': True, 'encode_attrs': True, 'on_nonconformance': 'warn', 'update_history': True}#
abstractmethod static get_extension() → str[source]#

Return the filename extension this writer produces, including the dot.

Used to name outputs when the caller gives a directory rather than a full path (see write_multi).

name: str = ''#

Name this writer is registered under. Concrete writers must set this.

write(obj: Dataset | DataTree, path: Path | str) → str[source]#

Public entrypoint: write an object to path.

Orchestration is the same for every format:

  1. can_write() - refuse early, with a reason the user can act on

  2. check_conformance() - CF-1.8 / ACDD / controlled vocabulary

  3. provenance - append to history and eoio:processing_steps

  4. encode_attrs() - make attributes serialisable

  5. write_dataset() - the format-specific part

Provenance is added before encoding and writing, so that it ends up in the written file rather than only on a discarded in-memory copy.

The input object is never modified.

Parameters:
  • obj – Dataset or DataTree to write.

  • path – Destination path.

Returns:

The path written, as a string.

Raises:

TypeError – If this writer cannot express obj.

abstractmethod write_dataset(obj: Dataset | DataTree, path: Path) → None[source]#

Write the object in this writer’s format.

Called with attributes already encoded and provenance already applied, so implementations only have to deal with the format itself.

Parameters:
  • obj – Dataset or DataTree to write.

  • path – Destination path, already resolved.

eoio.writers.base.WritableObject#

Objects a writer accepts.

alias of Dataset | DataTree

class eoio.writers.base.WriteCapability(ok: bool, reason: str = '')[source]#

Bases: object

Whether a writer can express a given object in its format.

Parameters:
  • ok – Whether the object can be written.

  • reason – Why not, when ok is False. Should say what is missing, so the user can act on it.

ok: bool#
reason: str = ''#
class eoio.writers.base.WriterConfig(write_params: Dict[str, Any], provided_write_params: FrozenSet[str] = frozenset({}))[source]#

Bases: object

Normalised, validated configuration for a writer instance.

provided_write_params: FrozenSet[str] = frozenset({})#

Names the caller actually supplied. Needed wherever a default and an explicitly requested value of the same parameter must behave differently: the Zarr writer refuses mode alongside append_dim, which it can only do without also refusing its own default.

write_params: Dict[str, Any]#