Skip to content

Commit

Permalink
Merge pull request #116 from lsst/tickets/DM-48788
Browse files Browse the repository at this point in the history
DM-48788: Use ruff format
  • Loading branch information
timj authored Feb 10, 2025
2 parents 384b627 + aa4eee9 commit 7f23998
Show file tree
Hide file tree
Showing 21 changed files with 104 additions and 123 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v4
Expand Down
16 changes: 0 additions & 16 deletions .github/workflows/lint.yaml

This file was deleted.

27 changes: 6 additions & 21 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,34 +1,19 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: v5.0.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 24.4.2
hooks:
- id: black
# It is recommended to specify the latest version of Python
# supported by your project here, or alternatively use
# pre-commit's default_language_version, see
# https://pre-commit.com/#top_level-default_language_version
language_version: python3.10
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
name: isort (python)
- repo: https://github.com/PyCQA/flake8
rev: 7.1.0
hooks:
- id: flake8
- id: check-toml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.5.1
rev: v0.9.4
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/numpy/numpydoc
rev: "v1.7.0"
rev: "v1.8.0"
hooks:
- id: numpydoc-validation
2 changes: 1 addition & 1 deletion doc/changes/DM-48074.feature.rst
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Introduced the `keyCheck` callback to `DictField` and `ConfigDictField`, allowing custom key validation during assignment. Added unit tests to ensure functionality.
Introduced the `keyCheck` callback to `DictField` and `ConfigDictField`, allowing custom key validation during assignment. Added unit tests to ensure functionality.
13 changes: 13 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ target-version = ["py310"]
[tool.isort]
profile = "black"
line_length = 110
known_first_party = ["lsst"]

[tool.towncrier]
package = "lsst.pex.config"
Expand Down Expand Up @@ -149,6 +150,10 @@ select = [
"W", # pycodestyle
"D", # pydocstyle
"UP", # pyupgrade
"I", # isort
"RUF022", # sort __all__
"C4", # comprehensions
"B", # bugbear
]
extend-select = [
"RUF100", # Warn about unused noqa
Expand All @@ -157,12 +162,20 @@ extend-select = [
[tool.ruff.lint.per-file-ignores]
"tests/testLib.py" = ["F403", "F405"] # Wildcard imports.

[tool.ruff.lint.isort]
known-first-party = ["lsst"]

[tool.ruff.lint.pycodestyle]
max-doc-length = 79

[tool.ruff.lint.pydocstyle]
convention = "numpy"

[tool.ruff.format]
docstring-code-format = true
# Formatter does not know about indenting.
docstring-code-line-length = 69

[tool.numpydoc_validation]
checks = [
"all", # All except the rules listed below.
Expand Down
4 changes: 2 additions & 2 deletions python/lsst/pex/config/callStack.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

__all__ = ["getCallerFrame", "getStackFrame", "StackFrame", "getCallStack"]
__all__ = ["StackFrame", "getCallStack", "getCallerFrame", "getStackFrame"]

import inspect
import linecache
Expand All @@ -50,7 +50,7 @@ def getCallerFrame(relative=0):
This function is excluded from the frame.
"""
frame = inspect.currentframe().f_back.f_back # Our caller's caller
for ii in range(relative):
for _ in range(relative):
frame = frame.f_back
return frame

Expand Down
2 changes: 1 addition & 1 deletion python/lsst/pex/config/comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
writing messages as well as floating-point comparisons and shortcuts.
"""

__all__ = ("getComparisonName", "compareScalars", "compareConfigs")
__all__ = ("compareConfigs", "compareScalars", "getComparisonName")

import numpy

Expand Down
40 changes: 20 additions & 20 deletions python/lsst/pex/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
"Config",
"ConfigMeta",
"Field",
"FieldTypeVar",
"FieldValidationError",
"UnexpectedProxyUsageError",
"FieldTypeVar",
)

import copy
Expand Down Expand Up @@ -390,7 +390,6 @@ class Field(Generic[FieldTypeVar]):
>>> class Example(Config):
... myInt = Field("An integer field.", int, default=0)
... name = Field[str](doc="A string Field")
...
>>> print(config.myInt)
0
>>> config.myInt = 5
Expand Down Expand Up @@ -730,13 +729,14 @@ def __get__(self, instance, owner=None, at=None, label="default"):
# try statements are almost free in python if they succeed
try:
return instance._storage[self.name]
except AttributeError:
except AttributeError as e:
if not isinstance(instance, Config):
return self
else:
raise AttributeError(
e.add_note(
f"Config {instance} is missing _storage attribute, likely incorrectly initialized"
)
raise

def __set__(
self, instance: Config, value: FieldTypeVar | None, at: Any = None, label: str = "assignment"
Expand Down Expand Up @@ -791,7 +791,7 @@ def __set__(
try:
self._validateValue(value)
except BaseException as e:
raise FieldValidationError(self, instance, str(e))
raise FieldValidationError(self, instance, str(e)) from e

instance._storage[self.name] = value
if at is None:
Expand Down Expand Up @@ -943,23 +943,25 @@ class behavior.
>>> from lsst.pex.config import Config, Field, ListField
>>> class DemoConfig(Config):
... intField = Field(doc="An integer field", dtype=int, default=42)
... listField = ListField(doc="List of favorite beverages.", dtype=str,
... default=['coffee', 'green tea', 'water'])
...
... listField = ListField(
... doc="List of favorite beverages.",
... dtype=str,
... default=["coffee", "green tea", "water"],
... )
>>> config = DemoConfig()
Configs support many `dict`-like APIs:
>>> config.keys()
['intField', 'listField']
>>> 'intField' in config
>>> "intField" in config
True
Individual fields can be accessed as attributes of the configuration:
>>> config.intField
42
>>> config.listField.append('earl grey tea')
>>> config.listField.append("earl grey tea")
>>> print(config.listField)
['coffee', 'green tea', 'water', 'earl grey tea']
"""
Expand Down Expand Up @@ -1102,30 +1104,27 @@ def update(self, **kw):
>>> from lsst.pex.config import Config, Field
>>> class DemoConfig(Config):
... fieldA = Field(doc='Field A', dtype=int, default=42)
... fieldB = Field(doc='Field B', dtype=bool, default=True)
... fieldC = Field(doc='Field C', dtype=str, default='Hello world')
...
... fieldA = Field(doc="Field A", dtype=int, default=42)
... fieldB = Field(doc="Field B", dtype=bool, default=True)
... fieldC = Field(doc="Field C", dtype=str, default="Hello world")
>>> config = DemoConfig()
These are the default values of each field:
>>> for name, value in config.iteritems():
... print(f"{name}: {value}")
...
fieldA: 42
fieldB: True
fieldC: 'Hello world'
Using this method to update ``fieldA`` and ``fieldC``:
>>> config.update(fieldA=13, fieldC='Updated!')
>>> config.update(fieldA=13, fieldC="Updated!")
Now the values of each field are:
>>> for name, value in config.iteritems():
... print(f"{name}: {value}")
...
fieldA: 13
fieldB: True
fieldC: 'Updated!'
Expand All @@ -1137,8 +1136,9 @@ def update(self, **kw):
try:
field = self._fields[name]
field.__set__(self, value, at=at, label=label)
except KeyError:
raise KeyError(f"No field of name {name} exists in config type {_typeStr(self)}")
except KeyError as e:
e.add_note(f"No field of name {name} exists in config type {_typeStr(self)}")
raise

def load(self, filename, root="config"):
"""Modify this config in place by executing the Python code in a
Expand Down Expand Up @@ -1415,7 +1415,7 @@ def _collectImports(self):
class.
"""
self._imports.add(self.__module__)
for name, field in self._fields.items():
for field in self._fields.values():
field._collectImports(self, self._imports)

def toDict(self):
Expand Down
16 changes: 7 additions & 9 deletions python/lsst/pex/config/configChoiceField.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ def __init__(self, dict_, value, at=None, label="assignment", setHistory=True):
if v not in self._dict:
# invoke __getitem__ to ensure it's present
self._dict.__getitem__(v, at=at)
except TypeError:
except TypeError as e:
msg = f"Value {value} is of incorrect type {_typeStr(value)}. Sequence type expected"
raise FieldValidationError(self._field, self._config, msg)
raise FieldValidationError(self._field, self._config, msg) from e
self._set = set(value)
else:
self._set = set()
Expand Down Expand Up @@ -293,10 +293,10 @@ def __getitem__(self, k, at=None, label="default"):
except KeyError:
try:
dtype = self.types[k]
except Exception:
except Exception as e:
raise FieldValidationError(
self._field, self._config, f"Unknown key {k!r} in Registry/ConfigChoiceField"
)
) from e
name = _joinNamePath(self._config._name, self._field.name, k)
if at is None:
at = getCallStack()
Expand All @@ -310,8 +310,8 @@ def __setitem__(self, k, value, at=None, label="assignment"):

try:
dtype = self.types[k]
except Exception:
raise FieldValidationError(self._field, self._config, f"Unknown key {k!r}")
except Exception as e:
raise FieldValidationError(self._field, self._config, f"Unknown key {k!r}") from e

if value != dtype and type(value) is not dtype:
msg = (
Expand Down Expand Up @@ -443,15 +443,13 @@ class ConfigChoiceField(Field[ConfigInstanceDict]):
>>> from lsst.pex.config import Config, ConfigChoiceField, Field
>>> class AaaConfig(Config):
... somefield = Field("doc", int)
...
The ``MyConfig`` config has a ``ConfigChoiceField`` field called ``choice``
that maps the ``AaaConfig`` type to the ``"AAA"`` key:
>>> TYPEMAP = {"AAA", AaaConfig}
>>> class MyConfig(Config):
... choice = ConfigChoiceField("doc for choice", TYPEMAP)
...
Creating an instance of ``MyConfig``:
Expand All @@ -460,7 +458,7 @@ class ConfigChoiceField(Field[ConfigInstanceDict]):
Setting value of the field ``somefield`` on the "AAA" key of the ``choice``
field:
>>> instance.choice['AAA'].somefield = 5
>>> instance.choice["AAA"].somefield = 5
**Selecting the active configuration**
Expand Down
2 changes: 1 addition & 1 deletion python/lsst/pex/config/configDictField.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def __init__(

check_errors = []
for name, check in (("dictCheck", dictCheck), ("keyCheck", keyCheck), ("itemCheck", itemCheck)):
if check is not None and not hasattr(check, "__call__"):
if check is not None and not callable(check):
check_errors.append(name)
if check_errors:
raise ValueError(f"{', '.join(check_errors)} must be callable")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations

__all__ = ["ConfigurableAction", "ActionTypeVar"]
__all__ = ["ActionTypeVar", "ConfigurableAction"]

from typing import Any, TypeVar

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations

__all__ = ("ConfigurableActionStructField", "ConfigurableActionStruct")
__all__ = ("ConfigurableActionStruct", "ConfigurableActionStructField")

import weakref
from collections.abc import Iterable, Iterator, Mapping
Expand Down Expand Up @@ -55,7 +55,7 @@ def __set__(
value = value._attrs
else:
raise ValueError(
"Can only update a ConfigurableActionStruct with an instance of such, or a " "mapping"
"Can only update a ConfigurableActionStruct with an instance of such, or a mapping"
)
for name, action in value.items():
setattr(instance, name, action)
Expand Down Expand Up @@ -196,7 +196,7 @@ def __setattr__(
setHistory=False,
) -> None:
if hasattr(self._config, "_frozen") and self._config._frozen:
msg = "Cannot modify a frozen Config. " f"Attempting to set item {attr} to value {value}"
msg = f"Cannot modify a frozen Config. Attempting to set item {attr} to value {value}"
raise FieldValidationError(self._field, self._config, msg)

# verify that someone has not passed a string with a space or leading
Expand Down Expand Up @@ -315,7 +315,7 @@ def __set__(
label: str = "assigment",
):
if instance._frozen:
msg = "Cannot modify a frozen Config. " f"Attempting to set field to value {value}"
msg = f"Cannot modify a frozen Config. Attempting to set field to value {value}"
raise FieldValidationError(self, instance, msg)

if at is None:
Expand Down
2 changes: 1 addition & 1 deletion python/lsst/pex/config/configurableActions/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
"ActionTest1",
"ActionTest2",
"ActionTest3",
"TestConfig",
"TestDivideAction",
"TestSingleColumnAction",
"TestConfig",
)

from lsst.pex.config import Config, Field
Expand Down
Loading

0 comments on commit 7f23998

Please sign in to comment.