Skip to content

Add namespace-agnostic type resolution to NamespaceCatalog (#1528) - #1531

Draft
rly wants to merge 2 commits into
devfrom
fix/1528-namespace-agnostic-resolution
Draft

Add namespace-agnostic type resolution to NamespaceCatalog (#1528)#1531
rly wants to merge 2 commits into
devfrom
fix/1528-namespace-agnostic-resolution

Conversation

@rly

@rly rly commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Motivation

Closes #1528. Groundwork for #608 (validating files that use multiple extensions across namespaces).

Cross-namespace consumers sometimes need to resolve a data type (its spec or its ancestor hierarchy) from only a bare data_type, without knowing which namespace defines it. NamespaceCatalog previously only exposed namespace-scoped lookups. The build layer already has the analogous namespace-agnostic convenience in TypeMap.get_dt_container_cls(data_type, namespace=None).

Changes

Additive, no breaking changes. On NamespaceCatalog:

  • get_spec(namespace=None, data_type=...): when namespace is None, search all loaded namespaces and return the spec from the first namespace that defines the type.
  • get_hierarchy(namespace=None, data_type=...): when namespace is None, return the owning namespace's hierarchy; empty tuple if no loaded namespace defines the type.
  • is_sub_data_type(namespace=None, ...): namespace optional for symmetry.
  • type_key / type_keys properties (new): report the catalog's data type key(s) from its spec classes.

Existing two-argument get_spec(namespace, data_type) / get_hierarchy(namespace, data_type) / is_sub_data_type(namespace, ...) calls (positional and keyword) are unchanged.

Note on colliding type names across namespaces

Different namespaces can define different types that share a name. The read/construct path differentiates them because every typed builder stores a namespace attribute and is resolved by (namespace, data_type) (TypeMap.get_clsget_builder_ns + get_dt_container_cls(dt, ns)). The namespace=None lookups added here resolve by bare name and return the first match, which assumes a name identifies the same type in every namespace (the same assumption TypeMap.get_dt_container_cls(namespace=None) already makes). This is documented on each method: pass an explicit namespace to disambiguate. To keep this collision-safe, downstream validation should dispatch each builder by its stored (namespace, data_type) rather than by bare name (tracked in #1529).

Testing

  • New tests/unit/spec_tests/test_multi_namespace_resolution.py: a shared dependency namespace plus two independent extension namespaces, including two different types that reuse the name Widget. Asserts search-all resolution, the unchanged two-argument behavior, the type-key properties, and that an explicit namespace disambiguates the colliding name (while namespace=None returns the documented first match).
  • Full spec_tests, build_tests, common, and validator_tests suites pass (1196 passed, 53 skipped). ruff clean.

How to test

pytest tests/unit/spec_tests/test_multi_namespace_resolution.py -v

Checklist

  • Did you update CHANGELOG.md with your changes?
  • Have you checked our Contributing document?
  • Have you ensured the PR clearly describes the problem and the solution?
  • Is your contribution compliant with our coding style? (checked by ruff)
  • Have you checked to ensure that there aren't other open Pull Requests for the same change?
  • Have you included the relevant issue number using "Fix #XXX" notation where XXX is the issue number?

🤖 Generated with Claude Code

Make get_spec, get_hierarchy, and is_sub_data_type accept namespace=None to
search all loaded namespaces; add get_subtypes(data_type) that unions subtypes
across namespaces; add type_key/type_keys properties reporting the catalog's
data type key(s). Groundwork for validating files that use multiple extensions
across namespaces (#608).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rly
rly force-pushed the fix/1528-namespace-agnostic-resolution branch from 15fdd21 to 4810baa Compare July 7, 2026 08:25
@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.19%. Comparing base (5ee6e94) to head (fdc99c3).

Files with missing lines Patch % Lines
src/hdmf/spec/namespace.py 80.00% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #1531      +/-   ##
==========================================
- Coverage   93.21%   93.19%   -0.03%     
==========================================
  Files          41       41              
  Lines       10186    10213      +27     
  Branches     2104     2114      +10     
==========================================
+ Hits         9495     9518      +23     
- Misses        414      416       +2     
- Partials      277      279       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…atch

The namespace=None lookups resolve a data type by bare name and return the first
match, mirroring TypeMap.get_dt_container_cls. Document that an explicit namespace
is needed to disambiguate a name defined differently in more than one namespace,
and remove get_subtypes: a cross-namespace subtype union conflates distinct
same-named types, so it is unsafe as a validation primitive. Add a collision test
showing explicit-namespace resolution stays correct.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add namespace-agnostic type resolution to NamespaceCatalog (get_spec/get_hierarchy/get_subtypes across all namespaces)

1 participant