pygx.contextual¶
Context-scoped value override for PyGX.
contextual
¶
Context-scoped value override for PyGX.
pygx.contextual provides a context-local (contextvars) stack of
variable bindings that inner code can read via
pg.contextual.get. Bindings are installed by
pg.contextual.override (a context manager);
concurrent asyncio tasks each see their own bindings, context-copying
mechanisms (asyncio.to_thread, anyio portals) carry them automatically,
and a plain threading.Thread receives them when the callable is wrapped
with pg.contextual.with_override. The module
sits between pg.topology (for MISSING_VALUE) and the
heavier pg.symbolic layer.
The symbols below are intentionally not re-exported at the top of pg.* —
access them as pg.contextual.<name>.
Override
dataclass
¶
Value marker for a contextual override of an attribute.
Attributes:
| Name | Type | Description |
|---|---|---|
value |
Any
|
The overriding value. |
cascade |
bool
|
If True, this override applies to both the current scope and
nested scopes, meaning it takes precedence over all nested
|
override_attrs |
bool
|
If True, this override also applies to attributes that already have values. |
Placeholder
¶
Placeholder(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: ValueFromParentChain, Extension
Value marker for a contextual attribute on a ContextualObject.
Used as the default value of a field on a pg.ContextualObject
subclass. At access time, the attribute resolves by walking the
chain of containing ContextualObjects, then consulting any
pg.contextual.override bindings in scope, and finally the
marker's own default (if given); if none applies, reading the
attribute raises AttributeError.
Example:
class A(pg.ContextualObject):
y: Any = pg.contextual.Placeholder()
z: Any = pg.contextual.Placeholder(default=0)
class B(pg.ContextualObject):
y: int
a: A
a = A()
assert a.z == 0 # Falls back to the marker's default.
b = B(y=2, a=a)
assert a.y == 2 # Resolved from the containing B.
Source code in pygx/symbolic/_object.py
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 | |
all
¶
Returns all values provided from pg.contextual.override in scope.
Source code in pygx/contextual/_contextual.py
current_overrides
¶
current_overrides() -> dict[str, Override]
Returns the Override markers in scope.
A snapshot of the merged override dict the current context sees —
marker objects included (unlike all(), which unwraps to plain
values and drops cascade / override_attrs). Re-installing the
snapshot elsewhere is spelled override(**current_overrides());
with_override and pg.concurrent.iter_sync both capture through
this.
Source code in pygx/contextual/_contextual.py
get
¶
Returns the value of a variable defined in pg.contextual.override.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
var_name
|
str
|
Name of the variable to look up in the current context's override scope. |
required |
default
|
Any
|
Value to return when the variable is not in scope. If omitted,
a missing variable raises |
RAISE_IF_HAS_ERROR
|
Returns:
| Type | Description |
|---|---|
Any
|
The overridden value, or |
Any
|
scope. |
Raises:
| Type | Description |
|---|---|
KeyError
|
The variable is not in scope and no |
Source code in pygx/contextual/_contextual.py
get_override
¶
get_override(var_name: str) -> Override | None
Returns the in-scope Override marker.
Looks up var_name in the current context's override scope; returns
None when the variable is not overridden.
Source code in pygx/contextual/_contextual.py
override
¶
override(
*, cascade: bool = False, override_attrs: bool = False, **variables: Any
) -> ContextManager[dict[str, Override]]
Context manager to provide contextual values under a scope.
Contextual value overrides are per-context (contextvars): concurrent
asyncio tasks each see their own scopes, and mechanisms that copy the
context into a worker (asyncio.to_thread, anyio portals) carry them
automatically. A plain threading.Thread starts with a fresh context —
wrap the user function with pg.contextual.with_override(func) to
propagate the current overrides there.
Known limitation (inherent to the contextvars idiom, shared with
e.g. asyncio.timeout): the scope must exit in the same
contextvars.Context it entered. An async generator holding an open
scope across a yield that is finalized by the event loop's
shutdown_asyncgens exits in a different context and raises
ValueError from the token reset.
(pg.concurrent.iter_sync is not subject to this: it drives the whole
iteration — every step and the close — in one task, #837.)
Example:
from typing import Any
import pygx as pg
class A(pg.ContextualObject):
x: int
y: Any = pg.contextual.Placeholder()
a = A(x=1)
with pg.contextual.override(y=2):
assert a.y == 2 # `y` has no value; the override supplies it.
assert a.x == 1 # `x` has a value; not affected by default.
with pg.contextual.override(x=3, override_attrs=True):
assert a.x == 3 # Overrides even attributes with values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cascade
|
bool
|
If True, this override applies to both the current scope and nested scopes, taking precedence over all nested overrides for the listed variables. |
False
|
override_attrs
|
bool
|
If True, the override also applies to attributes that already have explicit values. Otherwise overridden variables are only consulted for contextual attributes whose values are not present. |
False
|
**variables
|
Any
|
Key/values as overrides for contextual attributes. |
{}
|
Returns:
| Type | Description |
|---|---|
ContextManager[dict[str, Override]]
|
A context manager that installs the overrides for its scope, yielding |
ContextManager[dict[str, Override]]
|
a dict of attribute names to their |
ContextManager[dict[str, Override]]
|
markers. |
Source code in pygx/contextual/_contextual.py
with_override
¶
Wraps a user function so it carries the current contextual overrides.
The wrapped function may be called from another thread; the overrides
active at wrap-time will be re-installed for the duration of each call.
Coroutine functions are wrapped with an async wrapper. (Needed only where
the contextvars.Context is NOT already carried over: a plain
threading.Thread or an executor's worker thread. asyncio tasks and
asyncio.to_thread copy the context by themselves.)
Example:
import threading
from typing import Any
import pygx as pg
class A(pg.ContextualObject):
y: Any = pg.contextual.Placeholder()
a, seen = A(), []
def read(obj):
seen.append(obj.y)
with pg.contextual.override(y=3):
# A plain thread starts with a fresh context, so `read` must be
# wrapped to see the overrides there.
t = threading.Thread(target=pg.contextual.with_override(read), args=(a,))
t.start()
t.join()
assert seen == [3]
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[..., Any]
|
The user function to be wrapped. |
required |
Returns:
| Type | Description |
|---|---|
Callable[..., Any]
|
A wrapper function that re-installs the captured overrides on each |
Callable[..., Any]
|
invocation, suitable for handing off to another thread. |
Source code in pygx/contextual/_contextual.py
options: show_root_heading: false show_root_toc_entry: false members_order: source heading_level: 2