ContextualValue#

class penzai.core.context.ContextualValue[source]#

Bases: Generic[T]

A global value which can be modified in a scoped context.

Mutable global values can be difficult to reason about, but can also be very convenient for reducing boilerplate and focusing on the important parts if they are used carefully. Moreover, it’s common to only want to change a global value within a particular delimited scope, without affecting anything beyond that scope.

This class manages a restricted form of global value access to support this use case: read access is allowed anywhere, but updates are only “local”, in the sense that they apply for the duration of a delimited scope and don’t affect anything beyond that scope. (This is similar to a Reader monad in functional programming languages.)

If you have many values to set, or you want to set them conditionally, consider using a contextlib.ExitStack, e.g.

with contextlib.ExitStack() as stack:
  stack.enter_context(contextual_one.set_scoped(True))
  if foo:
    stack.enter_context(contextual_two.set_scoped(some_value))
  # logic that uses those values
# all contexts are exited here

or just

stack = contextlib.ExitStack()
stack.enter_context(contextual_one.set_scoped(True))
if foo:
  stack.enter_context(contextual_two.set_scoped(some_value))
# ... do something ...
# ... then eventually:
stack.close()

If you would like to modify contextual values at the global level (e.g. for interactive use), you can call enable_interactive_context in this module and then use set_interactive instead of set_scoped; this is actually implemented using an internally-managed global ExitStack.

Variables:
  • __module__ (str | None) – The module where this contextual value is defined.

  • __qualname__ (str | None) – The fully-qualified name of the contextual value within its module.

  • _raw_value (T) – The current value. Should not be used directly; use get to get it or set_scoped to set it within a context.

Methods

__init__(initial_value[, module, qualname])

Creates a contextual value.

get()

Retrieves the current value.

set_interactive(new_value)

Sets the value in interactive mode.

set_scoped(new_value)

Returns a context manager in which the value will be modified.

__init__(initial_value: T, module: str | None = None, qualname: str | None = None)[source]#

Creates a contextual value.

Parameters:
  • initial_value – A value to use outside of set_scoped contexts.

  • module – The module where this contextual value is defined. Usually this will be __name__ (for whichever model it was defined in).

  • qualname – The fully-qualified name of the contextual value within its module.

get() T[source]#

Retrieves the current value.

set_interactive(new_value: T) None[source]#

Sets the value in interactive mode.

This function should ONLY be called in an interactive setting (e.g. a Colab notebook or repl), and must be called after enable_interactive_context but before disable_interactive_context.

disable_interactive_context will restore all contexts set using set_interactive to their original states.

Parameters:

new_value – The new value to use in this context.

Raises:

RuntimeError – If interactive contexts have not been enabled.

set_scoped(new_value: T)[source]#

Returns a context manager in which the value will be modified.

This allows scoped modifications to the value, using something like

contextual = ContextualValue(10)

with contextual.set_scoped(3):
  v1 = contextual.get()  # v1 is 3

v2 = contextual.get()  # v2 is 10

If you want to conditionally set the value, consider using contextlib.ExitStack:

with contextlib.ExitStack() as exit_stack:
  if condition:
    exit_stack.enter_context(contextual.set_scoped(3))

  # use contextual.get() here
Parameters:

new_value – The new value to use in this context.

Returns:

A context manager in which new_value is set.