Add a `metadata` slot
Opened this issue · 0 comments
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. Parameter
s 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.
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 holoviz/panel#5856.
metadata attribute in dataclasses, attrs, pydantic and traitlets:
Code
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')