Repository navigation
Release 3.0.0: one field resolution, loud context stacks, per-context blocks, registry-owned lazy counterparts - #50
Merged
Conversation
…skipped or substituted build_context_stack is a context manager and takes scoped ancestors only. config_context, merge_configs, build_context_stack, resolve_with_provenance and the lazy resolver method raise where they used to continue with a layer missing. Uncalled helpers that substituted layers are deleted. Refs #34 (items 1, 2), #31. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… payload atomic, atomic_success and defer_live_invalidations kept their nesting in class attributes shared by the process, while config_context keeps its scope in context variables. The open block is now an AtomicBlock held in a ContextVar and the deferral nesting is a ContextVar count, so a thread outside any block no longer sees another thread's block (#30). TimeTravelChangeSet.legacy_entries is the only completion-callback payload, so it is renamed states_needing_navigation (#34 item 3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The lazy class of a config class was found by building its name from a prefix and reading that name off the module. The registry that already maps lazy to base now answers both directions, and nothing looks a lazy class up by name. A class's group abbreviation is declared once, on the class, and read on demand; a lazy class answers with its base's declaration. The @abbreviation decorator and the abbreviation= parameter write the same declaration, so decorator order no longer matters and two different declarations for one class raise. GROUP_ABBREVIATIONS_REGISTRY and LAZY_CONFIG_PREFIX are removed. The factory alone names a lazy class. Fixes #35 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ayer stack Attribute access and ObjectState both read resolve_with_provenance. The merged-config walk (resolve_field_inheritance) and the process-wide caches are deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to release/3.0.0-context-resolution
…ease/3.0.0-context-resolution
…into release/3.0.0-context-resolution
…se/3.0.0-context-resolution
…ld by their objects - get_provenance reports (None, source_type) when a state without a scope id inherits a field from another config of its own object. It returned None, which reads as 'set on this field', while the resolved value was inherited. - LiveContextResolver keeps each answer with the config object and context objects it was computed from and finds it again by identity. No key is built from id(...). Answers of an earlier cache token are dropped. - Version 3.0.0: public names are removed by the merged changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 12, 2026
Merged
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.
Fixes #29, #30, #31, #33, #34, #35
Release branch for 3.0.0. Public names are removed, so the major version changes (
__version__insrc/objectstate/__init__.py, the one place it is declared).Supersedes
fix/34-31-context-stack-loud(Building a context continues after failures with a layer missing or substituted #31, build_context_stack returns an open ExitStack; remove the deprecated ancestor format and legacy_entries #34 items 1-2)fix/29-33-one-resolver(Attribute reads and displayed provenance can resolve the same field to different values #29, Resolution caches are process-wide but depend on the per-thread context #33), which was stacked on Context stacks: with-only build_context_stack, scoped ancestors only, no layer skipped or substituted (#31, #34 items 1-2) #41fix/30-34-atomic-block-context(atomic and defer_live_invalidations share one counter across threads #30, build_context_stack returns an open ExitStack; remove the deprecated ancestor format and legacy_entries #34 item 3)fix/35-lazy-registry(The lazy counterpart of a class is looked up by constructed name #35)Merged in that order with merge commits. Git reported no textual conflict. #49 and #40 both edit
object_state_registry.pyin different places (#49 removes three calls to the deleted cache-clear functions; #40 moves the block state intoContextVars) and #41, #49 and #42 all editlazy_factory.py; each merged cleanly and every PR's tests pass together.Added on this branch
get_provenancefor a state without a scope id. With #49, anObjectStatewithout a scope id has its own object as a layer, so an unset field inherits from another config of the same object, as it already did for a state with a scope id and for an attribute read on the object. The resolved value was right, butget_provenancestill returnedNonefor it (itsscope_id is Nonebranch predates #49), which callers read as "set on this field". It now returns(None, source_type): the state's own scope id, and the type that supplied the value.tests/test_scopeless_state_resolution.pyruns each case with and without a scope id:("", WellFilterConfig)(state.scope_id, WellFilterConfig);(None, WellFilterConfig)without a scope idNone(not inherited)Two of these cases fail without the fix, both for the state without a scope id.
The third
id()-keyed cache.LiveContextResolverkeyed answers by(id(config_obj), attr_name, ids of the context objects, token). Each answer is now kept together with the config object and context objects it was computed from and found again withis; holding the objects means an address cannot be reused while its answer is kept. Answers belong to one cache token and are dropped when a new token arrives (before, every token's answers were kept for the life of the resolver).tests/test_live_context_resolver_answers.py; the reused-address test fails on the old code.Not changed:
object_state_registry.py:1054putsid(state)in an atomic block's entry signature. That is a comparison within one block while the states are held, not a cache key.Checks run locally (CPython 3.12)
pytest -n 2: 297 passed.ruff check src/ --select E9,F63,F7,F82: clean.scripts/check_ruff_added_lines.py --base origin/main: no diagnostics on added lines.black --check src/(non-blocking in CI): 23 files would be reformatted.What callers must change
Full ledger with file:line for every site:
OS-3.0-caller-ledger.md(kept with the OpenHCS integration notes). Summary:pyqt-reactive (10 sites)
services/widget_service.py:310-316:build_context_stack(..., ancestor_objects=...)raisesTypeError. UseObjectStateRegistry.get_ancestor_objects_with_scopes(...)andancestor_objects_with_scopes=.pyproject.toml:32:objectstate>=2.0,<3excludes this release.requirements-ci.txt:2names 2.1.1.tests/test_preview_formatters.py:10,229,230,279:GROUP_ABBREVIATIONS_REGISTRYis removed; declare with@abbreviation.tests/test_function_list_editor_tokens.py:32-34: assigns removed_atomic_*attributes.clickable_help_components.py:534,707,735can now receive(None, source_type)fromget_provenance; the same-window branch already handles it.formbridge (3 sites)
strategies/preview_formatting.py:28,31: importsGROUP_ABBREVIATIONS_REGISTRY; callobjectstate.lazy_factory.get_group_abbreviation.pyproject.toml:29: version pin.pycodify: none.
OpenHCS (10 sites, none in
openhcs/,benchmark/orscripts/)pyproject.toml:104: version pin.tests/unit/pyqt_gui/test_source_bindings_editor.py:3180-3181: asserts that only the edited cell flashes. It pinned the old disagreement between the two resolvers: with a scope id the same edit already flashed two cells onmain, becausestep_source_bindings_config.source_filtersinherits fromsource_bindings_configof the same object. Assert both paths.tests/unit/test_time_travel_navigation.py:53-55,tests/unit/pyqt_gui/test_ui_agent_bridge.py:3738-3740,tests/unit/pyqt_gui/test_parameter_form_manager_cross_window.py:178: assign removed block-state attributes.Removed names:
ancestor_objects=,resolve_field_inheritance,clear_mro_resolution_cache,invalidate_mro_cache_for_field,clear_extract_all_configs_cache,set_current_temp_global,_has_concrete_field_override,GROUP_ABBREVIATIONS_REGISTRY,LAZY_CONFIG_PREFIX,TimeTravelChangeSet.legacy_entries(nowstates_needing_navigation),ObjectStateRegistry._atomic_depth/_atomic_label/_atomic_triggering_scope/_atomic_snapshot_requested/_atomic_entry_signature/_deferred_invalidation_depth(readcurrent_atomic_block()), and six uncalled private functions listed in #41.Changed behaviour: failures that were swallowed now raise (
NoActiveContextError, constructor errors inconfig_contextandmerge_configs,get_base_global_config);get_context_layer_stack()returns aContextLayerStack, not a tuple of pairs;build_context_stacksets nothing until entered;atomicblocks belong to the thread or task that opened them. The four PR bodies list every item.Open point
tests/unit/test_compilation_session.py::test_compiler_streaming_config_snapshot_preserves_inherited_port(OpenHCS) failed once in a full-n 4unit run against this branch and not in the matching run againstmain. It passes alone and with its file. Not reproduced and not explained; first thing to check is thatbuild_context_stack(use_live=False)now reads only the saved global config.Not merged, tagged or released.
🤖 Generated with Claude Code