def get(self, obj, cls=None): # Call notify_access with event type fetch # If and only if one event exists, a return value will be produced # This return value is saved as the current value in obj._trait_values # Then call super get try: r = obj.notify_access( T.Bunch( name=self.name, value=obj._trait_values[self.name], owner=self, type="fetch", )) old_value = obj._trait_values[self.name] if r is not None and r != old_value: logger.debug( "Response from callback event 'fetch' on property {} does not match previous value. Overloading existing value {} with new value {}" .format(self.name, old_value, r)) obj._trait_values[self.name] = r except KeyError: pass return super().get(obj, cls=cls)
def notify_access(self, bunch): if not isinstance(bunch, T.Bunch): # cast to bunch if given a dict bunch = T.Bunch(bunch) name, type = bunch.name, bunch.type callables = [] callables.extend(self._trait_notifiers.get(name, {}).get(type, [])) callables.extend(self._trait_notifiers.get(name, {}).get(T.All, [])) callables.extend(self._trait_notifiers.get(T.All, {}).get(type, [])) callables.extend(self._trait_notifiers.get(T.All, {}).get(T.All, [])) # Call them all now # Traits catches and logs errors here. I allow them to raise if len(callables) > 1: raise TypeError( "Maximum number of callables allowed for a single attribute using the 'fetch' event is 1. Please check the documentation of DiTTo" ) for c in callables: # Bound methods have an additional 'self' argument. if isinstance(c, _CallbackWrapper): c = c.__call__ elif isinstance(c, EventHandler) and c.name is not None: c = getattr(self, c.name) return c(bunch)
def remove_traits(self, *names): "Remove traits that were dynamically added to the HasTraits instance" # Thanks to our guarantee from our `add_traits` that: # - `type(self)` is a singleton class and nobody else cares about it # - all relevant `TraitType`s are set on `type(self).__dict__` and nowhere else # to remove traits... we just delete them from `type(self)`. Pretty simple. if not names: return cls = type(self) for name in names: try: old = self._trait_values.pop(name) except KeyError: raise ValueError("The trait {} does not exist on {}".format( name, self)) delattr(cls, name) # to play by the rules (and make the map update on parameter deletion), # we notify that the trait is deleted. but `delete` is a non-standard # event, so don't expect anyone to be listening. self.notify_change( traitlets.Bunch( name=name, old=old, new=traitlets.Undefined, owner=self, type="delete", ))
def _on_change(self, change): self._notify_object.notify_change( traitlets.Bunch(change, name=self._notify_name, key=change["name"])) # ^ NOTE(gabe): Bunch is workaround for traitlets bug https://github.com/ipython/traitlets/pull/536 new_contents = self._make_widget_contents() self.widget.children = new_contents
def remove_traits(self, *names): """ Remove traits that were dynamically added to the HasTraits instance If you are manipulating a ParameterSet generated from a layer, instead use :meth:`update <descarteslabs.workflows.interactive.ParameterSet.update>`, which handles adding and removing traits in a more delclarative way. Example ------- >>> import traitlets >>> import descarteslabs.workflows as wf >>> class Listener(traitlets.HasTraits): ... @traitlets.observe("param") ... def handler(self, change): ... print(change['key']) ... print(change) >>> listener = Listener() >>> ps = wf.interactive.ParameterSet(listener, "param", foo=traitlets.Float()) # doctest: +SKIP >>> ps.foo = 1.1 # doctest: +SKIP foo {'name': 'param', 'old': 0.0, 'new': 1.1, 'owner': ParameterSet({'foo': 1.1}), 'type': 'change', 'key': 'foo' } >>> ps.add_traits(bar=traitlets.Unicode()) # doctest: +SKIP >>> ps.bar = 'quix' # doctest: +SKIP bar {'name': 'param', 'old': '', 'new': 'quix', 'owner': ParameterSet({'bar': 'quix', 'foo': 1.1}), 'type': 'change', 'key': 'bar' } >>> ps.remove_traits("foo") # doctest: +SKIP >>> ps.foo = 2.2 # doctest: +SKIP >>> # ^ nothing is printed, `foo` is no longer a trait >>> ps.to_dict() # doctest: +SKIP {'bar': 'quix'} """ # Thanks to our guarantee from our `add_traits` that: # - `type(self)` is a singleton class and nobody else cares about it # - all relevant `TraitType`s are set on `type(self).__dict__` and nowhere else # to remove traits... we just delete them from `type(self)`. Pretty simple. if not names: return cls = type(self) for name in names: try: old = self._trait_values.pop(name) except KeyError: raise ValueError("The trait {} does not exist on {}".format( name, self)) delattr(cls, name) # to play by the rules (and make the map update on parameter deletion), # we notify that the trait is deleted. but `delete` is a non-standard # event, so don't expect anyone to be listening. self.notify_change( traitlets.Bunch( name=name, old=old, new=traitlets.Undefined, owner=self, type="delete", ))