This release is a pre-release and may not be stable for production use.
kindling_ext_sdp
Spark Declarative Pipelines (SDP) declaration engine for Kindling.
This package turns existing DataEntities/DataPipes registrations into a
validated, pure-metadata DeclarationPlan that a concrete engine can emit as
pyspark.pipelines (OSS Spark 4.1+) or Databricks Lakeflow declarations.
Install it from PyPI with pip install spark-kindling-ext-sdp; in a uv project,
kindling env add spark-kindling-ext-sdp pins the version that matches the
project's Kindling release.
Phase 1 contains:
- The abstract
DeclarationEngineinterface and the shared plan builder. - Internal/external input classification derived from the registries.
- Capability gating (
OSS_SDPvsDATABRICKS_SDPfeature sets) with fail-fast, all-errors-at-once validation. - Dataset-type selection (entity tag, then engine config, then default
materialized_view).
Phase 2 adds:
OssSdpEngine— emits the plan through the OSSpyspark.pipelinesdecorator API (materialized views only). Provider-owned streaming sources are recognized in the shared plan but capability-gated off for OSS SDP until Spark pipeline parity is accepted separately. Thedpmodule is an injected dependency resolved lazily at declaration time, so importing this package still never importspyspark.pipelinesand Kindling keeps supporting Spark runtimes older than 4.1.SdpWriteGuardProvider— the write-inert provider personality installed by SDP-mode bootstrap ("SDP owns persistence"): reads delegate, every write path raisesSdpModeWriteError.- The bootstrap surface:
kindling.initialize(engine="sdp")activates the guard and skips the watermark aspect (SDP owns incrementality);kindling.declare_pipeline()— the mandatory LAST step of the entry point — builds, validates, and declares the plan. - The local validation harness (
write_pipeline_spec+dry_run): ephemeralspark-pipeline.yml+spark-pipelines dry-run, a unit-test-tier gate for the declared graph with no cluster in the loop (requires the Spark 4.1+ CLI:pip install 'pyspark[pipelines]>=4.1').
Phase 3 adds:
- Full entity-metadata emission on the OSS engine:
comment,table_properties(entity tagssdp.table_properties.<key>over an engine-configtable_propertiesblock, tag winning per key),partition_cols,cluster_by, andschema— the complete Spark 4.1dp.materialized_viewkeyword surface. Deliberate divergence from the runner engine, per the dual-engine parity criterion: SDP does NOT forcedelta.enableChangeDataFeed(that feeds the runner's watermark machinery); declare it as a tag if external consumers need CDF. - An
engine_factoryseam ondeclare_pipeline()so adapter packages reuse the whole bootstrap path. - The
kindling_ext_databricksadapter package (separate README): Lakeflow expectations, selected viaengine="databricks_sdp".
Provider-owned streaming sources:
- The shared planner can classify an external input as
EXTERNAL_STREAMING_SOURCEwhen its provider implementsDeclarableStreamingSourceandStreamableEntityProvider. - The provider returns an inert, secret-safe
StreamingSourceSpec; plans and diagnostics include tag/option names and validation constraints, never connection strings, SAS keys, passwords, JAAS values, or resolved secrets. - A streaming source must be a driving input selected by
driving_entity_ids; only one declarable streaming source is supported in a pipe, and temporal/AUTO CDC compositions reject the ambiguous shape. - The output dataset type is inferred as
streaming_table. Explicit materialized-view requests fail withstreaming_dataset_type_conflictinstead of being silently overridden. engine="sdp"reportsstreaming_source_lowering_not_supportedfor this shape today.engine="databricks_sdp"lowers it through Lakeflow; see the Databricks extension README.- An ordinary external Delta input has no provider-owned stream, so it is
a batch read unless a pipe opts in with the adapter-tier engine-config key
streaming_inputs: [<entity id>]. The opt-in reaches the same emission (streaming table + one append flow, non-driving inputs staying batch), is never inferred, and must name every driving input. See the Databricks extension README for the full rejection list.
Entry point shape (fixed bootstrap surface — never generated code):
import kindling
kindling.initialize(engine="sdp")
from my_app import register_all
register_all()
kindling.declare_pipeline()
See:
docs/proposals/declarative_pipelines_engine.md— the full proposal.docs/proposals/obsolete/sdp_engine_phase1_notes.md— Phase-1 architecture decisions (bootstrap ordering, provider write-inertness, deferrals).
Output dataset naming
Configure names within the pipeline's target catalog/schema independently of logical entity IDs and external table resolution:
kindling:
sdp:
dataset_naming: leaf
| Mode | Logical entity ID | Emitted dataset name |
|---|---|---|
normalized (default) |
silver.device_telemetry |
silver_device_telemetry |
leaf |
silver.device_telemetry |
device_telemetry |
Both modes replace hyphens with underscores. Config values ignore surrounding
whitespace and case; an omitted or null value uses normalized. Unknown modes
are reported alongside other declaration validation errors.
The setting is resolved by declare_pipeline() after configuration overlays
and applies to both sdp and databricks_sdp. Direct engine constructors
accept dataset_naming="leaf"; DatasetNameMapper implements the shared
pipeline-local naming strategy.
Names must be unique (case-insensitively) among selected outputs in one
declaration plan. Use kindling.declare_pipeline(pipe_ids=[...]) to select
the pipes belonging to each resource. Separate bronze and silver resources
can each emit device_telemetry; selecting both same-leaf outputs in one
resource fails before emission with the conflicting entity and pipe IDs.
Internal batch/stream reads use these single-part names. External Delta batch
reads resolve the registered entity metadata through the runtime's
EntityNameMapper before calling spark.table. This honors
provider.table_catalog, provider.table_schema, provider.table_name, and
provider.table_name_strategy, independently of the pipeline's current catalog
and its output naming mode. Logical IDs and pipe function argument names stay
unchanged; an explicit external_read_resolver still receives the logical ID.
Provider-owned streaming sources retain their provider streaming read path.
Reading leaf-named outputs from elsewhere
Enabling leaf does not change the consumer's EntityNameMapper. With
catalog dev_silver and schema cwmdp configured, that mapper still resolves
silver.device_telemetry to dev_silver.cwmdp.silver_device_telemetry,
while the leaf-mode pipeline writes dev_silver.cwmdp.device_telemetry.
Align the consumer's entity metadata with the mapper's leaf name strategy:
dataentities-bytag:
ldp_output:
"true":
tags:
provider.table_name_strategy: leaf
The strategy retains the entity's resolved catalog and schema, but uses only the
logical entity ID's final segment for its table name. Apply the ldp_output
tag to the matching entities; dataentities-bytag adds the mapper strategy to
the whole family. The pipeline continues to declare the single-part name
device_telemetry.
provider.table_name remains a complete override and takes precedence over
the name strategy, provider.table_catalog, and provider.table_schema.
Temporal chain base-event sources currently always use external table resolution, even if a producing pipe is selected in the same pipeline. Keep those sources in an upstream resource and align their external names as above; producing a source in the same resource does not establish a local temporal dependency.
On engine="databricks_sdp", kindling.lakeflow.temporal_mode: batch lowers
<events>__g0..gN as materialized views with batch reads instead of streaming
tables with append flows; the reserved names below are unchanged in both
modes. See the
Databricks extension documentation.
Generated dataset names
Lakeflow reserves helper names as well as selected outputs. Validation rejects
collisions with AUTO CDC <target>__scd_source, temporal <events>__g0..N
(where N is kindling.temporal.max_generations), <events>__ghi,
<events>__determinations, and <episodes>__episode_snapshot.
Conditional temporal helper names are reserved even when the current rules
do not use them, so later rule changes cannot introduce name collisions.
Selecting a temporal chain-events pipe also reserves and emits its registered chain-episodes sibling, even when the episodes pipe is not explicitly selected. These reservations apply only to the resource containing that chain or CDC target; other pipeline resources can use the same names.
Metadata
Release files for spark-kindling-ext-sdp 0.14.0rc1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| spark_kindling_ext_sdp-0.14.0rc1.tar.gz | 26.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| spark_kindling_ext_sdp-0.14.0rc1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 58.3 kB
Release files / spark_kindling_ext_sdp-0.14.0rc1.tar.gz
| Download URL | spark_kindling_ext_sdp-0.14.0rc1.tar.gz |
|---|---|
| Size | 26.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7fd62e6d93b394efecea004a0af4e2cc6f635b1e4056aeab1816010cb186b114
|
|
BLAKE2b-256 checksum How to use checksums |
2df669f8e70c2c0049b04103c4bc59bff233c867f43dd86f9179cecc69488c78
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.20 {"installer":{"name":"uv","version":"0.12.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / spark_kindling_ext_sdp-0.14.0rc1-py3-none-any.whl
| Download URL | spark_kindling_ext_sdp-0.14.0rc1-py3-none-any.whl |
|---|---|
| Size | 31.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c38e1968675280354e0f236d312c0180099ff8e5f3b7876744e5244f436f2d7d
|
|
BLAKE2b-256 checksum How to use checksums |
fa4b255445324bcca5ddf17e9f30943fe0d4f9d865ae7148ac2123b41bd390a5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.20 {"installer":{"name":"uv","version":"0.12.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|