Skip to content

Add raw wrapper to assign ref-like values without resolving #1090

@philippjfr

Description

@philippjfr

Summary

When assigning values to Parameters, Param automatically resolves ref-like objects (e.g. Parameters, reactive expressions, async generators). Sometimes, however, users need to store these ref-like values themselves — for forwarding, serialization, inspection, or deferred evaluation — rather than immediately resolving them.
This issue proposes adding a minimal wrapper, raw, that marks a value to be stored as-is. Alternative names considered: constant, const, or literal.


Problem

Param’s assignment model is designed for convenience — ref-like values are automatically resolved when assigned to a Parameter.
However, there are valid use cases where the reference object itself should be stored, not the resolved value:

  • Keeping a Parameter or reactive object to be connected later.
  • Holding an expression or generator for deferred computation.
  • Serializing configurations that intentionally contain unresolved refs.
  • Preserving symbolic or lazy evaluation constructs for inspection.

Proposed API

import param

# Store a ref-like value *as itself*, skipping resolution:
obj.some_param = param.Raw(ref_like)
# or the convenience function:
obj.some_param = param.raw(ref_like)

Alternative names under consideration:

  • param.const(value)
  • param.constant(value)
  • param.literal(value)

All would convey the same semantics: assign without resolving.


Semantics

  • raw(x) is a transient assignment wrapper.

  • During assignment, if a value is an instance of Raw, Param:

    • Unwraps it (x = x.value), and
    • Skips any resolution logic that would normally occur.
  • The stored value becomes the underlying ref-like object, not the wrapper.

  • Reading the value simply returns whatever was stored.


Examples

Keep a reactive or param reference

class C(param.Parameterized):
    value = param.Parameter()

ref_like = another_obj.param.some_param

c = C()
c.value = param.raw(ref_like)
assert c.value is ref_like

Keep a reactive expression or async generator

expr = rx.map(source, lambda v: v * 2)
c.value = param.raw(expr)      # store expression, not resolved value

async def stream(): yield 1
c.value = param.raw(stream())  # store generator, not consumed

Containers are stored verbatim

c.value = param.raw([rx.expr1, param.Parameter(), async_gen])

Implementation sketch

class Raw:
    __slots__ = ("value",)
    def __init__(self, value): self.value = value
    def __repr__(self): return f"Raw({self.value!r})"

def raw(value):
    return Raw(value)

In Parameter.__set__ (or equivalent):

if isinstance(new, Raw):
    new = new.value
    skip_ref_resolution = True

if not skip_ref_resolution:
    new = maybe_resolve(new)  # existing logic

Typing (optional)

from typing import Generic, TypeVar
T = TypeVar("T")

class Raw(Generic[T]):
    value: T

Backward compatibility

  • Fully backward compatible: no change unless users explicitly use Raw.
  • The wrapper is transient and does not appear in serialized state.
  • Stored value is the inner object.

Metadata

Metadata

Assignees

No one assigned

    Labels

    APIImprovement to API or something like thattype-featureFeature request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions