Follow-up cleanup from review of #204 (non-blocking; DRY).
Several small helpers in the new substrait.api layer re-implement logic that already exists elsewhere; consolidating avoids drift:
expr._merge_extensions_into (src/substrait/expr.py) hand-rolls extension URN/declaration dedup on strict proto equality, whereas substrait.utils.merge_extension_urns / merge_extension_declarations key on anchor/name identity (as builders/plan._merge_extensions does). The two can disagree on when declarations dedupe.
expr._sort_direction(descending, nulls_last) (src/substrait/expr.py) duplicates frame._SORT_DIRECTIONS (src/substrait/frame.py) — the same (descending, nulls_last) -> SortDirection mapping in two new files.
functions._multi_urn_helper (src/substrait/functions.py) loops registry.lookup_function per URN; ExtensionRegistry.list_functions_across_urns already resolves matches across every URN in one call, and FunctionEntry.urn recovers the winning extension.
Expr.alias (src/substrait/expr.py) repeats the referred_expr[0].output_names[0] = name rewrite already in narwhals/expression._alias.
Suggested direction: extract shared helpers (natural home: substrait.utils / builders.extended_expression) and have both call sites use them.
🤖 Filed with AI assistance as a follow-up to #204.
Follow-up cleanup from review of #204 (non-blocking; DRY).
Several small helpers in the new
substrait.apilayer re-implement logic that already exists elsewhere; consolidating avoids drift:expr._merge_extensions_into(src/substrait/expr.py) hand-rolls extension URN/declaration dedup on strict proto equality, whereassubstrait.utils.merge_extension_urns/merge_extension_declarationskey on anchor/name identity (asbuilders/plan._merge_extensionsdoes). The two can disagree on when declarations dedupe.expr._sort_direction(descending, nulls_last)(src/substrait/expr.py) duplicatesframe._SORT_DIRECTIONS(src/substrait/frame.py) — the same(descending, nulls_last) -> SortDirectionmapping in two new files.functions._multi_urn_helper(src/substrait/functions.py) loopsregistry.lookup_functionper URN;ExtensionRegistry.list_functions_across_urnsalready resolves matches across every URN in one call, andFunctionEntry.urnrecovers the winning extension.Expr.alias(src/substrait/expr.py) repeats thereferred_expr[0].output_names[0] = namerewrite already innarwhals/expression._alias.Suggested direction: extract shared helpers (natural home:
substrait.utils/builders.extended_expression) and have both call sites use them.🤖 Filed with AI assistance as a follow-up to #204.