diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e26e71fc..ead91c0e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,12 +20,12 @@ repos: - id: isort name: isort (python) - repo: https://github.com/PyCQA/flake8 - rev: 7.0.0 + rev: 7.1.0 hooks: - id: flake8 - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.4.6 + rev: v0.5.1 hooks: - id: ruff - repo: https://github.com/numpy/numpydoc diff --git a/pyproject.toml b/pyproject.toml index a5a98933..81f805d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -148,6 +148,7 @@ select = [ "N", # pep8-naming "W", # pycodestyle "D", # pydocstyle + "UP", # pyupgrade ] extend-select = [ "RUF100", # Warn about unused noqa diff --git a/python/lsst/pex/config/choiceField.py b/python/lsst/pex/config/choiceField.py index 55556b98..6ea2fd5a 100644 --- a/python/lsst/pex/config/choiceField.py +++ b/python/lsst/pex/config/choiceField.py @@ -89,10 +89,11 @@ def __init__(self, doc, dtype=None, allowed=None, default=None, optional=True, d for choice, choiceDoc in self.allowed.items(): if choice is not None and not isinstance(choice, dtype): raise ValueError( - "ChoiceField's allowed choice %s is of incorrect type %s. Expected %s" - % (choice, _typeStr(choice), _typeStr(dtype)) + f"ChoiceField's allowed choice {choice} is of incorrect type " + f"{_typeStr(choice)}. Expected {_typeStr(dtype)}" ) - self.__doc__ += "{}\n {}\n".format(f"``{str(choice)!r}``", choiceDoc) + # Force to a string so that additional quotes are added with !r + self.__doc__ += f"``{str(choice)!r}``\n {choiceDoc}\n" self.source = getStackFrame() diff --git a/python/lsst/pex/config/comparison.py b/python/lsst/pex/config/comparison.py index 7458a648..3ffba6c6 100644 --- a/python/lsst/pex/config/comparison.py +++ b/python/lsst/pex/config/comparison.py @@ -156,12 +156,12 @@ def compareConfigs(name, c1, c2, shortcut=True, rtol=1e-8, atol=1e-8, output=Non return True else: if output is not None: - output("LHS is None for %s" % name) + output(f"LHS is None for {name}") return False else: if c2 is None: if output is not None: - output("RHS is None for %s" % name) + output(f"RHS is None for {name}") return False if type(c1) is not type(c2): if output is not None: diff --git a/python/lsst/pex/config/config.py b/python/lsst/pex/config/config.py index a2800f3c..bd50740a 100644 --- a/python/lsst/pex/config/config.py +++ b/python/lsst/pex/config/config.py @@ -46,14 +46,9 @@ import tempfile import warnings from collections.abc import Mapping +from types import GenericAlias from typing import Any, ForwardRef, Generic, TypeVar, cast, overload -try: - from types import GenericAlias -except ImportError: - # cover python 3.8 usage - GenericAlias = type(Mapping[int, int]) - # if YAML is not available that's fine and we simply don't register # the yaml representer since we know it won't be used. try: @@ -130,7 +125,7 @@ def _autocast(x, dtype): ---------- x : object A value. - dtype : tpye + dtype : type Data type, such as `float`, `int`, or `str`. Returns @@ -140,7 +135,7 @@ def _autocast(x, dtype): ``dtype``. If the cast cannot be performed the original value of ``x`` is returned. """ - if dtype == float and isinstance(x, int): + if dtype is float and isinstance(x, int): return float(x) return x @@ -293,16 +288,9 @@ def __init__(self, field, config, msg): self.configSource = config._source error = ( - "%s '%s' failed validation: %s\n" - "For more information see the Field definition at:\n%s" - " and the Config definition at:\n%s" - % ( - self.fieldType.__name__, - self.fullname, - msg, - self.fieldSource.format(), - self.configSource.format(), - ) + f"{self.fieldType.__name__} '{self.fullname}' failed validation: {msg}\n" + f"For more information see the Field definition at:\n{self.fieldSource.format()}" + f" and the Config definition at:\n{self.configSource.format()}" ) super().__init__(error) @@ -456,13 +444,8 @@ def _parseTypingArgs( _typ = ForwardRef(unpackedParams) # type ignore below because typeshed seems to be wrong. It # indicates there are only 2 args, as it was in python 3.8, but - # 3.9+ takes 3 args. Attempt in old style and new style to - # work with both. - try: - result = _typ._evaluate(globals(), locals(), set()) # type: ignore - except TypeError: - # python 3.8 path - result = _typ._evaluate(globals(), locals()) + # 3.9+ takes 3 args. + result = _typ._evaluate(globals(), locals(), recursive_guard=set()) # type: ignore if result is None: raise ValueError("Could not deduce type from input") unpackedParams = cast(type, result) @@ -481,7 +464,7 @@ def __init__(self, doc, dtype=None, default=None, check=None, optional=False, de "dtype must either be supplied as an argument or as a type argument to the class" ) if dtype not in self.supportedTypes: - raise ValueError("Unsupported Field dtype %s" % _typeStr(dtype)) + raise ValueError(f"Unsupported Field dtype {_typeStr(dtype)}") source = getStackFrame() self._setup( @@ -626,14 +609,12 @@ def _validateValue(self, value): return if not isinstance(value, self.dtype): - msg = "Value {} is of incorrect type {}. Expected type {}".format( - value, - _typeStr(value), - _typeStr(self.dtype), + msg = ( + f"Value {value} is of incorrect type {_typeStr(value)}. Expected type {_typeStr(self.dtype)}" ) raise TypeError(msg) if self.check is not None and not self.check(value): - msg = "Value %s is not a valid value" % str(value) + msg = f"Value {value} is not a valid value" raise ValueError(msg) def _collectImports(self, instance, imports): @@ -1384,9 +1365,12 @@ def saveToStream(self, outfile, root="config", skipImports=False): configType = type(self) typeString = _typeStr(configType) outfile.write(f"import {configType.__module__}\n") + # We are required to write this on a single line because + # of later regex matching, rather than adopting black style + # formatting. outfile.write( - f"assert type({root})=={typeString}, 'config is of type %s.%s instead of " - f"{typeString}' % (type({root}).__module__, type({root}).__name__)\n" + f'assert type({root}) is {typeString}, f"config is of type ' + f'{{type({root}).__module__}}.{{type({root}).__name__}} instead of {typeString}"\n\n' ) for imp in sorted(self._imports): if imp in sys.modules and sys.modules[imp] is not None: @@ -1717,12 +1701,15 @@ def _classFromPython(config_py): """ # standard serialization has the form: # import config.class - # assert type(config)==config.class.Config, ... + # assert type(config) is config.class.Config, ... + # Older files use "type(config)==" instead. # We want to parse these two lines so we can get the class itself # Do a single regex to avoid large string copies when splitting a # large config into separate lines. - matches = re.search(r"^import ([\w.]+)\nassert .*==(.*?),", config_py) + # The assert regex cannot be greedy because the assert error string + # can include both "," and " is ". + matches = re.search(r"^import ([\w.]+)\nassert type\(\S+\)(?:\s*==\s*| is )(.*?),", config_py) if not matches: first_line, second_line, _ = config_py.split("\n", 2) diff --git a/python/lsst/pex/config/configChoiceField.py b/python/lsst/pex/config/configChoiceField.py index 07ea6c6c..fa0f3549 100644 --- a/python/lsst/pex/config/configChoiceField.py +++ b/python/lsst/pex/config/configChoiceField.py @@ -84,7 +84,7 @@ def __init__(self, dict_, value, at=None, label="assignment", setHistory=True): self._set = set() if setHistory: - self.__history.append(("Set selection to %s" % self, at, label)) + self.__history.append((f"Set selection to {self}", at, label)) @property def _config(self) -> Config: @@ -114,7 +114,7 @@ def add(self, value, at=None): # invoke __getitem__ to make sure it's present self._dict.__getitem__(value, at=at) - self.__history.append(("added %s to selection" % value, at, "selection")) + self.__history.append((f"added {value} to selection", at, "selection")) self._set.add(value) def discard(self, value, at=None): @@ -137,7 +137,7 @@ def discard(self, value, at=None): if at is None: at = getCallStack() - self.__history.append(("removed %s from selection" % value, at, "selection")) + self.__history.append((f"removed {value} from selection", at, "selection")) self._set.discard(value) def __len__(self): @@ -295,7 +295,7 @@ def __getitem__(self, k, at=None, label="default"): dtype = self.types[k] except Exception: raise FieldValidationError( - self._field, self._config, "Unknown key %r in Registry/ConfigChoiceField" % k + self._field, self._config, f"Unknown key {k!r} in Registry/ConfigChoiceField" ) name = _joinNamePath(self._config._name, self._field.name, k) if at is None: @@ -311,14 +311,12 @@ def __setitem__(self, k, value, at=None, label="assignment"): try: dtype = self.types[k] except Exception: - raise FieldValidationError(self._field, self._config, "Unknown key %r" % k) + raise FieldValidationError(self._field, self._config, f"Unknown key {k!r}") if value != dtype and type(value) is not dtype: - msg = "Value {} at key {} is of incorrect type {}. Expected type {}".format( - value, - k, - _typeStr(value), - _typeStr(dtype), + msg = ( + f"Value {value} at key {k} is of incorrect type {_typeStr(value)}. " + f"Expected type {_typeStr(dtype)}" ) raise FieldValidationError(self._field, self._config, msg) @@ -658,7 +656,7 @@ def _compare(self, instance1, instance2, shortcut, rtol, atol, output): name = getComparisonName( _joinNamePath(instance1._name, self.name), _joinNamePath(instance2._name, self.name) ) - if not compareScalars("selection for %s" % name, d1._selection, d2._selection, output=output): + if not compareScalars(f"selection for {name}", d1._selection, d2._selection, output=output): return False if d1._selection is None: return True diff --git a/python/lsst/pex/config/configDictField.py b/python/lsst/pex/config/configDictField.py index 532dd574..c285f10e 100644 --- a/python/lsst/pex/config/configDictField.py +++ b/python/lsst/pex/config/configDictField.py @@ -65,19 +65,15 @@ def __setitem__(self, k, x, at=None, label="setitem", setHistory=True): # validate keytype k = _autocast(k, self._field.keytype) if type(k) is not self._field.keytype: - msg = "Key {!r} is of type {}, expected type {}".format( - k, _typeStr(k), _typeStr(self._field.keytype) - ) + msg = f"Key {k!r} is of type {_typeStr(k)}, expected type {_typeStr(self._field.keytype)}" raise FieldValidationError(self._field, self._config, msg) # validate itemtype dtype = self._field.itemtype if type(x) is not self._field.itemtype and x != self._field.itemtype: - msg = "Value {} at key {!r} is of incorrect type {}. Expected type {}".format( - x, - k, - _typeStr(x), - _typeStr(self._field.itemtype), + msg = ( + f"Value {x} at key {k!r} is of incorrect type {_typeStr(x)}. " + f"Expected type {_typeStr(self._field.itemtype)}" ) raise FieldValidationError(self._field, self._config, msg) @@ -91,19 +87,19 @@ def __setitem__(self, k, x, at=None, label="setitem", setHistory=True): else: self._dict[k] = dtype(__name=name, __at=at, __label=label, **x._storage) if setHistory: - self.history.append(("Added item at key %s" % k, at, label)) + self.history.append((f"Added item at key {k}", at, label)) else: if x == dtype: x = dtype() oldValue.update(__at=at, __label=label, **x._storage) if setHistory: - self.history.append(("Modified item at key %s" % k, at, label)) + self.history.append((f"Modified item at key {k}", at, label)) def __delitem__(self, k, at=None, label="delitem"): if at is None: at = getCallStack() Dict.__delitem__(self, k, at, label, False) - self.history.append(("Removed item at key %s" % k, at, label)) + self.history.append((f"Removed item at key {k}", at, label)) class ConfigDictField(DictField): @@ -190,9 +186,9 @@ def __init__( deprecated=deprecated, ) if keytype not in self.supportedTypes: - raise ValueError("'keytype' %s is not a supported type" % _typeStr(keytype)) + raise ValueError(f"'keytype' {_typeStr(keytype)} is not a supported type") elif not issubclass(itemtype, Config): - raise ValueError("'itemtype' %s is not a supported type" % _typeStr(itemtype)) + raise ValueError(f"'itemtype' {_typeStr(itemtype)} is not a supported type") if dictCheck is not None and not hasattr(dictCheck, "__call__"): raise ValueError("'dictCheck' must be callable") if itemCheck is not None and not hasattr(itemCheck, "__call__"): @@ -293,7 +289,7 @@ def _compare(self, instance1, instance2, shortcut, rtol, atol, output): name = getComparisonName( _joinNamePath(instance1._name, self.name), _joinNamePath(instance2._name, self.name) ) - if not compareScalars("keys for %s" % name, set(d1.keys()), set(d2.keys()), output=output): + if not compareScalars(f"keys for {name}", set(d1.keys()), set(d2.keys()), output=output): return False equal = True for k, v1 in d1.items(): diff --git a/python/lsst/pex/config/configField.py b/python/lsst/pex/config/configField.py index b5cc8acd..32e778a3 100644 --- a/python/lsst/pex/config/configField.py +++ b/python/lsst/pex/config/configField.py @@ -82,7 +82,7 @@ class ConfigField(Field[FieldTypeVar]): def __init__(self, doc, dtype=None, default=None, check=None, deprecated=None): if dtype is None or not issubclass(dtype, Config): - raise ValueError("dtype=%s is not a subclass of Config" % _typeStr(dtype)) + raise ValueError(f"dtype={_typeStr(dtype)} is not a subclass of Config") if default is None: default = dtype source = getStackFrame() @@ -125,11 +125,7 @@ def __set__( name = _joinNamePath(prefix=instance._name, name=self.name) if value != self.dtype and type(value) is not self.dtype: - msg = "Value {} is of incorrect type {}. Expected {}".format( - value, - _typeStr(value), - _typeStr(self.dtype), - ) + msg = f"Value {value} is of incorrect type {_typeStr(value)}. Expected {_typeStr(self.dtype)}" raise FieldValidationError(self, instance, msg) if at is None: @@ -276,7 +272,7 @@ def validate(self, instance): value.validate() if self.check is not None and not self.check(value): - msg = "%s is not a valid value" % str(value) + msg = f"{value} is not a valid value" raise FieldValidationError(self, instance, msg) def _compare(self, instance1, instance2, shortcut, rtol, atol, output): diff --git a/python/lsst/pex/config/configurableActions/_configurableActionStructField.py b/python/lsst/pex/config/configurableActions/_configurableActionStructField.py index 73b8f04d..2f044919 100644 --- a/python/lsst/pex/config/configurableActions/_configurableActionStructField.py +++ b/python/lsst/pex/config/configurableActions/_configurableActionStructField.py @@ -315,7 +315,7 @@ def __set__( label: str = "assigment", ): if instance._frozen: - msg = "Cannot modify a frozen Config. " "Attempting to set field to value %s" % value + msg = "Cannot modify a frozen Config. " f"Attempting to set field to value {value}" raise FieldValidationError(self, instance, msg) if at is None: diff --git a/python/lsst/pex/config/configurableField.py b/python/lsst/pex/config/configurableField.py index 95e21795..0acd9877 100644 --- a/python/lsst/pex/config/configurableField.py +++ b/python/lsst/pex/config/configurableField.py @@ -308,8 +308,8 @@ def validateTarget(self, target, ConfigClass): raise AttributeError("'target' must define attribute 'ConfigClass'") if not issubclass(ConfigClass, Config): raise TypeError( - "'ConfigClass' is of incorrect type %s.'ConfigClass' must be a subclass of Config" - % _typeStr(ConfigClass) + f"'ConfigClass' is of incorrect type {_typeStr(ConfigClass)}. " + "'ConfigClass' must be a subclass of Config" ) if not hasattr(target, "__call__"): raise ValueError("'target' must be callable") @@ -389,10 +389,9 @@ def __set__(self, instance, value, at=None, label="assignment"): value = oldValue.ConfigClass() oldValue.update(__at=at, __label=label, **value._storage) else: - msg = "Value {} is of incorrect type {}. Expected {}".format( - value, - _typeStr(value), - _typeStr(oldValue.ConfigClass), + msg = ( + f"Value {value} is of incorrect type {_typeStr(value)}. " + f"Expected {_typeStr(oldValue.ConfigClass)}" ) raise FieldValidationError(self, instance, msg) @@ -418,9 +417,7 @@ def save(self, outfile, instance): # save target information ConfigClass = value.ConfigClass outfile.write( - "{}.retarget(target={}, ConfigClass={})\n\n".format( - fullname, _typeStr(target), _typeStr(ConfigClass) - ) + f"{fullname}.retarget(target={_typeStr(target)}, ConfigClass={_typeStr(ConfigClass)})\n\n" ) # save field values value._save(outfile) @@ -438,7 +435,7 @@ def validate(self, instance): value.validate() if self.check is not None and not self.check(value): - msg = "%s is not a valid value" % str(value) + msg = f"{value} is not a valid value" raise FieldValidationError(self, instance, msg) def __deepcopy__(self, memo): diff --git a/python/lsst/pex/config/dictField.py b/python/lsst/pex/config/dictField.py index ed263731..df30d18b 100644 --- a/python/lsst/pex/config/dictField.py +++ b/python/lsst/pex/config/dictField.py @@ -133,11 +133,9 @@ def __setitem__( raise FieldValidationError(self._field, self._config, msg) else: if type(x) is not self._field.itemtype and x is not None: - msg = "Value {} at key {!r} is of incorrect type {}. Expected type {}".format( - x, - k, - _typeStr(x), - _typeStr(self._field.itemtype), + msg = ( + f"Value {x} at key {k!r} is of incorrect type {_typeStr(x)}. " + f"Expected type {_typeStr(self._field.itemtype)}" ) raise FieldValidationError(self._field, self._config, msg) @@ -265,13 +263,8 @@ def _parseTypingArgs( _typ = ForwardRef(typ) # type ignore below because typeshed seems to be wrong. It # indicates there are only 2 args, as it was in python 3.8, but - # 3.9+ takes 3 args. Attempt in old style and new style to - # work with both. - try: - result = _typ._evaluate(globals(), locals(), set()) # type: ignore - except TypeError: - # python 3.8 path - result = _typ._evaluate(globals(), locals()) + # 3.9+ takes 3 args. + result = _typ._evaluate(globals(), locals(), recursive_guard=set()) # type: ignore if result is None: raise ValueError("Could not deduce type from input") typ = cast(type, result) @@ -314,9 +307,9 @@ def __init__( "keytype must either be supplied as an argument or as a type argument to the class" ) if keytype not in self.supportedTypes: - raise ValueError("'keytype' %s is not a supported type" % _typeStr(keytype)) + raise ValueError(f"'keytype' {_typeStr(keytype)} is not a supported type") elif itemtype is not None and itemtype not in self.supportedTypes: - raise ValueError("'itemtype' %s is not a supported type" % _typeStr(itemtype)) + raise ValueError(f"'itemtype' {_typeStr(itemtype)} is not a supported type") if dictCheck is not None and not hasattr(dictCheck, "__call__"): raise ValueError("'dictCheck' must be callable") if itemCheck is not None and not hasattr(itemCheck, "__call__"): @@ -356,7 +349,7 @@ def validate(self, instance): Field.validate(self, instance) value = self.__get__(instance) if value is not None and self.dictCheck is not None and not self.dictCheck(value): - msg = "%s is not a valid value" % str(value) + msg = f"{value} is not a valid value" raise FieldValidationError(self, instance, msg) def __set__( @@ -367,7 +360,7 @@ def __set__( label: str = "assignment", ) -> None: if instance._frozen: - msg = "Cannot modify a frozen Config. Attempting to set field to value %s" % value + msg = f"Cannot modify a frozen Config. Attempting to set field to value {value}" raise FieldValidationError(self, instance, msg) if at is None: @@ -433,11 +426,11 @@ def _compare(self, instance1, instance2, shortcut, rtol, atol, output): name = getComparisonName( _joinNamePath(instance1._name, self.name), _joinNamePath(instance2._name, self.name) ) - if not compareScalars("isnone for %s" % name, d1 is None, d2 is None, output=output): + if not compareScalars(f"isnone for {name}", d1 is None, d2 is None, output=output): return False if d1 is None and d2 is None: return True - if not compareScalars("keys for %s" % name, set(d1.keys()), set(d2.keys()), output=output): + if not compareScalars(f"keys for {name}", set(d1.keys()), set(d2.keys()), output=output): return False equal = True for k, v1 in d1.items(): diff --git a/python/lsst/pex/config/history.py b/python/lsst/pex/config/history.py index bc6073ac..2800f7d1 100644 --- a/python/lsst/pex/config/history.py +++ b/python/lsst/pex/config/history.py @@ -97,7 +97,7 @@ def __init__(self, text, category): try: color = Color.categories[category] except KeyError: - raise RuntimeError("Unknown category: %s" % category) + raise RuntimeError(f"Unknown category: {category}") self.rawText = str(text) x = color.lower().split(";") @@ -110,7 +110,7 @@ def __init__(self, text, category): try: self._code = "%s" % (30 + Color.colors[self.color]) except KeyError: - raise RuntimeError("Unknown colour: %s" % self.color) + raise RuntimeError(f"Unknown colour: {self.color}") if bold: self._code += ";1" @@ -149,7 +149,7 @@ def colorize(val=None): unknown.append(k) if unknown: - print("Unknown colourizing category: %s" % " ".join(unknown), file=sys.stderr) + print("Unknown colourizing category: {}".format(" ".join(unknown)), file=sys.stderr) return Color._colorize if sys.stdout.isatty() else False diff --git a/python/lsst/pex/config/listField.py b/python/lsst/pex/config/listField.py index d7fdd9b6..2117bb05 100644 --- a/python/lsst/pex/config/listField.py +++ b/python/lsst/pex/config/listField.py @@ -326,7 +326,7 @@ def __init__( "dtype must either be supplied as an argument or as a type argument to the class" ) if dtype not in Field.supportedTypes: - raise ValueError("Unsupported dtype %s" % _typeStr(dtype)) + raise ValueError(f"Unsupported dtype {_typeStr(dtype)}") if length is not None: if length <= 0: raise ValueError("'length' (%d) must be positive" % length) @@ -421,7 +421,7 @@ def validate(self, instance): msg = "Maximum allowed list length=%d, got length=%d" % (self.maxLength, lenValue) raise FieldValidationError(self, instance, msg) elif self.listCheck is not None and not self.listCheck(value): - msg = "%s is not a valid value" % str(value) + msg = f"{value} is not a valid value" raise FieldValidationError(self, instance, msg) def __set__( @@ -501,11 +501,11 @@ def _compare(self, instance1, instance2, shortcut, rtol, atol, output): name = getComparisonName( _joinNamePath(instance1._name, self.name), _joinNamePath(instance2._name, self.name) ) - if not compareScalars("isnone for %s" % name, l1 is None, l2 is None, output=output): + if not compareScalars(f"isnone for {name}", l1 is None, l2 is None, output=output): return False if l1 is None and l2 is None: return True - if not compareScalars("size for %s" % name, len(l1), len(l2), output=output): + if not compareScalars(f"size for {name}", len(l1), len(l2), output=output): return False equal = True for n, v1, v2 in zip(range(len(l1)), l1, l2): diff --git a/python/lsst/pex/config/rangeField.py b/python/lsst/pex/config/rangeField.py index 042ed034..e5c66c64 100644 --- a/python/lsst/pex/config/rangeField.py +++ b/python/lsst/pex/config/rangeField.py @@ -91,7 +91,7 @@ def __init__( deprecated=None, ): if dtype not in self.supportedTypes: - raise ValueError("Unsupported RangeField dtype %s" % (_typeStr(dtype))) + raise ValueError(f"Unsupported RangeField dtype {_typeStr(dtype)}") source = getStackFrame() if min is None and max is None: raise ValueError("min and max cannot both be None") diff --git a/python/lsst/pex/config/registry.py b/python/lsst/pex/config/registry.py index 43fb4a2c..18d1db96 100644 --- a/python/lsst/pex/config/registry.py +++ b/python/lsst/pex/config/registry.py @@ -122,12 +122,7 @@ class Registry(collections.abc.Mapping): def __init__(self, configBaseType=Config): if not issubclass(configBaseType, Config): - raise TypeError( - "configBaseType=%s must be a subclass of Config" - % _typeStr( - configBaseType, - ) - ) + raise TypeError(f"configBaseType={_typeStr(configBaseType)} must be a subclass of Config") self._configBaseType = configBaseType self._dict = {} @@ -162,15 +157,15 @@ def register(self, name, target, ConfigClass=None): the original ``target`` is stored. """ if name in self._dict: - raise RuntimeError("An item with name %r already exists" % name) + raise RuntimeError(f"An item with name {name!r} already exists") if ConfigClass is None: wrapper = target else: wrapper = ConfigurableWrapper(target, ConfigClass) if not issubclass(wrapper.ConfigClass, self._configBaseType): raise TypeError( - "ConfigClass=%s is not a subclass of %r" - % (_typeStr(wrapper.ConfigClass), _typeStr(self._configBaseType)) + f"ConfigClass={_typeStr(wrapper.ConfigClass)} is not a subclass of " + f"{_typeStr(self._configBaseType)!r}" ) self._dict[name] = wrapper @@ -292,7 +287,7 @@ def apply(self, *args, **kwargs): if self.active is None: if self._field._on_none is not None: return self._field._on_none(self, *args, **kwargs) - msg = "No selection has been made. Options: %s" % " ".join(self.types.registry.keys()) + msg = "No selection has been made. Options: {}".format(" ".join(self.types.registry.keys())) raise FieldValidationError(self._field, self._config, msg) return self.apply_with(self._selection, *args, **kwargs) diff --git a/python/lsst/pex/config/wrap.py b/python/lsst/pex/config/wrap.py index c77bee5e..28d79e64 100644 --- a/python/lsst/pex/config/wrap.py +++ b/python/lsst/pex/config/wrap.py @@ -152,7 +152,7 @@ def makeConfigClass(ctrl, name=None, base=Config, doc=None, module=None, cls=Non """ if name is None: if "Control" not in ctrl.__name__: - raise ValueError("Cannot guess appropriate Config class name for %s." % ctrl) + raise ValueError(f"Cannot guess appropriate Config class name for {ctrl}.") name = ctrl.__name__.replace("Control", "Config") if cls is None: cls = type(name, (base,), {"__doc__": doc}) @@ -213,7 +213,7 @@ def makeConfigClass(ctrl, name=None, base=Config, doc=None, module=None, cls=Non dtype = _dtypeMap.get(m.group("type"), None) FieldCls = ListField if dtype is None: - raise TypeError("Could not parse field type '%s'." % ctype) + raise TypeError(f"Could not parse field type '{ctype}'.") fields[k] = FieldCls(doc=doc, dtype=dtype, optional=True) # Define a number of methods to put in the new Config class. Note that diff --git a/tests/test_Config.py b/tests/test_Config.py index 06cb09d8..fc4cbf63 100644 --- a/tests/test_Config.py +++ b/tests/test_Config.py @@ -492,15 +492,12 @@ def testImports(self): def testBadImports(self): dummy = "somethingThatDoesntExist" - importing = ( - """ + importing = f""" try: - import %s + import {dummy} except ImportError: pass """ - % dummy - ) self.checkImportRoundTrip(importing, dummy, False) def testPickle(self): diff --git a/ups/pex_config.table b/ups/pex_config.table index ce1ad4fa..c6eb2d30 100644 --- a/ups/pex_config.table +++ b/ups/pex_config.table @@ -1,4 +1,5 @@ setupOptional(daf_base) +setupRequired(sconsUtils) envPrepend(LD_LIBRARY_PATH, ${PRODUCT_DIR}/lib) envPrepend(DYLD_LIBRARY_PATH, ${PRODUCT_DIR}/lib)