Skip to content

Custom Exporters

Palettize’s exporter system is extensible. You can create custom exporters for your specific output formats and distribute them as plugins.

All exporters inherit from BaseExporter and implement required methods.

from typing import Any, ClassVar
from collections.abc import Mapping
from palettize import Colormap, Opt, OptionSpec, ScalingFunction
from palettize.exporters import BaseExporter
class MyExporter(BaseExporter):
options: ClassVar[OptionSpec] = OptionSpec(
Opt("num_colors", int, 10, "Number of color steps.", minimum=2),
)
@property
def identifier(self) -> str:
"""Unique identifier used in CLI and get_exporter()"""
return "myformat"
@property
def name(self) -> str:
"""Human-readable name"""
return "My Custom Format"
@property
def default_file_extension(self) -> str | None:
"""Default file extension (without dot)"""
return "txt"
def export(
self,
colormap: Colormap,
scaler: ScalingFunction,
domain_min: float,
domain_max: float,
options: Mapping[str, Any] | None = None,
) -> str:
"""Generate output string"""
num_colors = self.resolve_options(options)["num_colors"]
lines = []
for i in range(num_colors):
position = i / (num_colors - 1) if num_colors > 1 else 0.5
data_val = domain_min + position * (domain_max - domain_min)
color = colormap.apply_scaler(data_val, scaler)
lines.append(f"{data_val}: {color}")
return "\n".join(lines)

Declare every option as an Opt. That declaration coerces CLI strings (-O num_colors=256), rejects bad values, and is what palettize formats myformat prints. Read the resolved dict inside export; do not call options.get on the raw mapping.

MethodTypeDescription
identifierpropertyUnique string ID
namepropertyDisplay name
export()methodGenerate output string
MethodTypeDefaultDescription
default_file_extensionpropertyNoneSuggested file extension

For use within your project:

from palettize import register_exporter
exporter = MyExporter()
register_exporter(exporter)
# Now available via get_exporter
from palettize import get_exporter
my_exp = get_exporter("myformat")

To replace an existing exporter:

register_exporter(exporter, overwrite=True)

Distribute your exporter as an installable package.

my-palettize-exporters/
├── pyproject.toml
├── src/
│ └── my_exporters/
│ ├── __init__.py
│ └── formats.py

src/my_exporters/formats.py:

from typing import Any, ClassVar
from collections.abc import Mapping
from palettize import Colormap, Opt, OptionSpec, ScalingFunction
from palettize.exporters import BaseExporter
class CSVExporter(BaseExporter):
options: ClassVar[OptionSpec] = OptionSpec(
Opt("num_colors", int, 256, "Number of color steps.", minimum=2),
)
@property
def identifier(self) -> str:
return "csv"
@property
def name(self) -> str:
return "CSV Color Table"
@property
def default_file_extension(self) -> str | None:
return "csv"
def export(
self,
colormap: Colormap,
scaler: ScalingFunction,
domain_min: float,
domain_max: float,
options: Mapping[str, Any] | None = None,
) -> str:
num_colors = self.resolve_options(options)["num_colors"]
lines = ["value,r,g,b,a"]
for i in range(num_colors):
pos = i / (num_colors - 1)
data_val = domain_min + pos * (domain_max - domain_min)
rgba = colormap.apply_scaler(data_val, scaler, output_format="rgba_tuple")
r, g, b, a = rgba
lines.append(f"{data_val},{r},{g},{b},{a}")
return "\n".join(lines)

pyproject.toml:

[project]
name = "my-palettize-exporters"
version = "0.1.0"
dependencies = ["palettize"]
[project.entry-points."palettize.exporters"]
csv = "my_exporters.formats:CSVExporter"
Terminal window
pip install my-palettize-exporters
# Now available in CLI
palettize list exporters # Shows "csv"
palettize create viridis -f csv -o colors.csv --domain 0,255
import xml.etree.ElementTree as ET
from typing import Any, ClassVar
from collections.abc import Mapping
from palettize import Colormap, Opt, OptionSpec, ScalingFunction
from palettize.exporters import BaseExporter
class XMLColorTableExporter(BaseExporter):
options: ClassVar[OptionSpec] = OptionSpec(
Opt("num_colors", int, 256, "Number of color steps.", minimum=2),
Opt("name", str, None, "Table name. Defaults to the colormap name."),
)
@property
def identifier(self) -> str:
return "xmlct"
@property
def name(self) -> str:
return "XML Color Table"
@property
def default_file_extension(self) -> str | None:
return "xml"
def export(
self,
colormap: Colormap,
scaler: ScalingFunction,
domain_min: float,
domain_max: float,
options: Mapping[str, Any] | None = None,
) -> str:
opts = self.resolve_options(options)
num_colors = opts["num_colors"]
cmap_name = opts["name"] or colormap.name or "colormap"
root = ET.Element("ColorTable", name=cmap_name)
root.set("domain_min", str(domain_min))
root.set("domain_max", str(domain_max))
for i in range(num_colors):
pos = i / (num_colors - 1)
data_val = domain_min + pos * (domain_max - domain_min)
hex_color = colormap.apply_scaler(data_val, scaler)
entry = ET.SubElement(root, "Entry")
entry.set("value", f"{data_val:.4f}")
entry.set("color", hex_color)
ET.indent(root, space=" ")
return ET.tostring(root, encoding="unicode", xml_declaration=True)

Opt is the validation. A bool option accepts true/false from the CLI, an int rejects 5.5, and choices limits a string to a set. palettize formats <id> prints help, the type, and the default.

from palettize import Opt, OptionSpec
options = OptionSpec(
Opt("num_colors", int, 256, "Number of colors.", minimum=2),
Opt("include_header", bool, True, "Include a header line."),
Opt("precision", int, 2, "Decimal places.", minimum=0),
)
opts = self.resolve_options(options_from_caller)
num_colors = opts["num_colors"]
import pytest
from palettize import create_colormap, get_scaler_by_name
from my_exporters.formats import CSVExporter
def test_csv_export():
cmap = create_colormap(preset="viridis")
scaler = get_scaler_by_name("linear", domain_min=0, domain_max=100)
exporter = CSVExporter()
output = exporter.export(cmap, scaler, 0, 100, {"num_colors": 5})
lines = output.strip().split("\n")
assert lines[0] == "value,r,g,b,a"
assert len(lines) == 6 # header + 5 colors
def test_csv_identifier():
exporter = CSVExporter()
assert exporter.identifier == "csv"
assert exporter.default_file_extension == "csv"
  1. Declare options with Opt: types, ranges, and help text live in one place
  2. Keep help to one line: palettize formats prints it as the option description
  3. Handle edge cases: Empty colormaps, single color, etc.
  4. Use meaningful defaults: Most users won’t specify options
  5. Return strings: The export() method always returns a string
  6. Test thoroughly: Cover various colormap types and options