tablevalidator-databricks 0.1.2__tar.gz → 0.1.3__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.
Files changed (21) hide show
  1. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/PKG-INFO +7 -7
  2. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/README.md +6 -6
  3. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks/templates/widget_notebook.py.tmpl +20 -20
  4. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks.egg-info/PKG-INFO +7 -7
  5. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks.egg-info/scm_version.json +2 -2
  6. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tests/test_generated_notebook_execution.py +65 -23
  7. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/pyproject.toml +0 -0
  8. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/setup.cfg +0 -0
  9. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks/__init__.py +0 -0
  10. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks/cli/__init__.py +0 -0
  11. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks/cli/main.py +0 -0
  12. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks/generator/__init__.py +0 -0
  13. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks/generator/generator.py +0 -0
  14. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks.egg-info/SOURCES.txt +0 -0
  15. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks.egg-info/dependency_links.txt +0 -0
  16. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks.egg-info/entry_points.txt +0 -0
  17. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks.egg-info/requires.txt +0 -0
  18. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks.egg-info/scm_file_list.json +0 -0
  19. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tablevalidator_databricks.egg-info/top_level.txt +0 -0
  20. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tests/test_cli.py +0 -0
  21. {tablevalidator_databricks-0.1.2 → tablevalidator_databricks-0.1.3}/tests/test_generator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tablevalidator-databricks
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Generates a widget-driven Databricks notebook UI on top of the table-validator package - no code required to run a comparison.
5
5
  License: MIT
6
6
  Requires-Python: >=3.9
@@ -45,12 +45,12 @@ Writes `./TableValidator.py` - a ready-to-use Databricks notebook. Options:
45
45
  tablevalidator-databricks init --output MyValidation.py --mode full
46
46
  ```
47
47
 
48
- - `--mode basic` - only the check-group multiselect and table pickers.
48
+ - `--mode basic` - only the check-group Yes/No dropdowns and table pickers.
49
49
  - `--mode full` (default) - also shows optional filter widgets (only/
50
- ignore columns, row filter, primary key).
51
- - `--mode schema` - same widgets as `basic`; intended for a lightweight
52
- schema-shape-only comparison (just deselect Column/Row in the checks
53
- multiselect after opening the notebook).
50
+ ignore columns, row filter, primary key). Every check defaults to "Yes".
51
+ - `--mode schema` - same widgets as `basic`, but Column/Row default to
52
+ "No" - a lightweight schema-shape-only comparison out of the box (still
53
+ changeable per-run).
54
54
 
55
55
  Then, in your Databricks workspace: **Workspace -> Import**, format
56
56
  "Source", and select the generated file. Databricks recognizes the
@@ -63,7 +63,7 @@ markers and opens it as a real notebook.
63
63
  2. Queries Unity Catalog (`SHOW CATALOGS` / `SHOW SCHEMAS IN ...` /
64
64
  `SHOW TABLES IN ...` via the notebook's own ambient Spark session) to
65
65
  populate Source/Target Catalog, Schema, and Table dropdowns.
66
- 3. Shows a "Validation Checks" multiselect (Catalog & Schema / Column /
66
+ 3. Shows a Yes/No dropdown per check group (Catalog & Schema / Column /
67
67
  Row - the three independently-selectable check groups the engine
68
68
  actually supports) plus, in `full` mode, optional text widgets for
69
69
  column filtering, a row filter, and a primary key.
@@ -31,12 +31,12 @@ Writes `./TableValidator.py` - a ready-to-use Databricks notebook. Options:
31
31
  tablevalidator-databricks init --output MyValidation.py --mode full
32
32
  ```
33
33
 
34
- - `--mode basic` - only the check-group multiselect and table pickers.
34
+ - `--mode basic` - only the check-group Yes/No dropdowns and table pickers.
35
35
  - `--mode full` (default) - also shows optional filter widgets (only/
36
- ignore columns, row filter, primary key).
37
- - `--mode schema` - same widgets as `basic`; intended for a lightweight
38
- schema-shape-only comparison (just deselect Column/Row in the checks
39
- multiselect after opening the notebook).
36
+ ignore columns, row filter, primary key). Every check defaults to "Yes".
37
+ - `--mode schema` - same widgets as `basic`, but Column/Row default to
38
+ "No" - a lightweight schema-shape-only comparison out of the box (still
39
+ changeable per-run).
40
40
 
41
41
  Then, in your Databricks workspace: **Workspace -> Import**, format
42
42
  "Source", and select the generated file. Databricks recognizes the
@@ -49,7 +49,7 @@ markers and opens it as a real notebook.
49
49
  2. Queries Unity Catalog (`SHOW CATALOGS` / `SHOW SCHEMAS IN ...` /
50
50
  `SHOW TABLES IN ...` via the notebook's own ambient Spark session) to
51
51
  populate Source/Target Catalog, Schema, and Table dropdowns.
52
- 3. Shows a "Validation Checks" multiselect (Catalog & Schema / Column /
52
+ 3. Shows a Yes/No dropdown per check group (Catalog & Schema / Column /
53
53
  Row - the three independently-selectable check groups the engine
54
54
  actually supports) plus, in `full` mode, optional text widgets for
55
55
  column filtering, a row filter, and a primary key.
@@ -180,18 +180,20 @@ print(
180
180
 
181
181
  # COMMAND ----------
182
182
 
183
- _ALL_CHECKS = ["Catalog & Schema", "Column", "Row"]
184
- # dbutils.widgets.multiselect's default value must itself be one of the
185
- # combinations Databricks is willing to accept - in practice this means
186
- # "all choices, comma-joined in the declared order" is the only default
187
- # guaranteed to work across Databricks runtime versions (a narrower
188
- # default, e.g. just "Catalog & Schema", was observed to fail with
189
- # "DefaultValueNotInChoicesList: Selection sequence must include ..." on
190
- # some runtimes). --mode schema still gets its narrower intent honored -
191
- # see the checks-selection cell below, which treats an EMPTY selection as
192
- # "run everything" and a real narrowing as an explicit choice; this
193
- # widget just always starts fully selected regardless of --mode.
194
- dbutils.widgets.multiselect("checks", ",".join(_ALL_CHECKS), _ALL_CHECKS, "Validation Checks")
183
+ # One Yes/No dropdown per check group, instead of a single multiselect -
184
+ # dbutils.widgets.multiselect enforces an internal "selection sequence"
185
+ # validation against its own default that was observed to reject even a
186
+ # same-length, same-items default on some Databricks runtimes/existing-
187
+ # widget states (DefaultValueNotInChoicesList), with no reliable
188
+ # workaround. Three independent dropdowns have no such cross-widget
189
+ # validation and are the more robust primitive for this.
190
+ _YES_NO = ["Yes", "No"]
191
+ _schema_default = "Yes"
192
+ _column_default = "No" if MODE == "schema" else "Yes"
193
+ _row_default = "No" if MODE == "schema" else "Yes"
194
+ dbutils.widgets.dropdown("check_catalog_schema", _schema_default, _YES_NO, "Run Catalog & Schema checks?")
195
+ dbutils.widgets.dropdown("check_column", _column_default, _YES_NO, "Run Column checks?")
196
+ dbutils.widgets.dropdown("check_row", _row_default, _YES_NO, "Run Row checks?")
195
197
 
196
198
  if SHOW_OPTIONAL_WIDGETS:
197
199
  dbutils.widgets.text("only_columns", "", "Only compare these columns (comma-separated, optional)")
@@ -279,15 +281,13 @@ def _source_or_target_string(prefix: str) -> str:
279
281
  source = _source_or_target_string("source")
280
282
  target = _source_or_target_string("target")
281
283
 
282
- _CHECK_MAP = {
283
- "Catalog & Schema": {ValidationType.CATALOG, ValidationType.SCHEMA},
284
- "Column": {ValidationType.COLUMN},
285
- "Row": {ValidationType.ROW},
286
- }
287
- selected_checks = [c for c in dbutils.widgets.get("checks").split(",") if c]
288
284
  enabled_validations = set()
289
- for check in selected_checks:
290
- enabled_validations |= _CHECK_MAP.get(check, set())
285
+ if dbutils.widgets.get("check_catalog_schema") == "Yes":
286
+ enabled_validations |= {ValidationType.CATALOG, ValidationType.SCHEMA}
287
+ if dbutils.widgets.get("check_column") == "Yes":
288
+ enabled_validations |= {ValidationType.COLUMN}
289
+ if dbutils.widgets.get("check_row") == "Yes":
290
+ enabled_validations |= {ValidationType.ROW}
291
291
  if not enabled_validations:
292
292
  enabled_validations = None # fall back to validate_tables()'s own "run everything" default
293
293
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tablevalidator-databricks
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Generates a widget-driven Databricks notebook UI on top of the table-validator package - no code required to run a comparison.
5
5
  License: MIT
6
6
  Requires-Python: >=3.9
@@ -45,12 +45,12 @@ Writes `./TableValidator.py` - a ready-to-use Databricks notebook. Options:
45
45
  tablevalidator-databricks init --output MyValidation.py --mode full
46
46
  ```
47
47
 
48
- - `--mode basic` - only the check-group multiselect and table pickers.
48
+ - `--mode basic` - only the check-group Yes/No dropdowns and table pickers.
49
49
  - `--mode full` (default) - also shows optional filter widgets (only/
50
- ignore columns, row filter, primary key).
51
- - `--mode schema` - same widgets as `basic`; intended for a lightweight
52
- schema-shape-only comparison (just deselect Column/Row in the checks
53
- multiselect after opening the notebook).
50
+ ignore columns, row filter, primary key). Every check defaults to "Yes".
51
+ - `--mode schema` - same widgets as `basic`, but Column/Row default to
52
+ "No" - a lightweight schema-shape-only comparison out of the box (still
53
+ changeable per-run).
54
54
 
55
55
  Then, in your Databricks workspace: **Workspace -> Import**, format
56
56
  "Source", and select the generated file. Databricks recognizes the
@@ -63,7 +63,7 @@ markers and opens it as a real notebook.
63
63
  2. Queries Unity Catalog (`SHOW CATALOGS` / `SHOW SCHEMAS IN ...` /
64
64
  `SHOW TABLES IN ...` via the notebook's own ambient Spark session) to
65
65
  populate Source/Target Catalog, Schema, and Table dropdowns.
66
- 3. Shows a "Validation Checks" multiselect (Catalog & Schema / Column /
66
+ 3. Shows a Yes/No dropdown per check group (Catalog & Schema / Column /
67
67
  Row - the three independently-selectable check groups the engine
68
68
  actually supports) plus, in `full` mode, optional text widgets for
69
69
  column filtering, a row filter, and a primary key.
@@ -1,7 +1,7 @@
1
1
  {
2
- "tag": "0.1.2",
2
+ "tag": "0.1.3",
3
3
  "distance": 0,
4
- "node": "gae96a50",
4
+ "node": "gd05d199",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-09-08"
@@ -15,6 +15,7 @@ from unittest.mock import MagicMock
15
15
 
16
16
  import pytest
17
17
 
18
+ from table_validator.config.schema import ValidationType
18
19
  from tablevalidator_databricks.generator.generator import generate_notebook
19
20
 
20
21
 
@@ -254,42 +255,83 @@ def test_catalog_override_drives_schema_and_table_lookup(notebook_path):
254
255
 
255
256
 
256
257
  # ---------------------------------------------------------------------------
257
- # --mode schema: regression test for a real production bug. The checks
258
- # multiselect's default used to be a narrower subset of its choices in
259
- # --mode schema (just "Catalog & Schema"), which Databricks rejected with
260
- # DefaultValueNotInChoicesList - _FakeWidgets.multiselect above replicates
261
- # that exact constraint.
258
+ # Validation-checks selection - three independent Yes/No dropdowns
259
+ # (check_catalog_schema / check_column / check_row), replacing an earlier
260
+ # single dbutils.widgets.multiselect that was observed in production to
261
+ # raise DefaultValueNotInChoicesList even with a default that appeared to
262
+ # satisfy multiselect's own documented constraints - three independent
263
+ # dropdowns have no equivalent cross-widget validation.
262
264
  # ---------------------------------------------------------------------------
263
- def test_schema_mode_multiselect_default_does_not_crash(tmp_path: Path) -> None:
264
- path = tmp_path / "TableValidator.py"
265
- generate_notebook(path, "schema")
265
+ def test_full_and_basic_mode_default_all_checks_to_yes(tmp_path: Path) -> None:
266
+ for mode in ("full", "basic"):
267
+ path = tmp_path / f"{mode}.py"
268
+ generate_notebook(path, mode)
269
+ spark = _fake_spark(
270
+ catalogs=["main"], schemas_by_catalog={"main": ["sales"]},
271
+ tables_by_schema={("main", "sales"): ["orders"]},
272
+ )
273
+ captured = {}
274
+
275
+ def fake_validate_tables(source, target, **kwargs):
276
+ captured["enabled_validations"] = kwargs.get("enabled_validations")
277
+ m = MagicMock()
278
+ m.__str__ = MagicMock(return_value="Overall status: PASS")
279
+ return m
266
280
 
281
+ _run_notebook(path, widget_values={}, spark=spark, validate_tables_mock=fake_validate_tables)
282
+
283
+ assert captured["enabled_validations"] == {
284
+ ValidationType.CATALOG, ValidationType.SCHEMA,
285
+ ValidationType.COLUMN, ValidationType.ROW,
286
+ }, f"mode={mode}"
287
+
288
+
289
+ def test_schema_mode_defaults_column_and_row_checks_to_no(tmp_path: Path) -> None:
290
+ path = tmp_path / "schema.py"
291
+ generate_notebook(path, "schema")
267
292
  spark = _fake_spark(
268
- catalogs=["main"],
269
- schemas_by_catalog={"main": ["sales"]},
293
+ catalogs=["main"], schemas_by_catalog={"main": ["sales"]},
270
294
  tables_by_schema={("main", "sales"): ["orders"]},
271
295
  )
296
+ captured = {}
297
+
298
+ def fake_validate_tables(source, target, **kwargs):
299
+ captured["enabled_validations"] = kwargs.get("enabled_validations")
300
+ m = MagicMock()
301
+ m.__str__ = MagicMock(return_value="Overall status: PASS")
302
+ return m
272
303
 
273
- # Must not raise _DefaultValueNotInChoicesList.
274
- _run_notebook(path, widget_values={}, spark=spark, validate_tables_mock=MagicMock())
304
+ _run_notebook(path, widget_values={}, spark=spark, validate_tables_mock=fake_validate_tables)
275
305
 
306
+ assert captured["enabled_validations"] == {ValidationType.CATALOG, ValidationType.SCHEMA}
276
307
 
277
- def test_multiselect_default_is_every_choice(notebook_path):
278
- """The checks widget's default must always be the full choices list,
279
- comma-joined in the declared order - the only default guaranteed to
280
- be accepted by dbutils.widgets.multiselect across Databricks runtime
281
- versions."""
308
+
309
+ def test_deselecting_every_check_falls_back_to_run_everything(notebook_path):
310
+ """All three Yes/No dropdowns set to "No" must fall back to
311
+ validate_tables()'s own "run everything" default (enabled_validations=
312
+ None), not silently run nothing."""
282
313
  spark = _fake_spark(
283
- catalogs=["main"],
284
- schemas_by_catalog={"main": ["sales"]},
314
+ catalogs=["main"], schemas_by_catalog={"main": ["sales"]},
285
315
  tables_by_schema={("main", "sales"): ["orders"]},
286
316
  )
317
+ captured = {}
318
+
319
+ def fake_validate_tables(source, target, **kwargs):
320
+ captured["enabled_validations"] = kwargs.get("enabled_validations")
321
+ m = MagicMock()
322
+ m.__str__ = MagicMock(return_value="Overall status: PASS")
323
+ return m
287
324
 
288
- globs = _run_notebook(notebook_path, widget_values={}, spark=spark, validate_tables_mock=MagicMock())
325
+ _run_notebook(
326
+ notebook_path,
327
+ widget_values={
328
+ "check_catalog_schema": "No", "check_column": "No", "check_row": "No",
329
+ },
330
+ spark=spark,
331
+ validate_tables_mock=fake_validate_tables,
332
+ )
289
333
 
290
- fake_widgets = globs["dbutils"].widgets
291
- choices = fake_widgets.multiselect_choices["checks"]
292
- assert fake_widgets.get("checks") == ",".join(choices)
334
+ assert captured["enabled_validations"] is None
293
335
 
294
336
 
295
337
  # ---------------------------------------------------------------------------