From 6f95c0d825ecc44da5643b61f83bea46cbdb4434 Mon Sep 17 00:00:00 2001 From: Alexander Zaft Date: Fri, 14 Apr 2023 07:12:03 +0200 Subject: [PATCH] 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 Reviewed-by: Georg Brandl Reviewed-by: Alexander Zaft --- frappy/client/__init__.py | 17 +++-- frappy/client/interactive.py | 18 ++--- frappy/config.py | 7 +- frappy/datatypes.py | 87 ++++++++++------------- frappy/errors.py | 6 +- frappy/features.py | 8 +-- frappy/gui/cfg_editor/config_file.py | 19 +++-- frappy/gui/cfg_editor/mainwindow.py | 8 +-- frappy/gui/cfg_editor/tree_widget_item.py | 2 +- frappy/gui/cfg_editor/utils.py | 8 +-- frappy/gui/cfg_editor/widgets.py | 4 +- frappy/gui/connection.py | 4 +- frappy/gui/console.py | 10 ++- frappy/gui/logwindow.py | 9 ++- frappy/gui/mainwindow.py | 2 +- frappy/gui/modulewidget.py | 11 ++- frappy/gui/nodewidget.py | 6 +- frappy/gui/paramview.py | 2 +- frappy/gui/plotting.py | 8 +-- frappy/gui/valuewidgets.py | 2 +- frappy/io.py | 15 ++-- frappy/lib/__init__.py | 14 ++-- frappy/lib/asynconn.py | 10 +-- frappy/lib/classdoc.py | 28 ++++---- frappy/lib/enum.py | 6 +- frappy/lib/sequence.py | 6 +- frappy/lib/statemachine.py | 7 +- frappy/logging.py | 6 +- frappy/modules.py | 42 +++++------ frappy/params.py | 26 ++++--- frappy/parse.py | 4 +- frappy/persistent.py | 2 +- frappy/properties.py | 24 +++---- frappy/protocol/dispatcher.py | 42 ++++++----- frappy/protocol/messages.py | 25 +++---- frappy/protocol/router.py | 18 ++--- frappy/proxy.py | 10 ++- frappy/rwhandler.py | 6 +- frappy/server.py | 30 ++++---- frappy/simulation.py | 4 +- frappy/states.py | 4 +- frappy_demo/lscsim.py | 4 +- frappy_demo/modules.py | 7 +- frappy_mlz/amagnet.py | 13 ++-- frappy_mlz/entangle.py | 18 ++--- frappy_psi/ah2700.py | 4 +- frappy_psi/ccu4.py | 4 +- frappy_psi/channelswitcher.py | 2 +- frappy_psi/historywriter.py | 11 +-- frappy_psi/k2601b.py | 8 +-- frappy_psi/mercury.py | 20 +++--- frappy_psi/motorvalve.py | 2 +- frappy_psi/ppms.py | 18 ++--- frappy_psi/ppmssim.py | 10 +-- frappy_psi/softcal.py | 10 +-- frappy_psi/trinamic.py | 9 ++- 56 files changed, 327 insertions(+), 380 deletions(-) diff --git a/frappy/client/__init__.py b/frappy/client/__init__.py index 123817b..32df0b2 100644 --- a/frappy/client/__init__.py +++ b/frappy/client/__init__.py @@ -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: diff --git a/frappy/client/interactive.py b/frappy/client/interactive.py index e53d4ab..e400dff 100644 --- a/frappy/client/interactive.py +++ b/frappy/client/interactive.py @@ -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 . = 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): diff --git a/frappy/config.py b/frappy/config.py index fac5d8d..b8b645e 100644 --- a/frappy/config.py +++ b/frappy/config.py @@ -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) diff --git a/frappy/datatypes.py b/frappy/datatypes.py index f7deed7..c2ae725 100644 --- a/frappy/datatypes.py +++ b/frappy/datatypes.py @@ -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 diff --git a/frappy/errors.py b/frappy/errors.py index 04ff8b1..3b47068 100644 --- a/frappy/errors.py +++ b/frappy/errors.py @@ -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: diff --git a/frappy/features.py b/frappy/features.py index afbcacd..bcbe10a 100644 --- a/frappy/features.py +++ b/frappy/features.py @@ -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 --- diff --git a/frappy/gui/cfg_editor/config_file.py b/frappy/gui/cfg_editor/config_file.py index afd39c5..9be17a5 100644 --- a/frappy/gui/cfg_editor/config_file.py +++ b/frappy/gui/cfg_editor/config_file.py @@ -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)] diff --git a/frappy/gui/cfg_editor/mainwindow.py b/frappy/gui/cfg_editor/mainwindow.py index 412a977..0cc76ac 100644 --- a/frappy/gui/cfg_editor/mainwindow.py +++ b/frappy/gui/cfg_editor/mainwindow.py @@ -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?', ''' -

Do you want to save changes%s?

+ file_name = f' in "{file_name}"' + return QMessageBox.question(self, 'Save file?', f''' +

Do you want to save changes{file_name}?

Your changes will be lost if you don't save them!

- ''' % file_name, + ''', QMessageBox.StandardButton.Cancel | QMessageBox.StandardButton.Close | QMessageBox.StandardButton.Save, diff --git a/frappy/gui/cfg_editor/tree_widget_item.py b/frappy/gui/cfg_editor/tree_widget_item.py index 2cdee6c..9f88328 100644 --- a/frappy/gui/cfg_editor/tree_widget_item.py +++ b/frappy/gui/cfg_editor/tree_widget_item.py @@ -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() diff --git a/frappy/gui/cfg_editor/utils.py b/frappy/gui/cfg_editor/utils.py index 028f18d..dc19276 100644 --- a/frappy/gui/cfg_editor/utils.py +++ b/frappy/gui/cfg_editor/utils.py @@ -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) diff --git a/frappy/gui/cfg_editor/widgets.py b/frappy/gui/cfg_editor/widgets.py index e3e9544..b472628 100644 --- a/frappy/gui/cfg_editor/widgets.py +++ b/frappy/gui/cfg_editor/widgets.py @@ -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 diff --git a/frappy/gui/connection.py b/frappy/gui/connection.py index ec95c1f..35cfa3c 100644 --- a/frappy/gui/connection.py +++ b/frappy/gui/connection.py @@ -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() diff --git a/frappy/gui/console.py b/frappy/gui/console.py index 4fa9b9f..74a478b 100644 --- a/frappy/gui/console.py +++ b/frappy/gui/console.py @@ -153,24 +153,22 @@ class Console(QWidget): return self._addLogEntry( - 'Request: ' - '%s' % toHtmlEscaped(msg), + f'Request: {toHtmlEscaped(msg)}', 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() diff --git a/frappy/gui/logwindow.py b/frappy/gui/logwindow.py index 032f2c8..11b98fd 100644 --- a/frappy/gui/logwindow.py +++ b/frappy/gui/logwindow.py @@ -75,11 +75,10 @@ class LogWindow(QMainWindow): s = record.getMessage() time = record.created if record.levelno == self.levels['Error']: - s = '%s' %s - s='[%s] %s: %s' \ - % (self.timecolor.name(), time, \ - self.messagecolors[record.levelno].name(), \ - record.name, s) + s = f'{s}' + s = f'[{time}] ' \ + f'' \ + f'{record.name}: {s}' self.logBrowser.append(s) def on_logLevel_currentTextChanged(self, level): diff --git a/frappy/gui/mainwindow.py b/frappy/gui/mainwindow.py index a2830e9..650578f 100644 --- a/frappy/gui/mainwindow.py +++ b/frappy/gui/mainwindow.py @@ -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) diff --git a/frappy/gui/modulewidget.py b/frappy/gui/modulewidget.py index 750011e..25abe50 100644 --- a/frappy/gui/modulewidget.py +++ b/frappy/gui/modulewidget.py @@ -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('%s:' % prop.capitalize()) + name = QLabel(f'{prop.capitalize()}:') 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...') diff --git a/frappy/gui/nodewidget.py b/frappy/gui/nodewidget.py index f71f3a7..5cd404c 100644 --- a/frappy/gui/nodewidget.py +++ b/frappy/gui/nodewidget.py @@ -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: diff --git a/frappy/gui/paramview.py b/frappy/gui/paramview.py index f3be64d..24cc7a4 100644 --- a/frappy/gui/paramview.py +++ b/frappy/gui/paramview.py @@ -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) diff --git a/frappy/gui/plotting.py b/frappy/gui/plotting.py index 91da4a9..9ad6c9b 100644 --- a/frappy/gui/plotting.py +++ b/frappy/gui/plotting.py @@ -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] diff --git a/frappy/gui/valuewidgets.py b/frappy/gui/valuewidgets.py index 6103d43..19a742b 100644 --- a/frappy/gui/valuewidgets.py +++ b/frappy/gui/valuewidgets.py @@ -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) diff --git a/frappy/io.py b/frappy/io.py index 9060797..d64141f 100644 --- a/frappy/io.py +++ b/frappy/io.py @@ -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 diff --git a/frappy/lib/__init__.py b/frappy/lib/__init__.py index 7a005b3..6c34812 100644 --- a/frappy/lib/__init__.py +++ b/frappy/lib/__init__.py @@ -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 ('