def test_property_illegal_push(context_mock): prop_no_push = Property(name=DEFAULT_PROPERTY_NAME, default_value=DEFAULT_PROPERTY_VALUE, allow_push=False) prop_no_push.set_parent_path(DEFAULT_MODULE_NAME) wrapper = PropertyWrapper(prop=prop_no_push, ctx=context_mock, allow_read=True, allow_write=True) with LogCapture(attributes=strip_prefix) as log_capture: assert not wrapper.push(child=Property(name=CHILD_PROPERTY_NAME)) log_capture.check( f'Unauthorized push in property {DEFAULT_MODULE_NAME}:{DEFAULT_PROPERTY_NAME}!', )
def test_flag_property(context_mock): prop_base = Property(name="flag_prop", is_flag_property=True) prop_base.set_parent_path(DEFAULT_MODULE_NAME) prop_wrapper = PropertyWrapper(prop=prop_base, ctx=context_mock, allow_read=True, allow_write=True) assert (prop_base._lock.locked()) prop_wrapper.set(True) assert (prop_wrapper.get() is True) context_mock.emit.assert_any_call( SignalRef(f"{prop_wrapper.prop.id()}:changed"), parents=None, wipe=True, payload=True, boring=False) context_mock.emit.assert_any_call( SignalRef(f"{prop_wrapper.prop.id()}:true"), parents=None, wipe=True, boring=False) context_mock.emit.reset_mock() prop_wrapper.set(False) assert (prop_wrapper.get() is False) context_mock.emit.assert_any_call( SignalRef(f"{prop_wrapper.prop.id()}:changed"), parents=None, wipe=True, payload=False, boring=False) context_mock.emit.assert_any_call( SignalRef(f"{prop_wrapper.prop.id()}:false"), parents=None, wipe=True, boring=False) context_mock.emit.reset_mock() prop_wrapper.set(None) assert (prop_wrapper.get() is None) context_mock.emit.assert_called_once_with( SignalRef(f"{prop_wrapper.prop.id()}:changed"), parents=None, wipe=True, payload=None, boring=False)
def test_property_child(under_test_read_write: PropertyWrapper, default_property_base, context_mock): assert under_test_read_write.push( Property(name=CHILD_PROPERTY_NAME, default_value=DEFAULT_PROPERTY_VALUE)) assert list(under_test_read_write.enum())[0] == CHILD_PROPERTY_ID assert under_test_read_write.prop.children[CHILD_PROPERTY_NAME].read( ) == DEFAULT_PROPERTY_VALUE
def add_prop(self, *, prop: Property) -> None: """ Add a copy of a property to this context. An error message will be generated, if a property with the same name has already been added previously. Note: Context will adopt a __copy__ of the given property, the actual property will not be changed. * `prop`: The property object that should be added. """ # prop = prop.clone() if prop.id() in self._properties: logger.error(f"Attempt to add property {prop.id()} twice!") return # Do not adopt the with self._lock: # register property self._properties[prop.id()] = prop # register all of the property's signals for signal in prop.signals(): self._add_sig(signal)
def rm_prop(self, *, prop: Property) -> None: """ Remove a property from this context. Generates error message, if the property was not added with add_prop() to the context previously * `prop`: The property to remove.object """ if prop.id() not in self._properties: logger.error(f"Attempt to remove unknown property {prop.id()}!") return # remove property from context self._properties.pop(prop.id()) states_to_remove: Set[State] = set() with self._lock: # remove all of the property's signals for signal in prop.signals(): self._rm_sig(signal) # remove all states that depend upon property for st in self._activations_per_state: if prop.id() in st.get_all_props_ids(): states_to_remove.add(st) for st in states_to_remove: self.rm_state(st=st)
def push(self, child: Property): """ Add a child to the property or to children of the property * `child`: Parent-less, child-less property object to add. Name of the child must be unique among existing children of this property. **Returns:** True if the push was successful, False otherwise """ if not self.allow_write: logger.error( f"Unauthorized push access in property-wrapper {self.prop.id()}!" ) return False if self.prop.push(child): self.ctx.emit(self.prop.pushed(), parents=self.spike_parents, wipe=False, payload=child.id(), boring=self.boring) return True return False
def push(self, parent_property_or_path: Union[str, Property], child: Property): """ Add a child to a property. Note: Child must not yet have a parent or children of itself. Write-access to parent is needed. * `parent_property_or_path`: (Path of the) parent property that should receive the new child. * `child`: Parent-less, child-less property object to add. **Returns:** True if the push was successful, False otherwise """ if child.parent_path: logger.error( f"State {self.state.name} attempted to push child property {child.name} to parent {parent_property_or_path}, but it already has parent {child.parent_path}!" ) return False if isinstance(parent_property_or_path, Property): parent_property_or_path = parent_property_or_path.id() if parent_property_or_path in self.properties: if self.properties[parent_property_or_path].push(child): self.properties[child.id()] = PropertyWrapper( prop=child, ctx=self.ctx, spike_parents=self.spike_parents, allow_read=self.properties[parent_property_or_path]. allow_read, allow_write=self.properties[parent_property_or_path]. allow_write, boring=self.state.boring) self.ctx.add_prop(prop=child) return True else: logger.error( f'State {self.state.name} attempted to add child-property {child.name} to non-accessible parent {parent_property_or_path}!' ) return False
def test_property(under_test_read_only: PropertyWrapper, default_property_base: Property): assert (default_property_base.id() == f"{DEFAULT_MODULE_NAME}:{DEFAULT_PROPERTY_NAME}") assert (not default_property_base._lock.locked()) assert (default_property_base.read() == DEFAULT_PROPERTY_VALUE)
def default_property_base(): prop = Property(name=DEFAULT_PROPERTY_NAME, default_value=DEFAULT_PROPERTY_VALUE) prop.set_parent_path(DEFAULT_MODULE_NAME) return prop
CORE_MODULE_CONFIG = {IMPORT_MODULES_CONFIG_KEY: [], TICK_RATE_CONFIG_KEY: 20} with Module(name="core", config=CORE_MODULE_CONFIG) as core_module: """ The startup signal, which is fired once when `Context.run()` is executed. """ sig_startup = Signal("startup") """ The shutdown signal, which is fired once when `Context.shutdown()` is called. """ sig_shutdown = Signal("shutdown") prop_pressure = Property(name="pressure", allow_read=True, allow_write=True, allow_push=False, allow_pop=False, default_value=False, always_signal_changed=False, is_flag_property=True) prop_activity = Property(name="activity", allow_read=True, allow_write=True, allow_push=False, allow_pop=False, default_value=0, always_signal_changed=False, boring=True) def create_and_run_context(*args, runtime_overrides=None):