def test_literal_parse_num(): e = parse(None, '16384') assert isinstance(e, literalvalues.LiteralValue) assert e.value == 16384 e = parse(None, '-3.14') assert isinstance(e, literalvalues.LiteralValue) assert e.value == -3.14
def test_literal_parse_bool(): e = parse(None, 'false') assert isinstance(e, literalvalues.LiteralValue) assert e.value == 0 e = parse(None, 'true') assert isinstance(e, literalvalues.LiteralValue) assert e.value == 1
async def test_parse_complex_expression(num_mock_port1, num_mock_port2): num_mock_port1.set_last_read_value(5) num_mock_port2.set_last_read_value(-4) e = parse('nid1', 'ADD(10, MUL($, 3.14), $nid2)') assert round(await e.eval(), 1) == 21.7 e = parse('nid1', 'ADD(10, MUL($, 3.14), DIV(10, 2), MIN($nid2, 10, $))') assert round(await e.eval(), 1) == 26.7
async def enable(self) -> None: if self._enabled: return self.debug('enabling') self._enabled = True self.invalidate_attr('enabled') # Reset port expression if self._expression: sexpression = str(self._expression) self.debug('resetting expression "%s"', sexpression) self._expression = core_expressions.parse(self.get_id(), sexpression) main.force_eval_expressions(self) try: await self.handle_enable() except Exception: self.error('failed to enable') self._enabled = False raise
async def attr_set_transform_write(self, stransform_write: str) -> None: if not stransform_write: self._transform_write = None return try: self.debug('parsing expression "%s"', stransform_write) transform_write = core_expressions.parse(self.get_id(), stransform_write) deps = transform_write.get_deps() for dep in deps: if not dep.startswith('$'): continue if dep != f'${self._id}': raise core_expressions.ExternalDependency( port_id=dep[1:], pos=stransform_write.index(dep)) self.debug('setting write transform "%s"', transform_write) self._transform_write = transform_write except core_expressions.ExpressionParseError as e: self.error('failed to set transform write expression "%s": %s', stransform_write, e) raise InvalidAttributeValue('transform_write', details=e.to_json()) from e
async def attr_set_expression(self, sexpression: str) -> None: if not await self.is_writable(): self.error('refusing to set expression on non-writable port') raise PortError('Cannot set expression on non-writable port') if self._sequence: self.debug('canceling current sequence') await self._sequence.cancel() self._sequence = None if not sexpression: self._expression = None return try: self.debug('parsing expression "%s"', sexpression) expression = core_expressions.parse(self.get_id(), sexpression) self.debug('checking for expression circular dependencies') await core_expressions.check_loops(self, expression) except core_expressions.ExpressionParseError as e: self.error('failed to set expression "%s": %s', sexpression, e) raise InvalidAttributeValue('expression', details=e.to_json()) from e self.debug('setting expression "%s"', expression) self._expression = expression main.force_eval_expressions(self)
async def test_port_value_circular_dependency(num_mock_port1, num_mock_port2): num_mock_port1.set_writable(True) num_mock_port2.set_writable(True) await num_mock_port1.set_attr('expression', 'ADD($nid2, 10)') e2 = parse('nid2', 'ADD($nid1, 10)') with pytest.raises(CircularDependency): await check_loops(num_mock_port2, e2)
async def test_parse_whitespace(num_mock_port1, num_mock_port2): num_mock_port1.set_last_read_value(5) num_mock_port2.set_last_read_value(-4) context = { 'port_values': { 'nid1': num_mock_port1.get_last_read_value(), 'nid2': num_mock_port2.get_last_read_value() } } e = parse('nid1', ' ADD (\t10, MUL ( $, 3.14 ) , $nid2 ) ') assert round(await e.eval(context=context), 1) == 21.7
async def attr_set_transform_write(self, stransform_write: str) -> None: if not stransform_write: self._transform_write = None return try: self.debug('parsing expression "%s"', stransform_write) transform_write = core_expressions.parse(self.get_id(), stransform_write) deps = transform_write.get_deps() for dep in deps: if not dep.startswith('$'): continue if dep != f'${self._id}': raise core_expressions.ExpressionError('Transform expression depends other ports') self.debug('setting write transform "%s"', transform_write) self._transform_write = transform_write except core_expressions.ExpressionError as e: self.error('failed to set transform write expression "%s": %s', stransform_write, e) raise InvalidAttributeValue('transform_write') from e
async def test_parse_whitespace(num_mock_port1, num_mock_port2): num_mock_port1.set_last_read_value(5) num_mock_port2.set_last_read_value(-4) e = parse('nid1', ' ADD (\t10, MUL ( $, 3.14 ) , $nid2 ) ') assert round(await e.eval(), 1) == 21.7
def _prepare_filter(self) -> None: event_types = self._filter.get('type') if event_types: if not isinstance(event_types, list): event_types = (event_types, ) self._filter_event_types = set(event_types) port_value = self._filter.get('port_value') if port_value is not None: if isinstance(port_value, str): # An expression try: self.debug('using value expression "%s"', port_value) self._filter_port_value = core_expressions.parse( self_port_id=None, sexpression=port_value) except core_expressions.ExpressionParseError as e: self.error('failed to parse port expression "%s": %s', port_value, e) raise else: if not isinstance(port_value, list): port_value = (port_value, ) self._filter_port_value = port_value self._filter_port_value_transition = self._filter.get( 'port_value_transition') self._filter_device_attrs = { n[7:]: v for n, v in self._filter.items() if n.startswith('device_') and not n.endswith('_transition') } self._filter_device_attr_transitions = { n[7:-11]: v for n, v in self._filter.items() if n.startswith('device_') and n.endswith('_transition') } self._filter_port_attrs = { n[5:]: v for n, v in self._filter.items() if n.startswith('port_') and n != 'port_value' and not n.endswith('_transition') } self._filter_port_attr_transitions = { n[5:-11]: v for n, v in self._filter.items() if n.startswith('port_') and n != 'port_value_transition' and n.endswith('_transition') } self._filter_slave_attrs = { n[6:]: v for n, v in self._filter.items() if n.startswith('slave_') and not n.endswith('_transition') } self._filter_slave_attr_transitions = { n[6:-11]: v for n, v in self._filter.items() if n.startswith('slave_') and n.endswith('_transition') } self._filter_device_attr_names.update(self._filter_device_attrs.keys()) self._filter_device_attr_names.update( self._filter_device_attr_transitions.keys()) self._filter_port_attr_names.update(self._filter_port_attrs.keys()) self._filter_port_attr_names.update( self._filter_port_attr_transitions.keys()) self._filter_slave_attr_names.update(self._filter_slave_attrs.keys()) self._filter_slave_attr_names.update( self._filter_slave_attr_transitions.keys()) self._filter_prepared = True self.debug('filter prepared')
def test_port_value_parse(num_mock_port1, num_mock_port2): e = parse('nid1', '$nid2') assert isinstance(e, port.PortValue) assert e.port_id == 'nid2' assert e.get_port() == num_mock_port2
async def test_parse_unknown_function(): with pytest.raises(UnknownFunction) as exc_info: parse(None, 'ADD(10, UNKNOWN_FUNC(3, 14))') assert exc_info.value.name == 'UNKNOWN_FUNC' assert exc_info.value.pos == 9
def test_port_ref_parse(num_mock_port1, num_mock_port2): e = parse('nid1', '@nid2') assert isinstance(e, port.PortRef) assert e.port_id == 'nid2' assert e.get_port() == num_mock_port2
def test_port_ref_inexistent(num_mock_port1): e = parse('nid1', '@nid2') assert isinstance(e, port.PortRef) assert e.port_id == 'nid2' assert e.get_port() is None
async def test_parse_unbalanced_parentheses(): with pytest.raises(UnbalancedParentheses) as exc_info: parse(None, 'ADD(10)), MUL(3, 14))') assert exc_info.value.pos == 8
def test_port_value_inexistent(num_mock_port1): e = parse('nid1', '$nid2') assert isinstance(e, port.PortValue) assert e.port_id == 'nid2' assert e.get_port() is None
async def test_parse_unexpected_end(): with pytest.raises(UnexpectedEnd): parse(None, 'ADD(10, MUL(3, 14)')
async def test_parse_unexpected_character(): with pytest.raises(UnexpectedCharacter) as exc_info: parse(None, '-ADD(10, $)') assert exc_info.value.c == '-' assert exc_info.value.pos == 1 with pytest.raises(UnexpectedCharacter) as exc_info: parse(None, 'ADD#(10, $)') assert exc_info.value.c == '#' assert exc_info.value.pos == 4 with pytest.raises(UnexpectedCharacter) as exc_info: parse(None, 'ADD(%$port, 10)') assert exc_info.value.c == '%' assert exc_info.value.pos == 5 with pytest.raises(UnexpectedCharacter) as exc_info: parse(None, 'ADD($%port, 10)') assert exc_info.value.c == '%' assert exc_info.value.pos == 7 with pytest.raises(UnexpectedCharacter) as exc_info: parse(None, 'ADD($port*, 10)') assert exc_info.value.c == '*' assert exc_info.value.pos == 11 with pytest.raises(UnexpectedCharacter) as exc_info: parse(None, 'ADD(#, 10, $)') assert exc_info.value.c == '#' assert exc_info.value.pos == 5 with pytest.raises(UnexpectedCharacter) as exc_info: parse(None, '3+4') assert exc_info.value.c == '+' assert exc_info.value.pos == 2
async def test_parse_empty(): with pytest.raises(EmptyExpression): parse(None, ' ')