From 24153d25842d46e663f32a5928e20903e128cdb8 Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Tue, 28 Oct 2025 09:50:43 +0100 Subject: [PATCH] datatypes: split base classes for internal and SECoP datatypes Simple datatypes used only in properties like ValueType of NoneOr do not need a couple of methods. Splitting the base class avoids warnings about unimlemented abstract methods. Change-Id: Ie7d5754c44a5fb5c3ed8569df544495450347082 --- frappy/datatypes.py | 106 ++++++++++++++++++++--------------------- frappy/params.py | 6 +-- test/test_datatypes.py | 2 +- 3 files changed, 56 insertions(+), 58 deletions(-) diff --git a/frappy/datatypes.py b/frappy/datatypes.py index 1bac6ee8..ebbefb85 100644 --- a/frappy/datatypes.py +++ b/frappy/datatypes.py @@ -53,13 +53,9 @@ def shortrepr(value): return r -# base class for all DataTypes -class DataType(HasProperties): - """base class for all data types""" - IS_COMMAND = False - unit = '' +class SimpleDataType(HasProperties): + """base class for simple datatypes, used in properties only""" default = None - client = False # used on the client side def __call__(self, value): """convert given value to our datatype and validate @@ -105,38 +101,10 @@ class DataType(HasProperties): """ return self.format_value(value, False) - def export_datatype(self): - """return a python object which after jsonifying identifies this datatype""" - raise ProgrammingError( - f"{type(self).__name__} is not able to be exported to SECoP. " - f"It is intended for internal use only." - ) - def export_value(self, value): """if needed, reformat value for transport""" return value - def import_value(self, value): - """opposite of export_value, reformat from transport to internal repr - - note: for importing from gui/configfile/commandline use :meth:`from_string` - instead. - """ - return self(value) - - def format_value(self, value, unit=True): - """format a value of this type into a string - - This is intended for 'nice' formatting for humans and is NOT - the opposite of :meth:`from_string` - - possible values of unit: - - True: use the string of the datatype - - False: return a value interpretable by ast.literal_eval (internal use only) - - any other string: use as unit (internal use only) - """ - raise NotImplementedError - def set_properties(self, **kwds): """init datatype properties""" try: @@ -161,6 +129,34 @@ class DataType(HasProperties): # looks like the simplest way to make a deep copy return get_datatype(self.export_datatype()) + +class DataType(SimpleDataType): + """base class for data types used in parameters and commands""" + IS_COMMAND = False + unit = '' + client = False # used on the client side + + def import_value(self, value): + """opposite of export_value, reformat from transport to internal repr + + note: for importing from gui/configfile/commandline use :meth:`from_string` + instead. + """ + return self(value) + + def format_value(self, value, unit=True): + """format a value of this type into a string + + This is intended for 'nice' formatting for humans and is NOT + the opposite of :meth:`from_string` + + possible values of unit: + - True: use the string of the datatype + - False: return a value interpretable by ast.literal_eval (internal use only) + - any other string: use as unit (internal use only) + """ + raise NotImplementedError + def compatible(self, other): """check other for compatibility @@ -169,6 +165,10 @@ class DataType(HasProperties): """ raise NotImplementedError + def export_datatype(self): + """return a python object which after jsonifying identifies this datatype""" + raise NotImplementedError + def set_main_unit(self, unit): """replace $ in unit by argument""" @@ -1131,10 +1131,23 @@ class CommandType(DataType): # internally used datatypes (i.e. only for programming the SEC-node) -class DataTypeType(DataType): +class DefaultType(DataType): + """datatype used as default for parameters + + needs some minimal interface to avoid errors when + the datatype of a parameter is not yet defined + """ + def __call__(self, value): + return value + + def setProperty(self, key, value): + """silently ignored""" + + +class DataTypeType(SimpleDataType): def __call__(self, value): """accepts a datatype""" - if isinstance(value, DataType): + if isinstance(value, SimpleDataType): return value #TODO: not needed anymore? try: @@ -1155,7 +1168,7 @@ class DataTypeType(DataType): raise NotImplementedError -class ValueType(DataType): +class ValueType(SimpleDataType): """Can take any python value. The optional (callable) validator can be used to restrict values to a @@ -1188,23 +1201,8 @@ class ValueType(DataType): """if needed, reformat value for transport""" return value - def import_value(self, value): - """opposite of export_value, reformat from transport to internal repr - note: for importing from gui/configfile/commandline use :meth:`from_string` - instead. - """ - raise NotImplementedError - - def setProperty(self, key, value): - """silently ignored - - as ValueType is used for the datatype default, this makes code - shorter for cases, where the datatype may not yet be defined - """ - - -class NoneOr(DataType): +class NoneOr(SimpleDataType): """validates a None or smth. else""" default = None @@ -1222,7 +1220,7 @@ class NoneOr(DataType): return self.other.export_value(value) -class OrType(DataType): +class OrType(SimpleDataType): def __init__(self, *types): super().__init__() self.types = types diff --git a/frappy/params.py b/frappy/params.py index c1f7fc03..5c3a8e56 100644 --- a/frappy/params.py +++ b/frappy/params.py @@ -25,8 +25,8 @@ import inspect from frappy.datatypes import ArrayOf, BoolType, CommandType, DataType, \ - DataTypeType, EnumType, FloatRange, NoneOr, OrType, StringType, StructOf, \ - TextType, TupleOf, ValueType + DataTypeType, DefaultType, EnumType, FloatRange, NoneOr, OrType, StringType, \ + StructOf, TextType, TupleOf, ValueType from frappy.errors import BadValueError, ProgrammingError, WrongTypeError from frappy.lib import generalConfig from frappy.properties import HasProperties, Property @@ -144,7 +144,7 @@ class Parameter(Accessible): extname='description', mandatory=True, export='always') datatype = Property( 'datatype of the Parameter (SECoP datainfo)', DataTypeType(), - extname='datainfo', mandatory=True, export='always', default=ValueType()) + extname='datainfo', mandatory=True, export='always', default=DefaultType()) readonly = Property( 'not changeable via SECoP (default True)', BoolType(), extname='readonly', default=True, export='always') diff --git a/test/test_datatypes.py b/test/test_datatypes.py index f585dbb4..11d6192d 100644 --- a/test/test_datatypes.py +++ b/test/test_datatypes.py @@ -75,7 +75,7 @@ def out_of_range(dt, *args): def test_DataType(): dt = DataType() - with pytest.raises(ProgrammingError): + with pytest.raises(NotImplementedError): dt.export_datatype() with pytest.raises(NotImplementedError): dt('')