add Datatype.to_string as counterpart of .from_string
This allows simple UIs using stringified versions in text input. - add frappy.client.SecopClient.setParameterFromString - fix datatype tests + add updateItem to CallbackObject (for doc) Change-Id: Ic7792bdc51ba0884637b2d4acc0e9433c669314d Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/34736 Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de> Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
parent
f9f713811e
commit
f8d8cbd76d
@ -22,6 +22,8 @@
|
|||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
"""general SECoP client"""
|
"""general SECoP client"""
|
||||||
|
|
||||||
|
# pylint: disable=too-many-positional-arguments
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import queue
|
import queue
|
||||||
import re
|
import re
|
||||||
@ -79,10 +81,12 @@ class CallbackObject:
|
|||||||
this is mainly for documentation, but it might be extended
|
this is mainly for documentation, but it might be extended
|
||||||
and used as a mixin for objects registered as a callback
|
and used as a mixin for objects registered as a callback
|
||||||
"""
|
"""
|
||||||
def updateEvent(self, module, parameter, value, timestamp, readerror):
|
def updateItem(self, module, parameter, item):
|
||||||
"""called whenever a value is changed
|
"""called whenever a value is changed
|
||||||
|
|
||||||
or when new callbacks are registered
|
:param module: the module name
|
||||||
|
:param parameter: the parameter name
|
||||||
|
:param item: a CacheItem object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def unhandledMessage(self, action, ident, data):
|
def unhandledMessage(self, action, ident, data):
|
||||||
@ -108,6 +112,12 @@ class CallbackObject:
|
|||||||
and on every changed module with module==<module name>
|
and on every changed module with module==<module name>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def updateEvent(self, module, parameter, value, timestamp, readerror):
|
||||||
|
"""legacy method: called whenever a value is changed
|
||||||
|
|
||||||
|
or when new callbacks are registered
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class CacheItem(tuple):
|
class CacheItem(tuple):
|
||||||
"""cache entry
|
"""cache entry
|
||||||
@ -115,12 +125,14 @@ class CacheItem(tuple):
|
|||||||
includes formatting information
|
includes formatting information
|
||||||
inheriting from tuple: compatible with old previous version of cache
|
inheriting from tuple: compatible with old previous version of cache
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __new__(cls, value, timestamp=None, readerror=None, datatype=None):
|
def __new__(cls, value, timestamp=None, readerror=None, datatype=None):
|
||||||
obj = tuple.__new__(cls, (value, timestamp, readerror))
|
obj = tuple.__new__(cls, (value, timestamp, readerror))
|
||||||
if datatype:
|
if datatype:
|
||||||
try:
|
try:
|
||||||
# override default method
|
# override default methods
|
||||||
obj.format_value = datatype.format_value
|
obj.format_value = datatype.format_value
|
||||||
|
obj.to_string = datatype.to_string
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
return obj
|
return obj
|
||||||
@ -138,29 +150,40 @@ class CacheItem(tuple):
|
|||||||
return self[2]
|
return self[2]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""format value without unit"""
|
"""format value without unit
|
||||||
|
|
||||||
|
may be used in this form for SecopClient.setParameterFromString
|
||||||
|
"""
|
||||||
if self[2]: # readerror
|
if self[2]: # readerror
|
||||||
return repr(self[2])
|
return repr(self[2])
|
||||||
return self.format_value(self[0], unit='') # skip unit
|
return self.to_string(self[0])
|
||||||
|
|
||||||
def formatted(self):
|
def formatted(self):
|
||||||
"""format value with using unit"""
|
"""format value with using unit
|
||||||
|
|
||||||
|
nicer format for humans, hard to parse
|
||||||
|
"""
|
||||||
if self[2]: # readerror
|
if self[2]: # readerror
|
||||||
return repr(self[2])
|
return repr(self[2])
|
||||||
return self.format_value(self[0])
|
return self.format_value(self[0])
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def format_value(value, unit=None):
|
def format_value(value):
|
||||||
"""typically overridden with datatype.format_value"""
|
"""typically overridden with datatype.format_value"""
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_string(value):
|
||||||
|
"""typically overridden with datatype.to_string"""
|
||||||
|
return str(value)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
args = (self.value,)
|
args = (self.value,)
|
||||||
if self.timestamp:
|
if self.timestamp:
|
||||||
args += (self.timestamp,)
|
args += (self.timestamp,)
|
||||||
if self.readerror:
|
if self.readerror:
|
||||||
args += (self.readerror,)
|
args += (self.readerror,)
|
||||||
return f'CacheItem{repr(args)}'
|
return f'CacheItem{args!r}'
|
||||||
|
|
||||||
|
|
||||||
class Cache(dict):
|
class Cache(dict):
|
||||||
@ -703,6 +726,17 @@ class SecopClient(ProxyClient):
|
|||||||
self.request(WRITEREQUEST, self.identifier[module, parameter], value)
|
self.request(WRITEREQUEST, self.identifier[module, parameter], value)
|
||||||
return self.cache[module, parameter]
|
return self.cache[module, parameter]
|
||||||
|
|
||||||
|
def setParameterFromString(self, module, parameter, formatted):
|
||||||
|
"""set parameter from string
|
||||||
|
|
||||||
|
formatted is a string in the form obtained by str(<cache item>)
|
||||||
|
"""
|
||||||
|
self.connect() # make sure we are connected
|
||||||
|
datatype = self.modules[module]['parameters'][parameter]['datatype']
|
||||||
|
value = datatype.from_string(formatted)
|
||||||
|
self.request(WRITEREQUEST, self.identifier[module, parameter], value)
|
||||||
|
return self.cache[module, parameter]
|
||||||
|
|
||||||
def execCommand(self, module, command, argument=None):
|
def execCommand(self, module, command, argument=None):
|
||||||
self.connect() # make sure we are connected
|
self.connect() # make sure we are connected
|
||||||
datatype = self.modules[module]['commands'][command]['datatype'].argument
|
datatype = self.modules[module]['commands'][command]['datatype'].argument
|
||||||
|
@ -25,13 +25,13 @@
|
|||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import ast
|
||||||
from base64 import b64decode, b64encode
|
from base64 import b64decode, b64encode
|
||||||
|
|
||||||
from frappy.errors import ConfigError, ProgrammingError, \
|
from frappy.errors import ConfigError, ProgrammingError, \
|
||||||
ProtocolError, RangeError, WrongTypeError
|
RangeError, WrongTypeError
|
||||||
from frappy.lib import clamp, generalConfig
|
from frappy.lib import clamp, generalConfig
|
||||||
from frappy.lib.enum import Enum
|
from frappy.lib.enum import Enum
|
||||||
from frappy.parse import Parser
|
|
||||||
from frappy.properties import HasProperties, Property
|
from frappy.properties import HasProperties, Property
|
||||||
|
|
||||||
generalConfig.set_default('lazy_number_validation', False)
|
generalConfig.set_default('lazy_number_validation', False)
|
||||||
@ -41,8 +41,6 @@ DEFAULT_MIN_INT = -16777216
|
|||||||
DEFAULT_MAX_INT = 16777216
|
DEFAULT_MAX_INT = 16777216
|
||||||
UNLIMITED = 1 << 64 # internal limit for integers, is probably high enough for any datatype size
|
UNLIMITED = 1 << 64 # internal limit for integers, is probably high enough for any datatype size
|
||||||
|
|
||||||
Parser = Parser()
|
|
||||||
|
|
||||||
|
|
||||||
def shortrepr(value):
|
def shortrepr(value):
|
||||||
"""shortened repr for error messages
|
"""shortened repr for error messages
|
||||||
@ -83,9 +81,29 @@ class DataType(HasProperties):
|
|||||||
return self(value)
|
return self(value)
|
||||||
|
|
||||||
def from_string(self, text):
|
def from_string(self, text):
|
||||||
"""interprets a given string and returns a validated (internal) value"""
|
"""interprets a given string and returns a validated (internal) value
|
||||||
# to evaluate values from configfiles, ui, etc...
|
|
||||||
raise NotImplementedError
|
intended to be given e.g. from a GUI text input
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
value = ast.literal_eval(text)
|
||||||
|
except Exception:
|
||||||
|
raise WrongTypeError(f'{shortrepr(text)} is no valid value') from None
|
||||||
|
return self(value)
|
||||||
|
|
||||||
|
def to_string(self, value):
|
||||||
|
"""convert a value of this type into a string
|
||||||
|
|
||||||
|
This is intended for a GUI text input and is the opposite of
|
||||||
|
:meth:`from_string`
|
||||||
|
- no units are shown
|
||||||
|
- value is not checked before formatting
|
||||||
|
|
||||||
|
typically the output is a stringified value in python syntax except for
|
||||||
|
- StringType: the bare string is returned
|
||||||
|
- EnumType: the name of the enum is returned
|
||||||
|
"""
|
||||||
|
return self.format_value(value, False)
|
||||||
|
|
||||||
def export_datatype(self):
|
def export_datatype(self):
|
||||||
"""return a python object which after jsonifying identifies this datatype"""
|
"""return a python object which after jsonifying identifies this datatype"""
|
||||||
@ -94,7 +112,6 @@ class DataType(HasProperties):
|
|||||||
f"It is intended for internal use only."
|
f"It is intended for internal use only."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def export_value(self, value):
|
def export_value(self, value):
|
||||||
"""if needed, reformat value for transport"""
|
"""if needed, reformat value for transport"""
|
||||||
return value
|
return value
|
||||||
@ -107,12 +124,17 @@ class DataType(HasProperties):
|
|||||||
"""
|
"""
|
||||||
return self(value)
|
return self(value)
|
||||||
|
|
||||||
def format_value(self, value, unit=None):
|
def format_value(self, value, unit=True):
|
||||||
"""format a value of this type into a str string
|
"""format a value of this type into a string
|
||||||
|
|
||||||
This is intended for 'nice' formatting for humans and is NOT
|
This is intended for 'nice' formatting for humans and is NOT
|
||||||
the opposite of :meth:`from_string`
|
the opposite of :meth:`from_string`
|
||||||
if unit is given, use it, else use the unit of the datatype (if any)"""
|
|
||||||
|
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
|
raise NotImplementedError
|
||||||
|
|
||||||
def set_properties(self, **kwds):
|
def set_properties(self, **kwds):
|
||||||
@ -259,15 +281,11 @@ class FloatRange(HasUnit, DataType):
|
|||||||
"""returns a python object fit for serialisation"""
|
"""returns a python object fit for serialisation"""
|
||||||
return float(value)
|
return float(value)
|
||||||
|
|
||||||
def from_string(self, text):
|
def format_value(self, value, unit=True):
|
||||||
value = float(text)
|
if unit is True:
|
||||||
return self(value)
|
|
||||||
|
|
||||||
def format_value(self, value, unit=None):
|
|
||||||
if unit is None:
|
|
||||||
unit = self.unit
|
unit = self.unit
|
||||||
if unit:
|
if unit:
|
||||||
return ' '.join([self.fmtstr % value, unit])
|
return f'{self.fmtstr % value} {unit}'
|
||||||
return self.fmtstr % value
|
return self.fmtstr % value
|
||||||
|
|
||||||
def compatible(self, other):
|
def compatible(self, other):
|
||||||
@ -337,11 +355,7 @@ class IntRange(DataType):
|
|||||||
"""returns a python object fit for serialisation"""
|
"""returns a python object fit for serialisation"""
|
||||||
return int(value)
|
return int(value)
|
||||||
|
|
||||||
def from_string(self, text):
|
def format_value(self, value, unit=True):
|
||||||
value = int(text)
|
|
||||||
return self(value)
|
|
||||||
|
|
||||||
def format_value(self, value, unit=None):
|
|
||||||
return f'{value}'
|
return f'{value}'
|
||||||
|
|
||||||
def compatible(self, other):
|
def compatible(self, other):
|
||||||
@ -458,15 +472,11 @@ class ScaledInteger(HasUnit, DataType):
|
|||||||
except Exception:
|
except Exception:
|
||||||
raise WrongTypeError(f'can not import {shortrepr(value)} to scaled') from None
|
raise WrongTypeError(f'can not import {shortrepr(value)} to scaled') from None
|
||||||
|
|
||||||
def from_string(self, text):
|
def format_value(self, value, unit=True):
|
||||||
value = float(text)
|
if unit is True:
|
||||||
return self(value)
|
|
||||||
|
|
||||||
def format_value(self, value, unit=None):
|
|
||||||
if unit is None:
|
|
||||||
unit = self.unit
|
unit = self.unit
|
||||||
if unit:
|
if unit:
|
||||||
return ' '.join([self.fmtstr % value, unit])
|
return f'{self.fmtstr % value} {unit}'
|
||||||
return self.fmtstr % value
|
return self.fmtstr % value
|
||||||
|
|
||||||
def compatible(self, other):
|
def compatible(self, other):
|
||||||
@ -483,6 +493,7 @@ class EnumType(DataType):
|
|||||||
:param members: members dict or None when using kwds only
|
:param members: members dict or None when using kwds only
|
||||||
:param kwds: (additional) members
|
:param kwds: (additional) members
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, enum_or_name='', members=None, **kwds):
|
def __init__(self, enum_or_name='', members=None, **kwds):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
if members is not None:
|
if members is not None:
|
||||||
@ -518,10 +529,19 @@ class EnumType(DataType):
|
|||||||
raise WrongTypeError(f'{shortrepr(value)} must be either int or str for an enum value') from None
|
raise WrongTypeError(f'{shortrepr(value)} must be either int or str for an enum value') from None
|
||||||
|
|
||||||
def from_string(self, text):
|
def from_string(self, text):
|
||||||
return self(text)
|
try:
|
||||||
|
return self._enum(text.strip())
|
||||||
|
except KeyError:
|
||||||
|
return super().from_string(text)
|
||||||
|
|
||||||
def format_value(self, value, unit=None):
|
def format_value(self, value, unit=True):
|
||||||
return f'{self._enum[value].name}<{self._enum[value].value}>'
|
if unit is False:
|
||||||
|
return repr(value.name)
|
||||||
|
# for humans: contains more information (name + code)
|
||||||
|
return f'{value.name}<{value.value}>'
|
||||||
|
|
||||||
|
def to_string(self, value):
|
||||||
|
return value.name
|
||||||
|
|
||||||
def set_name(self, name):
|
def set_name(self, name):
|
||||||
self._enum.name = name
|
self._enum.name = name
|
||||||
@ -584,12 +604,7 @@ class BLOBType(DataType):
|
|||||||
except Exception:
|
except Exception:
|
||||||
raise WrongTypeError(f'can not b64decode {shortrepr(value)}') from None
|
raise WrongTypeError(f'can not b64decode {shortrepr(value)}') from None
|
||||||
|
|
||||||
def from_string(self, text):
|
def format_value(self, value, unit=True):
|
||||||
value = text
|
|
||||||
# XXX: what should we do here?
|
|
||||||
return self(value)
|
|
||||||
|
|
||||||
def format_value(self, value, unit=None):
|
|
||||||
return repr(value)
|
return repr(value)
|
||||||
|
|
||||||
def compatible(self, other):
|
def compatible(self, other):
|
||||||
@ -653,13 +668,15 @@ class StringType(DataType):
|
|||||||
"""returns a python object fit for serialisation"""
|
"""returns a python object fit for serialisation"""
|
||||||
return f'{value}'
|
return f'{value}'
|
||||||
|
|
||||||
def from_string(self, text):
|
def format_value(self, value, unit=True):
|
||||||
value = str(text)
|
|
||||||
return self(value)
|
|
||||||
|
|
||||||
def format_value(self, value, unit=None):
|
|
||||||
return repr(value)
|
return repr(value)
|
||||||
|
|
||||||
|
def to_string(self, value):
|
||||||
|
return value
|
||||||
|
|
||||||
|
def from_string(self, text):
|
||||||
|
return self(text)
|
||||||
|
|
||||||
def compatible(self, other):
|
def compatible(self, other):
|
||||||
try:
|
try:
|
||||||
if self.minchars < other.minchars or self.maxchars > other.maxchars or \
|
if self.minchars < other.minchars or self.maxchars > other.maxchars or \
|
||||||
@ -700,25 +717,26 @@ class BoolType(DataType):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'BoolType()'
|
return 'BoolType()'
|
||||||
|
|
||||||
def __call__(self, value):
|
def from_string(self, text):
|
||||||
"""accepts 0, False, 1, True"""
|
"""accepts 0, False, 1, True"""
|
||||||
# TODO: probably remove conversion from string (not needed anymore with python cfg)
|
value = text.strip()
|
||||||
if value in [0, '0', 'False', 'false', 'no', 'off', False]:
|
if value in ['0', 'False', 'false', 'no', 'off']:
|
||||||
return False
|
return False
|
||||||
if value in [1, '1', 'True', 'true', 'yes', 'on', True]:
|
if value in ['1', 'True', 'true', 'yes', 'on']:
|
||||||
return True
|
return True
|
||||||
raise WrongTypeError(f'{shortrepr(value)} is not a boolean value!')
|
raise WrongTypeError(f'{shortrepr(value)} is not a boolean value!')
|
||||||
|
|
||||||
|
def __call__(self, value):
|
||||||
|
if value in (0, 1):
|
||||||
|
return bool(value)
|
||||||
|
raise WrongTypeError(f'{shortrepr(value)} is not a boolean value!')
|
||||||
|
|
||||||
def export_value(self, value):
|
def export_value(self, value):
|
||||||
"""returns a python object fit for serialisation"""
|
"""returns a python object fit for serialisation"""
|
||||||
return self(value)
|
return self(value)
|
||||||
|
|
||||||
def from_string(self, text):
|
def format_value(self, value, unit=True):
|
||||||
value = text
|
return repr(value)
|
||||||
return self(value)
|
|
||||||
|
|
||||||
def format_value(self, value, unit=None):
|
|
||||||
return repr(bool(value))
|
|
||||||
|
|
||||||
def compatible(self, other):
|
def compatible(self, other):
|
||||||
other(False)
|
other(False)
|
||||||
@ -754,6 +772,10 @@ class ArrayOf(DataType):
|
|||||||
self.members = members
|
self.members = members
|
||||||
self.set_properties(minlen=minlen, maxlen=maxlen)
|
self.set_properties(minlen=minlen, maxlen=maxlen)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit(self):
|
||||||
|
return self.members.unit
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
"""DataType.copy does not work when members are enums"""
|
"""DataType.copy does not work when members are enums"""
|
||||||
return ArrayOf(self.members.copy(), self.minlen, self.maxlen)
|
return ArrayOf(self.members.copy(), self.minlen, self.maxlen)
|
||||||
@ -823,25 +845,20 @@ class ArrayOf(DataType):
|
|||||||
"""returns a python object from serialisation"""
|
"""returns a python object from serialisation"""
|
||||||
return tuple(self.members.import_value(elem) for elem in value)
|
return tuple(self.members.import_value(elem) for elem in value)
|
||||||
|
|
||||||
def from_string(self, text):
|
def format_value(self, value, unit=True):
|
||||||
value, rem = Parser.parse(text)
|
innerunit = False
|
||||||
if rem:
|
if unit is True:
|
||||||
raise ProtocolError(f'trailing garbage: {rem!r}')
|
|
||||||
return self(value)
|
|
||||||
|
|
||||||
def format_value(self, value, unit=None):
|
|
||||||
innerunit = ''
|
|
||||||
if unit is None:
|
|
||||||
members = self.members
|
members = self.members
|
||||||
while isinstance(members, ArrayOf):
|
while isinstance(members, ArrayOf):
|
||||||
members = members.members
|
members = members.members
|
||||||
if members.unit:
|
if members.unit:
|
||||||
unit = members.unit
|
unit = members.unit
|
||||||
else:
|
else:
|
||||||
innerunit = None
|
unit = ''
|
||||||
|
innerunit = True
|
||||||
res = f"[{', '.join([self.members.format_value(elem, innerunit) for elem in value])}]"
|
res = f"[{', '.join([self.members.format_value(elem, innerunit) for elem in value])}]"
|
||||||
if unit:
|
if unit:
|
||||||
return ' '.join([res, unit])
|
return f'{res} {unit}'
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def compatible(self, other):
|
def compatible(self, other):
|
||||||
@ -918,14 +935,8 @@ class TupleOf(DataType):
|
|||||||
"""returns a python object from serialisation"""
|
"""returns a python object from serialisation"""
|
||||||
return tuple(sub.import_value(elem) for sub, elem in zip(self.members, value))
|
return tuple(sub.import_value(elem) for sub, elem in zip(self.members, value))
|
||||||
|
|
||||||
def from_string(self, text):
|
def format_value(self, value, unit=True):
|
||||||
value, rem = Parser.parse(text)
|
return f"({', '.join([sub.format_value(elem, unit) for sub, elem in zip(self.members, value)])})"
|
||||||
if rem:
|
|
||||||
raise ProtocolError(f'trailing garbage: {rem!r}')
|
|
||||||
return self(value)
|
|
||||||
|
|
||||||
def format_value(self, value, unit=None):
|
|
||||||
return f"({', '.join([sub.format_value(elem) for sub, elem in zip(self.members, value)])})"
|
|
||||||
|
|
||||||
def compatible(self, other):
|
def compatible(self, other):
|
||||||
if not isinstance(other, TupleOf):
|
if not isinstance(other, TupleOf):
|
||||||
@ -1036,14 +1047,13 @@ class StructOf(DataType):
|
|||||||
return {str(k): self.members[k].import_value(v)
|
return {str(k): self.members[k].import_value(v)
|
||||||
for k, v in value.items()}
|
for k, v in value.items()}
|
||||||
|
|
||||||
def from_string(self, text):
|
def format_value(self, value, unit=True):
|
||||||
value, rem = Parser.parse(text)
|
if unit is False:
|
||||||
if rem:
|
return '{%s}' % (', '.join(['%r: %s' % (k, self.members[k].format_value(v, False))
|
||||||
raise ProtocolError(f'trailing garbage: {rem!r}')
|
for k, v in value.items()]))
|
||||||
return self(dict(value))
|
# more human readable format (no quotes on keys)
|
||||||
|
return '{%s}' % (', '.join(['%s=%s' % (k, self.members[k].format_value(v, True))
|
||||||
def format_value(self, value, unit=None):
|
for k, v in value.items()]))
|
||||||
return '{%s}' % (', '.join(['%s=%s' % (k, self.members[k].format_value(v)) for k, v in value.items()]))
|
|
||||||
|
|
||||||
def compatible(self, other):
|
def compatible(self, other):
|
||||||
try:
|
try:
|
||||||
@ -1103,14 +1113,11 @@ class CommandType(DataType):
|
|||||||
raise ProgrammingError('values of type command can not be transported!')
|
raise ProgrammingError('values of type command can not be transported!')
|
||||||
|
|
||||||
def from_string(self, text):
|
def from_string(self, text):
|
||||||
value, rem = Parser.parse(text)
|
raise ProgrammingError('a string can not be converted to a command')
|
||||||
if rem:
|
|
||||||
raise ProtocolError(f'trailing garbage: {rem!r}')
|
|
||||||
return self(value)
|
|
||||||
|
|
||||||
def format_value(self, value, unit=None):
|
def format_value(self, value, unit=True):
|
||||||
# actually I have no idea what to do here!
|
# actually I have no idea what to do here!
|
||||||
raise NotImplementedError
|
raise ProgrammingError('commands can not be converted to a string')
|
||||||
|
|
||||||
def compatible(self, other):
|
def compatible(self, other):
|
||||||
try:
|
try:
|
||||||
@ -1221,7 +1228,6 @@ class OrType(DataType):
|
|||||||
self.types = types
|
self.types = types
|
||||||
self.default = self.types[0].default
|
self.default = self.types[0].default
|
||||||
|
|
||||||
|
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
"""accepts any of the given types, takes the first valid"""
|
"""accepts any of the given types, takes the first valid"""
|
||||||
for t in self.types:
|
for t in self.types:
|
||||||
|
@ -264,7 +264,7 @@ def test_EnumType():
|
|||||||
with pytest.raises(RangeError):
|
with pytest.raises(RangeError):
|
||||||
dt.import_value('A')
|
dt.import_value('A')
|
||||||
|
|
||||||
assert dt.format_value(3) == 'a<3>'
|
assert dt.format_value(dt(3)) == 'a<3>'
|
||||||
|
|
||||||
|
|
||||||
def test_BLOBType():
|
def test_BLOBType():
|
||||||
@ -378,20 +378,20 @@ def test_BoolType():
|
|||||||
with pytest.raises(WrongTypeError):
|
with pytest.raises(WrongTypeError):
|
||||||
dt('av')
|
dt('av')
|
||||||
|
|
||||||
assert dt('true') is True
|
assert dt.from_string('true') is True
|
||||||
assert dt('off') is False
|
assert dt.from_string('off') is False
|
||||||
assert dt(1) is True
|
assert dt(1) is True
|
||||||
|
|
||||||
assert dt.export_value('false') is False
|
assert dt.export_value(False) is False
|
||||||
assert dt.export_value(0) is False
|
assert dt.export_value(0) is False
|
||||||
assert dt.export_value('on') is True
|
assert dt.export_value(1) is True
|
||||||
|
|
||||||
assert dt.import_value(False) is False
|
assert dt.import_value(False) is False
|
||||||
assert dt.import_value(True) is True
|
assert dt.import_value(True) is True
|
||||||
with pytest.raises(WrongTypeError):
|
with pytest.raises(WrongTypeError):
|
||||||
dt.import_value('av')
|
dt.import_value('av')
|
||||||
|
|
||||||
assert dt.format_value(0) == "False"
|
assert dt.format_value(dt(0)) == "False"
|
||||||
assert dt.format_value(True) == "True"
|
assert dt.format_value(True) == "True"
|
||||||
|
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
@ -468,7 +468,7 @@ def test_TupleOf():
|
|||||||
assert dt.export_value([1, True]) == [1, True]
|
assert dt.export_value([1, True]) == [1, True]
|
||||||
assert dt.import_value([1, True]) == (1, True)
|
assert dt.import_value([1, True]) == (1, True)
|
||||||
|
|
||||||
assert dt.format_value([3,0]) == "(3, False)"
|
assert dt.format_value(dt([3,0])) == "(3, False)"
|
||||||
|
|
||||||
dt = TupleOf(EnumType('myenum', single=0))
|
dt = TupleOf(EnumType('myenum', single=0))
|
||||||
copytest(dt)
|
copytest(dt)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user