objectstate 3.0.4__tar.gz → 4.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {objectstate-3.0.4 → objectstate-4.0.0}/.github/badges/coverage.svg +1 -1
- objectstate-4.0.0/CHANGELOG.md +68 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/PKG-INFO +6 -6
- {objectstate-3.0.4 → objectstate-4.0.0}/README.md +5 -5
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/api/modules.rst +1 -1
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/architecture.rst +19 -20
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/examples/basic.rst +2 -2
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/index.rst +1 -1
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/quickstart.rst +4 -4
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/state_propagation.rst +40 -19
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/__init__.py +9 -13
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/context_manager.py +11 -23
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/docs/configuration_framework.rst +9 -19
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/docs/context_system.rst +11 -12
- objectstate-4.0.0/src/objectstate/global_config.py +96 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/lazy_factory.py +0 -59
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/object_state.py +10 -109
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/object_state_registry.py +21 -26
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/resolution_sources.py +9 -27
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/conftest.py +3 -6
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_callable_baseline.py +2 -2
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_construction_history.py +3 -3
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_context_failures_surface.py +3 -3
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_context_manager.py +0 -10
- objectstate-4.0.0/tests/test_global_config.py +37 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_object_state_restore.py +6 -6
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_object_state_update_instance.py +2 -2
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_one_field_resolution.py +19 -15
- objectstate-4.0.0/tests/test_one_holder_of_the_global_config.py +389 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_resolution_precedence_order.py +3 -3
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_resolved_values_follow_registered_ancestors.py +76 -129
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_scopeless_state_resolution.py +2 -2
- objectstate-3.0.4/src/objectstate/global_config.py +0 -174
- objectstate-3.0.4/tests/test_global_config.py +0 -68
- {objectstate-3.0.4 → objectstate-4.0.0}/.github/workflows/ci.yml +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/.github/workflows/coverage-pages.yml +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/.github/workflows/publish.yml +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/.gitignore +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/.readthedocs.yaml +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/BADGE_SETUP.md +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/LICENSE +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/.gitignore +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/Makefile +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/architectural_fixes.rst +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/archive/DOCUMENTATION_AUDIT.md +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/archive/DOCUMENTATION_FIXES.md +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/archive/README.md +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/conf.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/examples/atomic_operations.rst +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/examples/dual_axis.rst +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/examples/index.rst +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/examples/ui.rst +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/make.bat +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/performance/canonical-context-type-resolution-20261005.json +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/provenance.rst +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/state_management.rst +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/docs/undo_redo.rst +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/paper.bib +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/paper.md +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/plans/n_dimensional_inheritance.md +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/pyproject.toml +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/scripts/check_ruff_added_lines.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/scripts/release.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/scripts/update_and_release.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/scripts/update_version.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/scripts/verify_release_ready.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/codegen.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/collection_containers.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/config.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/construction_binding.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/docs/object_state_lifecycle.rst +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/dual_axis_resolver.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/edit_session.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/field_access.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/inspection.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/live_context_resolver.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/object_state_metadata.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/parameter_owner.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/parameter_structure.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/parametric_axes.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/placeholder.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/reified_generics.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/scope_key.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/snapshot_model.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/subfield_semantics.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/time_travel_profile.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/token_cache.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/transaction_checkpoint.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/ui_visibility.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/src/objectstate/value_semantics.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/__init__.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_atomic_block_context.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_authored_paths.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_codegen.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_config.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_field_access.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_history_persistence.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_inspection.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_integration.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_lazy_counterpart_registry.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_lazy_factory.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_live_context_resolver_answers.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_object_state_metadata_contracts.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_parameter_owner.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_parametric_axes.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_placeholder.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_registry_subscription.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_reified_generics.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_release_readiness.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_resolved_change_notifications.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_scope_key.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_subfield_semantics.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_timeline_projection.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_transaction_checkpoint.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_ui_visible_field_projection.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_undo_redo_cascade.py +0 -0
- {objectstate-3.0.4 → objectstate-4.0.0}/tests/test_value_semantics.py +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 82.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 82.78%"><title>coverage: 82.78%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="114" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="53" height="20" fill="#67ac09"/><rect width="114" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="865" y="150" fill-opacity=".8" filter="url(#blur)" textLength="430">82.78%</text><text x="865" y="150" fill-opacity=".3" textLength="430">82.78%</text></g><text x="865" y="140" textLength="430">82.78%</text></g></g></svg>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 4.0.0
|
|
4
|
+
|
|
5
|
+
The global config has one holder (#56). While a state whose object is of the
|
|
6
|
+
global config type is registered at the global scope (`scope_id=""`), that state
|
|
7
|
+
holds the global config: the live global config is the object its parameters
|
|
8
|
+
describe and the saved one is the object it last saved. While no such state is
|
|
9
|
+
registered, the object given to `set_global_config` is both. The global config is
|
|
10
|
+
one value per process; threads no longer each have their own.
|
|
11
|
+
|
|
12
|
+
Removed, and what to call instead:
|
|
13
|
+
|
|
14
|
+
- `ensure_global_config_context(config_type, config)` (also
|
|
15
|
+
`objectstate.lazy_factory.ensure_global_config_context`): call
|
|
16
|
+
`set_global_config(config_type, config)`. Not needed at all beside a registered
|
|
17
|
+
global state: registering the state makes its object the global config.
|
|
18
|
+
- `set_global_config_for_editing(config_type, config)`: call
|
|
19
|
+
`set_global_config(config_type, config)`.
|
|
20
|
+
- `set_live_global_config(config_type, config)`: with a registered global state,
|
|
21
|
+
edit the state (`state.update_parameter(path, value)`); the live global config
|
|
22
|
+
follows. Without one there is no separate live value; call `set_global_config`.
|
|
23
|
+
- `set_saved_global_config(config_type, config)`: with a registered global state,
|
|
24
|
+
save it (`state.mark_saved()`); to replace the object, live and saved, call
|
|
25
|
+
`state.update_object_instance(config)` or `set_global_config`. Without one, call
|
|
26
|
+
`set_global_config`.
|
|
27
|
+
- `get_current_global_config(config_type, use_live=...)`: call
|
|
28
|
+
`get_live_global_config(config_type)` or `get_saved_global_config(config_type)`.
|
|
29
|
+
- `clear_current_temp_global()`: nothing; no library code leaves the caller's
|
|
30
|
+
entered context any more. Leave a `config_context` block to leave its context.
|
|
31
|
+
- `objectstate.global_config.GlobalContextValues`: with a registered global state,
|
|
32
|
+
checkpoint the state (`ObjectStateTransactionCheckpoint.capture(state)` /
|
|
33
|
+
`.restore(state)`). Without one, keep `get_live_global_config(config_type)` and
|
|
34
|
+
put it back with `set_global_config`, or `forget_stored_global_config` when it
|
|
35
|
+
was `None`.
|
|
36
|
+
- `ObjectState.prepare_history_global_context()`: nothing; read
|
|
37
|
+
`get_live_global_config` / `get_saved_global_config`.
|
|
38
|
+
- `objectstate.lazy_factory.rebuild_lazy_config_with_new_global_reference(...)`:
|
|
39
|
+
nothing (it had no caller; it copied a lazy config and stored a global config as
|
|
40
|
+
a side effect).
|
|
41
|
+
- `ObjectStateRegistry._resolve_again_below` is now `resolve_again_below`.
|
|
42
|
+
|
|
43
|
+
Added: `set_global_config`, `forget_stored_global_config(config_type)` (a session
|
|
44
|
+
reset or a test that must leave nothing stored), `ObjectStateRegistry.global_config_state(config_type)`;
|
|
45
|
+
`get_live_global_config` and `get_saved_global_config` are exported from
|
|
46
|
+
`objectstate`.
|
|
47
|
+
|
|
48
|
+
Behaviour changes:
|
|
49
|
+
|
|
50
|
+
- `set_global_config` while a global state is registered replaces that state's
|
|
51
|
+
object (`update_object_instance`): unsaved edits of the global state are dropped
|
|
52
|
+
and its descendants are told at once. A call that only repeated what the state
|
|
53
|
+
already held must be deleted, not kept.
|
|
54
|
+
- `set_global_config` without a global state tells registered states at once (the
|
|
55
|
+
setters used to tell nobody until the next read).
|
|
56
|
+
- Registering a global state makes its object the global config at once, also when
|
|
57
|
+
nothing was stored before. Unregistering it makes the global config what
|
|
58
|
+
`set_global_config` last stored while no state held it (or the declared defaults).
|
|
59
|
+
- Every thread reads the same global config. A thread that used to see the declared
|
|
60
|
+
defaults because nothing was stored in it now sees the process's global config.
|
|
61
|
+
- Editing, replacing, restoring or time-travelling the global state no longer
|
|
62
|
+
leaves the caller's entered `config_context`: a block entered before the change
|
|
63
|
+
keeps the layers it was entered with until it is left.
|
|
64
|
+
- A global state that is not registered no longer changes the global config when it
|
|
65
|
+
is edited, saved, restored or given another object.
|
|
66
|
+
- `get_live_global_config` returns the object the registered global state builds
|
|
67
|
+
from its parameters (one object until the next change), not the instance the
|
|
68
|
+
application first passed in. Compare values, not identity.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: objectstate
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: Generic lazy dataclass configuration framework with dual-axis inheritance and contextvars-based resolution
|
|
5
5
|
Project-URL: Homepage, https://github.com/OpenHCSDev/objectstate
|
|
6
6
|
Project-URL: Documentation, https://objectstate.readthedocs.io
|
|
@@ -53,7 +53,7 @@ Description-Content-Type: text/markdown
|
|
|
53
53
|
- Y-Axis: Sibling inheritance within same context
|
|
54
54
|
- **Contextvars-Based**: Uses Python's `contextvars` for clean context management
|
|
55
55
|
- **UI Integration**: Placeholder text generation for configuration forms
|
|
56
|
-
- **
|
|
56
|
+
- **One global config**: held by the registered global state, or stored where no state exists
|
|
57
57
|
- **100% Generic**: No application-specific dependencies
|
|
58
58
|
- **Small dependency surface**: Uses ``python-introspect`` for callable and
|
|
59
59
|
dataclass analysis
|
|
@@ -112,7 +112,7 @@ resolution state.
|
|
|
112
112
|
When using the decorator pattern with `auto_create_decorator`, you need to establish the global configuration context for lazy resolution:
|
|
113
113
|
|
|
114
114
|
```python
|
|
115
|
-
from objectstate import
|
|
115
|
+
from objectstate import set_global_config
|
|
116
116
|
|
|
117
117
|
# After creating your global config instance
|
|
118
118
|
global_config = GlobalPipelineConfig(
|
|
@@ -121,15 +121,15 @@ global_config = GlobalPipelineConfig(
|
|
|
121
121
|
)
|
|
122
122
|
|
|
123
123
|
# REQUIRED: Establish global config context for lazy resolution
|
|
124
|
-
|
|
124
|
+
set_global_config(GlobalPipelineConfig, global_config)
|
|
125
125
|
|
|
126
126
|
# Now lazy configs can resolve from the global context
|
|
127
127
|
```
|
|
128
128
|
|
|
129
129
|
**Key differences:**
|
|
130
130
|
- `set_base_config_type(MyConfig)`: Sets the **type** (class) for the framework
|
|
131
|
-
- `
|
|
132
|
-
- Call `
|
|
131
|
+
- `set_global_config(GlobalConfig, instance)`: Sets the **instance** (concrete values) for resolution
|
|
132
|
+
- Call `set_global_config()` at application startup (GUI) or before pipeline execution
|
|
133
133
|
|
|
134
134
|
## Installation
|
|
135
135
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
- Y-Axis: Sibling inheritance within same context
|
|
17
17
|
- **Contextvars-Based**: Uses Python's `contextvars` for clean context management
|
|
18
18
|
- **UI Integration**: Placeholder text generation for configuration forms
|
|
19
|
-
- **
|
|
19
|
+
- **One global config**: held by the registered global state, or stored where no state exists
|
|
20
20
|
- **100% Generic**: No application-specific dependencies
|
|
21
21
|
- **Small dependency surface**: Uses ``python-introspect`` for callable and
|
|
22
22
|
dataclass analysis
|
|
@@ -75,7 +75,7 @@ resolution state.
|
|
|
75
75
|
When using the decorator pattern with `auto_create_decorator`, you need to establish the global configuration context for lazy resolution:
|
|
76
76
|
|
|
77
77
|
```python
|
|
78
|
-
from objectstate import
|
|
78
|
+
from objectstate import set_global_config
|
|
79
79
|
|
|
80
80
|
# After creating your global config instance
|
|
81
81
|
global_config = GlobalPipelineConfig(
|
|
@@ -84,15 +84,15 @@ global_config = GlobalPipelineConfig(
|
|
|
84
84
|
)
|
|
85
85
|
|
|
86
86
|
# REQUIRED: Establish global config context for lazy resolution
|
|
87
|
-
|
|
87
|
+
set_global_config(GlobalPipelineConfig, global_config)
|
|
88
88
|
|
|
89
89
|
# Now lazy configs can resolve from the global context
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
**Key differences:**
|
|
93
93
|
- `set_base_config_type(MyConfig)`: Sets the **type** (class) for the framework
|
|
94
|
-
- `
|
|
95
|
-
- Call `
|
|
94
|
+
- `set_global_config(GlobalConfig, instance)`: Sets the **instance** (concrete values) for resolution
|
|
95
|
+
- Call `set_global_config()` at application startup (GUI) or before pipeline execution
|
|
96
96
|
|
|
97
97
|
## Installation
|
|
98
98
|
|
|
@@ -12,7 +12,7 @@ Configuration and lazy types
|
|
|
12
12
|
Builds an application-specific configuration decorator and related lazy
|
|
13
13
|
types.
|
|
14
14
|
|
|
15
|
-
``config_context`` and ``
|
|
15
|
+
``config_context`` and ``set_global_config``
|
|
16
16
|
Install concrete configuration used during resolution.
|
|
17
17
|
|
|
18
18
|
``set_base_config_type``
|
|
@@ -487,8 +487,8 @@ How It Works
|
|
|
487
487
|
Global Configuration Context
|
|
488
488
|
-----------------------------
|
|
489
489
|
|
|
490
|
-
Understanding set_base_config_type vs
|
|
491
|
-
|
|
490
|
+
Understanding set_base_config_type vs set_global_config
|
|
491
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
492
492
|
|
|
493
493
|
The framework provides two related but distinct functions:
|
|
494
494
|
|
|
@@ -507,29 +507,27 @@ Sets the **type** (class) for the framework:
|
|
|
507
507
|
* Required for type validation and registry
|
|
508
508
|
* Does not set any concrete values
|
|
509
509
|
|
|
510
|
-
**
|
|
510
|
+
**set_global_config(config_type: Type, instance: Any)**
|
|
511
511
|
|
|
512
512
|
Sets the **instance** (concrete values) for resolution:
|
|
513
513
|
|
|
514
514
|
.. code-block:: python
|
|
515
515
|
|
|
516
|
-
from objectstate import
|
|
516
|
+
from objectstate import set_global_config
|
|
517
517
|
|
|
518
518
|
global_config = GlobalPipelineConfig(
|
|
519
519
|
num_workers=8,
|
|
520
520
|
output_dir="/data"
|
|
521
521
|
)
|
|
522
522
|
|
|
523
|
-
|
|
523
|
+
set_global_config(GlobalPipelineConfig, global_config)
|
|
524
524
|
|
|
525
|
-
* Call after creating global config instance
|
|
526
|
-
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
config: that state stores the live and the saved instance itself
|
|
532
|
-
(see :doc:`state_propagation`)
|
|
525
|
+
* Call after creating global config instance, in a process that registers no
|
|
526
|
+
``ObjectState`` for it (a script, a compile worker, an execution server)
|
|
527
|
+
* One value per process: every thread reads it
|
|
528
|
+
* Not needed when an ``ObjectState`` of the global config is registered at scope
|
|
529
|
+
``""``: that state's object is the global config, live and saved
|
|
530
|
+
(see :doc:`state_propagation`). Calling it then replaces that state's object.
|
|
533
531
|
|
|
534
532
|
**Key Differences:**
|
|
535
533
|
|
|
@@ -539,7 +537,7 @@ Sets the **instance** (concrete values) for resolution:
|
|
|
539
537
|
|
|
540
538
|
* - Aspect
|
|
541
539
|
- set_base_config_type
|
|
542
|
-
-
|
|
540
|
+
- set_global_config
|
|
543
541
|
* - What it sets
|
|
544
542
|
- Type (class)
|
|
545
543
|
- Instance (concrete values)
|
|
@@ -549,17 +547,18 @@ Sets the **instance** (concrete values) for resolution:
|
|
|
549
547
|
* - Purpose
|
|
550
548
|
- Type registration
|
|
551
549
|
- Enable lazy resolution
|
|
552
|
-
* -
|
|
553
|
-
-
|
|
554
|
-
-
|
|
550
|
+
* - Scope
|
|
551
|
+
- One per process
|
|
552
|
+
- One per process
|
|
555
553
|
|
|
556
554
|
Thread Safety
|
|
557
555
|
-------------
|
|
558
556
|
|
|
559
|
-
|
|
557
|
+
Threads see:
|
|
560
558
|
|
|
561
|
-
1. **
|
|
559
|
+
1. **One global config per process**, read by every thread
|
|
562
560
|
2. **ContextVars** for temporary contexts
|
|
563
561
|
3. **Immutable frozen dataclasses** (when using ``frozen=True``)
|
|
564
562
|
|
|
565
|
-
|
|
563
|
+
Entered contexts are per thread or task. The global config and the registry are shared
|
|
564
|
+
and not synchronised: keep edits in one thread.
|
|
@@ -174,7 +174,7 @@ Using the decorator pattern for automatic field injection and lazy class generat
|
|
|
174
174
|
.. code-block:: python
|
|
175
175
|
|
|
176
176
|
from dataclasses import dataclass
|
|
177
|
-
from objectstate import auto_create_decorator,
|
|
177
|
+
from objectstate import auto_create_decorator, set_global_config
|
|
178
178
|
|
|
179
179
|
# Create global config with auto_create_decorator
|
|
180
180
|
@auto_create_decorator
|
|
@@ -213,7 +213,7 @@ Using the decorator pattern for automatic field injection and lazy class generat
|
|
|
213
213
|
)
|
|
214
214
|
|
|
215
215
|
# REQUIRED: Establish global context for lazy resolution
|
|
216
|
-
|
|
216
|
+
set_global_config(GlobalPipelineConfig, global_config)
|
|
217
217
|
|
|
218
218
|
# Now you can access injected configs
|
|
219
219
|
print(f"Database: {global_config.database_config.host}:{global_config.database_config.port}")
|
|
@@ -19,7 +19,7 @@ Key Features
|
|
|
19
19
|
|
|
20
20
|
* **Contextvars-Based**: Uses Python's ``contextvars`` for clean context management
|
|
21
21
|
* **UI Integration**: Placeholder text generation for configuration forms
|
|
22
|
-
* **
|
|
22
|
+
* **One global config**: held by the registered global state, or stored where no state exists
|
|
23
23
|
* **100% Generic**: No application-specific dependencies
|
|
24
24
|
* **Small dependency surface**: Uses ``python-introspect`` for signature and
|
|
25
25
|
dataclass analysis
|
|
@@ -174,7 +174,7 @@ When using the decorator pattern with ``auto_create_decorator``, you need to est
|
|
|
174
174
|
|
|
175
175
|
from objectstate import (
|
|
176
176
|
auto_create_decorator,
|
|
177
|
-
|
|
177
|
+
set_global_config,
|
|
178
178
|
)
|
|
179
179
|
from dataclasses import dataclass
|
|
180
180
|
|
|
@@ -192,7 +192,7 @@ When using the decorator pattern with ``auto_create_decorator``, you need to est
|
|
|
192
192
|
)
|
|
193
193
|
|
|
194
194
|
# REQUIRED: Establish global config context
|
|
195
|
-
|
|
195
|
+
set_global_config(GlobalPipelineConfig, global_config)
|
|
196
196
|
|
|
197
197
|
# Now lazy configs can resolve from the global context
|
|
198
198
|
|
|
@@ -200,8 +200,8 @@ Understanding the Difference
|
|
|
200
200
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
201
201
|
|
|
202
202
|
* ``set_base_config_type(MyConfig)``: Sets the **type** (class) for the framework
|
|
203
|
-
* ``
|
|
204
|
-
* Call ``
|
|
203
|
+
* ``set_global_config(GlobalConfig, instance)``: Sets the **instance** (concrete values) for resolution
|
|
204
|
+
* Call ``set_global_config()`` at application startup (GUI) or before pipeline execution
|
|
205
205
|
|
|
206
206
|
Complete Example
|
|
207
207
|
----------------
|
|
@@ -132,7 +132,7 @@ registered state than before, every field is.
|
|
|
132
132
|
|
|
133
133
|
So no caller announces a change. Registering, unregistering or replacing an ancestor,
|
|
134
134
|
editing, saving, restoring, undoing or redoing an ancestor's value, and replacing the
|
|
135
|
-
global config with ``
|
|
135
|
+
global config with ``set_global_config`` all reach
|
|
136
136
|
a state on its next read, whether the state is registered or not. A registered state
|
|
137
137
|
is also told at once, so its subscribers hear of the change without a read; a state
|
|
138
138
|
that is not registered is referenced by nothing and tells its subscribers on its next
|
|
@@ -144,26 +144,47 @@ Limits:
|
|
|
144
144
|
``ObjectState.to_object`` returns the same function object for a state that wraps a
|
|
145
145
|
plain function, so states registered below a function-backed state follow its edits
|
|
146
146
|
only through the registry (that is, when they are registered themselves).
|
|
147
|
-
-
|
|
148
|
-
thread that
|
|
149
|
-
|
|
147
|
+
- Nothing is synchronised. Every thread reads the same registry and the same global
|
|
148
|
+
config, so a thread that edits while another reads must be kept apart by the
|
|
149
|
+
application (in a GUI: edit in the GUI thread).
|
|
150
150
|
|
|
151
151
|
The state of the global config
|
|
152
152
|
------------------------------
|
|
153
153
|
|
|
154
|
-
The global config is
|
|
155
|
-
|
|
156
|
-
``
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
154
|
+
The global config is the value every resolution starts from. There is one per config
|
|
155
|
+
type per process, read as edited (live) and as saved, and one thing holds it
|
|
156
|
+
(``objectstate.global_config``):
|
|
157
|
+
|
|
158
|
+
- The state registered at the global scope (``scope_id=""``) whose object is of that
|
|
159
|
+
type. The live global config is the object its parameters describe
|
|
160
|
+
(``object_for_resolution()``) and the saved global config is the object it last
|
|
161
|
+
saved. Nothing copies them anywhere: ``get_live_global_config``,
|
|
162
|
+
``get_saved_global_config``, ``config_context`` and every state's resolution ask the
|
|
163
|
+
registered state. So an edit, a save, a restore, a replaced object, an undo or a
|
|
164
|
+
redo of that state is that change of the global config, and its registered
|
|
165
|
+
descendants are told at once.
|
|
166
|
+
- While no such state is registered: the object given to ``set_global_config``. Nothing
|
|
167
|
+
edits it, so the live and the saved read are the same object. A process without
|
|
168
|
+
states (a compile worker, an execution server, a script) uses only this.
|
|
169
|
+
|
|
170
|
+
Registering the global state is all it takes to make its object the global config; no
|
|
171
|
+
``set_global_config`` call is needed beside it. Unregistering it (or travelling to a
|
|
172
|
+
point in history where it was not registered) ends that at once: the global config is
|
|
173
|
+
then again what ``set_global_config`` last stored while no state held it, or the
|
|
174
|
+
declared defaults.
|
|
175
|
+
|
|
176
|
+
``set_global_config(config_type, config)`` is the one way to replace the global config
|
|
177
|
+
from outside. While a state holds the global config the call is
|
|
178
|
+
``state.update_object_instance(config)``: the state takes the new object as its live
|
|
179
|
+
and saved value (unsaved edits are dropped) and its descendants and their subscribers
|
|
180
|
+
follow. A stored object that disagrees with a registered global state cannot be made.
|
|
181
|
+
|
|
182
|
+
Threads play no part. The registry is one per process and so is the stored object, so
|
|
183
|
+
a worker thread reads the global config the GUI thread edits, with or without
|
|
184
|
+
``spawn_thread_with_context`` (which copies the entered ``config_context`` layers, not
|
|
185
|
+
the global config, into the new thread).
|
|
186
|
+
|
|
187
|
+
A ``config_context`` block entered before the global config changes keeps the layers
|
|
188
|
+
it was entered with until it is left.
|
|
189
|
+
|
|
169
190
|
|
|
@@ -9,7 +9,7 @@ Key Features:
|
|
|
9
9
|
- Dual-axis inheritance (context hierarchy + sibling inheritance)
|
|
10
10
|
- Contextvars-based context management
|
|
11
11
|
- Placeholder text generation for UI
|
|
12
|
-
-
|
|
12
|
+
- One global config per process, held by the registered global state
|
|
13
13
|
|
|
14
14
|
Quick Start:
|
|
15
15
|
>>> from objectstate import (
|
|
@@ -47,7 +47,7 @@ Modules:
|
|
|
47
47
|
- dual_axis_resolver: Dual-axis inheritance resolver
|
|
48
48
|
- context_manager: Contextvars-based context management
|
|
49
49
|
- placeholder: Placeholder text generation for UI
|
|
50
|
-
- global_config:
|
|
50
|
+
- global_config: The global config and the one thing that holds it
|
|
51
51
|
- config: Framework configuration (pluggable types and behaviors)
|
|
52
52
|
"""
|
|
53
53
|
|
|
@@ -58,7 +58,6 @@ from objectstate.lazy_factory import (
|
|
|
58
58
|
register_lazy_type_mapping,
|
|
59
59
|
get_base_type_for_lazy,
|
|
60
60
|
get_lazy_type_for_base,
|
|
61
|
-
ensure_global_config_context,
|
|
62
61
|
# Constructor patching for code execution
|
|
63
62
|
patch_lazy_constructors,
|
|
64
63
|
# Global config type checking
|
|
@@ -86,7 +85,6 @@ from objectstate.dual_axis_resolver import (
|
|
|
86
85
|
from objectstate.context_manager import (
|
|
87
86
|
config_context,
|
|
88
87
|
get_current_temp_global,
|
|
89
|
-
clear_current_temp_global,
|
|
90
88
|
merge_configs,
|
|
91
89
|
extract_all_configs,
|
|
92
90
|
get_base_global_config,
|
|
@@ -111,10 +109,8 @@ from objectstate.placeholder import LazyDefaultPlaceholderService
|
|
|
111
109
|
|
|
112
110
|
# Global config
|
|
113
111
|
from objectstate.global_config import (
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
set_saved_global_config,
|
|
117
|
-
set_live_global_config,
|
|
112
|
+
set_global_config,
|
|
113
|
+
forget_stored_global_config,
|
|
118
114
|
get_saved_global_config,
|
|
119
115
|
get_live_global_config,
|
|
120
116
|
)
|
|
@@ -155,7 +151,6 @@ __all__ = [
|
|
|
155
151
|
'register_lazy_type_mapping',
|
|
156
152
|
'get_base_type_for_lazy',
|
|
157
153
|
'get_lazy_type_for_base',
|
|
158
|
-
'ensure_global_config_context',
|
|
159
154
|
# Constructor patching for code execution
|
|
160
155
|
'patch_lazy_constructors',
|
|
161
156
|
# Global config type checking
|
|
@@ -172,7 +167,6 @@ __all__ = [
|
|
|
172
167
|
# Context
|
|
173
168
|
'config_context',
|
|
174
169
|
'get_current_temp_global',
|
|
175
|
-
'clear_current_temp_global',
|
|
176
170
|
'merge_configs',
|
|
177
171
|
'extract_all_configs',
|
|
178
172
|
'get_base_global_config',
|
|
@@ -182,8 +176,10 @@ __all__ = [
|
|
|
182
176
|
# Placeholder
|
|
183
177
|
'LazyDefaultPlaceholderService',
|
|
184
178
|
# Global config
|
|
185
|
-
'
|
|
186
|
-
'
|
|
179
|
+
'set_global_config',
|
|
180
|
+
'forget_stored_global_config',
|
|
181
|
+
'get_live_global_config',
|
|
182
|
+
'get_saved_global_config',
|
|
187
183
|
# Configuration
|
|
188
184
|
'set_base_config_type',
|
|
189
185
|
'get_base_config_type',
|
|
@@ -210,7 +206,7 @@ __all__ = [
|
|
|
210
206
|
'ObjectStateEditSession',
|
|
211
207
|
]
|
|
212
208
|
|
|
213
|
-
__version__ = "
|
|
209
|
+
__version__ = "4.0.0"
|
|
214
210
|
__author__ = 'OpenHCS Team'
|
|
215
211
|
__description__ = 'Generic configuration framework for lazy dataclass resolution'
|
|
216
212
|
|
|
@@ -182,7 +182,7 @@ def config_context(
|
|
|
182
182
|
mask_with_none: If True, None values override/mask base config values, and
|
|
183
183
|
field resolution does not look at any layer outside this one.
|
|
184
184
|
If False (default), None values are ignored (normal inheritance).
|
|
185
|
-
Use True when editing GlobalPipelineConfig to mask
|
|
185
|
+
Use True when editing GlobalPipelineConfig to mask the
|
|
186
186
|
loaded instance with static class defaults.
|
|
187
187
|
use_live_global: If True (default), use LIVE global config (UI sees unsaved edits).
|
|
188
188
|
If False, use SAVED global config (compiler sees saved values).
|
|
@@ -665,20 +665,21 @@ def get_base_global_config(use_live: bool = True):
|
|
|
665
665
|
"""
|
|
666
666
|
Get the global config a context starts from when none is active.
|
|
667
667
|
|
|
668
|
-
This is the config
|
|
669
|
-
|
|
670
|
-
base config type is set or its defaults cannot be constructed.
|
|
668
|
+
This is the global config of the base config type (see ``objectstate.global_config``),
|
|
669
|
+
or an instance of that type holding its declared defaults when there is none.
|
|
670
|
+
Raises when no base config type is set or its defaults cannot be constructed.
|
|
671
671
|
|
|
672
672
|
Args:
|
|
673
673
|
use_live: If True (default), return LIVE config (UI sees unsaved edits).
|
|
674
674
|
If False, return SAVED config (compiler sees saved values).
|
|
675
675
|
"""
|
|
676
676
|
from objectstate.config import get_base_config_type
|
|
677
|
-
from objectstate.global_config import
|
|
677
|
+
from objectstate.global_config import get_live_global_config, get_saved_global_config
|
|
678
678
|
|
|
679
679
|
base_config_type = get_base_config_type()
|
|
680
680
|
|
|
681
|
-
|
|
681
|
+
read = get_live_global_config if use_live else get_saved_global_config
|
|
682
|
+
current_global = read(base_config_type)
|
|
682
683
|
if current_global is not None:
|
|
683
684
|
return current_global
|
|
684
685
|
|
|
@@ -698,17 +699,6 @@ def get_current_temp_global():
|
|
|
698
699
|
return current_temp_global.get(None)
|
|
699
700
|
|
|
700
701
|
|
|
701
|
-
def clear_current_temp_global():
|
|
702
|
-
"""
|
|
703
|
-
Leave the active context without restoring an outer one.
|
|
704
|
-
|
|
705
|
-
After this no context is active: lazy fields fall back to their declared
|
|
706
|
-
defaults until the next ``config_context`` is entered.
|
|
707
|
-
"""
|
|
708
|
-
current_temp_global.set(None)
|
|
709
|
-
context_layer_stack.set(NO_LAYERS)
|
|
710
|
-
|
|
711
|
-
|
|
712
702
|
# Utility functions for debugging and introspection
|
|
713
703
|
|
|
714
704
|
def get_context_info() -> Dict[str, Any]:
|
|
@@ -899,12 +889,10 @@ def spawn_thread_with_context(
|
|
|
899
889
|
):
|
|
900
890
|
"""Spawn a thread with current context propagated.
|
|
901
891
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
don't automatically propagate to spawned threads. This utility wraps
|
|
907
|
-
threading.Thread to propagate the current context using contextvars.copy_context().
|
|
892
|
+
A new thread starts with no ``config_context`` entered. This starts one with the
|
|
893
|
+
layers the caller has entered (``contextvars.copy_context()``), so plain lazy
|
|
894
|
+
objects read in it what they read in the caller. The global config and the
|
|
895
|
+
registry are one per process and need no copying.
|
|
908
896
|
|
|
909
897
|
Args:
|
|
910
898
|
target: The function to run in thread
|
|
@@ -126,7 +126,7 @@ Usage Example
|
|
|
126
126
|
|
|
127
127
|
from objectstate import (
|
|
128
128
|
auto_create_decorator, config_context,
|
|
129
|
-
|
|
129
|
+
set_global_config, LazyDataclassFactory
|
|
130
130
|
)
|
|
131
131
|
|
|
132
132
|
# Create global config with concrete values
|
|
@@ -139,7 +139,7 @@ Usage Example
|
|
|
139
139
|
)
|
|
140
140
|
|
|
141
141
|
# Establish global context
|
|
142
|
-
|
|
142
|
+
set_global_config(GlobalAppConfig, global_config)
|
|
143
143
|
|
|
144
144
|
# Create app config with lazy configs for inheritance
|
|
145
145
|
app_config = AppConfig(
|
|
@@ -169,28 +169,18 @@ The ``objectstate`` package contains:
|
|
|
169
169
|
UI placeholder generation showing inherited values
|
|
170
170
|
|
|
171
171
|
**global_config.py**
|
|
172
|
-
|
|
172
|
+
The global config and the one thing that holds it
|
|
173
173
|
|
|
174
174
|
**object_state.py**
|
|
175
175
|
ObjectState and ObjectStateRegistry for UI state management
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
Global Config and Editing
|
|
178
|
+
-------------------------
|
|
179
179
|
|
|
180
|
-
The
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
1. **Initialization**: Context set during application startup
|
|
185
|
-
2. **Live Updates**: Context synchronized with form edits in real-time
|
|
186
|
-
3. **Restoration**: Original context restored on cancel
|
|
187
|
-
4. **Cleanup**: Context cleared on window close
|
|
188
|
-
|
|
189
|
-
**UI Integration**
|
|
190
|
-
|
|
191
|
-
The configuration window implements special handling for global configuration to ensure thread-local context stays synchronized with form edits. Each field change triggers immediate context update, ensuring placeholder values in other windows update immediately.
|
|
192
|
-
|
|
193
|
-
When users cancel config editing, the original context is restored to prevent context pollution from experimental changes.
|
|
180
|
+
The global config is one value per process. While an ``ObjectState`` of it is
|
|
181
|
+
registered at scope ``""`` that state holds it: a form edit is an edit of the state
|
|
182
|
+
and is the live global config at once, cancel is ``restore_saved`` and save is
|
|
183
|
+
``mark_saved``. Nothing else is synchronised or restored.
|
|
194
184
|
|
|
195
185
|
**Implementation Details**
|
|
196
186
|
|