Skip to content

Commit a929983

Browse files
authored
feat(extensions): canonical src/ bundle layout with libraries, init hooks, python modules and extensions deprecation (#39)
Replaces the single-directory extensions/ convention with a structured src/ layout that separates cluster libraries, spec-referenced Python, and lifecycle init scripts. Renames extension_loader.py to bundle_loader.py to reflect its broader role. Layout changes - Pipeline bundles: src/libraries/ (wheels + sys.path), src/python/ (spec Python), src/init/pre/ and src/init/post/ (lifecycle scripts) - Framework bundle: custom code lives exclusively under src/local/ (libraries/, python/, init/pre/, init/post/) — no top-level src/ equivalents - FrameworkPaths constants renamed to LOCAL_* to reflect local/ prefix; all *sourcePath confs resolve to src/ so constants omit the src/ segment Deprecation - extensions/ on sys.path deprecated (v0.13.0), removed in v1.0.0 - extensions/ is registered unconditionally alongside src/python/ when both exist - extensions/libraries/ (never shipped) dropped with no deprecation window Code - extension_loader.py → bundle_loader.py; uses FrameworkPaths.LOCAL_* for framework paths and PipelineBundlePaths.* for bundle paths - dataflow_spec_builder: search paths updated to LOCAL_* framework constants - constants.py: FrameworkPaths gains LOCAL_* paths; PipelineBundlePaths paths corrected to omit redundant src/ prefix Samples & template - bronze_sample migrated: extensions/libraries/ → src/python/, extensions/pre_init|post_init/ → src/init/pre|post/ - New libraries demo: phonenumbers wheel in src/libraries/, installed via environment.dependencies; demonstrates full install→import chain - pipeline_bundle_template scaffolded with README.md for each new src/ directory - src/local/ scaffolded in framework src/ with README.md files Docs & ADRs - feature_python_extensions.rst rewritten: new title, bundle context table, src/python/ replaces src/extensions/ throughout, environment.dependencies replaces libraries: whl/pypi syntax - feature_python_functions.rst, feature_python_source.rst: updated to src/python/ with deprecation notices for src/extensions/ - build_pipeline_bundle_steps.rst, build_pipeline_bundle_structure.rst: updated directory diagrams and added steps for libraries/python/init - docs/decisions/0001-bundle-src-layout.md, 0002-extensions-deprecation.md: new ADRs
1 parent 577e8da commit a929983

40 files changed

Lines changed: 1365 additions & 251 deletions

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v0.12.3
1+
v0.13.0
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# ADR-0001: Canonical pipeline bundle `src/` layout
2+
3+
**Date:** 2026-05-16
4+
**Status:** Accepted
5+
**PR:** feature/init-hooks-extensions-layout (PR1 / v0.13.0)
6+
7+
---
8+
9+
## Context
10+
11+
The framework previously used a single `extensions/` tree for both importable Python
12+
modules and lifecycle scripts. This caused:
13+
14+
- No separation between "code that gets installed on the cluster" and "code that spec
15+
references by module path".
16+
- No clear, fork-safe area for customer customisation.
17+
- Ambiguity between `extensions/` (sys.path) and `extensions/libraries/` (a variant
18+
that was never released to `main`).
19+
20+
OSS consumers also expected a predictable, documented `src/` layout rather than a
21+
single catch-all directory.
22+
23+
## Decision
24+
25+
Introduce a canonical `src/` layout for pipeline bundles:
26+
27+
| Path | Role |
28+
|------|------|
29+
| `src/libraries/` | **Optional** location for wheel files bundled with the pipeline. The directory is also added to `sys.path` for loose `.py` / packages (secondary role). |
30+
| `src/python/` | All customer Python referenced by Data Flow Specs (`pythonModule`, `pythonTransform.module`). Added to `sys.path`. |
31+
| `src/init/pre/` | Lifecycle scripts run **before** SDP declarations inside `initialize_pipeline()`. |
32+
| `src/init/post/` | Lifecycle scripts run **after** SDP declarations. |
33+
34+
The **framework bundle** (`framework.sourcePath`) follows a different layout — custom
35+
code lives exclusively under `src/local/`:
36+
37+
| Path | Role |
38+
|------|------|
39+
| `src/local/libraries/` | Org-wide cluster-install wheels / loose `.py` (sys.path). |
40+
| `src/local/python/` | Org-wide spec-referenced Python (sys.path). |
41+
| `src/local/init/pre/` | Org-wide pre-init lifecycle scripts. |
42+
| `src/local/init/post/` | Org-wide post-init lifecycle scripts. |
43+
44+
Pipeline bundles do **not** have a `src/local/` directory.
45+
46+
**Init script execution model:** `runpy.run_path(..., run_name='__main__')`, sorted
47+
filename order, files starting with `_` skipped. Framework bundle scripts run before
48+
pipeline bundle scripts at each phase.
49+
50+
**Naming:** These directories are called **"init scripts"** (not "hooks") in all
51+
user-facing documentation to avoid confusion with callback-registration APIs.
52+
53+
**`src/local/` name rationale:** Follows the `git config --local` / Django
54+
`local_settings.py` convention — "local" signals "not for upstream".
55+
56+
## Consequences
57+
58+
- Cluster library installation is handled via the DAB pipeline **`environment.dependencies`**
59+
section — the framework is not involved. Customers choose from any supported source:
60+
61+
| Source | Example `environment.dependencies` entry |
62+
|--------|------------------------------------------|
63+
| Bundle wheel (file in `src/libraries/`) | `- /Workspace/${workspace.file_path}/src/libraries/my_pkg.whl` |
64+
| UC Volumes path | `- /Volumes/catalog/schema/my_pkg.whl` |
65+
| PyPI | `- requests>=2.28` |
66+
| Artifact repo (Artifactory/Nexus) | `- https://artifactory.example.com/my_pkg.whl` |
67+
68+
`src/libraries/` is only needed when the wheel is bundled alongside the pipeline
69+
code. It is **not** required if libraries are sourced from PyPI, UC Volumes, or an
70+
artifact repository.
71+
- New bundles should place spec-referenced Python in `src/python/` and cluster-install
72+
artefacts in `src/libraries/` (if bundled).
73+
- The `pipeline_bundle_template/` and samples are migrated to this layout in this PR.
74+
- Existing bundles using `extensions/` continue to work until `v1.0.0` (see ADR-0002).
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# ADR-0002: Deprecation of `extensions/`
2+
3+
**Date:** 2026-05-16
4+
**Status:** Accepted
5+
**PR:** feature/init-hooks-extensions-layout (PR1 / v0.13.0) — deprecation warnings
6+
chore/remove-legacy-paths (PR5 / v1.0.0) — removal
7+
8+
---
9+
10+
## Context
11+
12+
One legacy convention was released in `main` before ADR-0001:
13+
14+
**Flat `extensions/`** — top-level `.py` files under `extensions/` added to `sys.path`
15+
for spec-referenced Python.
16+
17+
A second path (`extensions/libraries/`) was only ever on feature branches and was
18+
**never released to `main`**, so it carries no backward-compat obligation and is
19+
dropped without a deprecation window.
20+
21+
The new canonical path (`src/python/`) was introduced in ADR-0001.
22+
23+
## Decision
24+
25+
| Legacy behaviour | Released? | Action in v0.13.0 | Removed in |
26+
|-----------------|-----------|-----------------|------------|
27+
| Flat `extensions/` on `sys.path` | Yes | Emit `DeprecationWarning` + structured log | **v1.0.0** |
28+
| `extensions/libraries/` on `sys.path` | No | Drop immediately, no deprecation window | **v0.13.0** (this PR) |
29+
30+
**One-minor deprecation window:** deprecated behaviour is removed in the first minor
31+
version after the deprecation warning lands — giving consumers one release cycle to
32+
migrate before the path disappears.
33+
34+
**Migration is mechanical:** move `.py` files from `extensions/` to `src/python/`.
35+
Spec `module` strings in Data Flow Specs are unchanged — both directories are on
36+
`sys.path` during the deprecation window. `extensions/` is registered on `sys.path`
37+
unconditionally when it contains `.py` files, regardless of whether `src/python/`
38+
also exists.
39+
40+
## Consequences
41+
42+
- Consumers on `extensions/` receive a `DeprecationWarning` at pipeline startup from
43+
`v0.13.0` onwards.
44+
- `extensions/` `sys.path` registration is removed in `v1.0.0`.
45+
- No deprecation warning is emitted for `extensions/libraries/` (never shipped).

docs/source/build_pipeline_bundle_steps.rst

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,13 @@ A new Pipeline Bundle can be created using the following methods.
5555
│ └── my_first_pipeline.yml
5656
├── scratch/
5757
├── src/
58-
│ ├── dataflows
59-
│ └── pipeline_configs
58+
│ ├── dataflows/
59+
│ ├── init/
60+
│ │ ├── pre/
61+
│ │ └── post/
62+
│ ├── libraries/
63+
│ ├── pipeline_configs/
64+
│ └── python/
6065
├── databricks.yml
6166
└── README.md
6267

@@ -78,8 +83,8 @@ A new Pipeline Bundle can be created using the following methods.
7883

7984
You can always copy an existing Pipeline Bundle to use as a starting point for a new Pipeline Bundle. If doing this bear in mind that you may need to:
8085

81-
- Reset the targets and parameter in the ``databricks.yml`` file
82-
- Clean out the following folders: ``resources``, ``src/dataflows`` and ``src/pipeline_configs``
86+
- Reset the targets and parameters in the ``databricks.yml`` file
87+
- Clear out the following folders: ``resources/``, ``src/dataflows/``, ``src/pipeline_configs/``, ``src/python/``, ``src/libraries/``, ``src/init/pre/``, and ``src/init/post/``
8388

8489
2. Update the ``databricks.yml`` File
8590
-------------------------------------
@@ -173,7 +178,21 @@ Iterate over the following steps to create each individual Data Flow:
173178

174179
If necessary add any substitutions required for your Data Flow to the substitutions file.
175180

176-
3. **Build the Data Flow Spec:**
181+
3. **Add Init Scripts** *(optional)*:
182+
183+
If your pipeline requires Spark configuration, event hook registration, or any one-time setup that must run outside of Data Flow logic, add ``.py`` scripts to:
184+
185+
- ``src/init/pre/`` — run **before** SDP dataflow declarations
186+
- ``src/init/post/`` — run **after** SDP dataflow declarations
187+
188+
Scripts are executed in sorted filename order. Files whose names begin with ``_`` are skipped. Use a numeric prefix (e.g. ``01_setup.py``) to control execution order.
189+
190+
Refer to the :doc:`feature_python_extensions` section for full details.
191+
192+
.. note::
193+
This step is optional and only required when pipeline-level lifecycle setup is needed.
194+
195+
4. **Build the Data Flow Spec:**
177196

178197
a. Create a sub-directory per you selected bundle structure:
179198

@@ -207,6 +226,17 @@ Iterate over the following steps to create each individual Data Flow:
207226

208227
Refer to the :doc:`feature_data_quality_expectations` section for guidance on how to create an expectations file.
209228

229+
f. **Add Pipeline Logic Modules** *(optional)*:
230+
231+
If your Data Flow Spec references custom Python code — e.g. ``pythonModule``, ``pythonTransform.module``, or a custom sink — add the corresponding ``.py`` modules or packages to ``src/python/``.
232+
233+
The framework adds ``src/python/`` to ``sys.path`` at pipeline initialisation so spec strings such as ``"module": "my_transforms"`` resolve without any extra configuration.
234+
235+
Refer to the :doc:`feature_python_extensions` section for details on flat vs. package layout options.
236+
237+
.. note::
238+
This step is optional and only required when your Data Flow Spec references a custom Python module.
239+
210240
7. Create your Pipeline Definitions
211241
-----------------------------------
212242

@@ -303,3 +333,14 @@ To create a single Pipeline definition, follow these steps:
303333
pipeline.dataFlowIdFilter: <value_flow_id>
304334
pipeline.flowGroupIdFilter: <value_flow_group_id>
305335
pipeline.fileFilter: <value_file_path>
336+
337+
4. **Add Cluster Libraries** *(optional)*:
338+
339+
If your pipeline requires third-party or in-house Python packages installed on the cluster, add them via ``environment.dependencies`` in your pipeline resource YAML. Refer to the :doc:`feature_python_extensions` section for full details. Sources include:
340+
341+
- **PyPI** — ``- my_package>=1.0``
342+
- **UC Volumes** — ``- /Volumes/catalog/schema/my_pkg.whl``
343+
- **Artifact repository** (Artifactory, Nexus) — ``- https://artifactory.example.com/my_pkg.whl``
344+
- **Bundle wheel** (wheel stored with the pipeline code) — ``- /Workspace/${workspace.file_path}/src/libraries/my_package.whl``
345+
346+
For the last case, place the ``.whl`` file in ``src/libraries/``. You may also place loose ``.py`` files or packages there if they need to be on ``sys.path`` without being spec-referenced (e.g. a shared utility imported indirectly).

docs/source/build_pipeline_bundle_structure.rst

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,55 @@ The high-level structure of a Pipeline Bundle never changes and is as follows:
6565
│ └── my_first_pipeline.yml
6666
├── scratch/
6767
├── src/
68-
│ ├── dataflows
69-
│ └── pipeline_configs
68+
│ ├── dataflows/ # Data Flow Spec files (required)
69+
│ ├── init/
70+
│ │ ├── pre/ # Lifecycle scripts — run before SDP declarations (optional)
71+
│ │ └── post/ # Lifecycle scripts — run after SDP declarations (optional)
72+
│ ├── libraries/ # Cluster-install artefacts + sys.path loose .py (optional)
73+
│ ├── pipeline_configs/ # Global and env-specific pipeline config (required)
74+
│ └── python/ # Spec-referenced Python modules and packages (optional)
7075
├── databricks.yml
7176
└── README.md
7277

7378
.. note::
7479

7580
Refer to the :doc:`concepts` section for more details on the different components of a Pipeline Bundle.
7681

82+
The ``src/`` directories serve distinct purposes:
83+
84+
.. list-table::
85+
:header-rows: 1
86+
:widths: 25 75
87+
88+
* - Directory
89+
- Purpose
90+
* - ``src/dataflows/``
91+
- All Data Flow Spec files. The framework reads every spec file recursively regardless
92+
of sub-folder structure. See below for organisation options.
93+
* - ``src/init/pre/`` and ``src/init/post/``
94+
- **Optional.** Lifecycle ``.py`` scripts executed before and after SDP declarations
95+
inside ``initialize_pipeline()``. Run in sorted filename order; files starting
96+
with ``_`` are skipped.
97+
* - ``src/libraries/``
98+
- **Optional.** Wheels bundled with the pipeline and referenced in the DAB
99+
``libraries:`` YAML, plus any loose ``.py`` modules that need to be on
100+
``sys.path`` without being spec-referenced. Libraries may equally be sourced from
101+
PyPI, UC Volumes, or artifact repositories — ``src/libraries/`` is only needed
102+
when the wheel travels with the bundle.
103+
* - ``src/pipeline_configs/``
104+
- Global and environment-specific pipeline configuration (``global.json``,
105+
substitutions, secrets).
106+
* - ``src/python/``
107+
- **Optional.** All customer Python referenced by Data Flow Specs —
108+
``pythonModule``, ``pythonTransform.module``, and custom sinks. Added to
109+
``sys.path`` at pipeline initialisation.
110+
111+
.. seealso::
112+
113+
:doc:`feature_python_extensions` — full reference for ``src/libraries/``,
114+
``src/python/``, ``src/init/``, and ``src/local/config/``, including examples,
115+
deprecation notices, and the cluster library installation options.
116+
77117
It is the structure of the ``src/dataflows`` directory that is flexible and can be organised in the way that best suits your standards and ways of working. The Framework will:
78118

79119
* Read all the Data Flow Spec files under the ``src/dataflows`` directory, regardless of the folder structure. Filtering of the Dataflows is done when defining your Pipeline and is discussed in the :doc:`build_pipeline_bundle_steps` section.

docs/source/feature_python_dependency_management.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@ Overview
1414

1515
The Lakeflow Framework provides flexible Python dependency management at two levels:
1616

17-
1. **Framework Level**: Global dependencies required by the framework or custom extensions (``requirements.txt``)
17+
1. **Framework Level**: Global dependencies required by the framework itself (``requirements.txt``)
1818
2. **Pipeline Bundle Level**: Bundle-specific dependencies configured via Databricks Asset Bundles
1919

2020
This separation allows the framework to maintain its core dependencies independently while enabling pipeline developers to add custom packages for their specific use cases.
2121

22+
.. note::
23+
24+
This page covers installing Python packages onto the cluster via the DAB **pipeline environment** (``pip install`` style). If you need to install a wheel that travels with your pipeline code or add loose ``.py`` files to ``sys.path``, see :doc:`feature_python_extensions` for the ``src/libraries/`` approach.
25+
2226
.. important::
2327

2428
Databricks recommends using the **pipeline environment settings** to manage Python dependencies.

0 commit comments

Comments
 (0)