Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pkg-py/src/commons/_catalog/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

from . import _databricks, _snowflake
from ._core import (
CatalogAmbiguousColumnError,
CatalogAmbiguousTableError,
CatalogClaimedRelationError,
Manifest,
MergedDictionary,
Relation,
Expand Down Expand Up @@ -40,7 +43,10 @@

__all__ = [
"CatalogAccessError",
"CatalogAmbiguousColumnError",
"CatalogAmbiguousTableError",
"CatalogAuthorizationError",
"CatalogClaimedRelationError",
"CatalogSessionChangedError",
"CatalogTransientError",
"Manifest",
Expand Down
18 changes: 15 additions & 3 deletions pkg-py/src/commons/_catalog/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@
_GLOB_SPECIAL = re.compile(r"([][{}()+.^$|\\])")


class CatalogAmbiguousTableError(ValueError):
"""An authored table name matches more than one selected relation."""


class CatalogClaimedRelationError(ValueError):
"""Two authored tables match the same selected relation."""


class CatalogAmbiguousColumnError(ValueError):
"""An authored column name matches more than one discovered column."""


@dataclass(frozen=True)
class Selector:
"""One entry of a `tables` selection, naming a relation or a namespace.
Expand Down Expand Up @@ -442,7 +454,7 @@ def _dictionary_matches(
if label is None:
continue
if label in claimed:
raise ValueError(
raise CatalogClaimedRelationError(
f"Authored tables {claimed[label]!r} and {authored_name!r} "
f"both match selected relation {label!r}."
)
Expand Down Expand Up @@ -504,7 +516,7 @@ def has_suffix(

def _abort_ambiguous(authored_name: str, candidates: list[str]) -> None:
listed = ", ".join(repr(item) for item in sorted(candidates))
raise ValueError(
raise CatalogAmbiguousTableError(
f"Authored table {authored_name!r} matches more than one selected "
f"relation: {listed}. Use its fully qualified name in the data "
f"dictionary."
Expand Down Expand Up @@ -562,7 +574,7 @@ def _merge_columns(
]
if len(candidates) > 1:
listed = ", ".join(repr(name) for name in candidates)
raise ValueError(
raise CatalogAmbiguousColumnError(
f"Authored column {authored_name!r} matches more than one "
f"discovered column: {listed}."
)
Expand Down
12 changes: 11 additions & 1 deletion pkg-py/tests/test_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
import pytest

from commons._catalog import (
CatalogAmbiguousColumnError,
CatalogAmbiguousTableError,
CatalogClaimedRelationError,
Manifest,
MergedDictionary,
Relation,
Expand Down Expand Up @@ -508,13 +511,20 @@ def run_merge_case(case: dict) -> MergedDictionary:
)


_MERGE_ERRORS = {
"ambiguous": CatalogAmbiguousTableError,
"claimed": CatalogClaimedRelationError,
"ambiguous_column": CatalogAmbiguousColumnError,
}


def test_the_dictionary_merge_matches_the_shared_contract():
cases = load_shared_fixture("catalog-merge")["merge"]
assert cases
for case in cases:
expect = case["expect"]
if "error" in expect:
with pytest.raises(ValueError):
with pytest.raises(_MERGE_ERRORS[expect["error"]]):
run_merge_case(case)
continue

Expand Down
2 changes: 2 additions & 0 deletions pkg-r/NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

* R code run by `run_r` can no longer send a signal to the application process's ID or to its process group. On Linux, the programs it starts can also no longer leave its process group, so they all stop when the R session ends.

* When a data dictionary cannot be matched to the warehouse catalog, the error now has a condition class you can catch: `commons_catalog_ambiguous_table_error` when an authored table name matches more than one selected relation, `commons_catalog_claimed_relation_error` when two authored tables match the same relation, and `commons_catalog_ambiguous_column_error` when an authored column name matches more than one discovered column.

* The CSS classes on `run_r`'s display are now `commons-run-display`, `commons-run-details`, `commons-run-code`, and `commons-run-plot`, without the `-r`, because the Python package's code tool uses the same classes. App CSS that targets the old `commons-run-r-*` names needs updating.

# commons 0.1.1
Expand Down
3 changes: 3 additions & 0 deletions pkg-r/R/catalog.R
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ catalog_dictionary_matches <- function(
cli::cli_abort(
"Authored tables {.val {c(other, authored_name)}} both match selected
relation {.val {label}}.",
class = "commons_catalog_claimed_relation_error",
call = call
)
}
Expand Down Expand Up @@ -530,6 +531,7 @@ catalog_abort_ambiguous_dictionary_table <- function(
relation: {.val {matches}}.",
"i" = "Use its fully qualified name in the data dictionary."
),
class = "commons_catalog_ambiguous_table_error",
call = call
)
}
Expand Down Expand Up @@ -593,6 +595,7 @@ catalog_merge_dictionary_columns <- function(
cli::cli_abort(
"Authored column {.val {authored_name}} matches more than one discovered
column: {.val {names(out)[candidates]}}.",
class = "commons_catalog_ambiguous_column_error",
call = call
)
}
Expand Down
8 changes: 7 additions & 1 deletion pkg-r/tests/testthat/test-catalog-merge.R
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ test_that("exclusion globs match the shared contract", {
}
})

merge_error_class <- c(
ambiguous = "commons_catalog_ambiguous_table_error",
claimed = "commons_catalog_claimed_relation_error",
ambiguous_column = "commons_catalog_ambiguous_column_error"
)

test_that("the dictionary merge matches the shared contract", {
cases <- shared_fixture("catalog-merge")$merge
expect_gt(length(cases), 0L)
Expand Down Expand Up @@ -70,7 +76,7 @@ test_that("the dictionary merge matches the shared contract", {
)
}
if (!is.null(case$expect$error)) {
expect_error(run())
expect_error(run(), class = merge_error_class[[case$expect$error]])
next
}
merged <- run()
Expand Down
Loading