diff --git a/docs/sphinxext/docscrape.py b/docs/sphinxext/docscrape.py index e5c07f59ded..5654f7e9319 100644 --- a/docs/sphinxext/docscrape.py +++ b/docs/sphinxext/docscrape.py @@ -326,8 +326,9 @@ def parse_item_name(text): description = line_match.group("desc") if line_match.group("trailing") and description: self._error_location( - "Unexpected comma or period after function list at index %d of " - 'line "%s"' % (line_match.end("trailing"), line), + "Unexpected comma or period after function list " + f"at index {line_match.end('trailing')} of line " + f'"{line}"', error=False, ) if not description and line.startswith(" "): @@ -414,8 +415,8 @@ def _parse(self): section = " ".join(section) if self.get(section): self._error_location( - "The section %s appears twice in %s" - % (section, "\n".join(self._doc._str)) + f"The section {section} appears twice " + f"in {'\n'.join(self._doc._str)}" ) if section in ("Parameters", "Other Parameters", "Attributes", "Methods"): diff --git a/docs/sphinxext/github.py b/docs/sphinxext/github.py index 4f74b64d648..c117d492f22 100644 --- a/docs/sphinxext/github.py +++ b/docs/sphinxext/github.py @@ -37,7 +37,7 @@ def make_link_node(rawtext, app, type, slug, options): if not base.endswith('/'): base += '/' except AttributeError as err: - raise ValueError('github_project_url configuration value is not set (%s)' % str(err)) + raise ValueError(f'github_project_url configuration value is not set ({err})') ref = base + type + '/' + slug + '/' set_classes(options) @@ -71,11 +71,11 @@ def ghissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): except ValueError: msg = inliner.reporter.error( 'GitHub issue number must be a number greater than or equal to 1; ' - '"%s" is invalid.' % text, line=lineno) + f'"{text}" is invalid.', line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] app = inliner.document.settings.env.app - #app.info('issue %r' % text) + #app.info(f'issue {text!r}') if 'pull' in name.lower(): category = 'pull' elif 'issue' in name.lower(): @@ -83,7 +83,7 @@ def ghissue_role(name, rawtext, text, lineno, inliner, options={}, content=[]): else: msg = inliner.reporter.error( 'GitHub roles include "ghpull" and "ghissue", ' - '"%s" is invalid.' % name, line=lineno) + f'"{name}" is invalid.', line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] node = make_link_node(rawtext, app, category, str(issue_num), options) @@ -105,7 +105,7 @@ def ghuser_role(name, rawtext, text, lineno, inliner, options={}, content=[]): :param content: The directive content for customization. """ app = inliner.document.settings.env.app - #app.info('user link %r' % text) + #app.info(f'user link {text!r}') ref = 'https://www.github.com/' + text node = nodes.reference(rawtext, text, refuri=ref, **options) return [node], [] @@ -126,7 +126,7 @@ def ghcommit_role(name, rawtext, text, lineno, inliner, options={}, content=[]): :param content: The directive content for customization. """ app = inliner.document.settings.env.app - #app.info('user link %r' % text) + #app.info(f'user link {text!r}') try: base = app.config.github_project_url if not base: @@ -134,7 +134,7 @@ def ghcommit_role(name, rawtext, text, lineno, inliner, options={}, content=[]): if not base.endswith('/'): base += '/' except AttributeError as err: - raise ValueError('github_project_url configuration value is not set (%s)' % str(err)) + raise ValueError(f'github_project_url configuration value is not set ({err})') ref = base + text node = nodes.reference(rawtext, text[:6], refuri=ref, **options) diff --git a/docs/sphinxext/math_dollar.py b/docs/sphinxext/math_dollar.py index ad415deb905..665f2fff2c7 100644 --- a/docs/sphinxext/math_dollar.py +++ b/docs/sphinxext/math_dollar.py @@ -33,7 +33,7 @@ def dollars_to_math(source): def repl(matchobj): global _data s = matchobj.group(0) - t = "___XXX_REPL_%d___" % len(_data) + t = f"___XXX_REPL_{len(_data)}___" _data[t] = s return t s = re.sub(r"({[^{}$]*\$[^{}$]*\$[^{}]*})", repl, s) diff --git a/niworkflows/__about__.py b/niworkflows/__about__.py index e0923bf4334..fc446d83146 100644 --- a/niworkflows/__about__.py +++ b/niworkflows/__about__.py @@ -29,9 +29,7 @@ from datetime import datetime __packagename__ = "niworkflows" -__copyright__ = "Copyright {}, The NiPreps Developers".format( - datetime.now().year -) +__copyright__ = f"Copyright {datetime.now().year}, The NiPreps Developers" __credits__ = [ "Oscar Esteban", "Ross Blair", diff --git a/niworkflows/anat/ants.py b/niworkflows/anat/ants.py index 5854f7c9d64..afe1a1fb4f7 100644 --- a/niworkflows/anat/ants.py +++ b/niworkflows/anat/ants.py @@ -721,19 +721,19 @@ def init_atropos_wf( # De-pad depad_mask = pe.Node( - ImageMath(operation="PadImage", op2="-%d" % padding), name="23_depad_mask" + ImageMath(operation="PadImage", op2=f"-{padding}"), name="23_depad_mask" ) depad_segm = pe.Node( - ImageMath(operation="PadImage", op2="-%d" % padding), name="24_depad_segm" + ImageMath(operation="PadImage", op2=f"-{padding}"), name="24_depad_segm" ) depad_gm = pe.Node( - ImageMath(operation="PadImage", op2="-%d" % padding), name="25_depad_gm" + ImageMath(operation="PadImage", op2=f"-{padding}"), name="25_depad_gm" ) depad_wm = pe.Node( - ImageMath(operation="PadImage", op2="-%d" % padding), name="26_depad_wm" + ImageMath(operation="PadImage", op2=f"-{padding}"), name="26_depad_wm" ) depad_csf = pe.Node( - ImageMath(operation="PadImage", op2="-%d" % padding), name="27_depad_csf" + ImageMath(operation="PadImage", op2=f"-{padding}"), name="27_depad_csf" ) msk_conform = pe.Node(niu.Function(function=_conform_mask), name="msk_conform") @@ -1061,7 +1061,7 @@ def _select_labels(in_segm, labels): for label in labels: newnii = nii.__class__(np.uint8(label_data == label), nii.affine, nii.header) newnii.set_data_dtype("uint8") - out_file = fname_presuffix(in_segm, suffix="_class-%02d" % label, newpath=cwd) + out_file = fname_presuffix(in_segm, suffix=f"_class-{label:%02d}", newpath=cwd) newnii.to_filename(out_file) out_files.append(out_file) return out_files diff --git a/niworkflows/data/__init__.py b/niworkflows/data/__init__.py index ef1bddab104..b69eb32ffaf 100644 --- a/niworkflows/data/__init__.py +++ b/niworkflows/data/__init__.py @@ -110,7 +110,7 @@ class Loader: .. automethod:: cached """ - def __init__(self, anchor: Union[str, ModuleType]): + def __init__(self, anchor: str | ModuleType): self._anchor = anchor self.files = files(anchor) self.exit_stack = ExitStack() diff --git a/niworkflows/func/tests/test_util.py b/niworkflows/func/tests/test_util.py index 76fd652a3a1..ffdcb292128 100755 --- a/niworkflows/func/tests/test_util.py +++ b/niworkflows/func/tests/test_util.py @@ -85,11 +85,10 @@ parameters = zip(bold_datasets, exp_masks) if not bold_datasets: + _folder_contents = "\n".join([str(p) for p in datapath.glob("ds*/*.nii.gz")]) raise RuntimeError( f"Data folder <{datapath}> was provided, but no images were found. " - "Folder contents:\n{}".format( - "\n".join([str(p) for p in datapath.glob("ds*/*.nii.gz")]) - ) + f"Folder contents:\n{_folder_contents}" ) diff --git a/niworkflows/interfaces/bids.py b/niworkflows/interfaces/bids.py index 2c139c6796f..6009d82ceec 100644 --- a/niworkflows/interfaces/bids.py +++ b/niworkflows/interfaces/bids.py @@ -858,8 +858,7 @@ def _run_interface(self, runtime): for fname in self._fields: if not self._undef_fields and fname not in metadata: raise KeyError( - 'Metadata field "%s" not found for file %s' - % (fname, self.inputs.in_file) + f'Metadata field "{fname}" not found for file {self.inputs.in_file}' ) self._results[fname] = metadata.get(fname, Undefined) return runtime @@ -945,7 +944,7 @@ def _run_interface(self, runtime): if dest.exists(): continue else: - raise FileNotFoundError("Expected to find '%s' to copy" % source) + raise FileNotFoundError(f"Expected to find '{source}' to copy") if ( space == 'fsaverage' diff --git a/niworkflows/interfaces/confounds.py b/niworkflows/interfaces/confounds.py index 267652674ab..9ce2bcf2548 100644 --- a/niworkflows/interfaces/confounds.py +++ b/niworkflows/interfaces/confounds.py @@ -298,13 +298,13 @@ def spike_regressors( mask = reduce((lambda x, y: x | y), mask.values()) for lag in lags: - mask = set([m + lag for m in mask]) | mask + mask = {m + lag for m in mask} | mask mask = mask.intersection(indices) if minimum_contiguous is not None: post_final = data.shape[0] + 1 - epoch_length = np.diff(sorted(mask | set([-1, post_final]))) - 1 - epoch_end = sorted(mask | set([post_final])) + epoch_length = np.diff(sorted(mask | {-1, post_final})) - 1 + epoch_end = sorted(mask | {post_final}) for end, length in zip(epoch_end, epoch_length): if length < minimum_contiguous: mask = mask | set(range(end - length, end)) @@ -357,7 +357,7 @@ def temporal_derivatives(order, variables, data): if 0 in order: data_deriv[0] = data[variables] variables_deriv[0] = variables - order = set(order) - set([0]) + order = set(order) - {0} for o in order: variables_deriv[o] = [f"{v}_derivative{o}" for v in variables] data_deriv[o] = np.tile(np.nan, data[variables].shape) @@ -400,7 +400,7 @@ def exponential_terms(order, variables, data): if 1 in order: data_exp[1] = data[variables] variables_exp[1] = variables - order = set(order) - set([1]) + order = set(order) - {1} for o in order: variables_exp[o] = [f"{v}_power{o}" for v in variables] data_exp[o] = data[variables] ** o diff --git a/niworkflows/interfaces/freesurfer.py b/niworkflows/interfaces/freesurfer.py index 392bbe01cfb..092728f2cb7 100644 --- a/niworkflows/interfaces/freesurfer.py +++ b/niworkflows/interfaces/freesurfer.py @@ -139,7 +139,7 @@ def cmdline(self): if source is None: return cmd - return "cp {} {}".format(source, self._list_outputs()["out_file"]) + return f"cp {source} {self._list_outputs()['out_file']}" class _FSInjectBrainExtractedInputSpec(BaseInterfaceInputSpec): diff --git a/niworkflows/interfaces/header.py b/niworkflows/interfaces/header.py index 657c7f0f35d..df301aeb0b4 100644 --- a/niworkflows/interfaces/header.py +++ b/niworkflows/interfaces/header.py @@ -100,7 +100,7 @@ def _run_interface(self, runtime): _copyxform( self.inputs.hdr_file, out_name, - message="CopyXForm (niworkflows v%s)" % __version__, + message=f"CopyXForm (niworkflows v{__version__})", ) self._results[f].append(out_name) @@ -543,10 +543,7 @@ def _run_interface(self, runtime): img.to_filename(out_fname) if warning_txt: - snippet = '

%s

\n%s\n' % ( - warning_txt, - description, - ) + snippet = f'

{warning_txt}

\n{description}\n' with open(out_report, "w") as fobj: fobj.write(indent(snippet, "\t" * 3)) diff --git a/niworkflows/interfaces/images.py b/niworkflows/interfaces/images.py index aafb791fb4b..20b6bcb2fef 100644 --- a/niworkflows/interfaces/images.py +++ b/niworkflows/interfaces/images.py @@ -139,7 +139,7 @@ def _run_interface(self, runtime): filenii = nb.load(f) filenii = nb.squeeze_image(filenii) if len(filenii.shape) == 5: - raise RuntimeError("Input image (%s) is 5D." % f) + raise RuntimeError(f"Input image ({f}) is 5D.") if filenii.dataobj.ndim == 4: nii_list += nb.four_to_three(filenii) else: diff --git a/niworkflows/interfaces/itk.py b/niworkflows/interfaces/itk.py index ce9ad7fed22..403bc52daa0 100644 --- a/niworkflows/interfaces/itk.py +++ b/niworkflows/interfaces/itk.py @@ -188,7 +188,7 @@ def _applytfms(args): in_file, in_xform, ifargs, index, newpath = args out_file = fname_presuffix( - in_file, suffix="_xform-%05d" % index, newpath=newpath, use_ext=True + in_file, suffix=f"_xform-{index:05d}", newpath=newpath, use_ext=True ) copy_dtype = ifargs.pop("copy_dtype", False) @@ -250,8 +250,8 @@ def _arrange_xfms(transforms, num_files, tmp_folder): if nxforms != num_files: raise RuntimeError( - "Number of transforms (%d) found in the ITK file does not match" - " the number of input image files (%d)." % (nxforms, num_files) + f"Number of transforms ({nxforms}) found in the ITK file does" + f" not match the number of input image files ({num_files})." ) # At this point splitting transforms will be necessary, generate a base name @@ -262,7 +262,7 @@ def _arrange_xfms(transforms, num_files, tmp_folder): split_xfms = [] for xform_i in range(nxforms): # Find start token to extract - startidx = tfdata.index("#Transform %d" % xform_i) + startidx = tfdata.index(f"#Transform {xform_i}") next_xform = base_xform + tfdata[startidx + 1:startidx + 4] + [""] xfm_file = out_base(xform_i) with open(xfm_file, "w") as out_xfm: diff --git a/niworkflows/interfaces/nibabel.py b/niworkflows/interfaces/nibabel.py index 58766138581..eee3d587ddc 100644 --- a/niworkflows/interfaces/nibabel.py +++ b/niworkflows/interfaces/nibabel.py @@ -661,9 +661,8 @@ def _gen_reference( # Keep 0, 2, 3, 4 unchanged resampled.header.set_qform(xform, int(xform_code)) resampled.header.set_sform(xform, int(xform_code)) - resampled.header["descrip"] = "reference image generated by %s." % ( - message or "(unknown software)" - ) + message = message or "(unknown software)" + resampled.header["descrip"] = f"reference image generated by {message}." resampled.to_filename(out_file) return out_file diff --git a/niworkflows/interfaces/norm.py b/niworkflows/interfaces/norm.py index 4c4875cd597..2103b02eaf8 100644 --- a/niworkflows/interfaces/norm.py +++ b/niworkflows/interfaces/norm.py @@ -162,9 +162,7 @@ def _get_settings(self): return self.inputs.settings # Define a prefix for output files based on the modality of the moving image. - filestart = "{}-mni_registration_{}_".format( - self.inputs.moving.lower(), self.inputs.flavor - ) + filestart = f"{self.inputs.moving.lower()}-mni_registration_{self.inputs.flavor}_" data_dir = load_data() # Get a list of settings files that match the flavor. @@ -224,7 +222,7 @@ def _run_interface(self, runtime): NIWORKFLOWS_LOG.warning("Retry #%d failed.", self.retry) # Save outputs (if available) term_out = _write_outputs( - interface_result.runtime, ".nipype-%04d" % self.retry + interface_result.runtime, f".nipype-{self.retry:04d}" ) if term_out: NIWORKFLOWS_LOG.warning( @@ -243,7 +241,7 @@ def _run_interface(self, runtime): # If all tries fail, raise an error. raise RuntimeError( - "Robust spatial normalization failed after %d retries." % (self.retry - 1) + f"Robust spatial normalization failed after {self.retry - 1} retries." ) def _get_ants_args(self): @@ -435,10 +433,9 @@ def _get_ants_args(self): self._reference_image = ref_template if not op.isfile(self._reference_image): raise ValueError( - """\ -The registration reference must be an existing file, but path "%s" \ + f"""\ +The registration reference must be an existing file, but path "{ref_template}" \ cannot be found.""" - % ref_template ) # Get the template specified by the user. diff --git a/niworkflows/interfaces/reportlets/masks.py b/niworkflows/interfaces/reportlets/masks.py index bac370fe280..ad3277801bb 100644 --- a/niworkflows/interfaces/reportlets/masks.py +++ b/niworkflows/interfaces/reportlets/masks.py @@ -140,7 +140,7 @@ def _post_run_hook(self, runtime): if len(self.inputs.mask_files) != 1: raise ValueError( "ACompCorRPT only supports a single input mask. " - "A list %s was found." % self.inputs.mask_files + f"A list {self.inputs.mask_files} was found." ) self._anat_file = self.inputs.realigned_file self._mask_file = self.inputs.mask_files[0] @@ -182,7 +182,7 @@ def _post_run_hook(self, runtime): if isinstance(high_variance_masks, list): raise ValueError( "TCompCorRPT only supports a single output high variance mask. " - "A list %s was found." % high_variance_masks + f"A list {high_variance_masks} was found." ) self._anat_file = self.inputs.realigned_file self._mask_file = high_variance_masks diff --git a/niworkflows/interfaces/surf.py b/niworkflows/interfaces/surf.py index 0606805bf0e..3fc9c228ba8 100644 --- a/niworkflows/interfaces/surf.py +++ b/niworkflows/interfaces/surf.py @@ -509,13 +509,12 @@ def _run_interface(self, runtime): "VolGeomC_A": "0.0", "VolGeomC_S": "0.0", } - meta["AnatomicalStructurePrimary"] = "Cortex%s" % ( - "Left" if self.inputs.surf_key.startswith("lh") else "Right" - ) + _lr = "Left" if self.inputs.surf_key.startswith("lh") else "Right" + meta["AnatomicalStructurePrimary"] = f"Cortex{_lr}" meta["AnatomicalStructureSecondary"] = SECONDARY_ANAT_STRUC[ self.inputs.surf_key.split(".")[-1] ] - meta["Name"] = "%s_average.gii" % self.inputs.surf_key + meta["Name"] = f"{self.inputs.surf_key}_average.gii" out_file = Path(runtime.cwd) / meta["Name"] out_file = ply2gii(self.inputs.in_file, meta, out_file=out_file) @@ -754,7 +753,7 @@ def ply2gii(in_file, metadata, out_file=None): metadata.update( zip( ("SurfaceCenterX", "SurfaceCenterY", "SurfaceCenterZ"), - ["%.4f" % c for c in surf.centroid], + [f"{c:.4f}" for c in surf.centroid], ) ) diff --git a/niworkflows/interfaces/tests/test_bids.py b/niworkflows/interfaces/tests/test_bids.py index 0119aa883e2..6b2401e335f 100644 --- a/niworkflows/interfaces/tests/test_bids.py +++ b/niworkflows/interfaces/tests/test_bids.py @@ -652,7 +652,7 @@ def test_ReadSidecarJSON_connection(testdata_dir, field): "derivatives, subjects_dir", [ (os.getenv("FREESURFER_HOME"), "subjects"), - ("/tmp", "{}/{}".format((os.getenv("FREESURFER_HOME"), "subjects"))), + ("/tmp", "{}/{}".format(os.getenv("FREESURFER_HOME"), "subjects")), ], ) def test_fsdir_noaction(derivatives, subjects_dir): @@ -661,7 +661,7 @@ def test_fsdir_noaction(derivatives, subjects_dir): res = bintfs.BIDSFreeSurferDir( derivatives=derivatives, subjects_dir=subjects_dir, freesurfer_home=fshome ).run() - assert res.outputs.subjects_dir == "%s/subjects" % fshome + assert res.outputs.subjects_dir == f"{fshome}/subjects" @pytest.mark.skipif(not os.getenv("FREESURFER_HOME"), reason="No FreeSurfer") diff --git a/niworkflows/interfaces/tests/test_cifti.py b/niworkflows/interfaces/tests/test_cifti.py index 2a18de7cb71..ae4086aaa7e 100644 --- a/niworkflows/interfaces/tests/test_cifti.py +++ b/niworkflows/interfaces/tests/test_cifti.py @@ -79,7 +79,7 @@ def test__create_cifti_image(tmp_path): cifti_file = _create_cifti_image(bold_file, volume_label, dummy_fnames, dummy_fnames, 2.0) cimg = nb.load(cifti_file) - series, bm = [cimg.header.get_axis(i) for i in (0, 1)] + series, bm = (cimg.header.get_axis(i) for i in (0, 1)) assert len(series) == 1 # Time assert len(bm) == 8 # Voxel diff --git a/niworkflows/interfaces/tests/test_images.py b/niworkflows/interfaces/tests/test_images.py index a013a7dcad0..3c2302219a8 100644 --- a/niworkflows/interfaces/tests/test_images.py +++ b/niworkflows/interfaces/tests/test_images.py @@ -58,13 +58,13 @@ def test_signal_extraction_equivalence(tmp_path, nvols, nmasks, ext, factor): se1 = nl.SignalExtraction( in_file=img_fname, label_files=masks_fname, - class_labels=["a%d" % i for i in range(nmasks)], + class_labels=[f"a{i}" for i in range(nmasks)], out_file=nlsignals, ) se2 = im.SignalExtraction( in_file=img_fname, label_files=masks_fname, - class_labels=["a%d" % i for i in range(nmasks)], + class_labels=[f"a{i}" for i in range(nmasks)], out_file=imsignals, ) diff --git a/niworkflows/interfaces/tests/test_itk.py b/niworkflows/interfaces/tests/test_itk.py index 7fd015f5b73..7811c3a3db2 100644 --- a/niworkflows/interfaces/tests/test_itk.py +++ b/niworkflows/interfaces/tests/test_itk.py @@ -52,7 +52,7 @@ def test_applytfms(tmpdir, ext, copy_dtype, in_dtype): args = (in_file, in_xform, ifargs, 0, str(tmpdir)) out_file, cmdline = _applytfms(args) - assert out_file == str(tmpdir / ("src_xform-%05d%s" % (0, ext))) + assert out_file == str(tmpdir / ("src_xform-{0:05d}{ext}")) out_nii = nb.load(out_file) assert np.allclose(nii.affine, out_nii.affine) diff --git a/niworkflows/interfaces/tests/test_utility.py b/niworkflows/interfaces/tests/test_utility.py index 0b18b55b2c2..3c66e8150e6 100644 --- a/niworkflows/interfaces/tests/test_utility.py +++ b/niworkflows/interfaces/tests/test_utility.py @@ -36,7 +36,7 @@ def test_KeySelect(): def test_tsv2json(tmp_path): - Path.write_bytes(tmp_path / 'empty.tsv', bytes()) + Path.write_bytes(tmp_path / 'empty.tsv', b'') res = _tsv2json(tmp_path / 'empty.tsv', None, 'any_column') assert res == {} res = _tsv2json(tmp_path / 'empty.tsv', None, 'any_column', additional_metadata={'a': 'b'}) diff --git a/niworkflows/interfaces/utility.py b/niworkflows/interfaces/utility.py index 6b9412dd129..a107008f74a 100644 --- a/niworkflows/interfaces/utility.py +++ b/niworkflows/interfaces/utility.py @@ -169,7 +169,8 @@ def __init__(self, keys=None, fields=None, **inputs): _invalid = set(self.input_spec.class_editable_traits()).intersection(fields) if _invalid: - raise ValueError("Some fields are invalid (%s)." % ", ".join(_invalid)) + _invalid = ", ".join(_invalid) + raise ValueError(f"Some fields are invalid ({_invalid}).") self._fields = fields @@ -196,7 +197,7 @@ def _check_len(self, name, new): return if name == "key" and new not in self.inputs.keys: - raise ValueError('Selected key "%s" not found in the index' % new) + raise ValueError(f'Selected key "{new}" not found in the index') if name in self._fields: if isinstance(new, str) or len(new) < 1: @@ -532,10 +533,10 @@ def _tsv2json( re_to_snake = r"(^.+?|.*?)((? @@ -548,7 +548,7 @@ def generate_reports( logger = logging.getLogger("cli") error_list = ", ".join( - "%s (%d)" % (subid, err) + f"{subid} ({err})" for subid, err in zip(subject_list, report_errors) if err ) diff --git a/niworkflows/tests/test_confounds.py b/niworkflows/tests/test_confounds.py index 0cd871a417f..5d000869f14 100644 --- a/niworkflows/tests/test_confounds.py +++ b/niworkflows/tests/test_confounds.py @@ -37,7 +37,7 @@ def _smoke_test_report(report_interface, artifact_name): save_artifacts = os.getenv("SAVE_CIRCLE_ARTIFACTS", False) if save_artifacts: copy(out_report, os.path.join(save_artifacts, artifact_name)) - assert os.path.isfile(out_report), 'Report "%s" does not exist' % out_report + assert os.path.isfile(out_report), f'Report "{out_report}" does not exist' def _expand_test(model_formula): diff --git a/niworkflows/tests/test_segmentation.py b/niworkflows/tests/test_segmentation.py index 8022452ae55..e8de6114210 100644 --- a/niworkflows/tests/test_segmentation.py +++ b/niworkflows/tests/test_segmentation.py @@ -47,7 +47,7 @@ def _smoke_test_report(report_interface, artifact_name): save_artifacts = os.getenv("SAVE_CIRCLE_ARTIFACTS", False) if save_artifacts: copy(out_report, os.path.join(save_artifacts, artifact_name)) - assert os.path.isfile(out_report), 'Report "%s" does not exist' % out_report + assert os.path.isfile(out_report), f'Report "{out_report}" does not exist' @pytest.mark.skipif(not has_fsl, reason="No FSL") @@ -129,7 +129,7 @@ def test_ROIsPlot2(tmp_path): for i in range(1, 5): seg = np.zeros_like(newdata, dtype="uint8") seg[(newdata > 0) & (newdata <= i)] = 1 - out_file = str(tmp_path / ("segments%02d.nii.gz" % i)) + out_file = str(tmp_path / (f"segments{i:02d}.nii.gz")) nb.Nifti1Image(seg, im.affine, hdr).to_filename(out_file) out_files.append(out_file) roi_rpt = ROIsPlot( diff --git a/niworkflows/tests/test_utils.py b/niworkflows/tests/test_utils.py index 5f69bfaa534..848c11558f0 100644 --- a/niworkflows/tests/test_utils.py +++ b/niworkflows/tests/test_utils.py @@ -88,6 +88,6 @@ def test_compression(tmp_path): size = int(os.stat(uncompressed).st_size) size_compress = int(os.stat(compressed).st_size) assert size >= size_compress, ( - "The uncompressed report is smaller (%d)" - "than the compressed report (%d)" % (size, size_compress) + f"The uncompressed report is smaller ({size})" + f"than the compressed report ({size_compress})" ) diff --git a/niworkflows/utils/images.py b/niworkflows/utils/images.py index c75a238b1b8..2f5499e33b3 100644 --- a/niworkflows/utils/images.py +++ b/niworkflows/utils/images.py @@ -95,7 +95,8 @@ def _copyxform(ref_image, out_image, message=None): header = resampled.header.copy() header.set_qform(qform, int(qform_code)) header.set_sform(sform, int(sform_code)) - header["descrip"] = "xform matrices modified by %s." % (message or "(unknown)") + message = message or "(unknown)" + header["descrip"] = f"xform matrices modified by {message}." newimg = resampled.__class__(resampled.dataobj, orig.affine, header) newimg.to_filename(out_image) diff --git a/niworkflows/utils/misc.py b/niworkflows/utils/misc.py index 3c0fc798db0..1186415ab0f 100644 --- a/niworkflows/utils/misc.py +++ b/niworkflows/utils/misc.py @@ -155,7 +155,7 @@ def fix_multi_T1w_source_name(in_files): base, in_file = os.path.split(in_file) subject_label = in_file.split("_", 1)[0].split("-")[1] - return os.path.join(base, "sub-%s_T1w.nii.gz" % subject_label) + return os.path.join(base, f"sub-{subject_label}_T1w.nii.gz") def add_suffix(in_files, suffix): diff --git a/niworkflows/utils/spaces.py b/niworkflows/utils/spaces.py index aec2337137f..e3bb4e9a8a7 100644 --- a/niworkflows/utils/spaces.py +++ b/niworkflows/utils/spaces.py @@ -171,24 +171,25 @@ def __attrs_post_init__(self): if self.space in self._standard_spaces: object.__setattr__(self, "standard", True) - _cohorts = ["%s" % t for t in _tfapi.TF_LAYOUT.get_cohorts(template=self.space)] + _cohorts = [f"{t}" for t in _tfapi.TF_LAYOUT.get_cohorts(template=self.space)] if "cohort" in self.spec: if not _cohorts: raise ValueError( - 'standard space "%s" does not accept a cohort ' - "specification." % self.space + f'standard space "{self.space}" does not accept a cohort ' + "specification." ) - if str(self.spec["cohort"]) not in _cohorts: + _cohort = str(self.spec["cohort"]) + if _cohort not in _cohorts: raise ValueError( - 'standard space "%s" does not contain any cohort ' - 'named "%s".' % (self.space, self.spec["cohort"]) + f'standard space "{self.space}" does not contain any cohort ' + f'named "{_cohort}".' ) elif _cohorts: - _cohorts = ", ".join(['"cohort-%s"' % c for c in _cohorts]) + _cohorts = ", ".join([f'"cohort-{c}"' for c in _cohorts]) raise ValueError( - 'standard space "%s" is not fully defined.\n' - "Set a valid cohort selector from: %s." % (self.space, _cohorts) + f'standard space "{self.space}" is not fully defined.\n' + f"Set a valid cohort selector from: {_cohorts}." ) @property @@ -207,8 +208,8 @@ def fullname(self): """ if "cohort" not in self.spec: return self.space - cohort = self.spec["cohort"] - return f"{self.space}:cohort-{self.spec['cohort']}" + _cohort = self.spec["cohort"] + return f"{self.space}:cohort-{_cohort}" @property def legacyname(self): @@ -245,8 +246,8 @@ def _check_name(self, attribute, value): valid = list(self._standard_spaces) + NONSTANDARD_REFERENCES if value not in valid: raise ValueError( - 'space identifier "%s" is invalid.\nValid ' - "identifiers are: %s" % (value, ", ".join(valid)) + f'space identifier "{value}" is invalid.\n' + f"Valid identifiers are: {', '.join(valid)}" ) def __str__(self): @@ -530,7 +531,7 @@ def __str__(self): """ spaces = ", ".join([str(s) for s in self.references]) or "." - return "Spatial References: %s" % spaces + return f"Spatial References: {spaces}" @property def references(self): @@ -564,14 +565,14 @@ def append(self, value): self._refs += [self.check_space(value)] return - raise ValueError('space "%s" already in spaces.' % str(value)) + raise ValueError(f'space "{value}" already in spaces.') def insert(self, index, value, error=True): """Concatenate one more space.""" if value not in self: self._refs.insert(index, self.check_space(value)) elif error is True: - raise ValueError('space "%s" already in spaces.' % str(value)) + raise ValueError(f'space "{value}" already in spaces.') def get_spaces(self, standard=True, nonstandard=True, dim=(2, 3)): """ diff --git a/niworkflows/viz/plots.py b/niworkflows/viz/plots.py index a2091bf1aab..d6f369fd75f 100644 --- a/niworkflows/viz/plots.py +++ b/niworkflows/viz/plots.py @@ -426,7 +426,7 @@ def spikesplot( ax.set_xlabel("time (frame #)") else: ax.set_xlabel("time (s)") - ax.set_xticklabels(["%.02f" % t for t in (tr * np.array(xticks)).tolist()]) + ax.set_xticklabels([f"{t:.02f}" for t in (tr * np.array(xticks)).tolist()]) # Handle Y axis ylabel = "slice-wise noise average on background" @@ -488,7 +488,7 @@ def spikesplot( # if yticks: # # ax.set_yticks(yticks) - # # ax.set_yticklabels(['%.02f' % y for y in yticks]) + # # ax.set_yticklabels([f'{y:.02f}' for y in yticks]) # # Plot maximum and minimum horizontal lines # ax.plot((0, ntsteps - 1), (yticks[0], yticks[0]), 'k:') # ax.plot((0, ntsteps - 1), (yticks[-1], yticks[-1]), 'k:') @@ -577,13 +577,13 @@ def confoundplot( else: ax_ts.set_xlabel("time (s)") labels = tr * np.array(xticks) - ax_ts.set_xticklabels(["%.02f" % t for t in labels.tolist()]) + ax_ts.set_xticklabels([f"{t:%.02f}" for t in labels.tolist()]) else: ax_ts.set_xticklabels([]) if name is not None: if units is not None: - name += " [%s]" % units + name += f" [{units}]" ax_ts.annotate( name, @@ -651,10 +651,11 @@ def confoundplot( stdv = 0 p95 = 0 + units = units or "" stats_label = ( - r"max: {max:.3f}{units} $\bullet$ mean: {mean:.3f}{units} " - r"$\bullet$ $\sigma$: {sigma:.3f}" - ).format(max=maxv, mean=mean, units=units or "", sigma=stdv) + fr"max: {maxv:.3f}{units} $\bullet$ mean: {mean:.3f}{units} " + fr"$\bullet$ $\sigma$: {stdv:.3f}" + ) ax_ts.annotate( stats_label, xy=(0.98, 0.7), @@ -678,7 +679,7 @@ def confoundplot( # Annotate percentile 95 ax_ts.plot((0, ntsteps - 1), [p95] * 2, linewidth=0.1, color="lightgray") ax_ts.annotate( - "%.2f" % p95, + f"{p95:%.2f}", xy=(0, p95), xytext=(-1, 0), textcoords="offset points", @@ -695,7 +696,7 @@ def confoundplot( ax_ts.plot((0, ntsteps - 1), [thr] * 2, linewidth=0.2, color="dimgray") ax_ts.annotate( - "%.2f" % thr, + f"{thr:.2f}", xy=(0, thr), xytext=(-1, 0), textcoords="offset points", @@ -822,14 +823,14 @@ def compcor_variance_plot( ax[m].text( 0, 100 * thr, - "{:.0f}".format(100 * thr), + f"{100 * thr:.0f}", fontsize="x-small", bbox=bbox_txt, ) ax[m].text( varexp[thr][0], 25, - "{} components explain\n{:.0f}% of variance".format(varexp[thr][0], 100 * thr), + f"{varexp[thr][0]} components explain\n{100 * thr:.0f}% of variance", rotation=90, horizontalalignment="center", fontsize="xx-small",