param 2.2.1__tar.gz → 2.3.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.
Files changed (76) hide show
  1. {param-2.2.1 → param-2.3.0}/.gitignore +1 -1
  2. param-2.3.0/PKG-INFO +512 -0
  3. param-2.3.0/README.md +414 -0
  4. {param-2.2.1 → param-2.3.0}/numbergen/__init__.py +51 -29
  5. {param-2.2.1 → param-2.3.0}/param/__init__.py +54 -26
  6. {param-2.2.1 → param-2.3.0}/param/_utils.py +234 -183
  7. {param-2.2.1 → param-2.3.0}/param/_version.py +16 -3
  8. {param-2.2.1 → param-2.3.0}/param/depends.py +4 -4
  9. {param-2.2.1 → param-2.3.0}/param/ipython.py +22 -8
  10. {param-2.2.1 → param-2.3.0}/param/parameterized.py +2212 -928
  11. {param-2.2.1 → param-2.3.0}/param/parameters.py +193 -236
  12. {param-2.2.1 → param-2.3.0}/param/reactive.py +978 -241
  13. {param-2.2.1 → param-2.3.0}/param/serializer.py +4 -4
  14. {param-2.2.1 → param-2.3.0}/param/version.py +45 -15
  15. {param-2.2.1 → param-2.3.0}/pyproject.toml +15 -9
  16. param-2.3.0/tests/__init__.py +1 -0
  17. {param-2.2.1 → param-2.3.0}/tests/conftest.py +5 -1
  18. {param-2.2.1 → param-2.3.0}/tests/testaddparameter.py +2 -0
  19. {param-2.2.1 → param-2.3.0}/tests/testbytesparam.py +1 -1
  20. {param-2.2.1 → param-2.3.0}/tests/testcompositeparams.py +3 -3
  21. param-2.3.0/tests/testdefaultfactory.py +310 -0
  22. {param-2.2.1 → param-2.3.0}/tests/testdefaults.py +4 -3
  23. param-2.3.0/tests/testdeprecations.py +87 -0
  24. {param-2.2.1 → param-2.3.0}/tests/testdynamicparams.py +12 -12
  25. {param-2.2.1 → param-2.3.0}/tests/testfiledeserialization.py +1 -1
  26. param-2.3.0/tests/testimports.py +30 -0
  27. {param-2.2.1 → param-2.3.0}/tests/testipythonmagic.py +1 -1
  28. {param-2.2.1 → param-2.3.0}/tests/testjsonserialization.py +1 -1
  29. {param-2.2.1 → param-2.3.0}/tests/testlist.py +24 -2
  30. {param-2.2.1 → param-2.3.0}/tests/testlistselector.py +16 -7
  31. {param-2.2.1 → param-2.3.0}/tests/testnumpy.py +1 -1
  32. {param-2.2.1 → param-2.3.0}/tests/testobjectselector.py +15 -6
  33. {param-2.2.1 → param-2.3.0}/tests/testpandas.py +1 -1
  34. {param-2.2.1 → param-2.3.0}/tests/testparamdepends.py +19 -0
  35. param-2.3.0/tests/testparameter.py +69 -0
  36. {param-2.2.1 → param-2.3.0}/tests/testparameterizedobject.py +151 -12
  37. {param-2.2.1 → param-2.3.0}/tests/testparamunion.py +1 -1
  38. {param-2.2.1 → param-2.3.0}/tests/testreactive.py +59 -58
  39. {param-2.2.1 → param-2.3.0}/tests/testrefs.py +49 -20
  40. {param-2.2.1 → param-2.3.0}/tests/testsignatures.py +3 -22
  41. {param-2.2.1 → param-2.3.0}/tests/teststringparam.py +1 -1
  42. {param-2.2.1 → param-2.3.0}/tests/testtimedependent.py +28 -28
  43. {param-2.2.1 → param-2.3.0}/tests/testutils.py +122 -2
  44. {param-2.2.1 → param-2.3.0}/tests/testversion.py +3 -1
  45. {param-2.2.1 → param-2.3.0}/tests/testwatch.py +12 -11
  46. {param-2.2.1 → param-2.3.0}/tests/utils.py +8 -3
  47. param-2.2.1/PKG-INFO +0 -107
  48. param-2.2.1/README.md +0 -18
  49. param-2.2.1/tests/__init__.py +0 -0
  50. param-2.2.1/tests/testdeprecations.py +0 -205
  51. param-2.2.1/tests/testimports.py +0 -20
  52. {param-2.2.1 → param-2.3.0}/LICENSE.txt +0 -0
  53. {param-2.2.1 → param-2.3.0}/param/display.py +0 -0
  54. {param-2.2.1 → param-2.3.0}/tests/testbind.py +0 -0
  55. {param-2.2.1 → param-2.3.0}/tests/testbooleanparam.py +0 -0
  56. {param-2.2.1 → param-2.3.0}/tests/testcalendardateparam.py +0 -0
  57. {param-2.2.1 → param-2.3.0}/tests/testcalendardaterangeparam.py +0 -0
  58. {param-2.2.1 → param-2.3.0}/tests/testcallable.py +0 -0
  59. {param-2.2.1 → param-2.3.0}/tests/testclassselector.py +0 -0
  60. {param-2.2.1 → param-2.3.0}/tests/testcolorparameter.py +0 -0
  61. {param-2.2.1 → param-2.3.0}/tests/testcomparator.py +0 -0
  62. {param-2.2.1 → param-2.3.0}/tests/testcustomparam.py +0 -0
  63. {param-2.2.1 → param-2.3.0}/tests/testdateparam.py +0 -0
  64. {param-2.2.1 → param-2.3.0}/tests/testdaterangeparam.py +0 -0
  65. {param-2.2.1 → param-2.3.0}/tests/testfileselector.py +0 -0
  66. {param-2.2.1 → param-2.3.0}/tests/testmultifileselector.py +0 -0
  67. {param-2.2.1 → param-2.3.0}/tests/testnumbergen.py +0 -0
  68. {param-2.2.1 → param-2.3.0}/tests/testnumberparameter.py +0 -0
  69. {param-2.2.1 → param-2.3.0}/tests/testparameterizedrepr.py +0 -0
  70. {param-2.2.1 → param-2.3.0}/tests/testparamoutput.py +0 -0
  71. {param-2.2.1 → param-2.3.0}/tests/testpathparam.py +0 -0
  72. {param-2.2.1 → param-2.3.0}/tests/testpickle.py +0 -0
  73. {param-2.2.1 → param-2.3.0}/tests/testrangeparameter.py +0 -0
  74. {param-2.2.1 → param-2.3.0}/tests/testreprhtml.py +0 -0
  75. {param-2.2.1 → param-2.3.0}/tests/testselector.py +0 -0
  76. {param-2.2.1 → param-2.3.0}/tests/testtupleparam.py +0 -0
@@ -20,7 +20,7 @@ jupyter_execute/
20
20
  doc/Reference_Manual/
21
21
  doc/user_guide/data.pickle
22
22
  doc/user_guide/output.*
23
- doc/reference/generated
23
+ doc/reference/param/generated
24
24
 
25
25
  # Unit test / Coverage report
26
26
  .coverage
param-2.3.0/PKG-INFO ADDED
@@ -0,0 +1,512 @@
1
+ Metadata-Version: 2.4
2
+ Name: param
3
+ Version: 2.3.0
4
+ Summary: Make your Python code clearer and more reliable by declaring Parameters.
5
+ Project-URL: Homepage, https://param.holoviz.org/
6
+ Project-URL: Tracker, https://github.com/holoviz/param/issues
7
+ Project-URL: Releases, https://github.com/holoviz/param/releases
8
+ Project-URL: Source, https://github.com/holoviz/param
9
+ Project-URL: HoloViz, https://holoviz.org/
10
+ Author-email: HoloViz <developers@holoviz.org>
11
+ Maintainer-email: HoloViz <developers@holoviz.org>
12
+ License: BSD-3-Clause
13
+ License-File: LICENSE.txt
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: License :: OSI Approved :: BSD License
18
+ Classifier: Natural Language :: English
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
26
+ Classifier: Topic :: Scientific/Engineering
27
+ Classifier: Topic :: Software Development :: Libraries
28
+ Requires-Python: >=3.10
29
+ Provides-Extra: all
30
+ Requires-Dist: aiohttp; extra == 'all'
31
+ Requires-Dist: cloudpickle; extra == 'all'
32
+ Requires-Dist: gmpy2; extra == 'all'
33
+ Requires-Dist: ipython; extra == 'all'
34
+ Requires-Dist: jsonschema; extra == 'all'
35
+ Requires-Dist: nbval; extra == 'all'
36
+ Requires-Dist: nest-asyncio; extra == 'all'
37
+ Requires-Dist: numpy; extra == 'all'
38
+ Requires-Dist: odfpy; extra == 'all'
39
+ Requires-Dist: openpyxl; extra == 'all'
40
+ Requires-Dist: pandas; extra == 'all'
41
+ Requires-Dist: panel; extra == 'all'
42
+ Requires-Dist: pyarrow; extra == 'all'
43
+ Requires-Dist: pytest; extra == 'all'
44
+ Requires-Dist: pytest-asyncio; extra == 'all'
45
+ Requires-Dist: pytest-cov; extra == 'all'
46
+ Requires-Dist: pytest-xdist; extra == 'all'
47
+ Requires-Dist: tables; extra == 'all'
48
+ Requires-Dist: xlrd; extra == 'all'
49
+ Provides-Extra: examples
50
+ Requires-Dist: aiohttp; extra == 'examples'
51
+ Requires-Dist: pandas; extra == 'examples'
52
+ Requires-Dist: panel; extra == 'examples'
53
+ Provides-Extra: tests
54
+ Requires-Dist: pytest; extra == 'tests'
55
+ Requires-Dist: pytest-asyncio; extra == 'tests'
56
+ Requires-Dist: pytest-cov; extra == 'tests'
57
+ Provides-Extra: tests-deser
58
+ Requires-Dist: odfpy; extra == 'tests-deser'
59
+ Requires-Dist: openpyxl; extra == 'tests-deser'
60
+ Requires-Dist: pyarrow; extra == 'tests-deser'
61
+ Requires-Dist: tables; extra == 'tests-deser'
62
+ Requires-Dist: xlrd; extra == 'tests-deser'
63
+ Provides-Extra: tests-examples
64
+ Requires-Dist: aiohttp; extra == 'tests-examples'
65
+ Requires-Dist: nbval; extra == 'tests-examples'
66
+ Requires-Dist: pandas; extra == 'tests-examples'
67
+ Requires-Dist: panel; extra == 'tests-examples'
68
+ Requires-Dist: pytest; extra == 'tests-examples'
69
+ Requires-Dist: pytest-asyncio; extra == 'tests-examples'
70
+ Requires-Dist: pytest-xdist; extra == 'tests-examples'
71
+ Provides-Extra: tests-full
72
+ Requires-Dist: aiohttp; extra == 'tests-full'
73
+ Requires-Dist: cloudpickle; extra == 'tests-full'
74
+ Requires-Dist: gmpy2; extra == 'tests-full'
75
+ Requires-Dist: ipython; extra == 'tests-full'
76
+ Requires-Dist: jsonschema; extra == 'tests-full'
77
+ Requires-Dist: nbval; extra == 'tests-full'
78
+ Requires-Dist: nest-asyncio; extra == 'tests-full'
79
+ Requires-Dist: numpy; extra == 'tests-full'
80
+ Requires-Dist: odfpy; extra == 'tests-full'
81
+ Requires-Dist: openpyxl; extra == 'tests-full'
82
+ Requires-Dist: pandas; extra == 'tests-full'
83
+ Requires-Dist: panel; extra == 'tests-full'
84
+ Requires-Dist: pyarrow; extra == 'tests-full'
85
+ Requires-Dist: pytest; extra == 'tests-full'
86
+ Requires-Dist: pytest-asyncio; extra == 'tests-full'
87
+ Requires-Dist: pytest-cov; extra == 'tests-full'
88
+ Requires-Dist: pytest-xdist; extra == 'tests-full'
89
+ Requires-Dist: tables; extra == 'tests-full'
90
+ Requires-Dist: xlrd; extra == 'tests-full'
91
+ Provides-Extra: tests-pypy
92
+ Requires-Dist: cloudpickle; extra == 'tests-pypy'
93
+ Requires-Dist: ipython; extra == 'tests-pypy'
94
+ Requires-Dist: jsonschema; extra == 'tests-pypy'
95
+ Requires-Dist: nest-asyncio; extra == 'tests-pypy'
96
+ Requires-Dist: numpy; extra == 'tests-pypy'
97
+ Description-Content-Type: text/markdown
98
+
99
+ <a href="https://param.holoviz.org/">
100
+ <img src="https://raw.githubusercontent.com/holoviz/param/main/doc/_static/logo_horizontal.png" width=250>
101
+ </a>
102
+
103
+ # Param
104
+
105
+ **Param** is a zero-dependency Python library that provides two main features:
106
+
107
+ - Easily create classes with **rich, declarative attributes** - `Parameter` objects - that include extended metadata for various purposes such as runtime type and range validation, documentation strings, default values or factories, nullability, etc. In this sense, Param is conceptually similar to libraries like Pydantic, Python's dataclasses, or Traitlets.
108
+ - A suite of expressive and composable APIs for **reactive programming**, enabling automatic updates on attribute changes, and declaring complex reactive dependencies and expressions that can be introspected by other frameworks to implement their own reactive workflows.
109
+
110
+ This combination of **rich attributes** and **reactive APIs** makes Param a solid foundation for constructing user interfaces, graphical applications, and responsive systems where data integrity and automatic synchronization are paramount. In fact, Param serves as the backbone of HoloViz’s [Panel](https://panel.holoviz.org) and [HoloViews](https://holoviews.org) libraries, powering their rich interactivity and data-driven workflows.
111
+
112
+ Here is a very simple example showing both features at play. We declare a UserForm class with three parameters: `age` as an *Integer* parameter and and `name` as a *String* parameter for user data, and `submit` as an *Event* parameter to simulate a button in a user interface. We also declare that the `save_user_to_db` method should be called automatically when the value of the `submit` attribute changes.
113
+
114
+ ```python
115
+ import param
116
+
117
+ class UserForm(param.Parameterized):
118
+ age = param.Integer(bounds=(0, None), doc='User age')
119
+ name = param.String(doc='User name')
120
+ submit = param.Event()
121
+
122
+ @param.depends('submit', watch=True)
123
+ def save_user_to_db(self):
124
+ print(f'Saving user to db: name={self.name}, age={self.age}')
125
+ ...
126
+
127
+ user = UserForm(name='Bob', age=25)
128
+
129
+ user.submit = True # => Saving user to db: name=Bob, age=25
130
+ ```
131
+
132
+ ---
133
+
134
+ Enjoying Param? Show your support with a [Github star](https://github.com/holoviz/param) to help others discover it too! ⭐️
135
+
136
+ ---
137
+
138
+ | | |
139
+ | --- | --- |
140
+ | Downloads | [![PyPi Downloads](https://img.shields.io/pypi/dm/param?label=pypi)](https://pypistats.org/packages/param)
141
+ | Build Status | [![Linux/MacOS/Windows Build Status](https://github.com/holoviz/param/workflows/tests/badge.svg)](https://github.com/holoviz/param/actions/workflows/test.yaml)
142
+ | Coverage | [![codecov](https://codecov.io/gh/holoviz/param/branch/main/graph/badge.svg)](https://codecov.io/gh/holoviz/param) |
143
+ | Latest dev release | [![Github tag](https://img.shields.io/github/v/tag/holoviz/param.svg?label=tag&colorB=11ccbb)](https://github.com/holoviz/param/tags) |
144
+ | Latest release | [![Github release](https://img.shields.io/github/release/holoviz/param.svg?label=tag&colorB=11ccbb)](https://github.com/holoviz/param/releases) [![PyPI version](https://img.shields.io/pypi/v/param.svg?colorB=cc77dd)](https://pypi.python.org/pypi/param) [![conda-forge version](https://img.shields.io/conda/v/conda-forge/param.svg?label=conda%7Cconda-forge&colorB=4488ff)](https://anaconda.org/conda-forge/param) [![defaults version](https://img.shields.io/conda/v/anaconda/param.svg?label=conda%7Cdefaults&style=flat&colorB=4488ff)](https://anaconda.org/anaconda/param) [![param version](https://img.shields.io/conda/v/pyviz/param.svg?colorB=4488ff&style=flat)](https://anaconda.org/pyviz/param) |
145
+ | Python | [![Python support](https://img.shields.io/pypi/pyversions/param.svg)](https://pypi.org/project/param/)
146
+ | Docs | [![gh-pages](https://img.shields.io/github/last-commit/holoviz/param/gh-pages.svg)](https://github.com/holoviz/param/tree/gh-pages) [![site](https://img.shields.io/website-up-down-green-red/https/param.holoviz.org.svg)](https://param.holoviz.org) |
147
+ | Binder | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/holoviz/param/main?labpath=doc) |
148
+ | Support | [![Discourse](https://img.shields.io/discourse/status?server=https%3A%2F%2Fdiscourse.holoviz.org)](https://discourse.holoviz.org/) |
149
+
150
+
151
+ ## Rich class attributes for runtime validation and more
152
+
153
+ Param lets you create classes and declare facts about each of their attributes through rich `Parameter` objects. Once you have done that, Param can handle runtime attribute validation (type checking, range validation, etc.) and more (documentation, serialization, etc.). Let's see how to use `Parameter` objects with a simple example, a `Processor` class that has three attributes.
154
+
155
+ ```python
156
+ import param
157
+
158
+ # Create a class by inheriting from Parameterized
159
+ class Processor(param.Parameterized):
160
+
161
+ # An Integer parameter that allows only integer values between 0 and 10,
162
+ # has a default of 3, and a custom docstring.
163
+ retries = param.Integer(default=3, bounds=(0, 10), doc="Retry attempts.")
164
+
165
+ # A Boolean parameter, False by default.
166
+ verbose = param.Boolean(default=False, doc="Emit progress messages.")
167
+
168
+ # A Selector parameter, with only two allowed values "fast" and "accurate",
169
+ # "fast" by default.
170
+ mode = param.Selector(
171
+ default="fast", objects=["fast", "accurate"],
172
+ doc="Execution strategy."
173
+ )
174
+
175
+ def run(self):
176
+ if self.verbose:
177
+ print(f"[{self.mode}] retry={self.retries}")
178
+ # ...main logic...
179
+
180
+
181
+ # You can of course override the default value on instantiation.
182
+ processor = Processor(verbose=True)
183
+
184
+ # A Parameterized instance behaves as a usual instance of a class.
185
+ processor.run()
186
+ # => [fast] retry=3
187
+
188
+ # Parameterized implements a nice and simple repr.
189
+ print(repr(processor))
190
+ # => Processor(mode='accurate', name='Processor00042', retries=3, verbose=False)
191
+
192
+ # Attempting to set the retries attribute to 42 will raise
193
+ # an error as the value must be between 0 and 10.
194
+ try:
195
+ processor.retries = 42
196
+ except ValueError as e:
197
+ print(e)
198
+ # => Integer parameter 'Processor.retries' must be at most 10, not 42.
199
+
200
+ # The `.param` namespace allows to get a hold on Parameter objects via
201
+ # indexing or attribute access.
202
+ print(processor.param['mode'].objects) # => ['fast', 'accurate']
203
+ print(processor.param.mode.objects) # => ['fast', 'accurate']
204
+
205
+ # This namespace offers many more useful methods, such as `.update()`
206
+ # to update multiple parameters at once, or `.values()` to obtain
207
+ # a dict of parameter name to parameter value.
208
+ processor.param.update(mode='accurate', verbose=False)
209
+ print(processor.param.values())
210
+ # => {'mode': 'accurate', 'name': 'Processor00042', 'retries': 3, 'verbose': False}
211
+ ```
212
+
213
+ Runtime attribute validation is a great feature that helps build defendable code bases! Alternative libraries, like [Pydantic](https://docs.pydantic.dev) and others, excel at input validation, and if this is only what you need, you should probably look into them. Where Param shines is when you also need:
214
+
215
+ - Attributes that are also available at the class level, allowing to easily configure a hierarchy of classes and their instances.
216
+ - Parameters with rich metadata (`default`, `doc`, `label`, `bounds`, etc.) that downstream tooling can inspect to build configuration UIs, CLIs, or documentation automatically.
217
+ - Parameterized subclasses that inherit Parameter metadata from their parents, and can selectively override certain attributes (e.g. overriding `default` in a subclass).
218
+
219
+ Let's see this in action by extending the example above with a custom processor subclass:
220
+
221
+ ```python
222
+ class CustomProcessor(Processor):
223
+ # This subclass overrides the bounds of the `retries` Parameter
224
+ # and the default of the `verbose` Parameter. All the other
225
+ # Parameter metadata are inherited from Processor.
226
+ retries = param.Integer(bounds=(0, 100))
227
+ verbose = param.Boolean(default=True)
228
+
229
+
230
+ # Attributes exist at both the class-level and instance-level
231
+ print(CustomProcessor.verbose) # => True
232
+
233
+ # The `.param` namespace is also available at the class-level.
234
+ # Parameter metadata inheritance in action, with `default`
235
+ # inherited from Processor and `bounds` overridden by CustomProcessor.
236
+ print(CustomProcessor.param['retries'].default) # => 3
237
+ print(CustomProcessor.param['retries'].bounds) # => (0, 100)
238
+
239
+ # Class attributes are also runtime validated.
240
+ try:
241
+ CustomProcessor.retries = 200
242
+ except ValueError as e:
243
+ print(e)
244
+ # => Integer parameter 'CustomProcessor.retries' must be at most 100, not 200.
245
+
246
+ cprocessor = CustomProcessor()
247
+
248
+ # As in normal Python classes, class-level attribute values apply
249
+ # to all instances that didn't override it.
250
+ print(cprocessor.mode) # => 'fast'
251
+ Processor.mode = 'accurate'
252
+ print(cprocessor.mode) # => 'accurate'
253
+ ```
254
+
255
+ ## Reactive Programming
256
+
257
+ Param extends beyond rich class attributes with a suite of APIs for reactive programming. Let's do a quick tour!
258
+
259
+ We'll start with APIs that trigger side-effects only, which either have the noun watch in their name or are invoked with `watch=True`:
260
+
261
+ 1. `<parameterized_obj>.param.watch(fn, *parameters, ...)`: Low-level, imperative API to attach callbacks to parameter changes, the callback receives one or more rich `Event` objects.
262
+ 2. `@depends(*parameter_names, watch=True)`: In a Parameterized class, declare dependencies and automatically watch parameters for changes to call the decorated method.
263
+ 3. `bind(fn, *references, watch=True, **kwargs)`: Function binding with automatic references (parameters, bound functions, reactive expressions) watching and triggering on changes.
264
+
265
+ ```python
266
+ import param
267
+
268
+ def debug_event(event: param.parameterized.Event):
269
+ print(event)
270
+
271
+ class SideEffectExample(param.Parameterized):
272
+ a = param.String()
273
+ b = param.String()
274
+ c = param.String()
275
+
276
+ def __init__(self, **params):
277
+ super().__init__(**params)
278
+ # We register the debug_event callback, that will be called when a changes.
279
+ self.param.watch(debug_event, 'a') # 1.
280
+
281
+ # We declare an automatic dependency between b and the print_b method.
282
+ @param.depends('b', watch=True) # 2.
283
+ def print_b(self):
284
+ print(f"print_b: {self.b=}")
285
+
286
+ sfe = SideEffectExample()
287
+ # We update the value of a and immediately see that debug_event is called
288
+ # with a rich Event object.
289
+ sfe.a = 'foo'
290
+ # => Event(
291
+ # => Event(
292
+ # what='value', name='a',
293
+ # obj=SideEffectExample(a='foo', b='', c='', name='SideEffectExample00008'),
294
+ # cls=SideEffectExample(a='foo', b='', c='', name='SideEffectExample00008'),
295
+ # old='', new='foo', type='changed'
296
+ # )
297
+
298
+ # Updating b automatically calls print_b, which is simply invoked as is without
299
+ # any rich Event object passed. When called, b is already updated.
300
+ sfe.b = 'bar'
301
+ # print_b: self.b='bar'
302
+
303
+ def print_c(c):
304
+ print(f"print_c: {c=}")
305
+
306
+ # We can also bind a function to a parameter.
307
+ param.bind(print_c, sfe.param.c, watch=True) # 3.
308
+
309
+ # Updating c invokes the bound function with the updated value.
310
+ sfe.c = 'baz'
311
+ # print_c: c='baz'
312
+ ```
313
+
314
+ Let's continue the tour with what we'll call "reactive APIs". Contrary to the APIs presented above, in this group parameter updates do not immediately trigger side effects. Instead, these APIs let you declare relationships, dependencies, and expressions, which can be introspected by other frameworks to set up their own reactive workflows.
315
+
316
+ 1. `@depends(*parameter_names)`: In a Parameterized class, declare parameter dependencies by decorating a method.
317
+ 2. `bind(fn, *references, **kwargs)`: Create a bound function, that when called, will always use the current parameter/reference value. `bind` is essentially a reactive version of [`functools.partial`](https://docs.python.org/3/library/functools.html#functools.partial).
318
+ 3. `rx()`: Fluent API to create reactive expressions, which allow chaining and composing operations.
319
+
320
+ ```python
321
+ import param
322
+
323
+ class ReactiveExample(param.Parameterized):
324
+ x = param.Integer()
325
+ y = param.Integer()
326
+
327
+ @param.depends('x', 'y') # 1.
328
+ def sum(self):
329
+ return self.x + self.y
330
+
331
+ re = ReactiveExample()
332
+
333
+ def mul(a, b):
334
+ return a * b
335
+
336
+ bound_mul = param.bind(mul, re.param.x, re.param.y) # 2.
337
+ re.param.update(x=2, y=4)
338
+ bound_mul()
339
+ # 8
340
+ ```
341
+
342
+ `rx()` is the highest-level reactive API Param offers. We'll show a simple example first, using literal values as input of three source reactive expressions, that we combine with simple arithmetic operations.
343
+
344
+ ```python
345
+ from param import rx
346
+
347
+ # Reactive expressions can be created by simply wrapping an object with `rx()`
348
+ val1 = rx(0)
349
+ val2 = rx(0)
350
+ factor = rx(1)
351
+
352
+ # We can then use these objects as if they were the object they wrap
353
+ # (a reactive expression is a proxy of its resolved value). Since the
354
+ # inputs are of type `int`, we are able to use the `+` and `*` operators
355
+ # to create a new reactive expression object `res`. This works by
356
+ # overloading the Python data model (`__add__` and `__mul__` in this
357
+ # example).
358
+ res = (val1 + val2) * factor
359
+ # We have built a small computational graph, adding first `val1` and
360
+ # `val2`, and then multiplying the sum with `factor`.
361
+
362
+ # We can obtain the resolved value of `res` via the `.rx.value` property
363
+ # (`.rx` being a namespace holding other useful attributes and methods).
364
+ print(res.rx.value) # => 0, from (0 + 0) * 1
365
+
366
+ # We can then update one of the source reactive expressions by setting
367
+ # it via the `.rx.value` property.
368
+ val1.rx.value = 2
369
+ # Requesting the value of `res` resolves its updated value (2 instead of 0).
370
+ print(res.rx.value) # => 2, from (2 + 0) * 1
371
+
372
+ # Similarly, we can update `factor` and request the new value of `res`.
373
+ # Note that only the last multiplication is recomputed; the intermediate
374
+ # result of `val1 + val2` is automatically cached.
375
+ factor.rx.value = 10
376
+ print(res.rx.value) # => 20, from (2 + 0) * 10
377
+ ```
378
+
379
+ The snippet below shows a slightly more complex example that reveals the power of reactive expressions.
380
+
381
+ ```python
382
+ import param
383
+
384
+ class RXExample(param.Parameterized):
385
+ val1 = param.String('foo')
386
+ val2 = param.String('bar')
387
+
388
+ example = RXExample()
389
+
390
+ # Reactive expressions can be created directly from Parameters by
391
+ # calling `.rx()`. The source of the reactive expression is not a
392
+ # literal value, but a fully dynamic Parameter object.
393
+ print(example.param.val1.rx().title().rx.value) # => 'Foo'
394
+
395
+ # By updating the parameter value, we update the source value of
396
+ # this expression.
397
+ example.val1 = 'fab'
398
+ print(example.param.val1.rx().title().rx.value) # => 'Fab'
399
+
400
+ # Reactive expressions aren't constrained to one data type; the source
401
+ # value of `cond1` is a string and the resolved value a is boolean.
402
+ cond1 = example.param.val1.rx().startswith('o')
403
+ print(cond1.rx.value) # => False
404
+
405
+ # Python does not allow all of its data model to be overloaded, and
406
+ # special methods like `.rx.or_` have been implemented to cover
407
+ # these cases.
408
+ cond2 = example.param.val2.rx().startswith('b')
409
+ print(cond1.rx.or_(cond2).rx.value) # => True
410
+ ```
411
+
412
+ A `Parameter` does not have to refer to a specific static value but can reference another object and update reactively when its value changes. We'll show a few examples of supported *references* (Parameters, bound functions, reactive expressions, etc.). Setting parameter values with references is an effective way to establish automatic one-way linking between a reference and a parameter (the reference being often driven by another parameter).
413
+
414
+ ```python
415
+ import param
416
+
417
+ class X(param.Parameterized):
418
+
419
+ # The source parameter is going to serve as the input of all
420
+ # our references.
421
+ source = param.Number()
422
+
423
+ class Y(param.Parameterized):
424
+
425
+ # The target Parameter of this Y class is declared to accept references
426
+ # with allow_refs=True (False by default).
427
+ target = param.Number(allow_refs=True)
428
+
429
+ # We add this automatic callback for you to better understand when the
430
+ # updates actually occur; watch_target will be called when the resolved
431
+ # value of target changes.
432
+ @param.depends('target', watch=True)
433
+ def watch_target(self):
434
+ print(f'y.target updated to {self.target}')
435
+
436
+ x = X(source=1)
437
+
438
+ # The first example of a reference is simply another Parameter, here
439
+ # the source parameter of the x instance.
440
+ y = Y(target=x.param['source'])
441
+ # y.target is already equal to the value of x.source
442
+ print(y.target) # => 1
443
+
444
+ # When x.source is updated, y.target is immediately and automatically updated.
445
+ x.source = 2
446
+ # y.target updated to 2
447
+ print(y.target) # => 2
448
+
449
+ # We can override a reference with another reference, in this case a function
450
+ # bound to the x parameter of source.
451
+ y.target = param.bind(lambda x: x + 10, x.param['source'])
452
+ # y.target updated to 12
453
+ print(y.target) # => 12
454
+
455
+ # When x.source is updated, the bound function is immediately called to update
456
+ # the value of y.target.
457
+ x.source = 3
458
+ # y.target updated to 13
459
+ print(y.target) # => 13
460
+
461
+ # Another kind of accepted reference is a reactive expression.
462
+ y.target = x.param['source'].rx() * 20
463
+ # y.target updated to 60
464
+ print(y.target) # => 60
465
+
466
+ # Similarly, when x.source is updated, the reactive expression is immediately
467
+ # resolved to update the value of y.target
468
+ x.source = 5
469
+ # y.target updated to 100
470
+ print(y.target) # => 100
471
+ ```
472
+
473
+ ## Support & Feedback
474
+
475
+ - Visit [Param's website](https://param.holoviz.org/)
476
+ - Usage questions and showcases -> [HoloViz Community](https://holoviz.org/community.html)
477
+ - Bug reports and feature requests -> [Github](https://github.com/holoviz/param)
478
+ - Chat -> [Discord](https://discord.gg/rb6gPXbdAr)
479
+
480
+ For more detail check out the [HoloViz Community Guide](https://holoviz.org/community.html).
481
+
482
+
483
+ ## Contributing
484
+
485
+ Check out the [Contributing Guide](CONTRIBUTING.MD).
486
+
487
+ ## License
488
+
489
+ Param is completely free and open-source. It is licensed under the [BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause).
490
+
491
+
492
+ ## Sponsors
493
+
494
+ The Param project is also very grateful for the sponsorship by the organizations and companies below:
495
+
496
+ <table align="center">
497
+ <tr>
498
+ <td>
499
+ <a href="https://www.anaconda.com/">
500
+ <img src="https://static.bokeh.org/sponsor/anaconda.png"
501
+ alt="Anaconda Logo" width="200"/>
502
+ </a>
503
+ </td>
504
+ <td>
505
+ <a href="https://numfocus.org/">
506
+ <img src="https://numfocus.org/wp-content/uploads/2017/03/numfocusweblogo_orig-1.png"
507
+ alt="NumFOCUS Logo" width="200"/>
508
+ </a>
509
+ </td>
510
+
511
+ </tr>
512
+ </table>