diff --git a/Attribute_wrapper/__init__.py b/Attribute_wrapper/__init__.py index a917d916747f8d6a1c191e57c15ffd953486b29a..2787b160cc4c457efb5bd2fd32a15dbf21c7bd7c 100644 --- a/Attribute_wrapper/__init__.py +++ b/Attribute_wrapper/__init__.py @@ -5,4 +5,4 @@ try: except ImportError: # for Python<3.8 import importlib_metadata as metadata -__version__ = metadata.version("AttributeWrapper") +#__version__ = metadata.version("AttributeWrapper") diff --git a/Attribute_wrapper/attribute_wrapper.py b/Attribute_wrapper/attribute_wrapper.py index 4923c17127a80407c448092badf731dfe919350d..cbb0138a9eca149e9d2d955569d1a82636420b44 100644 --- a/Attribute_wrapper/attribute_wrapper.py +++ b/Attribute_wrapper/attribute_wrapper.py @@ -4,6 +4,7 @@ import logging from functools import reduce from operator import mul +from functools import wraps import numpy from tango import AttrWriteType, AttReqType @@ -13,6 +14,7 @@ logger = logging.getLogger() __all__ = ["AttributeWrapper"] + def fault_on_error(): """ Wrapper to catch exceptions. Sets the device in a FAULT state if any occurs. @@ -63,7 +65,8 @@ class AttributeIO(object): def cached_write_function(self, value): """Writes the given value to the device, and updates the cache.""" - # flexible array sizes are not supported by all clients. make sure we only write arrays of maximum size. + # flexible array sizes are not supported by all clients. make sure we only + # write arrays of maximum size. if self.attribute_wrapper.shape != (): if isinstance(value, numpy.ndarray): value_shape = value.shape @@ -117,14 +120,19 @@ class AttributeWrapper(attribute): f"Attribute needs to be a Tango-supported numpy, str or bool type, but has type {datatype}" ) - self.comms_id = comms_id # store data that can be used to identify the comms interface to use. not used by the wrapper itself - self.comms_annotation = comms_annotation # store data that can be used by the comms interface. not used by the wrapper itself + # store data that can be used to identify the comms interface to use. not + # used by the wrapper itself + self.comms_id = comms_id + # store data that can be used by the comms interface. not used by the + # wrapper itself + self.comms_annotation = comms_annotation self.datatype = datatype if dims == (1,): # scalar - # Tango defines a scalar as having dimensions (1,0), see https://pytango.readthedocs.io/en/stable/server_api/attribute.html + # Tango defines a scalar as having dimensions (1,0), see + # https://pytango.readthedocs.io/en/stable/server_api/attribute.html max_dim_x = 1 max_dim_y = 0 dtype = datatype @@ -181,7 +189,8 @@ class AttributeWrapper(attribute): read_func_wrapper reads the attribute value, stores it and returns it" """ - # lofar.read_attribute ignores fisallowed. So check again if we're allowed to read. + # lofar.read_attribute ignores fisallowed. So check again if we're + # allowed to read. if not device.is_attribute_access_allowed(AttReqType.READ_REQ): return None @@ -205,7 +214,8 @@ class AttributeWrapper(attribute): read_func_wrapper reads the attribute value, stores it and returns it" """ - # lofar.read_attribute ignores fisallowed. So check again if we're allowed to read. + # lofar.read_attribute ignores fisallowed. So check again if we're + # allowed to read. if not device.is_attribute_access_allowed(AttReqType.READ_REQ): return None @@ -225,7 +235,8 @@ class AttributeWrapper(attribute): # The provided function will be used with the call signature "(device: Device, req_type: AttReqType) -> bool". # # NOTE: fisallowed=<callable> does not work: https://gitlab.com/tango-controls/pytango/-/issues/435 - # So we have to use fisallowed=<str> here, which causes the function device.<str> to be called. + # So we have to use fisallowed=<str> here, which causes the function + # device.<str> to be called. super().__init__( dtype=dtype, max_dim_y=max_dim_y, diff --git a/Attribute_wrapper/cool_module.py b/Attribute_wrapper/cool_module.py deleted file mode 100644 index bd9416c291b7ed52a278bc6966f2dac155e4aa05..0000000000000000000000000000000000000000 --- a/Attribute_wrapper/cool_module.py +++ /dev/null @@ -1,6 +0,0 @@ -""" Cool module containing functions, classes and other useful things """ - - -def greeter(): - """Prints a nice message""" - print("Hello World!") diff --git a/VERSION b/VERSION new file mode 100644 index 0000000000000000000000000000000000000000..49d59571fbf6e077eece30f8c418b6aad15e20b0 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1 diff --git a/requirements.txt b/requirements.txt index 87f08311d942584129c1ccd977b00ca3d357a2b5..ad16c920e10a99e58dc092cad31462d0daa29c4d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ numpy >= 1.20.0 # BSD -testtools>=2.2.0 # MIT \ No newline at end of file +stestr>=3.0.0 # Apache-2.0 +testscenarios>=0.5.0 # Apache-2.0/BSD +testtools>=2.4.0 # MIT \ No newline at end of file diff --git a/tests/test_attr_wrapper.py b/tests/test_attr_wrapper.py index 24d798869beac09d6c26f5618021702ec0430000..97985b1a55c93b191ecf14d170043ded9b71c661 100644 --- a/tests/test_attr_wrapper.py +++ b/tests/test_attr_wrapper.py @@ -6,7 +6,7 @@ import asyncio -import mock +from unittest import mock import numpy # External imports @@ -15,11 +15,10 @@ from tango.server import attribute, command, Device, DeviceMeta # Test imports from tango.test_context import DeviceTestContext -import AttributeWrapper -from tangostationcontrol.test import base +from Attribute_wrapper.attribute_wrapper import AttributeWrapper # Internal imports -from test.test_client import TestClient +from test_client import TestClient SCALAR_DIMS = (1,) SPECTRUM_DIMS = (4,) @@ -37,11 +36,12 @@ def dev_init(device): asyncio.run(i.async_set_comm_client(device, device.test_client)) device.test_client.start() + class device_wrapper(Device, metaclass=DeviceMeta): @classmethod def attr_list(cls): """Return a list of all the AttributeWrapper members of this class.""" - return [v for k, v in cls.__dict__.items() if type(v) == AttributeWrapper] + return [v for k, v in cls.__dict__.items() if isinstance(v, AttributeWrapper)] def off(self): self.set_state(DevState.OFF) @@ -52,12 +52,11 @@ class device_wrapper(Device, metaclass=DeviceMeta): def initialise(self): self.set_state(DevState.INIT) -class TestAttributeTypes(base.TestCase): + +class TestAttributeTypes(): def setUp(self): # Avoid the device trying to access itself as a client - self.deviceproxy_patch = mock.patch.object( - Device, "DeviceProxy" - ) + self.deviceproxy_patch = mock.patch.object(device_wrapper, "DeviceProxy") self.deviceproxy_patch.start() self.addCleanup(self.deviceproxy_patch.stop) @@ -572,7 +571,6 @@ class TestAttributeTypes(base.TestCase): def read_R_test(self, dev, dtype, test_type): """Test device""" with DeviceTestContext(dev, process=True) as proxy: - # initialise proxy.initialise() proxy.on() @@ -589,7 +587,8 @@ class TestAttributeTypes(base.TestCase): proxy.image_R ) # is needed for STR since they act differently - # cant use all() for 2d arrays so instead compare the dimensions and then flatten to 2d + # cant use all() for 2d arrays so instead compare the dimensions and + # then flatten to 2d self.assertEqual( val.shape, expected.shape, @@ -632,13 +631,11 @@ class TestAttributeTypes(base.TestCase): def write_RW_test(self, dev, dtype, test_type): """Test device""" with DeviceTestContext(dev, process=True) as proxy: - # initialise proxy.initialise() proxy.on() if test_type == "scalar": - if dtype is str: val = STR_SCALAR_VAL else: @@ -678,7 +675,6 @@ class TestAttributeTypes(base.TestCase): try: with DeviceTestContext(dev, process=True) as proxy: - # initialise proxy.initialise() proxy.on() @@ -695,7 +691,8 @@ class TestAttributeTypes(base.TestCase): proxy.image_RW ) # is needed for STR since they act differently - # cant use all() for 2d arrays so instead compare the dimensions and then flatten to 2d + # cant use all() for 2d arrays so instead compare the dimensions and + # then flatten to 2d self.assertEqual( val.shape, expected.shape, @@ -792,7 +789,6 @@ class TestAttributeTypes(base.TestCase): """Test device""" try: with DeviceTestContext(dev, process=True) as proxy: - # initialise proxy.initialise() proxy.on() @@ -1029,12 +1025,10 @@ class TestAttributeTypes(base.TestCase): ) -class TestAttributeAccess(base.TestCase): +class TestAttributeAccess(): def setUp(self): # Avoid the device trying to access itself as a client - self.deviceproxy_patch = mock.patch.object( - Device, "DeviceProxy" - ) + self.deviceproxy_patch = mock.patch.object(Device, "DeviceProxy") self.deviceproxy_patch.start() self.addCleanup(self.deviceproxy_patch.stop) diff --git a/tests/test_client.py b/tests/test_client.py index 1b31de2c47c7ec2255d98cbaa8e3c40e82af47ee..b1a0c98591294b4b6b13c7da0531cc455436d64e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -6,13 +6,10 @@ import logging # External imports import numpy -# Test imports -from tangostationcontrol.clients.comms_client import CommClient - logger = logging.getLogger() -class TestClient(CommClient): +class TestClient: """ this class provides an example implementation of a comms_client. During initialisation it creates a correctly shaped zero filled value. on read that value is returned and on write its modified. @@ -126,7 +123,8 @@ class TestClient(CommClient): # process the comms_annotation self._setup_annotation(annotation) - # get all the necessary data to set up the read/write functions from the AttributeWrapper + # get all the necessary data to set up the read/write functions from the + # AttributeWrapper dims, dtype = self._setup_value_conversion(attribute) # configure and return the read/write functions diff --git a/tox.ini b/tox.ini index d5388949afbca56f8f7bd3c979409c7cfd78d3b0..10e61a9c74aca3448266f66147ef9fe2c50ef057 100644 --- a/tox.ini +++ b/tox.ini @@ -14,6 +14,9 @@ setenv = deps = -r{toxinidir}/requirements.txt -r{toxinidir}/tests/requirements.txt +commands_pre = + {envpython} --version + pip install --no-cache pytango commands = {envpython} --version {envpython} -m pytest @@ -34,9 +37,9 @@ commands = black: {envpython} -m black --version black: {envpython} -m black --check --diff . pylint: {envpython} -m pylint --version - pylint: {envpython} -m pylint my_awesome_app tests - format: {envpython} -m autopep8 -v -aa --in-place --recursive my_awesome_app - format: {envpython} -m autopep8 -v -aa --in-place --recursive tests + pylint: {envpython} -m pylint AttributeWrapper tests + format: {envpython} -m autopep8 -v -aa --in-place --recursive Attribute_wrapper/ + format: {envpython} -m autopep8 -v -aa --in-place --recursive tests/ format: {envpython} -m black -v .