holoviz / param

Param: Make your Python code clearer and more reliable by declaring Parameters
https://param.holoviz.org
BSD 3-Clause "New" or "Revised" License
412 stars 69 forks source link

Add a `metadata` slot #898

Open maximlt opened 6 months ago

maximlt commented 6 months ago

Libraries like Pydantic, attrs, Traitlets and the built-in dataclasses all offer a way to attach metadata to their field/attributes with some more or less convenient API to retrieve them. Parameters in Param doesn't have a similar slot.

From experience I know I've needed that a few times and I've definitely abused the precedence slot to attach some data to Parameters.

Another motivation to add a metadata slot is related to Panel. The Param Pane offered by Panel is very practical to turn a Parameterized class into a viewable component, Panel maintaining a mapping between Parameter types and its widget types. However, sometimes one needs to configure a widget just a bit more and in this case Panel offers two approaches shown below: (1) pass additional widget configs to pn.Param or (2) use <WidgetClass>.from_param(<parameter>, ...) to instantiate a widget manually.

image

import param
import panel as pn

pn.extension()

class User(param.Parameterized):
    fullname = param.String()

user = User()

pn.Param(user.param, widgets={'fullname': {'width': 100}})  # 1

pn.widgets.TextInput.from_param(user.param.fullname, width=100)  # 2

I tend to use option (2) as there's often something custom I need to do (e.g. adding some text in between widgets, laying them out in a special way) that isn't made possible by (1), or at least not in a straightforward way. This makes the code quite verbose. Instead Panel could leverage the metadata slot to automatically pass widget kwargs collected from a special namespace (e.g. panel_kwargs) to .from_param() (that I believe is used in the implementation of pn.Param):

class User(param.Parameterized):
    fullname = param.String(metadata={'panel_kwargs': {'width': 100}})

user = User()
pn.Param(user.param)

This was recently discussed in https://github.com/holoviz/panel/issues/5856.


metadata attribute in dataclasses, attrs, pydantic and traitlets:

image

Code

```python metadata = {'namespace': {'key1': 'value1'}} ## `dataclasses` import dataclasses @dataclasses.dataclass class User: name: str = dataclasses.field(metadata=metadata) user = User(name='bob') dataclasses.fields(user)[0].metadata ## `attrs` import attrs @attrs.define class User: name: str = attrs.field(metadata=metadata) user = User(name='bob') attrs.fields(User)[0].metadata ## `pydantic` import pydantic class User(pydantic.BaseModel): name: str = pydantic.Field(metadata=metadata) user = User(name='bob') user.model_fields['name'].json_schema_extra['metadata'] ## `traitlets` import traitlets class User(traitlets.HasTraits): name = traitlets.Unicode().tag(**metadata) user = User(name='bob') user.trait_metadata('name', 'namespace') ```