Skip to content
Snippets Groups Projects
Commit 8dd9f345 authored by Matthias Stefan Bodenbenner's avatar Matthias Stefan Bodenbenner
Browse files

replaced "nonce" with "label"

refactored serialize.py
parent 2f77285e
Branches
Tags 6.3.0
No related merge requests found
[![Build](https://git-ce.rwth-aachen.de/wzl-mq-ms/forschung-lehre/lava/unified-device-interface/python/badges/master/pipeline.svg)](https://git-ce.rwth-aachen.de/wzl-mq-ms/forschung-lehre/lava/unified-device-interface/python/commits/master)
# Python Unified Device Interface
Current stable version: 6.2.0
Current stable version: 6.3.0
Stable legacy version: 5.2.7
## Installation
......@@ -65,6 +65,11 @@ Funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation)
## Recent changes
**6.3.0** - 2022-06-09
- added property "label" for measurements as replacement for "nonce"
- marked usage of keyword "nonce" as deprecated
**6.2.0** - 2022-06-02
- added XML as dataformat for response bodies and published messages
......
from setuptools import setup, find_packages
setup(name='wzl-udi',
version='6.2.0',
version='6.3.0',
url='https://git-ce.rwth-aachen.de/wzl-mq-public/soil/python',
author='Matthias Bodenbenner',
author_email='m.bodenbenner@wzl.mq.rwth-aachen.de',
......
# -*- coding: utf-8 -*-
import asyncio
import traceback
import functools
from typing import Dict
from typing import Dict, Any
from aiohttp import web
from aiohttp.web import middleware
from aiohttp.web_request import Request
from wzl.utilities import root_logger
from .error import ServerException
......@@ -19,7 +21,7 @@ from ..soil.object import Object
from ..soil.component import Component
from ..soil.parameter import Parameter
from ..utils.constants import BASE_UUID_PATTERN, HTTP_GET, HTTP_OPTIONS
from ..utils.serializer import XMLSerializer
from ..utils import serialize
logger = root_logger.get(__name__)
......@@ -50,8 +52,21 @@ async def cors(request, handler):
class HTTPServer(object):
"""Provides a web application as of the aiohttp-library.
def __init__(self, loop, host, port, model, dataformat='json'):
"""
def __init__(self, loop: asyncio.ProactorEventLoop, host: str, port: int, model: Component,
dataformat: str = 'json'):
"""Constructor
Args:
loop: The asyncio-event-loop used to execute the server.
host: Hostname of the server, i.e. a URL such as 'localhost'.
port: Port the server should run at.
model: The root component of the SOIL model, should be initialized via Component.load(...)
dataformat: String specifying the dataformat of the responses of the server, either 'json' (default) or 'xml'.
"""
if dataformat not in ['json', 'xml']:
raise ValueError('Dataformat must be one of "json" or "xml".')
......@@ -80,7 +95,15 @@ class HTTPServer(object):
logger.info('HTTP-Server serving on {}:{}'.format(host, port))
@staticmethod
def parse_uuids(request):
def parse_uuids(request: Request):
"""Splits the request URL to extract the FQID of the targeted element of the SOIL-Interface.
Args:
request:
Returns:
"""
uuids = request.match_info.get('uuids', 'uuids')
uuid_list = uuids.split('/')
while '' in uuid_list:
......@@ -103,7 +126,7 @@ class HTTPServer(object):
root = 'parameter'
else:
root = 'error'
xml = XMLSerializer.serialize(root, body)
xml = serialize.to_xml(root, body)
return web.Response(text=xml, status=status, content_type='application/xml')
async def get(self, request):
......
from deprecated import deprecated
import warnings
from typing import Dict
from deprecated import deprecated
from wzl.utilities import root_logger
from .figure import Figure
from ..utils.constants import HTTP_GET
from ..utils.error import SerialisationException
from .figure import Figure
logger = root_logger.get(__name__)
class Measurement(Figure):
def __init__(self, uuid, name, description, datatype, dimension, range, getter, unit, nonce=None, ontology: str = None):
def __init__(self, uuid, name, description, datatype, dimension, range, getter, unit, label=None,
ontology: str = None):
Figure.__init__(self, uuid, name, description, datatype, dimension, range, None, getter, ontology)
if uuid[:3] != 'MEA':
raise Exception('{}: The UUID must start with MEA!'.format(uuid))
......@@ -19,7 +22,7 @@ class Measurement(Figure):
self._covariance = None
self._uncertainty = None
self._timestamp = None
self._nonce = nonce
self._label = label
@property
def unit(self):
......@@ -38,8 +41,14 @@ class Measurement(Figure):
return self._timestamp
@property
@deprecated(version='6.3.0',
reason='"Nonce" has been renamed to "label".')
def nonce(self):
return self._nonce
return self._label
@property
def label(self):
return self._label
def __getitem__(self, item: str, method=HTTP_GET):
"""
......@@ -52,7 +61,12 @@ class Measurement(Figure):
if item == "unit":
return self._unit
if item == 'nonce':
return self._nonce
warnings.warn(
'Usage of the keyword "nonce" is deprecated and will be removed in future versions. Use "label" instead.',
DeprecationWarning)
return self._label
if item == 'label':
return self._label
if item == 'covariance':
return self._covariance
if item == 'uncertainty':
......@@ -73,7 +87,12 @@ class Measurement(Figure):
if key in ['value', 'timestamp', 'covariance', 'uncertainty']:
raise KeyError('The {} attribute of a measurement can not be set manually!'.format(key))
elif key == "nonce":
self._nonce = self._nonce
warnings.warn(
'Usage of the keyword "nonce" is deprecated and will be removed in future versions. Use "label" instead.',
DeprecationWarning)
self._label = self._label
elif key == "label":
self._label = self._label
elif key == 'unit':
self._unit = self._unit
else:
......@@ -88,7 +107,8 @@ class Measurement(Figure):
"""
# list is empty provide all attributes of the default-serialization
if not keys:
keys = ['uuid', 'name', 'description', 'datatype', 'value', 'dimension', 'range', 'timestamp', 'nonce', 'covariance', 'uncertainty',
keys = ['uuid', 'name', 'description', 'datatype', 'value', 'dimension', 'range', 'timestamp', 'label',
'nonce', 'covariance', 'uncertainty',
'unit', 'ontology']
if 'value' in keys and 'timestamp' not in keys:
keys += ['timestamp']
......@@ -117,15 +137,19 @@ class Measurement(Figure):
uuid = dictionary['uuid']
if uuid[:3] != 'MEA':
raise SerialisationException(
'The Measurement can not be deserialized. The UUID must start with MEA, but actually starts with {}!'.format(uuid[:3]))
'The Measurement can not be deserialized. The UUID must start with MEA, but actually starts with {}!'.format(
uuid[:3]))
if 'name' not in dictionary:
raise SerialisationException('{}: The measurement can not be deserialized. Name is missing!'.format(uuid))
if 'description' not in dictionary:
raise SerialisationException('{}: The measurement can not be deserialized. Description is missing!'.format(uuid))
raise SerialisationException(
'{}: The measurement can not be deserialized. Description is missing!'.format(uuid))
if 'datatype' not in dictionary:
raise SerialisationException('{}: The measurement can not be deserialized. Datatype is missing!'.format(uuid))
raise SerialisationException(
'{}: The measurement can not be deserialized. Datatype is missing!'.format(uuid))
if 'dimension' not in dictionary:
raise SerialisationException('{}: The measurement can not be deserialized. Dimension is missing!'.format(uuid))
raise SerialisationException(
'{}: The measurement can not be deserialized. Dimension is missing!'.format(uuid))
if 'value' not in dictionary:
raise SerialisationException('{}: The measurement can not be deserialized. Value is missing!'.format(uuid))
if 'range' not in dictionary:
......@@ -134,7 +158,8 @@ class Measurement(Figure):
raise SerialisationException('{}: The measurement can not be deserialized. Unit is missing!'.format(uuid))
try:
ontology = dictionary['ontology'] if 'ontology' in dictionary else None
return Measurement(dictionary['uuid'], dictionary['name'], dictionary['description'], dictionary['datatype'], dictionary['dimension'],
return Measurement(dictionary['uuid'], dictionary['name'], dictionary['description'],
dictionary['datatype'], dictionary['dimension'],
dictionary['range'], implementation, dictionary['unit'], ontology)
except Exception as e:
raise SerialisationException('{}: The measurement can not be deserialized. {}'.format(uuid, e))
......@@ -7,7 +7,7 @@ from wzl.utilities import root_logger
from wzl.mqtt.client import MQTTPublisher
from .event import Event
from ..utils.serializer import XMLSerializer
from ..utils import serialize
logger = root_logger.get(__name__)
......@@ -251,7 +251,7 @@ class StreamScheduler(object):
if self._dataformat == 'json':
message = json.dumps(job.data)
elif self._dataformat == 'xml':
message = XMLSerializer.serialize(job.type, job.data)
message = serialize.to_xml(job.type, job.data)
publisher.publish(job.topic, message, 1)
job.schedule()
next = job.determine_next(next)
......
......@@ -2,23 +2,28 @@ from typing import Any, Dict
from xml.etree.ElementTree import Element, tostring
class XMLSerializer(object):
def to_xml(root: str, data: Dict) -> str:
""" Takes a dictionary and translates it to a XML-string.
@staticmethod
Args:
root: String used as tag of the enclosing root-element of the xml-tree.
data: Dictionary to be serialized to XML-string.
Returns:
String of an XML-Element-Tree.
"""
def _recursive_serialization(tag: str, data: Any) -> Element:
element = Element(tag)
if isinstance(data, dict):
for key, value in data.items():
element.append(XMLSerializer._recursive_serialization(key, value))
element.append(_recursive_serialization(key, value))
elif isinstance(data, list):
for index, value in enumerate(data):
child = XMLSerializer._recursive_serialization(tag, value)
child = _recursive_serialization(tag, value)
child.set('index', str(index))
element.append(child)
else:
element.text = str(data)
return element
@staticmethod
def serialize(root: str, data: Dict) -> str:
return tostring(XMLSerializer._recursive_serialization(root, data), encoding='unicode')
return tostring(_recursive_serialization(root, data), encoding='unicode')
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment