Convert formatting automatically to f-strings

Automatically convert formatting with the following call:
flynt -ll 2000 -v frappy*
Result: 303/381 auto-converted.
Failing conversions will be looked at manually in a follow-up commit.

Change-Id: Icd996b27221202faccc15af78e0380cf52ee37f2
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/30900
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Georg Brandl <g.brandl@fz-juelich.de>
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
This commit is contained in:
Alexander Zaft 2023-04-14 07:12:03 +02:00 committed by Markus Zolliker
parent a9d479ba0a
commit 6f95c0d825
56 changed files with 327 additions and 380 deletions

View File

@ -112,7 +112,7 @@ class CacheItem(tuple):
try:
value = datatype.import_value(value)
except (KeyError, ValueError, AttributeError):
readerror = ValueError('can not import %r as %r' % (value, datatype))
readerror = ValueError(f'can not import {value!r} as {datatype!r}')
value = None
obj = tuple.__new__(cls, (value, timestamp, readerror))
try:
@ -151,7 +151,7 @@ class CacheItem(tuple):
args += (self.timestamp,)
if self.readerror:
args += (self.readerror,)
return 'CacheItem%s' % repr(args)
return f'CacheItem{repr(args)}'
class ProxyClient:
@ -216,7 +216,7 @@ class ProxyClient:
elif cbname == 'nodeStateChange':
cbfunc(self.online, self.state)
if kwds:
raise TypeError('unknown callback: %s' % (', '.join(kwds)))
raise TypeError(f"unknown callback: {', '.join(kwds)}")
def unregister_callback(self, key, *args, **kwds):
"""unregister a callback
@ -310,11 +310,10 @@ class SecopClient(ProxyClient):
if reply:
self.secop_version = reply.decode('utf-8')
else:
raise self.error_map('HardwareError')('no answer to %s' % IDENTREQUEST)
raise self.error_map('HardwareError')(f'no answer to {IDENTREQUEST}')
if not VERSIONFMT.match(self.secop_version):
raise self.error_map('HardwareError')('bad answer to %s: %r' %
(IDENTREQUEST, self.secop_version))
raise self.error_map('HardwareError')(f'bad answer to {IDENTREQUEST}: {self.secop_version!r}')
# inform that the other party still uses a legacy identifier
# see e.g. Frappy Bug #4659 (https://forge.frm2.tum.de/redmine/issues/4659)
if not self.secop_version.startswith(IDENTPREFIX):
@ -387,9 +386,9 @@ class SecopClient(ProxyClient):
if module_param is None and ':' not in (ident or ''):
# allow missing ':value'/':target'
if action == WRITEREPLY:
module_param = self.internal.get('%s:target' % ident, None)
module_param = self.internal.get(f'{ident}:target', None)
else:
module_param = self.internal.get('%s:value' % ident, None)
module_param = self.internal.get(f'{ident}:value', None)
if module_param is not None:
if action.startswith(ERRORPREFIX):
timestamp = data[2].get('t', None)
@ -546,7 +545,7 @@ class SecopClient(ProxyClient):
iname = self.internalize_name(aname)
datatype = get_datatype(aentry['datainfo'], iname)
aentry = dict(aentry, datatype=datatype)
ident = '%s:%s' % (modname, aname)
ident = f'{modname}:{aname}'
self.identifier[modname, iname] = ident
self.internal[ident] = modname, iname
if datatype.IS_COMMAND:

View File

@ -80,7 +80,7 @@ class Logger:
if tm.tm_min != self._minute:
self._minute = tm.tm_min
print(CLR + time.strftime('--- %H:%M:%S ---', tm))
sec = ('%6.3f' % (now % 60.0)).replace(' ', '0')
sec = f'{now % 60.0:6.3f}'.replace(' ', '0')
print(CLR + sec, str(fmt) % args)
else:
print(CLR + (str(fmt) % args))
@ -101,7 +101,7 @@ class PrettyFloat(float):
- always display a decimal point
"""
def __repr__(self):
result = '%.12g' % self
result = f'{self:.12g}'
if '.' in result or 'e' in result:
return result
return result + '.'
@ -124,7 +124,7 @@ class Module:
self._running = None
self._status = None
props = secnode.modules[name]['properties']
self._title = '# %s (%s)' % (props.get('implementation', ''), props.get('interface_classes', [''])[0])
self._title = f"# {props.get('implementation', '')} ({props.get('interface_classes', [''])[0]})"
def _one_line(self, pname, minwid=0):
"""return <module>.<param> = <value> truncated to one line"""
@ -134,7 +134,7 @@ class Module:
vallen = 113 - len(self._name) - len(pname)
if len(result) > vallen:
result = result[:vallen - 4] + ' ...'
return '%s.%s = %s' % (self._name, pname, result)
return f'{self._name}.{pname} = {result}'
def _isBusy(self):
return self.status[0] // 100 == StatusType.BUSY // 100
@ -188,7 +188,7 @@ class Module:
else:
self._secnode.log.error('can not set %r on module %s', item, self._name)
self._watched_params = params
print('--- %s:\nlog: %s, watch: %s' % (self._name, self._log_level, ' '.join(self._watched_params)))
print(f"--- {self._name}:\nlog: {self._log_level}, watch: {' '.join(self._watched_params)}")
def _start_watching(self):
for pname in self._watched_params:
@ -318,11 +318,11 @@ def watch(*args, **kwds):
modules.append(mobj)
mobj._set_watching()
else:
print('do not know %r' % mobj)
print(f'do not know {mobj!r}')
for key, arg in kwds.items():
mobj = getattr(main, key, None)
if mobj is None:
print('do not know %r' % key)
print(f'do not know {key!r}')
else:
modules.append(mobj)
mobj._set_watching(arg)
@ -379,7 +379,7 @@ class Client(SecopClient):
attrs[pname] = Param(pname, pinfo['datainfo'])
for cname in moddesc['commands']:
attrs[cname] = Command(cname, modname, self)
mobj = type('M_%s' % modname, (Module,), attrs)(modname, self)
mobj = type(f'M_{modname}', (Module,), attrs)(modname, self)
if 'status' in mobj._parameters:
self.register_callback((modname, 'status'), updateEvent=mobj._status_value_update)
self.register_callback((modname, 'value'), updateEvent=mobj._status_value_update)
@ -405,7 +405,7 @@ class Client(SecopClient):
self.log.info('unhandled: %s %s %r', action, ident, data)
def __repr__(self):
return 'Client(%r)' % self.uri
return f'Client({self.uri!r})'
class Console(code.InteractiveConsole):

View File

@ -68,9 +68,7 @@ class Mod(dict):
# matches name from spec
if not re.match(r'^[a-zA-Z]\w{0,62}$', name, re.ASCII):
raise ConfigError('Not a valid SECoP Module name: "%s". '
'Does it only contain letters, numbers and underscores?'
% (name))
raise ConfigError(f'Not a valid SECoP Module name: "{name}". Does it only contain letters, numbers and underscores?')
# Make parameters out of all keywords
groups = {}
for key, val in kwds.items():
@ -152,8 +150,7 @@ def to_config_path(cfgfile, log):
filename = None
if filename is None:
raise ConfigError("Couldn't find cfg file %r in %s"
% (cfgfile, generalConfig.confdir))
raise ConfigError(f"Couldn't find cfg file {cfgfile!r} in {generalConfig.confdir}")
if not filename.endswith('_cfg.py'):
log.warning("Config files should end in '_cfg.py': %s", os.path.basename(filename))
log.debug('Using config file %s for %s', filename, cfgfile)

View File

@ -227,7 +227,7 @@ class FloatRange(HasUnit, DataType):
raise
value = float(value)
except Exception:
raise WrongTypeError('can not convert %s to a float' % shortrepr(value)) from None
raise WrongTypeError(f'can not convert {shortrepr(value)} to a float') from None
# map +/-infty to +/-max possible number
return clamp(-sys.float_info.max, value, sys.float_info.max)
@ -241,8 +241,7 @@ class FloatRange(HasUnit, DataType):
# silently clamp when outside by not more than prec
return clamp(self.min, value, self.max)
info = self.exportProperties()
raise RangeError('%.14g must be between %g and %g' %
(value, info.get('min', float('-inf')), info.get('max', float('inf'))))
raise RangeError(f"{value:.14g} must be between {info.get('min', float('-inf')):g} and {info.get('max', float('inf')):g}")
def __repr__(self):
hints = self.get_info()
@ -314,7 +313,7 @@ class IntRange(DataType):
fvalue = float(value)
value = int(value)
except Exception:
raise WrongTypeError('can not convert %s to an int' % shortrepr(value)) from None
raise WrongTypeError(f'can not convert {shortrepr(value)} to an int') from None
if round(fvalue) != fvalue:
raise WrongTypeError('%r should be an int')
return value
@ -334,7 +333,7 @@ class IntRange(DataType):
args = args[:1]
if args[0] == DEFAULT_MIN_INT:
args = ()
return 'IntRange%s' % repr(args)
return f'IntRange{repr(args)}'
def export_value(self, value):
"""returns a python object fit for serialisation"""
@ -435,7 +434,7 @@ class ScaledInteger(HasUnit, DataType):
raise
value = float(value)
except Exception:
raise WrongTypeError('can not convert %s to float' % shortrepr(value)) from None
raise WrongTypeError(f'can not convert {shortrepr(value)} to float') from None
intval = int(round(value / self.scale))
return float(intval * self.scale) # return 'actual' value (which is more discrete than a float)
@ -445,11 +444,10 @@ class ScaledInteger(HasUnit, DataType):
if self.min - self.scale < value < self.max + self.scale:
# silently clamp when outside by not more than self.scale
return clamp(self(self.min), result, self(self.max))
raise RangeError('%.14g must be between between %g and %g' %
(value, self.min, self.max))
raise RangeError(f'{value:.14g} must be between between {self.min:g} and {self.max:g}')
def __repr__(self):
hints = self.get_info(scale=float('%g' % self.scale),
hints = self.get_info(scale=float(f'{self.scale:g}'),
min=int(round(self.min / self.scale)),
max=int(round(self.max / self.scale)))
return 'ScaledInteger(%s)' % (', '.join('%s=%r' % kv for kv in hints.items()))
@ -522,14 +520,14 @@ class EnumType(DataType):
return self._enum[value]
except (KeyError, TypeError): # TypeError will be raised when value is not hashable
if isinstance(value, (int, str)):
raise RangeError('%s is not a member of enum %r' % (shortrepr(value), self._enum)) from None
raise WrongTypeError('%s must be either int or str for an enum value' % (shortrepr(value))) from None
raise RangeError(f'{shortrepr(value)} is not a member of enum {self._enum!r}') from None
raise WrongTypeError(f'{shortrepr(value)} must be either int or str for an enum value') from None
def from_string(self, text):
return self(text)
def format_value(self, value, unit=None):
return '%s<%s>' % (self._enum[value].name, self._enum[value].value)
return f'{self._enum[value].name}<{self._enum[value].value}>'
def set_name(self, name):
self._enum.name = name
@ -571,7 +569,7 @@ class BLOBType(DataType):
def __call__(self, value):
"""accepts bytes only"""
if not isinstance(value, bytes):
raise WrongTypeError('%s must be of type bytes' % shortrepr(value))
raise WrongTypeError(f'{shortrepr(value)} must be of type bytes')
size = len(value)
if size < self.minbytes:
raise RangeError(
@ -636,12 +634,12 @@ class StringType(DataType):
def __call__(self, value):
"""accepts strings only"""
if not isinstance(value, str):
raise WrongTypeError('%s has the wrong type!' % shortrepr(value))
raise WrongTypeError(f'{shortrepr(value)} has the wrong type!')
if not self.isUTF8:
try:
value.encode('ascii')
except UnicodeEncodeError:
raise RangeError('%s contains non-ascii character!' % shortrepr(value)) from None
raise RangeError(f'{shortrepr(value)} contains non-ascii character!') from None
size = len(value)
if size < self.minchars:
raise RangeError(
@ -656,7 +654,7 @@ class StringType(DataType):
def export_value(self, value):
"""returns a python object fit for serialisation"""
return '%s' % value
return f'{value}'
def import_value(self, value):
"""returns a python object from serialisation"""
@ -716,7 +714,7 @@ class BoolType(DataType):
return False
if value in [1, '1', 'True', 'true', 'yes', 'on', True]:
return True
raise WrongTypeError('%s is not a boolean value!' % shortrepr(value))
raise WrongTypeError(f'{shortrepr(value)} is not a boolean value!')
def export_value(self, value):
"""returns a python object fit for serialisation"""
@ -794,8 +792,7 @@ class ArrayOf(DataType):
'members': self.members.export_datatype()}
def __repr__(self):
return 'ArrayOf(%s, %s, %s)' % (
repr(self.members), self.minlen, self.maxlen)
return f'ArrayOf({repr(self.members)}, {self.minlen}, {self.maxlen})'
def check_type(self, value):
try:
@ -808,8 +805,7 @@ class ArrayOf(DataType):
raise RangeError(
'array too big, holds at most %d elements!' % self.maxlen)
except TypeError:
raise WrongTypeError('%s can not be converted to ArrayOf DataType!'
% type(value).__name__) from None
raise WrongTypeError(f'{type(value).__name__} can not be converted to ArrayOf DataType!') from None
def __call__(self, value):
"""accepts any sequence, converts to tuple (immutable!)"""
@ -841,7 +837,7 @@ class ArrayOf(DataType):
def from_string(self, text):
value, rem = Parser.parse(text)
if rem:
raise ProtocolError('trailing garbage: %r' % rem)
raise ProtocolError(f'trailing garbage: {rem!r}')
return self(value)
def format_value(self, value, unit=None):
@ -854,7 +850,7 @@ class ArrayOf(DataType):
unit = members.unit
else:
innerunit = None
res = '[%s]' % (', '.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:
return ' '.join([res, unit])
return res
@ -895,16 +891,15 @@ class TupleOf(DataType):
return {'type': 'tuple', 'members': [subtype.export_datatype() for subtype in self.members]}
def __repr__(self):
return 'TupleOf(%s)' % ', '.join([repr(st) for st in self.members])
return f"TupleOf({', '.join([repr(st) for st in self.members])})"
def check_type(self, value):
try:
if len(value) != len(self.members):
raise WrongTypeError(
'tuple needs %d elements' % len(self.members))
f'tuple needs {len(self.members)} elements')
except TypeError:
raise WrongTypeError('%s can not be converted to TupleOf DataType!'
% type(value).__name__) from None
raise WrongTypeError(f'{type(value).__name__} can not be converted to TupleOf DataType!') from None
def __call__(self, value):
"""accepts any sequence, converts to tuple"""
@ -936,12 +931,11 @@ class TupleOf(DataType):
def from_string(self, text):
value, rem = Parser.parse(text)
if rem:
raise ProtocolError('trailing garbage: %r' % rem)
raise ProtocolError(f'trailing garbage: {rem!r}')
return self(value)
def format_value(self, value, unit=None):
return '(%s)' % (', '.join([sub.format_value(elem)
for sub, elem in zip(self.members, value)]))
return f"({', '.join([sub.format_value(elem) for sub, elem in zip(self.members, value)])})"
def compatible(self, other):
if not isinstance(other, TupleOf):
@ -998,7 +992,7 @@ class StructOf(DataType):
return res
def __repr__(self):
opt = ', optional=%r' % self.optional if self.optional else ''
opt = f', optional={self.optional!r}' if self.optional else ''
return 'StructOf(%s%s)' % (', '.join(
['%s=%s' % (n, repr(st)) for n, st in list(self.members.items())]), opt)
@ -1008,8 +1002,7 @@ class StructOf(DataType):
if set(dict(value)) != set(self.members):
raise WrongTypeError('member names do not match') from None
except TypeError:
raise WrongTypeError('%s can not be converted a StructOf'
% type(value).__name__) from None
raise WrongTypeError(f'{type(value).__name__} can not be converted a StructOf') from None
try:
return ImmutableDict((str(k), self.members[k](v))
for k, v in list(value.items()))
@ -1021,13 +1014,12 @@ class StructOf(DataType):
try:
superfluous = set(dict(value)) - set(self.members)
except TypeError:
raise WrongTypeError('%s can not be converted a StructOf'
% type(value).__name__) from None
raise WrongTypeError(f'{type(value).__name__} can not be converted a StructOf') from None
if superfluous - set(self.optional):
raise WrongTypeError('struct contains superfluous members: %s' % ', '.join(superfluous))
raise WrongTypeError(f"struct contains superfluous members: {', '.join(superfluous)}")
missing = set(self.members) - set(value) - set(self.optional)
if missing:
raise WrongTypeError('missing struct elements: %s' % ', '.join(missing))
raise WrongTypeError(f"missing struct elements: {', '.join(missing)}")
try:
if previous is None:
return ImmutableDict((str(k), self.members[k].validate(v))
@ -1052,7 +1044,7 @@ class StructOf(DataType):
def from_string(self, text):
value, rem = Parser.parse(text)
if rem:
raise ProtocolError('trailing garbage: %r' % rem)
raise ProtocolError(f'trailing garbage: {rem!r}')
return self(dict(value))
def format_value(self, value, unit=None):
@ -1103,8 +1095,8 @@ class CommandType(DataType):
def __repr__(self):
if self.result is None:
return 'CommandType(%s)' % (repr(self.argument) if self.argument else '')
return 'CommandType(%s, %s)' % (repr(self.argument), repr(self.result))
return f"CommandType({repr(self.argument) if self.argument else ''})"
return f'CommandType({repr(self.argument)}, {repr(self.result)})'
def __call__(self, value):
raise ProgrammingError('commands can not be converted to a value')
@ -1118,7 +1110,7 @@ class CommandType(DataType):
def from_string(self, text):
value, rem = Parser.parse(text)
if rem:
raise ProtocolError('trailing garbage: %r' % rem)
raise ProtocolError(f'trailing garbage: {rem!r}')
return self(value)
def format_value(self, value, unit=None):
@ -1184,8 +1176,7 @@ class ValueType(DataType):
try:
return self.validator(value)
except Exception as e:
raise ConfigError('Validator %s raised %r for value %s' \
% (self.validator, e, value)) from e
raise ConfigError(f'Validator {self.validator} raised {e!r} for value {value}') from e
return value
def copy(self):
@ -1242,7 +1233,7 @@ class OrType(DataType):
return t.validate(value) # use always strict validation
except Exception:
pass
raise WrongTypeError("Invalid Value, must conform to one of %s" % (', '.join((str(t) for t in self.types))))
raise WrongTypeError(f"Invalid Value, must conform to one of {', '.join(str(t) for t in self.types)}")
Int8 = IntRange(-(1 << 7), (1 << 7) - 1)
@ -1264,7 +1255,7 @@ class LimitsType(TupleOf):
"""accepts an ordered tuple of numeric member types"""
limits = TupleOf.validate(self, value)
if limits[1] < limits[0]:
raise RangeError('Maximum Value %s must be greater than minimum value %s!' % (limits[1], limits[0]))
raise RangeError(f'Maximum Value {limits[1]} must be greater than minimum value {limits[0]}!')
return limits
@ -1312,7 +1303,7 @@ class StatusType(TupleOf):
first = 'Status' # enum name
bad = {n for n in args if n not in StatusType.__dict__ or n.startswith('_')} # avoid built-in attributes
if bad:
raise ProgrammingError('positional arguments %r must be standard status code names' % bad)
raise ProgrammingError(f'positional arguments {bad!r} must be standard status code names')
self.enum = Enum(Enum(first, **{n: StatusType.__dict__[n] for n in args}), **kwds)
super().__init__(EnumType(self.enum), StringType())
@ -1374,9 +1365,9 @@ def get_datatype(json, pname=''):
kwargs = json.copy()
base = kwargs.pop('type')
except (TypeError, KeyError, AttributeError):
raise WrongTypeError('a data descriptor must be a dict containing a "type" key, not %r' % json) from None
raise WrongTypeError(f'a data descriptor must be a dict containing a "type" key, not {json!r}') from None
try:
return DATATYPES[base](pname=pname, **kwargs)
except Exception as e:
raise WrongTypeError('invalid data descriptor: %r (%s)' % (json, str(e))) from None
raise WrongTypeError(f'invalid data descriptor: {json!r} ({str(e)})') from None

View File

@ -54,7 +54,7 @@ class SECoPError(RuntimeError):
res = []
res.extend((repr(a) for a in self.args))
#res.extend(('%s=%r' % i for i in self.kwds.items()))
return '%s(%s)' % (self.name or type(self).__name__, ', '.join(res))
return f"{self.name or type(self).__name__}({', '.join(res)})"
def __str__(self):
return self.format(True)
@ -76,7 +76,7 @@ class SECoPError(RuntimeError):
prefix = '' if self.name2class.get(self.name) == type(self) else type(self).__name__
prefix += ''.join(' in ' + m for m in mlist).strip()
if prefix:
return '%s: %s' % (prefix, super().__str__())
return f'{prefix}: {super().__str__()}'
return super().__str__()
def __eq__(self, other):
@ -268,7 +268,7 @@ def secop_error(exc):
"""turn into InternalError, if not already a SECoPError"""
if isinstance(exc, SECoPError):
return exc
return InternalError('%s: %s' % (type(exc).__name__, exc))
return InternalError(f'{type(exc).__name__}: {exc}')
# TODO: check if these are really needed:

View File

@ -79,7 +79,7 @@ class HasTargetLimits(Feature):
min_, max_ = self.abslimits
t_min, t_max = self.apply_offset(1, dt.min, dt.max)
if t_min > max_ or t_max < min_:
raise ConfigError('abslimits not within %s range' % pname)
raise ConfigError(f'abslimits not within {pname} range')
self.abslimits = clamp(t_min, min_, t_max), clamp(t_min, max_, t_max)
super().checkProperties()
@ -90,8 +90,8 @@ class HasTargetLimits(Feature):
min_, max_ = self.apply_offset(-1, *self.abslimits)
if not min_ <= value[0] <= value[1] <= max_:
if value[0] > value[1]:
raise RangeError('invalid interval: %r' % value)
raise RangeError('limits not within abs limits [%g, %g]' % (min_, max_))
raise RangeError(f'invalid interval: {value!r}')
raise RangeError(f'limits not within abs limits [{min_:g}, {max_:g}]')
self.limits = value
self.saveParameters()
return self.limits
@ -100,7 +100,7 @@ class HasTargetLimits(Feature):
"""check if value is valid"""
min_, max_ = self.target_limits
if not min_ <= value <= max_:
raise RangeError('limits violation: %g outside [%g, %g]' % (value, min_, max_))
raise RangeError(f'limits violation: {value:g} outside [{min_:g}, {max_:g}]')
# --- legacy mixins, not agreed as standard ---

View File

@ -57,13 +57,13 @@ def write_config(file_name, tree_widget):
blank_lines += 1
value = value.replace('\n\n', '\n.\n')
value = value.replace('\n', '\n ')
itm_lines[id(itm)] = '[%s %s]\n' % (itm.kind, itm.name) +\
itm_lines[id(itm)] = f'[{itm.kind} {itm.name}]\n' +\
value_str % (SECTIONS[itm.kind], value)
# TODO params and props
elif itm.kind == PARAMETER and value:
itm_lines[id(itm)] = value_str % (itm.name, value)
elif itm.kind == PROPERTY:
prop_name = '.%s' % itm.name
prop_name = f'.{itm.name}'
if par.kind == PARAMETER:
prop_name = par.name + prop_name
itm_lines[id(itm)] = value_str % (prop_name, value)
@ -72,7 +72,7 @@ def write_config(file_name, tree_widget):
for key, dict_value in itm_lines.items():
if key == id(par):
value = value.replace('\n', '\n# ')
temp_itm_lines[id(itm)] = '# %s' % value
temp_itm_lines[id(itm)] = f'# {value}'
temp_itm_lines[key] = dict_value
itm_lines.clear()
itm_lines.update(temp_itm_lines)
@ -166,8 +166,7 @@ def get_comments(node, ifs, mods, file_path):
if line.startswith('#'):
line = line[1:].strip()
if index and all_lines[index-1][0] == '#':
current_comment.set_value('%s\n%s' % (current_comment.
get_value(), line))
current_comment.set_value(f'{current_comment.get_value()}\n{line}')
else:
current_comment = TreeWidgetItem(COMMENT, '#', line)
next_line = get_next_line(index, all_lines)
@ -196,12 +195,12 @@ def insert_comment(index, line, all_lines, current_comment, node, all_ifs, all_m
# pylint: disable=inconsistent-return-statements
def insert_section_comment(line, current_comment, node, all_ifs, all_mods):
try:
if line.startswith('[%s' % NODE):
if line.startswith(f'[{NODE}'):
node.insertChild(0, current_comment)
elif line.startswith('[%s' % INTERFACE):
elif line.startswith(f'[{INTERFACE}'):
all_ifs[get_name_of_section(line)]. \
insertChild(0, current_comment)
elif line.startswith('[%s' % MODULE):
elif line.startswith(f'[{MODULE}'):
all_mods[get_name_of_section(line)]. \
insertChild(0, current_comment)
else:
@ -219,9 +218,9 @@ def insert_param_prop_comment(index, line, all_lines, current_comment,
if not parent:
# TODO invalid file
pass
if parent.startswith('[%s' % NODE):
if parent.startswith(f'[{NODE}'):
parent_item = node
elif parent.startswith('[%s' % INTERFACE):
elif parent.startswith(f'[{INTERFACE}'):
parent_item = all_ifs[get_name_of_section(parent)]
else:
parent_item = all_mods[get_name_of_section(parent)]

View File

@ -166,13 +166,13 @@ class MainWindow(QMainWindow):
def show_save_message(self, file_name=''):
if file_name:
file_name = ' in "%s"' % file_name
return QMessageBox.question(self, 'Save file?', '''
<h2>Do you want to save changes%s?</h2>
file_name = f' in "{file_name}"'
return QMessageBox.question(self, 'Save file?', f'''
<h2>Do you want to save changes{file_name}?</h2>
<p>
Your changes will be lost if you don't save them!
</p>
''' % file_name,
''',
QMessageBox.StandardButton.Cancel |
QMessageBox.StandardButton.Close |
QMessageBox.StandardButton.Save,

View File

@ -52,7 +52,7 @@ class TreeWidgetItem(QTreeWidgetItem):
self.parameters = parameters or {}
self.properties = properties or {}
if self.kind and self.kind != 'node':
setTreeIcon(self, '%s.png' % self.kind)
setTreeIcon(self, f'{self.kind}.png')
else:
setTreeIcon(self, 'empty.png')
font = QFont()

View File

@ -40,7 +40,7 @@ def loadUi(widget, uiname, subdir='ui'):
def setIcon(widget, icon_name):
widget.setIcon(QIcon(':/cfg_editor/%s' % icon_name))
widget.setIcon(QIcon(f':/cfg_editor/{icon_name}'))
widget.setIconSize(QSize(60, 60))
@ -58,11 +58,11 @@ def set_name_edit_style(invalid, name_edit, button_box=None):
def setTreeIcon(widget, icon_name):
widget.setIcon(0, QIcon(':/cfg_editor/%s' % icon_name))
widget.setIcon(0, QIcon(f':/cfg_editor/{icon_name}'))
def setActionIcon(widget, icon_name):
widget.setIcon(QIcon(':/cfg_editor/%s' % icon_name))
widget.setIcon(QIcon(f':/cfg_editor/{icon_name}'))
def get_subtree_nodes(tree_widget_item):
@ -117,7 +117,7 @@ def get_modules():
if not path.isfile(path.join(base_path, dirname, filename)) or \
filename == '__init__.py' or filename[-3:] != '.py':
continue
module = '%s.%s' % (dirname, filename[:-3])
module = f'{dirname}.{filename[:-3]}'
module_in_file = False
try:
__import__(module)

View File

@ -338,7 +338,7 @@ class AddDialog(QDialog):
the value from self.value"""
super().__init__(parent)
loadUi(self, 'add_dialog.ui')
self.setWindowTitle('Add %s' % kind)
self.setWindowTitle(f'Add {kind}')
self.kind = kind
self.invalid_names = invalid_names
if self.invalid_names:
@ -478,7 +478,7 @@ class TreeComboBox(QComboBox):
act_item = act_index.model().itemFromIndex(act_index)
value += act_item.text()
while act_item.parent():
value = '%s.%s' % (act_item.parent().text(), value)
value = f'{act_item.parent().text()}.{value}'
act_item = act_item.parent()
return value

View File

@ -42,7 +42,7 @@ class QSECNode(QObject):
self.equipmentId = conn.properties['equipment_id']
self.log.info('Switching to logger %s', self.equipmentId)
self.log.name = '.'.join((parent_logger.name, self.equipmentId))
self.nodename = '%s (%s)' % (self.equipmentId, conn.uri)
self.nodename = f'{self.equipmentId} ({conn.uri})'
self.modules = conn.modules
self.properties = self.conn.properties
self.protocolVersion = conn.secop_version
@ -106,7 +106,7 @@ class QSECNode(QObject):
self.stateChange.emit(self.nodename, online, state)
def unhandledMessage(self, action, specifier, data):
self.unhandledMsg.emit('%s %s %r' % (action, specifier, data))
self.unhandledMsg.emit(f'{action} {specifier} {data!r}')
def terminate_connection(self):
self.conn.disconnect()

View File

@ -153,24 +153,22 @@ class Console(QWidget):
return
self._addLogEntry(
'<span style="font-weight:bold">Request:</span> '
'<tt>%s</tt>' % toHtmlEscaped(msg),
f'<span style="font-weight:bold">Request:</span> <tt>{toHtmlEscaped(msg)}</tt>',
raw=True)
# msg = msg.split(' ', 2)
try:
reply = self._node.syncCommunicate(*self._node.decode_message(msg))
if msg == 'describe':
_, eid, stuff = self._node.decode_message(reply)
reply = '%s %s %s' % (_, eid, json.dumps(
stuff, indent=2, separators=(',', ':'), sort_keys=True))
reply = f"{_} {eid} {json.dumps(stuff, indent=2, separators=(',', ':'), sort_keys=True)}"
self._addLogEntry(reply.rstrip('\n'))
else:
self._addLogEntry(reply.rstrip('\n'))
except SECoPError as e:
einfo = e.args[0] if len(e.args) == 1 else json.dumps(e.args)
self._addLogEntry('%s: %s' % (e.name, einfo), error=True)
self._addLogEntry(f'{e.name}: {einfo}', error=True)
except Exception as e:
self._addLogEntry('error when sending %r: %r' % (msg, e),
self._addLogEntry(f'error when sending {msg!r}: {e!r}',
error=True)
self.msgLineEdit.clear()

View File

@ -75,11 +75,10 @@ class LogWindow(QMainWindow):
s = record.getMessage()
time = record.created
if record.levelno == self.levels['Error']:
s = '<b>%s</b>' %s
s='<span style="color:%s">[%s] </span><span style="color:%s">%s: %s</span>' \
% (self.timecolor.name(), time, \
self.messagecolors[record.levelno].name(), \
record.name, s)
s = f'<b>{s}</b>'
s = f'<span style="color:{self.timecolor.name()}">[{time}] </span>' \
f'<span style="color:{self.messagecolors[record.levelno].name()}">' \
f'{record.name}: {s}</span>'
self.logBrowser.append(s)
def on_logLevel_currentTextChanged(self, level):

View File

@ -211,7 +211,7 @@ class MainWindow(QMainWindow):
except Exception as e:
self.log.error('error in addNode: %r', e)
QMessageBox.critical(self.parent(),
'Connecting to %s failed!' % host, str(e))
f'Connecting to {host} failed!', str(e))
def _addNode(self, host):
prevWidget = self._nodeWidgets.get(host)

View File

@ -34,7 +34,7 @@ class CommandDialog(QDialog):
super().__init__(parent)
loadUi(self, 'cmddialog.ui')
self.setWindowTitle('Arguments for %s' % cmdname)
self.setWindowTitle(f'Arguments for {cmdname}')
# row = 0
self._labels = []
@ -65,15 +65,14 @@ class CommandDialog(QDialog):
def showCommandResultDialog(command, args, result, extras=''):
m = QMessageBox()
args = '' if args is None else repr(args)
m.setText('calling: %s(%s)\nyielded: %r\nqualifiers: %s' %
(command, args, result, extras))
m.setText(f'calling: {command}({args})\nyielded: {result!r}\nqualifiers: {extras}')
m.exec()
def showErrorDialog(command, args, error):
m = QMessageBox()
args = '' if args is None else repr(args)
m.setText('calling: %s(%s)\nraised %r' % (command, args, error))
m.setText(f'calling: {command}({args})\nraised {error!r}')
m.exec()
@ -271,7 +270,7 @@ class ModuleWidget(QWidget):
for prop, value in props.items():
l = QHBoxLayout()
l.setContentsMargins(0,0,0,0)
name = QLabel('<b>%s:</b>' % prop.capitalize())
name = QLabel(f'<b>{prop.capitalize()}:</b>')
val = QLabel(str(value))
val.setWordWrap(True)
l.addWidget(name)
@ -353,7 +352,7 @@ class ModuleWidget(QWidget):
return
plotButton = QToolButton()
plotButton.setIcon(QIcon(':/icons/plot'))
plotButton.setToolTip('Plot %s' % param)
plotButton.setToolTip(f'Plot {param}')
plotAddButton = QToolButton()
plotAddButton.setIcon(QIcon(':/icons/plot-add'))
plotAddButton.setToolTip('Plot With...')

View File

@ -81,7 +81,7 @@ class NodeWidget(QWidget):
self._detailedParams[module] = {}
for param in node.getParameters(module):
view = ParameterView(node, module, param)
view.setWindowTitle('%s:%s:%s - Properties' % (self._node.equipmentId, module, param))
view.setWindowTitle(f'{self._node.equipmentId}:{module}:{param} - Properties')
self._detailedParams[module][param] = view
viewLayout.addWidget(widget)
@ -154,7 +154,7 @@ class NodeWidget(QWidget):
menu_plot_ext.setEnabled(False)
else:
for (m, p), plot in self._activePlots.items():
opt_ext = menu_plot_ext.addAction("%s:%s" % (m, p))
opt_ext = menu_plot_ext.addAction(f"{m}:{p}")
opt_ext.triggered.connect(
lambda plot=plot: self._requestPlot(item, plot))
@ -178,7 +178,7 @@ class NodeWidget(QWidget):
QInputDialog.InputDialogOption.UseListViewForComboBoxItems)
dialog.setComboBoxItems(plots.keys())
dialog.setTextValue(list(plots)[0])
dialog.setWindowTitle('Plot %s with...' % param)
dialog.setWindowTitle(f'Plot {param} with...')
dialog.setLabelText('')
if dialog.exec() == QInputDialog.DialogCode.Accepted:

View File

@ -38,7 +38,7 @@ class ParameterView(QWidget):
self._propWidgets = {} # widget cache do avoid garbage collection
self.paramNameLabel.setText("%s:%s" % (module, parameter))
self.paramNameLabel.setText(f"{module}:{parameter}")
self._initParameterWidgets()
# self._node.newData.connect(self._updateValue)

View File

@ -104,11 +104,11 @@ class PlotWidget(QWidget):
def addCurve(self, node, module, param):
paramData = node._getDescribingParameterData(module, param)
name = '%s:%s' % (module, param)
name = f'{module}:{param}'
unit = paramData.get('unit', '')
if unit:
unit = '/' + unit
curve = self.plot.plot(name='%s%s' % (name, unit))
curve = self.plot.plot(name=f'{name}{unit}')
if 'min' in paramData and 'max' in paramData:
curve.setXRange(paramData['min'], paramData['max'])
@ -118,7 +118,7 @@ class PlotWidget(QWidget):
node.newData.connect(self.update)
def setCurveColor(self, module, param, color):
curve = self.curves['%s:%s' % (module, param)]
curve = self.curves[f'{module}:{param}']
curve.setPen(color)
def scrollUpdate(self):
@ -129,7 +129,7 @@ class PlotWidget(QWidget):
curve.setData(x,y)
def update(self, module, param, value):
name = '%s:%s' % (module, param)
name = f'{module}:{param}'
if name not in self.curves:
return
curve = self.curves[name]

View File

@ -260,5 +260,5 @@ class msg(QDialog):
super().reject()
def done(self, how):
print('done(%r)' % how)
print(f'done({how!r})')
return super().done(how)

View File

@ -55,7 +55,7 @@ class HasIO(Module):
io = opts.get('io')
super().__init__(name, logger, opts, srv)
if self.uri:
opts = {'uri': self.uri, 'description': 'communication device for %s' % name,
opts = {'uri': self.uri, 'description': f'communication device for {name}',
'export': False}
ioname = self.ioDict.get(self.uri)
if not ioname:
@ -66,7 +66,7 @@ class HasIO(Module):
self.ioDict[self.uri] = ioname
self.io = ioname
elif not io:
raise ConfigError("Module %s needs a value for either 'uri' or 'io'" % name)
raise ConfigError(f"Module {name} needs a value for either 'uri' or 'io'")
def communicate(self, *args):
return self.io.communicate(*args)
@ -228,14 +228,14 @@ class StringIO(IOBase):
return bytes([value])
if isinstance(value, bytes):
return value
raise ValueError('invalid end_of_line: %s' % repr(value))
raise ValueError(f'invalid end_of_line: {repr(value)}')
def earlyInit(self):
super().earlyInit()
eol = self.end_of_line
if isinstance(eol, (tuple, list)):
if len(eol) not in (1, 2):
raise ValueError('invalid end_of_line: %s' % eol)
raise ValueError(f'invalid end_of_line: {eol}')
else:
eol = [eol]
# eol for read and write might be distinct
@ -253,8 +253,7 @@ class StringIO(IOBase):
reply = self.communicate(command)
if not re.match(regexp, reply):
self.closeConnection()
raise CommunicationFailedError('bad response: %s does not match %s' %
(reply, regexp))
raise CommunicationFailedError(f'bad response: {reply} does not match {regexp}')
@Command(StringType(), result=StringType())
def communicate(self, command):
@ -340,7 +339,7 @@ def make_bytes(string):
def hexify(bytes_):
return ' '.join('%02x' % r for r in bytes_)
return ' '.join(f'{r:02x}' for r in bytes_)
class BytesIO(IOBase):
@ -365,7 +364,7 @@ class BytesIO(IOBase):
reply = self.communicate(make_bytes(request), replylen)
if not replypat.match(reply):
self.closeConnection()
raise CommunicationFailedError('bad response: %r does not match %r' % (reply, expected))
raise CommunicationFailedError(f'bad response: {reply!r} does not match {expected!r}')
@Command((BLOBType(), IntRange(0)), result=BLOBType())
def communicate(self, request, replylen): # pylint: disable=arguments-differ

View File

@ -102,11 +102,11 @@ class GeneralConfig:
if cfg.get('confdir') is None:
cfg['confdir'] = path.dirname(configfile)
for key in mandatory:
cfg[key] = environ.get('FRAPPY_%s' % key.upper(), cfg.get(key))
cfg[key] = environ.get(f'FRAPPY_{key.upper()}', cfg.get(key))
missing_keys = [key for key in mandatory if cfg[key] is None]
if missing_keys:
if configfile:
raise KeyError('missing value for %s in %s' % (' and '.join(missing_keys), configfile))
raise KeyError(f"missing value for {' and '.join(missing_keys)} in {configfile}")
raise KeyError('missing %s' % ' and '.join('FRAPPY_%s' % k.upper() for k in missing_keys))
# this is not customizable
cfg['basedir'] = repodir
@ -215,7 +215,7 @@ def get_class(spec):
def mkthread(func, *args, **kwds):
t = threading.Thread(
name='%s:%s' % (func.__module__, func.__name__),
name=f'{func.__module__}:{func.__name__}',
target=func,
args=args,
kwargs=kwds)
@ -250,7 +250,7 @@ def formatExtendedTraceback(exc_info=None):
linecache.checkcache(filename)
line = linecache.getline(filename, tb.tb_lineno, frame.f_globals)
if line:
item = item + ' %s\n' % line.strip()
item = item + f' {line.strip()}\n'
ret.append(item)
if filename not in ('<script>', '<string>'):
ret += formatExtendedFrame(tb.tb_frame)
@ -271,7 +271,7 @@ def formatExtendedStack(level=1):
linecache.checkcache(filename)
line = linecache.getline(filename, lineno, f.f_globals)
if line:
item = item + ' %s\n' % line.strip()
item = item + f' {line.strip()}\n'
ret.insert(1, item)
if filename != '<script>':
ret[2:2] = formatExtendedFrame(f)
@ -317,10 +317,10 @@ def parseHostPort(host, defaultport):
else:
host, port = host
if not HOSTNAMEPAT.match(host):
raise ValueError('illegal host name %r' % host)
raise ValueError(f'illegal host name {host!r}')
if 0 < port < 65536:
return host, port
raise ValueError('illegal port number: %r' % port)
raise ValueError(f'illegal port number: {port!r}')
def tcpSocket(host, defaultport, timeout=None):

View File

@ -68,7 +68,7 @@ class AsynConn:
if '/dev' in uri:
raise ValueError("the correct uri for a serial port is: "
"'serial:///dev/<tty>[?<option>=<value>[&<option>=value ...]]'") from None
raise ValueError('invalid hostname %r' % uri) from None
raise ValueError(f'invalid hostname {uri!r}') from None
iocls = cls.SCHEME_MAP['tcp']
return object.__new__(iocls)
@ -130,7 +130,7 @@ class AsynConn:
if timeout:
if time.time() < end:
continue
raise TimeoutError('timeout in readline (%g sec)' % timeout)
raise TimeoutError(f'timeout in readline ({timeout:g} sec)')
return None
self._rxbuffer += data
@ -149,7 +149,7 @@ class AsynConn:
if timeout:
if time.time() < end:
continue
raise TimeoutError('timeout in readbytes (%g sec)' % timeout)
raise TimeoutError(f'timeout in readbytes ({timeout:g} sec)')
return None
self._rxbuffer += data
line = self._rxbuffer[:nbytes]
@ -261,7 +261,7 @@ class AsynSerial(AsynConn):
try:
key, value = kv.split('=')
except TypeError:
raise ConfigError('%r must be <key>=<value>' % kv) from None
raise ConfigError(f'{kv!r} must be <key>=<value>') from None
if key == 'parity':
options[key] = value
else:
@ -271,7 +271,7 @@ class AsynSerial(AsynConn):
name = parity.upper()
fullname = self.PARITY_NAMES[name[0]]
if not fullname.startswith(name):
raise ConfigError('illegal parity: %s' % parity)
raise ConfigError(f'illegal parity: {parity}')
options['parity'] = name[0]
if 'timeout' not in options:
options['timeout'] = self.timeout

View File

@ -37,8 +37,8 @@ def fmt_param(name, param):
else:
dtinfo = [short_doc(param.datatype), 'rd' if param.readonly else 'wr',
None if param.export else 'hidden']
dtinfo = '*(%s)* ' % ', '.join(filter(None, dtinfo))
return '- **%s** - %s%s\n' % (name, dtinfo, desc)
dtinfo = f"*({', '.join(filter(None, dtinfo))})* "
return f'- **{name}** - {dtinfo}{desc}\n'
def fmt_command(name, command):
@ -46,8 +46,8 @@ def fmt_command(name, command):
if '(' in desc[0:2]:
dtinfo = '' # note: we expect that desc contains argument list
else:
dtinfo = '*%s*' % short_doc(command.datatype) + ' -%s ' % ('' if command.export else ' *(hidden)*')
return '- **%s**\\ %s%s\n' % (name, dtinfo, desc)
dtinfo = f'*{short_doc(command.datatype)}*' + f" -{'' if command.export else ' *(hidden)*'} "
return f'- **{name}**\\ {dtinfo}{desc}\n'
def fmt_property(name, prop):
@ -58,8 +58,8 @@ def fmt_property(name, prop):
dtinfo = [short_doc(prop.datatype), None if prop.export else 'hidden']
dtinfo = ', '.join(filter(None, dtinfo))
if dtinfo:
dtinfo = '*(%s)* ' % dtinfo
return '- **%s** - %s%s\n' % (name, dtinfo, desc)
dtinfo = f'*({dtinfo})* '
return f'- **{name}** - {dtinfo}{desc}\n'
SIMPLETYPES = {
@ -78,22 +78,22 @@ def short_doc(datatype, internal=False):
# pylint: disable=possibly-unused-variable
def doc_EnumType(dt):
return 'one of %s' % str(tuple(dt._enum.keys()))
return f'one of {str(tuple(dt._enum.keys()))}'
def doc_ArrayOf(dt):
return 'array of %s' % short_doc(dt.members, True)
return f'array of {short_doc(dt.members, True)}'
def doc_TupleOf(dt):
return 'tuple of (%s)' % ', '.join(short_doc(m, True) for m in dt.members)
return f"tuple of ({', '.join(short_doc(m, True) for m in dt.members)})"
def doc_CommandType(dt):
argument = short_doc(dt.argument, True) if dt.argument else ''
result = ' -> %s' % short_doc(dt.result, True) if dt.result else ''
return '(%s)%s' % (argument, result) # return argument list only
result = f' -> {short_doc(dt.result, True)}' if dt.result else ''
return f'({argument}){result}' # return argument list only
def doc_NoneOr(dt):
other = short_doc(dt.other, True)
return '%s or None' % other if other else None
return f'{other} or None' if other else None
def doc_OrType(dt):
types = [short_doc(t, True) for t in dt.types]
@ -138,7 +138,7 @@ def append_to_doc(cls, lines, itemcls, name, attrname, fmtfunc):
only the first appearance of a tag above is considered
"""
doc = '\n'.join(lines)
title = 'SECoP %s' % name.title()
title = f'SECoP {name.title()}'
allitems = getattr(cls, attrname, {})
fmtdict = {n: fmtfunc(n, p) for n, p in allitems.items() if isinstance(p, itemcls)}
head, _, tail = doc.partition('{all %s}' % name)
@ -177,7 +177,7 @@ def append_to_doc(cls, lines, itemcls, name, attrname, fmtfunc):
fmted.append('- see also %s\n' % (', '.join(':class:`%s.%s`' % (c.__module__, c.__name__)
for c in cls.__mro__ if c in clsset)))
doc = '%s\n\n:%s: %s\n\n%s' % (head, title, ' '.join(fmted), tail)
doc = f"{head}\n\n:{title}: {' '.join(fmted)}\n\n{tail}"
lines[:] = doc.split('\n')

View File

@ -315,16 +315,16 @@ class Enum(dict):
def __setattr__(self, key, value):
if self.name and key != 'name':
raise TypeError('Enum %r can not be changed!' % self.name)
raise TypeError(f'Enum {self.name!r} can not be changed!')
super().__setattr__(key, value)
def __setitem__(self, key, value):
if self.name:
raise TypeError('Enum %r can not be changed!' % self.name)
raise TypeError(f'Enum {self.name!r} can not be changed!')
super().__setitem__(key, value)
def __delitem__(self, key):
raise TypeError('Enum %r can not be changed!' % self.name)
raise TypeError(f'Enum {self.name!r} can not be changed!')
def __repr__(self):
return 'Enum(%r, %s)' % (self.name, ', '.join('%s=%d' % (m.name, m.value) for m in self.members))

View File

@ -170,9 +170,9 @@ class SequencerMixin:
result = step.func(store, *step.args)
if self._seq_stopflag:
if result:
self._seq_stopped = 'stopped while %s' % step.desc
self._seq_stopped = f'stopped while {step.desc}'
else:
self._seq_stopped = 'stopped after %s' % step.desc
self._seq_stopped = f'stopped after {step.desc}'
cleanup_func = step.kwds.get('cleanup', None)
if callable(cleanup_func):
try:
@ -188,5 +188,5 @@ class SequencerMixin:
except Exception as e:
self.log.exception(
'error in sequence step %r: %s', step.desc, e)
self._seq_error = 'during %s: %s' % (step.desc, e)
self._seq_error = f'during {step.desc}: {e}'
break

View File

@ -121,7 +121,7 @@ class StateMachine:
cls = type(self)
for key, value in kwds.items():
if hasattr(cls, key):
raise AttributeError('can not set %s.%s' % (cls.__name__, key))
raise AttributeError(f'can not set {cls.__name__}.{key}')
setattr(self, key, value)
def _cleanup(self, reason):
@ -181,8 +181,7 @@ class StateMachine:
break
if not callable(ret):
ret = self._cleanup(RuntimeError(
'%s: return value must be callable, Retry or Finish, not %r'
% (self.statefunc.__name__, ret)))
f'{self.statefunc.__name__}: return value must be callable, Retry or Finish, not {ret!r}'))
except Exception as e:
ret = self._cleanup(e)
if ret is None:
@ -190,7 +189,7 @@ class StateMachine:
self._new_state(ret)
else:
ret = self._cleanup(RuntimeError(
'%s: too many states chained - probably infinite loop' % self.statefunc.__name__))
f'{self.statefunc.__name__}: too many states chained - probably infinite loop'))
if ret:
self._new_state(ret)
continue

View File

@ -47,7 +47,7 @@ def check_level(level):
return level
except KeyError:
pass
raise ValueError('%r is not a valid level' % level)
raise ValueError(f'{level!r} is not a valid level')
class RemoteLogHandler(mlzlog.Handler):
@ -113,7 +113,7 @@ class ComLogfileHandler(LogfileHandler):
"""handler for logging communication"""
def format(self, record):
return '%s %s' % (self.formatter.formatTime(record), record.getMessage())
return f'{self.formatter.formatTime(record)} {record.getMessage()}'
class HasComlog:
@ -125,7 +125,7 @@ class HasComlog:
def earlyInit(self):
super().earlyInit()
if self.comlog and generalConfig.initialized and generalConfig.comlog:
self._comLog = mlzlog.Logger('COMLOG.%s' % self.name)
self._comLog = mlzlog.Logger(f'COMLOG.{self.name}')
self._comLog.handlers[:] = []
directory = join(logger.logdir, logger.rootname, 'comlog', self.DISPATCHER.name)
self._comLog.addHandler(ComLogfileHandler(

View File

@ -135,7 +135,7 @@ class HasAccessibles(HasProperties):
except Exception as e:
self.log.debug("read_%s failed with %r", pname, e)
if isinstance(e, SECoPError):
e.raising_methods.append('%s.read_%s' % (self.name, pname))
e.raising_methods.append(f'{self.name}.read_{pname}')
self.announceUpdate(pname, err=e)
raise
self.announceUpdate(pname, value, validate=False)
@ -190,7 +190,7 @@ class HasAccessibles(HasProperties):
new_value = value if new_value is None else validate(new_value)
except Exception as e:
if isinstance(e, SECoPError):
e.raising_methods.append('%s.write_%s' % (self.name, pname))
e.raising_methods.append(f'{self.name}.write_{pname}')
self.announceUpdate(pname, err=e)
raise
self.announceUpdate(pname, new_value, validate=False)
@ -209,11 +209,9 @@ class HasAccessibles(HasProperties):
if not pname:
continue
if prefix == 'do':
raise ProgrammingError('%r: old style command %r not supported anymore'
% (cls.__name__, attrname))
raise ProgrammingError(f'{cls.__name__!r}: old style command {attrname!r} not supported anymore')
if prefix in ('read', 'write') and attrname not in cls.checkedMethods:
raise ProgrammingError('%s.%s defined, but %r is no parameter'
% (cls.__name__, attrname, pname))
raise ProgrammingError(f'{cls.__name__}.{attrname} defined, but {pname!r} is no parameter')
try:
# update Status type
@ -368,12 +366,11 @@ class Module(HasAccessibles):
else:
self.setProperty(key, value)
except BadValueError:
errors.append('%s: value %r does not match %r!' %
(key, value, self.propertyDict[key].datatype))
errors.append(f'{key}: value {value!r} does not match {self.propertyDict[key].datatype!r}!')
# 3) set automatic properties
mycls, = self.__class__.__bases__ # skip the wrapper class
myclassname = '%s.%s' % (mycls.__module__, mycls.__name__)
myclassname = f'{mycls.__module__}.{mycls.__name__}'
self.implementation = myclassname
# list of all 'secop' modules
# self.interface_classes = [
@ -416,20 +413,16 @@ class Module(HasAccessibles):
for propname, propvalue in cfg.items():
aobj.setProperty(propname, propvalue)
except KeyError:
errors.append("'%s' has no property '%s'" %
(aname, propname))
errors.append(f"'{aname}' has no property '{propname}'")
except BadValueError as e:
errors.append('%s.%s: %s' %
(aname, propname, str(e)))
errors.append(f'{aname}.{propname}: {str(e)}')
else:
bad.append(aname)
# 3) complain about names not found as accessible or property names
if bad:
errors.append(
'%s does not exist (use one of %s)' %
(', '.join(bad), ', '.join(list(self.accessibles) +
list(self.propertyDict))))
f"{', '.join(bad)} does not exist (use one of {', '.join(list(self.accessibles) + list(self.propertyDict))})")
# 4) register value for writing, if given
# apply default when no value is given (in cfg or as Parameter argument)
# or complain, when cfg is needed
@ -441,13 +434,13 @@ class Module(HasAccessibles):
if not pobj.hasDatatype():
head, _, postfix = pname.rpartition('_')
if postfix not in ('min', 'max', 'limits'):
errors.append('%s needs a datatype' % pname)
errors.append(f'{pname} needs a datatype')
continue
# when datatype is not given, properties are set automagically
pobj.setProperty('readonly', False)
baseparam = self.parameters.get(head)
if not baseparam:
errors.append('parameter %r is given, but not %r' % (pname, head))
errors.append(f'parameter {pname!r} is given, but not {head!r}')
continue
dt = baseparam.datatype
if dt is None:
@ -459,16 +452,15 @@ class Module(HasAccessibles):
pobj.setProperty('datatype', dt)
pobj.setProperty('default', getattr(dt, postfix))
if not pobj.description:
pobj.setProperty('description', 'limit for %s' % pname)
pobj.setProperty('description', f'limit for {pname}')
if pobj.value is None:
if pobj.needscfg:
errors.append('%r has no default '
'value and was not given in config!' % pname)
errors.append(f'{pname!r} has no default value and was not given in config!')
if pobj.default is None:
# we do not want to call the setter for this parameter for now,
# this should happen on the first read
pobj.readerror = ConfigError('parameter %r not initialized' % pname)
pobj.readerror = ConfigError(f'parameter {pname!r} not initialized')
# above error will be triggered on activate after startup,
# when not all hardware parameters are read because of startup timeout
pobj.default = pobj.datatype.default
@ -504,7 +496,7 @@ class Module(HasAccessibles):
try:
aobj.checkProperties()
except (ConfigError, ProgrammingError) as e:
errors.append('%s: %s' % (aname, e))
errors.append(f'{aname}: {e}')
if errors:
raise ConfigError(errors)
@ -832,8 +824,8 @@ class Module(HasAccessibles):
max_ = getattr(self, parametername + '_max', float('inf'))
if not min_ <= value <= max_:
if min_ > max_:
raise RangeError('invalid limits: [%g, %g]' % (min_, max_))
raise RangeError('limits violation: %g outside [%g, %g]' % (value, min_, max_))
raise RangeError(f'invalid limits: [{min_:g}, {max_:g}]')
raise RangeError(f'limits violation: {value:g} outside [{min_:g}, {max_:g}]')
class Readable(Module):

View File

@ -92,8 +92,8 @@ class Accessible(HasProperties):
def __repr__(self):
props = []
for k, v in sorted(self.propertyValues.items()):
props.append('%s=%r' % (k, v))
return '%s(%s)' % (self.__class__.__name__, ', '.join(props))
props.append(f'{k}={v!r}')
return f"{self.__class__.__name__}({', '.join(props)})"
class Parameter(Accessible):
@ -227,7 +227,7 @@ class Parameter(Accessible):
elif predefined_cls is None:
self.export = '_' + self.name
else:
raise ProgrammingError('can not use %r as name of a Parameter' % self.name)
raise ProgrammingError(f'can not use {self.name!r} as name of a Parameter')
if 'export' in self.ownProperties:
# avoid export=True overrides export=<name>
self.ownProperties['export'] = self.export
@ -317,10 +317,9 @@ class Parameter(Accessible):
try:
self.datatype.setProperty(key, value)
except KeyError:
raise ProgrammingError('cannot set %s on parameter with datatype %s'
% (key, type(self.datatype).__name__)) from None
raise ProgrammingError(f'cannot set {key} on parameter with datatype {type(self.datatype).__name__}') from None
except BadValueError as e:
raise ProgrammingError('property %s: %s' % (key, str(e))) from None
raise ProgrammingError(f'property {key}: {str(e)}') from None
def checkProperties(self):
super().checkProperties()
@ -395,8 +394,7 @@ class Command(Accessible):
def __set_name__(self, owner, name):
self.name = name
if self.func is None:
raise ProgrammingError('Command %s.%s must be used as a method decorator' %
(owner.__name__, name))
raise ProgrammingError(f'Command {owner.__name__}.{name} must be used as a method decorator')
self.datatype = CommandType(self.argument, self.result)
if self.export is True:
@ -406,7 +404,7 @@ class Command(Accessible):
elif predefined_cls is None:
self.export = '_' + name
else:
raise ProgrammingError('can not use %r as name of a Command' % name) from None
raise ProgrammingError(f'can not use {name!r} as name of a Command') from None
if 'export' in self.ownProperties:
# avoid export=True overrides export=<name>
self.ownProperties['export'] = self.export
@ -419,7 +417,7 @@ class Command(Accessible):
if obj is None:
return self
if not self.func:
raise ProgrammingError('Command %s not properly configured' % self.name) from None
raise ProgrammingError(f'Command {self.name} not properly configured') from None
return self.func.__get__(obj, owner)
def __call__(self, func):
@ -452,7 +450,7 @@ class Command(Accessible):
this is needed when the @Command is missing on a method overriding a command"""
if not callable(value):
raise ProgrammingError('%s = %r is overriding a Command' % (self.name, value))
raise ProgrammingError(f'{self.name} = {value!r} is overriding a Command')
self.func = value
if value.__doc__:
self.description = inspect.cleandoc(value.__doc__)
@ -478,7 +476,7 @@ class Command(Accessible):
super().setProperty('result', command.result)
super().setProperty(key, value)
except ValueError as e:
raise ProgrammingError('property %s: %s' % (key, str(e))) from None
raise ProgrammingError(f'property {key}: {str(e)}') from None
def do(self, module_obj, argument):
"""perform function call
@ -504,7 +502,7 @@ class Command(Accessible):
res = func(argument)
else:
if argument is not None:
raise WrongTypeError('%s.%s takes no arguments' % (module_obj.__class__.__name__, self.name))
raise WrongTypeError(f'{module_obj.__class__.__name__}.{self.name} takes no arguments')
res = func()
if self.result:
return self.result(res)
@ -515,7 +513,7 @@ class Command(Accessible):
def __repr__(self):
result = super().__repr__()
return result[:-1] + ', %r)' % self.func if self.func else result
return result[:-1] + f', {self.func!r})' if self.func else result
# list of predefined accessibles with their type

View File

@ -111,7 +111,7 @@ class Parser:
return None, text
res, rem = self.parse_sub(text)
if res is None:
print('remtuple %r %r %r' % (rem, text, bra))
print(f'remtuple {rem!r} {text!r} {bra!r}')
if rem[0] == bra:
# allow trailing separator
return tuple(reslist), rem[1:].strip()
@ -175,5 +175,5 @@ class Parser:
def parse(self, orgtext):
res, rem = self.parse_sub(orgtext)
if rem and rem[0] in ',;':
return self.parse_sub('[%s]' % orgtext)
return self.parse_sub(f'[{orgtext}]')
return res, rem

View File

@ -74,7 +74,7 @@ class PersistentMixin(Module):
super().__init__(name, logger, cfgdict, srv)
persistentdir = os.path.join(generalConfig.logdir, 'persistent')
os.makedirs(persistentdir, exist_ok=True)
self.persistentFile = os.path.join(persistentdir, '%s.%s.json' % (self.DISPATCHER.equipment_id, self.name))
self.persistentFile = os.path.join(persistentdir, f'{self.DISPATCHER.equipment_id}.{self.name}.json')
self.initData = {} # "factory" settings
loaded = self.loadPersistentData()
for pname in self.parameters:

View File

@ -39,7 +39,7 @@ class HasDescriptors(Object):
bad = [k for k, v in cls.__dict__.items()
if isinstance(v, tuple) and len(v) == 1 and hasattr(v[0], '__set_name__')]
if bad:
raise ProgrammingError('misplaced trailing comma after %s.%s' % (cls.__name__, '/'.join(bad)))
raise ProgrammingError(f"misplaced trailing comma after {cls.__name__}.{'/'.join(bad)}")
# storage for 'properties of a property'
@ -94,19 +94,19 @@ class Property:
return type(self)(**self.__dict__)
def __repr__(self):
extras = ['default=%s' % repr(self.default)]
extras = [f'default={repr(self.default)}']
if self.export:
extras.append('extname=%r' % self.extname)
extras.append('export=%r' % self.export)
extras.append(f'extname={self.extname!r}')
extras.append(f'export={self.export!r}')
if self.mandatory:
extras.append('mandatory=True')
if not self.settable:
extras.append('settable=False')
if self.value is not UNSET:
extras.append('value=%s' % repr(self.value))
extras.append(f'value={repr(self.value)}')
if not self.name:
extras.append('name=%r' % self.name)
return 'Property(%r, %s, %s)' % (self.description, self.datatype, ', '.join(extras))
extras.append(f'name={self.name!r}')
return f"Property({self.description!r}, {self.datatype}, {', '.join(extras)})"
class HasProperties(HasDescriptors):
@ -147,10 +147,8 @@ class HasProperties(HasDescriptors):
po.value = po.datatype.validate(value)
except BadValueError:
if callable(value):
raise ProgrammingError('method %s.%s collides with property of %s' %
(cls.__name__, pn, base.__name__)) from None
raise ProgrammingError('can not set property %s.%s to %r' %
(cls.__name__, pn, value)) from None
raise ProgrammingError(f'method {cls.__name__}.{pn} collides with property of {base.__name__}') from None
raise ProgrammingError(f'can not set property {cls.__name__}.{pn} to {value!r}') from None
cls.propertyDict[pn] = po
def checkProperties(self):
@ -160,14 +158,14 @@ class HasProperties(HasDescriptors):
try:
self.propertyValues[pn] = po.datatype.validate(self.propertyValues[pn])
except (KeyError, BadValueError):
raise ConfigError('%s needs a value of type %r!' % (pn, po.datatype)) from None
raise ConfigError(f'{pn} needs a value of type {po.datatype!r}!') from None
for pn, po in self.propertyDict.items():
if pn.startswith('min'):
maxname = 'max' + pn[3:]
minval = self.propertyValues.get(pn, po.default)
maxval = self.propertyValues.get(maxname, minval)
if minval > maxval:
raise ConfigError('%s=%r must be <= %s=%r for %r' % (pn, minval, maxname, maxval, self))
raise ConfigError(f'{pn}={minval!r} must be <= {maxname}={maxval!r} for {self!r}')
def getProperties(self):
return self.propertyDict

View File

@ -53,10 +53,10 @@ from frappy.protocol.messages import COMMANDREPLY, DESCRIPTIONREPLY, \
def make_update(modulename, pobj):
if pobj.readerror:
return (ERRORPREFIX + EVENTREPLY, '%s:%s' % (modulename, pobj.export),
return (ERRORPREFIX + EVENTREPLY, f'{modulename}:{pobj.export}',
# error-report !
[pobj.readerror.name, str(pobj.readerror), {'t': pobj.timestamp}])
return (EVENTREPLY, '%s:%s' % (modulename, pobj.export),
return (EVENTREPLY, f'{modulename}:{pobj.export}',
[pobj.export_value(), {'t': pobj.timestamp}])
@ -114,7 +114,7 @@ class Dispatcher:
if ':' not in eventname:
# also remove 'more specific' subscriptions
for k, v in self._subscriptions.items():
if k.startswith('%s:' % eventname):
if k.startswith(f'{eventname}:'):
v.discard(conn)
if eventname in self._subscriptions:
self._subscriptions[eventname].discard(conn)
@ -151,7 +151,7 @@ class Dispatcher:
return self._modules[modulename]
if modulename in list(self._modules.values()):
return modulename
raise NoSuchModuleError('Module %r does not exist on this SEC-Node!' % modulename)
raise NoSuchModuleError(f'Module {modulename!r} does not exist on this SEC-Node!')
def remove_module(self, modulename_or_obj):
moduleobj = self.get_module(modulename_or_obj)
@ -160,7 +160,7 @@ class Dispatcher:
self._export.remove(modulename)
self._modules.pop(modulename)
self._subscriptions.pop(modulename, None)
for k in [kk for kk in self._subscriptions if kk.startswith('%s:' % modulename)]:
for k in [kk for kk in self._subscriptions if kk.startswith(f'{modulename}:')]:
self._subscriptions.pop(k, None)
def list_module_names(self):
@ -201,25 +201,25 @@ class Dispatcher:
# command is also accepted
result = result['accessibles'][pname]
elif pname:
raise NoSuchParameterError('Module %r has no parameter %r' % (modname, pname))
raise NoSuchParameterError(f'Module {modname!r} has no parameter {pname!r}')
elif not modname or modname == '.':
result['equipment_id'] = self.equipment_id
result['firmware'] = 'FRAPPY - The Python Framework for SECoP'
result['version'] = '2021.02'
result.update(self.nodeprops)
else:
raise NoSuchModuleError('Module %r does not exist' % modname)
raise NoSuchModuleError(f'Module {modname!r} does not exist')
return result
def _execute_command(self, modulename, exportedname, argument=None):
moduleobj = self.get_module(modulename)
if moduleobj is None:
raise NoSuchModuleError('Module %r does not exist' % modulename)
raise NoSuchModuleError(f'Module {modulename!r} does not exist')
cname = moduleobj.accessiblename2attr.get(exportedname)
cobj = moduleobj.commands.get(cname)
if cobj is None:
raise NoSuchCommandError('Module %r has no command %r' % (modulename, cname or exportedname))
raise NoSuchCommandError(f'Module {modulename!r} has no command {cname or exportedname!r}')
if cobj.argument:
argument = cobj.argument.import_value(argument)
@ -233,18 +233,16 @@ class Dispatcher:
def _setParameterValue(self, modulename, exportedname, value):
moduleobj = self.get_module(modulename)
if moduleobj is None:
raise NoSuchModuleError('Module %r does not exist' % modulename)
raise NoSuchModuleError(f'Module {modulename!r} does not exist')
pname = moduleobj.accessiblename2attr.get(exportedname)
pobj = moduleobj.parameters.get(pname)
if pobj is None:
raise NoSuchParameterError('Module %r has no parameter %r' % (modulename, pname or exportedname))
raise NoSuchParameterError(f'Module {modulename!r} has no parameter {pname or exportedname!r}')
if pobj.constant is not None:
raise ReadOnlyError("Parameter %s:%s is constant and can not be changed remotely"
% (modulename, pname))
raise ReadOnlyError(f"Parameter {modulename}:{pname} is constant and can not be changed remotely")
if pobj.readonly:
raise ReadOnlyError("Parameter %s:%s can not be changed remotely"
% (modulename, pname))
raise ReadOnlyError(f"Parameter {modulename}:{pname} can not be changed remotely")
# validate!
value = pobj.datatype.validate(value, previous=pobj.value)
@ -256,12 +254,12 @@ class Dispatcher:
def _getParameterValue(self, modulename, exportedname):
moduleobj = self.get_module(modulename)
if moduleobj is None:
raise NoSuchModuleError('Module %r does not exist' % modulename)
raise NoSuchModuleError(f'Module {modulename!r} does not exist')
pname = moduleobj.accessiblename2attr.get(exportedname)
pobj = moduleobj.parameters.get(pname)
if pobj is None:
raise NoSuchParameterError('Module %r has no parameter %r' % (modulename, pname or exportedname))
raise NoSuchParameterError(f'Module {modulename!r} has no parameter {pname or exportedname!r}')
if pobj.constant is not None:
# really needed? we could just construct a readreply instead....
# raise ReadOnlyError('This parameter is constant and can not be accessed remotely.')
@ -293,11 +291,11 @@ class Dispatcher:
action, specifier, data = '_ident', None, None
self.log.debug('Looking for handle_%s', action)
handler = getattr(self, 'handle_%s' % action, None)
handler = getattr(self, f'handle_{action}', None)
if handler:
return handler(conn, specifier, data)
raise ProtocolError('unhandled message: %s' % repr(msg))
raise ProtocolError(f'unhandled message: {repr(msg)}')
# now the (defined) handlers for the different requests
def handle_help(self, conn, specifier, data):
@ -351,13 +349,13 @@ class Dispatcher:
if ':' in specifier:
modulename, exportedname = specifier.split(':', 1)
if modulename not in self._export:
raise NoSuchModuleError('Module %r does not exist' % modulename)
raise NoSuchModuleError(f'Module {modulename!r} does not exist')
moduleobj = self.get_module(modulename)
if exportedname is not None:
pname = moduleobj.accessiblename2attr.get(exportedname, True)
if pname and pname not in moduleobj.accessibles:
# what if we try to subscribe a command here ???
raise NoSuchParameterError('Module %r has no parameter %r' % (modulename, pname))
raise NoSuchParameterError(f'Module {modulename!r} has no parameter {pname!r}')
modules = [(modulename, pname)]
else:
modules = [(modulename, None)]
@ -392,7 +390,7 @@ class Dispatcher:
def send_log_msg(self, conn, modname, level, msg):
"""send log message """
conn.send_reply((LOG_EVENT, '%s:%s' % (modname, level), msg))
conn.send_reply((LOG_EVENT, f'{modname}:{level}', msg))
def set_all_log_levels(self, conn, level):
for modobj in self._modules.values():

View File

@ -88,17 +88,14 @@ REQUEST2REPLY = {
}
HelpMessage = """Try one of the following:
'%s' to query protocol version
'%s' to read the description
'%s <module>[:<parameter>]' to request reading a value
'%s <module>[:<parameter>] value' to request changing a value
'%s <module>[:<command>]' to execute a command
'%s <nonce>' to request a heartbeat response
'%s' to activate async updates
'%s' to deactivate updates
'%s [<module>] <loglevel>' to activate logging events
""" % (IDENTREQUEST, DESCRIPTIONREQUEST, READREQUEST,
WRITEREQUEST, COMMANDREQUEST, HEARTBEATREQUEST,
ENABLEEVENTSREQUEST, DISABLEEVENTSREQUEST,
LOGGING_REQUEST)
HelpMessage = f"""Try one of the following:
'{IDENTREQUEST}' to query protocol version
'{DESCRIPTIONREQUEST}' to read the description
'{READREQUEST} <module>[:<parameter>]' to request reading a value
'{WRITEREQUEST} <module>[:<parameter>] value' to request changing a value
'{COMMANDREQUEST} <module>[:<command>]' to execute a command
'{HEARTBEATREQUEST} <nonce>' to request a heartbeat response
'{ENABLEEVENTSREQUEST}' to activate async updates
'{DISABLEEVENTSREQUEST}' to deactivate updates
'{LOGGING_REQUEST} [<module>] <loglevel>' to activate logging events
"""

View File

@ -55,11 +55,11 @@ class SecopClient(frappy.client.SecopClient):
return name
def updateEvent(self, module, parameter, value, timestamp, readerror):
specifier = '%s:%s' % (module, parameter)
specifier = f'{module}:{parameter}'
if readerror:
msg = ERRORPREFIX + EVENTREPLY, specifier, (readerror.name, str(readerror), dict(t=timestamp))
msg = ERRORPREFIX + EVENTREPLY, specifier, (readerror.name, str(readerror), {'t': timestamp})
else:
msg = EVENTREPLY, specifier, (value, dict(t=timestamp))
msg = EVENTREPLY, specifier, (value, {'t': timestamp})
self.dispatcher.broadcast_event(msg)
def nodeStateChange(self, online, state):
@ -74,7 +74,7 @@ class SecopClient(frappy.client.SecopClient):
if module is None:
self.dispatcher.restart()
self._shutdown = True
raise frappy.errors.SECoPError('descriptive data for node %r has changed' % self.nodename)
raise frappy.errors.SECoPError(f'descriptive data for node {self.nodename!r} has changed')
class Router(frappy.protocol.dispatcher.Dispatcher):
@ -139,7 +139,7 @@ class Router(frappy.protocol.dispatcher.Dispatcher):
data = node.descriptive_data.copy()
modules = data.pop('modules')
equipment_id = data.pop('equipment_id', 'unknown')
node_description.append('--- %s ---\n%s' % (equipment_id, data.pop('description', '')))
node_description.append(f"--- {equipment_id} ---\n{data.pop('description', '')}")
node_description.append('\n'.join('%s: %r' % kv for kv in data.items()))
for modname, moddesc in modules.items():
if modname in allmodules:
@ -154,12 +154,12 @@ class Router(frappy.protocol.dispatcher.Dispatcher):
super().handle_activate(conn, specifier, data)
for node in self.nodes:
for (module, parameter), (value, t, readerror) in node.cache.items():
spec = '%s:%s' % (module, parameter)
spec = f'{module}:{parameter}'
if readerror:
reply = ERRORPREFIX + EVENTREPLY, spec, (readerror.name, str(readerror), dict(t=t))
reply = ERRORPREFIX + EVENTREPLY, spec, (readerror.name, str(readerror), {'t': t})
else:
datatype = node.modules[module]['parameters'][parameter]['datatype']
reply = EVENTREPLY, spec, [datatype.export_value(value), dict(t=t)]
reply = EVENTREPLY, spec, [datatype.export_value(value), {'t': t}]
self.broadcast_event(reply)
return ENABLEEVENTSREPLY, None, None
@ -175,7 +175,7 @@ class Router(frappy.protocol.dispatcher.Dispatcher):
node = self.node_by_module[module]
if node.online:
return node.request(READREQUEST, specifier, data)
return ERRORPREFIX + READREQUEST, specifier, SecopClient.disconnectedError + (dict(t=node.disconnect_time),)
return ERRORPREFIX + READREQUEST, specifier, SecopClient.disconnectedError + ({'t': node.disconnect_time},)
def handle_change(self, conn, specifier, data):
module = specifier.split(':')[0]

View File

@ -40,7 +40,7 @@ class ProxyModule(HasIO, Module):
enablePoll = False
def ioClass(self, name, logger, opts, srv):
opts['description'] = 'secnode %s on %s' % (opts.get('module', name), opts['uri'])
opts['description'] = f"secnode {opts.get('module', name)} on {opts['uri']}"
return SecNode(name, logger, opts, srv)
def updateEvent(self, module, parameter, value, timestamp, readerror):
@ -179,7 +179,7 @@ def proxy_class(remote_class, name=None):
proxycls.accessibles = {}
break
else:
raise ConfigError('%r is no SECoP module class' % remote_class)
raise ConfigError(f'{remote_class!r} is no SECoP module class')
attrs = rcls.propertyDict.copy()
@ -220,7 +220,7 @@ def proxy_class(remote_class, name=None):
attrs[aname] = cobj(cfunc)
else:
raise ConfigError('do not now about %r in %s.accessibles' % (aobj, remote_class))
raise ConfigError(f'do not now about {aobj!r} in {remote_class}.accessibles')
return type(name+"_", (proxycls,), attrs)
@ -235,8 +235,6 @@ def Proxy(name, logger, cfgdict, srv):
remote_class = remote_class['value']
if 'description' not in cfgdict:
cfgdict['description'] = 'remote module %s on %s' % (
cfgdict.get('module', name),
cfgdict.get('io', {'value:': '?'})['value'])
cfgdict['description'] = f"remote module {cfgdict.get('module', name)} on {cfgdict.get('io', {'value:': '?'})['value']}"
return proxy_class(remote_class)(name, logger, cfgdict, srv)

View File

@ -87,7 +87,7 @@ class Handler:
if (func.__module__, func.__qualname__) in self.method_names:
# make sure method name is not used twice
# (else __set_name__ will not be called)
raise ProgrammingError('duplicate method %r' % func.__qualname__)
raise ProgrammingError(f'duplicate method {func.__qualname__!r}')
self.func = func
func.wrapped = False
self.method_names.add((func.__module__, func.__qualname__))
@ -113,8 +113,8 @@ class Handler:
wrapped.poll = getattr(wrapped, 'poll', self.poll)
func = getattr(owner, method_name, None)
if func and method_name in owner.__dict__:
raise ProgrammingError('superfluous method %s.%s (overwritten by %s)'
% (owner.__name__, method_name, self.__class__.__name__))
raise ProgrammingError(f'superfluous method {owner.__name__}.' \
f'{method_name} (overwritten by {self.__class__.__name__})')
setattr(owner, method_name, wrapped)
def wrap(self, key):

View File

@ -123,8 +123,7 @@ class Server:
self.run()
def unknown_options(self, cls, options):
return ("%s class don't know how to handle option(s): %s" %
(cls.__name__, ', '.join(options)))
return f"{cls.__name__} class don't know how to handle option(s): {', '.join(options)}"
def run(self):
while self._restart:
@ -189,24 +188,24 @@ class Server:
cls = get_class(classname)
except Exception as e:
if str(e) == 'no such class':
errors.append('%s not found' % classname)
errors.append(f'{classname} not found')
else:
failed.add(pymodule)
if failure_traceback is None:
failure_traceback = traceback.format_exc()
errors.append('error importing %s' % classname)
errors.append(f'error importing {classname}')
else:
try:
modobj = cls(modname, self.log.getChild(modname), opts, self)
self.modules[modname] = modobj
except ConfigError as e:
errors.append('error creating module %s:' % modname)
errors.append(f'error creating module {modname}:')
for errtxt in e.args[0] if isinstance(e.args[0], list) else [e.args[0]]:
errors.append(' ' + errtxt)
except Exception:
if failure_traceback is None:
failure_traceback = traceback.format_exc()
errors.append('error creating %s' % modname)
errors.append(f'error creating {modname}')
missing_super = set()
# all objs created, now start them up and interconnect
@ -216,8 +215,7 @@ class Server:
# also call earlyInit on the modules
modobj.earlyInit()
if not modobj.earlyInitDone:
missing_super.add('%s was not called, probably missing super call'
% modobj.earlyInit.__qualname__)
missing_super.add(f'{modobj.earlyInit.__qualname__} was not called, probably missing super call')
# handle attached modules
for modname, modobj in self.modules.items():
@ -231,10 +229,10 @@ class Server:
if isinstance(attobj, propobj.basecls):
attached_modules[propname] = attobj
else:
errors.append('attached module %s=%r must inherit from %r'
% (propname, attname, propobj.basecls.__qualname__))
errors.append(f'attached module {propname}={attname!r} '\
f'must inherit from {propobj.basecls.__qualname__!r}')
except SECoPError as e:
errors.append('module %s, attached %s: %s' % (modname, propname, str(e)))
errors.append(f'module {modname}, attached {propname}: {str(e)}')
modobj.attachedModules = attached_modules
# call init on each module after registering all
@ -242,22 +240,20 @@ class Server:
try:
modobj.initModule()
if not modobj.initModuleDone:
missing_super.add('%s was not called, probably missing super call'
% modobj.initModule.__qualname__)
missing_super.add(f'{modobj.initModule.__qualname__} was not called, probably missing super call')
except Exception as e:
if failure_traceback is None:
failure_traceback = traceback.format_exc()
errors.append('error initializing %s: %r' % (modname, e))
errors.append(f'error initializing {modname}: {e!r}')
if not self._testonly:
start_events = MultiEvent(default_timeout=30)
for modname, modobj in self.modules.items():
# startModule must return either a timeout value or None (default 30 sec)
start_events.name = 'module %s' % modname
start_events.name = f'module {modname}'
modobj.startModule(start_events)
if not modobj.startModuleDone:
missing_super.add('%s was not called, probably missing super call'
% modobj.startModule.__qualname__)
missing_super.add(f'{modobj.startModule.__qualname__} was not called, probably missing super call')
errors.extend(missing_super)
if errors:

View File

@ -37,7 +37,7 @@ class SimBase:
if extra_params:
for k in extra_params['value'].split(','):
k = k.strip()
attrs[k] = Parameter('extra_param: %s' % k.strip(),
attrs[k] = Parameter(f'extra_param: {k.strip()}',
datatype=FloatRange(),
default=0.0)
@ -54,7 +54,7 @@ class SimBase:
attrs['write_' + k] = writer
return object.__new__(type('SimBase_%s' % devname, (cls,), attrs))
return object.__new__(type(f'SimBase_{devname}', (cls,), attrs))
def initModule(self):
super().initModule()

View File

@ -85,11 +85,11 @@ class HasStates:
if sm.next_task:
if isinstance(sm.next_task, Stop):
if newstate and status is not None:
status = status[0], 'stopping (%s)' % status[1]
status = status[0], f'stopping ({status[1]})'
elif newstate:
# restart case
if status is not None:
status = sm.status[0], 'restarting (%s)' % status[1]
status = sm.status[0], f'restarting ({status[1]})'
else:
# start case
status = self.get_status(sm.next_task.newstate, BUSY)

View File

@ -66,7 +66,7 @@ class Ls370Sim(Communicator):
self._data['RDGST?%d' % channel] = '0'
def communicate(self, command):
self.comLog('> %s' % command)
self.comLog(f'> {command}')
chunks = command.split(';')
reply = []
@ -86,7 +86,7 @@ class Ls370Sim(Communicator):
self.data[qcmd] = arg
break
reply = ';'.join(reply)
self.comLog('< %s' % reply)
self.comLog(f'< {reply}')
return reply

View File

@ -74,7 +74,7 @@ class Switch(Drivable):
def write_target(self, value):
# could tell HW
self.status = (self.Status.BUSY, 'switching %s' % value.name.upper())
self.status = (self.Status.BUSY, f'switching {value.name.upper()}')
# note: setting self.target to the new value is done after this....
def read_status(self):
@ -284,8 +284,7 @@ class Label(Readable):
dev_ts = self.DISPATCHER.get_module(self.subdev_ts)
if dev_ts:
strings.append('at %.3f %s' %
(dev_ts.read_value(), dev_ts.parameters['value'].datatype.unit))
strings.append(f"at {dev_ts.read_value():.3f} {dev_ts.parameters['value'].datatype.unit}")
else:
strings.append('No connection to sample temp!')
@ -299,7 +298,7 @@ class Label(Readable):
state = 'Persistent' if mf_mode else 'Non-persistent'
else:
state = mf_stat[1] or 'ramping'
strings.append('%s at %.1f %s' % (state, mf_val, mf_unit))
strings.append(f'{state} at {mf_val:.1f} {mf_unit}')
else:
strings.append('No connection to magnetic field!')

View File

@ -117,8 +117,7 @@ class GarfieldMagnet(SequencerMixin, Drivable):
minfield = -maxfield
if not minfield <= field <= maxfield:
raise ValueError(self,
'requested field %g T out of range %g..%g T' %
(field, minfield, maxfield))
f'requested field {field:g} T out of range {minfield:g}..{maxfield:g} T')
while minfield <= field <= maxfield:
# binary search
trycurr = 0.5 * (mincurr + maxcurr)
@ -171,8 +170,7 @@ class GarfieldMagnet(SequencerMixin, Drivable):
amin, amax = self.abslimits
if umin > umax:
raise ValueError(
self, 'user minimum (%s) above the user '
'maximum (%s)' % (umin, umax))
self, f'user minimum ({umin}) above the user maximum ({umax})')
if umin < amin - abs(amin * 1e-12):
umin = amin
if umax > amax + abs(amax * 1e-12):
@ -265,16 +263,13 @@ class GarfieldMagnet(SequencerMixin, Drivable):
cleanup=self._ramp_current_cleanup))
seq.append(
Step(
'set polarity %s' %
wanted_polarity,
f'set polarity {wanted_polarity}',
0.3,
self._set_polarity,
wanted_polarity)) # no cleanup
seq.append(
Step(
'ramping to %.3fT (%.2fA)' %
(target,
wanted_current),
f'ramping to {target:.3f}T ({wanted_current:.2f}A)',
0.3,
self._ramp_current,
wanted_current,

View File

@ -123,13 +123,11 @@ def describe_dev_error(exc):
m = re.search(r'Command (\w+) not allowed when the '
r'device is in (\w+) state', fulldesc)
if m:
fulldesc = 'executing %r not allowed in state %s' \
% (m.group(1), m.group(2))
fulldesc = f'executing {m.group(1)!r} not allowed in state {m.group(2)}'
elif reason == 'API_DeviceNotExported':
m = re.search(r'Device ([\w/]+) is not', fulldesc)
if m:
fulldesc = 'Tango device %s is not exported, is the server ' \
'running?' % m.group(1)
fulldesc = f'Tango device {m.group(1)} is not exported, is the server running?'
elif reason == 'API_CorbaException':
if 'TRANSIENT_CallTimedout' in fulldesc:
fulldesc = 'Tango client-server call timed out'
@ -139,15 +137,14 @@ def describe_dev_error(exc):
elif reason == 'API_CantConnectToDevice':
m = re.search(r'connect to device ([\w/]+)', fulldesc)
if m:
fulldesc = 'connection to Tango device %s failed, is the server ' \
'running?' % m.group(1)
fulldesc = f'connection to Tango device {m.group(1)} failed, is the server running?'
elif reason == 'API_CommandTimedOut':
if 'acquire serialization' in fulldesc:
fulldesc = 'Tango call timed out waiting for lock on server'
# append origin if wanted
if origin:
fulldesc += ' in %s' % origin
fulldesc += f' in {origin}'
return fulldesc
@ -568,8 +565,7 @@ class AnalogOutput(PyTangoDevice, Drivable):
amin, amax = self.abslimits
if umin > umax:
raise ValueError(
self, 'user minimum (%s) above the user '
'maximum (%s)' % (umin, umax))
self, f'user minimum ({umin}) above the user maximum ({umax})')
if umin < amin - abs(amin * 1e-12):
umin = amin
if umax > amax + abs(amax * 1e-12):
@ -831,7 +827,7 @@ class NamedDigitalInput(DigitalInput):
mapping = eval(mapping)
self.accessibles['value'].setProperty('datatype', EnumType('value', **mapping))
except Exception as e:
raise ValueError('Illegal Value for mapping: %r' % self.mapping) from e
raise ValueError(f'Illegal Value for mapping: {self.mapping!r}') from e
def read_value(self):
value = self._dev.value
@ -909,7 +905,7 @@ class NamedDigitalOutput(DigitalOutput):
self.accessibles['value'].setProperty('datatype', EnumType('value', **mapping))
self.accessibles['target'].setProperty('datatype', EnumType('target', **mapping))
except Exception as e:
raise ValueError('Illegal Value for mapping: %r' % self.mapping) from e
raise ValueError(f'Illegal Value for mapping: {self.mapping!r}') from e
def write_target(self, value):
# map from enum-str to integer value

View File

@ -84,9 +84,9 @@ class Capacitance(HasIO, Readable):
return self.voltage
def write_freq(self, value):
self.value = self.parse_reply(self.communicate('FR %g;SI' % value))
self.value = self.parse_reply(self.communicate(f'FR {value:g};SI'))
return self.freq
def write_voltage(self, value):
self.value = self.parse_reply(self.communicate('V %g;SI' % value))
self.value = self.parse_reply(self.communicate(f'V {value:g};SI'))
return self.voltage

View File

@ -84,13 +84,13 @@ class HeLevel(HasIO, Readable):
return self.query('hem')
def write_empty_length(self, value):
return self.query('hem=%g' % value)
return self.query(f'hem={value:g}')
def read_full_length(self):
return self.query('hfu')
def write_full_length(self, value):
return self.query('hfu=%g' % value)
return self.query(f'hfu={value:g}')
def read_sample_rate(self):
return self.query('hf')

View File

@ -145,7 +145,7 @@ class ChannelSwitcher(Drivable):
def write_target(self, channel):
if channel not in self._channels:
raise ValueError('%r is no valid channel' % channel)
raise ValueError(f'{channel!r} is no valid channel')
if channel == self.target and self._channels[channel].enabled:
return channel
chan = self._channels[channel]

View File

@ -31,11 +31,12 @@ def make_cvt_list(dt, tail=''):
tail is a postfix to be appended in case of tuples and structs
"""
if isinstance(dt, (EnumType, IntRange, BoolType)):
return[(int, tail, dict(type='NUM'))]
return[(int, tail, {type: 'NUM'})]
if isinstance(dt, (FloatRange, ScaledInteger)):
return [(dt.import_value, tail, dict(type='NUM', unit=dt.unit, period=5) if dt.unit else {})]
return [(dt.import_value, tail,
{'type': 'NUM', 'unit': dt.unit, 'period': 5} if dt.unit else {})]
if isinstance(dt, StringType):
return [(lambda x: x, tail, dict(type='STR'))]
return [(lambda x: x, tail, {'type': 'STR'})]
if isinstance(dt, TupleOf):
items = enumerate(dt.members)
elif isinstance(dt, StructOf):
@ -44,7 +45,7 @@ def make_cvt_list(dt, tail=''):
return [] # ArrayType, BlobType and TextType are ignored: too much data, probably not used
result = []
for subkey, elmtype in items:
for fun, tail_, opts in make_cvt_list(elmtype, '%s.%s' % (tail, subkey)):
for fun, tail_, opts in make_cvt_list(elmtype, f'{tail}.{subkey}'):
result.append((lambda v, k=subkey, f=fun: f(v[k]), tail_, opts))
return result
@ -122,7 +123,7 @@ class FrappyHistoryWriter(frappyhistory.FrappyWriter):
def send_reply(self, msg):
action, ident, value = msg
if not action.endswith('update'):
print('unknown async message %r' % msg)
print(f'unknown async message {msg!r}')
return
now = self._init_time or time.time() # on initialisation, use the same timestamp for all
if action == 'update':

View File

@ -78,7 +78,7 @@ class SourceMeter(HasIO, Module):
def write_ilimit(self, value):
if self.mode == 'current':
return self.ilimit
return float(self.communicate('smua.source.limiti = %g print(smua.source.limiti)' % value))
return float(self.communicate(f'smua.source.limiti = {value:g} print(smua.source.limiti)'))
def read_vlimit(self):
if self.mode == 'voltage':
@ -88,7 +88,7 @@ class SourceMeter(HasIO, Module):
def write_vlimit(self, value):
if self.mode == 'voltage':
return self.ilimit
return float(self.communicate('smua.source.limitv = %g print(smua.source.limitv)' % value))
return float(self.communicate(f'smua.source.limitv = {value:g} print(smua.source.limitv)'))
class Power(HasIO, Readable):
@ -130,7 +130,7 @@ class Current(HasIO, Writable):
raise ValueError('current exceeds limit')
if not self.active:
self.sourcemeter.write_mode('current') # triggers update_mode -> set active to True
value = float(self.communicate('smua.source.leveli = %g print(smua.source.leveli)' % value))
value = float(self.communicate(f'smua.source.leveli = {value:g} print(smua.source.leveli)'))
return value
def read_limit(self):
@ -176,7 +176,7 @@ class Voltage(HasIO, Writable):
raise ValueError('voltage exceeds limit')
if not self.active:
self.sourcemeter.write_mode('voltage') # triggers update_mode -> set active to True
value = float(self.communicate('smua.source.levelv = %g print(smua.source.levelv)' % value))
value = float(self.communicate(f'smua.source.levelv = {value:g} print(smua.source.levelv)'))
return value
def read_limit(self):

View File

@ -40,7 +40,7 @@ SELF = 0
def as_float(value):
if isinstance(value, str):
return float(VALUE_UNIT.match(value).group(1))
return '%g' % value
return f'{value:g}'
def as_string(value):
@ -78,9 +78,9 @@ class MercuryChannel(HasIO):
head, sep, tail = adr.partition(':')
for i, (channel_type, slot) in enumerate(zip(self.channel_type.split(','), self.slot.split(','))):
if head == str(i):
return 'DEV:%s:%s%s%s' % (slot, channel_type, sep, tail)
return f'DEV:{slot}:{channel_type}{sep}{tail}'
if head == channel_type:
return 'DEV:%s:%s%s%s' % (slot, head, sep, tail)
return f'DEV:{slot}:{head}{sep}{tail}'
return adr
def multiquery(self, adr, names=(), convert=as_float):
@ -101,9 +101,9 @@ class MercuryChannel(HasIO):
-> query command will be READ:DEV:DB3.G1:PRES:SIG:PERC
"""
adr = self._complete_adr(adr)
cmd = 'READ:%s:%s' % (adr, ':'.join(names))
cmd = f"READ:{adr}:{':'.join(names)}"
reply = self.communicate(cmd)
head = 'STAT:%s:' % adr
head = f'STAT:{adr}:'
try:
assert reply.startswith(head)
replyiter = iter(reply[len(head):].split(':'))
@ -111,7 +111,7 @@ class MercuryChannel(HasIO):
assert keys == tuple(names)
return tuple(convert(r) for r in result)
except (AssertionError, AttributeError, ValueError):
raise HardwareError('invalid reply %r to cmd %r' % (reply, cmd)) from None
raise HardwareError(f'invalid reply {reply!r} to cmd {cmd!r}') from None
def multichange(self, adr, values, convert=as_float):
"""set parameter(s) in mercury syntax
@ -129,10 +129,10 @@ class MercuryChannel(HasIO):
-> change command will be SET:DEV:DB6.T1:TEMP:LOOP:P:5:I:2:D:0
"""
adr = self._complete_adr(adr)
params = ['%s:%s' % (k, convert(v)) for k, v in values]
cmd = 'SET:%s:%s' % (adr, ':'.join(params))
params = [f'{k}:{convert(v)}' for k, v in values]
cmd = f"SET:{adr}:{':'.join(params)}"
reply = self.communicate(cmd)
head = 'STAT:SET:%s:' % adr
head = f'STAT:SET:{adr}:'
try:
assert reply.startswith(head)
@ -142,7 +142,7 @@ class MercuryChannel(HasIO):
assert any(v == 'VALID' for v in valid)
return tuple(convert(r) for r in result)
except (AssertionError, AttributeError, ValueError) as e:
raise HardwareError('invalid reply %r to cmd %r' % (reply, cmd)) from e
raise HardwareError(f'invalid reply {reply!r} to cmd {cmd!r}') from e
def query(self, adr, convert=as_float):
"""query a single parameter

View File

@ -72,7 +72,7 @@ class MotorValve(PersistentMixin, Drivable):
def write_target(self, target):
if self.status[0] == ERROR:
raise HardwareError('%s: need refrun' % self.status[1])
raise HardwareError(f'{self.status[1]}: need refrun')
self.target = target
self._state.start(self.goto_target, count=3)
return self.target

View File

@ -80,7 +80,7 @@ class Main(Communicator):
def communicate(self, command):
"""GPIB command"""
with self.lock:
self.comLog('> %s' % command)
self.comLog(f'> {command}')
reply = self._ppms_device.send(command)
self.comLog("< %s", reply)
return reply
@ -151,7 +151,7 @@ class PpmsBase(HasIO, Readable):
"""write command and check if result is OK"""
reply = self.communicate(command)
if reply != 'OK':
raise HardwareError('bad reply %r to command %r' % (reply, command))
raise HardwareError(f'bad reply {reply!r} to command {command!r}')
class PpmsDrivable(Drivable, PpmsBase):
@ -447,12 +447,12 @@ class Temp(PpmsDrivable):
if status[0] == StatusType.IDLE:
status = (status[0], 'stopped')
else:
status = (status[0], 'stopping (%s)' % status[1])
status = (status[0], f'stopping ({status[1]})')
if self._expected_target_time:
# handle timeout
if self.isDriving(status):
if now > self._expected_target_time + self.timeout:
status = (StatusType.WARN, 'timeout while %s' % status[1])
status = (StatusType.WARN, f'timeout while {status[1]}')
else:
self._expected_target_time = 0
self.status = status
@ -492,7 +492,7 @@ class Temp(PpmsDrivable):
if newtarget != self.target:
self.log.debug('stop at %s K', newtarget)
self.write_target(newtarget)
self.status = self.status[0], 'stopping (%s)' % self.status[1]
self.status = self.status[0], f'stopping ({self.status[1]})'
self._stopped = True
@ -577,7 +577,7 @@ class Field(PpmsDrivable):
if status[0] == StatusType.IDLE:
status = (status[0], 'stopped')
else:
status = (status[0], 'stopping (%s)' % status[1])
status = (status[0], f'stopping ({status[1]})')
self.status = status
def write_target(self, target):
@ -620,7 +620,7 @@ class Field(PpmsDrivable):
if newtarget != self.target:
self.log.debug('stop at %s T', newtarget)
self.write_target(newtarget)
self.status = (self.status[0], 'stopping (%s)' % self.status[1])
self.status = (self.status[0], f'stopping ({self.status[1]})')
self._stopped = True
@ -698,7 +698,7 @@ class Position(PpmsDrivable):
if status[0] == StatusType.IDLE:
status = (status[0], 'stopped')
else:
status = (status[0], 'stopping (%s)' % status[1])
status = (status[0], f'stopping ({status[1]})')
self.status = status
def write_target(self, target):
@ -722,5 +722,5 @@ class Position(PpmsDrivable):
if newtarget != self.target:
self.log.debug('stop at %s T', newtarget)
self.write_target(newtarget)
self.status = (self.status[0], 'stopping (%s)' % self.status[1])
self.status = (self.status[0], f'stopping ({self.status[1]})')
self._stopped = True

View File

@ -48,7 +48,7 @@ class NamedList:
return setattr(self, self.__keys__[index], value)
def __repr__(self):
return ",".join("%.7g" % val for val in self.aslist())
return ",".join(f"{val:.7g}" for val in self.aslist())
class PpmsSim:
@ -198,10 +198,10 @@ class PpmsSim:
def getdat(self, mask):
mask = int(mask) & 0x8000ff # all channels up to i2 plus ts
output = ['%d' % mask, '%.2f' % (time.time() - self.start)]
output = ['%d' % mask, f'{time.time() - self.start:.2f}']
for i, chan in self.CHANNELS.items():
if (1 << i) & mask:
output.append("%.7g" % getattr(self, chan))
output.append(f"{getattr(self, chan):.7g}")
return ",".join(output)
@ -219,12 +219,12 @@ class QDevice:
name, args = command.split('?')
name += args.strip()
result = getattr(self.sim, name.lower()).aslist()
result = ",".join("%.7g" % arg for arg in result)
result = ",".join(f"{arg:.7g}" for arg in result)
# print(command, '/', result)
else:
# print(command)
name, args = command.split()
args = json.loads("[%s]" % args)
args = json.loads(f"[{args}]")
if name.startswith('BRIDGE') or name.startswith('DRVOUT'):
name = name + str(int(args[0]))
getattr(self.sim, name.lower()).setvalues(args)

View File

@ -121,7 +121,7 @@ class CalCurve:
# then try adding all kinds as extension
for nam in calibname, calibname.upper(), calibname.lower():
for kind in KINDS:
filename = join(path.strip(), '%s.%s' % (nam, kind))
filename = join(path.strip(), f'{nam}.{kind}')
if exists(filename):
break
else:
@ -148,7 +148,7 @@ class CalCurve:
for line in f:
parser.parse(line)
except Exception as e:
raise ValueError('calib curve %s: %s' % (calibspec, e)) from e
raise ValueError(f'calib curve {calibspec}: {e}') from e
self.convert_x = nplog if parser.logx else linear
self.convert_y = npexp if parser.logy else linear
x = np.asarray(parser.xdata)
@ -157,11 +157,11 @@ class CalCurve:
x = np.flip(x)
y = np.flip(y)
elif np.any(x[:-1] >= x[1:]): # some not increasing
raise ValueError('calib curve %s is not monotonic' % calibspec)
raise ValueError(f'calib curve {calibspec} is not monotonic')
try:
self.spline = splrep(x, y, s=0, k=min(3, len(x) - 1))
except (ValueError, TypeError) as e:
raise ValueError('invalid calib curve %s' % calibspec) from e
raise ValueError(f'invalid calib curve {calibspec}') from e
def __call__(self, value):
"""convert value
@ -194,7 +194,7 @@ class Sensor(Readable):
self.rawsensor.registerCallbacks(self, ['status']) # auto update status
self._calib = CalCurve(self.calib)
if self.description == '_':
self.description = '%r calibrated with curve %r' % (self.rawsensor, self.calib)
self.description = f'{self.rawsensor!r} calibrated with curve {self.calib!r}'
def doPoll(self):
self.read_status()

View File

@ -334,9 +334,9 @@ class Motor(PersistentMixin, HasIO, Drivable):
return IDLE, ''
if self.auto_reset:
self._need_reset = True
return IDLE, 'stalled: %s' % reason
return IDLE, f'stalled: {reason}'
self.log.error('out of tolerance by %.3g (%s)', diff, reason)
return ERROR, 'out of tolerance (%s)' % reason
return ERROR, f'out of tolerance ({reason})'
def write_target(self, target):
for _ in range(2): # for auto reset
@ -345,8 +345,7 @@ class Motor(PersistentMixin, HasIO, Drivable):
abs(target - self.encoder) > self.move_limit + self.tolerance):
# pylint: disable=bad-string-format-type
# pylint wrongly does not recognise encoder as a descriptor
raise RangeError('can not move more than %g deg (%g -> %g)' %
(self.move_limit, self.encoder, target))
raise RangeError(f'can not move more than {self.move_limit:g} deg ({self.encoder:g} -> {target:g})')
diff = self.encoder - self.steppos
if self._need_reset:
if self.auto_reset:
@ -359,7 +358,7 @@ class Motor(PersistentMixin, HasIO, Drivable):
if self.status[0] == IDLE:
continue
raise HardwareError('auto reset failed')
raise HardwareError('need reset (%s)' % self.status[1])
raise HardwareError(f'need reset ({self.status[1]})')
break
if abs(diff) > self.tolerance:
if abs(diff) > self.encoder_tolerance and self.has_encoder: