From 7935487dcfa3548d6bab497cfe3bc4649df44a94 Mon Sep 17 00:00:00 2001 From: Niels Pardon Date: Mon, 8 Jun 2026 11:07:14 +0200 Subject: [PATCH] fix(extensions): expose top-level metadata in YAML extension files build_simple_extensions populated metadata for functions and types but silently dropped the file-level metadata field, so it was not available at runtime. Pass it through and add tests covering metadata exposure at the file, function, and type levels. Fixes #149 Co-Authored-By: Claude Opus 4.8 (1M context) --- src/substrait/simple_extension_utils.py | 1 + tests/test_simple_extension_utils.py | 51 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/test_simple_extension_utils.py diff --git a/src/substrait/simple_extension_utils.py b/src/substrait/simple_extension_utils.py index 7baa9aa..738bede 100644 --- a/src/substrait/simple_extension_utils.py +++ b/src/substrait/simple_extension_utils.py @@ -172,6 +172,7 @@ def build_type_variation(d: dict) -> se.TypeVariation: def build_simple_extensions(d: dict) -> se.SimpleExtensions: return se.SimpleExtensions( urn=d["urn"], + metadata=d.get("metadata"), dependencies=d.get("dependencies"), types=[build_type_model(t) for t in d["types"]] if "types" in d else None, type_variations=[build_type_variation(t) for t in d["type_variations"]] diff --git a/tests/test_simple_extension_utils.py b/tests/test_simple_extension_utils.py new file mode 100644 index 0000000..9a9ae0a --- /dev/null +++ b/tests/test_simple_extension_utils.py @@ -0,0 +1,51 @@ +"""Tests for parsing simple extension YAML dicts into schema objects.""" + +from substrait.simple_extension_utils import build_simple_extensions + + +def test_metadata_is_exposed_at_all_levels(): + """Metadata should be carried through from the YAML dict at every level + where the simple-extension schema supports it (file, function, type).""" + definitions = { + "urn": "extension:test:metadata", + "metadata": {"author": "me", "version": 1}, + "types": [ + {"name": "point", "metadata": {"kind": "geometry"}}, + ], + "scalar_functions": [ + { + "name": "f", + "impls": [{"return": "i64"}], + "metadata": {"category": "math"}, + }, + ], + "aggregate_functions": [ + { + "name": "g", + "impls": [{"return": "i64"}], + "metadata": {"category": "stats"}, + }, + ], + "window_functions": [ + { + "name": "h", + "impls": [{"return": "i64"}], + "metadata": {"category": "window"}, + }, + ], + } + + extensions = build_simple_extensions(definitions) + + assert extensions.metadata == {"author": "me", "version": 1} + assert extensions.types[0].metadata == {"kind": "geometry"} + assert extensions.scalar_functions[0].metadata == {"category": "math"} + assert extensions.aggregate_functions[0].metadata == {"category": "stats"} + assert extensions.window_functions[0].metadata == {"category": "window"} + + +def test_metadata_defaults_to_none_when_absent(): + """Metadata is optional and should default to None when not present.""" + extensions = build_simple_extensions({"urn": "extension:test:no-metadata"}) + + assert extensions.metadata is None