Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved param and rx docs #977

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f9faae2
improved docs
MarcSkovMadsen Nov 13, 2024
563ea82
pre-commit
MarcSkovMadsen Nov 13, 2024
c5f6167
document .param namespace
MarcSkovMadsen Nov 13, 2024
ed229c3
fix
MarcSkovMadsen Nov 13, 2024
896f94d
align rx docstring
MarcSkovMadsen Nov 13, 2024
755496d
add type
MarcSkovMadsen Nov 13, 2024
b712cc1
align rx docstring
MarcSkovMadsen Nov 13, 2024
25733d2
align docstrings
MarcSkovMadsen Nov 13, 2024
65ae984
fix
MarcSkovMadsen Nov 13, 2024
6a7be70
Update tests/testreactive.py
MarcSkovMadsen Nov 13, 2024
edda46b
Fix lint
hoxbro Nov 13, 2024
ecbd92b
Update param/parameterized.py
MarcSkovMadsen Nov 14, 2024
3a17a10
Update param/parameterized.py
MarcSkovMadsen Nov 14, 2024
b55ef26
Update param/parameterized.py
MarcSkovMadsen Nov 14, 2024
173ff9c
Update param/reactive.py
MarcSkovMadsen Nov 14, 2024
f7c92da
review feedback
MarcSkovMadsen Nov 14, 2024
ba18b87
Merge branch 'enhancement/rx-docs' of https://github.com/holoviz/para…
MarcSkovMadsen Nov 14, 2024
2d5b832
Update param/parameterized.py
MarcSkovMadsen Nov 14, 2024
63f96df
review feedback
MarcSkovMadsen Nov 14, 2024
aa1b61c
Update param/parameterized.py
MarcSkovMadsen Nov 23, 2024
f26a0e2
Update param/parameterized.py
MarcSkovMadsen Nov 23, 2024
44cbfed
review feedback
MarcSkovMadsen Nov 23, 2024
a52d39c
Merge branch 'enhancement/rx-docs' of https://github.com/holoviz/para…
MarcSkovMadsen Nov 23, 2024
27a11e5
review feedback
MarcSkovMadsen Nov 23, 2024
0092617
review feedback
MarcSkovMadsen Nov 23, 2024
930ca17
review feedback
MarcSkovMadsen Nov 23, 2024
8964a56
fix failing test
MarcSkovMadsen Nov 23, 2024
3237360
Merge branch 'main' of https://github.com/holoviz/param into enhancem…
MarcSkovMadsen Nov 23, 2024
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
238 changes: 225 additions & 13 deletions param/parameterized.py
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,38 @@ def schema(self, safe=False, subset=None, mode='json'):

@property
def rx(self):
"""
The reactive namespace.

Provides reactive versions of operations that cannot be made reactive through operator overloading, such as
`.rx.and_` and `.rx.bool`. Calling this namespace (`()`) returns a reactive expression.

Returns
-------
Reactive expression
The result of calling the reactive namespace is a reactive expression.

User Guide
----------
https://param.holoviz.org/user_guide/Reactive_Expressions.html#special-methods-on-rx

Examples
--------
Create a Parameterized instance:

>>> import param
>>> class P(param.Parameterized):
... a = param.Number()
>>> p = P(a=1)

Get the current value:

>>> a = p.param.a.rx.value

Call it to get a reactive expression:

>>> rx_value = p.param.a.rx()
"""
from .reactive import reactive_ops
return reactive_ops(self)

Expand Down Expand Up @@ -2248,12 +2280,41 @@ def set_default(self_,param_name,value):
cls = self_.cls
setattr(cls,param_name,value)

def add_parameter(self_, param_name, param_obj):
def add_parameter(self_, param_name: str, param_obj: Parameter):
MarcSkovMadsen marked this conversation as resolved.
Show resolved Hide resolved
"""
Add a new Parameter object into this object's class.
Add a new Parameter object to this class.

This method allows dynamically adding a Parameter to the class, resulting in behavior equivalent to declaring
the Parameter in the class's source code.

Parameters
----------
param_name : str
The name of the parameter to add.
param_obj : Parameter
The Parameter object to add.

Examples
--------
Create a Parameterized class:

Should result in a Parameter equivalent to one declared
in the class's source code.
>>> import param
>>> class P(param.Parameterized):
>>> a = param.Number()
>>> b = param.String()
Comment on lines +2303 to +2304
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
>>> a = param.Number()
>>> b = param.String()
... a = param.Number()
... b = param.String()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

>>> p = P()

Add a new parameter to the class `P` via the class namespace `P.param`:

>>> P.param.add_parameter('c', param.Tuple(default=(1, 2, 3)))
>>> print(p.c)
MarcSkovMadsen marked this conversation as resolved.
Show resolved Hide resolved
(1, 2, 3)

Add a new parameter to the class `P` via the instance namespace `p.param`:

>>> p.param.add_parameter('d', param.Tuple(default=(3, 2, 1)))
>>> p.d
(3, 2, 1)
"""
# Could have just done setattr(cls,param_name,param_obj),
# which is supported by the metaclass's __setattr__ , but
Expand Down Expand Up @@ -2297,13 +2358,60 @@ def params(self_, parameter_name=None):

def update(self_, arg=Undefined, /, **kwargs):
"""
For the given dictionary or iterable or set of param=value
keyword arguments, sets the corresponding parameter of this
object or class to the given value.
Update multiple parameters of this object or class before triggering events.

Allows setting the parameters of the object or class using a dictionary, an iterable, or keyword arguments
in the form of `param=value`. The specified parameters will be updated to the given values.

This method can also be used as a context manager to temporarily set and then reset parameter values.
MarcSkovMadsen marked this conversation as resolved.
Show resolved Hide resolved

Parameters
----------
**params : dict or iterable or keyword arguments
The parameters to update, provided as a dictionary, iterable, or keyword arguments in `param=value` format.

User Guide
----------
https://param.holoviz.org/user_guide/Parameters.html#other-parameterized-methods

Examples
--------

Create a Parameterized class:

>>> import param
>>> class P(param.Parameterized):
... a = param.String()
... b = param.String()

Define the instance:

>>> p = P(a="0. Hello", b="0. World")

Use `.update` to update the parameters:

May also be used as a context manager to temporarily set and
then reset parameter values.
>>> p.param.update(a="1. Hello", b="2. World")
>>> p.a, p.b
('1. Hello', '1. World')

Update the parameters temporarily:

>>> with p.param.update(a="2. Hello", b="2. World"):
... print(p.a, p.b)
2. Hello 2. World

>>> p.a, p.b
('1. Hello', '1. World')

Lets see that events are triggered **after** all parameters have been updated

>>> @param.depends(p.param.a, watch=True)
... def print_a_b(a):
... print(p.a, p.b)
>>> my_param.param.update(a="3. Hello",b="3. World")
3. Hello 3. World
"""

refs = {}
if self_.self is not None:
private = self_.self._param__private
Expand Down Expand Up @@ -2586,6 +2694,41 @@ class or instance that contains an iterable collection of
obj.param.set_dynamic_time_fn(time_fn,sublistattr)

def serialize_parameters(self_, subset=None, mode='json'):
"""
Return the serialized parameters of the Parameterized object.

Parameters
----------
subset : list, optional
A list of parameter names to serialize. If None, all parameters will be serialized. Defaults to None.
mode : str, optional
The serialization format. By default, only 'json' is supported. Defaults to 'json'.

Returns
-------
Any
The serialized value.

User Guide
----------
https://param.holoviz.org/user_guide/Serialization_and_Persistence.html#serializing-with-json

Examples
--------
Create a Parameterized instance and serialize its parameters:

>>> import param
>>> class P(param.Parameterized):
... a = param.Number()
... b = param.String()
>>> p = P(a=1, b="hello")

Serialize parameters:

>>> serialized_data = p.param.serialize_parameters()
>>> print(serialized_data)
{"name": "P00002", "a": 1, "b": "hello"}
"""
self_or_cls = self_.self_or_cls
if mode not in Parameter._serializers:
raise ValueError(f'Mode {mode!r} not in available serialization formats {list(Parameter._serializers.keys())!r}')
Expand All @@ -2599,7 +2742,44 @@ def serialize_value(self_, pname, mode='json'):
serializer = Parameter._serializers[mode]
return serializer.serialize_parameter_value(self_or_cls, pname)

def deserialize_parameters(self_, serialization, subset=None, mode='json'):
def deserialize_parameters(self_, serialization, subset=None, mode='json') -> dict:
MarcSkovMadsen marked this conversation as resolved.
Show resolved Hide resolved
"""
Deserialize the given serialized data. This data can be used to create a
`Parameterized` object or update the parameters of an existing `Parameterized` object.

Parameters
----------
serialization : str
The serialized parameter data as a JSON string.
subset : list of str, optional
A list of parameter names to deserialize. If `None`, all parameters will be
deserialized. Defaults to `None`.
mode : str, optional
The serialization format. By default, only 'json' is supported.
Defaults to 'json'.

Returns
-------
dict
A dictionary with parameter names as keys and deserialized values.

User Guide
----------
https://param.holoviz.org/user_guide/Serialization_and_Persistence.html#serializing-with-json

Examples
--------
>>> import param
>>> class P(param.Parameterized):
... a = param.Number()
... b = param.String()
...
>>> serialized_data = '{"a": 1, "b": "hello"}'
>>> deserialized_data = P.param.deserialize_parameters(serialized_data)
>>> print(deserialized_data)
{'a': 1, 'b': 'hello'}
>>> instance = P(**deserialized_data)
"""
self_or_cls = self_.self_or_cls
serializer = Parameter._serializers[mode]
return serializer.deserialize_parameters(self_or_cls, serialization, subset=subset)
Expand Down Expand Up @@ -4138,10 +4318,10 @@ class Parameterized(metaclass=ParameterizedMetaclass):
parameter in the instance will get the value given as a keyword
argument. For example:

class Foo(Parameterized):
xx = Parameter(default=1)
>>> class Foo(Parameterized):
... xx = Parameter(default=1)

foo = Foo(xx=20)
>>> foo = Foo(xx=20)

in this case foo.xx gets the value 20.

Expand Down Expand Up @@ -4194,6 +4374,38 @@ def __init__(self, **params):

@property
def param(self):
"""
The `.param` namespace for `Parameterized` classes and instances.

This namespace provides access to powerful methods and properties for managing
parameters in a `Parameterized` object. It includes utilities for adding parameters,
updating parameters, debugging, serialization, logging, and more.

User Guide
----------
For more details on parameter objects and instances, see:
https://param.holoviz.org/user_guide/Parameters.html#parameter-objects-and-instances

Examples
--------
Basic usage of `.param` in a `Parameterized` class:

>>> import param
>>>
>>> class MyClass(param.Parameterized):
... value = param.Parameter()
>>>
>>> my_instance = MyClass(value=0)

Access the `value` parameter of `my_instance`:

>>> my_instance.param.value # the Parameter instance

Note that this is different from the current `value` of `my_instance`:

>>> my_instance.value # the current parameter value
0
"""
return Parameters(self.__class__, self=self)

#PARAM3_DEPRECATION
Expand Down
72 changes: 68 additions & 4 deletions param/reactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,19 +181,47 @@ class NestedResolver(Resolver):

class reactive_ops:
"""
Namespace for reactive operators.
The reactive namespace.

Implements operators that cannot be implemented using regular
Python syntax.
Provides reactive versions of operations that cannot be made reactive through operator overloading, such as
`.rx.and_` and `.rx.bool`. Calling this namespace (`()`) returns a reactive expression.

Returns
-------
Reactive expression
The result of calling the reactive namespace is a reactive expression.

User Guide
----------
https://param.holoviz.org/user_guide/Reactive_Expressions.html#special-methods-on-rx

Examples
--------
Create a Parameterized instance:

>>> import param
>>> class P(param.Parameterized):
... a = param.Number()
>>> p = P(a=1)

Get the current value:

>>> a = p.param.a.rx.value

Call it to get a reactive expression:

>>> rx_value = p.param.a.rx()
"""


def __init__(self, reactive):
self._reactive = reactive

def _as_rx(self):
return self._reactive if isinstance(self._reactive, rx) else self()

def __call__(self):
"""Creates a reactive expression."""
rxi = self._reactive
return rxi if isinstance(rx, rx) else rx(rxi)

Expand Down Expand Up @@ -755,14 +783,50 @@ def __init__(
]
self._setup_invalidations(depth)
self._kwargs = kwargs
self.rx = reactive_ops(self)
self._rx = reactive_ops(self)
MarcSkovMadsen marked this conversation as resolved.
Show resolved Hide resolved
self._init = True
for name, accessor in _display_accessors.items():
setattr(self, name, accessor(self))
for name, (accessor, predicate) in rx._accessors.items():
if predicate is None or predicate(self._current):
setattr(self, name, accessor(self))

@property
def rx(self) -> reactive_ops:
"""
The reactive namespace.

Provides reactive versions of operations that cannot be made reactive through operator overloading, such as
`.rx.and_` and `.rx.bool`. Calling this namespace (`()`) returns a reactive expression.

Returns
-------
Reactive expression
The result of calling the reactive namespace is a reactive expression.

User Guide
----------
https://param.holoviz.org/user_guide/Reactive_Expressions.html#special-methods-on-rx

Examples
--------
Create a Parameterized instance:

>>> import param
>>> class P(param.Parameterized):
... a = param.Number()
>>> p = P(a=1)

Get the current value:

>>> a = p.param.a.rx.value

Call it to get a reactive expression:

>>> rx_value = p.param.a.rx()
"""
return self._rx

@property
def _obj(self):
if self._shared_obj is None:
Expand Down
Loading