Skip to content

Write a transformer

Prerequisites · You have a per-row or per-batch transformation to apply between read and write. End state · Transformer in the pipeline at a known order slot, registered via entry points.

Minimal transformer

from datacoolie.transformers.base import BaseTransformer
from datacoolie.core.models import DataFlow


class PiiMaskerTransformer(BaseTransformer):
    # Slots 40-50 are reserved for user plugins. Pick one.
    ORDER = 45

    @property
    def order(self) -> int:
        return self.ORDER

    def transform(self, df, dataflow: DataFlow):
        cfg = dataflow.transform.configure.get("pii_mask", {})
        cols = cfg.get("columns", [])
        if not cols:
            self._mark_skipped()
            return df

        for c in cols:
            df = self._engine.add_column(
                df, c, f"CASE WHEN {c} IS NULL THEN NULL ELSE '***' END"
            )

        self._mark_applied(f"cols={len(cols)}")
        return df

Register

[project.entry-points."datacoolie.transformers"]
pii_masker = "mypkg.transformers:PiiMaskerTransformer"

Opt in from metadata

Registering a transformer makes it resolvable, but does not add it to the driver's DEFAULT_TRANSFORMERS. Use the documented driver extension hook:

from datacoolie import transformer_registry
from datacoolie.orchestration.driver import DataCoolieDriver


class PiiDriver(DataCoolieDriver):
    def _create_transformer_pipeline(self):
        pipeline = super()._create_transformer_pipeline()
        pipeline.add_transformer(
            transformer_registry.get("pii_masker", engine=self._engine)
        )
        return pipeline


driver = PiiDriver(engine=engine, metadata_provider=metadata)

TransformerPipeline sorts the combined list by each transformer's order when it runs.

Order slot cheat-sheet

Slots Who owns them
0–9 Reserved for future framework pre-cast work
10 SchemaConverter
20 Deduplicator
30 ColumnAdder
35 RowFilter
40–50 Your plugins
60 SCD2ColumnAdder
70 SystemColumnAdder
80 PartitionHandler
90 ColumnNameSanitizer
100+ Reserved for future framework post-sanitize work

See ADR-0003.

Tracking labels

Call _mark_applied(), _mark_applied("detail"), or _mark_skipped() inside transform so the ETL log records exactly what your transformer did. Without a call, the default is to record your class name.