Skip to content

Commit 046d0a1

Browse files
authored
Merge pull request #493 from effigies/maint/modernize
fix: Adopt some more opinionated ruff rules, manual resolutions
2 parents 6869160 + 39df868 commit 046d0a1

27 files changed

+140
-174
lines changed

.circleci/version.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22

33
import sdcflows
44

5-
print(sdcflows.__version__, end='', file=open('/tmp/.docker-version.txt', 'w'))
5+
with open('/tmp/.docker-version.txt', 'w') as f:
6+
f.write(sdcflows.__version__)

.git-blame-ignore-revs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# Thu May 15 11:41:29 2025 -0400 - [email protected] - chore: Manual ruff fixes [ignore-rev]
2+
e5153dbc00b089144b9f4b415234b27c58b2388b
3+
# Thu May 15 11:11:38 2025 -0400 - [email protected] - run: ruff check --fix [ignore-rev]
4+
2bb216b0a12612e31713b34d8ab2ee98fa2cc703
5+
# Thu May 15 09:34:34 2025 -0400 - [email protected] - run: ruff check --fix [ignore-rev]
6+
dc3706038661984718da079e24a863842407a837
17
# Thu May 15 09:29:29 2025 -0400 - [email protected] - run: ruff format [ignore-rev]
28
d97ae316c0bdf71084a0732760ceed5221033fc2
39
# Thu May 15 09:26:57 2025 -0400 - [email protected] - run: ruff check --fix [ignore-rev]

.maint/update_authors.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,11 @@ def get_git_lines(fname='line-contributors.txt'):
130130
if not lines:
131131
raise RuntimeError(
132132
"""\
133-
Could not find line-contributors from git repository.%s"""
134-
% """ \
133+
Could not find line-contributors from git repository.{}""".format(
134+
""" \
135135
git-line-summary not found, please install git-extras. """
136-
* (cmd[0] is None)
136+
* (cmd[0] is None)
137+
)
137138
)
138139
return [' '.join(line.strip().split()[1:-1]) for line in lines if '%' in line]
139140

@@ -219,7 +220,7 @@ def zenodo(
219220
elif isinstance(creator['affiliation'], list):
220221
creator['affiliation'] = creator['affiliation'][0]
221222

222-
Path(zenodo_file).write_text('%s\n' % json.dumps(zenodo, indent=2, ensure_ascii=False))
223+
Path(zenodo_file).write_text(f'{json.dumps(zenodo, indent=2, ensure_ascii=False)}\n')
223224

224225

225226
@cli.command()
@@ -266,10 +267,8 @@ def _aslist(value):
266267

267268
aff_indexes = [
268269
', '.join(
269-
[
270-
'%d' % (affiliations.index(a) + 1)
271-
for a in _aslist(author.get('affiliation', 'Unaffiliated'))
272-
]
270+
str(affiliations.index(a) + 1)
271+
for a in _aslist(author.get('affiliation', 'Unaffiliated'))
273272
)
274273
for author in hits
275274
]
@@ -280,15 +279,13 @@ def _aslist(value):
280279
file=sys.stderr,
281280
)
282281

283-
print('Authors (%d):' % len(hits))
284-
print(
285-
'%s.'
286-
% '; '.join(['%s \\ :sup:`%s`\\ ' % (i['name'], idx) for i, idx in zip(hits, aff_indexes)])
287-
)
282+
print(f'Authors ({len(hits)}):')
283+
print('; '.join([rf'{i["name"]} \ :sup:`{idx}`\ ' for i, idx in zip(hits, aff_indexes)]) + '.')
288284

289285
print(
290-
'\n\nAffiliations:\n%s'
291-
% '\n'.join(['{0: >2}. {1}'.format(i + 1, a) for i, a in enumerate(affiliations)])
286+
'\n\nAffiliations:\n{}'.format(
287+
'\n'.join([f'{i + 1: >2}. {a}' for i, a in enumerate(affiliations)])
288+
)
292289
)
293290

294291

.pre-commit-config.yaml

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,9 @@ repos:
1212
- id: end-of-file-fixer
1313
- id: fix-byte-order-marker
1414
- id: trailing-whitespace
15-
# Enable when no significant PRs are in progress
16-
# - repo: https://github.com/psf/black
17-
# rev: 23.1.0
18-
# hooks:
19-
# - id: black
20-
# - repo: https://github.com/pycqa/isort
21-
# rev: 5.12.0
22-
# hooks:
23-
# - id: isort
15+
- repo: https://github.com/astral-sh/ruff-pre-commit
16+
rev: v0.12.5
17+
hooks:
18+
- id: ruff
19+
args: [ --fix ]
20+
- id: ruff-format

docs/tools/apigen.py

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
DEBUG = True
2828

2929

30-
class ApiDocWriter(object):
30+
class ApiDocWriter:
3131
"""Class for automatic detection and parsing of API docs
3232
to Sphinx-parsable reST format"""
3333

@@ -185,9 +185,8 @@ def _parse_module(self, uri):
185185
# nothing that we could handle here.
186186
return ([], [])
187187

188-
f = open(filename, 'rt')
189-
functions, classes = self._parse_lines(f)
190-
f.close()
188+
with open(filename) as f:
189+
functions, classes = self._parse_lines(f)
191190
return functions, classes
192191

193192
def _parse_module_with_import(self, uri):
@@ -217,14 +216,10 @@ def _parse_module_with_import(self, uri):
217216
continue
218217
obj = mod.__dict__[obj_str]
219218
# Check if function / class defined in module
220-
if not self.other_defines and not getmodule(obj) == mod:
219+
if not self.other_defines and getmodule(obj) != mod:
221220
continue
222221
# figure out if obj is a function or class
223-
if (
224-
hasattr(obj, 'func_name')
225-
or isinstance(obj, BuiltinFunctionType)
226-
or isinstance(obj, FunctionType)
227-
):
222+
if hasattr(obj, 'func_name') or isinstance(obj, (BuiltinFunctionType, FunctionType)):
228223
functions.append(obj_str)
229224
else:
230225
try:
@@ -278,7 +273,7 @@ def generate_api_doc(self, uri):
278273

279274
# Make a shorter version of the uri that omits the package name for
280275
# titles
281-
uri_short = re.sub(r'^%s\.' % self.package_name, '', uri)
276+
uri_short = re.sub(rf'^{self.package_name}\.', '', uri)
282277

283278
head = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
284279
body = ''
@@ -345,20 +340,12 @@ def _survives_exclude(self, matchstr, match_type):
345340
elif match_type == 'package':
346341
patterns = self.package_skip_patterns
347342
else:
348-
raise ValueError('Cannot interpret match type "%s"' % match_type)
343+
raise ValueError(f'Cannot interpret match type "{match_type}"')
349344
# Match to URI without package name
350345
L = len(self.package_name)
351346
if matchstr[:L] == self.package_name:
352347
matchstr = matchstr[L:]
353-
for pat in patterns:
354-
try:
355-
pat.search
356-
except AttributeError:
357-
pat = re.compile(pat)
358-
if pat.search(matchstr):
359-
return False
360-
361-
return True
348+
return not any(re.search(pat, matchstr) for pat in patterns)
362349

363350
def discover_modules(self):
364351
r"""Return module sequence discovered from ``self.package_name``
@@ -426,7 +413,7 @@ def write_modules_api(self, modules, outdir):
426413
written_modules = []
427414

428415
for ulm, mods in module_by_ulm.items():
429-
print('Generating docs for %s:' % ulm)
416+
print(f'Generating docs for {ulm}:')
430417
document_head = []
431418
document_body = []
432419

@@ -438,11 +425,8 @@ def write_modules_api(self, modules, outdir):
438425
document_body.append(body)
439426

440427
out_module = ulm + self.rst_extension
441-
outfile = os.path.join(outdir, out_module)
442-
fileobj = open(outfile, 'wt')
443-
444-
fileobj.writelines(document_head + document_body)
445-
fileobj.close()
428+
with open(os.path.join(outdir, out_module), 'w') as fileobj:
429+
fileobj.writelines(document_head + document_body)
446430
written_modules.append(out_module)
447431

448432
self.written_modules = written_modules
@@ -497,14 +481,13 @@ def write_index(self, outdir, froot='gen', relative_to=None):
497481
relpath = (outdir + os.path.sep).replace(relative_to + os.path.sep, '')
498482
else:
499483
relpath = outdir
500-
idx = open(path, 'wt')
501-
w = idx.write
502-
w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
503-
504-
title = 'API Reference'
505-
w(title + '\n')
506-
w('=' * len(title) + '\n\n')
507-
w('.. toctree::\n\n')
508-
for f in self.written_modules:
509-
w(' %s\n' % os.path.join(relpath, f))
510-
idx.close()
484+
with open(path, 'w') as idx:
485+
w = idx.write
486+
w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
487+
488+
title = 'API Reference'
489+
w(title + '\n')
490+
w('=' * len(title) + '\n\n')
491+
w('.. toctree::\n\n')
492+
for f in self.written_modules:
493+
w(f' {os.path.join(relpath, f)}\n')

docs/tools/buildmodref.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#!/usr/bin/env python
22
"""Script to auto-generate API docs."""
33

4-
from __future__ import division, print_function
5-
64
# stdlib imports
75
import sys
86

@@ -16,7 +14,7 @@
1614

1715

1816
def abort(error):
19-
print('*WARNING* API documentation not generated: %s' % error)
17+
print(f'*WARNING* API documentation not generated: {error}')
2018
exit()
2119

2220

@@ -43,13 +41,13 @@ def writeapi(package, outdir, source_version, other_defines=True):
4341
docwriter = ApiDocWriter(package, rst_extension='.rst', other_defines=other_defines)
4442

4543
docwriter.package_skip_patterns += [
46-
r'\.%s$' % package,
44+
rf'\.{package}$',
4745
r'.*test.*$',
4846
r'\.version.*$',
4947
]
5048
docwriter.write_api_docs(outdir)
5149
docwriter.write_index(outdir, 'index', relative_to=outdir)
52-
print('%d files written' % len(docwriter.written_modules))
50+
print(f'{len(docwriter.written_modules)} files written')
5351

5452

5553
if __name__ == '__main__':

pyproject.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,15 @@ ignore = [
179179
line-length = 99
180180

181181
[tool.ruff.lint]
182-
select = [
182+
extend-select = [
183183
"F",
184184
"E",
185+
"C4",
185186
"W",
187+
"B",
186188
"I",
189+
"SIM",
190+
"UP",
187191
]
188192
ignore = [
189193
"E203",
@@ -198,6 +202,9 @@ inline-quotes = "single"
198202
[tool.ruff.lint.extend-per-file-ignores]
199203
"*/__init__.py" = ["F401"]
200204
"docs/conf.py" = ["E265"]
205+
"*/test_*.py" = [
206+
"B018", # Unassigned expressions are fine
207+
]
201208

202209
[tool.ruff.format]
203210
quote-style = "single"

sdcflows/cli/main.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626
def main(argv=None):
2727
"""Entry point for SDCFlows' CLI."""
28-
import atexit
2928
import gc
3029
import os
3130
import sys
@@ -34,8 +33,6 @@ def main(argv=None):
3433
from sdcflows import config
3534
from sdcflows.cli.parser import parse_args
3635

37-
atexit.register(config.restore_env)
38-
3936
# Run parser
4037
parse_args(argv)
4138

sdcflows/cli/parser.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
#
2323
"""Standalone command line executable for estimation of fieldmaps."""
2424

25-
import re
2625
from argparse import Action, ArgumentDefaultsHelpFormatter, ArgumentParser
2726
from functools import partial
2827
from pathlib import Path
@@ -46,9 +45,7 @@ def _parse_participant_labels(value):
4645
['s060']
4746
4847
"""
49-
return sorted(
50-
set(re.sub(r'^sub-', '', item.strip()) for item in re.split(r'\s+', f'{value}'.strip()))
51-
)
48+
return sorted({item.removeprefix('sub-') for item in value.split()})
5249

5350

5451
def _parser():
@@ -301,12 +298,10 @@ def parse_args(args=None, namespace=None):
301298

302299
# Ensure input and output folders are not the same
303300
if output_dir == bids_dir:
301+
suggested_path = bids_dir / 'derivatives' / f'sdcflows_{version.split("+")[0]}'
304302
parser.error(
305303
'The selected output folder is the same as the input BIDS folder. '
306-
'Please modify the output path (suggestion: %s).'
307-
% bids_dir
308-
/ 'derivatives'
309-
/ ('sdcflows_%s' % version.split('+')[0])
304+
f'Please modify the output path (suggestion: {suggested_path}).'
310305
)
311306

312307
if bids_dir in work_dir.parents:

sdcflows/config.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@
124124
from importlib_metadata import version as get_version
125125

126126
# Ignore annoying warnings
127+
import contextlib
128+
127129
from sdcflows import __version__
128130
from sdcflows._warnings import logging
129131

@@ -200,7 +202,7 @@
200202
_memory_gb = None
201203
try:
202204
if 'linux' in sys.platform:
203-
with open('/proc/meminfo', 'r') as f_in:
205+
with open('/proc/meminfo') as f_in:
204206
_meminfo_lines = f_in.readlines()
205207
_mem_total_line = [line for line in _meminfo_lines if 'MemTotal' in line][0]
206208
_mem_total = float(_mem_total_line.split()[1])
@@ -220,7 +222,7 @@
220222
class _Config:
221223
"""An abstract class forbidding instantiation."""
222224

223-
_paths = tuple()
225+
_paths = ()
224226

225227
def __init__(self):
226228
"""Avert instantiation."""
@@ -239,10 +241,8 @@ def load(cls, settings, init=True):
239241
setattr(cls, k, v)
240242

241243
if init:
242-
try:
244+
with contextlib.suppress(AttributeError):
243245
cls.init()
244-
except AttributeError:
245-
pass
246246

247247
@classmethod
248248
def get(cls):
@@ -647,13 +647,3 @@ def _process_initializer(config_file: Path):
647647

648648
# Set the maximal number of threads per process
649649
os.environ['OMP_NUM_THREADS'] = f'{config.nipype.omp_nthreads}'
650-
651-
652-
def restore_env():
653-
"""Restore the original environment."""
654-
655-
for k in os.environ.keys():
656-
del os.environ[k]
657-
658-
for k, v in environment._pre_sdcflows.items():
659-
os.environ[k] = v

0 commit comments

Comments
 (0)