Skip to content

Commit 3273c8b

Browse files
rustyconoverclaude
andcommitted
feat(catalog): let declarative Catalog advertise source_url
The metadata-quality linter's VGI004 rule expects catalogs to advertise a source_url (repo/docs homepage) via the catalog_catalogs discovery record (CatalogInfo.source_url / duckdb_databases()). The declarative Catalog descriptor had no way to set it, and the default catalogs() serializer always emitted source_url=None. Add a settable source_url field to the Catalog descriptor and thread it through ReadOnlyCatalogInterface.catalogs() so it flows into CatalogInfo. Schema-level vgi.description_llm / vgi.description_md tags (VGI116/VGI118) were already wired: Schema.tags flows through Schema.to_schema_info() into SchemaInfo.tags, so no change was needed there. Mirrors the vgi-rust fix (CatalogModel.source_url + serializer emit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 02c3e39 commit 3273c8b

6 files changed

Lines changed: 48 additions & 1 deletion

File tree

‎tests/catalog/test_declarative.py‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,20 @@ def test_catalog_multiple_schemas(self) -> None:
12071207
catalog = Catalog(name="myapp", schemas=[main, analytics])
12081208
assert len(catalog.schemas) == 2
12091209

1210+
def test_catalog_source_url_defaults_to_none(self) -> None:
1211+
"""Catalog source_url is None unless set."""
1212+
catalog = Catalog(name="myapp", schemas=[Schema(name="main")])
1213+
assert catalog.source_url is None
1214+
1215+
def test_catalog_source_url_settable(self) -> None:
1216+
"""Catalog source_url can advertise the repo/docs homepage."""
1217+
catalog = Catalog(
1218+
name="myapp",
1219+
schemas=[Schema(name="main")],
1220+
source_url="https://github.com/example/myapp",
1221+
)
1222+
assert catalog.source_url == "https://github.com/example/myapp"
1223+
12101224

12111225
class TestCatalogValidation:
12121226
"""Tests for Catalog validation."""
@@ -1311,6 +1325,25 @@ def test_catalogs_returns_catalog_name(self, catalog_interface: ReadOnlyCatalogI
13111325
infos = catalog_interface.catalogs()
13121326
assert [i.name for i in infos] == ["testapp"]
13131327

1328+
def test_catalogs_omits_source_url_when_unset(self, catalog_interface: ReadOnlyCatalogInterface) -> None:
1329+
"""catalogs() yields source_url=None when the Catalog doesn't set one."""
1330+
infos = catalog_interface.catalogs()
1331+
assert infos[0].source_url is None
1332+
1333+
def test_catalogs_serializes_source_url(self, users_table: Table) -> None:
1334+
"""catalogs() surfaces the Catalog.source_url into CatalogInfo."""
1335+
1336+
class SourcedCatalog(ReadOnlyCatalogInterface):
1337+
catalog = Catalog(
1338+
name="sourced",
1339+
default_schema="main",
1340+
schemas=[Schema(name="main", tables=[users_table])],
1341+
source_url="https://github.com/example/sourced",
1342+
)
1343+
1344+
infos = SourcedCatalog().catalogs()
1345+
assert infos[0].source_url == "https://github.com/example/sourced"
1346+
13141347
def test_catalog_attach(self, catalog_interface: ReadOnlyCatalogInterface) -> None:
13151348
"""catalog_attach returns result with correct defaults."""
13161349
result = catalog_interface.catalog_attach(

‎tests/catalog/test_example_worker_catalog.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ def test_catalogs_returns_example(self) -> None:
7878
catalogs = client.catalogs()
7979
assert "example" in [c.name for c in catalogs]
8080

81+
def test_catalogs_advertises_source_url(self) -> None:
82+
"""The declarative catalog's source_url round-trips through discovery."""
83+
client = Client(EXAMPLE_WORKER)
84+
catalogs = client.catalogs()
85+
example = next(c for c in catalogs if c.name == "example")
86+
assert example.source_url == "https://github.com/query-farm/vgi-python"
87+
8188
def test_catalog_attach_works(self) -> None:
8289
"""Can attach to the 'example' catalog."""
8390
client = Client(EXAMPLE_WORKER)

‎uv.lock‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎vgi/_test_fixtures/worker.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ def _build_enum_stats() -> dict[str, ColumnStatisticsInput]:
320320
default_schema="main",
321321
comment="Example VGI catalog for testing",
322322
tags={"source": "vgi-fixture-worker", "version": "1"},
323+
source_url="https://github.com/query-farm/vgi-python",
323324
schemas=[
324325
Schema(
325326
name="main",

‎vgi/catalog/catalog_interface.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2319,6 +2319,7 @@ def catalogs(self) -> list[CatalogInfo]:
23192319
implementation_version=None,
23202320
data_version_spec=None,
23212321
attach_option_specs=[spec.serialize() for spec in self.attach_option_specs],
2322+
source_url=self.catalog.source_url if self.catalog is not None else None,
23222323
)
23232324
]
23242325

‎vgi/catalog/descriptors.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,10 @@ class Catalog:
873873
schemas: Sequence of Schema objects defining the catalog contents.
874874
comment: Optional comment describing the catalog.
875875
tags: Optional key-value tags associated with the catalog.
876+
source_url: Where this worker's code lives — repo, build, or docs
877+
homepage. ``None`` (the default) when the worker doesn't advertise
878+
a source location. Surfaced via the ``catalog_catalogs()`` discovery
879+
record (``CatalogInfo.source_url``).
876880
877881
"""
878882

@@ -881,6 +885,7 @@ class Catalog:
881885
schemas: Sequence[Schema] = ()
882886
comment: str | None = None
883887
tags: dict[str, str] = field(default_factory=dict)
888+
source_url: str | None = None
884889

885890
def __post_init__(self) -> None:
886891
"""Validate catalog configuration."""

0 commit comments

Comments
 (0)