Skip to content

Commit

Permalink
Allow Variables to reference each other (#259)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr authored Mar 24, 2022
1 parent a539aa2 commit 3b84954
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 23 deletions.
3 changes: 1 addition & 2 deletions lumen/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,7 @@ def __init__(self, specification=None, **params):
self.auth = Auth.from_spec(state.spec.get('auth', {}))
self.config = Config.from_spec(state.spec.get('config', {}))
self.defaults = Defaults.from_spec(state.spec.get('defaults', {}))
vars = Variables.from_spec(state.spec.get('variables', {}))
self.variables = state._variables[pn.state.curdoc] = vars
self.variables = Variables.from_spec(state.spec.get('variables', {}))
self.defaults.apply()

# Load and populate template
Expand Down
6 changes: 5 additions & 1 deletion lumen/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,14 @@ def _resolve_source_ref(self, refs):
def resolve_reference(self, reference, variables=None):
if not is_ref(reference):
raise ValueError('References should be prefixed by $ symbol.')
from .variables import Variable
refs = reference[1:].split('.')
vars = variables or self.variables
if refs[0] == 'variables':
return vars[refs[1]]
value = vars[refs[1]]
if isinstance(value, Variable):
value = value.value
return value
return self._resolve_source_ref(refs)


Expand Down
2 changes: 1 addition & 1 deletion lumen/ui/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def _yaml_upload(self, event):
return

def _create_new(self, event):
self.spec = {'config': {}, 'sources': {}, 'targets': []}
self.spec = {'config': {}, 'sources': {}, 'targets': [], 'variables': {}}

def _selected(self, event):
self.spec = event.obj.spec
61 changes: 42 additions & 19 deletions lumen/variables/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,34 +20,48 @@


class Variables(param.Parameterized):
"""
The Variables component stores a number Variable types and mirrors
their values onto dynamically created parameters allowing other
components to easily watch changes in a variable.
"""

def __init__(self, **vars):
super().__init__()
for p, var in vars.items():
vtype = type(var.value)
ptype = _PARAM_MAP.get(vtype, param.Parameter)
self.param.add_parameter(p, ptype())
var.param.watch(partial(self._update_value, p), 'value')
var.param.trigger('value')
self._vars = vars

def _update_value(self, p, event):
self.param.update({p: event.new})

def __getitem__(self, key):
return getattr(self, key)
def __init__(self, **params):
super().__init__(**params)
self._vars = {}

@classmethod
def from_spec(cls, spec):
vars = {}
variables = cls()
if pn.state.curdoc:
state._variables[pn.state.curdoc] = variables
for name, var_spec in spec.items():
if not isinstance(var_spec, dict):
var_spec = {
'type': 'constant',
'default': var_spec
}
vars[name] = Variable.from_spec(dict(var_spec, name=name), vars)
return cls(**vars)
var = Variable.from_spec(dict(var_spec, name=name), variables)
variables.add_variable(var)
return variables

def add_variable(self, var):
"""
Adds a new variable to the Variables instance and sets up
a parameter that can be watched.
"""
self._vars[var.name] = var
self.param.add_parameter(var.name, param.Parameter(default=var.value))
var.param.watch(partial(self._update_value, var.name), 'value')

def _update_value(self, name, event):
self.param.update({name: event.new})

def __getitem__(self, key):
if key in self.param:
return getattr(self, key)
else:
raise KeyError(f'No variable named {key!r} has been defined.')

@property
def panel(self):
Expand All @@ -60,6 +74,14 @@ def panel(self):


class Variable(Component):
"""
A Variable may declare a static or dynamic value that can be
referenced from other components. The source of the Variable value
can be anything from an environment variable to a widget or URL
parameter. Variable components allow a concise way to configure
other components and make it possible to orchestrate actions across
multiple components.
"""

default = param.Parameter(doc="""
Default value to use if no other value is defined""")
Expand All @@ -74,7 +96,8 @@ class Variable(Component):
secure = param.Boolean(default=False, constant=True, doc="""
Whether the variable should be treated as secure.""")

value = param.Parameter()
value = param.Parameter(doc="""
The materialized value of the variable.""")

variable_type = None

Expand Down

0 comments on commit 3b84954

Please sign in to comment.