databuck-sdk 0.5.46__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- databuck_sdk-0.5.46/MANIFEST.in +3 -0
- databuck_sdk-0.5.46/PKG-INFO +354 -0
- databuck_sdk-0.5.46/README.md +335 -0
- databuck_sdk-0.5.46/RELEASING.md +46 -0
- databuck_sdk-0.5.46/databuck/__init__.py +8 -0
- databuck_sdk-0.5.46/databuck/__main__.py +7 -0
- databuck_sdk-0.5.46/databuck/agentic_rules.py +927 -0
- databuck_sdk-0.5.46/databuck/lake_rules.py +1036 -0
- databuck_sdk-0.5.46/databuck/sdk.py +1406 -0
- databuck_sdk-0.5.46/databuck_sdk.egg-info/PKG-INFO +354 -0
- databuck_sdk-0.5.46/databuck_sdk.egg-info/SOURCES.txt +19 -0
- databuck_sdk-0.5.46/databuck_sdk.egg-info/dependency_links.txt +1 -0
- databuck_sdk-0.5.46/databuck_sdk.egg-info/requires.txt +1 -0
- databuck_sdk-0.5.46/databuck_sdk.egg-info/top_level.txt +1 -0
- databuck_sdk-0.5.46/pyproject.toml +39 -0
- databuck_sdk-0.5.46/setup.cfg +4 -0
- databuck_sdk-0.5.46/tests/test_combined_export.py +623 -0
- databuck_sdk-0.5.46/tests/test_critical_selection.py +455 -0
- databuck_sdk-0.5.46/tests/test_jar_download.py +115 -0
- databuck_sdk-0.5.46/tests/test_reference_documents.py +614 -0
- databuck_sdk-0.5.46/tests/test_rule_export_extensions.py +113 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: databuck-sdk
|
|
3
|
+
Version: 0.5.46
|
|
4
|
+
Summary: DataBuck data quality SDK for PySpark and Databricks
|
|
5
|
+
Author: DataBuck
|
|
6
|
+
Project-URL: Repository, https://github.com/FirstEigen-Labs/databuck_sdk
|
|
7
|
+
Keywords: data quality,databricks,pyspark,data profiling
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Database
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: google-genai>=1.0.0
|
|
19
|
+
|
|
20
|
+
# databuck-sdk
|
|
21
|
+
|
|
22
|
+
Python wrapper for the DataBuck Spark SDK. It discovers data-quality rules from
|
|
23
|
+
an existing Spark DataFrame or a registered Spark table and exports expectations
|
|
24
|
+
that Databricks Lakeflow Declarative Pipelines can execute before downstream
|
|
25
|
+
data is published.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
python -m pip install databuck-sdk
|
|
31
|
+
python -c "import databuck"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
If `databuck-spark-sdk` is installed, remove it before installing this package;
|
|
35
|
+
both distributions provide the same `databuck` Python import:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
python -m pip uninstall databuck-spark-sdk
|
|
39
|
+
python -m pip install --upgrade databuck-sdk
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Importing `databuck` downloads `databuck-spark-sdk.jar` from the DataBuck S3
|
|
43
|
+
URL to `databuck/jars/` in the installed Python package, with progress output.
|
|
44
|
+
The downloaded path is set in `DATABUCK_SPARK_SDK_JAR` for the current Python
|
|
45
|
+
process. A later import reuses the existing JAR.
|
|
46
|
+
The JAR is not included in the wheel because it exceeds PyPI's
|
|
47
|
+
default per-file upload limit. An internet connection and write access to the
|
|
48
|
+
installed package directory are required for the first download.
|
|
49
|
+
|
|
50
|
+
`python -m databuck` is an explicit download command that also prints the
|
|
51
|
+
local path. Set `DATABUCK_SPARK_SDK_AUTO_DOWNLOAD=0` before import to defer the
|
|
52
|
+
download when offline. `DataBuck.jar_path()` returns the path after download.
|
|
53
|
+
Downloading the JAR does not install it as a Databricks compute library.
|
|
54
|
+
|
|
55
|
+
To use an existing JAR or another writable destination, set
|
|
56
|
+
`DATABUCK_SPARK_SDK_JAR` to that file path. If the download URL changes, set
|
|
57
|
+
`DATABUCK_SPARK_SDK_JAR_URL` to the new HTTPS URL before importing or running
|
|
58
|
+
`python -m databuck`. The download is checked against
|
|
59
|
+
the published JAR's SHA-256 hash. If the JAR content changes, publish a new
|
|
60
|
+
Python package version with its new hash, or set
|
|
61
|
+
`DATABUCK_SPARK_SDK_JAR_SHA256` to the expected hash.
|
|
62
|
+
|
|
63
|
+
### Databricks notebook setup
|
|
64
|
+
|
|
65
|
+
Run this once in the notebook to install the Python package:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
%pip install databuck-sdk
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
After installing or upgrading, restart Python in the next cell so the notebook
|
|
72
|
+
loads the new package version:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
dbutils.library.restartPython()
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Then verify the version and set a writable Unity Catalog Volume **file path**
|
|
79
|
+
before importing `databuck`:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import os
|
|
83
|
+
from importlib.metadata import version
|
|
84
|
+
|
|
85
|
+
print(version("databuck-sdk"))
|
|
86
|
+
|
|
87
|
+
os.environ["DATABUCK_SPARK_SDK_JAR"] = (
|
|
88
|
+
"/Volumes/catalog/schema/volume/databuck-spark-sdk.jar"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
from databuck import DataBuck # Downloads the JAR to that path on first import.
|
|
92
|
+
|
|
93
|
+
print(DataBuck.jar_path())
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Use the full filename ending in `.jar`, not just the Volume directory. Importing
|
|
97
|
+
`databuck` downloads or reuses the JAR at that path. Setting the variable after
|
|
98
|
+
the import is too late to choose the initial download location. A Volume keeps
|
|
99
|
+
the JAR available beyond the current notebook driver session.
|
|
100
|
+
|
|
101
|
+
Before calling `DataBuck.discover(...)`, attach the downloaded JAR to the
|
|
102
|
+
**compute that runs discovery**: in Databricks, open **Compute** > select that
|
|
103
|
+
compute > **Libraries** > **Install New**, select the Volume as the library
|
|
104
|
+
source, choose the JAR path printed above, and install it. If the compute is
|
|
105
|
+
already running, restart it or reattach the notebook so Spark loads the new
|
|
106
|
+
library. Then read your Spark DataFrame and run the discovery example below.
|
|
107
|
+
The Python import downloads the file; it does not attach the JAR to compute.
|
|
108
|
+
See [Databricks compute-scoped library instructions](https://docs.databricks.com/aws/en/libraries/cluster-libraries).
|
|
109
|
+
|
|
110
|
+
## Local PySpark usage
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from pyspark.sql import SparkSession
|
|
114
|
+
import os
|
|
115
|
+
|
|
116
|
+
from databuck import DataBuck
|
|
117
|
+
|
|
118
|
+
spark = (
|
|
119
|
+
SparkSession.builder
|
|
120
|
+
.config("spark.jars", DataBuck.jar_path())
|
|
121
|
+
.getOrCreate()
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
df = spark.read.csv("customers.csv", header=True)
|
|
125
|
+
|
|
126
|
+
print(DataBuck.count(df))
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Discover and export rules for Databricks
|
|
130
|
+
|
|
131
|
+
Run discovery in a Databricks notebook or Spark job against an existing DataFrame,
|
|
132
|
+
or pass a Spark table name and the SDK will read it into a DataFrame internally.
|
|
133
|
+
Pass PDF and DOCX policy files for context-aware BuckGPT rules.
|
|
134
|
+
The SDK exports generated rules as JSON (`.json`) or YAML (`.yaml` / `.yml`).
|
|
135
|
+
A Lakeflow Declarative Pipeline then loads and executes those rules as
|
|
136
|
+
expectations. Choose one of the two JSON inputs below; both use the same
|
|
137
|
+
discovery and export flow. The YAML workflow appears below.
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from databuck import DataBuck
|
|
141
|
+
|
|
142
|
+
context = {
|
|
143
|
+
"gemini_api_key": "YOUR_GEMINI_API_KEY",
|
|
144
|
+
"domain_name": "banking",
|
|
145
|
+
"pdf_paths": [
|
|
146
|
+
"/Volumes/catalog/schema/volume/customer_policy.pdf",
|
|
147
|
+
"/Volumes/catalog/schema/volume/data_contract.docx",
|
|
148
|
+
],
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# Option 1: use a DataFrame already available in your pipeline.
|
|
152
|
+
json_path = DataBuck.discover(
|
|
153
|
+
df, "/Volumes/catalog/schema/volume/expectations.json", context=context
|
|
154
|
+
)
|
|
155
|
+
print(json_path)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Or pass a registered Spark table name. The SDK calls `spark.read.table(...)`
|
|
159
|
+
internally, then passes that DataFrame through the same discovery and export:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
json_path = DataBuck.discover(
|
|
163
|
+
"catalog.schema.telco_customer_subscription",
|
|
164
|
+
"/Volumes/catalog/schema/volume/expectations.json",
|
|
165
|
+
context=context,
|
|
166
|
+
)
|
|
167
|
+
print(json_path)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Replace the example Volume paths and table name with files and a table available
|
|
171
|
+
in your workspace. The table-name option uses the notebook's Spark session and
|
|
172
|
+
requires read access to the registered table; it does not take separate database
|
|
173
|
+
connection details. Passing an existing DataFrame avoids reading the table again.
|
|
174
|
+
|
|
175
|
+
The call combines automatic and policy-informed rules. The output extension
|
|
176
|
+
selects JSON or YAML. With `domain_name`, JSON and YAML contain selected HIGH
|
|
177
|
+
and MEDIUM rules from both sources. The SDK writes `*_all_rules.csv` for review
|
|
178
|
+
and `*_high_medium_rules.csv` beside the output file for the selected rules.
|
|
179
|
+
The selected-rules CSV includes rule names, expressions, sources, and
|
|
180
|
+
priorities.
|
|
181
|
+
|
|
182
|
+
`pdf_paths` accepts PDF and DOCX files available to the notebook. For a
|
|
183
|
+
dataset-level uniqueness rule, the output includes a `dataset_checks` section.
|
|
184
|
+
Run its SQL against the full dataset separately, replacing `{{DATAFRAME}}` with
|
|
185
|
+
your source table or view. Dataset checks are not Lakeflow row expectations.
|
|
186
|
+
|
|
187
|
+
To export only automatic profiling rules, omit `context`:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
json_path = DataBuck.discover(
|
|
191
|
+
df, "/Volumes/catalog/schema/volume/expectations.json"
|
|
192
|
+
)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Without context, the output contains automatic rules and JSON places them in
|
|
196
|
+
the `warn` group. To include policy-informed rules, pass a business statement
|
|
197
|
+
or policy document along with the Gemini API key.
|
|
198
|
+
|
|
199
|
+
The JSON contains three action dictionaries: `warn`, `drop`, and `fail`.
|
|
200
|
+
In a Databricks Lakeflow Declarative Pipeline, use the dictionaries with
|
|
201
|
+
`dp.expect_all`, `dp.expect_all_or_drop`, and `dp.expect_all_or_fail`,
|
|
202
|
+
respectively.
|
|
203
|
+
|
|
204
|
+
For example, add this to the Lakeflow Declarative Pipeline source file. The
|
|
205
|
+
expectations run when the pipeline updates; the SDK's discovery call above
|
|
206
|
+
generates the file beforehand:
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
import json
|
|
210
|
+
from pyspark import pipelines as dp
|
|
211
|
+
|
|
212
|
+
with open("/Volumes/catalog/schema/volume/expectations.json", encoding="utf-8") as stream:
|
|
213
|
+
expectations = json.load(stream)
|
|
214
|
+
|
|
215
|
+
@dp.materialized_view(name="telco_customers_checked")
|
|
216
|
+
@dp.expect_all(expectations["warn"])
|
|
217
|
+
@dp.expect_all_or_drop(expectations["drop"])
|
|
218
|
+
@dp.expect_all_or_fail(expectations["fail"])
|
|
219
|
+
def customers_checked():
|
|
220
|
+
return spark.read.table("catalog.schema.telco_customer_subscription")
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### What the pipeline results look like
|
|
224
|
+
|
|
225
|
+
`DataBuck.discover(df, output_path, ...)` prints the rules and returns the path to the rules
|
|
226
|
+
file. The row results appear **after the Lakeflow Declarative Pipeline runs**,
|
|
227
|
+
in the validated table's **Expectations** view. For example, one telco demo run
|
|
228
|
+
showed:
|
|
229
|
+
|
|
230
|
+
| Pipeline result | Records |
|
|
231
|
+
| --- | ---: |
|
|
232
|
+
| Written | 58 (58%) |
|
|
233
|
+
| Dropped | 42 (42%) |
|
|
234
|
+
|
|
235
|
+
| Expectation | Action shown in Databricks | Failed records |
|
|
236
|
+
| --- | --- | ---: |
|
|
237
|
+
| `BuckGPT_Rule_006` | ALLOW | 60 |
|
|
238
|
+
| `BuckGPT_Rule_007` | DROP | 40 |
|
|
239
|
+
| `BuckGPT_Rule_009` | DROP | 20 |
|
|
240
|
+
| `valid_length_network_type` | DROP | 4 |
|
|
241
|
+
| `valid_pattern_network_type` | DROP | 4 |
|
|
242
|
+
|
|
243
|
+
`ALLOW` corresponds to `dp.expect_all`: the violation is recorded without
|
|
244
|
+
dropping the row. `DROP` corresponds to `dp.expect_all_or_drop`: invalid rows
|
|
245
|
+
are excluded from the validated output. A `FAIL` expectation stops the pipeline
|
|
246
|
+
update when violated. A row can fail more than one rule, so the per-rule failed
|
|
247
|
+
record counts do not add up to the dropped-row total. These numbers are from one
|
|
248
|
+
demo update; actual results depend on the source data and exported rules.
|
|
249
|
+
|
|
250
|
+
The pipeline must read a DataFrame with the columns used by the exported
|
|
251
|
+
expectations. Regenerate the rules file when the source schema or business
|
|
252
|
+
policy changes, then refresh the pipeline.
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"warn": {"not_null_customer_email": "`customer_email` IS NOT NULL"},
|
|
257
|
+
"drop": {"not_null_order_id": "`order_id` IS NOT NULL"},
|
|
258
|
+
"fail": {"valid_pattern_currency_code": "`currency_code` IS NULL OR CAST(`currency_code` AS STRING) RLIKE '(?:^[A-Z][A-Z][A-Z]$)'"}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The example shows the file shape; actual actions depend on the generated rules.
|
|
263
|
+
|
|
264
|
+
## Export flat YAML expectations
|
|
265
|
+
|
|
266
|
+
Pass a `.yaml` or `.yml` output file to write a flat YAML document. Pass either
|
|
267
|
+
an existing DataFrame or a table name. Choose one of these calls:
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
from databuck import DataBuck
|
|
271
|
+
|
|
272
|
+
context = {
|
|
273
|
+
"gemini_api_key": "YOUR_GEMINI_API_KEY",
|
|
274
|
+
"pdf_paths": [
|
|
275
|
+
"/Volumes/catalog/schema/volume/customer_policy.pdf",
|
|
276
|
+
"/Volumes/catalog/schema/volume/data_contract.docx",
|
|
277
|
+
],
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
# Option 1: use a DataFrame already available in your pipeline.
|
|
281
|
+
yaml_path = DataBuck.discover(
|
|
282
|
+
df,
|
|
283
|
+
"/Volumes/catalog/schema/volume/telco_expectations.yaml",
|
|
284
|
+
context=context,
|
|
285
|
+
)
|
|
286
|
+
print(yaml_path)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Or pass a registered Spark table name. The SDK reads it into a DataFrame
|
|
290
|
+
internally before discovering and exporting the rules:
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
yaml_path = DataBuck.discover(
|
|
294
|
+
"catalog.schema.telco_customer_subscription",
|
|
295
|
+
"/Volumes/catalog/schema/volume/telco_expectations.yaml",
|
|
296
|
+
context=context,
|
|
297
|
+
)
|
|
298
|
+
print(yaml_path)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
No separate `table_name=` argument is needed. When you pass a table name, YAML
|
|
302
|
+
includes a `table:` field. When you pass a DataFrame, YAML contains
|
|
303
|
+
`expectations:` without a `table:` field because the original table name is
|
|
304
|
+
not reliably available from a DataFrame.
|
|
305
|
+
|
|
306
|
+
For example, the generated file has this shape (the names and SQL depend on the
|
|
307
|
+
discovered rules):
|
|
308
|
+
|
|
309
|
+
```yaml
|
|
310
|
+
table: catalog.schema.telco_customer_subscription
|
|
311
|
+
|
|
312
|
+
expectations:
|
|
313
|
+
not_null_subscriber_id: "`subscriber_id` IS NOT NULL"
|
|
314
|
+
BuckGPT_Rule_001: "NOT COALESCE((subscription_status_cd = 'INVALID'), FALSE)"
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
YAML combines automatic and policy-informed rules under `expectations` without
|
|
318
|
+
`warn`, `drop`, or `fail` groups. It does not call the action classifier. Use
|
|
319
|
+
JSON when the pipeline needs those action groups.
|
|
320
|
+
Omit `context` to write only automatically discovered rules.
|
|
321
|
+
|
|
322
|
+
To run the flat YAML expectations in a Lakeflow Declarative Pipeline, add
|
|
323
|
+
`PyYAML` to the pipeline's Python dependencies and load the `expectations`
|
|
324
|
+
mapping:
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
import yaml
|
|
328
|
+
from pyspark import pipelines as dp
|
|
329
|
+
|
|
330
|
+
with open("/Volumes/catalog/schema/volume/telco_expectations.yaml", encoding="utf-8") as stream:
|
|
331
|
+
rules_doc = yaml.safe_load(stream)
|
|
332
|
+
|
|
333
|
+
source_table = rules_doc.get("table") or "catalog.schema.telco_customer_subscription"
|
|
334
|
+
|
|
335
|
+
@dp.materialized_view(name="telco_customers_checked")
|
|
336
|
+
@dp.expect_all(rules_doc["expectations"])
|
|
337
|
+
def telco_customers_checked():
|
|
338
|
+
return spark.read.table(source_table)
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
If you discovered from a DataFrame, set the fallback `source_table` to the
|
|
342
|
+
table or transformation that produces the same columns and rows.
|
|
343
|
+
|
|
344
|
+
In this example, YAML rules are monitored with `dp.expect_all`: invalid rows
|
|
345
|
+
remain in the output and their failures are recorded. Use the action-grouped
|
|
346
|
+
JSON format if rules must drop rows or fail the pipeline update.
|
|
347
|
+
|
|
348
|
+
`DataBuck.discover_rules(df)` still returns automatic profiling rules without
|
|
349
|
+
exporting. For compatibility, `DataBuck.discover(df, context_dict)` still
|
|
350
|
+
returns BuckGPT rules without exporting. Pass an output file path as the second
|
|
351
|
+
argument to `DataBuck.discover(...)` to combine discovery and export.
|
|
352
|
+
|
|
353
|
+
In JSON, each action dictionary maps expectation names to Spark SQL pass
|
|
354
|
+
conditions, such as `{"not_null_subscriber_id": "`subscriber_id` IS NOT NULL"}`.
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# databuck-sdk
|
|
2
|
+
|
|
3
|
+
Python wrapper for the DataBuck Spark SDK. It discovers data-quality rules from
|
|
4
|
+
an existing Spark DataFrame or a registered Spark table and exports expectations
|
|
5
|
+
that Databricks Lakeflow Declarative Pipelines can execute before downstream
|
|
6
|
+
data is published.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
python -m pip install databuck-sdk
|
|
12
|
+
python -c "import databuck"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If `databuck-spark-sdk` is installed, remove it before installing this package;
|
|
16
|
+
both distributions provide the same `databuck` Python import:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
python -m pip uninstall databuck-spark-sdk
|
|
20
|
+
python -m pip install --upgrade databuck-sdk
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Importing `databuck` downloads `databuck-spark-sdk.jar` from the DataBuck S3
|
|
24
|
+
URL to `databuck/jars/` in the installed Python package, with progress output.
|
|
25
|
+
The downloaded path is set in `DATABUCK_SPARK_SDK_JAR` for the current Python
|
|
26
|
+
process. A later import reuses the existing JAR.
|
|
27
|
+
The JAR is not included in the wheel because it exceeds PyPI's
|
|
28
|
+
default per-file upload limit. An internet connection and write access to the
|
|
29
|
+
installed package directory are required for the first download.
|
|
30
|
+
|
|
31
|
+
`python -m databuck` is an explicit download command that also prints the
|
|
32
|
+
local path. Set `DATABUCK_SPARK_SDK_AUTO_DOWNLOAD=0` before import to defer the
|
|
33
|
+
download when offline. `DataBuck.jar_path()` returns the path after download.
|
|
34
|
+
Downloading the JAR does not install it as a Databricks compute library.
|
|
35
|
+
|
|
36
|
+
To use an existing JAR or another writable destination, set
|
|
37
|
+
`DATABUCK_SPARK_SDK_JAR` to that file path. If the download URL changes, set
|
|
38
|
+
`DATABUCK_SPARK_SDK_JAR_URL` to the new HTTPS URL before importing or running
|
|
39
|
+
`python -m databuck`. The download is checked against
|
|
40
|
+
the published JAR's SHA-256 hash. If the JAR content changes, publish a new
|
|
41
|
+
Python package version with its new hash, or set
|
|
42
|
+
`DATABUCK_SPARK_SDK_JAR_SHA256` to the expected hash.
|
|
43
|
+
|
|
44
|
+
### Databricks notebook setup
|
|
45
|
+
|
|
46
|
+
Run this once in the notebook to install the Python package:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
%pip install databuck-sdk
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
After installing or upgrading, restart Python in the next cell so the notebook
|
|
53
|
+
loads the new package version:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
dbutils.library.restartPython()
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Then verify the version and set a writable Unity Catalog Volume **file path**
|
|
60
|
+
before importing `databuck`:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import os
|
|
64
|
+
from importlib.metadata import version
|
|
65
|
+
|
|
66
|
+
print(version("databuck-sdk"))
|
|
67
|
+
|
|
68
|
+
os.environ["DATABUCK_SPARK_SDK_JAR"] = (
|
|
69
|
+
"/Volumes/catalog/schema/volume/databuck-spark-sdk.jar"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
from databuck import DataBuck # Downloads the JAR to that path on first import.
|
|
73
|
+
|
|
74
|
+
print(DataBuck.jar_path())
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Use the full filename ending in `.jar`, not just the Volume directory. Importing
|
|
78
|
+
`databuck` downloads or reuses the JAR at that path. Setting the variable after
|
|
79
|
+
the import is too late to choose the initial download location. A Volume keeps
|
|
80
|
+
the JAR available beyond the current notebook driver session.
|
|
81
|
+
|
|
82
|
+
Before calling `DataBuck.discover(...)`, attach the downloaded JAR to the
|
|
83
|
+
**compute that runs discovery**: in Databricks, open **Compute** > select that
|
|
84
|
+
compute > **Libraries** > **Install New**, select the Volume as the library
|
|
85
|
+
source, choose the JAR path printed above, and install it. If the compute is
|
|
86
|
+
already running, restart it or reattach the notebook so Spark loads the new
|
|
87
|
+
library. Then read your Spark DataFrame and run the discovery example below.
|
|
88
|
+
The Python import downloads the file; it does not attach the JAR to compute.
|
|
89
|
+
See [Databricks compute-scoped library instructions](https://docs.databricks.com/aws/en/libraries/cluster-libraries).
|
|
90
|
+
|
|
91
|
+
## Local PySpark usage
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from pyspark.sql import SparkSession
|
|
95
|
+
import os
|
|
96
|
+
|
|
97
|
+
from databuck import DataBuck
|
|
98
|
+
|
|
99
|
+
spark = (
|
|
100
|
+
SparkSession.builder
|
|
101
|
+
.config("spark.jars", DataBuck.jar_path())
|
|
102
|
+
.getOrCreate()
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
df = spark.read.csv("customers.csv", header=True)
|
|
106
|
+
|
|
107
|
+
print(DataBuck.count(df))
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Discover and export rules for Databricks
|
|
111
|
+
|
|
112
|
+
Run discovery in a Databricks notebook or Spark job against an existing DataFrame,
|
|
113
|
+
or pass a Spark table name and the SDK will read it into a DataFrame internally.
|
|
114
|
+
Pass PDF and DOCX policy files for context-aware BuckGPT rules.
|
|
115
|
+
The SDK exports generated rules as JSON (`.json`) or YAML (`.yaml` / `.yml`).
|
|
116
|
+
A Lakeflow Declarative Pipeline then loads and executes those rules as
|
|
117
|
+
expectations. Choose one of the two JSON inputs below; both use the same
|
|
118
|
+
discovery and export flow. The YAML workflow appears below.
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from databuck import DataBuck
|
|
122
|
+
|
|
123
|
+
context = {
|
|
124
|
+
"gemini_api_key": "YOUR_GEMINI_API_KEY",
|
|
125
|
+
"domain_name": "banking",
|
|
126
|
+
"pdf_paths": [
|
|
127
|
+
"/Volumes/catalog/schema/volume/customer_policy.pdf",
|
|
128
|
+
"/Volumes/catalog/schema/volume/data_contract.docx",
|
|
129
|
+
],
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# Option 1: use a DataFrame already available in your pipeline.
|
|
133
|
+
json_path = DataBuck.discover(
|
|
134
|
+
df, "/Volumes/catalog/schema/volume/expectations.json", context=context
|
|
135
|
+
)
|
|
136
|
+
print(json_path)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Or pass a registered Spark table name. The SDK calls `spark.read.table(...)`
|
|
140
|
+
internally, then passes that DataFrame through the same discovery and export:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
json_path = DataBuck.discover(
|
|
144
|
+
"catalog.schema.telco_customer_subscription",
|
|
145
|
+
"/Volumes/catalog/schema/volume/expectations.json",
|
|
146
|
+
context=context,
|
|
147
|
+
)
|
|
148
|
+
print(json_path)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Replace the example Volume paths and table name with files and a table available
|
|
152
|
+
in your workspace. The table-name option uses the notebook's Spark session and
|
|
153
|
+
requires read access to the registered table; it does not take separate database
|
|
154
|
+
connection details. Passing an existing DataFrame avoids reading the table again.
|
|
155
|
+
|
|
156
|
+
The call combines automatic and policy-informed rules. The output extension
|
|
157
|
+
selects JSON or YAML. With `domain_name`, JSON and YAML contain selected HIGH
|
|
158
|
+
and MEDIUM rules from both sources. The SDK writes `*_all_rules.csv` for review
|
|
159
|
+
and `*_high_medium_rules.csv` beside the output file for the selected rules.
|
|
160
|
+
The selected-rules CSV includes rule names, expressions, sources, and
|
|
161
|
+
priorities.
|
|
162
|
+
|
|
163
|
+
`pdf_paths` accepts PDF and DOCX files available to the notebook. For a
|
|
164
|
+
dataset-level uniqueness rule, the output includes a `dataset_checks` section.
|
|
165
|
+
Run its SQL against the full dataset separately, replacing `{{DATAFRAME}}` with
|
|
166
|
+
your source table or view. Dataset checks are not Lakeflow row expectations.
|
|
167
|
+
|
|
168
|
+
To export only automatic profiling rules, omit `context`:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
json_path = DataBuck.discover(
|
|
172
|
+
df, "/Volumes/catalog/schema/volume/expectations.json"
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Without context, the output contains automatic rules and JSON places them in
|
|
177
|
+
the `warn` group. To include policy-informed rules, pass a business statement
|
|
178
|
+
or policy document along with the Gemini API key.
|
|
179
|
+
|
|
180
|
+
The JSON contains three action dictionaries: `warn`, `drop`, and `fail`.
|
|
181
|
+
In a Databricks Lakeflow Declarative Pipeline, use the dictionaries with
|
|
182
|
+
`dp.expect_all`, `dp.expect_all_or_drop`, and `dp.expect_all_or_fail`,
|
|
183
|
+
respectively.
|
|
184
|
+
|
|
185
|
+
For example, add this to the Lakeflow Declarative Pipeline source file. The
|
|
186
|
+
expectations run when the pipeline updates; the SDK's discovery call above
|
|
187
|
+
generates the file beforehand:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
import json
|
|
191
|
+
from pyspark import pipelines as dp
|
|
192
|
+
|
|
193
|
+
with open("/Volumes/catalog/schema/volume/expectations.json", encoding="utf-8") as stream:
|
|
194
|
+
expectations = json.load(stream)
|
|
195
|
+
|
|
196
|
+
@dp.materialized_view(name="telco_customers_checked")
|
|
197
|
+
@dp.expect_all(expectations["warn"])
|
|
198
|
+
@dp.expect_all_or_drop(expectations["drop"])
|
|
199
|
+
@dp.expect_all_or_fail(expectations["fail"])
|
|
200
|
+
def customers_checked():
|
|
201
|
+
return spark.read.table("catalog.schema.telco_customer_subscription")
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### What the pipeline results look like
|
|
205
|
+
|
|
206
|
+
`DataBuck.discover(df, output_path, ...)` prints the rules and returns the path to the rules
|
|
207
|
+
file. The row results appear **after the Lakeflow Declarative Pipeline runs**,
|
|
208
|
+
in the validated table's **Expectations** view. For example, one telco demo run
|
|
209
|
+
showed:
|
|
210
|
+
|
|
211
|
+
| Pipeline result | Records |
|
|
212
|
+
| --- | ---: |
|
|
213
|
+
| Written | 58 (58%) |
|
|
214
|
+
| Dropped | 42 (42%) |
|
|
215
|
+
|
|
216
|
+
| Expectation | Action shown in Databricks | Failed records |
|
|
217
|
+
| --- | --- | ---: |
|
|
218
|
+
| `BuckGPT_Rule_006` | ALLOW | 60 |
|
|
219
|
+
| `BuckGPT_Rule_007` | DROP | 40 |
|
|
220
|
+
| `BuckGPT_Rule_009` | DROP | 20 |
|
|
221
|
+
| `valid_length_network_type` | DROP | 4 |
|
|
222
|
+
| `valid_pattern_network_type` | DROP | 4 |
|
|
223
|
+
|
|
224
|
+
`ALLOW` corresponds to `dp.expect_all`: the violation is recorded without
|
|
225
|
+
dropping the row. `DROP` corresponds to `dp.expect_all_or_drop`: invalid rows
|
|
226
|
+
are excluded from the validated output. A `FAIL` expectation stops the pipeline
|
|
227
|
+
update when violated. A row can fail more than one rule, so the per-rule failed
|
|
228
|
+
record counts do not add up to the dropped-row total. These numbers are from one
|
|
229
|
+
demo update; actual results depend on the source data and exported rules.
|
|
230
|
+
|
|
231
|
+
The pipeline must read a DataFrame with the columns used by the exported
|
|
232
|
+
expectations. Regenerate the rules file when the source schema or business
|
|
233
|
+
policy changes, then refresh the pipeline.
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"warn": {"not_null_customer_email": "`customer_email` IS NOT NULL"},
|
|
238
|
+
"drop": {"not_null_order_id": "`order_id` IS NOT NULL"},
|
|
239
|
+
"fail": {"valid_pattern_currency_code": "`currency_code` IS NULL OR CAST(`currency_code` AS STRING) RLIKE '(?:^[A-Z][A-Z][A-Z]$)'"}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The example shows the file shape; actual actions depend on the generated rules.
|
|
244
|
+
|
|
245
|
+
## Export flat YAML expectations
|
|
246
|
+
|
|
247
|
+
Pass a `.yaml` or `.yml` output file to write a flat YAML document. Pass either
|
|
248
|
+
an existing DataFrame or a table name. Choose one of these calls:
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
from databuck import DataBuck
|
|
252
|
+
|
|
253
|
+
context = {
|
|
254
|
+
"gemini_api_key": "YOUR_GEMINI_API_KEY",
|
|
255
|
+
"pdf_paths": [
|
|
256
|
+
"/Volumes/catalog/schema/volume/customer_policy.pdf",
|
|
257
|
+
"/Volumes/catalog/schema/volume/data_contract.docx",
|
|
258
|
+
],
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
# Option 1: use a DataFrame already available in your pipeline.
|
|
262
|
+
yaml_path = DataBuck.discover(
|
|
263
|
+
df,
|
|
264
|
+
"/Volumes/catalog/schema/volume/telco_expectations.yaml",
|
|
265
|
+
context=context,
|
|
266
|
+
)
|
|
267
|
+
print(yaml_path)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Or pass a registered Spark table name. The SDK reads it into a DataFrame
|
|
271
|
+
internally before discovering and exporting the rules:
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
yaml_path = DataBuck.discover(
|
|
275
|
+
"catalog.schema.telco_customer_subscription",
|
|
276
|
+
"/Volumes/catalog/schema/volume/telco_expectations.yaml",
|
|
277
|
+
context=context,
|
|
278
|
+
)
|
|
279
|
+
print(yaml_path)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
No separate `table_name=` argument is needed. When you pass a table name, YAML
|
|
283
|
+
includes a `table:` field. When you pass a DataFrame, YAML contains
|
|
284
|
+
`expectations:` without a `table:` field because the original table name is
|
|
285
|
+
not reliably available from a DataFrame.
|
|
286
|
+
|
|
287
|
+
For example, the generated file has this shape (the names and SQL depend on the
|
|
288
|
+
discovered rules):
|
|
289
|
+
|
|
290
|
+
```yaml
|
|
291
|
+
table: catalog.schema.telco_customer_subscription
|
|
292
|
+
|
|
293
|
+
expectations:
|
|
294
|
+
not_null_subscriber_id: "`subscriber_id` IS NOT NULL"
|
|
295
|
+
BuckGPT_Rule_001: "NOT COALESCE((subscription_status_cd = 'INVALID'), FALSE)"
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
YAML combines automatic and policy-informed rules under `expectations` without
|
|
299
|
+
`warn`, `drop`, or `fail` groups. It does not call the action classifier. Use
|
|
300
|
+
JSON when the pipeline needs those action groups.
|
|
301
|
+
Omit `context` to write only automatically discovered rules.
|
|
302
|
+
|
|
303
|
+
To run the flat YAML expectations in a Lakeflow Declarative Pipeline, add
|
|
304
|
+
`PyYAML` to the pipeline's Python dependencies and load the `expectations`
|
|
305
|
+
mapping:
|
|
306
|
+
|
|
307
|
+
```python
|
|
308
|
+
import yaml
|
|
309
|
+
from pyspark import pipelines as dp
|
|
310
|
+
|
|
311
|
+
with open("/Volumes/catalog/schema/volume/telco_expectations.yaml", encoding="utf-8") as stream:
|
|
312
|
+
rules_doc = yaml.safe_load(stream)
|
|
313
|
+
|
|
314
|
+
source_table = rules_doc.get("table") or "catalog.schema.telco_customer_subscription"
|
|
315
|
+
|
|
316
|
+
@dp.materialized_view(name="telco_customers_checked")
|
|
317
|
+
@dp.expect_all(rules_doc["expectations"])
|
|
318
|
+
def telco_customers_checked():
|
|
319
|
+
return spark.read.table(source_table)
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
If you discovered from a DataFrame, set the fallback `source_table` to the
|
|
323
|
+
table or transformation that produces the same columns and rows.
|
|
324
|
+
|
|
325
|
+
In this example, YAML rules are monitored with `dp.expect_all`: invalid rows
|
|
326
|
+
remain in the output and their failures are recorded. Use the action-grouped
|
|
327
|
+
JSON format if rules must drop rows or fail the pipeline update.
|
|
328
|
+
|
|
329
|
+
`DataBuck.discover_rules(df)` still returns automatic profiling rules without
|
|
330
|
+
exporting. For compatibility, `DataBuck.discover(df, context_dict)` still
|
|
331
|
+
returns BuckGPT rules without exporting. Pass an output file path as the second
|
|
332
|
+
argument to `DataBuck.discover(...)` to combine discovery and export.
|
|
333
|
+
|
|
334
|
+
In JSON, each action dictionary maps expectation names to Spark SQL pass
|
|
335
|
+
conditions, such as `{"not_null_subscriber_id": "`subscriber_id` IS NOT NULL"}`.
|