diff --git a/.copier-answers.yml b/.copier-answers.yml
index 3a29eb49..4ffd0e3e 100644
--- a/.copier-answers.yml
+++ b/.copier-answers.yml
@@ -1,5 +1,5 @@
# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
-_commit: c721391
+_commit: 91ffffb
_src_path: gh:scipp/copier_template
description: SANS data reduction for the European Spallation Source
max_python: '3.12'
diff --git a/.github/workflows/copier.yml b/.github/workflows/copier.yml
deleted file mode 100644
index b1fe6ce4..00000000
--- a/.github/workflows/copier.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: Copier template sync
-
-on:
- workflow_dispatch:
- schedule:
- - cron: '0 1 * * 1'
-
-jobs:
- update:
- name: Sync with copier template
- runs-on: 'ubuntu-latest'
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-python@v5
- with:
- python-version: '3.X'
- - run: pip install copier
- - run: copier update --skip-answered --vcs-ref=HEAD --conflict=rej
- - name: Create Pull Request
- uses: peter-evans/create-pull-request@v6
- with:
- title: '[CRON] Sync with copier template'
- body: |
- This PR updates the project to the latest version of scipp's [copier-template](https://github.com/scipp/copier_template).
- Remember to check for any conflicts and resolve them.
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index ab1be29a..98aaf568 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -65,7 +65,7 @@ jobs:
name: docs_html
path: html/
- - uses: JamesIves/github-pages-deploy-action@v4.5.0
+ - uses: JamesIves/github-pages-deploy-action@v4.6.1
if: ${{ inputs.publish }}
with:
branch: gh-pages
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 19830db7..4442b1b9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -13,15 +13,6 @@ repos:
- id: trailing-whitespace
args: [ --markdown-linebreak-ext=md ]
exclude: '\.svg'
- - repo: https://github.com/pycqa/isort
- rev: 5.12.0
- hooks:
- - id: isort
- name: isort (python)
- - repo: https://github.com/psf/black-pre-commit-mirror
- rev: 23.11.0
- hooks:
- - id: black
- repo: https://github.com/kynan/nbstripout
rev: 0.6.0
hooks:
@@ -29,18 +20,14 @@ repos:
types: [ "jupyter" ]
args: [ "--drop-empty-cells",
"--extra-keys 'metadata.language_info.version cell.metadata.jp-MarkdownHeadingCollapsed cell.metadata.pycharm'" ]
- - repo: https://github.com/pycqa/flake8
- rev: 6.1.0
- hooks:
- - id: flake8
- types: ["python"]
- additional_dependencies: ["flake8-bugbear==23.9.16"]
- - repo: https://github.com/pycqa/bandit
- rev: 1.7.5
- hooks:
- - id: bandit
- additional_dependencies: ["bandit[toml]"]
- args: ["-c", "pyproject.toml"]
+ - repo: https://github.com/astral-sh/ruff-pre-commit
+ rev: v0.4.3
+ hooks:
+ - id: ruff
+ args: [ --fix ]
+ types_or: [ python, pyi, jupyter ]
+ - id: ruff-format
+ types_or: [ python, pyi ]
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
hooks:
diff --git a/docs/_templates/doc_version.html b/docs/_templates/doc_version.html
index 48f9aacf..395dc60a 100644
--- a/docs/_templates/doc_version.html
+++ b/docs/_templates/doc_version.html
@@ -1,2 +1,2 @@
-Current {{ project }} version: {{ version }} (older versions).
+Current ESSsans version: {{ version }} (older versions).
diff --git a/docs/conf.py b/docs/conf.py
index d9579e30..e5d679b0 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,16 +1,19 @@
-# -*- coding: utf-8 -*-
-
import doctest
import os
import sys
+from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as get_version
+from sphinx.util import logging
+
sys.path.insert(0, os.path.abspath('.'))
+logger = logging.getLogger(__name__)
+
# General information about the project.
-project = u'ESSsans'
-copyright = u'2024 Scipp contributors'
-author = u'Scipp contributors'
+project = 'ESSsans'
+copyright = '2024 Scipp contributors'
+author = 'Scipp contributors'
html_show_sourcelink = True
@@ -34,6 +37,8 @@
import sciline.sphinxext.domain_types # noqa: F401
extensions.append('sciline.sphinxext.domain_types')
+ # See https://github.com/tox-dev/sphinx-autodoc-typehints/issues/457
+ suppress_warnings = ["config.cache"]
except ModuleNotFoundError:
pass
@@ -110,8 +115,15 @@
# built documents.
#
-release = get_version("esssans")
-version = ".".join(release.split('.')[:3]) # CalVer
+try:
+ release = get_version("esssans")
+ version = ".".join(release.split('.')[:3]) # CalVer
+except PackageNotFoundError:
+ logger.info(
+ "Warning: determining version from package metadata failed, falling back to "
+ "a dummy version number."
+ )
+ release = version = "0.0.0-dev"
warning_is_error = True
diff --git a/docs/developer/coding-conventions.md b/docs/developer/coding-conventions.md
index b23c0eb4..4fafc18d 100644
--- a/docs/developer/coding-conventions.md
+++ b/docs/developer/coding-conventions.md
@@ -2,7 +2,7 @@
## Code formatting
-There are no explicit code formatting conventions since we use `black` to enforce a format.
+There are no explicit code formatting conventions since we use `ruff` to enforce a format.
## Docstring format
diff --git a/docs/user-guide/common/beam-center-finder.ipynb b/docs/user-guide/common/beam-center-finder.ipynb
index 50e69f74..379f9c64 100644
--- a/docs/user-guide/common/beam-center-finder.ipynb
+++ b/docs/user-guide/common/beam-center-finder.ipynb
@@ -408,7 +408,7 @@
"p.ax.axhline(0, color='cyan')\n",
"p.ax.plot(0, 0, '+', color='k', ms=10)\n",
"dx = 0.25\n",
- "style = dict(ha='center', va='center', color='w')\n",
+ "style = dict(ha='center', va='center', color='w') # noqa: C408\n",
"p.ax.text(dx, dx, 'North-East', **style)\n",
"p.ax.text(-dx, dx, 'North-West', **style)\n",
"p.ax.text(dx, -dx, 'South-East', **style)\n",
@@ -433,7 +433,7 @@
"metadata": {},
"outputs": [],
"source": [
- "kwargs = dict(\n",
+ "kwargs = dict( # noqa: C408\n",
" data=masked,\n",
" norm=workflow.compute(NormWavelengthTerm[SampleRun]),\n",
" graph=workflow.compute(sans.conversions.ElasticCoordTransformGraph),\n",
diff --git a/docs/user-guide/isis/sans2d.ipynb b/docs/user-guide/isis/sans2d.ipynb
index 03a33bca..580a8f72 100644
--- a/docs/user-guide/isis/sans2d.ipynb
+++ b/docs/user-guide/isis/sans2d.ipynb
@@ -283,7 +283,7 @@
")\n",
"parts = (CleanSummedQ[SampleRun, Numerator], CleanSummedQ[SampleRun, Denominator])\n",
"iofqs = (IofQ[SampleRun], IofQ[BackgroundRun], BackgroundSubtractedIofQ)\n",
- "keys = monitors + (MaskedData[SampleRun],) + parts + iofqs\n",
+ "keys = (*monitors, MaskedData[SampleRun], *parts, *iofqs)\n",
"\n",
"results = workflow.compute(keys)\n",
"\n",
diff --git a/docs/user-guide/isis/zoom.ipynb b/docs/user-guide/isis/zoom.ipynb
index 270ed393..03e347e1 100644
--- a/docs/user-guide/isis/zoom.ipynb
+++ b/docs/user-guide/isis/zoom.ipynb
@@ -213,7 +213,7 @@
")\n",
"parts = (CleanSummedQ[SampleRun, Numerator], CleanSummedQ[SampleRun, Denominator])\n",
"iofqs = (IofQ[SampleRun],)\n",
- "keys = monitors + (MaskedData[SampleRun],) + parts + iofqs\n",
+ "keys = (*monitors, MaskedData[SampleRun], *parts, *iofqs)\n",
"\n",
"results = workflow.compute(keys)\n",
"\n",
diff --git a/docs/user-guide/loki/loki-direct-beam.ipynb b/docs/user-guide/loki/loki-direct-beam.ipynb
index 2be77a72..8c95902a 100644
--- a/docs/user-guide/loki/loki-direct-beam.ipynb
+++ b/docs/user-guide/loki/loki-direct-beam.ipynb
@@ -38,14 +38,10 @@
},
"outputs": [],
"source": [
- "import numpy as np\n",
"import scipp as sc\n",
- "import sciline\n",
- "import scippneutron as scn\n",
"import plopp as pp\n",
"from ess import sans\n",
"from ess import loki\n",
- "from ess import isissans as isis\n",
"from ess.sans.types import *"
]
},
diff --git a/pyproject.toml b/pyproject.toml
index abb95dc5..7102f7a7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -67,17 +67,43 @@ filterwarnings = [
'ignore:\n Sentinel is not a public part of the traitlets API:DeprecationWarning',
]
-[tool.bandit]
-# Excluding tests because bandit doesn't like `assert`.
-exclude_dirs = ["docs/conf.py", "tests"]
+[tool.ruff]
+line-length = 88
+extend-include = ["*.ipynb"]
+extend-exclude = [
+ ".*", "__pycache__", "build", "dist", "install",
+]
+
+[tool.ruff.lint]
+# See https://docs.astral.sh/ruff/rules/
+select = ["B", "C4", "DTZ", "E", "F", "G", "I", "PERF", "PGH", "PT", "PYI", "RUF", "S", "T20", "UP", "W"]
+ignore = [
+ # Conflict with ruff format, see
+ # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
+ "COM812", "COM819", "D206", "D300", "E111", "E114", "E117", "ISC001", "ISC002", "Q000", "Q001", "Q002", "Q003", "W191",
+]
+fixable = ["I001", "B010"]
+isort.known-first-party = ["esssans"]
+pydocstyle.convention = "numpy"
-[tool.black]
-skip-string-normalization = true
+[tool.ruff.lint.per-file-ignores]
+# those files have an increased risk of relying on import order
+"__init__.py" = ["I"]
+"tests/*" = [
+ "S101", # asserts are fine in tests
+ "B018", # 'useless expressions' are ok because some tests just check for exceptions
+]
+"*.ipynb" = [
+ "E501", # longer lines are sometimes more readable
+ "F403", # *-imports used with domain types
+ "F405", # linter may fail to find names because of *-imports
+ "I", # we don't collect imports at the top
+ "S101", # asserts are used for demonstration and are safe in notebooks
+ "T201", # printing is ok for demonstration purposes
+]
-[tool.isort]
-skip_gitignore = true
-profile = "black"
-known_first_party = ["esssans"]
+[tool.ruff.format]
+quote-style = "preserve"
[tool.mypy]
strict = true
@@ -87,7 +113,6 @@ enable_error_code = [
"redundant-expr",
"truthy-bool",
]
-show_error_codes = true
warn_unreachable = true
[project.entry-points."beamlime.stateless"]
diff --git a/requirements/make_base.py b/requirements/make_base.py
index 1e1f48e3..68a17e84 100644
--- a/requirements/make_base.py
+++ b/requirements/make_base.py
@@ -1,7 +1,6 @@
import sys
from argparse import ArgumentParser
from pathlib import Path
-from typing import List
import tomli
@@ -20,7 +19,7 @@
"""
-def write_dependencies(dependency_name: str, dependencies: List[str]) -> None:
+def write_dependencies(dependency_name: str, dependencies: list[str]) -> None:
path = Path(f"{dependency_name}.in")
if path.exists():
sections = path.read_text().split(CUSTOM_AUTO_SEPARATOR)
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 1ba190c5..00000000
--- a/setup.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-[flake8]
-# See https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#line-length
-max-line-length = 88
-extend-ignore = E203
diff --git a/src/ess/isissans/data.py b/src/ess/isissans/data.py
index ec89faa6..be4bc8b0 100644
--- a/src/ess/isissans/data.py
+++ b/src/ess/isissans/data.py
@@ -2,7 +2,6 @@
# Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
import sciline
import scipp as sc
-
from ess.sans.data import Registry
from ess.sans.types import (
BackgroundRun,
diff --git a/src/ess/isissans/general.py b/src/ess/isissans/general.py
index 82127c27..d49bb7a0 100644
--- a/src/ess/isissans/general.py
+++ b/src/ess/isissans/general.py
@@ -3,6 +3,7 @@
"""
Providers for the ISIS instruments.
"""
+
import scipp as sc
from ..sans.types import (
@@ -37,7 +38,7 @@
def default_parameters() -> dict:
return {
CorrectForGravity: False,
- DimsToKeep: tuple(),
+ DimsToKeep: (),
MonitorOffset[Incident]: MonitorOffset(sc.vector([0, 0, 0], unit='m')),
MonitorOffset[Transmission]: MonitorOffset(sc.vector([0, 0, 0], unit='m')),
DetectorBankOffset: DetectorBankOffset(sc.vector([0, 0, 0], unit='m')),
@@ -72,7 +73,7 @@ def data_to_tof(
def monitor_to_tof(
- da: ConfiguredReducibleMonitor[RunType, MonitorType]
+ da: ConfiguredReducibleMonitor[RunType, MonitorType],
) -> TofMonitor[RunType, MonitorType]:
"""Dummy conversion of monitor data to time-of-flight data.
The monitor data already has a time-of-flight coordinate."""
diff --git a/src/ess/isissans/io.py b/src/ess/isissans/io.py
index 4a5f739a..e0698913 100644
--- a/src/ess/isissans/io.py
+++ b/src/ess/isissans/io.py
@@ -3,10 +3,10 @@
"""
File loading functions for ISIS data, NOT using Mantid.
"""
+
from typing import NewType
import scipp as sc
-
from ess.sans.types import MaskedDetectorIDs, PixelMaskFilename
CalibrationFilename = NewType('CalibrationFilename', str)
@@ -33,7 +33,7 @@ def read_xml_detector_masking(filename: PixelMaskFilename) -> MaskedDetectorIDs:
"""
import xml.etree.ElementTree as ET # nosec
- tree = ET.parse(filename) # nosec
+ tree = ET.parse(filename) # noqa: S314
root = tree.getroot()
masked_detids = []
diff --git a/src/ess/isissans/mantidio.py b/src/ess/isissans/mantidio.py
index e35d22bd..b1ed1d1d 100644
--- a/src/ess/isissans/mantidio.py
+++ b/src/ess/isissans/mantidio.py
@@ -3,14 +3,14 @@
"""
File loading functions for ISIS data using Mantid.
"""
+
from typing import NewType, NoReturn
import sciline
import scipp as sc
import scippneutron as scn
-from scipp.constants import g
-
from ess.sans.types import DirectBeam, DirectBeamFilename, Filename, RunType, SampleRun
+from scipp.constants import g
from .data import LoadedFileContents
from .io import CalibrationFilename
diff --git a/src/ess/isissans/sans2d.py b/src/ess/isissans/sans2d.py
index 1e788b98..e0598b03 100644
--- a/src/ess/isissans/sans2d.py
+++ b/src/ess/isissans/sans2d.py
@@ -4,7 +4,6 @@
import sciline
import scipp as sc
-
from ess.sans import providers as sans_providers
from ess.sans.types import MaskedData, SampleRun, ScatteringRunType, TofData
diff --git a/src/ess/isissans/visualization.py b/src/ess/isissans/visualization.py
index 8640f3f7..af3d5da3 100644
--- a/src/ess/isissans/visualization.py
+++ b/src/ess/isissans/visualization.py
@@ -3,6 +3,7 @@
"""
Plotting functions for ISIS data.
"""
+
import warnings
from typing import Any
@@ -44,7 +45,7 @@ def plot_flat_detector_xy(
'Cannot plot 2-D instrument view of data array with non-constant '
'y coordinate along tubes. Use scippneutron.instrument_view instead.'
)
- plot_kwargs = dict(aspect='equal')
+ plot_kwargs = {'aspect': 'equal'}
plot_kwargs.update(kwargs)
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=RuntimeWarning)
diff --git a/src/ess/isissans/zoom.py b/src/ess/isissans/zoom.py
index 2bb91e28..6d355d62 100644
--- a/src/ess/isissans/zoom.py
+++ b/src/ess/isissans/zoom.py
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
import sciline
-
from ess.sans import providers as sans_providers
from .data import load_tutorial_direct_beam, load_tutorial_run
diff --git a/src/ess/loki/general.py b/src/ess/loki/general.py
index 48bb8d3b..a87d83e9 100644
--- a/src/ess/loki/general.py
+++ b/src/ess/loki/general.py
@@ -3,12 +3,10 @@
"""
Default parameters, providers and utility functions for the loki workflow.
"""
-from typing import Optional
import sciline
import scipp as sc
from ess.reduce import nexus
-
from ess.sans import providers as sans_providers
from ..sans.common import gravity_vector
@@ -48,7 +46,7 @@
def default_parameters() -> dict:
return {
CorrectForGravity: False,
- DimsToKeep: tuple(),
+ DimsToKeep: (),
NeXusMonitorName[Incident]: 'monitor_1',
NeXusMonitorName[Transmission]: 'monitor_2',
TransformationPath: 'transform',
@@ -88,7 +86,7 @@ def LokiAtLarmorWorkflow() -> sciline.Pipeline:
DETECTOR_BANK_RESHAPING = {
'larmor_detector': lambda x: x.fold(
- dim='detector_number', sizes=dict(layer=4, tube=32, straw=7, pixel=512)
+ dim='detector_number', sizes={'layer': 4, 'tube': 32, 'straw': 7, 'pixel': 512}
)
}
@@ -126,7 +124,7 @@ def get_monitor_data(
def _add_variances_and_coordinates(
da: sc.DataArray,
source_position: sc.Variable,
- sample_position: Optional[sc.Variable] = None,
+ sample_position: sc.Variable | None = None,
) -> sc.DataArray:
out = da.copy(deep=False)
if out.bins is not None:
diff --git a/src/ess/loki/io.py b/src/ess/loki/io.py
index 57c3bcbd..70c47bd2 100644
--- a/src/ess/loki/io.py
+++ b/src/ess/loki/io.py
@@ -3,9 +3,9 @@
"""
Loading and merging of LoKI data.
"""
+
import scipp as sc
from ess.reduce import nexus
-
from ess.sans.types import (
Filename,
LoadedNeXusDetector,
diff --git a/src/ess/loki/workflow.py b/src/ess/loki/workflow.py
index 992e677d..bb5c97d3 100644
--- a/src/ess/loki/workflow.py
+++ b/src/ess/loki/workflow.py
@@ -3,8 +3,6 @@
import sciline
import scipp as sc
import scippnexus as snx
-from scippneutron.io.nexus.load_nexus import JSONGroup
-
from ess.loki.general import (
get_monitor_data,
get_source_position,
@@ -24,12 +22,12 @@
WavelengthBins,
WavelengthMonitor,
)
+from scippneutron.io.nexus.load_nexus import JSONGroup
class MonitorHistogram(
sciline.ScopeTwoParams[RunType, MonitorType, sc.DataArray], sc.DataArray
-):
- ...
+): ...
def _hist_monitor_wavelength(
diff --git a/src/ess/sans/__init__.py b/src/ess/sans/__init__.py
index c38cc2b4..928672d6 100644
--- a/src/ess/sans/__init__.py
+++ b/src/ess/sans/__init__.py
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
+# ruff: noqa: E402, F401
import importlib.metadata
diff --git a/src/ess/sans/beam_center_finder.py b/src/ess/sans/beam_center_finder.py
index 748de25b..0d67ce0a 100644
--- a/src/ess/sans/beam_center_finder.py
+++ b/src/ess/sans/beam_center_finder.py
@@ -2,7 +2,7 @@
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
import uuid
-from typing import Dict, List, NewType, Union
+from typing import NewType
import numpy as np
import sciline
@@ -117,7 +117,7 @@ def beam_center_from_center_of_mass(
return _offsets_to_vector(data=summed, xy=xy, graph=graph)
-def _offsets_to_vector(data: sc.DataArray, xy: List[float], graph: dict) -> sc.Variable:
+def _offsets_to_vector(data: sc.DataArray, xy: list[float], graph: dict) -> sc.Variable:
"""
Convert x,y offsets inside the plane normal to the beam to a vector in absolute
coordinates.
@@ -133,15 +133,15 @@ def _offsets_to_vector(data: sc.DataArray, xy: List[float], graph: dict) -> sc.V
def _iofq_in_quadrants(
- xy: List[float],
+ xy: list[float],
data: sc.DataArray,
norm: sc.DataArray,
graph: dict,
- q_bins: Union[int, sc.Variable],
+ q_bins: int | sc.Variable,
wavelength_bins: sc.Variable,
transform: sc.Variable,
pixel_shape: sc.DataGroup,
-) -> Dict[str, sc.DataArray]:
+) -> dict[str, sc.DataArray]:
"""
Compute the intensity as a function of Q inside 4 quadrants in Phi.
@@ -191,7 +191,7 @@ def _iofq_in_quadrants(
params[LabFrameTransform[SampleRun]] = transform
params[ElasticCoordTransformGraph] = graph
params[BeamCenter] = _offsets_to_vector(data=data, xy=xy, graph=graph)
- params[DimsToKeep] = tuple()
+ params[DimsToKeep] = ()
params[WavelengthMask] = None
params[WavelengthBands] = None
@@ -223,7 +223,7 @@ def _iofq_in_quadrants(
return out
-def _cost(xy: List[float], *args) -> float:
+def _cost(xy: list[float], *args) -> float:
"""
Cost function for determining how close the :math:`I(Q)` curves are in all four
quadrants. The cost is defined as
@@ -287,7 +287,7 @@ def _cost(xy: List[float], *args) -> float:
'try restricting your Q range, or increasing the size of your Q bins to '
'improve statistics in the denominator.'
)
- logger.info(f'Beam center finder: x={xy[0]}, y={xy[1]}, cost={out}')
+ logger.info('Beam center finder: x=%s, y=%s, cost=%s', xy[0], xy[1], out)
return out
@@ -408,17 +408,17 @@ def beam_center_from_iofq(
the results for finding the beam center.
This is what is now implemented in this version of the algorithm.
- """ # noqa: E501
+ """
from scipy.optimize import minimize
logger = get_logger('sans')
- logger.info(f'Requested minimizer: {minimizer}')
- logger.info(f'Requested tolerance: {tolerance}')
+ logger.info('Requested minimizer: %s', minimizer)
+ logger.info('Requested tolerance: %s', tolerance)
minimizer = minimizer or 'Nelder-Mead'
tolerance = tolerance or 0.1
- logger.info(f'Using minimizer: {minimizer}')
- logger.info(f'Using tolerance: {tolerance}')
+ logger.info('Using minimizer: %s', minimizer)
+ logger.info('Using tolerance: %s', tolerance)
# Flatten positions dim which is required during the iterations for slicing with a
# boolean mask
@@ -431,7 +431,7 @@ def beam_center_from_iofq(
# Use center of mass to get initial guess for beam center
com_shift = beam_center_from_center_of_mass(data, graph)
- logger.info(f'Initial guess for beam center: {com_shift}')
+ logger.info('Initial guess for beam center: %s', com_shift)
coords = data.transform_coords(
['cylindrical_x', 'cylindrical_y'], graph=graph
@@ -452,6 +452,6 @@ def beam_center_from_iofq(
)
center = _offsets_to_vector(data=data, xy=res.x, graph=graph)
- logger.info(f'Final beam center value: {center}')
- logger.info(f'Beam center finder minimizer info: {res}')
+ logger.info('Final beam center value: %s', center)
+ logger.info('Beam center finder minimizer info: %s', res)
return center
diff --git a/src/ess/sans/common.py b/src/ess/sans/common.py
index 54b51fbf..debfe23e 100644
--- a/src/ess/sans/common.py
+++ b/src/ess/sans/common.py
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
-
import uuid
-from typing import Optional
import numpy as np
import scipp as sc
@@ -18,7 +16,7 @@ def gravity_vector() -> sc.Variable:
def mask_range(
- da: sc.DataArray, mask: sc.DataArray, name: Optional[str] = None
+ da: sc.DataArray, mask: sc.DataArray, name: str | None = None
) -> sc.DataArray:
"""
Mask a range on a data array.
diff --git a/src/ess/sans/data.py b/src/ess/sans/data.py
index 05db2211..ed80296d 100644
--- a/src/ess/sans/data.py
+++ b/src/ess/sans/data.py
@@ -1,10 +1,9 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
-from typing import Dict
class Registry:
- def __init__(self, instrument: str, files: Dict[str, str], version: str):
+ def __init__(self, instrument: str, files: dict[str, str], version: str):
import pooch
self._registry = pooch.create(
diff --git a/src/ess/sans/direct_beam.py b/src/ess/sans/direct_beam.py
index 60e4b90d..3fd2fe8c 100644
--- a/src/ess/sans/direct_beam.py
+++ b/src/ess/sans/direct_beam.py
@@ -1,8 +1,5 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
-
-from typing import List
-
import numpy as np
import scipp as sc
from sciline import Pipeline
@@ -55,7 +52,7 @@ def _compute_efficiency_correction(
return out.rename_dims({wavelength_band_dim: 'wavelength'})
-def direct_beam(*, workflow: Pipeline, I0: sc.Variable, niter: int = 5) -> List[dict]:
+def direct_beam(*, workflow: Pipeline, I0: sc.Variable, niter: int = 5) -> list[dict]:
"""
Compute the direct beam function.
diff --git a/src/ess/sans/i_of_q.py b/src/ess/sans/i_of_q.py
index 6fb5b963..1bc70306 100644
--- a/src/ess/sans/i_of_q.py
+++ b/src/ess/sans/i_of_q.py
@@ -237,7 +237,7 @@ def _bin_in_q(
# Make dims to flatten contiguous, keep wavelength as the last dim
data_dims = list(stripped.dims)
- for dim in to_flatten + ['wavelength']:
+ for dim in [*to_flatten, 'wavelength']:
data_dims.remove(dim)
data_dims.append(dim)
stripped = stripped.transpose(data_dims)
diff --git a/src/ess/sans/logging.py b/src/ess/sans/logging.py
index 53196876..7dad85fe 100644
--- a/src/ess/sans/logging.py
+++ b/src/ess/sans/logging.py
@@ -19,16 +19,17 @@
import inspect
import logging
import logging.config
+from collections.abc import Callable, Sequence
from copy import copy
from os import PathLike
-from typing import Any, Callable, List, Optional, Sequence, Union
+from typing import Any
import scipp as sc
import scippneutron as scn
from scipp.utils import running_in_jupyter
-def get_logger(subname: Optional[str] = None) -> logging.Logger:
+def get_logger(subname: str | None = None) -> logging.Logger:
"""Return one of ess's loggers.
Parameters
@@ -49,7 +50,7 @@ def get_logger(subname: Optional[str] = None) -> logging.Logger:
def log_call(
- *, instrument: str, message: str = None, level: Union[int, str] = logging.INFO
+ *, instrument: str, message: str | None = None, level: int | str = logging.INFO
):
"""
Decorator that logs a message every time the function is called.
@@ -109,7 +110,7 @@ def format(self, record: logging.LogRecord) -> str:
return super().format(record)
-def default_loggers_to_configure() -> List[logging.Logger]:
+def default_loggers_to_configure() -> list[logging.Logger]:
"""
Return a list of all loggers that get configured by ess by default.
"""
@@ -124,13 +125,13 @@ def default_loggers_to_configure() -> List[logging.Logger]:
def configure(
*,
- filename: Optional[Union[str, PathLike]] = 'scipp.ess.log',
- file_level: Union[str, int] = logging.INFO,
- stream_level: Union[str, int] = logging.WARNING,
- widget_level: Union[str, int] = logging.INFO,
+ filename: str | PathLike | None = 'scipp.ess.log',
+ file_level: str | int = logging.INFO,
+ stream_level: str | int = logging.WARNING,
+ widget_level: str | int = logging.INFO,
show_thread: bool = False,
show_process: bool = False,
- loggers: Optional[Sequence[Union[str, logging.Logger]]] = None,
+ loggers: Sequence[str | logging.Logger] | None = None,
):
"""Set up logging for the ess package.
@@ -200,7 +201,7 @@ def configure(
def configure_workflow(
- workflow_name: Optional[str] = None, *, display: Optional[bool] = None, **kwargs
+ workflow_name: str | None = None, *, display: bool | None = None, **kwargs
) -> logging.Logger:
"""Configure logging for a reduction workflow.
@@ -272,7 +273,7 @@ def greet():
]
-def _deduce_instrument_name(f: Any) -> Optional[str]:
+def _deduce_instrument_name(f: Any) -> str | None:
# Assumes package name: ess.[.subpackage]
package = inspect.getmodule(f).__package__
components = package.split('.', 2)
@@ -293,7 +294,7 @@ def _function_name(f: Callable) -> str:
def _make_stream_handler(
- level: Union[str, int], show_thread: bool, show_process: bool
+ level: str | int, show_thread: bool, show_process: bool
) -> logging.StreamHandler:
handler = logging.StreamHandler()
handler.setLevel(level)
@@ -302,8 +303,8 @@ def _make_stream_handler(
def _make_file_handler(
- filename: Union[str, PathLike],
- level: Union[str, int],
+ filename: str | PathLike,
+ level: str | int,
show_thread: bool,
show_process: bool,
) -> logging.FileHandler:
@@ -314,13 +315,13 @@ def _make_file_handler(
def _make_handlers(
- filename: Optional[Union[str, PathLike]],
- file_level: Union[str, int],
- stream_level: Union[str, int],
- widget_level: Union[str, int],
+ filename: str | PathLike | None,
+ file_level: str | int,
+ stream_level: str | int,
+ widget_level: str | int,
show_thread: bool,
show_process: bool,
-) -> List[logging.Handler]:
+) -> list[logging.Handler]:
handlers = [_make_stream_handler(stream_level, show_thread, show_process)]
if filename is not None:
handlers.append(
@@ -332,7 +333,7 @@ def _make_handlers(
def _configure_logger(
- logger: logging.Logger, handlers: List[logging.Handler], level: Union[str, int]
+ logger: logging.Logger, handlers: list[logging.Handler], level: str | int
):
for handler in handlers:
logger.addHandler(handler)
@@ -348,16 +349,14 @@ def _configure_mantid_logging(level: str):
pass
-def _base_level(levels: List[Union[str, int]]) -> int:
+def _base_level(levels: list[str | int]) -> int:
return min(
- (
- logging.getLevelName(level) if isinstance(level, str) else level
- for level in levels
- )
+ logging.getLevelName(level) if isinstance(level, str) else level
+ for level in levels
)
-def _mantid_version() -> Optional[str]:
+def _mantid_version() -> str | None:
try:
import mantid
diff --git a/src/ess/sans/masking.py b/src/ess/sans/masking.py
index 9345d0dd..c343b2e3 100644
--- a/src/ess/sans/masking.py
+++ b/src/ess/sans/masking.py
@@ -3,6 +3,7 @@
"""
Masking functions for the loki workflow.
"""
+
import numpy as np
import scipp as sc
diff --git a/src/ess/sans/types.py b/src/ess/sans/types.py
index e5babc59..d9e1acac 100644
--- a/src/ess/sans/types.py
+++ b/src/ess/sans/types.py
@@ -5,6 +5,7 @@
The domain types are used to define parameters and to request results from a Sciline
pipeline."""
+
from collections.abc import Sequence
from enum import Enum
from typing import NewType, TypeVar
diff --git a/src/ess/sans/uncertainty.py b/src/ess/sans/uncertainty.py
index a691e19a..9899a221 100644
--- a/src/ess/sans/uncertainty.py
+++ b/src/ess/sans/uncertainty.py
@@ -2,30 +2,30 @@
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
"""Tools for handling statistical uncertainties."""
-from typing import Dict, TypeVar, Union, overload
+from typing import TypeVar, overload
import scipp as sc
-T = TypeVar("T", bound=Union[sc.Variable, sc.DataArray])
+T = TypeVar("T", bound=sc.Variable | sc.DataArray)
@overload
def broadcast_with_upper_bound_variances(
- data: sc.Variable, sizes: Dict[str, int]
+ data: sc.Variable, sizes: dict[str, int]
) -> sc.Variable:
pass
@overload
def broadcast_with_upper_bound_variances(
- data: sc.DataArray, sizes: Dict[str, int]
+ data: sc.DataArray, sizes: dict[str, int]
) -> sc.DataArray:
pass
def broadcast_with_upper_bound_variances(
- data: Union[sc.Variable, sc.DataArray], sizes: Dict[str, int]
-) -> Union[sc.Variable, sc.DataArray]:
+ data: sc.Variable | sc.DataArray, sizes: dict[str, int]
+) -> sc.Variable | sc.DataArray:
if _no_variance_broadcast(data, sizes):
return data
size = 1
@@ -38,15 +38,15 @@ def broadcast_with_upper_bound_variances(
def drop_variances_if_broadcast(
- data: Union[sc.Variable, sc.DataArray], sizes: Dict[str, int]
-) -> Union[sc.Variable, sc.DataArray]:
+ data: sc.Variable | sc.DataArray, sizes: dict[str, int]
+) -> sc.Variable | sc.DataArray:
if _no_variance_broadcast(data, sizes):
return data
return sc.values(data)
def _no_variance_broadcast(
- data: Union[sc.Variable, sc.DataArray], sizes: Dict[str, int]
+ data: sc.Variable | sc.DataArray, sizes: dict[str, int]
) -> bool:
return (data.variances is None) or all(
data.sizes.get(dim) == size for dim, size in sizes.items()
diff --git a/src/ess/sans/workflow.py b/src/ess/sans/workflow.py
index 3c8a7f48..d04ae90e 100644
--- a/src/ess/sans/workflow.py
+++ b/src/ess/sans/workflow.py
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
-from typing import Hashable, Iterable
+from collections.abc import Hashable, Iterable
import pandas as pd
import sciline
diff --git a/tests/common_test.py b/tests/common_test.py
index 234d4e77..21f78560 100644
--- a/tests/common_test.py
+++ b/tests/common_test.py
@@ -4,7 +4,6 @@
import numpy as np
import pytest
import scipp as sc
-
from ess.sans.common import mask_range
diff --git a/tests/i_of_q_test.py b/tests/i_of_q_test.py
index 1a2074df..258d8a36 100644
--- a/tests/i_of_q_test.py
+++ b/tests/i_of_q_test.py
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
import scipp as sc
-
from ess import sans
merge_func = sans.workflow.merge_contributions
diff --git a/tests/io_test.py b/tests/io_test.py
index 57019e75..a853b52d 100644
--- a/tests/io_test.py
+++ b/tests/io_test.py
@@ -6,13 +6,12 @@
import scipp as sc
import scipp.testing
import scippnexus as snx
-from scippnexus.application_definitions import nxcansas
-
from ess.sans.io import save_background_subtracted_iofq
from ess.sans.types import BackgroundSubtractedIofQ, OutFilename, RunNumber, RunTitle
+from scippnexus.application_definitions import nxcansas
-@pytest.mark.parametrize('use_edges', (True, False))
+@pytest.mark.parametrize('use_edges', [True, False])
def test_save_background_subtracted_iofq(use_edges, tmp_path):
def background_subtracted_iofq() -> BackgroundSubtractedIofQ:
i = sc.arange('Q', 0.0, 400.0)
diff --git a/tests/isissans/sans2d_reduction_test.py b/tests/isissans/sans2d_reduction_test.py
index b4108a71..1a80226d 100644
--- a/tests/isissans/sans2d_reduction_test.py
+++ b/tests/isissans/sans2d_reduction_test.py
@@ -1,11 +1,10 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
-from typing import Callable, List
+from collections.abc import Callable
import pytest
import sciline
import scipp as sc
-
from ess import isissans as isis
from ess import sans
from ess.isissans import MonitorOffset, SampleOffset, sans2d
@@ -71,7 +70,7 @@ def make_params() -> dict:
params[CorrectForGravity] = True
params[UncertaintyBroadcastMode] = UncertaintyBroadcastMode.upper_bound
params[ReturnEvents] = False
- params[DimsToKeep] = tuple()
+ params[DimsToKeep] = ()
return params
@@ -145,9 +144,9 @@ def test_workflow_is_deterministic():
def test_pipeline_raises_VariancesError_if_normalization_errors_not_dropped():
params = make_params()
- params[
- NonBackgroundWavelengthRange
- ] = None # Make sure we raise in iofq_denominator
+ params[NonBackgroundWavelengthRange] = (
+ None # Make sure we raise in iofq_denominator
+ )
params[UncertaintyBroadcastMode] = UncertaintyBroadcastMode.fail
pipeline = sciline.Pipeline(sans2d_providers(), params=params)
with pytest.raises(sc.VariancesError):
@@ -181,10 +180,12 @@ def test_pipeline_can_compute_intermediate_results():
# TODO See scipp/sciline#57 for plans on a builtin way to do this
-def as_dict(funcs: List[Callable[..., type]]) -> dict:
+def as_dict(funcs: list[Callable[..., type]]) -> dict:
from typing import get_type_hints
- return dict(zip([get_type_hints(func)['return'] for func in funcs], funcs))
+ return dict(
+ zip([get_type_hints(func)['return'] for func in funcs], funcs, strict=True)
+ )
def pixel_dependent_direct_beam(
diff --git a/tests/isissans/zoom_reduction_test.py b/tests/isissans/zoom_reduction_test.py
index 391b44ba..8bc6b980 100644
--- a/tests/isissans/zoom_reduction_test.py
+++ b/tests/isissans/zoom_reduction_test.py
@@ -2,7 +2,6 @@
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
import sciline
import scipp as sc
-
from ess import isissans as isis
from ess import sans
from ess.sans.types import (
diff --git a/tests/loki/common.py b/tests/loki/common.py
index d9a27c02..a066eba5 100644
--- a/tests/loki/common.py
+++ b/tests/loki/common.py
@@ -1,9 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
-from typing import Callable, List
+from collections.abc import Callable
import scipp as sc
-
from ess import loki, sans
from ess.sans.types import (
BackgroundRun,
@@ -30,12 +29,12 @@ def make_params(no_masks: bool = True) -> dict:
params[NeXusDetectorName] = 'larmor_detector'
params[Filename[SampleRun]] = loki.data.loki_tutorial_sample_run_60339()
params[Filename[BackgroundRun]] = loki.data.loki_tutorial_background_run_60393()
- params[
- Filename[TransmissionRun[SampleRun]]
- ] = loki.data.loki_tutorial_sample_transmission_run()
- params[
- Filename[TransmissionRun[BackgroundRun]]
- ] = loki.data.loki_tutorial_run_60392()
+ params[Filename[TransmissionRun[SampleRun]]] = (
+ loki.data.loki_tutorial_sample_transmission_run()
+ )
+ params[Filename[TransmissionRun[BackgroundRun]]] = (
+ loki.data.loki_tutorial_run_60392()
+ )
params[Filename[EmptyBeamRun]] = loki.data.loki_tutorial_run_60392()
params[WavelengthBins] = sc.linspace(
@@ -56,7 +55,7 @@ def make_params(no_masks: bool = True) -> dict:
return params
-def loki_providers_no_beam_center_finder() -> List[Callable]:
+def loki_providers_no_beam_center_finder() -> list[Callable]:
from ess.isissans.io import read_xml_detector_masking
return list(
@@ -69,7 +68,8 @@ def loki_providers_no_beam_center_finder() -> List[Callable]:
)
-def loki_providers() -> List[Callable]:
- return loki_providers_no_beam_center_finder() + [
- sans.beam_center_finder.beam_center_from_center_of_mass
+def loki_providers() -> list[Callable]:
+ return [
+ *loki_providers_no_beam_center_finder(),
+ sans.beam_center_finder.beam_center_from_center_of_mass,
]
diff --git a/tests/loki/directbeam_test.py b/tests/loki/directbeam_test.py
index adef944e..e4b3295f 100644
--- a/tests/loki/directbeam_test.py
+++ b/tests/loki/directbeam_test.py
@@ -5,13 +5,12 @@
import sciline
import scipp as sc
-from scipp.scipy.interpolate import interp1d
-
from ess import loki, sans
from ess.sans.types import DimsToKeep, QBins, WavelengthBands, WavelengthBins
+from scipp.scipy.interpolate import interp1d
sys.path.insert(0, str(Path(__file__).resolve().parent))
-from common import loki_providers, make_params # noqa: E402
+from common import loki_providers, make_params
def _get_I0(qbins: sc.Variable) -> sc.Variable:
diff --git a/tests/loki/iofq_test.py b/tests/loki/iofq_test.py
index 377ad92e..22346ca3 100644
--- a/tests/loki/iofq_test.py
+++ b/tests/loki/iofq_test.py
@@ -7,8 +7,6 @@
import pytest
import sciline
import scipp as sc
-from scipp.testing import assert_identical
-
from ess import loki, sans
from ess.sans.conversions import ElasticCoordTransformGraph
from ess.sans.types import (
@@ -34,9 +32,10 @@
WavelengthBands,
WavelengthBins,
)
+from scipp.testing import assert_identical
sys.path.insert(0, str(Path(__file__).resolve().parent))
-from common import ( # noqa: E402
+from common import (
loki_providers,
loki_providers_no_beam_center_finder,
make_params,
diff --git a/tests/loki/live_reduction_test.py b/tests/loki/live_reduction_test.py
index 76aec700..65c3c1e1 100644
--- a/tests/loki/live_reduction_test.py
+++ b/tests/loki/live_reduction_test.py
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
import sciline as sl
-
from ess.loki.workflow import LoKiMonitorWorkflow
diff --git a/tests/normalization_test.py b/tests/normalization_test.py
index 5d62578e..ec5b18d0 100644
--- a/tests/normalization_test.py
+++ b/tests/normalization_test.py
@@ -4,7 +4,6 @@
import numpy as np
import pytest
import scipp as sc
-
from ess.isissans.data import sans2d_solid_angle_reference
from ess.sans import normalization
@@ -29,7 +28,7 @@ def _sans2d_geometry():
}
# Rotate +y to -x
transform = sc.spatial.rotation(value=[0, 0, 1 / 2**0.5, 1 / 2**0.5])
- return dict(pixel_shape=pixel_shape, transform=transform)
+ return {'pixel_shape': pixel_shape, 'transform': transform}
def _mantid_sans2d_solid_angle_data():
diff --git a/tests/package_test.py b/tests/package_test.py
index 218b68c8..9c5ac938 100644
--- a/tests/package_test.py
+++ b/tests/package_test.py
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
import pytest
-
from ess import isissans, loki, sans
diff --git a/tests/uncertainty_test.py b/tests/uncertainty_test.py
index e7fd0247..f317097b 100644
--- a/tests/uncertainty_test.py
+++ b/tests/uncertainty_test.py
@@ -2,9 +2,8 @@
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
import scipp as sc
-from scipp.testing import assert_identical
-
from ess.sans.uncertainty import broadcast_with_upper_bound_variances
+from scipp.testing import assert_identical
def test_broadcast_returns_original_if_no_new_dims():
diff --git a/tox.ini b/tox.ini
index c096cde9..1f1091fb 100644
--- a/tox.ini
+++ b/tox.ini
@@ -10,14 +10,14 @@ commands = pytest {posargs}
[testenv:nightly]
deps = -r requirements/nightly.txt
-commands = pytest
+commands = pytest {posargs}
[testenv:unpinned]
description = Test with unpinned dependencies, as a user would install now.
deps =
-r requirements/basetest.txt
esssans
-commands = pytest
+commands = pytest {posargs}
[testenv:docs]
description = invoke sphinx-build to build the HTML docs