Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions docs/provenance.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ Get the source scope_id and type for an inherited field value:
resolve_with_provenance()
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Resolve a field value AND find its provenance source in ONE walk:
Resolve a field in the active context. This is the one resolution: attribute access on a
lazy config returns its ``value``, and ``ObjectState`` stores all three parts.

.. code-block:: python

Expand All @@ -109,11 +110,21 @@ Resolve a field value AND find its provenance source in ONE walk:

**Returns:**

A ``ResolvedField`` named tuple:

* ``value``: The resolved field value (may be ``None``)
* ``source_scope``: The scope_id where the value was found (or ``None``)
* ``source_type``: The type that provided the value (or ``None``)
* ``source_scope``: The scope id of the layer that supplied the value. ``None`` for a layer
entered with ``config_context(obj)`` without a ``scope_id``.
* ``source_type``: The type the value was read from: the container type or one of its bases.

When no layer holds a value, ``value`` is the declared default and the source names the
outermost layer holding the nearest type in the MRO (``None`` when no layer holds one).

**Performance:** Single walk instead of separate resolve + provenance calls.
Raises ``NoActiveContextError`` when no context is active.

**Caching:** the answer is kept on the layer stack it was computed from
(``ContextLayerStack.resolved_fields``) and is dropped with that stack. Entering or leaving a
context, in this thread or another, never clears another context's answers.

get_field_provenance()
~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -142,13 +153,14 @@ The provenance system uses the context layer stack for tracking:

from objectstate.context_manager import get_context_layer_stack

# Get the current layer stack for provenance tracking
layers = get_context_layer_stack()
# Returns: [(scope_id, obj), ...] tuples

# Used by get_field_provenance() to determine which scope provided a resolved value
stack = get_context_layer_stack()
for layer in stack.layers: # outermost first
print(layer.scope_id, layer.obj)

The context layer stack tracks ``(scope_id, obj)`` tuples for provenance tracking, parallel to the merged config. It preserves hierarchy for inheritance source lookup.
The stack holds one ``ContextLayer`` for every object entered with ``config_context()``, with or
without a scope id, above the global config the context started from (scope ``""``). It is the
value ``resolve_with_provenance()`` reads. A layer entered with ``mask_with_none=True`` hides the
layers outside it.

Usage Examples
--------------
Expand Down Expand Up @@ -285,7 +297,7 @@ How Provenance Works

The provenance system works through these mechanisms:

1. **Context Layer Stack**: The ``config_context()`` manager tracks ``(scope_id, obj)`` tuples as contexts are pushed.
1. **Context Layer Stack**: The ``config_context()`` manager adds a ``ContextLayer`` for every object entered.

2. **Dual-Axis Resolution with Provenance**: When resolving inherited fields, ``resolve_with_provenance()`` walks both the MRO and context hierarchy, returning the first concrete value found along with its source information.

Expand Down
10 changes: 4 additions & 6 deletions src/objectstate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,14 @@

# Resolver
from objectstate.dual_axis_resolver import (
resolve_field_inheritance,
_has_concrete_field_override,
ResolvedField,
resolve_with_provenance,
)

# Context
from objectstate.context_manager import (
config_context,
get_current_temp_global,
set_current_temp_global,
clear_current_temp_global,
merge_configs,
extract_all_configs,
Expand Down Expand Up @@ -168,12 +167,11 @@
'is_global_config_type',
'is_global_config_instance',
# Resolver
'resolve_field_inheritance',
'_has_concrete_field_override',
'ResolvedField',
'resolve_with_provenance',
# Context
'config_context',
'get_current_temp_global',
'set_current_temp_global',
'clear_current_temp_global',
'merge_configs',
'extract_all_configs',
Expand Down
Loading