Skip to content
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
16 changes: 4 additions & 12 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,14 @@ repos:
rev: v0.7.0
hooks:
- id: ruff
args:
- --fix

- repo: https://github.com/asottile/pyupgrade
rev: v3.19.0
hooks:
- id: pyupgrade
args: [--py39-plus, --keep-runtime-typing]
name: Upgrade code with exceptions
args: ["--fix"]
exclude: |
(?x)(
^versioneer.py|
^monai/_version.py|
^monai/networks/| # avoid typing rewrites
^monai/apps/detection/utils/anchor_utils.py| # avoid typing rewrites
^tests/test_compute_panoptic_quality.py # avoid typing rewrites
^monai/networks/| # todo: avoid typing rewrites
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These do not appear to be correct changes for these comments.

^monai/apps/detection/utils/anchor_utils.py| # todo: avoid typing rewrites
^tests/test_compute_panoptic_quality.py # todo: avoid typing rewrites
)

- repo: https://github.com/asottile/yesqa
Expand Down
22 changes: 6 additions & 16 deletions monai/apps/deepgrow/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,9 @@ def _save_data_2d(vol_idx, vol_image, vol_label, dataset_dir, relative_path):
logging.warning(f"Unique labels {unique_labels_count} exceeds 20. Please check if this is correct.")

logging.info(
"{} => Image Shape: {} => {}; Label Shape: {} => {}; Unique Labels: {}".format(
vol_idx,
vol_image.shape,
image_count,
vol_label.shape if vol_label is not None else None,
label_count,
unique_labels_count,
)
f"{vol_idx} => Image Shape: {vol_image.shape} => {image_count};"
f" Label Shape: {vol_label.shape if vol_label is not None else None} => {label_count};"
f" Unique Labels: {unique_labels_count}"
)
return data_list

Expand Down Expand Up @@ -259,13 +254,8 @@ def _save_data_3d(vol_idx, vol_image, vol_label, dataset_dir, relative_path):
logging.warning(f"Unique labels {unique_labels_count} exceeds 20. Please check if this is correct.")

logging.info(
"{} => Image Shape: {} => {}; Label Shape: {} => {}; Unique Labels: {}".format(
vol_idx,
vol_image.shape,
image_count,
vol_label.shape if vol_label is not None else None,
label_count,
unique_labels_count,
)
f"{vol_idx} => Image Shape: {vol_image.shape} => {image_count};"
f" Label Shape: {vol_label.shape if vol_label is not None else None} => {label_count};"
f" Unique Labels: {unique_labels_count}"
)
return data_list
18 changes: 9 additions & 9 deletions monai/apps/nnunet/nnunet_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import os
import shutil
from pathlib import Path
from typing import Any, Optional, Union
from typing import Any

import numpy as np
import torch
Expand All @@ -36,17 +36,17 @@


def get_nnunet_trainer(
dataset_name_or_id: Union[str, int],
dataset_name_or_id: str | int,
configuration: str,
fold: Union[int, str],
fold: int | str,
trainer_class_name: str = "nnUNetTrainer",
plans_identifier: str = "nnUNetPlans",
use_compressed_data: bool = False,
continue_training: bool = False,
only_run_validation: bool = False,
disable_checkpointing: bool = False,
device: str = "cuda",
pretrained_model: Optional[str] = None,
pretrained_model: str | None = None,
) -> Any: # type: ignore
"""
Get the nnUNet trainer instance based on the provided configuration.
Expand Down Expand Up @@ -166,7 +166,7 @@ class ModelnnUNetWrapper(torch.nn.Module):
restoring network architecture, and setting up the predictor for inference.
"""

def __init__(self, predictor: object, model_folder: Union[str, Path], model_name: str = "model.pt"): # type: ignore
def __init__(self, predictor: object, model_folder: str | Path, model_name: str = "model.pt"): # type: ignore
super().__init__()
self.predictor = predictor

Expand Down Expand Up @@ -294,7 +294,7 @@ def forward(self, x: MetaTensor) -> MetaTensor:
return MetaTensor(out_tensor, meta=x.meta)


def get_nnunet_monai_predictor(model_folder: Union[str, Path], model_name: str = "model.pt") -> ModelnnUNetWrapper:
def get_nnunet_monai_predictor(model_folder: str | Path, model_name: str = "model.pt") -> ModelnnUNetWrapper:
"""
Initializes and returns a `nnUNetMONAIModelWrapper` containing the corresponding `nnUNetPredictor`.
The model folder should contain the following files, created during training:
Expand Down Expand Up @@ -426,9 +426,9 @@ def get_network_from_nnunet_plans(
plans_file: str,
dataset_file: str,
configuration: str,
model_ckpt: Optional[str] = None,
model_ckpt: str | None = None,
model_key_in_ckpt: str = "model",
) -> Union[torch.nn.Module, Any]:
) -> torch.nn.Module | Any:
"""
Load and initialize a nnUNet network based on nnUNet plans and configuration.

Expand Down Expand Up @@ -518,7 +518,7 @@ def convert_monai_bundle_to_nnunet(nnunet_config: dict, bundle_root_folder: str,
from nnunetv2.utilities.dataset_name_id_conversion import maybe_convert_to_dataset_name

def subfiles(
folder: Union[str, Path], prefix: Optional[str] = None, suffix: Optional[str] = None, sort: bool = True
folder: str | Path, prefix: str | None = None, suffix: str | None = None, sort: bool = True
) -> list[str]:
res = [
i.name
Expand Down
4 changes: 2 additions & 2 deletions monai/handlers/stats_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ def _default_iteration_print(self, engine: Engine) -> None:
"ignoring non-scalar output in StatsHandler,"
" make sure `output_transform(engine.state.output)` returns"
" a scalar or dictionary of key and scalar pairs to avoid this warning."
" {}:{}".format(name, type(value))
f" {name}:{type(value)}"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: Missing space after colon in f-string. Should be f" {name}: {type(value)}"

)
continue # not printing multi dimensional output
out_str += self.key_var_format.format(name, value.item() if isinstance(value, torch.Tensor) else value)
Expand All @@ -273,7 +273,7 @@ def _default_iteration_print(self, engine: Engine) -> None:
"ignoring non-scalar output in StatsHandler,"
" make sure `output_transform(engine.state.output)` returns"
" a scalar or a dictionary of key and scalar pairs to avoid this warning."
" {}".format(type(loss))
f" {type(loss)}"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: Missing space after colon in f-string. Should be f" {type(loss)}"

)

if not out_str:
Expand Down
4 changes: 2 additions & 2 deletions monai/handlers/tensorboard_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def _default_iteration_writer(self, engine: Engine, writer: SummaryWriter | Summ
"ignoring non-scalar output in TensorBoardStatsHandler,"
" make sure `output_transform(engine.state.output)` returns"
" a scalar or dictionary of key and scalar pairs to avoid this warning."
" {}:{}".format(name, type(value))
f" {name}:{type(value)}"
)
continue # not plot multi dimensional output
self._write_scalar(
Expand All @@ -280,7 +280,7 @@ def _default_iteration_writer(self, engine: Engine, writer: SummaryWriter | Summ
"ignoring non-scalar output in TensorBoardStatsHandler,"
" make sure `output_transform(engine.state.output)` returns"
" a scalar or a dictionary of key and scalar pairs to avoid this warning."
" {}".format(type(loss))
f" {type(loss)}"
)
writer.flush()

Expand Down
3 changes: 1 addition & 2 deletions monai/losses/adversarial_loss.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ def __init__(

if criterion.lower() not in list(AdversarialCriterions):
raise ValueError(
"Unrecognised criterion entered for Adversarial Loss. Must be one in: %s"
% ", ".join(AdversarialCriterions)
"Unrecognised criterion entered for Adversarial Loss. Must be one in: {}".format(", ".join(AdversarialCriterions))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using .format() is less idiomatic than f-strings in modern Python. Consider f"Unrecognised criterion entered for Adversarial Loss. Must be one in: {', '.join(AdversarialCriterions)}"

)

# Depending on the criterion, a different activation layer is used.
Expand Down
2 changes: 1 addition & 1 deletion monai/losses/dice.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ def __init__(
raise ValueError(f"dist_matrix must be C x C, got {dist_matrix.shape[0]} x {dist_matrix.shape[1]}.")

if weighting_mode not in ["default", "GDL"]:
raise ValueError("weighting_mode must be either 'default' or 'GDL, got %s." % weighting_mode)
raise ValueError(f"weighting_mode must be either 'default' or 'GDL', got {weighting_mode}.")

self.m = dist_matrix
if isinstance(self.m, np.ndarray):
Expand Down
3 changes: 1 addition & 2 deletions monai/losses/ds_loss.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

from __future__ import annotations

from typing import Union

import torch
import torch.nn.functional as F
Expand Down Expand Up @@ -70,7 +69,7 @@ def get_loss(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor:
target = F.interpolate(target, size=input.shape[2:], mode=self.interp_mode)
return self.loss(input, target) # type: ignore[no-any-return]

def forward(self, input: Union[None, torch.Tensor, list[torch.Tensor]], target: torch.Tensor) -> torch.Tensor:
def forward(self, input: None | torch.Tensor | list[torch.Tensor], target: torch.Tensor) -> torch.Tensor:
if isinstance(input, (list, tuple)):
weights = self.get_weights(levels=len(input))
loss = torch.tensor(0, dtype=torch.float, device=target.device)
Expand Down
7 changes: 3 additions & 4 deletions monai/losses/focal_loss.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

import warnings
from collections.abc import Sequence
from typing import Optional

import torch
import torch.nn.functional as F
Expand Down Expand Up @@ -153,7 +152,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor:
if target.shape != input.shape:
raise ValueError(f"ground truth has different shape ({target.shape}) from input ({input.shape})")

loss: Optional[torch.Tensor] = None
loss: torch.Tensor | None = None
input = input.float()
target = target.float()
if self.use_softmax:
Expand Down Expand Up @@ -203,7 +202,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor:


def softmax_focal_loss(
input: torch.Tensor, target: torch.Tensor, gamma: float = 2.0, alpha: Optional[float] = None
input: torch.Tensor, target: torch.Tensor, gamma: float = 2.0, alpha: float | None = None
) -> torch.Tensor:
"""
FL(pt) = -alpha * (1 - pt)**gamma * log(pt)
Expand All @@ -225,7 +224,7 @@ def softmax_focal_loss(


def sigmoid_focal_loss(
input: torch.Tensor, target: torch.Tensor, gamma: float = 2.0, alpha: Optional[float] = None
input: torch.Tensor, target: torch.Tensor, gamma: float = 2.0, alpha: float | None = None
) -> torch.Tensor:
"""
FL(pt) = -alpha * (1 - pt)**gamma * log(pt)
Expand Down
3 changes: 1 addition & 2 deletions monai/losses/perceptual.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ def __init__(

if network_type.lower() not in list(PercetualNetworkType):
raise ValueError(
"Unrecognised criterion entered for Adversarial Loss. Must be one in: %s"
% ", ".join(PercetualNetworkType)
"Unrecognised criterion entered for Adversarial Loss. Must be one in: {}".format(", ".join(PercetualNetworkType))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: Error message says "Adversarial Loss" but this is the PerceptualLoss class. Copy-paste error from adversarial_loss.py?

)

if cache_dir:
Expand Down
4 changes: 2 additions & 2 deletions monai/losses/spatial_mask.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import inspect
import warnings
from collections.abc import Callable
from typing import Any, Optional
from typing import Any

import torch
from torch.nn.modules.loss import _Loss
Expand Down Expand Up @@ -47,7 +47,7 @@ def __init__(
if not callable(self.loss):
raise ValueError("The loss function is not callable.")

def forward(self, input: torch.Tensor, target: torch.Tensor, mask: Optional[torch.Tensor] = None) -> torch.Tensor:
def forward(self, input: torch.Tensor, target: torch.Tensor, mask: torch.Tensor | None = None) -> torch.Tensor:
"""
Args:
input: the shape should be BNH[WD].
Expand Down
16 changes: 8 additions & 8 deletions monai/losses/sure_loss.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from __future__ import annotations

from typing import Callable, Optional
from typing import Callable

import torch
import torch.nn as nn
Expand Down Expand Up @@ -42,10 +42,10 @@ def sure_loss_function(
operator: Callable,
x: torch.Tensor,
y_pseudo_gt: torch.Tensor,
y_ref: Optional[torch.Tensor] = None,
eps: Optional[float] = -1.0,
perturb_noise: Optional[torch.Tensor] = None,
complex_input: Optional[bool] = False,
y_ref: torch.Tensor | None = None,
eps: float | None = -1.0,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: eps defaults to -1.0 which is a sentinel value, so None as the type hint is misleading. Consider using float = -1.0 instead of float | None = -1.0. Is -1.0 the only sentinel value, or can callers explicitly pass None to trigger special behavior?

perturb_noise: torch.Tensor | None = None,
complex_input: bool | None = False,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: complex_input defaults to False (a bool), so the type hint should be bool = False not bool | None = False. The parameter is never checked for None in the function body

) -> torch.Tensor:
"""
Args:
Expand Down Expand Up @@ -131,7 +131,7 @@ class SURELoss(_Loss):
(https://arxiv.org/pdf/2310.01799.pdf)
"""

def __init__(self, perturb_noise: Optional[torch.Tensor] = None, eps: Optional[float] = None) -> None:
def __init__(self, perturb_noise: torch.Tensor | None = None, eps: float | None = None) -> None:
"""
Args:
perturb_noise (torch.Tensor, optional): The noise vector of shape
Expand All @@ -149,8 +149,8 @@ def forward(
operator: Callable,
x: torch.Tensor,
y_pseudo_gt: torch.Tensor,
y_ref: Optional[torch.Tensor] = None,
complex_input: Optional[bool] = False,
y_ref: torch.Tensor | None = None,
complex_input: bool | None = False,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: same issue: complex_input defaults to False but is typed as bool | None. Should be bool = False

) -> torch.Tensor:
"""
Args:
Expand Down
4 changes: 2 additions & 2 deletions monai/metrics/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import warnings
from collections.abc import Iterable, Sequence
from functools import lru_cache, partial
from functools import partial, cache
from types import ModuleType
from typing import Any

Expand Down Expand Up @@ -465,7 +465,7 @@ def prepare_spacing(
ENCODING_KERNEL = {2: [[8, 4], [2, 1]], 3: [[[128, 64], [32, 16]], [[8, 4], [2, 1]]]}


@lru_cache(maxsize=None)
@cache
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The decorator @cache does not accept any arguments, so the parameter device=None is not being cached as part of the key. This means calls with different devices will return the same cached tensor on the wrong device, causing errors. Either use @lru_cache(maxsize=None) with a hashable device wrapper, or accept that the cache is device-agnostic and convert the result after retrieval. Should the cache be keyed by device, or is it acceptable to convert the cached tensor to the requested device after retrieval?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a valid complaint that should be looked at.

def _get_neighbour_code_to_normals_table(device=None):
"""
returns a lookup table. For every binary neighbour code (2x2x2 neighbourhood = 8 neighbours = 8 bits = 256 codes)
Expand Down
6 changes: 3 additions & 3 deletions monai/transforms/utility/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from collections.abc import Hashable, Mapping, Sequence
from copy import deepcopy
from functools import partial
from typing import Any, Callable, Union
from typing import Any, Callable

import numpy as np
import torch
Expand Down Expand Up @@ -1216,7 +1216,7 @@ def __init__(self, name: str, *args, **kwargs) -> None:
transform, _ = optional_import("torchio.transforms", "0.18.0", min_version, name=name)
self.trans = transform(*args, **kwargs)

def __call__(self, img: Union[NdarrayOrTensor, Mapping[Hashable, NdarrayOrTensor]]):
def __call__(self, img: NdarrayOrTensor | Mapping[Hashable, NdarrayOrTensor]):
"""
Args:
img: an instance of torchio.Subject, torchio.Image, numpy.ndarray, torch.Tensor, SimpleITK.Image,
Expand Down Expand Up @@ -1248,7 +1248,7 @@ def __init__(self, name: str, *args, **kwargs) -> None:
transform, _ = optional_import("torchio.transforms", "0.18.0", min_version, name=name)
self.trans = transform(*args, **kwargs)

def __call__(self, img: Union[NdarrayOrTensor, Mapping[Hashable, NdarrayOrTensor]]):
def __call__(self, img: NdarrayOrTensor | Mapping[Hashable, NdarrayOrTensor]):
"""
Args:
img: an instance of torchio.Subject, torchio.Image, numpy.ndarray, torch.Tensor, SimpleITK.Image,
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ target-version = "py39"
select = [
"E", "F", "W", # flake8
"NPY", # NumPy specific rules
"UP", # pyupgrade
]
extend-ignore = [
"E741", # ambiguous variable name
Expand Down
5 changes: 2 additions & 3 deletions tests/profile_subclass/profiling.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,8 @@ def main():

b_min, b_avg, b_med, b_std = bench(tensor_1, tensor_2)
print(
"Type {} time (microseconds): min: {}, avg: {}, median: {}, and std {}.".format(
t.__name__, (10**6 * b_min), (10**6) * b_avg, (10**6) * b_med, (10**6) * b_std
)
f"Type {t.__name__} time (microseconds):"
f" min: {10**6 * b_min}, avg: {(10**6) * b_avg}, median: {(10**6) * b_med}, and std {(10**6) * b_std}."
Comment on lines +66 to +67
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: f-string split across lines without proper string concatenation or parentheses. Python will treat these as two separate expressions and only print the first line. Lines 66-67 need either parentheses around both strings or explicit concatenation.

Suggested change
f"Type {t.__name__} time (microseconds):"
f" min: {10**6 * b_min}, avg: {(10**6) * b_avg}, median: {(10**6) * b_med}, and std {(10**6) * b_std}."
f"Type {t.__name__} time (microseconds):"
f" min: {10**6 * b_min}, avg: {(10**6) * b_avg}, median: {(10**6) * b_med}, and std {(10**6) * b_std}."

)


Expand Down
Loading