Skip to content

fix: Adopt some more opinionated ruff rules, manual resolutions #493

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .circleci/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

import sdcflows

print(sdcflows.__version__, end='', file=open('/tmp/.docker-version.txt', 'w'))
with open('/tmp/.docker-version.txt', 'w') as f:
f.write(sdcflows.__version__)
6 changes: 6 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Thu May 15 11:41:29 2025 -0400 - [email protected] - chore: Manual ruff fixes [ignore-rev]
d0da75c1e4d6345ea1cc2b337668f6774288195e
# Thu May 15 11:11:38 2025 -0400 - [email protected] - run: ruff check --fix [ignore-rev]
497670f170f87cdcfb32cc525f5bf0668f96129a
# Thu May 15 09:34:34 2025 -0400 - [email protected] - run: ruff check --fix [ignore-rev]
dc3706038661984718da079e24a863842407a837
# Thu May 15 09:29:29 2025 -0400 - [email protected] - run: ruff format [ignore-rev]
d97ae316c0bdf71084a0732760ceed5221033fc2
# Thu May 15 09:26:57 2025 -0400 - [email protected] - run: ruff check --fix [ignore-rev]
Expand Down
27 changes: 12 additions & 15 deletions .maint/update_authors.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,11 @@ def get_git_lines(fname='line-contributors.txt'):
if not lines:
raise RuntimeError(
"""\
Could not find line-contributors from git repository.%s"""
% """ \
Could not find line-contributors from git repository.{}""".format(
""" \
git-line-summary not found, please install git-extras. """
* (cmd[0] is None)
* (cmd[0] is None)
)
)
return [' '.join(line.strip().split()[1:-1]) for line in lines if '%' in line]

Expand Down Expand Up @@ -219,7 +220,7 @@ def zenodo(
elif isinstance(creator['affiliation'], list):
creator['affiliation'] = creator['affiliation'][0]

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


@cli.command()
Expand Down Expand Up @@ -266,10 +267,8 @@ def _aslist(value):

aff_indexes = [
', '.join(
[
'%d' % (affiliations.index(a) + 1)
for a in _aslist(author.get('affiliation', 'Unaffiliated'))
]
str(affiliations.index(a) + 1)
for a in _aslist(author.get('affiliation', 'Unaffiliated'))
)
for author in hits
]
Expand All @@ -280,15 +279,13 @@ def _aslist(value):
file=sys.stderr,
)

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

print(
'\n\nAffiliations:\n%s'
% '\n'.join(['{0: >2}. {1}'.format(i + 1, a) for i, a in enumerate(affiliations)])
'\n\nAffiliations:\n{}'.format(
'\n'.join([f'{i + 1: >2}. {a}' for i, a in enumerate(affiliations)])
)
)


Expand Down
15 changes: 6 additions & 9 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@ repos:
- id: end-of-file-fixer
- id: fix-byte-order-marker
- id: trailing-whitespace
# Enable when no significant PRs are in progress
# - repo: https://github.com/psf/black
# rev: 23.1.0
# hooks:
# - id: black
# - repo: https://github.com/pycqa/isort
# rev: 5.12.0
# hooks:
# - id: isort
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.10
hooks:
- id: ruff
args: [ --fix ]
- id: ruff-format
59 changes: 21 additions & 38 deletions docs/tools/apigen.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
DEBUG = True


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

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

f = open(filename, 'rt')
functions, classes = self._parse_lines(f)
f.close()
with open(filename) as f:
functions, classes = self._parse_lines(f)
return functions, classes

def _parse_module_with_import(self, uri):
Expand Down Expand Up @@ -217,14 +216,10 @@ def _parse_module_with_import(self, uri):
continue
obj = mod.__dict__[obj_str]
# Check if function / class defined in module
if not self.other_defines and not getmodule(obj) == mod:
if not self.other_defines and getmodule(obj) != mod:
continue
# figure out if obj is a function or class
if (
hasattr(obj, 'func_name')
or isinstance(obj, BuiltinFunctionType)
or isinstance(obj, FunctionType)
):
if hasattr(obj, 'func_name') or isinstance(obj, (BuiltinFunctionType, FunctionType)):
functions.append(obj_str)
else:
try:
Expand Down Expand Up @@ -278,7 +273,7 @@ def generate_api_doc(self, uri):

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

head = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
body = ''
Expand Down Expand Up @@ -345,20 +340,12 @@ def _survives_exclude(self, matchstr, match_type):
elif match_type == 'package':
patterns = self.package_skip_patterns
else:
raise ValueError('Cannot interpret match type "%s"' % match_type)
raise ValueError(f'Cannot interpret match type "{match_type}"')
# Match to URI without package name
L = len(self.package_name)
if matchstr[:L] == self.package_name:
matchstr = matchstr[L:]
for pat in patterns:
try:
pat.search
except AttributeError:
pat = re.compile(pat)
if pat.search(matchstr):
return False

return True
return not any(re.search(pat, matchstr) for pat in patterns)

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

for ulm, mods in module_by_ulm.items():
print('Generating docs for %s:' % ulm)
print(f'Generating docs for {ulm}:')
document_head = []
document_body = []

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

out_module = ulm + self.rst_extension
outfile = os.path.join(outdir, out_module)
fileobj = open(outfile, 'wt')

fileobj.writelines(document_head + document_body)
fileobj.close()
with open(os.path.join(outdir, out_module), 'w') as fileobj:
fileobj.writelines(document_head + document_body)
written_modules.append(out_module)

self.written_modules = written_modules
Expand Down Expand Up @@ -497,14 +481,13 @@ def write_index(self, outdir, froot='gen', relative_to=None):
relpath = (outdir + os.path.sep).replace(relative_to + os.path.sep, '')
else:
relpath = outdir
idx = open(path, 'wt')
w = idx.write
w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')

title = 'API Reference'
w(title + '\n')
w('=' * len(title) + '\n\n')
w('.. toctree::\n\n')
for f in self.written_modules:
w(' %s\n' % os.path.join(relpath, f))
idx.close()
with open(path, 'w') as idx:
w = idx.write
w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')

title = 'API Reference'
w(title + '\n')
w('=' * len(title) + '\n\n')
w('.. toctree::\n\n')
for f in self.written_modules:
w(f' {os.path.join(relpath, f)}\n')
8 changes: 3 additions & 5 deletions docs/tools/buildmodref.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Script to auto-generate API docs."""

from __future__ import division, print_function

# stdlib imports
import sys

Expand All @@ -16,7 +14,7 @@


def abort(error):
print('*WARNING* API documentation not generated: %s' % error)
print(f'*WARNING* API documentation not generated: {error}')
exit()


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

docwriter.package_skip_patterns += [
r'\.%s$' % package,
rf'\.{package}$',
r'.*test.*$',
r'\.version.*$',
]
docwriter.write_api_docs(outdir)
docwriter.write_index(outdir, 'index', relative_to=outdir)
print('%d files written' % len(docwriter.written_modules))
print(f'{len(docwriter.written_modules)} files written')


if __name__ == '__main__':
Expand Down
7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,12 @@ line-length = 99
select = [
"F",
"E",
"C",
"W",
"B",
"I",
"SIM",
"UP",
]
ignore = [
"E203",
Expand All @@ -198,6 +202,9 @@ inline-quotes = "single"
[tool.ruff.lint.extend-per-file-ignores]
"*/__init__.py" = ["F401"]
"docs/conf.py" = ["E265"]
"*/test_*.py" = [
"B018", # Unassigned expressions are fine
]

[tool.ruff.format]
quote-style = "single"
3 changes: 0 additions & 3 deletions sdcflows/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

def main(argv=None):
"""Entry point for SDCFlows' CLI."""
import atexit
import gc
import os
import sys
Expand All @@ -34,8 +33,6 @@ def main(argv=None):
from sdcflows import config
from sdcflows.cli.parser import parse_args

atexit.register(config.restore_env)

# Run parser
parse_args(argv)

Expand Down
11 changes: 3 additions & 8 deletions sdcflows/cli/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
#
"""Standalone command line executable for estimation of fieldmaps."""

import re
from argparse import Action, ArgumentDefaultsHelpFormatter, ArgumentParser
from functools import partial
from pathlib import Path
Expand All @@ -46,9 +45,7 @@
['s060']

"""
return sorted(
set(re.sub(r'^sub-', '', item.strip()) for item in re.split(r'\s+', f'{value}'.strip()))
)
return sorted({item.removeprefix('sub-') for item in value.split()})


def _parser():
Expand Down Expand Up @@ -301,12 +298,10 @@

# Ensure input and output folders are not the same
if output_dir == bids_dir:
suggested_path = bids_dir / 'derivatives' / f'sdcflows_{version.split("+")[0]}'

Check warning on line 301 in sdcflows/cli/parser.py

View check run for this annotation

Codecov / codecov/patch

sdcflows/cli/parser.py#L301

Added line #L301 was not covered by tests
parser.error(
'The selected output folder is the same as the input BIDS folder. '
'Please modify the output path (suggestion: %s).'
% bids_dir
/ 'derivatives'
/ ('sdcflows_%s' % version.split('+')[0])
f'Please modify the output path (suggestion: {suggested_path}).'
)

if bids_dir in work_dir.parents:
Expand Down
20 changes: 5 additions & 15 deletions sdcflows/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@
from importlib_metadata import version as get_version

# Ignore annoying warnings
import contextlib

from sdcflows import __version__
from sdcflows._warnings import logging

Expand Down Expand Up @@ -200,7 +202,7 @@
_memory_gb = None
try:
if 'linux' in sys.platform:
with open('/proc/meminfo', 'r') as f_in:
with open('/proc/meminfo') as f_in:
_meminfo_lines = f_in.readlines()
_mem_total_line = [line for line in _meminfo_lines if 'MemTotal' in line][0]
_mem_total = float(_mem_total_line.split()[1])
Expand All @@ -220,7 +222,7 @@
class _Config:
"""An abstract class forbidding instantiation."""

_paths = tuple()
_paths = ()

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

if init:
try:
with contextlib.suppress(AttributeError):
cls.init()
except AttributeError:
pass

@classmethod
def get(cls):
Expand Down Expand Up @@ -647,13 +647,3 @@ def _process_initializer(config_file: Path):

# Set the maximal number of threads per process
os.environ['OMP_NUM_THREADS'] = f'{config.nipype.omp_nthreads}'


def restore_env():
"""Restore the original environment."""

for k in os.environ.keys():
del os.environ[k]

for k, v in environment._pre_sdcflows.items():
os.environ[k] = v
Loading
Loading