Repository navigation
Test that field resolution is one precedence order; characterise the crossed case - #52
Merged
Merged
Conversation
…) sites Enumerates every subset of 3 config classes x 3 scopes for one field and checks: - a site most specific on both axes wins (true under either loop order); - removing losing sites changes neither the winner nor its reported source; - the two-site results form a strict total order whose first member wins every set; - stored value, attribute read, resolver answer and ObjectState's resolved value and source agree in every case and stay distinct. Crossed pairs (no site most specific on both axes) are the only cases that show which axis is the outer loop. Those tests are labelled as a characterisation of today's class-first behaviour, because the documentation states both orders. No production change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tests only, no production change. Checks the field resolver (
dual_axis_resolver._resolve_in_layers) against two results on two-axis resolution.What the resolver does
_resolve_in_layersis the only place the order is decided. The class axis (MRO of the type being read) is the outer loop and the scope axis (context layers, innermost first) is the inner loop. Attribute access on a lazy config,resolve_with_provenanceand bothObjectStateresolution paths all go through it.New tests (
tests/test_resolution_precedence_order.py)Three classes (
Leaf(Middle(Base))) x three scopes (step, plate, global) give nine definition sites; all 512 subsets are resolved forLeaf.value.Hold under either loop order:
ObjectState's resolved value and source agree in every subset, and are asserted separately.Characterisation of today's behaviour (labelled as such in the file):
ObjectStatekeepsNonestored and reports the resolver's value and source on a crossed pair.Checked by mutation: a scope-first resolver fails only the characterisation tests; a resolver that switches order when three or more values are present fails the removal and total-order tests.
Owner question: which axis is meant to be outer?
Before this PR no test asserted a winner for a crossed pair.
_ancestor_supplies_base_type_onlyintest_one_field_resolution.pybuilds one, but only compares two reads that both use the same resolver, so it passes under either order.The documentation states both orders:
_resolve_in_layersdocstring ("MRO order dominates layer order") andsrc/objectstate/docs/context_system.rst("For each type in the container type's MRO, most specific first, it asks every layer from innermost to outermost");src/objectstate/docs/configuration_framework.rst("checkAppConfigcontext first, thenGlobalAppConfigcontext, walking MRO at each level").Concrete example, reading
Leaf.valueon a step:Leafin the global config"Leaf@global"Baseon the plate"Base@plate"Today the step reads
"Leaf@global"with source("", Leaf): a value the user set on the plate for the base class loses to the global value on the subclass. Scope first would give"Base@plate"with source("plate", Base).If class first is intended, drop the "characterisation" labels and correct the sentence in
configuration_framework.rst. If scope first is intended, the change is the loop nesting in_resolve_in_layersand the expected winner in the two characterisation tests.Test run
Fresh
uv venv, Python 3.12:312 passed, 22 warnings(297 before, 15 new).🤖 Generated with Claude Code