Metadata-Version: 2.4
Name: vcti-tree-exporter
Version: 2.0.0
Summary: Format-agnostic exporters for vcti trees: an Exporter protocol and registry, with per-format writers as plugins.
Author: Visual Collaboration Technologies Inc.
License-Expression: LicenseRef-Proprietary
Project-URL: Homepage, https://github.com/vcollab/vcti-python-tree-exporter
Project-URL: Repository, https://github.com/vcollab/vcti-python-tree-exporter
Project-URL: Changelog, https://github.com/vcollab/vcti-python-tree-exporter/blob/main/CHANGELOG.md
Keywords: tree,export,serialization,exporter,protocol,registry,plugin
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Operating System :: OS Independent
Classifier: Typing :: Typed
Requires-Python: <3.15,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: vcti-tree>=2.0.0
Requires-Dist: vcti-datanode>=2.0.0
Requires-Dist: vcti-plugin-catalog>=1.0.1
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Provides-Extra: lint
Requires-Dist: ruff; extra == "lint"
Provides-Extra: typecheck
Requires-Dist: mypy; extra == "typecheck"
Dynamic: license-file

# Tree Exporter

Format-agnostic exporters for vcti trees: an `Exporter` protocol and a plugin catalog, with per-format writers as plugins.

`vcti-tree-exporter` defines *what an exporter is* and *how to catalogue and
look one up*. It implements no file format itself — concrete writers (HDF5,
JSON, CSV, NumPy, …) live in separate plugin packages and are registered by the
consumer. The thing being exported is any
[`vcti-tree`](https://github.com/vcollab/vcti-python-tree) tree whose node
payloads are [`vcti-datanode`](https://github.com/vcollab/vcti-python-datanode)
`DataNode`s (data + attributes), so one tree can be written to any registered
format without the producer knowing about serialization.

Cataloguing and lookup are built on
[`vcti-plugin-catalog`](https://github.com/vcollab/vcti-python-plugin-catalog)
(`Descriptor` + `Registry`).

## Installation

```bash
pip install vcti-tree-exporter
```

The base package is intentionally small. Install a format plugin for each
format you need:

```bash
pip install vcti-tree-exporter-hdf5    # single .h5 file
pip install vcti-tree-exporter-json    # single .json document
pip install vcti-tree-exporter-npz     # single .npz archive
pip install vcti-tree-exporter-csv     # directory tree of CSV tables
```

## Core concepts

| Piece | Role |
|---|---|
| `Exporter` | Structural protocol — a single `export(tree, path, *, overwrite=False)` method. Pure behavior; format name and extension are **not** on the exporter. |
| `ExporterDescriptor` | A `vcti-plugin-catalog` `Descriptor[Exporter]`: the exporter is its `instance`; `format`, `extension`, and any other metadata are filterable `attributes`. |
| `get_exporter_descriptor()` | Factory each plugin package provides, returning its `ExporterDescriptor`. |
| `build_registry(descriptors)` | Build a plugin-catalog `Registry` from those descriptors. |
| `get_exporter(registry, id)` | Resolve an exporter instance by descriptor id. |

## Quick Start

Register the descriptors your application uses, then resolve one and write a
tree:

```python
from pathlib import Path
from vcti.tree.exporter.core import build_registry, get_exporter
from vcti.tree.exporter.hdf5 import get_exporter_descriptor as hdf5_descriptor
from vcti.tree.exporter.json import get_exporter_descriptor as json_descriptor

registry = build_registry([hdf5_descriptor(), json_descriptor()])
get_exporter(registry, "hdf5").export(tree, Path("model.h5"), overwrite=True)
```

`registry` is an ordinary `vcti.plugincatalog.Registry`, so the general way to
select an exporter is to filter by attributes, then use the matching id — the
same pattern as the rest of the ecosystem:

```python
from vcti.lookup import Rule
from vcti.tree.exporter.core import EXTENSION_ATTR

(desc,) = registry.lookup.filter([Rule(EXTENSION_ATTR, "==", ".h5")])
desc.exporter.export(tree, Path("model.h5"))
```

Exporter ids are a small, fixed, well-known set, so using `get_exporter(registry,
"hdf5")` directly is also fine.

## Writing a format plugin

A plugin's exporter only has to satisfy the protocol — one `export` method, no
base class — and ship a `get_exporter_descriptor()` factory carrying its
metadata:

```python
from pathlib import Path
from vcti.tree.exporter.core import EXTENSION_ATTR, FORMAT_ATTR, ExporterDescriptor

class JsonExporter:
    def export(self, tree, path: Path, *, overwrite: bool = False) -> None:
        ...   # walk the tree (node.name, node.attributes, node.load()), write JSON

def get_exporter_descriptor() -> ExporterDescriptor:
    return ExporterDescriptor(
        id="json", name="JSON Exporter", exporter=JsonExporter(),
        attributes={FORMAT_ATTR: "json", EXTENSION_ATTR: ".json"},
    )
```

Ship it in its own package (with its own format dependencies, e.g. `h5py` for
HDF5). The consuming application decides which descriptors to register — there
is no implicit global discovery.

## Dependencies

- [vcti-tree](https://github.com/vcollab/vcti-python-tree) — the tree protocols the exporter reads.
- [vcti-datanode](https://github.com/vcollab/vcti-python-datanode) — the node payload type.
- [vcti-plugin-catalog](https://github.com/vcollab/vcti-python-plugin-catalog) — the `Descriptor` / `Registry` catalog.

(No heavy/runtime format dependencies live here — those belong to the per-format plugin packages.)
