示例#1
0
    async def load_from_data(self, data: GenericJSONDict) -> None:
        attrs_start = ['enabled']  # These will be loaded first, in this order
        attrs_end = ['expression']  # These will be loaded last, in this order

        attr_items = data.items()
        attr_items = [a for a in attr_items if (a[0] not in attrs_start) and (a[0] not in attrs_end)]

        attr_items_start = []
        for n in attrs_start:
            v = data.get(n)
            if v is not None:
                attr_items_start.append((n, v))

        # Sort the rest of the attributes alphabetically
        attr_items.sort(key=lambda i: i[0])

        attr_items_end = []
        for n in attrs_end:
            v = data.get(n)
            if v is not None:
                attr_items_end.append((n, v))

        attr_items = attr_items_start + attr_items + attr_items_end

        for name, value in attr_items:
            if name in ('id', 'value'):
                continue  # Value is also among the persisted fields

            try:
                self.debug('loading %s = %s', name, json_utils.dumps(value))
                await self.set_attr(name, value)

            except Exception as e:
                self.error('failed to set attribute %s = %s: %s', name, json_utils.dumps(value), e)

        # Value
        if await self.is_persisted() and data.get('value') is not None:
            self._value = data['value']
            self.debug('loaded value = %s', json_utils.dumps(self._value))

            if await self.is_writable():
                # Write the just-loaded value to the port
                value = self._value
                if self._transform_write:
                    value = await self.adapt_value_type(self._transform_write.eval())

                await self.write_value(value)

        elif self.is_enabled():
            try:
                value = await self.read_transformed_value()
                if value is not None:
                    self._value = value
                    self.debug('read value = %s', json_utils.dumps(self._value))

            except Exception as e:
                self.error('failed to read value: %s', e, exc_info=True)
示例#2
0
    def _record_from_db(cls, db_record: GenericJSONDict, fields: Optional[Set[str]] = None) -> Record:
        if fields is not None:
            return {k: (cls._value_from_db(v) if k != 'id' else v) for k, v in db_record.items() if k in fields}

        else:
            return {k: (cls._value_from_db(v) if k != 'id' else v) for k, v in db_record.items()}
示例#3
0
async def set_port_attrs(port: core_ports.BasePort, attrs: GenericJSONDict, ignore_extra_attrs: bool) -> None:
    non_modifiable_attrs = await port.get_non_modifiable_attrs()

    def unexpected_field_code(field: str) -> str:
        if field in non_modifiable_attrs:
            return 'attribute-not-modifiable'

        else:
            return 'no-such-attribute'

    schema = await port.get_schema()
    if ignore_extra_attrs:
        schema = dict(schema)
        schema['additionalProperties'] = True  # Ignore non-existent and non-modifiable attributes

    core_api_schema.validate(
        attrs,
        schema,
        unexpected_field_code=unexpected_field_code,
        unexpected_field_name='attribute'
    )

    # Step validation
    attrdefs = await port.get_attrdefs()
    for name, value in attrs.items():
        attrdef = attrdefs.get(name)
        if attrdef is None:
            continue

        step = attrdef.get('step')
        min_ = attrdef.get('min')
        if None not in (step, min_) and step != 0 and (value - min_) % step:
            raise core_api.APIError(400, 'invalid-field', field=name)

    errors_by_name = {}

    async def set_attr(attr_name: str, attr_value: Attribute) -> None:
        core_api.logger.debug('setting attribute %s = %s on %s', attr_name, json_utils.dumps(attr_value), port)

        try:
            await port.set_attr(attr_name, attr_value)

        except Exception as e1:
            errors_by_name[attr_name] = e1

    value = attrs.pop('value', None)

    if attrs:
        await asyncio.wait([set_attr(n, v) for n, v in attrs.items()])

    if errors_by_name:
        name, error = next(iter(errors_by_name.items()))

        if isinstance(error, core_api.APIError):
            raise error

        elif isinstance(error, core_ports.InvalidAttributeValue):
            raise core_api.APIError(400, 'invalid-field', field=name, details=error.details)

        elif isinstance(error, core_ports.PortTimeout):
            raise core_api.APIError(504, 'port-timeout')

        elif isinstance(error, core_ports.PortError):
            raise core_api.APIError(502, 'port-error', code=str(error))

        else:
            # Transform any unhandled exception into APIError(500)
            raise core_api.APIError(500, 'unexpected-error', message=str(error)) from error

    # If value is supplied among attrs, use it to update port value, but in background and ignoring any errors
    if value is not None and port.is_enabled():
        asyncio.create_task(port.write_transformed_value(value, reason=core_ports.CHANGE_REASON_API))

    await port.save()
示例#4
0
 def _record_from_db(cls, db_record: GenericJSONDict) -> Record:
     return {
         k: (cls._value_from_db(v) if k != 'id' else v)
         for k, v in db_record.items()
     }