table-validator 0.1.8__tar.gz → 0.1.10__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 (51) hide show
  1. {table_validator-0.1.8/table_validator.egg-info → table_validator-0.1.10}/PKG-INFO +8 -5
  2. {table_validator-0.1.8 → table_validator-0.1.10}/README.md +6 -4
  3. {table_validator-0.1.8 → table_validator-0.1.10}/pyproject.toml +1 -0
  4. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/auth/azure_auth.py +22 -5
  5. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/auth/databricks_auth.py +15 -0
  6. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/cli/main.py +158 -12
  7. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/cli/wizard.py +344 -12
  8. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/config/schema.py +114 -1
  9. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/connectors/azure_connector.py +97 -15
  10. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/connectors/databricks_connector.py +121 -34
  11. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/models.py +44 -0
  12. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/reports/excel_report.py +448 -11
  13. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/validators/catalog_validator.py +418 -117
  14. table_validator-0.1.10/table_validator/validators/mismatch_classifier.py +221 -0
  15. {table_validator-0.1.8 → table_validator-0.1.10/table_validator.egg-info}/PKG-INFO +8 -5
  16. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator.egg-info/SOURCES.txt +2 -0
  17. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator.egg-info/requires.txt +1 -0
  18. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator.egg-info/scm_file_list.json +2 -0
  19. table_validator-0.1.10/table_validator.egg-info/scm_version.json +8 -0
  20. {table_validator-0.1.8 → table_validator-0.1.10}/tests/test_catalog_validator.py +695 -7
  21. {table_validator-0.1.8 → table_validator-0.1.10}/tests/test_cli.py +349 -4
  22. {table_validator-0.1.8 → table_validator-0.1.10}/tests/test_databricks_connector.py +187 -0
  23. table_validator-0.1.10/tests/test_excel_report.py +1158 -0
  24. table_validator-0.1.10/tests/test_mismatch_classifier.py +170 -0
  25. {table_validator-0.1.8 → table_validator-0.1.10}/tests/test_row_validator.py +88 -0
  26. {table_validator-0.1.8 → table_validator-0.1.10}/tests/test_wizard.py +520 -2
  27. table_validator-0.1.8/table_validator.egg-info/scm_version.json +0 -8
  28. table_validator-0.1.8/tests/test_excel_report.py +0 -417
  29. {table_validator-0.1.8 → table_validator-0.1.10}/LICENSE +0 -0
  30. {table_validator-0.1.8 → table_validator-0.1.10}/setup.cfg +0 -0
  31. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/__init__.py +0 -0
  32. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/auth/__init__.py +0 -0
  33. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/cli/__init__.py +0 -0
  34. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/cli/partition_prompt.py +0 -0
  35. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/cli/summary_table.py +0 -0
  36. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/config/__init__.py +0 -0
  37. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/config/manager.py +0 -0
  38. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/connectors/__init__.py +0 -0
  39. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/engine/__init__.py +0 -0
  40. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/engine/comparison_engine.py +0 -0
  41. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/reports/__init__.py +0 -0
  42. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/validators/__init__.py +0 -0
  43. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/validators/blob_discovery.py +0 -0
  44. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator/validators/row_validator.py +0 -0
  45. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator.egg-info/dependency_links.txt +0 -0
  46. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator.egg-info/entry_points.txt +0 -0
  47. {table_validator-0.1.8 → table_validator-0.1.10}/table_validator.egg-info/top_level.txt +0 -0
  48. {table_validator-0.1.8 → table_validator-0.1.10}/tests/__init__.py +0 -0
  49. {table_validator-0.1.8 → table_validator-0.1.10}/tests/test_blob_discovery.py +0 -0
  50. {table_validator-0.1.8 → table_validator-0.1.10}/tests/test_partition_prompt.py +0 -0
  51. {table_validator-0.1.8 → table_validator-0.1.10}/tests/test_report_command.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: table-validator
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: CLI tool for validating data migrations between Azure (Blob Storage / SQL Database) and Databricks Delta Lake catalogs
5
5
  License: MIT
6
6
  Requires-Python: >=3.9
@@ -15,6 +15,7 @@ Requires-Dist: databricks-sql-connector>=3.0
15
15
  Requires-Dist: pandas>=2.0
16
16
  Requires-Dist: numpy>=1.24
17
17
  Requires-Dist: azure-storage-blob>=12.14
18
+ Requires-Dist: azure-identity>=1.15
18
19
  Requires-Dist: openpyxl>=3.1
19
20
  Requires-Dist: pyarrow>=14.0
20
21
  Requires-Dist: PyYAML>=6.0
@@ -38,6 +39,7 @@ Currently supported sources (target is always Databricks):
38
39
  - Databricks catalog → Databricks catalog
39
40
  - Azure Blob Storage (CSV / Excel / Parquet) → Databricks table
40
41
  - Azure SQL Database → Databricks catalog
42
+ - Azure Synapse SQL pool (dedicated or serverless) → Databricks catalog
41
43
 
42
44
  More source platforms can be added behind the same connector/validator
43
45
  interfaces described below.
@@ -80,10 +82,11 @@ Interactive wizard that walks you through:
80
82
 
81
83
  1. Azure Storage account + container (optional, skip if you don't have a Blob source) and account key
82
84
  2. Azure SQL server + database (optional, skip if you don't have a SQL source) and username/password
83
- 3. Databricks workspace URL, SQL Warehouse HTTP path, and personal access token
84
- 4. Source table (catalog / schema / table)
85
- 5. Target table (catalog / schema / table)
86
- 6. Which validations to run (catalog / schema / column / row)
85
+ 3. Azure Synapse SQL pool endpoint + database (optional, skip if you don't have a Synapse source), then either a SQL login (username/password) or a Microsoft Entra ID service principal (tenant ID + client ID + client secret)
86
+ 4. Databricks workspace URL, SQL Warehouse HTTP path, and personal access token
87
+ 5. Source table (catalog / schema / table)
88
+ 6. Target table (catalog / schema / table)
89
+ 7. Which validations to run (catalog / schema / column / row)
87
90
 
88
91
  ```bash
89
92
  tablevalidator validate
@@ -11,6 +11,7 @@ Currently supported sources (target is always Databricks):
11
11
  - Databricks catalog → Databricks catalog
12
12
  - Azure Blob Storage (CSV / Excel / Parquet) → Databricks table
13
13
  - Azure SQL Database → Databricks catalog
14
+ - Azure Synapse SQL pool (dedicated or serverless) → Databricks catalog
14
15
 
15
16
  More source platforms can be added behind the same connector/validator
16
17
  interfaces described below.
@@ -53,10 +54,11 @@ Interactive wizard that walks you through:
53
54
 
54
55
  1. Azure Storage account + container (optional, skip if you don't have a Blob source) and account key
55
56
  2. Azure SQL server + database (optional, skip if you don't have a SQL source) and username/password
56
- 3. Databricks workspace URL, SQL Warehouse HTTP path, and personal access token
57
- 4. Source table (catalog / schema / table)
58
- 5. Target table (catalog / schema / table)
59
- 6. Which validations to run (catalog / schema / column / row)
57
+ 3. Azure Synapse SQL pool endpoint + database (optional, skip if you don't have a Synapse source), then either a SQL login (username/password) or a Microsoft Entra ID service principal (tenant ID + client ID + client secret)
58
+ 4. Databricks workspace URL, SQL Warehouse HTTP path, and personal access token
59
+ 5. Source table (catalog / schema / table)
60
+ 6. Target table (catalog / schema / table)
61
+ 7. Which validations to run (catalog / schema / column / row)
60
62
 
61
63
  ```bash
62
64
  tablevalidator validate
@@ -19,6 +19,7 @@ dependencies = [
19
19
  "pandas>=2.0",
20
20
  "numpy>=1.24",
21
21
  "azure-storage-blob>=12.14",
22
+ "azure-identity>=1.15",
22
23
  "openpyxl>=3.1",
23
24
  "pyarrow>=14.0",
24
25
  "PyYAML>=6.0",
@@ -26,22 +26,36 @@ class AzureCredential:
26
26
 
27
27
  storage_account_key authenticates AzureConnector (Blob Storage);
28
28
  sql_username/sql_password authenticate AzureSqlConnector (Azure SQL
29
- Database). Both are optional here since a given validation run may
30
- only need one of the two.
29
+ Database); synapse_username/synapse_password authenticate the same
30
+ AzureSqlConnector class when pointed at a Synapse SQL pool instead
31
+ (Synapse SQL is protocol-identical T-SQL/ODBC, so no separate
32
+ connector class exists - only a separate credential pair, since a
33
+ Synapse SQL login is a distinct principal from an Azure SQL DB one).
34
+ synapse_client_secret is the alternative to that pair: the Entra ID
35
+ service-principal secret used when config.azure.synapse_auth_mode is
36
+ 'entra_service_principal', paired with the non-secret tenant_id /
37
+ synapse_client_id that live in config.yaml.
38
+ All fields are optional here since a given validation run may only
39
+ need one pair.
31
40
  """
32
41
 
33
42
  storage_account_key: Optional[str] = None
34
43
  sql_username: Optional[str] = None
35
44
  sql_password: Optional[str] = None
45
+ synapse_username: Optional[str] = None
46
+ synapse_password: Optional[str] = None
47
+ synapse_client_secret: Optional[str] = None
36
48
 
37
49
 
38
50
  def get_azure_credential(config: ValidatorConfig, env_path: Path = ENV_PATH) -> AzureCredential:
39
51
  """
40
52
  Resolve Azure credentials for the given config.
41
53
 
42
- Phase 1: reads AZURE_STORAGE_KEY / AZURE_SQL_USERNAME / AZURE_SQL_PASSWORD
43
- from ~/.table_validator/.env. A later phase can swap this body for
44
- Azure CLI / Service Principal auth without changing any caller.
54
+ Phase 1: reads AZURE_STORAGE_KEY / AZURE_SQL_USERNAME /
55
+ AZURE_SQL_PASSWORD / SYNAPSE_USERNAME / SYNAPSE_PASSWORD /
56
+ SYNAPSE_CLIENT_SECRET from ~/.table_validator/.env. A later phase can
57
+ swap this body for Azure CLI / managed-identity auth without changing
58
+ any caller.
45
59
  """
46
60
  values = dotenv_values(env_path) if env_path.exists() else {}
47
61
 
@@ -49,4 +63,7 @@ def get_azure_credential(config: ValidatorConfig, env_path: Path = ENV_PATH) ->
49
63
  storage_account_key=values.get("AZURE_STORAGE_KEY") or None,
50
64
  sql_username=values.get("AZURE_SQL_USERNAME") or None,
51
65
  sql_password=values.get("AZURE_SQL_PASSWORD") or None,
66
+ synapse_username=values.get("SYNAPSE_USERNAME") or None,
67
+ synapse_password=values.get("SYNAPSE_PASSWORD") or None,
68
+ synapse_client_secret=values.get("SYNAPSE_CLIENT_SECRET") or None,
52
69
  )
@@ -29,3 +29,18 @@ def get_databricks_token(config: ValidatorConfig, env_path: Path = ENV_PATH) ->
29
29
  """
30
30
  values = dotenv_values(env_path) if env_path.exists() else {}
31
31
  return values.get("DATABRICKS_TOKEN") or None
32
+
33
+
34
+ def host_from_workspace_url(workspace_url: Optional[str]) -> Optional[str]:
35
+ """DatabricksConnector wants a bare hostname; the wizard stores a full
36
+ https:// workspace URL, so strip the scheme and any trailing path.
37
+
38
+ Shared by cli/main.py (building the connector for `validate`) and
39
+ cli/wizard.py (building a connector during `configure` for the
40
+ column-mapping live picker) - kept here rather than in either CLI
41
+ module so neither has to import from the other.
42
+ """
43
+ if not workspace_url:
44
+ return None
45
+ host = workspace_url.replace("https://", "").replace("http://", "")
46
+ return host.split("/")[0]
@@ -10,7 +10,7 @@ from typing import Dict, Optional
10
10
  import typer
11
11
 
12
12
  from table_validator.auth.azure_auth import get_azure_credential
13
- from table_validator.auth.databricks_auth import get_databricks_token
13
+ from table_validator.auth.databricks_auth import get_databricks_token, host_from_workspace_url
14
14
  from table_validator.cli.summary_table import (
15
15
  print_summary_table,
16
16
  summary_from_excel,
@@ -19,7 +19,12 @@ from table_validator.cli.summary_table import (
19
19
  from table_validator.cli.partition_prompt import build_partition_prompt
20
20
  from table_validator.cli.wizard import run_configure_wizard
21
21
  from table_validator.config.manager import CONFIG_PATH, ConfigNotFoundError, require_config
22
- from table_validator.config.schema import SourceType, ValidationType, ValidatorConfig
22
+ from table_validator.config.schema import (
23
+ SourceType,
24
+ SynapseAuthMode,
25
+ ValidationType,
26
+ ValidatorConfig,
27
+ )
23
28
  from table_validator.connectors.azure_connector import AzureConnector, AzureSqlConnector
24
29
  from table_validator.connectors.databricks_connector import DatabricksConnector
25
30
  from table_validator.models import (
@@ -207,6 +212,18 @@ def validate(
207
212
  "prompt on its own, but --yes makes that explicit."
208
213
  ),
209
214
  ),
215
+ skip_category_summary: bool = typer.Option(
216
+ False,
217
+ "--skip-category-summary",
218
+ help=(
219
+ "Omit the 'Mismatch Categories' sheet (root-cause "
220
+ "classification + plain-English summary of Data Mismatches) "
221
+ "from the Excel report. Every other sheet, including Data "
222
+ "Mismatches itself, is unaffected - this recovers the exact "
223
+ "report shape from before the mismatch-categorization "
224
+ "feature existed, for anyone who doesn't want it."
225
+ ),
226
+ ),
210
227
  ) -> None:
211
228
  """Run validation and produce an Excel validation report."""
212
229
 
@@ -256,7 +273,7 @@ def validate(
256
273
 
257
274
  try:
258
275
  databricks = DatabricksConnector(
259
- host=_host_from_workspace_url(config.databricks.workspace_url),
276
+ host=host_from_workspace_url(config.databricks.workspace_url),
260
277
  token=token,
261
278
  http_path=config.databricks.http_path,
262
279
  )
@@ -281,6 +298,8 @@ def validate(
281
298
  result = _run_blob_validation(config, azure_credential, databricks)
282
299
  elif config.source_type == SourceType.AZURE_SQL:
283
300
  result = _run_sql_validation(config, azure_credential, databricks)
301
+ elif config.source_type == SourceType.SYNAPSE:
302
+ result = _run_synapse_validation(config, azure_credential, databricks)
284
303
  else:
285
304
  result = _run_databricks_validation(config, databricks, mode=mode, yes=yes)
286
305
  except ValueError as exc:
@@ -310,6 +329,7 @@ def validate(
310
329
  result, str(output),
311
330
  source_type=config.source_type.value,
312
331
  enabled_validations=report_enabled_validations,
332
+ skip_category_summary=skip_category_summary,
313
333
  )
314
334
  except PermissionError:
315
335
  typer.secho(
@@ -444,6 +464,17 @@ def _run_databricks_validation(
444
464
  tables_restriction = [config.source_table.table]
445
465
  if config.source_table.table.lower() != config.target_table.table.lower():
446
466
  table_map = {config.source_table.table: config.target_table.table}
467
+ else:
468
+ # Schema-wide sweep (no single table named on both sides) - apply
469
+ # the wizard's optional only_tables allowlist / table_map renaming
470
+ # for this schema, if the user set either. Both are no-ops
471
+ # (tables_restriction stays None, table_map stays {}) unless the
472
+ # user actually configured them, so a plain schema sweep behaves
473
+ # exactly as before this feature existed.
474
+ if config.only_tables:
475
+ tables_restriction = list(config.only_tables)
476
+ if config.table_map:
477
+ table_map = dict(config.table_map)
447
478
 
448
479
  # A configured primary key only applies to the single named table
449
480
  # (not a catalog-wide sweep) - CatalogValidator looks it up by
@@ -473,6 +504,7 @@ def _run_databricks_validation(
473
504
  only_columns=config.only_columns,
474
505
  ignore_columns=config.ignore_columns,
475
506
  ignore_datatype_columns=config.ignore_datatype_columns,
507
+ column_map=config.column_map,
476
508
  )
477
509
 
478
510
  partition_prompt = build_partition_prompt(yes=yes)
@@ -602,6 +634,113 @@ def _run_sql_validation(
602
634
  return validator.validate(request)
603
635
 
604
636
 
637
+ def _run_synapse_validation(
638
+ config: ValidatorConfig,
639
+ azure_credential,
640
+ databricks: DatabricksConnector,
641
+ ) -> CatalogValidationResponse:
642
+ """source_type == synapse: Azure Synapse SQL pool (dedicated or
643
+ serverless) against a Databricks catalog. Synapse SQL is
644
+ protocol-identical T-SQL over the same ODBC driver Azure SQL Database
645
+ uses, so this reuses AzureSqlConnector/AzureSqlValidator UNCHANGED,
646
+ just constructed with the Synapse server/database/credentials instead
647
+ of an Azure SQL DB's - no new connector or validator class, mirrors
648
+ _run_sql_validation's logic exactly (see that function for the
649
+ schema_map/table_map/primary_keys/FULL-mode rationale, identical
650
+ here).
651
+
652
+ Auth is the one place Synapse genuinely diverges from the Azure SQL
653
+ path: config.azure.synapse_auth_mode selects between a SQL login
654
+ (SYNAPSE_USERNAME/SYNAPSE_PASSWORD) and a Microsoft Entra ID service
655
+ principal (azure.tenant_id + azure.synapse_client_id +
656
+ SYNAPSE_CLIENT_SECRET). Entra is what a workspace with SQL
657
+ authentication disabled requires - the connector fetches an access
658
+ token and passes it via the ODBC access-token attribute rather than
659
+ sending UID/PWD at all.
660
+
661
+ Not yet verified against a live Synapse SQL pool for pool-specific
662
+ T-SQL quirks (dedicated pools have historically had some system-view/
663
+ HASHBYTES-adjacent gaps vs. Azure SQL DB) - flagged here rather than
664
+ assumed away; report back if a specific query fails against your pool
665
+ and it can be special-cased in AzureSqlConnector the same way
666
+ decimal_as_integer/float formatting already are for Databricks
667
+ compatibility.
668
+ """
669
+ if not config.azure.synapse_server or not config.azure.synapse_database:
670
+ raise ValueError(
671
+ "azure.synapse_server and azure.synapse_database are required "
672
+ "for a Synapse source. Run 'tablevalidator configure' first."
673
+ )
674
+
675
+ if config.azure.synapse_auth_mode == SynapseAuthMode.ENTRA_SERVICE_PRINCIPAL:
676
+ missing_entra = []
677
+ if not config.azure.tenant_id:
678
+ missing_entra.append("azure.tenant_id (in config.yaml)")
679
+ if not config.azure.synapse_client_id:
680
+ missing_entra.append("azure.synapse_client_id (in config.yaml)")
681
+ if not azure_credential.synapse_client_secret:
682
+ missing_entra.append("SYNAPSE_CLIENT_SECRET (in ~/.table_validator/.env)")
683
+ if missing_entra:
684
+ raise ValueError(
685
+ "Synapse is configured for Entra service-principal auth but "
686
+ "these are missing: " + ", ".join(missing_entra)
687
+ + ". Run 'tablevalidator configure' first."
688
+ )
689
+
690
+ synapse = AzureSqlConnector(
691
+ server=config.azure.synapse_server,
692
+ database=config.azure.synapse_database,
693
+ tenant_id=config.azure.tenant_id,
694
+ client_id=config.azure.synapse_client_id,
695
+ client_secret=azure_credential.synapse_client_secret,
696
+ )
697
+ else:
698
+ if not azure_credential.synapse_username or not azure_credential.synapse_password:
699
+ raise ValueError(
700
+ "No Synapse username/password found in ~/.table_validator/.env. "
701
+ "Run 'tablevalidator configure' first."
702
+ )
703
+
704
+ synapse = AzureSqlConnector(
705
+ server=config.azure.synapse_server,
706
+ database=config.azure.synapse_database,
707
+ username=azure_credential.synapse_username,
708
+ password=azure_credential.synapse_password,
709
+ )
710
+
711
+ schema_map: Dict[str, str] = {}
712
+ schemas_restriction = None
713
+ if config.synapse_source.schema_name and config.target_table.schema_name:
714
+ schema_map = {config.synapse_source.schema_name: config.target_table.schema_name}
715
+ schemas_restriction = [config.synapse_source.schema_name]
716
+
717
+ table_map: Dict[str, str] = {}
718
+ tables_restriction = None
719
+ if config.synapse_source.table and config.target_table.table:
720
+ table_map = {config.synapse_source.table: config.target_table.table}
721
+ tables_restriction = [config.synapse_source.table]
722
+
723
+ primary_keys: Dict[str, list] = {}
724
+ if config.primary_key and config.target_table.table:
725
+ primary_keys[config.target_table.table] = config.primary_key
726
+ if config.target_table.schema_name:
727
+ key = f"{config.target_table.schema_name}.{config.target_table.table}"
728
+ primary_keys[key] = config.primary_key
729
+
730
+ request = AzureSqlValidationRequest(
731
+ target_catalog=config.target_table.catalog or "",
732
+ schemas=schemas_restriction,
733
+ schema_map=schema_map,
734
+ tables=tables_restriction,
735
+ table_map=table_map,
736
+ primary_keys=primary_keys,
737
+ data_compare_mode=DataCompareMode.FULL,
738
+ )
739
+
740
+ validator = AzureSqlValidator(synapse, databricks)
741
+ return validator.validate(request)
742
+
743
+
605
744
  # ---------------------------------------------------------------------------
606
745
  # Helpers
607
746
  # ---------------------------------------------------------------------------
@@ -631,6 +770,11 @@ def _missing_config_fields(config: ValidatorConfig) -> list:
631
770
  missing.append("azure.sql_server")
632
771
  if not config.azure.sql_database:
633
772
  missing.append("azure.sql_database")
773
+ elif config.source_type == SourceType.SYNAPSE:
774
+ if not config.azure.synapse_server:
775
+ missing.append("azure.synapse_server")
776
+ if not config.azure.synapse_database:
777
+ missing.append("azure.synapse_database")
634
778
  else:
635
779
  if not config.source_table.catalog:
636
780
  missing.append("source_table.catalog")
@@ -638,15 +782,6 @@ def _missing_config_fields(config: ValidatorConfig) -> list:
638
782
  return missing
639
783
 
640
784
 
641
- def _host_from_workspace_url(workspace_url: Optional[str]) -> Optional[str]:
642
- """DatabricksConnector wants a bare hostname; the wizard stores a full
643
- https:// workspace URL, so strip the scheme and any trailing path."""
644
- if not workspace_url:
645
- return None
646
- host = workspace_url.replace("https://", "").replace("http://", "")
647
- return host.split("/")[0]
648
-
649
-
650
785
  def _open_in_default_app(path: Path) -> None:
651
786
  """
652
787
  Launch `path` in whatever application the OS has associated with its
@@ -704,6 +839,17 @@ def _describe_scope(config: ValidatorConfig) -> str:
704
839
  source_desc = f"sql:{database}.{config.sql_source.schema_name} (all tables)"
705
840
  else:
706
841
  source_desc = f"sql:{database}.{config.sql_source.schema_name}.{config.sql_source.table}"
842
+ elif config.source_type == SourceType.SYNAPSE:
843
+ database = config.azure.synapse_database or "?"
844
+ if not config.synapse_source.schema_name:
845
+ source_desc = f"synapse:{database} (all schemas)"
846
+ elif not config.synapse_source.table:
847
+ source_desc = f"synapse:{database}.{config.synapse_source.schema_name} (all tables)"
848
+ else:
849
+ source_desc = (
850
+ f"synapse:{database}.{config.synapse_source.schema_name}."
851
+ f"{config.synapse_source.table}"
852
+ )
707
853
  else:
708
854
  source_desc = _side(config.source_table)
709
855