From fdd07f36ecb91cfcd491533f4792e1a67a9f89fc Mon Sep 17 00:00:00 2001 From: Felix Schnabel Date: Tue, 10 Jan 2023 19:14:08 +0100 Subject: [PATCH] Consistent usage of annotation syntax (#5836) Fixes #5835. ### Description Use `from __future__ import annotations` in all source files using annotations, so we have consistent usage (and not only in ~5 files). The new annotations are also more readable e.g. `Optional[Union[List[str], str]]` vs. `list[str] | str | None`. Secondly, this issue includes changing `from typing import Callable, Sequence, ...` to `from collections.abc import Callable, Sequence, ...` since the ones in the `typing` module are deprecated. They are interchangeable even for `isinstance` checks (see also [this stackoverflow thread](https://stackoverflow.com/questions/62547848/should-isinstance-check-against-typing-or-collections-abc)). ### Types of changes - [x] Non-breaking change (fix or new feature that would not break existing functionality). - [x] Quick tests passed locally by running `./runtests.sh --quick --unittests --disttests`. Signed-off-by: Felix Schnabel --- monai/__init__.py | 2 + monai/_extensions/__init__.py | 2 + monai/_extensions/loader.py | 7 +- monai/apps/__init__.py | 2 + monai/apps/auto3dseg/__init__.py | 2 + monai/apps/auto3dseg/__main__.py | 2 + monai/apps/auto3dseg/auto_runner.py | 36 +- monai/apps/auto3dseg/bundle_gen.py | 19 +- monai/apps/auto3dseg/data_analyzer.py | 22 +- monai/apps/auto3dseg/ensemble_builder.py | 19 +- monai/apps/auto3dseg/hpo_gen.py | 8 +- monai/apps/auto3dseg/transforms.py | 6 +- monai/apps/auto3dseg/utils.py | 9 +- monai/apps/datasets.py | 36 +- monai/apps/deepedit/interaction.py | 10 +- monai/apps/deepedit/transforms.py | 60 ++-- monai/apps/deepgrow/dataset.py | 9 +- monai/apps/deepgrow/interaction.py | 9 +- monai/apps/deepgrow/transforms.py | 45 +-- monai/apps/detection/metrics/coco.py | 52 +-- monai/apps/detection/metrics/matching.py | 14 +- .../detection/networks/retinanet_detector.py | 77 ++-- .../detection/networks/retinanet_network.py | 17 +- monai/apps/detection/transforms/array.py | 50 +-- monai/apps/detection/transforms/box_ops.py | 28 +- monai/apps/detection/transforms/dictionary.py | 111 +++--- monai/apps/detection/utils/ATSS_matcher.py | 11 +- monai/apps/detection/utils/anchor_utils.py | 22 +- monai/apps/detection/utils/box_coder.py | 10 +- monai/apps/detection/utils/box_selector.py | 10 +- monai/apps/detection/utils/detector_utils.py | 28 +- .../detection/utils/hard_negative_sampler.py | 11 +- monai/apps/detection/utils/predict_utils.py | 14 +- monai/apps/mmars/__init__.py | 2 + monai/apps/mmars/mmars.py | 12 +- monai/apps/mmars/model_desc.py | 6 +- monai/apps/nuclick/transforms.py | 8 +- monai/apps/pathology/__init__.py | 2 + monai/apps/pathology/data/__init__.py | 2 + monai/apps/pathology/data/datasets.py | 45 +-- monai/apps/pathology/engines/__init__.py | 2 + monai/apps/pathology/engines/utils.py | 8 +- monai/apps/pathology/handlers/__init__.py | 2 + .../pathology/handlers/prob_map_producer.py | 16 +- monai/apps/pathology/handlers/utils.py | 7 +- monai/apps/pathology/inferers/__init__.py | 2 + monai/apps/pathology/inferers/inferer.py | 27 +- monai/apps/pathology/losses/__init__.py | 2 + monai/apps/pathology/losses/hovernet_loss.py | 4 +- monai/apps/pathology/metrics/__init__.py | 2 + monai/apps/pathology/metrics/lesion_froc.py | 10 +- monai/apps/pathology/transforms/__init__.py | 2 + .../pathology/transforms/post/__init__.py | 2 + monai/apps/pathology/transforms/post/array.py | 59 ++-- .../pathology/transforms/post/dictionary.py | 52 +-- .../pathology/transforms/spatial/__init__.py | 2 + .../pathology/transforms/spatial/array.py | 12 +- .../transforms/spatial/dictionary.py | 17 +- .../pathology/transforms/stain/__init__.py | 2 + .../apps/pathology/transforms/stain/array.py | 12 +- .../pathology/transforms/stain/dictionary.py | 14 +- monai/apps/pathology/utils.py | 6 +- monai/apps/reconstruction/complex_utils.py | 7 +- monai/apps/reconstruction/fastmri_reader.py | 12 +- monai/apps/reconstruction/mri_utils.py | 2 + .../networks/blocks/varnetblock.py | 2 + .../networks/nets/coil_sensitivity_model.py | 14 +- .../networks/nets/complex_unet.py | 12 +- .../reconstruction/networks/nets/utils.py | 13 +- .../reconstruction/networks/nets/varnet.py | 2 + monai/apps/reconstruction/transforms/array.py | 4 +- .../reconstruction/transforms/dictionary.py | 24 +- monai/apps/tcia/__init__.py | 2 + monai/apps/tcia/label_desc.py | 4 +- monai/apps/tcia/utils.py | 7 +- monai/apps/utils.py | 18 +- monai/auto3dseg/__init__.py | 2 + monai/auto3dseg/algo_gen.py | 2 + monai/auto3dseg/analyzer.py | 47 +-- monai/auto3dseg/operations.py | 2 + monai/auto3dseg/seg_summarizer.py | 18 +- monai/auto3dseg/utils.py | 24 +- monai/bundle/__init__.py | 2 + monai/bundle/__main__.py | 2 + monai/bundle/config_item.py | 31 +- monai/bundle/config_parser.py | 31 +- monai/bundle/reference_resolver.py | 27 +- monai/bundle/scripts.py | 124 +++---- monai/bundle/utils.py | 2 + monai/config/__init__.py | 2 + monai/config/deviceconfig.py | 2 + monai/config/type_definitions.py | 2 + monai/data/__init__.py | 2 + monai/data/box_utils.py | 60 ++-- monai/data/csv_saver.py | 7 +- monai/data/dataloader.py | 2 + monai/data/dataset.py | 101 +++--- monai/data/dataset_summary.py | 11 +- monai/data/decathlon_datalist.py | 29 +- monai/data/fft_utils.py | 2 + monai/data/folder_layout.py | 2 + monai/data/grid_dataset.py | 12 +- monai/data/image_dataset.py | 19 +- monai/data/image_reader.py | 143 ++++---- monai/data/image_writer.py | 55 +-- monai/data/iterable_dataset.py | 29 +- monai/data/nifti_saver.py | 6 +- monai/data/nifti_writer.py | 10 +- monai/data/png_saver.py | 8 +- monai/data/png_writer.py | 8 +- monai/data/samplers.py | 16 +- monai/data/synthetic.py | 14 +- monai/data/test_time_augmentation.py | 17 +- monai/data/thread_buffer.py | 5 +- monai/data/torchscript_utils.py | 17 +- monai/data/utils.py | 91 ++--- monai/data/video_dataset.py | 15 +- monai/data/wsi_datasets.py | 50 +-- monai/data/wsi_reader.py | 87 ++--- monai/engines/__init__.py | 2 + monai/engines/multi_gpu_supervised_trainer.py | 14 +- monai/engines/utils.py | 35 +- monai/engines/workflow.py | 35 +- monai/fl/client/__init__.py | 2 + monai/fl/client/client_algo.py | 16 +- monai/fl/client/monai_algo.py | 61 ++-- monai/fl/utils/constants.py | 2 + monai/fl/utils/exchange_object.py | 10 +- monai/fl/utils/filters.py | 2 + monai/handlers/__init__.py | 2 + monai/handlers/checkpoint_loader.py | 12 +- monai/handlers/checkpoint_saver.py | 29 +- monai/handlers/classification_saver.py | 13 +- monai/handlers/confusion_matrix.py | 6 +- monai/handlers/decollate_batch.py | 8 +- monai/handlers/earlystop_handler.py | 7 +- monai/handlers/garbage_collector.py | 2 + monai/handlers/hausdorff_distance.py | 8 +- monai/handlers/ignite_metric.py | 11 +- monai/handlers/logfile_handler.py | 8 +- monai/handlers/lr_schedule_handler.py | 9 +- monai/handlers/mean_dice.py | 6 +- monai/handlers/mean_iou.py | 6 +- monai/handlers/metric_logger.py | 11 +- monai/handlers/metrics_saver.py | 13 +- monai/handlers/mlflow_handler.py | 27 +- monai/handlers/nvtx_handlers.py | 20 +- monai/handlers/panoptic_quality.py | 6 +- monai/handlers/parameter_scheduler.py | 13 +- monai/handlers/postprocessing.py | 5 +- monai/handlers/probability_maps.py | 10 +- monai/handlers/regression_metrics.py | 14 +- monai/handlers/roc_auc.py | 6 +- monai/handlers/smartcache_handler.py | 2 + monai/handlers/stats_handler.py | 13 +- monai/handlers/surface_distance.py | 6 +- monai/handlers/tensorboard_handlers.py | 11 +- monai/handlers/utils.py | 13 +- monai/handlers/validation_handler.py | 6 +- monai/inferers/__init__.py | 2 + monai/inferers/inferer.py | 35 +- monai/inferers/utils.py | 31 +- monai/losses/__init__.py | 2 + monai/losses/contrastive.py | 2 + monai/losses/deform.py | 4 +- monai/losses/dice.py | 40 ++- monai/losses/ds_loss.py | 8 +- monai/losses/focal_loss.py | 12 +- monai/losses/giou_loss.py | 4 +- monai/losses/image_dissimilarity.py | 13 +- monai/losses/multi_scale.py | 6 +- monai/losses/spatial_mask.py | 8 +- monai/losses/ssim_loss.py | 2 + monai/losses/tversky.py | 10 +- monai/losses/unified_focal_loss.py | 9 +- monai/metrics/__init__.py | 2 + monai/metrics/active_learning_metrics.py | 2 + monai/metrics/confusion_matrix.py | 12 +- monai/metrics/cumulative_average.py | 6 +- monai/metrics/f_beta_score.py | 6 +- monai/metrics/froc.py | 19 +- monai/metrics/generalized_dice.py | 13 +- monai/metrics/hausdorff_distance.py | 17 +- monai/metrics/loss_metric.py | 6 +- monai/metrics/meandice.py | 6 +- monai/metrics/meaniou.py | 6 +- monai/metrics/metric.py | 16 +- monai/metrics/panoptic_quality.py | 20 +- monai/metrics/regression.py | 33 +- monai/metrics/rocauc.py | 10 +- monai/metrics/surface_dice.py | 11 +- monai/metrics/surface_distance.py | 11 +- monai/metrics/utils.py | 9 +- monai/networks/__init__.py | 2 + monai/networks/blocks/__init__.py | 2 + monai/networks/blocks/acti_norm.py | 14 +- monai/networks/blocks/activation.py | 2 + monai/networks/blocks/aspp.py | 8 +- monai/networks/blocks/backbone_fpn_utils.py | 18 +- monai/networks/blocks/convolutions.py | 38 +- monai/networks/blocks/crf.py | 4 +- monai/networks/blocks/denseblock.py | 14 +- monai/networks/blocks/dints_block.py | 18 +- monai/networks/blocks/downsample.py | 10 +- monai/networks/blocks/dynunet_block.py | 56 +-- monai/networks/blocks/encoder.py | 11 +- monai/networks/blocks/fcn.py | 12 +- .../blocks/feature_pyramid_network.py | 22 +- monai/networks/blocks/fft_utils_t.py | 8 +- monai/networks/blocks/localnet_block.py | 24 +- monai/networks/blocks/mlp.py | 9 +- monai/networks/blocks/patchembedding.py | 12 +- monai/networks/blocks/regunet_block.py | 30 +- monai/networks/blocks/segresnet_block.py | 8 +- monai/networks/blocks/selfattention.py | 2 + .../networks/blocks/squeeze_and_excitation.py | 31 +- monai/networks/blocks/transformerblock.py | 2 + monai/networks/blocks/unetr_block.py | 24 +- monai/networks/blocks/upsample.py | 26 +- monai/networks/blocks/warp.py | 5 +- monai/networks/layers/__init__.py | 2 + monai/networks/layers/convutils.py | 24 +- monai/networks/layers/drop_path.py | 2 + monai/networks/layers/factories.py | 43 ++- monai/networks/layers/filtering.py | 2 + monai/networks/layers/gmm.py | 2 + monai/networks/layers/simplelayers.py | 30 +- monai/networks/layers/spatial_transforms.py | 10 +- monai/networks/layers/utils.py | 10 +- monai/networks/layers/weight_init.py | 2 + monai/networks/nets/__init__.py | 2 + monai/networks/nets/ahnet.py | 48 +-- monai/networks/nets/attentionunet.py | 8 +- monai/networks/nets/autoencoder.py | 25 +- monai/networks/nets/basic_unet.py | 36 +- monai/networks/nets/basic_unetplusplus.py | 10 +- monai/networks/nets/classifier.py | 18 +- monai/networks/nets/densenet.py | 26 +- monai/networks/nets/dints.py | 49 +-- monai/networks/nets/dynunet.py | 30 +- monai/networks/nets/efficientnet.py | 58 +-- monai/networks/nets/flexible_unet.py | 29 +- monai/networks/nets/fullyconnectednet.py | 24 +- monai/networks/nets/generator.py | 12 +- monai/networks/nets/highresnet.py | 22 +- monai/networks/nets/hovernet.py | 44 +-- monai/networks/nets/milmodel.py | 12 +- monai/networks/nets/netadapter.py | 10 +- monai/networks/nets/regressor.py | 12 +- monai/networks/nets/regunet.py | 37 +- monai/networks/nets/resnet.py | 37 +- monai/networks/nets/segresnet.py | 24 +- monai/networks/nets/segresnet_ds.py | 44 +-- monai/networks/nets/senet.py | 31 +- monai/networks/nets/swin_unetr.py | 18 +- monai/networks/nets/torchvision_fc.py | 8 +- monai/networks/nets/transchex.py | 14 +- monai/networks/nets/unet.py | 16 +- monai/networks/nets/unetr.py | 8 +- monai/networks/nets/varautoencoder.py | 22 +- monai/networks/nets/vit.py | 8 +- monai/networks/nets/vitautoenc.py | 8 +- monai/networks/nets/vnet.py | 46 +-- monai/networks/utils.py | 40 ++- monai/optimizers/__init__.py | 2 + monai/optimizers/lr_finder.py | 26 +- monai/optimizers/lr_scheduler.py | 2 + monai/optimizers/novograd.py | 8 +- monai/optimizers/utils.py | 4 +- monai/transforms/__init__.py | 2 + monai/transforms/adaptors.py | 2 + monai/transforms/compose.py | 17 +- monai/transforms/croppad/array.py | 166 +++++---- monai/transforms/croppad/batch.py | 9 +- monai/transforms/croppad/dictionary.py | 133 ++++--- monai/transforms/intensity/array.py | 160 ++++----- monai/transforms/intensity/dictionary.py | 234 ++++++------- monai/transforms/inverse.py | 9 +- monai/transforms/inverse_batch_transform.py | 17 +- monai/transforms/io/array.py | 22 +- monai/transforms/io/dictionary.py | 21 +- monai/transforms/lazy/functional.py | 4 +- monai/transforms/lazy/utils.py | 4 +- monai/transforms/meta_utility/dictionary.py | 14 +- monai/transforms/nvtx.py | 2 + monai/transforms/post/array.py | 70 ++-- monai/transforms/post/dictionary.py | 91 ++--- monai/transforms/signal/array.py | 12 +- monai/transforms/smooth_field/array.py | 59 ++-- monai/transforms/smooth_field/dictionary.py | 43 +-- monai/transforms/spatial/array.py | 331 +++++++++--------- monai/transforms/spatial/dictionary.py | 310 ++++++++-------- monai/transforms/transform.py | 25 +- monai/transforms/utility/array.py | 115 +++--- monai/transforms/utility/dictionary.py | 169 ++++----- monai/transforms/utils.py | 149 ++++---- .../transforms/utils_create_transform_ims.py | 2 + .../utils_pytorch_numpy_unification.py | 31 +- monai/utils/__init__.py | 2 + monai/utils/aliases.py | 2 + monai/utils/decorators.py | 2 + monai/utils/deprecate_utils.py | 19 +- monai/utils/dist.py | 14 +- monai/utils/enums.py | 9 +- monai/utils/jupyter_utils.py | 33 +- monai/utils/misc.py | 44 +-- monai/utils/module.py | 15 +- monai/utils/nvtx.py | 10 +- monai/utils/profiling.py | 8 +- monai/utils/state_cacher.py | 9 +- monai/utils/type_conversion.py | 31 +- monai/visualize/__init__.py | 2 + monai/visualize/class_activation_maps.py | 19 +- monai/visualize/img2tensorboard.py | 16 +- monai/visualize/occlusion_sensitivity.py | 37 +- monai/visualize/utils.py | 10 +- monai/visualize/visualizer.py | 4 +- setup.cfg | 2 + setup.py | 2 + tests/__init__.py | 2 + tests/clang_format_utils.py | 2 + tests/croppers.py | 2 + tests/hvd_evenly_divisible_all_gather.py | 2 + tests/min_tests.py | 2 + tests/ngc_bundle_download.py | 2 + tests/padders.py | 5 +- tests/profile_subclass/cprofile_profiling.py | 2 + tests/profile_subclass/min_classes.py | 2 + tests/profile_subclass/profiling.py | 2 + tests/profile_subclass/pyspy_profiling.py | 2 + tests/runner.py | 2 + tests/test_acn_block.py | 2 + tests/test_activations.py | 2 + tests/test_activationsd.py | 2 + tests/test_adaptors.py | 2 + tests/test_add_channeld.py | 2 + tests/test_add_coordinate_channels.py | 2 + tests/test_add_coordinate_channelsd.py | 2 + tests/test_add_extreme_points_channel.py | 2 + tests/test_add_extreme_points_channeld.py | 2 + tests/test_adjust_contrast.py | 2 + tests/test_adjust_contrastd.py | 2 + tests/test_adn.py | 2 + tests/test_affine.py | 2 + tests/test_affine_grid.py | 2 + tests/test_affine_transform.py | 2 + tests/test_affined.py | 2 + tests/test_ahnet.py | 2 + tests/test_alias.py | 2 + tests/test_anchor_box.py | 2 + tests/test_apply.py | 2 + tests/test_apply_filter.py | 2 + tests/test_arraydataset.py | 2 + tests/test_as_channel_first.py | 2 + tests/test_as_channel_firstd.py | 2 + tests/test_as_channel_last.py | 2 + tests/test_as_channel_lastd.py | 2 + tests/test_as_discrete.py | 2 + tests/test_as_discreted.py | 2 + tests/test_atss_box_matcher.py | 2 + tests/test_attentionunet.py | 2 + tests/test_auto3dseg.py | 2 + tests/test_auto3dseg_ensemble.py | 5 +- tests/test_auto3dseg_hpo.py | 5 +- tests/test_autoencoder.py | 2 + tests/test_basic_unet.py | 2 + tests/test_basic_unetplusplus.py | 2 + tests/test_bending_energy.py | 2 + tests/test_bilateral_approx_cpu.py | 2 + tests/test_bilateral_approx_cuda.py | 2 + tests/test_bilateral_precise.py | 2 + tests/test_blend_images.py | 2 + tests/test_border_pad.py | 2 + tests/test_border_padd.py | 2 + tests/test_bounding_rect.py | 2 + tests/test_bounding_rectd.py | 2 + tests/test_box_coder.py | 2 + tests/test_box_transform.py | 2 + tests/test_box_utils.py | 2 + tests/test_bundle_ckpt_export.py | 2 + tests/test_bundle_download.py | 2 + tests/test_bundle_get_data.py | 2 + tests/test_bundle_init_bundle.py | 2 + tests/test_bundle_utils.py | 2 + tests/test_bundle_verify_metadata.py | 2 + tests/test_bundle_verify_net.py | 2 + tests/test_cachedataset.py | 2 + tests/test_cachedataset_parallel.py | 2 + tests/test_cachedataset_persistent_workers.py | 2 + tests/test_cachentransdataset.py | 2 + tests/test_call_dist.py | 2 + tests/test_cast_to_type.py | 2 + tests/test_cast_to_typed.py | 2 + tests/test_center_scale_crop.py | 2 + tests/test_center_scale_cropd.py | 2 + tests/test_center_spatial_crop.py | 2 + tests/test_center_spatial_cropd.py | 2 + tests/test_channel_pad.py | 2 + tests/test_check_hash.py | 2 + tests/test_check_missing_files.py | 2 + tests/test_classes_to_indices.py | 2 + tests/test_classes_to_indicesd.py | 2 + tests/test_complex_utils.py | 2 + tests/test_component_locator.py | 2 + tests/test_compose.py | 2 + tests/test_compose_get_number_conversions.py | 5 +- tests/test_compute_confusion_matrix.py | 14 +- tests/test_compute_f_beta.py | 2 + tests/test_compute_froc.py | 2 + tests/test_compute_generalized_dice.py | 2 + tests/test_compute_ho_ver_maps.py | 2 + tests/test_compute_ho_ver_maps_d.py | 2 + tests/test_compute_meandice.py | 2 + tests/test_compute_meaniou.py | 2 + tests/test_compute_panoptic_quality.py | 2 + tests/test_compute_regression_metrics.py | 2 + tests/test_compute_roc_auc.py | 2 + tests/test_compute_variance.py | 2 + tests/test_concat_itemsd.py | 2 + tests/test_config_item.py | 2 + tests/test_config_parser.py | 2 + tests/test_contrastive_loss.py | 2 + tests/test_convert_data_type.py | 7 +- tests/test_convert_to_multi_channel.py | 2 + tests/test_convert_to_multi_channeld.py | 2 + tests/test_convert_to_torchscript.py | 2 + tests/test_convolutions.py | 2 + tests/test_copy_itemsd.py | 2 + tests/test_copy_model_state.py | 2 + tests/test_correct_crop_centers.py | 2 + .../test_create_cross_validation_datalist.py | 2 + tests/test_create_grid_and_affine.py | 2 + tests/test_crf_cpu.py | 2 + tests/test_crf_cuda.py | 2 + tests/test_crop_foreground.py | 2 + tests/test_crop_foregroundd.py | 2 + tests/test_cross_validation.py | 2 + tests/test_csv_dataset.py | 2 + tests/test_csv_iterable_dataset.py | 2 + tests/test_csv_saver.py | 2 + tests/test_cucim_dict_transform.py | 2 + tests/test_cucim_transform.py | 2 + tests/test_cumulative.py | 2 + tests/test_cumulative_average.py | 2 + tests/test_cumulative_average_dist.py | 2 + tests/test_cv2_dist.py | 2 + tests/test_data_stats.py | 2 + tests/test_data_statsd.py | 2 + tests/test_dataloader.py | 2 + tests/test_dataset.py | 2 + tests/test_dataset_func.py | 2 + tests/test_dataset_summary.py | 2 + tests/test_decathlondataset.py | 2 + tests/test_decollate.py | 7 +- tests/test_deepedit_interaction.py | 2 + tests/test_deepedit_transforms.py | 2 + tests/test_deepgrow_dataset.py | 2 + tests/test_deepgrow_interaction.py | 2 + tests/test_deepgrow_transforms.py | 2 + tests/test_delete_itemsd.py | 2 + tests/test_denseblock.py | 2 + tests/test_densenet.py | 2 + tests/test_deprecated.py | 2 + tests/test_detect_envelope.py | 2 + tests/test_detection_coco_metrics.py | 2 + tests/test_detector_boxselector.py | 2 + tests/test_detector_utils.py | 2 + tests/test_dev_collate.py | 2 + tests/test_dice_ce_loss.py | 2 + tests/test_dice_focal_loss.py | 2 + tests/test_dice_loss.py | 2 + tests/test_dints_cell.py | 2 + tests/test_dints_mixop.py | 2 + tests/test_dints_network.py | 2 + tests/test_discriminator.py | 2 + tests/test_divisible_pad.py | 2 + tests/test_divisible_padd.py | 2 + tests/test_download_and_extract.py | 2 + tests/test_downsample_block.py | 2 + tests/test_drop_path.py | 2 + tests/test_ds_loss.py | 2 + tests/test_dvf2ddf.py | 2 + tests/test_dynunet.py | 6 +- tests/test_dynunet_block.py | 2 + tests/test_efficientnet.py | 2 + tests/test_ensemble_evaluator.py | 2 + tests/test_ensure_channel_first.py | 2 + tests/test_ensure_channel_firstd.py | 2 + tests/test_ensure_tuple.py | 2 + tests/test_ensure_type.py | 2 + tests/test_ensure_typed.py | 2 + tests/test_enum_bound_interp.py | 2 + tests/test_eval_mode.py | 2 + .../test_evenly_divisible_all_gather_dist.py | 2 + tests/test_factorized_increase.py | 2 + tests/test_factorized_reduce.py | 2 + tests/test_fastmri_reader.py | 2 + tests/test_fft_utils.py | 2 + tests/test_fg_bg_to_indices.py | 2 + tests/test_fg_bg_to_indicesd.py | 2 + tests/test_file_basename.py | 2 + tests/test_fill_holes.py | 2 + tests/test_fill_holesd.py | 2 + tests/test_fl_exchange_object.py | 2 + tests/test_fl_monai_algo.py | 2 + tests/test_fl_monai_algo_dist.py | 2 + tests/test_fl_monai_algo_stats.py | 2 + tests/test_flatten_sub_keysd.py | 2 + tests/test_flexible_unet.py | 7 +- tests/test_flip.py | 2 + tests/test_flipd.py | 2 + tests/test_focal_loss.py | 2 + tests/test_folder_layout.py | 2 + tests/test_foreground_mask.py | 2 + tests/test_foreground_maskd.py | 2 + tests/test_fourier.py | 2 + tests/test_fpn_block.py | 2 + tests/test_from_engine_hovernet.py | 2 + tests/test_fullyconnectednet.py | 2 + tests/test_gaussian.py | 2 + tests/test_gaussian_filter.py | 2 + tests/test_gaussian_sharpen.py | 2 + tests/test_gaussian_sharpend.py | 2 + tests/test_gaussian_smooth.py | 2 + tests/test_gaussian_smoothd.py | 2 + tests/test_generalized_dice_focal_loss.py | 2 + tests/test_generalized_dice_loss.py | 2 + .../test_generalized_wasserstein_dice_loss.py | 2 + tests/test_generate_distance_map.py | 2 + tests/test_generate_distance_mapd.py | 2 + tests/test_generate_instance_border.py | 2 + tests/test_generate_instance_borderd.py | 2 + tests/test_generate_instance_centroid.py | 2 + tests/test_generate_instance_centroidd.py | 2 + tests/test_generate_instance_contour.py | 2 + tests/test_generate_instance_contourd.py | 2 + tests/test_generate_instance_type.py | 2 + tests/test_generate_instance_typed.py | 2 + ...est_generate_label_classes_crop_centers.py | 2 + tests/test_generate_param_groups.py | 2 + ...est_generate_pos_neg_label_crop_centers.py | 2 + tests/test_generate_spatial_bounding_box.py | 2 + tests/test_generate_succinct_contour.py | 2 + tests/test_generate_succinct_contourd.py | 2 + tests/test_generate_watershed_markers.py | 2 + tests/test_generate_watershed_markersd.py | 2 + tests/test_generate_watershed_mask.py | 2 + tests/test_generate_watershed_maskd.py | 2 + tests/test_generator.py | 2 + tests/test_get_equivalent_dtype.py | 2 + tests/test_get_extreme_points.py | 2 + tests/test_get_layers.py | 2 + tests/test_get_package_version.py | 2 + tests/test_get_unique_labels.py | 2 + tests/test_gibbs_noise.py | 2 + tests/test_gibbs_noised.py | 2 + tests/test_giou_loss.py | 2 + tests/test_global_mutual_information_loss.py | 2 + tests/test_globalnet.py | 2 + tests/test_gmm.py | 2 + tests/test_grid_dataset.py | 2 + tests/test_grid_distortion.py | 2 + tests/test_grid_distortiond.py | 2 + tests/test_grid_patch.py | 2 + tests/test_grid_patchd.py | 2 + tests/test_grid_pull.py | 2 + tests/test_grid_split.py | 2 + tests/test_grid_splitd.py | 2 + tests/test_handler_checkpoint_loader.py | 2 + tests/test_handler_checkpoint_saver.py | 2 + tests/test_handler_classification_saver.py | 2 + .../test_handler_classification_saver_dist.py | 2 + tests/test_handler_confusion_matrix.py | 8 +- tests/test_handler_confusion_matrix_dist.py | 2 + tests/test_handler_decollate_batch.py | 2 + tests/test_handler_early_stop.py | 2 + tests/test_handler_garbage_collector.py | 2 + tests/test_handler_hausdorff_distance.py | 5 +- tests/test_handler_logfile.py | 2 + tests/test_handler_lr_scheduler.py | 2 + tests/test_handler_mean_dice.py | 2 + tests/test_handler_mean_iou.py | 2 + tests/test_handler_metric_logger.py | 2 + tests/test_handler_metrics_saver.py | 2 + tests/test_handler_metrics_saver_dist.py | 2 + tests/test_handler_mlflow.py | 2 + tests/test_handler_nvtx.py | 2 + tests/test_handler_panoptic_quality.py | 2 + tests/test_handler_parameter_scheduler.py | 2 + tests/test_handler_post_processing.py | 2 + tests/test_handler_prob_map_producer.py | 2 + tests/test_handler_regression_metrics.py | 2 + tests/test_handler_regression_metrics_dist.py | 2 + tests/test_handler_rocauc.py | 2 + tests/test_handler_rocauc_dist.py | 2 + tests/test_handler_smartcache.py | 2 + tests/test_handler_stats.py | 2 + tests/test_handler_surface_distance.py | 5 +- tests/test_handler_tb_image.py | 2 + tests/test_handler_tb_stats.py | 2 + tests/test_handler_validation.py | 2 + tests/test_hardnegsampler.py | 2 + tests/test_hashing.py | 2 + tests/test_hausdorff_distance.py | 5 +- tests/test_header_correct.py | 2 + tests/test_highresnet.py | 2 + tests/test_hilbert_transform.py | 2 + tests/test_histogram_normalize.py | 2 + tests/test_histogram_normalized.py | 2 + tests/test_hovernet.py | 2 + ...t_hovernet_instance_map_post_processing.py | 2 + ..._hovernet_instance_map_post_processingd.py | 2 + tests/test_hovernet_loss.py | 2 + ...t_hovernet_nuclear_type_post_processing.py | 2 + ..._hovernet_nuclear_type_post_processingd.py | 2 + tests/test_identity.py | 2 + tests/test_identityd.py | 2 + tests/test_image_dataset.py | 2 + tests/test_image_filter.py | 2 + tests/test_image_rw.py | 2 + tests/test_img2tensorboard.py | 2 + tests/test_init_reader.py | 2 + tests/test_integration_autorunner.py | 5 +- tests/test_integration_bundle_run.py | 2 + tests/test_integration_classification_2d.py | 2 + tests/test_integration_determinism.py | 2 + tests/test_integration_fast_train.py | 2 + tests/test_integration_gpu_customization.py | 5 +- tests/test_integration_segmentation_3d.py | 2 + tests/test_integration_sliding_window.py | 2 + tests/test_integration_stn.py | 2 + tests/test_integration_unet_2d.py | 2 + tests/test_integration_workers.py | 2 + tests/test_integration_workflows.py | 2 + tests/test_integration_workflows_gan.py | 2 + tests/test_intensity_stats.py | 2 + tests/test_intensity_statsd.py | 2 + tests/test_inverse.py | 6 +- tests/test_inverse_array.py | 2 + tests/test_inverse_collation.py | 2 + tests/test_invert.py | 2 + tests/test_invertd.py | 2 + tests/test_is_supported_format.py | 2 + tests/test_iterable_dataset.py | 2 + tests/test_itk_writer.py | 2 + tests/test_k_space_spike_noise.py | 2 + tests/test_k_space_spike_noised.py | 2 + .../test_keep_largest_connected_component.py | 2 + .../test_keep_largest_connected_componentd.py | 2 + tests/test_kspace_mask.py | 2 + tests/test_label_filter.py | 2 + tests/test_label_filterd.py | 2 + tests/test_label_quality_score.py | 2 + tests/test_label_to_contour.py | 2 + tests/test_label_to_contourd.py | 2 + tests/test_label_to_mask.py | 2 + tests/test_label_to_maskd.py | 2 + tests/test_lambda.py | 2 + tests/test_lambdad.py | 2 + tests/test_lesion_froc.py | 2 + tests/test_list_data_collate.py | 2 + tests/test_list_to_dict.py | 2 + tests/test_lltm.py | 2 + tests/test_lmdbdataset.py | 2 + tests/test_lmdbdataset_dist.py | 2 + tests/test_load_decathlon_datalist.py | 2 + tests/test_load_image.py | 2 + tests/test_load_imaged.py | 2 + tests/test_load_spacing_orientation.py | 2 + tests/test_loader_semaphore.py | 2 + ...local_normalized_cross_correlation_loss.py | 2 + tests/test_localnet.py | 2 + tests/test_localnet_block.py | 2 + tests/test_look_up_option.py | 2 + tests/test_loss_metric.py | 2 + tests/test_lr_finder.py | 2 + tests/test_lr_scheduler.py | 2 + tests/test_make_nifti.py | 2 + tests/test_map_binary_to_indices.py | 2 + tests/test_map_classes_to_indices.py | 2 + tests/test_map_label_value.py | 2 + tests/test_map_label_valued.py | 2 + tests/test_map_transform.py | 2 + tests/test_mask_intensity.py | 2 + tests/test_mask_intensityd.py | 2 + tests/test_masked_dice_loss.py | 2 + tests/test_masked_inference_wsi_dataset.py | 2 + tests/test_masked_loss.py | 2 + tests/test_masked_patch_wsi_dataset.py | 2 + tests/test_matshow3d.py | 2 + tests/test_mean_ensemble.py | 2 + tests/test_mean_ensembled.py | 2 + tests/test_median_filter.py | 2 + tests/test_median_smooth.py | 2 + tests/test_median_smoothd.py | 2 + tests/test_mednistdataset.py | 2 + tests/test_meta_affine.py | 2 + tests/test_meta_tensor.py | 5 +- tests/test_metatensor_integration.py | 2 + tests/test_milmodel.py | 2 + tests/test_mlp.py | 2 + tests/test_mmar_download.py | 2 + tests/test_module_list.py | 2 + tests/test_monai_env_vars.py | 2 + tests/test_mri_utils.py | 2 + tests/test_multi_scale.py | 2 + tests/test_net_adapter.py | 2 + tests/test_network_consistency.py | 2 + tests/test_nifti_endianness.py | 6 +- tests/test_nifti_header_revise.py | 2 + tests/test_nifti_rw.py | 2 + tests/test_nifti_saver.py | 2 + tests/test_normalize_intensity.py | 2 + tests/test_normalize_intensityd.py | 2 + tests/test_npzdictitemdataset.py | 2 + tests/test_nrrd_reader.py | 2 + tests/test_nuclick_transforms.py | 2 + tests/test_numpy_reader.py | 2 + tests/test_nvtx_decorator.py | 2 + tests/test_nvtx_transform.py | 2 + tests/test_occlusion_sensitivity.py | 8 +- tests/test_one_of.py | 2 + tests/test_optim_novograd.py | 2 + tests/test_optional_import.py | 2 + tests/test_ori_ras_lps.py | 2 + tests/test_orientation.py | 2 + tests/test_orientationd.py | 6 +- tests/test_p3d_block.py | 2 + tests/test_pad_collation.py | 5 +- tests/test_pad_mode.py | 2 + tests/test_parallel_execution.py | 2 + tests/test_parallel_execution_dist.py | 2 + tests/test_partition_dataset.py | 2 + tests/test_partition_dataset_classes.py | 2 + tests/test_patch_dataset.py | 2 + tests/test_patch_wsi_dataset.py | 2 + tests/test_patchembedding.py | 2 + tests/test_pathology_he_stain.py | 2 + tests/test_pathology_he_stain_dict.py | 2 + tests/test_pathology_prob_nms.py | 2 + tests/test_persistentdataset.py | 2 + tests/test_persistentdataset_dist.py | 2 + tests/test_phl_cpu.py | 2 + tests/test_phl_cuda.py | 2 + tests/test_pil_reader.py | 2 + tests/test_plot_2d_or_3d_image.py | 2 + tests/test_png_rw.py | 2 + tests/test_png_saver.py | 2 + tests/test_polyval.py | 2 + tests/test_prepare_batch_default.py | 2 + tests/test_prepare_batch_default_dist.py | 2 + tests/test_prepare_batch_extra_input.py | 2 + tests/test_prepare_batch_hovernet.py | 2 + tests/test_preset_filters.py | 2 + tests/test_print_info.py | 2 + tests/test_print_transform_backends.py | 2 + tests/test_probnms.py | 2 + tests/test_probnmsd.py | 6 +- tests/test_profiling.py | 2 + tests/test_pytorch_version_after.py | 2 + tests/test_query_memory.py | 2 + tests/test_rand_adjust_contrast.py | 2 + tests/test_rand_adjust_contrastd.py | 2 + tests/test_rand_affine.py | 2 + tests/test_rand_affine_grid.py | 2 + tests/test_rand_affined.py | 2 + tests/test_rand_axis_flip.py | 2 + tests/test_rand_axis_flipd.py | 2 + tests/test_rand_bias_field.py | 2 + tests/test_rand_bias_fieldd.py | 2 + tests/test_rand_coarse_dropout.py | 2 + tests/test_rand_coarse_dropoutd.py | 2 + tests/test_rand_coarse_shuffle.py | 2 + tests/test_rand_coarse_shuffled.py | 2 + tests/test_rand_crop_by_label_classes.py | 2 + tests/test_rand_crop_by_label_classesd.py | 2 + tests/test_rand_crop_by_pos_neg_label.py | 2 + tests/test_rand_crop_by_pos_neg_labeld.py | 2 + tests/test_rand_cucim_dict_transform.py | 2 + tests/test_rand_cucim_transform.py | 2 + tests/test_rand_deform_grid.py | 2 + tests/test_rand_elastic_2d.py | 2 + tests/test_rand_elastic_3d.py | 2 + tests/test_rand_elasticd_2d.py | 2 + tests/test_rand_elasticd_3d.py | 2 + tests/test_rand_flip.py | 2 + tests/test_rand_flipd.py | 2 + tests/test_rand_gaussian_noise.py | 2 + tests/test_rand_gaussian_noised.py | 2 + tests/test_rand_gaussian_sharpen.py | 2 + tests/test_rand_gaussian_sharpend.py | 2 + tests/test_rand_gaussian_smooth.py | 2 + tests/test_rand_gaussian_smoothd.py | 2 + tests/test_rand_gibbs_noise.py | 2 + tests/test_rand_gibbs_noised.py | 2 + tests/test_rand_grid_distortion.py | 2 + tests/test_rand_grid_distortiond.py | 2 + tests/test_rand_grid_patch.py | 2 + tests/test_rand_grid_patchd.py | 2 + tests/test_rand_histogram_shift.py | 2 + tests/test_rand_histogram_shiftd.py | 2 + tests/test_rand_k_space_spike_noise.py | 2 + tests/test_rand_k_space_spike_noised.py | 2 + tests/test_rand_lambda.py | 2 + tests/test_rand_lambdad.py | 2 + tests/test_rand_rician_noise.py | 2 + tests/test_rand_rician_noised.py | 2 + tests/test_rand_rotate.py | 7 +- tests/test_rand_rotate90.py | 2 + tests/test_rand_rotate90d.py | 2 + tests/test_rand_rotated.py | 7 +- tests/test_rand_scale_crop.py | 2 + tests/test_rand_scale_cropd.py | 2 + tests/test_rand_scale_intensity.py | 2 + tests/test_rand_scale_intensityd.py | 2 + tests/test_rand_shift_intensity.py | 2 + tests/test_rand_shift_intensityd.py | 2 + tests/test_rand_spatial_crop.py | 2 + tests/test_rand_spatial_crop_samples.py | 2 + tests/test_rand_spatial_crop_samplesd.py | 2 + tests/test_rand_spatial_cropd.py | 2 + tests/test_rand_std_shift_intensity.py | 2 + tests/test_rand_std_shift_intensityd.py | 2 + tests/test_rand_weighted_crop.py | 2 + tests/test_rand_weighted_cropd.py | 2 + tests/test_rand_zoom.py | 2 + tests/test_rand_zoomd.py | 2 + tests/test_random_order.py | 2 + tests/test_randomizable.py | 2 + tests/test_randomizable_transform_type.py | 2 + tests/test_randtorchvisiond.py | 2 + tests/test_recon_net_utils.py | 2 + ...est_reference_based_normalize_intensity.py | 2 + tests/test_reference_based_spatial_cropd.py | 2 + tests/test_reference_resolver.py | 2 + tests/test_reg_loss_integration.py | 2 + tests/test_regunet.py | 2 + tests/test_regunet_block.py | 2 + tests/test_remove_repeated_channel.py | 2 + tests/test_remove_repeated_channeld.py | 2 + tests/test_remove_small_objects.py | 5 +- tests/test_repeat_channel.py | 2 + tests/test_repeat_channeld.py | 2 + tests/test_replace_module.py | 5 +- tests/test_require_pkg.py | 2 + tests/test_resample.py | 2 + tests/test_resample_backends.py | 2 + tests/test_resample_datalist.py | 2 + tests/test_resample_to_match.py | 2 + tests/test_resample_to_matchd.py | 2 + tests/test_resampler.py | 2 + tests/test_resize.py | 2 + tests/test_resize_with_pad_or_crop.py | 2 + tests/test_resize_with_pad_or_cropd.py | 2 + tests/test_resized.py | 2 + tests/test_resnet.py | 2 + tests/test_retinanet.py | 2 + tests/test_retinanet_detector.py | 2 + tests/test_retinanet_predict_utils.py | 2 + tests/test_rotate.py | 9 +- tests/test_rotate90.py | 2 + tests/test_rotate90d.py | 2 + tests/test_rotated.py | 7 +- tests/test_safe_dtype_range.py | 7 +- tests/test_saliency_inferer.py | 2 + tests/test_sample_slices.py | 2 + tests/test_sampler_dist.py | 2 + tests/test_save_classificationd.py | 2 + tests/test_save_image.py | 2 + tests/test_save_imaged.py | 2 + tests/test_save_state.py | 2 + tests/test_savitzky_golay_filter.py | 2 + tests/test_savitzky_golay_smooth.py | 2 + tests/test_savitzky_golay_smoothd.py | 2 + tests/test_scale_intensity.py | 2 + tests/test_scale_intensity_range.py | 2 + .../test_scale_intensity_range_percentiles.py | 2 + ...test_scale_intensity_range_percentilesd.py | 2 + tests/test_scale_intensity_ranged.py | 2 + tests/test_scale_intensityd.py | 2 + tests/test_se_block.py | 2 + tests/test_se_blocks.py | 2 + tests/test_seg_loss_integration.py | 2 + tests/test_segresnet.py | 2 + tests/test_segresnet_block.py | 2 + tests/test_segresnet_ds.py | 2 + tests/test_select_cross_validation_folds.py | 2 + tests/test_select_itemsd.py | 2 + tests/test_selfattention.py | 2 + tests/test_senet.py | 2 + tests/test_separable_filter.py | 2 + tests/test_set_determinism.py | 2 + tests/test_set_visible_devices.py | 2 + tests/test_shift_intensity.py | 2 + tests/test_shift_intensityd.py | 2 + tests/test_shuffle_buffer.py | 2 + tests/test_signal_continuouswavelet.py | 2 + tests/test_signal_fillempty.py | 2 + tests/test_signal_rand_add_gaussiannoise.py | 2 + tests/test_signal_rand_add_sine.py | 2 + tests/test_signal_rand_add_sine_partial.py | 2 + tests/test_signal_rand_add_squarepulse.py | 2 + ...est_signal_rand_add_squarepulse_partial.py | 2 + tests/test_signal_rand_drop.py | 2 + tests/test_signal_rand_scale.py | 2 + tests/test_signal_rand_shift.py | 2 + tests/test_signal_remove_frequency.py | 2 + tests/test_simple_aspp.py | 2 + tests/test_simulatedelay.py | 2 + tests/test_simulatedelayd.py | 2 + tests/test_skip_connection.py | 2 + tests/test_slice_inferer.py | 2 + tests/test_sliding_patch_wsi_dataset.py | 2 + .../test_sliding_window_hovernet_inference.py | 2 + tests/test_sliding_window_inference.py | 2 + tests/test_smartcache_patch_wsi_dataset.py | 2 + tests/test_smartcachedataset.py | 2 + tests/test_smooth_field.py | 2 + tests/test_sobel_gradient.py | 2 + tests/test_sobel_gradientd.py | 2 + tests/test_spacing.py | 9 +- tests/test_spacingd.py | 5 +- tests/test_spatial_crop.py | 2 + tests/test_spatial_cropd.py | 2 + tests/test_spatial_pad.py | 2 + tests/test_spatial_padd.py | 2 + tests/test_spatial_resample.py | 2 + tests/test_spatial_resampled.py | 2 + tests/test_split_channel.py | 2 + tests/test_split_channeld.py | 2 + tests/test_split_on_grid.py | 2 + tests/test_split_on_grid_dict.py | 2 + tests/test_splitdim.py | 2 + tests/test_splitdimd.py | 2 + tests/test_squeezedim.py | 2 + tests/test_squeezedimd.py | 2 + tests/test_ssim_loss.py | 2 + tests/test_ssim_metric.py | 2 + tests/test_state_cacher.py | 2 + tests/test_std_shift_intensity.py | 2 + tests/test_std_shift_intensityd.py | 2 + tests/test_str2bool.py | 2 + tests/test_str2list.py | 2 + tests/test_subpixel_upsample.py | 2 + tests/test_surface_dice.py | 2 + tests/test_surface_distance.py | 5 +- tests/test_swin_unetr.py | 2 + tests/test_synthetic.py | 2 + tests/test_tciadataset.py | 2 + tests/test_testtimeaugmentation.py | 2 + tests/test_thread_buffer.py | 2 + tests/test_threadcontainer.py | 2 + tests/test_threshold_intensity.py | 2 + tests/test_threshold_intensityd.py | 2 + tests/test_tile_on_grid.py | 5 +- tests/test_tile_on_grid_dict.py | 5 +- tests/test_timedcall_dist.py | 2 + tests/test_to_contiguous.py | 2 + tests/test_to_cupy.py | 2 + tests/test_to_cupyd.py | 2 + tests/test_to_device.py | 2 + tests/test_to_deviced.py | 2 + tests/test_to_from_meta_tensord.py | 5 +- tests/test_to_numpy.py | 2 + tests/test_to_numpyd.py | 2 + tests/test_to_onehot.py | 2 + tests/test_to_pil.py | 2 + tests/test_to_pild.py | 2 + tests/test_to_tensor.py | 2 + tests/test_to_tensord.py | 2 + tests/test_torchscript_utils.py | 2 + tests/test_torchvision.py | 2 + tests/test_torchvision_fc_model.py | 2 + tests/test_torchvisiond.py | 2 + tests/test_traceable_transform.py | 2 + tests/test_train_mode.py | 2 + tests/test_transchex.py | 2 + tests/test_transform.py | 2 + tests/test_transformerblock.py | 2 + tests/test_transpose.py | 2 + tests/test_transposed.py | 2 + tests/test_tversky_loss.py | 2 + tests/test_unet.py | 2 + tests/test_unetr.py | 2 + tests/test_unetr_block.py | 2 + tests/test_unified_focal_loss.py | 2 + tests/test_upsample_block.py | 2 + tests/test_utils_pytorch_numpy_unification.py | 2 + tests/test_varautoencoder.py | 2 + tests/test_varnet.py | 2 + tests/test_version_leq.py | 2 + tests/test_video_datasets.py | 7 +- tests/test_vis_cam.py | 2 + tests/test_vis_gradbased.py | 2 + tests/test_vis_gradcam.py | 8 +- tests/test_vit.py | 2 + tests/test_vitautoenc.py | 2 + tests/test_vnet.py | 2 + tests/test_vote_ensemble.py | 2 + tests/test_vote_ensembled.py | 2 + tests/test_warp.py | 2 + tests/test_watershed.py | 2 + tests/test_watershedd.py | 2 + tests/test_weight_init.py | 2 + tests/test_weighted_random_sampler_dist.py | 2 + tests/test_with_allow_missing_keys.py | 2 + tests/test_write_metrics_reports.py | 2 + tests/test_wsireader.py | 10 +- tests/test_zipdataset.py | 2 + tests/test_zoom.py | 2 + tests/test_zoom_affine.py | 2 + tests/test_zoomd.py | 2 + tests/testing_data/cpp_resample_answers.py | 11 +- tests/testing_data/integration_answers.py | 2 + tests/utils.py | 30 +- 1015 files changed, 5277 insertions(+), 3483 deletions(-) diff --git a/monai/__init__.py b/monai/__init__.py index 3f6c06d82d..62ca00c5c5 100644 --- a/monai/__init__.py +++ b/monai/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import sys diff --git a/monai/_extensions/__init__.py b/monai/_extensions/__init__.py index fd32d71840..47d0c7021a 100644 --- a/monai/_extensions/__init__.py +++ b/monai/_extensions/__init__.py @@ -9,4 +9,6 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .loader import load_module diff --git a/monai/_extensions/loader.py b/monai/_extensions/loader.py index 35b050ba56..186eadd3e9 100644 --- a/monai/_extensions/loader.py +++ b/monai/_extensions/loader.py @@ -9,13 +9,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import platform from _thread import interrupt_main from contextlib import contextmanager from glob import glob from os import path from threading import Timer -from typing import Optional import torch @@ -44,9 +45,7 @@ def timeout(time, message): pass -def load_module( - module_name: str, defines: Optional[dict] = None, verbose_build: bool = False, build_timeout: int = 300 -): +def load_module(module_name: str, defines: dict | None = None, verbose_build: bool = False, build_timeout: int = 300): """ Handles the loading of c++ extension modules. diff --git a/monai/apps/__init__.py b/monai/apps/__init__.py index 3df0e95a98..9cc7aeb8e0 100644 --- a/monai/apps/__init__.py +++ b/monai/apps/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .datasets import CrossValidation, DecathlonDataset, MedNISTDataset, TciaDataset from .mmars import MODEL_DESC, RemoteMMARKeys, download_mmar, get_model_spec, load_from_mmar from .utils import SUPPORTED_HASH_TYPES, check_hash, download_and_extract, download_url, extractall, get_logger, logger diff --git a/monai/apps/auto3dseg/__init__.py b/monai/apps/auto3dseg/__init__.py index 7c335f4850..a90c626da9 100644 --- a/monai/apps/auto3dseg/__init__.py +++ b/monai/apps/auto3dseg/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .auto_runner import AutoRunner from .bundle_gen import BundleAlgo, BundleGen from .data_analyzer import DataAnalyzer diff --git a/monai/apps/auto3dseg/__main__.py b/monai/apps/auto3dseg/__main__.py index eec56b7582..d169467ba9 100644 --- a/monai/apps/auto3dseg/__main__.py +++ b/monai/apps/auto3dseg/__main__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from monai.apps.auto3dseg.auto_runner import AutoRunner from monai.apps.auto3dseg.bundle_gen import BundleAlgo, BundleGen from monai.apps.auto3dseg.data_analyzer import DataAnalyzer diff --git a/monai/apps/auto3dseg/auto_runner.py b/monai/apps/auto3dseg/auto_runner.py index 2ba10b9833..2d83b0690f 100644 --- a/monai/apps/auto3dseg/auto_runner.py +++ b/monai/apps/auto3dseg/auto_runner.py @@ -9,12 +9,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import subprocess from copy import deepcopy from time import sleep -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, cast import numpy as np import torch @@ -204,21 +206,21 @@ class AutoRunner: """ - analyze_params: Optional[Dict] + analyze_params: dict | None def __init__( self, work_dir: str = "./work_dir", - input: Union[Dict[str, Any], str, None] = None, - algos: Optional[Union[Dict, List, str]] = None, - analyze: Optional[bool] = None, - algo_gen: Optional[bool] = None, - train: Optional[bool] = None, + input: dict[str, Any] | str | None = None, + algos: dict | list | str | None = None, + analyze: bool | None = None, + algo_gen: bool | None = None, + train: bool | None = None, hpo: bool = False, hpo_backend: str = "nni", ensemble: bool = True, not_use_cache: bool = False, - templates_path_or_url: Optional[str] = None, + templates_path_or_url: str | None = None, **kwargs, ): @@ -234,7 +236,7 @@ def __init__( input = self.data_src_cfg_name logger.info(f"Input config is not provided, using the default {input}") - if isinstance(input, Dict): + if isinstance(input, dict): self.data_src_cfg = input ConfigParser.export_config_file( config=input, filepath=self.data_src_cfg_name, fmt="yaml", default_flow_style=None, sort_keys=False @@ -279,14 +281,14 @@ def __init__( self.set_num_fold(num_fold=self.num_fold) self.gpu_customization = False - self.gpu_customization_specs: Dict[str, Any] = {} + self.gpu_customization_specs: dict[str, Any] = {} # hpo if hpo_backend.lower() != "nni": raise NotImplementedError("HPOGen backend only supports NNI") self.hpo = hpo and has_nni self.set_hpo_params() - self.search_space: Dict[str, Dict[str, Any]] = {} + self.search_space: dict[str, dict[str, Any]] = {} self.hpo_tasks = 0 def read_cache(self): @@ -336,7 +338,7 @@ def export_cache(self, **kwargs): ) def set_gpu_customization( - self, gpu_customization: bool = False, gpu_customization_specs: Optional[Dict[str, Any]] = None + self, gpu_customization: bool = False, gpu_customization_specs: dict[str, Any] | None = None ): """ Set options for GPU-based parameter customization/optimization. @@ -389,7 +391,7 @@ def set_num_fold(self, num_fold: int = 5): if self.ensemble_method_name == "AlgoEnsembleBestByFold": self.ensemble_method.n_fold = self.num_fold # type: ignore - def set_training_params(self, params: Optional[Dict[str, Any]] = None): + def set_training_params(self, params: dict[str, Any] | None = None): """ Set the training params for all algos. @@ -404,7 +406,7 @@ def set_training_params(self, params: Optional[Dict[str, Any]] = None): """ self.train_params = deepcopy(params) if params is not None else {} - def set_prediction_params(self, params: Optional[Dict[str, Any]] = None): + def set_prediction_params(self, params: dict[str, Any] | None = None): """ Set the prediction params for all algos. @@ -420,7 +422,7 @@ def set_prediction_params(self, params: Optional[Dict[str, Any]] = None): """ self.pred_params = deepcopy(params) if params is not None else {} - def set_analyze_params(self, params: Optional[Dict[str, Any]] = None): + def set_analyze_params(self, params: dict[str, Any] | None = None): """ Set the data analysis extra params. @@ -438,7 +440,7 @@ def set_analyze_params(self, params: Optional[Dict[str, Any]] = None): else: self.analyze_params = deepcopy(params) - def set_hpo_params(self, params: Optional[Dict[str, Any]] = None): + def set_hpo_params(self, params: dict[str, Any] | None = None): """ Set parameters for the HPO module and the algos before the training. It will attempt to (1) override bundle templates with the key-value pairs in ``params`` (2) change the config of the HPO module (e.g. NNI) if the @@ -536,7 +538,7 @@ def set_ensemble_method(self, ensemble_method_name: str = "AlgoEnsembleBestByFol else: raise NotImplementedError(f"Ensemble method {self.ensemble_method_name} is not implemented.") - def _train_algo_in_sequence(self, history: List[Dict[str, Any]]): + def _train_algo_in_sequence(self, history: list[dict[str, Any]]): """ Train the Algos in a sequential scheme. The order of training is randomized. diff --git a/monai/apps/auto3dseg/bundle_gen.py b/monai/apps/auto3dseg/bundle_gen.py index 59e90b795b..397244a73a 100644 --- a/monai/apps/auto3dseg/bundle_gen.py +++ b/monai/apps/auto3dseg/bundle_gen.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import importlib import os import shutil @@ -16,10 +18,11 @@ import sys import time import warnings +from collections.abc import Mapping from copy import deepcopy from pathlib import Path from tempfile import TemporaryDirectory -from typing import Any, Dict, List, Mapping, Optional, Union +from typing import Any from urllib.parse import urlparse import torch @@ -369,10 +372,10 @@ class BundleGen(AlgoGen): def __init__( self, algo_path: str = ".", - algos: Optional[Union[Dict, List, str]] = None, - templates_path_or_url: Optional[str] = None, - data_stats_filename: Optional[str] = None, - data_src_cfg_name: Optional[str] = None, + algos: dict | list | str | None = None, + templates_path_or_url: str | None = None, + data_stats_filename: str | None = None, + data_src_cfg_name: str | None = None, ): if algos is None or isinstance(algos, (list, tuple, str)): @@ -426,7 +429,7 @@ def __init__( self.data_stats_filename = data_stats_filename self.data_src_cfg_filename = data_src_cfg_name - self.history: List[Dict] = [] + self.history: list[dict] = [] def set_data_stats(self, data_stats_filename: str): """ @@ -454,7 +457,7 @@ def get_data_src(self): """Get the data source filename""" return self.data_src_cfg_filename - def get_history(self) -> List: + def get_history(self) -> list: """get the history of the bundleAlgo object with their names/identifiers""" return self.history @@ -463,7 +466,7 @@ def generate( output_folder=".", num_fold: int = 5, gpu_customization: bool = False, - gpu_customization_specs: Optional[Dict[str, Any]] = None, + gpu_customization_specs: dict[str, Any] | None = None, ): """ Generate the bundle scripts/configs for each bundleAlgo diff --git a/monai/apps/auto3dseg/data_analyzer.py b/monai/apps/auto3dseg/data_analyzer.py index 4f9faa4928..ffa52f6646 100644 --- a/monai/apps/auto3dseg/data_analyzer.py +++ b/monai/apps/auto3dseg/data_analyzer.py @@ -9,9 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings from os import path -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, cast import numpy as np import torch @@ -111,18 +113,18 @@ class DataAnalyzer: def __init__( self, - datalist: Union[str, Dict], + datalist: str | dict, dataroot: str = "", output_path: str = "./data_stats.yaml", average: bool = True, do_ccp: bool = False, - device: Union[str, torch.device] = "cpu", + device: str | torch.device = "cpu", worker: int = 2, image_key: str = "image", - label_key: Optional[str] = "label", - hist_bins: Optional[Union[list, int]] = 0, - hist_range: Optional[list] = None, - fmt: Optional[str] = "yaml", + label_key: str | None = "label", + hist_bins: list | int | None = 0, + hist_range: list | None = None, + fmt: str | None = "yaml", histogram_only: bool = False, **extra_params, ): @@ -146,7 +148,7 @@ def __init__( self.extra_params = extra_params @staticmethod - def _check_data_uniformity(keys: List[str], result: Dict): + def _check_data_uniformity(keys: list[str], result: dict): """ Check data uniformity since DataAnalyzer provides no support to multi-modal images with different affine matrices/spacings due to monai transforms. @@ -227,7 +229,7 @@ def get_all_case_stats(self, key="training", transform_list=None): files, _ = datafold_read(datalist=self.datalist, basedir=self.dataroot, fold=-1, key=key) dataset = Dataset(data=files, transform=transform) dataloader = DataLoader(dataset, batch_size=1, shuffle=False, num_workers=self.worker, collate_fn=no_collation) - result: Dict[DataStatsKeys, Any] = {DataStatsKeys.SUMMARY: {}, DataStatsKeys.BY_CASE: []} + result: dict[DataStatsKeys, Any] = {DataStatsKeys.SUMMARY: {}, DataStatsKeys.BY_CASE: []} if not has_tqdm: warnings.warn("tqdm is not installed. not displaying the caching progress.") @@ -261,7 +263,7 @@ def get_all_case_stats(self, key="training", transform_list=None): ) result[DataStatsKeys.BY_CASE].append(stats_by_cases) - result[DataStatsKeys.SUMMARY] = summarizer.summarize(cast(List, result[DataStatsKeys.BY_CASE])) + result[DataStatsKeys.SUMMARY] = summarizer.summarize(cast(list, result[DataStatsKeys.BY_CASE])) if not self._check_data_uniformity([ImageStatsKeys.SPACING], result): print("Data spacing is not completely uniform. MONAI transforms may provide unexpected result") diff --git a/monai/apps/auto3dseg/ensemble_builder.py b/monai/apps/auto3dseg/ensemble_builder.py index c63658789b..b6c590658b 100644 --- a/monai/apps/auto3dseg/ensemble_builder.py +++ b/monai/apps/auto3dseg/ensemble_builder.py @@ -9,10 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os from abc import ABC, abstractmethod +from collections.abc import Sequence from copy import deepcopy -from typing import Any, Dict, List, Optional, Sequence, Union +from typing import Any from warnings import warn import numpy as np @@ -67,7 +70,7 @@ def get_algo_ensemble(self): """ return self.algo_ensemble - def set_infer_files(self, dataroot: str, data_list_or_path: Union[str, List], data_key: str = "testing"): + def set_infer_files(self, dataroot: str, data_list_or_path: str | list, data_key: str = "testing"): """ Set the files to perform model inference. @@ -78,7 +81,7 @@ def set_infer_files(self, dataroot: str, data_list_or_path: Union[str, List], da self.infer_files = [] - if isinstance(data_list_or_path, List): + if isinstance(data_list_or_path, list): self.infer_files = data_list_or_path elif isinstance(data_list_or_path, str): datalist = ConfigParser.load_config_file(data_list_or_path) @@ -113,7 +116,7 @@ def ensemble_pred(self, preds, sigmoid=False): else: return VoteEnsemble(num_classes=preds[0].shape[0])(classes) - def __call__(self, pred_param: Optional[Dict[str, Any]] = None): + def __call__(self, pred_param: dict[str, Any] | None = None): """ Use the ensembled model to predict result. @@ -233,7 +236,7 @@ def collect_algos(self) -> None: self.algo_ensemble = [] for f_idx in range(self.n_fold): best_score = -1.0 - best_model: Optional[BundleAlgo] = None + best_model: BundleAlgo | None = None for algo in self.algos: # algorithm folder: {net}_{fold_index}_{other} identifier = algo[AlgoEnsembleKeys.ID].split("_")[1] @@ -264,8 +267,8 @@ class AlgoEnsembleBuilder: """ - def __init__(self, history: Sequence[Dict], data_src_cfg_filename: Optional[str] = None): - self.infer_algos: List[Dict[AlgoEnsembleKeys, Any]] = [] + def __init__(self, history: Sequence[dict], data_src_cfg_filename: str | None = None): + self.infer_algos: list[dict[AlgoEnsembleKeys, Any]] = [] self.ensemble: AlgoEnsemble self.data_src_cfg = ConfigParser(globals=False) @@ -292,7 +295,7 @@ def __init__(self, history: Sequence[Dict], data_src_cfg_filename: Optional[str] self.add_inferer(name, gen_algo, best_metric) - def add_inferer(self, identifier: str, gen_algo: BundleAlgo, best_metric: Optional[float] = None): + def add_inferer(self, identifier: str, gen_algo: BundleAlgo, best_metric: float | None = None): """ Add model inferer to the builder. diff --git a/monai/apps/auto3dseg/hpo_gen.py b/monai/apps/auto3dseg/hpo_gen.py index a80890f570..922c93790f 100644 --- a/monai/apps/auto3dseg/hpo_gen.py +++ b/monai/apps/auto3dseg/hpo_gen.py @@ -9,10 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os from abc import abstractmethod from copy import deepcopy -from typing import Optional, cast +from typing import cast from warnings import warn from monai.apps.auto3dseg.bundle_gen import BundleAlgo @@ -106,7 +108,7 @@ class NNIGen(HPOGen): NNI command manually. """ - def __init__(self, algo: Optional[Algo] = None, params=None): + def __init__(self, algo: Algo | None = None, params=None): self.algo: Algo self.hint = "" self.obj_filename = "" @@ -281,7 +283,7 @@ class OptunaGen(HPOGen): """ - def __init__(self, algo: Optional[Algo] = None, params=None) -> None: + def __init__(self, algo: Algo | None = None, params=None) -> None: self.algo: Algo self.obj_filename = "" diff --git a/monai/apps/auto3dseg/transforms.py b/monai/apps/auto3dseg/transforms.py index 2793eb9202..98250ad3bd 100644 --- a/monai/apps/auto3dseg/transforms.py +++ b/monai/apps/auto3dseg/transforms.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Dict, Hashable, Mapping +from collections.abc import Hashable, Mapping import numpy as np import torch @@ -50,7 +52,7 @@ def __init__( self.source_key = source_key self.allowed_shape_difference = allowed_shape_difference - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) image_shape = d[self.source_key].shape[1:] for key in self.key_iterator(d): diff --git a/monai/apps/auto3dseg/utils.py b/monai/apps/auto3dseg/utils.py index f031bfde35..438fabb322 100644 --- a/monai/apps/auto3dseg/utils.py +++ b/monai/apps/auto3dseg/utils.py @@ -9,16 +9,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os -from typing import Dict, List, Optional from monai.apps.auto3dseg.bundle_gen import BundleAlgo from monai.auto3dseg import algo_from_pickle, algo_to_pickle def import_bundle_algo_history( - output_folder: str = ".", template_path: Optional[str] = None, only_trained: bool = True -) -> List: + output_folder: str = ".", template_path: str | None = None, only_trained: bool = True +) -> list: """ import the history of the bundleAlgo object with their names/identifiers @@ -55,7 +56,7 @@ def import_bundle_algo_history( return history -def export_bundle_algo_history(history: List[Dict[str, BundleAlgo]]): +def export_bundle_algo_history(history: list[dict[str, BundleAlgo]]): """ Save all the BundleAlgo in the history to algo_object.pkl in each individual folder diff --git a/monai/apps/datasets.py b/monai/apps/datasets.py index f9867599b3..1fc78de831 100644 --- a/monai/apps/datasets.py +++ b/monai/apps/datasets.py @@ -9,12 +9,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import sys import warnings +from collections.abc import Callable, Sequence from pathlib import Path -from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union import numpy as np @@ -88,14 +90,14 @@ def __init__( self, root_dir: PathLike, section: str, - transform: Union[Sequence[Callable], Callable] = (), + transform: Sequence[Callable] | Callable = (), download: bool = False, seed: int = 0, val_frac: float = 0.1, test_frac: float = 0.1, cache_num: int = sys.maxsize, cache_rate: float = 1.0, - num_workers: Optional[int] = 1, + num_workers: int | None = 1, progress: bool = True, copy_cache: bool = True, as_contiguous: bool = True, @@ -148,7 +150,7 @@ def get_num_classes(self) -> int: """Get number of classes.""" return self.num_class - def _generate_data_list(self, dataset_dir: PathLike) -> List[Dict]: + def _generate_data_list(self, dataset_dir: PathLike) -> list[dict]: """ Raises: ValueError: When ``section`` is not one of ["training", "validation", "test"]. @@ -286,7 +288,7 @@ def __init__( root_dir: PathLike, task: str, section: str, - transform: Union[Sequence[Callable], Callable] = (), + transform: Sequence[Callable] | Callable = (), download: bool = False, seed: int = 0, val_frac: float = 0.2, @@ -362,7 +364,7 @@ def get_indices(self) -> np.ndarray: def randomize(self, data: np.ndarray) -> None: self.R.shuffle(data) - def get_properties(self, keys: Optional[Union[Sequence[str], str]] = None): + def get_properties(self, keys: Sequence[str] | str | None = None): """ Get the loaded properties of dataset with specified keys. If no keys specified, return all the loaded properties. @@ -374,14 +376,14 @@ def get_properties(self, keys: Optional[Union[Sequence[str], str]] = None): return {key: self._properties[key] for key in ensure_tuple(keys)} return {} - def _generate_data_list(self, dataset_dir: PathLike) -> List[Dict]: + def _generate_data_list(self, dataset_dir: PathLike) -> list[dict]: # the types of the item in data list should be compatible with the dataloader dataset_dir = Path(dataset_dir) section = "training" if self.section in ["training", "validation"] else "test" datalist = load_decathlon_datalist(dataset_dir / "dataset.json", True, section) return self._split_datalist(datalist) - def _split_datalist(self, datalist: List[Dict]) -> List[Dict]: + def _split_datalist(self, datalist: list[dict]) -> list[dict]: if self.section == "test": return datalist length = len(datalist) @@ -489,14 +491,14 @@ def __init__( root_dir: PathLike, collection: str, section: str, - transform: Union[Sequence[Callable], Callable] = (), + transform: Sequence[Callable] | Callable = (), download: bool = False, download_len: int = -1, seg_type: str = "SEG", - modality_tag: Tuple = (0x0008, 0x0060), - ref_series_uid_tag: Tuple = (0x0020, 0x000E), - ref_sop_uid_tag: Tuple = (0x0008, 0x1155), - specific_tags: Tuple = ( + modality_tag: tuple = (0x0008, 0x0060), + ref_series_uid_tag: tuple = (0x0020, 0x000E), + ref_sop_uid_tag: tuple = (0x0008, 0x1155), + specific_tags: tuple = ( (0x0008, 0x1115), # Referenced Series Sequence (0x0008, 0x1140), # Referenced Image Sequence (0x3006, 0x0010), # Referenced Frame of Reference Sequence @@ -630,7 +632,7 @@ def _download_series_reference_data(self, series_uid: str, download_dir: str): if not os.path.exists(seg_dir): shutil.copytree(seg_first_dir, seg_dir) - def _generate_data_list(self, dataset_dir: PathLike) -> List[Dict]: + def _generate_data_list(self, dataset_dir: PathLike) -> list[dict]: # the types of the item in data list should be compatible with the dataloader dataset_dir = Path(dataset_dir) datalist = [] @@ -649,7 +651,7 @@ def _generate_data_list(self, dataset_dir: PathLike) -> List[Dict]: return self._split_datalist(datalist) - def _split_datalist(self, datalist: List[Dict]) -> List[Dict]: + def _split_datalist(self, datalist: list[dict]) -> list[dict]: if self.section == "test": return datalist length = len(datalist) @@ -711,7 +713,7 @@ def __init__(self, dataset_cls, nfolds: int = 5, seed: int = 0, **dataset_params self.seed = seed self.dataset_params = dataset_params - def get_dataset(self, folds: Union[Sequence[int], int], **dataset_params): + def get_dataset(self, folds: Sequence[int] | int, **dataset_params): """ Generate dataset based on the specified fold indices in the cross validation group. @@ -727,7 +729,7 @@ def get_dataset(self, folds: Union[Sequence[int], int], **dataset_params): dataset_params_.update(dataset_params) class _NsplitsDataset(self.dataset_cls): # type: ignore - def _split_datalist(self, datalist: List[Dict]) -> List[Dict]: + def _split_datalist(self, datalist: list[dict]) -> list[dict]: data = partition_dataset(data=datalist, num_partitions=nfolds, shuffle=True, seed=seed) return select_cross_validation_folds(partitions=data, folds=folds) diff --git a/monai/apps/deepedit/interaction.py b/monai/apps/deepedit/interaction.py index 14af8c975b..05623cf248 100644 --- a/monai/apps/deepedit/interaction.py +++ b/monai/apps/deepedit/interaction.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Dict, Sequence, Union +from __future__ import annotations + +from collections.abc import Callable, Sequence import numpy as np import torch @@ -43,9 +45,9 @@ class Interaction: def __init__( self, deepgrow_probability: float, - transforms: Union[Sequence[Callable], Callable], + transforms: Sequence[Callable] | Callable, train: bool, - label_names: Union[None, Dict[str, int]] = None, + label_names: None | dict[str, int] = None, click_probability_key: str = "probability", max_interactions: int = 1, ) -> None: @@ -57,7 +59,7 @@ def __init__( self.click_probability_key = click_probability_key self.max_interactions = max_interactions - def __call__(self, engine: Union[SupervisedTrainer, SupervisedEvaluator], batchdata: Dict[str, torch.Tensor]): + def __call__(self, engine: SupervisedTrainer | SupervisedEvaluator, batchdata: dict[str, torch.Tensor]): if batchdata is None: raise ValueError("Must provide batch data for current iteration.") diff --git a/monai/apps/deepedit/transforms.py b/monai/apps/deepedit/transforms.py index fb9bd8e2e2..89790b2624 100644 --- a/monai/apps/deepedit/transforms.py +++ b/monai/apps/deepedit/transforms.py @@ -9,11 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import json import logging import random import warnings -from typing import Dict, Hashable, List, Mapping, Optional +from collections.abc import Hashable, Mapping import numpy as np import torch @@ -67,8 +69,8 @@ def _apply(self, image): image = np.concatenate([image, signal], axis=0) return image - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) for key in self.key_iterator(d): if key == "image": tmp_image = self._apply(d[key]) @@ -94,8 +96,8 @@ def __init__(self, keys: KeysCollection, label_names=None, allow_missing_keys: b self.label_names = label_names - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) for key in self.key_iterator(d): # Dictionary containing new label numbers new_label_names = {} @@ -145,8 +147,8 @@ def __init__(self, keys: KeysCollection, label_names=None, allow_missing_keys: b "left adrenal gland": 14, } - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) for key in self.key_iterator(d): if key == "label": # Taking one label at a time @@ -232,8 +234,8 @@ def _get_signal(self, image, guidance): signal = np.zeros((1, image.shape[-2], image.shape[-1]), dtype=np.float32) return signal - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) for key in self.key_iterator(d): if key == "image": image = d[key] @@ -276,8 +278,8 @@ def _apply(self, label, d): sids[key_label] = l_ids return sids - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) for key in self.key_iterator(d): if key == "label": label = d[key] @@ -324,7 +326,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.sids_key = sids self.sid_key = sid - self.sid: Dict[str, int] = dict() + self.sid: dict[str, int] = dict() self.guidance = guidance self.connected_regions = connected_regions @@ -384,8 +386,8 @@ def _randomize(self, d, key_label): sid = None self.sid[key_label] = sid - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) for key in self.key_iterator(d): if key == "label": label_guidances = {} @@ -442,8 +444,8 @@ def disparity(label, pred): def _apply(self, label, pred): return self.disparity(label, pred) - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) for key in self.key_iterator(d): if key == "label": all_discrepancies = {} @@ -503,10 +505,10 @@ def __init__( self.discrepancy = discrepancy self.probability = probability self._will_interact = None - self.is_pos: Optional[bool] = None - self.is_other: Optional[bool] = None + self.is_pos: bool | None = None + self.is_other: bool | None = None self.default_guidance = None - self.guidance: Dict[str, List[List[int]]] = {} + self.guidance: dict[str, list[list[int]]] = {} def randomize(self, data=None): probability = data[self.probability] @@ -566,8 +568,8 @@ def add_guidance(self, guidance, discrepancy, label_names, labels): tmp_label = 1 - tmp_label self.guidance[key_label].append(self.find_guidance(discrepancy[1] * tmp_label)) - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) guidance = d[self.guidance_key] discrepancy = d[self.discrepancy] self.randomize(data) @@ -637,7 +639,7 @@ def __init__( ref_image, guidance: str = "guidance", label_names=None, - meta_keys: Optional[str] = None, + meta_keys: str | None = None, meta_key_postfix: str = "meta_dict", ): self.ref_image = ref_image @@ -715,8 +717,8 @@ class SplitPredsLabeld(MapTransform): """ - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) for key in self.key_iterator(d): if key == "pred": for idx, (key_label, _) in enumerate(d["label_names"].items()): @@ -753,7 +755,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.sids_key = sids self.sid_key = sid - self.sid: Dict[str, int] = dict() + self.sid: dict[str, int] = dict() self.guidance = guidance self.connected_regions = connected_regions @@ -816,8 +818,8 @@ def _randomize(self, d, key_label): sid = None self.sid[key_label] = sid - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) for key in self.key_iterator(d): if key == "label": label_guidances = {} @@ -867,8 +869,8 @@ def _apply(self, label, d): sids[key_label] = l_ids return sids - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) for key in self.key_iterator(d): if key == "label": label = d[key] diff --git a/monai/apps/deepgrow/dataset.py b/monai/apps/deepgrow/dataset.py index dcdba512d6..8942051974 100644 --- a/monai/apps/deepgrow/dataset.py +++ b/monai/apps/deepgrow/dataset.py @@ -9,9 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import os -from typing import Dict, List, Union import numpy as np @@ -30,7 +31,7 @@ def create_dataset( limit: int = 0, relative_path: bool = False, transforms=None, -) -> List[Dict]: +) -> list[dict]: """ Utility to pre-process and create dataset list for Deepgrow training over on existing one. The input data list is normally a list of images and labels (3D volume) that needs pre-processing @@ -144,7 +145,7 @@ def _default_transforms(image_key, label_key, pixdim): def _save_data_2d(vol_idx, vol_image, vol_label, dataset_dir, relative_path): - data_list: List[Dict[str, Union[str, int]]] = [] + data_list: list[dict[str, str | int]] = [] image_count = 0 label_count = 0 @@ -211,7 +212,7 @@ def _save_data_2d(vol_idx, vol_image, vol_label, dataset_dir, relative_path): def _save_data_3d(vol_idx, vol_image, vol_label, dataset_dir, relative_path): - data_list: List[Dict[str, Union[str, int]]] = [] + data_list: list[dict[str, str | int]] = [] image_count = 0 label_count = 0 diff --git a/monai/apps/deepgrow/interaction.py b/monai/apps/deepgrow/interaction.py index e8e95f87f5..c134d45d22 100644 --- a/monai/apps/deepgrow/interaction.py +++ b/monai/apps/deepgrow/interaction.py @@ -8,7 +8,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Dict, Sequence, Union + +from __future__ import annotations + +from collections.abc import Callable, Sequence import torch @@ -38,7 +41,7 @@ class Interaction: def __init__( self, - transforms: Union[Sequence[Callable], Callable], + transforms: Sequence[Callable] | Callable, max_interactions: int, train: bool, key_probability: str = "probability", @@ -52,7 +55,7 @@ def __init__( self.train = train self.key_probability = key_probability - def __call__(self, engine: Union[SupervisedTrainer, SupervisedEvaluator], batchdata: Dict[str, torch.Tensor]): + def __call__(self, engine: SupervisedTrainer | SupervisedEvaluator, batchdata: dict[str, torch.Tensor]): if batchdata is None: raise ValueError("Must provide batch data for current iteration.") diff --git a/monai/apps/deepgrow/transforms.py b/monai/apps/deepgrow/transforms.py index e8b191845a..9beb52616e 100644 --- a/monai/apps/deepgrow/transforms.py +++ b/monai/apps/deepgrow/transforms.py @@ -8,8 +8,11 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + +from __future__ import annotations + import json -from typing import Callable, Dict, Hashable, List, Optional, Sequence, Union +from collections.abc import Callable, Hashable, Sequence import numpy as np import torch @@ -50,8 +53,8 @@ def _apply(self, label): sids.append(sid) return np.asarray(sids) - def __call__(self, data) -> Dict: - d: Dict = dict(data) + def __call__(self, data) -> dict: + d: dict = dict(data) label = d[self.label].numpy() if isinstance(data[self.label], torch.Tensor) else data[self.label] if label.shape[0] != 1: raise ValueError(f"Only supports single channel labels, got label shape {label.shape}!") @@ -396,12 +399,12 @@ def __init__( self, keys: KeysCollection, source_key: str, - spatial_size: Union[Sequence[int], np.ndarray], + spatial_size: Sequence[int] | np.ndarray, select_fn: Callable = is_positive, - channel_indices: Optional[IndexSelection] = None, + channel_indices: IndexSelection | None = None, margin: int = 0, allow_smaller: bool = True, - meta_keys: Optional[KeysCollection] = None, + meta_keys: KeysCollection | None = None, meta_key_postfix=DEFAULT_POST_FIX, start_coord_key: str = "foreground_start_coord", end_coord_key: str = "foreground_end_coord", @@ -504,9 +507,9 @@ def __init__( depth_first: bool = True, spatial_dims: int = 2, slice_key: str = "slice", - meta_keys: Optional[str] = None, + meta_keys: str | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, - dimensions: Optional[int] = None, + dimensions: int | None = None, ): self.ref_image = ref_image self.guidance = guidance @@ -523,7 +526,7 @@ def _apply(self, pos_clicks, neg_clicks, factor, slice_num): pos = neg = [] if self.dimensions == 2: - points: List = list(pos_clicks) + points: list = list(pos_clicks) points.extend(neg_clicks) slices = list(np.unique(np.array(points)[:, self.axis])) @@ -614,7 +617,7 @@ def __init__( guidance: str, spatial_size, margin=20, - meta_keys: Optional[KeysCollection] = None, + meta_keys: KeysCollection | None = None, meta_key_postfix=DEFAULT_POST_FIX, start_coord_key: str = "foreground_start_coord", end_coord_key: str = "foreground_end_coord", @@ -653,8 +656,8 @@ def bounding_box(self, points, img_shape): box_start[di], box_end[di] = min_d, max_d return box_start, box_end - def __call__(self, data) -> Dict: - d: Dict = dict(data) + def __call__(self, data) -> dict: + d: dict = dict(data) first_key: Hashable = self.first_key(d) if first_key == (): return d @@ -730,7 +733,7 @@ def __init__( self, guidance: str, ref_image: str, - meta_keys: Optional[str] = None, + meta_keys: str | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, cropped_shape_key: str = "foreground_cropped_shape", ) -> None: @@ -740,10 +743,10 @@ def __init__( self.meta_key_postfix = meta_key_postfix self.cropped_shape_key = cropped_shape_key - def __call__(self, data) -> Dict: + def __call__(self, data) -> dict: d = dict(data) guidance = d[self.guidance] - meta_dict: Dict = d[self.meta_keys or f"{self.ref_image}_{self.meta_key_postfix}"] + meta_dict: dict = d[self.meta_keys or f"{self.ref_image}_{self.meta_key_postfix}"] current_shape = d[self.ref_image].shape[1:] cropped_shape = meta_dict[self.cropped_shape_key][1:] factor = np.divide(current_shape, cropped_shape) @@ -811,9 +814,9 @@ def __init__( keys: KeysCollection, ref_image: str, slice_only: bool = False, - mode: Union[Sequence[Union[InterpolateMode, str]], InterpolateMode, str] = InterpolateMode.NEAREST, - align_corners: Union[Sequence[Optional[bool]], Optional[bool]] = None, - meta_keys: Optional[str] = None, + mode: Sequence[InterpolateMode | str] | InterpolateMode | str = InterpolateMode.NEAREST, + align_corners: Sequence[bool | None] | bool | None = None, + meta_keys: str | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, start_coord_key: str = "foreground_start_coord", end_coord_key: str = "foreground_end_coord", @@ -835,9 +838,9 @@ def __init__( self.original_shape_key = original_shape_key self.cropped_shape_key = cropped_shape_key - def __call__(self, data) -> Dict: + def __call__(self, data) -> dict: d = dict(data) - meta_dict: Dict = d[f"{self.ref_image}_{self.meta_key_postfix}"] + meta_dict: dict = d[f"{self.ref_image}_{self.meta_key_postfix}"] for key, mode, align_corners, meta_key in self.key_iterator(d, self.mode, self.align_corners, self.meta_keys): image = d[key] @@ -919,7 +922,7 @@ def __init__( keys, guidance="guidance", axis: int = 0, - meta_keys: Optional[KeysCollection] = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, allow_missing_keys: bool = False, ): diff --git a/monai/apps/detection/metrics/coco.py b/monai/apps/detection/metrics/coco.py index 8dba3fa7da..2cea7c0928 100644 --- a/monai/apps/detection/metrics/coco.py +++ b/monai/apps/detection/metrics/coco.py @@ -61,9 +61,11 @@ The changes include 1) code reformatting, 2) docstrings. """ +from __future__ import annotations + import logging as logger import time -from typing import Dict, List, Sequence, Tuple, Union +from collections.abc import Sequence import numpy as np @@ -153,7 +155,7 @@ def __init__( self.recall_thresholds = np.linspace(0.0, 1.00, int(np.round((1.00 - 0.0) / 0.01)) + 1, endpoint=True) self.max_detections = max_detection - def __call__(self, *args, **kwargs) -> Tuple[Dict[str, float], Union[Dict[str, np.ndarray], None]]: + def __call__(self, *args, **kwargs) -> tuple[dict[str, float], dict[str, np.ndarray] | None]: """ Compute metric. See :func:`compute` for more information. @@ -162,8 +164,8 @@ def __call__(self, *args, **kwargs) -> Tuple[Dict[str, float], Union[Dict[str, n **kwargs: keyword arguments passed to :func:`compute` Returns: - Dict[str, float]: dictionary with scalar values for evaluation - Dict[str, np.ndarray]: dictionary with arrays, e.g. for visualization of graphs + dict[str, float]: dictionary with scalar values for evaluation + dict[str, np.ndarray]: dictionary with arrays, e.g. for visualization of graphs """ return self.compute(*args, **kwargs) @@ -192,13 +194,13 @@ def get_iou_thresholds(self) -> Sequence[float]: """ return list(self.iou_thresholds) - def compute(self, results_list: List[Dict[int, Dict[str, np.ndarray]]]) -> Tuple[Dict[str, float], None]: + def compute(self, results_list: list[dict[int, dict[str, np.ndarray]]]) -> tuple[dict[str, float], None]: """ Compute COCO metrics Args: - results_list (List[Dict[int, Dict[str, np.ndarray]]]): list with results per image (in list) - per category (dict). Inner Dict contains multiple results obtained by :func:`box_matching_batch`. + results_list (list[dict[int, dict[str, np.ndarray]]]): list with results per image (in list) + per category (dict). Inner dict contains multiple results obtained by :func:`box_matching_batch`. - `dtMatches`: matched detections [T, D], where T = number of thresholds, D = number of detections @@ -211,13 +213,13 @@ def compute(self, results_list: List[Dict[int, Dict[str, np.ndarray]]]) -> Tuple indicate which detections should be ignored Returns: - Dict[str, float], dictionary with coco metrics + dict[str, float], dictionary with coco metrics """ if self.verbose: logger.info("Start COCO metric computation...") tic = time.time() - dataset_statistics = self._compute_statistics(results_list=results_list) # Dict[str, Union[np.ndarray, List]] + dataset_statistics = self._compute_statistics(results_list=results_list) # dict[str, Union[np.ndarray, list]] if self.verbose: toc = time.time() @@ -232,13 +234,13 @@ def compute(self, results_list: List[Dict[int, Dict[str, np.ndarray]]]) -> Tuple logger.info(f"COCO metrics computed in t={(toc - tic):0.2f}s.") return results, None - def _compute_ap(self, dataset_statistics: Dict[str, Union[np.ndarray, List]]) -> Dict[str, float]: + def _compute_ap(self, dataset_statistics: dict[str, np.ndarray | list]) -> dict[str, float]: """ Compute AP metrics Args: - dataset_statistics (List[Dict[int, Dict[str, np.ndarray]]]): list with result s per image (in list) - per category (dict). Inner Dict contains multiple results obtained by :func:`box_matching_batch`. + dataset_statistics (list[dict[int, dict[str, np.ndarray]]]): list with result s per image (in list) + per category (dict). Inner dict contains multiple results obtained by :func:`box_matching_batch`. - `dtMatches`: matched detections [T, D], where T = number of thresholds, D = number of detections @@ -279,13 +281,13 @@ def _compute_ap(self, dataset_statistics: Dict[str, Union[np.ndarray, List]]) -> results[key] = self._select_ap(dataset_statistics, iou_idx=[idx], cls_idx=cls_idx, max_det_idx=-1) return results - def _compute_ar(self, dataset_statistics: Dict[str, Union[np.ndarray, List]]) -> Dict[str, float]: + def _compute_ar(self, dataset_statistics: dict[str, np.ndarray | list]) -> dict[str, float]: """ Compute AR metrics Args: - dataset_statistics (List[Dict[int, Dict[str, np.ndarray]]]): list with result s per image (in list) - per category (dict). Inner Dict contains multiple results obtained by :func:`box_matching_batch`. + dataset_statistics (list[dict[int, dict[str, np.ndarray]]]): list with result s per image (in list) + per category (dict). Inner dict contains multiple results obtained by :func:`box_matching_batch`. - `dtMatches`: matched detections [T, D], where T = number of thresholds, D = number of detections @@ -324,8 +326,8 @@ def _compute_ar(self, dataset_statistics: Dict[str, Union[np.ndarray, List]]) -> @staticmethod def _select_ap( dataset_statistics: dict, - iou_idx: Union[int, List[int], np.ndarray, None] = None, - cls_idx: Union[int, Sequence[int], None] = None, + iou_idx: int | list[int] | np.ndarray | None = None, + cls_idx: int | Sequence[int] | None = None, max_det_idx: int = -1, ) -> float: """ @@ -359,8 +361,8 @@ def _select_ap( @staticmethod def _select_ar( dataset_statistics: dict, - iou_idx: Union[int, Sequence[int], None] = None, - cls_idx: Union[int, Sequence[int], None] = None, + iou_idx: int | Sequence[int] | None = None, + cls_idx: int | Sequence[int] | None = None, max_det_idx: int = -1, ) -> float: """ @@ -395,16 +397,14 @@ def _select_ar( return float(np.mean(rec[rec > -1])) - def _compute_statistics( - self, results_list: List[Dict[int, Dict[str, np.ndarray]]] - ) -> Dict[str, Union[np.ndarray, List]]: + def _compute_statistics(self, results_list: list[dict[int, dict[str, np.ndarray]]]) -> dict[str, np.ndarray | list]: """ Compute statistics needed for COCO metrics (mAP, AP of individual classes, mAP@IoU_Thresholds, AR) Adapted from https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/cocoeval.py Args: - results_list (List[Dict[int, Dict[str, np.ndarray]]]): list with result s per image (in list) - per category (dict). Inner Dict contains multiple results obtained by :func:`box_matching_batch`. + results_list (list[dict[int, dict[str, np.ndarray]]]): list with result s per image (in list) + per category (dict). Inner dict contains multiple results obtained by :func:`box_matching_batch`. - `dtMatches`: matched detections [T, D], where T = number of thresholds, D = number of detections @@ -487,9 +487,9 @@ def _compute_stats_single_threshold( tp: np.ndarray, fp: np.ndarray, dt_scores_sorted: np.ndarray, - recall_thresholds: Union[np.ndarray, Sequence[float]], + recall_thresholds: np.ndarray | Sequence[float], num_gt: int, -) -> Tuple[float, np.ndarray, np.ndarray]: +) -> tuple[float, np.ndarray, np.ndarray]: """ Compute recall value, precision curve and scores thresholds Adapted from https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/cocoeval.py diff --git a/monai/apps/detection/metrics/matching.py b/monai/apps/detection/metrics/matching.py index 37e6e2fa06..6efcb701d4 100644 --- a/monai/apps/detection/metrics/matching.py +++ b/monai/apps/detection/metrics/matching.py @@ -62,7 +62,9 @@ 3) allow input args gt_ignore to be optional. (If so, no GT boxes will be ignored.) """ -from typing import Callable, Dict, List, Sequence, Union +from __future__ import annotations + +from collections.abc import Callable, Sequence import numpy as np @@ -77,9 +79,9 @@ def matching_batch( pred_scores: Sequence[np.ndarray], gt_boxes: Sequence[np.ndarray], gt_classes: Sequence[np.ndarray], - gt_ignore: Union[Sequence[Sequence[bool]], Sequence[np.ndarray], None] = None, + gt_ignore: Sequence[Sequence[bool]] | Sequence[np.ndarray] | None = None, max_detections: int = 100, -) -> List[Dict[int, Dict[str, np.ndarray]]]: +) -> list[dict[int, dict[str, np.ndarray]]]: """ Match boxes of a batch to corresponding ground truth for each category independently. @@ -185,7 +187,7 @@ def matching_batch( def _matching_no_gt( iou_thresholds: Sequence[float], pred_scores: np.ndarray, max_detections: int -) -> Dict[str, np.ndarray]: +) -> dict[str, np.ndarray]: """ Matching result with not ground truth in image @@ -228,7 +230,7 @@ def _matching_no_gt( } -def _matching_no_pred(iou_thresholds: Sequence[float], gt_ignore: np.ndarray) -> Dict[str, np.ndarray]: +def _matching_no_pred(iou_thresholds: Sequence[float], gt_ignore: np.ndarray) -> dict[str, np.ndarray]: """ Matching result with no predictions @@ -275,7 +277,7 @@ def _matching_single_image_single_class( gt_ignore: np.ndarray, max_detections: int, iou_thresholds: Sequence[float], -) -> Dict[str, np.ndarray]: +) -> dict[str, np.ndarray]: """ Adapted from https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/cocoeval.py diff --git a/monai/apps/detection/networks/retinanet_detector.py b/monai/apps/detection/networks/retinanet_detector.py index 5064304cbb..44bd0ec199 100644 --- a/monai/apps/detection/networks/retinanet_detector.py +++ b/monai/apps/detection/networks/retinanet_detector.py @@ -37,8 +37,11 @@ https://github.com/pytorch/vision/blob/main/torchvision/models/detection/retinanet.py """ +from __future__ import annotations + import warnings -from typing import Any, Callable, Dict, List, Sequence, Tuple, Union +from collections.abc import Callable, Sequence +from typing import Any import torch from torch import Tensor, nn @@ -209,14 +212,14 @@ def __init__( ) # if new coming input images has same shape with # self.previous_image_shape, there is no need to generate new anchors. - self.anchors: Union[List[Tensor], None] = None - self.previous_image_shape: Union[Any, None] = None + self.anchors: list[Tensor] | None = None + self.previous_image_shape: Any | None = None self.box_overlap_metric = box_overlap_metric self.debug = debug # default setting for training - self.fg_bg_sampler: Union[Any, None] = None + self.fg_bg_sampler: Any | None = None self.set_cls_loss(torch.nn.BCEWithLogitsLoss(reduction="mean")) # classification loss self.set_box_regression_loss( torch.nn.SmoothL1Loss(beta=1.0 / 9, reduction="mean"), encode_gt=True, decode_pred=False @@ -234,7 +237,7 @@ def __init__( # default setting for inference, # can be updated by self.set_sliding_window_inferer(*) - self.inferer: Union[SlidingWindowInferer, None] = None + self.inferer: SlidingWindowInferer | None = None # can be updated by self.set_box_selector_parameters(*), self.box_selector = BoxSelector( box_overlap_metric=self.box_overlap_metric, @@ -245,7 +248,7 @@ def __init__( apply_sigmoid=True, ) - def set_box_coder_weights(self, weights: Tuple[float]): + def set_box_coder_weights(self, weights: tuple[float]): """ Set the weights for box coder. @@ -382,15 +385,15 @@ def set_balanced_sampler(self, batch_size_per_image: int, positive_fraction: flo def set_sliding_window_inferer( self, - roi_size: Union[Sequence[int], int], + roi_size: Sequence[int] | int, sw_batch_size: int = 1, overlap: float = 0.5, - mode: Union[BlendMode, str] = BlendMode.CONSTANT, - sigma_scale: Union[Sequence[float], float] = 0.125, - padding_mode: Union[PytorchPadMode, str] = PytorchPadMode.CONSTANT, + mode: BlendMode | str = BlendMode.CONSTANT, + sigma_scale: Sequence[float] | float = 0.125, + padding_mode: PytorchPadMode | str = PytorchPadMode.CONSTANT, cval: float = 0.0, - sw_device: Union[torch.device, str, None] = None, - device: Union[torch.device, str, None] = None, + sw_device: torch.device | str | None = None, + device: torch.device | str | None = None, progress: bool = False, cache_roi_weight_map: bool = False, ): @@ -446,10 +449,10 @@ def set_box_selector_parameters( def forward( self, - input_images: Union[List[Tensor], Tensor], - targets: Union[List[Dict[str, Tensor]], None] = None, + input_images: list[Tensor] | Tensor, + targets: list[dict[str, Tensor]] | None = None, use_inferer: bool = False, - ) -> Union[Dict[str, Tensor], List[Dict[str, Tensor]]]: + ) -> dict[str, Tensor] | list[dict[str, Tensor]]: """ Returns a dict of losses during training, or a list predicted dict of boxes and labels during inference. @@ -536,7 +539,7 @@ def _check_detector_training_components(self): "or set classification loss function as Focal loss with self.set_cls_loss(*)" ) - def generate_anchors(self, images: Tensor, head_outputs: Dict[str, List[Tensor]]): + def generate_anchors(self, images: Tensor, head_outputs: dict[str, list[Tensor]]): """ Generate anchors and store it in self.anchors: List[Tensor]. We generate anchors only when there is no stored anchors, @@ -552,7 +555,7 @@ def generate_anchors(self, images: Tensor, head_outputs: Dict[str, List[Tensor]] self.anchors = self.anchor_generator(images, head_outputs[self.cls_key]) # List[Tensor], len = batchsize self.previous_image_shape = images.shape - def _reshape_maps(self, result_maps: List[Tensor]) -> Tensor: + def _reshape_maps(self, result_maps: list[Tensor]) -> Tensor: """ Concat network output map list to a single Tensor. This function is used in both training and inference. @@ -596,12 +599,12 @@ def _reshape_maps(self, result_maps: List[Tensor]) -> Tensor: def postprocess_detections( self, - head_outputs_reshape: Dict[str, Tensor], - anchors: List[Tensor], - image_sizes: List[List[int]], + head_outputs_reshape: dict[str, Tensor], + anchors: list[Tensor], + image_sizes: list[list[int]], num_anchor_locs_per_level: Sequence[int], need_sigmoid: bool = True, - ) -> List[Dict[str, Tensor]]: + ) -> list[dict[str, Tensor]]: """ Postprocessing to generate detection result from classification logits and box regression. Use self.box_selector to select the final output boxes for each image. @@ -626,7 +629,7 @@ def postprocess_detections( ] # split outputs per level - split_head_outputs: Dict[str, List[Tensor]] = {} + split_head_outputs: dict[str, list[Tensor]] = {} for k in head_outputs_reshape: split_head_outputs[k] = list(head_outputs_reshape[k].split(num_anchors_per_level, dim=1)) split_anchors = [list(a.split(num_anchors_per_level)) for a in anchors] # List[List[Tensor]] @@ -637,7 +640,7 @@ def postprocess_detections( num_images = len(image_sizes) # B - detections: List[Dict[str, Tensor]] = [] + detections: list[dict[str, Tensor]] = [] for index in range(num_images): box_regression_per_image = [ @@ -667,11 +670,11 @@ def postprocess_detections( def compute_loss( self, - head_outputs_reshape: Dict[str, Tensor], - targets: List[Dict[str, Tensor]], - anchors: List[Tensor], + head_outputs_reshape: dict[str, Tensor], + targets: list[dict[str, Tensor]], + anchors: list[Tensor], num_anchor_locs_per_level: Sequence[int], - ) -> Dict[str, Tensor]: + ) -> dict[str, Tensor]: """ Compute losses. @@ -696,8 +699,8 @@ def compute_loss( return {self.cls_key: losses_cls, self.box_reg_key: losses_box_regression} def compute_anchor_matched_idxs( - self, anchors: List[Tensor], targets: List[Dict[str, Tensor]], num_anchor_locs_per_level: Sequence[int] - ) -> List[Tensor]: + self, anchors: list[Tensor], targets: list[dict[str, Tensor]], num_anchor_locs_per_level: Sequence[int] + ) -> list[Tensor]: """ Compute the matched indices between anchors and ground truth (gt) boxes in targets. output[k][i] represents the matched gt index for anchor[i] in image k. @@ -768,7 +771,7 @@ def compute_anchor_matched_idxs( return matched_idxs def compute_cls_loss( - self, cls_logits: Tensor, targets: List[Dict[str, Tensor]], matched_idxs: List[Tensor] + self, cls_logits: Tensor, targets: list[dict[str, Tensor]], matched_idxs: list[Tensor] ) -> Tensor: """ Compute classification losses. @@ -800,9 +803,9 @@ def compute_cls_loss( def compute_box_loss( self, box_regression: Tensor, - targets: List[Dict[str, Tensor]], - anchors: List[Tensor], - matched_idxs: List[Tensor], + targets: list[dict[str, Tensor]], + anchors: list[Tensor], + matched_idxs: list[Tensor], ) -> Tensor: """ Compute box regression losses. @@ -845,8 +848,8 @@ def compute_box_loss( return losses def get_cls_train_sample_per_image( - self, cls_logits_per_image: Tensor, targets_per_image: Dict[str, Tensor], matched_idxs_per_image: Tensor - ) -> Tuple[Tensor, Tensor]: + self, cls_logits_per_image: Tensor, targets_per_image: dict[str, Tensor], matched_idxs_per_image: Tensor + ) -> tuple[Tensor, Tensor]: """ Get samples from one image for classification losses computation. @@ -923,10 +926,10 @@ def get_cls_train_sample_per_image( def get_box_train_sample_per_image( self, box_regression_per_image: Tensor, - targets_per_image: Dict[str, Tensor], + targets_per_image: dict[str, Tensor], anchors_per_image: Tensor, matched_idxs_per_image: Tensor, - ) -> Tuple[Tensor, Tensor]: + ) -> tuple[Tensor, Tensor]: """ Get samples from one image for box regression losses computation. diff --git a/monai/apps/detection/networks/retinanet_network.py b/monai/apps/detection/networks/retinanet_network.py index 4a0d8dc228..e363a08471 100644 --- a/monai/apps/detection/networks/retinanet_network.py +++ b/monai/apps/detection/networks/retinanet_network.py @@ -37,8 +37,11 @@ https://github.com/pytorch/vision/blob/main/torchvision/models/detection/retinanet.py """ +from __future__ import annotations + import math -from typing import Callable, Dict, List, Sequence, Union +from collections.abc import Callable, Sequence +from typing import Dict import torch from torch import Tensor, nn @@ -94,7 +97,7 @@ def __init__( self.num_classes = num_classes self.num_anchors = num_anchors - def forward(self, x: List[Tensor]) -> List[Tensor]: + def forward(self, x: list[Tensor]) -> list[Tensor]: """ It takes a list of feature maps as inputs, and outputs a list of classification maps. Each output classification map has same spatial size with the corresponding input feature map, @@ -163,7 +166,7 @@ def __init__(self, in_channels: int, num_anchors: int, spatial_dims: int): torch.nn.init.normal_(layer.weight, std=0.01) # type: ignore torch.nn.init.zeros_(layer.bias) # type: ignore - def forward(self, x: List[Tensor]) -> List[Tensor]: + def forward(self, x: list[Tensor]) -> list[Tensor]: """ It takes a list of feature maps as inputs, and outputs a list of box regression maps. Each output box regression map has same spatial size with the corresponding input feature map, @@ -262,7 +265,7 @@ def __init__( num_classes: int, num_anchors: int, feature_extractor, - size_divisible: Union[Sequence[int], int] = 1, + size_divisible: Sequence[int] | int = 1, ): super().__init__() @@ -290,7 +293,7 @@ def __init__( self.cls_key: str = "classification" self.box_reg_key: str = "box_regression" - def forward(self, images: Tensor) -> Dict[str, List[Tensor]]: + def forward(self, images: Tensor) -> dict[str, list[Tensor]]: """ It takes an image tensor as inputs, and outputs a dictionary ``head_outputs``. ``head_outputs[self.cls_key]`` is the predicted classification maps, a list of Tensor. @@ -320,7 +323,7 @@ def forward(self, images: Tensor) -> Dict[str, List[Tensor]]: # compute classification and box regression maps from the feature maps # expandable for mask prediction in the future - head_outputs: Dict[str, List[Tensor]] = {self.cls_key: self.classification_head(feature_maps)} + head_outputs: dict[str, list[Tensor]] = {self.cls_key: self.classification_head(feature_maps)} head_outputs[self.box_reg_key] = self.regression_head(feature_maps) return head_outputs @@ -331,7 +334,7 @@ def resnet_fpn_feature_extractor( spatial_dims: int, pretrained_backbone: bool = False, returned_layers: Sequence[int] = (1, 2, 3), - trainable_backbone_layers: Union[int, None] = None, + trainable_backbone_layers: int | None = None, ): """ Constructs a feature extractor network with a ResNet-FPN backbone, used as feature_extractor in RetinaNet. diff --git a/monai/apps/detection/transforms/array.py b/monai/apps/detection/transforms/array.py index 6d59c8b49b..816812e850 100644 --- a/monai/apps/detection/transforms/array.py +++ b/monai/apps/detection/transforms/array.py @@ -13,7 +13,9 @@ https://github.com/Project-MONAI/MONAI/wiki/MONAI_Design """ -from typing import Callable, Optional, Sequence, Tuple, Type, Union +from __future__ import annotations + +from typing import Callable, Sequence import numpy as np import torch @@ -108,8 +110,8 @@ class ConvertBoxMode(Transform): def __init__( self, - src_mode: Union[str, BoxMode, Type[BoxMode], None] = None, - dst_mode: Union[str, BoxMode, Type[BoxMode], None] = None, + src_mode: str | BoxMode | type[BoxMode] | None = None, + dst_mode: str | BoxMode | type[BoxMode] | None = None, ) -> None: self.src_mode = src_mode self.dst_mode = dst_mode @@ -148,7 +150,7 @@ class ConvertBoxToStandardMode(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, mode: Union[str, BoxMode, Type[BoxMode], None] = None) -> None: + def __init__(self, mode: str | BoxMode | type[BoxMode] | None = None) -> None: self.mode = mode def __call__(self, boxes: NdarrayOrTensor) -> NdarrayOrTensor: @@ -173,7 +175,7 @@ class AffineBox(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __call__(self, boxes: NdarrayOrTensor, affine: Union[NdarrayOrTensor, None]) -> NdarrayOrTensor: # type: ignore + def __call__(self, boxes: NdarrayOrTensor, affine: NdarrayOrTensor | None) -> NdarrayOrTensor: # type: ignore """ Args: boxes: source bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` @@ -200,12 +202,12 @@ class ZoomBox(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, zoom: Union[Sequence[float], float], keep_size: bool = False, **kwargs) -> None: + def __init__(self, zoom: Sequence[float] | float, keep_size: bool = False, **kwargs) -> None: self.zoom = zoom self.keep_size = keep_size self.kwargs = kwargs - def __call__(self, boxes: torch.Tensor, src_spatial_size: Union[Sequence[int], int, None] = None): + def __call__(self, boxes: torch.Tensor, src_spatial_size: Sequence[int] | int | None = None): """ Args: boxes: source bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` @@ -260,11 +262,11 @@ class ResizeBox(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, spatial_size: Union[Sequence[int], int], size_mode: str = "all", **kwargs) -> None: + def __init__(self, spatial_size: Sequence[int] | int, size_mode: str = "all", **kwargs) -> None: self.size_mode = look_up_option(size_mode, ["all", "longest"]) self.spatial_size = spatial_size - def __call__(self, boxes: NdarrayOrTensor, src_spatial_size: Union[Sequence[int], int]): # type: ignore + def __call__(self, boxes: NdarrayOrTensor, src_spatial_size: Sequence[int] | int): # type: ignore """ Args: boxes: source bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` @@ -309,10 +311,10 @@ class FlipBox(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, spatial_axis: Optional[Union[Sequence[int], int]] = None) -> None: + def __init__(self, spatial_axis: Sequence[int] | int | None = None) -> None: self.spatial_axis = spatial_axis - def __call__(self, boxes: NdarrayOrTensor, spatial_size: Union[Sequence[int], int]): # type: ignore + def __call__(self, boxes: NdarrayOrTensor, spatial_size: Sequence[int] | int): # type: ignore """ Args: boxes: bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` @@ -339,9 +341,9 @@ def __init__(self, remove_empty: bool = False) -> None: def __call__( # type: ignore self, boxes: NdarrayOrTensor, - labels: Union[Sequence[NdarrayOrTensor], NdarrayOrTensor], - spatial_size: Union[Sequence[int], int], - ) -> Tuple[NdarrayOrTensor, Union[Tuple, NdarrayOrTensor]]: + labels: Sequence[NdarrayOrTensor] | NdarrayOrTensor, + spatial_size: Sequence[int] | int, + ) -> tuple[NdarrayOrTensor, tuple | NdarrayOrTensor]: """ Args: boxes: bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` @@ -392,7 +394,7 @@ def __init__(self, bg_label: int = -1, ellipse_mask: bool = False) -> None: self.ellipse_mask = ellipse_mask def __call__( # type: ignore - self, boxes: NdarrayOrTensor, labels: NdarrayOrTensor, spatial_size: Union[Sequence[int], int] + self, boxes: NdarrayOrTensor, labels: NdarrayOrTensor, spatial_size: Sequence[int] | int ) -> NdarrayOrTensor: """ Args: @@ -427,7 +429,7 @@ def __init__(self, bg_label: int = -1, box_dtype=torch.float32, label_dtype=torc self.box_dtype = box_dtype self.label_dtype = label_dtype - def __call__(self, boxes_mask: NdarrayOrTensor) -> Tuple[NdarrayOrTensor, NdarrayOrTensor]: + def __call__(self, boxes_mask: NdarrayOrTensor) -> tuple[NdarrayOrTensor, NdarrayOrTensor]: """ Args: boxes_mask: int16 array, sized (num_box, H, W). Each channel represents a box. @@ -470,20 +472,18 @@ class SpatialCropBox(SpatialCrop): def __init__( self, - roi_center: Union[Sequence[int], NdarrayOrTensor, None] = None, - roi_size: Union[Sequence[int], NdarrayOrTensor, None] = None, - roi_start: Union[Sequence[int], NdarrayOrTensor, None] = None, - roi_end: Union[Sequence[int], NdarrayOrTensor, None] = None, - roi_slices: Optional[Sequence[slice]] = None, + roi_center: Sequence[int] | NdarrayOrTensor | None = None, + roi_size: Sequence[int] | NdarrayOrTensor | None = None, + roi_start: Sequence[int] | NdarrayOrTensor | None = None, + roi_end: Sequence[int] | NdarrayOrTensor | None = None, + roi_slices: Sequence[slice] | None = None, ) -> None: super().__init__(roi_center, roi_size, roi_start, roi_end, roi_slices) for s in self.slices: if s.start < 0 or s.stop < 0 or (s.step is not None and s.step < 0): raise ValueError("Currently negative indexing is not supported for SpatialCropBox.") - def __call__( # type: ignore - self, boxes: NdarrayOrTensor, labels: Union[Sequence[NdarrayOrTensor], NdarrayOrTensor] - ): + def __call__(self, boxes: NdarrayOrTensor, labels: Sequence[NdarrayOrTensor] | NdarrayOrTensor): # type: ignore """ Args: boxes: bounding boxes, Nx4 or Nx6 torch tensor or ndarray. The box mode is assumed to be ``StandardMode`` @@ -526,7 +526,7 @@ class RotateBox90(Rotate90): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __call__(self, boxes: NdarrayOrTensor, spatial_size: Union[Sequence[int], int]): # type: ignore + def __call__(self, boxes: NdarrayOrTensor, spatial_size: Sequence[int] | int): # type: ignore """ Args: img: channel first array, must have shape: (num_channels, H[, W, ..., ]), diff --git a/monai/apps/detection/transforms/box_ops.py b/monai/apps/detection/transforms/box_ops.py index d2445577d0..fe8d5f81a7 100644 --- a/monai/apps/detection/transforms/box_ops.py +++ b/monai/apps/detection/transforms/box_ops.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + +from collections.abc import Sequence from copy import deepcopy -from typing import Optional, Sequence, Tuple, Union import numpy as np import torch @@ -99,7 +101,7 @@ def apply_affine_to_boxes(boxes: NdarrayOrTensor, affine: NdarrayOrTensor) -> Nd return boxes_affine -def zoom_boxes(boxes: NdarrayOrTensor, zoom: Union[Sequence[float], float]): +def zoom_boxes(boxes: NdarrayOrTensor, zoom: Sequence[float] | float): """ Zoom boxes @@ -126,9 +128,7 @@ def zoom_boxes(boxes: NdarrayOrTensor, zoom: Union[Sequence[float], float]): return apply_affine_to_boxes(boxes=boxes, affine=affine) -def resize_boxes( - boxes: NdarrayOrTensor, src_spatial_size: Union[Sequence[int], int], dst_spatial_size: Union[Sequence[int], int] -): +def resize_boxes(boxes: NdarrayOrTensor, src_spatial_size: Sequence[int] | int, dst_spatial_size: Sequence[int] | int): """ Resize boxes when the corresponding image is resized @@ -158,11 +158,7 @@ def resize_boxes( return zoom_boxes(boxes=boxes, zoom=zoom) -def flip_boxes( - boxes: NdarrayOrTensor, - spatial_size: Union[Sequence[int], int], - flip_axes: Optional[Union[Sequence[int], int]] = None, -): +def flip_boxes(boxes: NdarrayOrTensor, spatial_size: Sequence[int] | int, flip_axes: Sequence[int] | int | None = None): """ Flip boxes when the corresponding image is flipped @@ -200,7 +196,7 @@ def flip_boxes( def convert_box_to_mask( boxes: NdarrayOrTensor, labels: NdarrayOrTensor, - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, bg_label: int = -1, ellipse_mask: bool = False, ) -> NdarrayOrTensor: @@ -276,7 +272,7 @@ def convert_box_to_mask( def convert_mask_to_box( boxes_mask: NdarrayOrTensor, bg_label: int = -1, box_dtype=torch.float32, label_dtype=torch.long -) -> Tuple[NdarrayOrTensor, NdarrayOrTensor]: +) -> tuple[NdarrayOrTensor, NdarrayOrTensor]: """ Convert int16 mask image to box, which has the same size with the input image @@ -325,8 +321,8 @@ def convert_mask_to_box( def select_labels( - labels: Union[Sequence[NdarrayOrTensor], NdarrayOrTensor], keep: NdarrayOrTensor -) -> Union[Tuple, NdarrayOrTensor]: + labels: Sequence[NdarrayOrTensor] | NdarrayOrTensor, keep: NdarrayOrTensor +) -> tuple | NdarrayOrTensor: """ For element in labels, select indices keep from it. @@ -380,9 +376,7 @@ def swapaxes_boxes(boxes: NdarrayOrTensor, axis1: int, axis2: int): return boxes_swap -def rot90_boxes( - boxes: NdarrayOrTensor, spatial_size: Union[Sequence[int], int], k: int = 1, axes: Tuple[int, int] = (0, 1) -): +def rot90_boxes(boxes: NdarrayOrTensor, spatial_size: Sequence[int] | int, k: int = 1, axes: tuple[int, int] = (0, 1)): """ Rotate boxes by 90 degrees in the plane specified by axes. Rotation direction is from the first towards the second axis. diff --git a/monai/apps/detection/transforms/dictionary.py b/monai/apps/detection/transforms/dictionary.py index fa365895b5..12cdaea8f2 100644 --- a/monai/apps/detection/transforms/dictionary.py +++ b/monai/apps/detection/transforms/dictionary.py @@ -14,8 +14,11 @@ Class names are ended with 'd' to denote dictionary-based transforms. """ + +from __future__ import annotations + +from collections.abc import Hashable, Mapping, Sequence from copy import deepcopy -from typing import Dict, Hashable, List, Mapping, Optional, Sequence, Tuple, Type, Union import numpy as np import torch @@ -109,8 +112,8 @@ class ConvertBoxModed(MapTransform, InvertibleTransform): def __init__( self, box_keys: KeysCollection, - src_mode: Union[str, BoxMode, Type[BoxMode], None] = None, - dst_mode: Union[str, BoxMode, Type[BoxMode], None] = None, + src_mode: str | BoxMode | type[BoxMode] | None = None, + dst_mode: str | BoxMode | type[BoxMode] | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -127,14 +130,14 @@ def __init__( super().__init__(box_keys, allow_missing_keys) self.converter = ConvertBoxMode(src_mode=src_mode, dst_mode=dst_mode) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) self.push_transform(d, key, extra_info={"src": self.converter.src_mode, "dst": self.converter.dst_mode}) return d - def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): tr = self.get_most_recent_transform(d, key) @@ -167,7 +170,7 @@ class ConvertBoxToStandardModed(MapTransform, InvertibleTransform): def __init__( self, box_keys: KeysCollection, - mode: Union[str, BoxMode, Type[BoxMode], None] = None, + mode: str | BoxMode | type[BoxMode] | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -182,14 +185,14 @@ def __init__( super().__init__(box_keys, allow_missing_keys) self.converter = ConvertBoxToStandardMode(mode=mode) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) self.push_transform(d, key, extra_info={"mode": self.converter.mode}) return d - def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): tr = self.get_most_recent_transform(d, key) @@ -230,8 +233,8 @@ def __init__( box_keys: KeysCollection, box_ref_image_keys: str, allow_missing_keys: bool = False, - image_meta_key: Union[str, None] = None, - image_meta_key_postfix: Union[str, None] = DEFAULT_POST_FIX, + image_meta_key: str | None = None, + image_meta_key_postfix: str | None = DEFAULT_POST_FIX, affine_lps_to_ras=False, ) -> None: super().__init__(box_keys, allow_missing_keys) @@ -246,7 +249,7 @@ def __init__( self.converter_to_image_coordinate = AffineBox() self.affine_lps_to_ras = affine_lps_to_ras - def extract_affine(self, data: Mapping[Hashable, torch.Tensor]) -> Tuple[NdarrayOrTensor, torch.Tensor]: + def extract_affine(self, data: Mapping[Hashable, torch.Tensor]) -> tuple[NdarrayOrTensor, torch.Tensor]: d = dict(data) meta_key = self.image_meta_key @@ -274,7 +277,7 @@ def extract_affine(self, data: Mapping[Hashable, torch.Tensor]) -> Tuple[Ndarray inv_affine_t = torch.inverse(affine_t.to(COMPUTE_DTYPE)) return affine, inv_affine_t - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) affine, inv_affine_t = self.extract_affine(data) # type: ignore @@ -284,7 +287,7 @@ def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, N self.push_transform(d, key, extra_info={"affine": affine}) return d - def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): transform = self.get_most_recent_transform(d, key) @@ -322,8 +325,8 @@ def __init__( box_keys: KeysCollection, box_ref_image_keys: str, allow_missing_keys: bool = False, - image_meta_key: Union[str, None] = None, - image_meta_key_postfix: Union[str, None] = DEFAULT_POST_FIX, + image_meta_key: str | None = None, + image_meta_key_postfix: str | None = DEFAULT_POST_FIX, affine_lps_to_ras=False, ) -> None: super().__init__( @@ -331,7 +334,7 @@ def __init__( ) self.converter_to_world_coordinate = AffineBox() - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) affine, inv_affine_t = self.extract_affine(data) # type: ignore @@ -379,10 +382,10 @@ def __init__( image_keys: KeysCollection, box_keys: KeysCollection, box_ref_image_keys: KeysCollection, - zoom: Union[Sequence[float], float], + zoom: Sequence[float] | float, mode: SequenceStr = InterpolateMode.AREA, padding_mode: SequenceStr = NumpyPadMode.EDGE, - align_corners: Union[Sequence[Optional[bool]], Optional[bool]] = None, + align_corners: Sequence[bool | None] | bool | None = None, keep_size: bool = True, allow_missing_keys: bool = False, **kwargs, @@ -398,7 +401,7 @@ def __init__( self.zoomer = Zoom(zoom=zoom, keep_size=keep_size, **kwargs) self.keep_size = keep_size - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) # zoom box @@ -423,7 +426,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): @@ -493,11 +496,11 @@ def __init__( box_keys: KeysCollection, box_ref_image_keys: KeysCollection, prob: float = 0.1, - min_zoom: Union[Sequence[float], float] = 0.9, - max_zoom: Union[Sequence[float], float] = 1.1, + min_zoom: Sequence[float] | float = 0.9, + max_zoom: Sequence[float] | float = 1.1, mode: SequenceStr = InterpolateMode.AREA, padding_mode: SequenceStr = NumpyPadMode.EDGE, - align_corners: Union[Sequence[Optional[bool]], Optional[bool]] = None, + align_corners: Sequence[bool | None] | bool | None = None, keep_size: bool = True, allow_missing_keys: bool = False, **kwargs, @@ -514,14 +517,12 @@ def __init__( self.align_corners = ensure_tuple_rep(align_corners, len(self.image_keys)) self.keep_size = keep_size - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandZoomBoxd": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandZoomBoxd: super().set_random_state(seed, state) self.rand_zoom.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) first_key: Hashable = self.first_key(d) if first_key == (): @@ -564,7 +565,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): @@ -609,7 +610,7 @@ def __init__( image_keys: KeysCollection, box_keys: KeysCollection, box_ref_image_keys: KeysCollection, - spatial_axis: Optional[Union[Sequence[int], int]] = None, + spatial_axis: Sequence[int] | int | None = None, allow_missing_keys: bool = False, ) -> None: self.image_keys = ensure_tuple(image_keys) @@ -620,7 +621,7 @@ def __init__( self.flipper = Flip(spatial_axis=spatial_axis) self.box_flipper = FlipBox(spatial_axis=self.flipper.spatial_axis) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.image_keys: @@ -632,7 +633,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc self.push_transform(d, box_key, extra_info={"spatial_size": spatial_size, "type": "box_key"}) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): @@ -673,7 +674,7 @@ def __init__( box_keys: KeysCollection, box_ref_image_keys: KeysCollection, prob: float = 0.1, - spatial_axis: Optional[Union[Sequence[int], int]] = None, + spatial_axis: Sequence[int] | int | None = None, allow_missing_keys: bool = False, ) -> None: self.image_keys = ensure_tuple(image_keys) @@ -685,13 +686,11 @@ def __init__( self.flipper = Flip(spatial_axis=spatial_axis) self.box_flipper = FlipBox(spatial_axis=spatial_axis) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandFlipBoxd": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandFlipBoxd: super().set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) self.randomize(None) @@ -711,7 +710,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc self.push_transform(d, box_key, extra_info={"spatial_size": spatial_size, "type": "box_key"}) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): @@ -784,7 +783,7 @@ def __init__( self.box_ref_image_keys = box_ref_image_keys_tuple[0] self.clipper = ClipBoxToImage(remove_empty=remove_empty) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) spatial_size = d[self.box_ref_image_keys].shape[1:] labels = [d[label_key] for label_key in self.label_keys] # could be multiple arrays @@ -872,7 +871,7 @@ def __init__( self.bg_label = min_fg_label - 1 # make sure background label is always smaller than fg labels. self.converter = BoxToMask(bg_label=self.bg_label, ellipse_mask=ellipse_mask) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for box_key, label_key, box_mask_key, box_ref_image_key in zip( @@ -954,7 +953,7 @@ def __init__( self.converter = MaskToBox(bg_label=self.bg_label, box_dtype=box_dtype, label_dtype=label_dtype) self.box_dtype = box_dtype - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for box_key, label_key, box_mask_key in zip(self.box_keys, self.label_keys, self.box_mask_keys): @@ -1021,16 +1020,16 @@ def __init__( image_keys: KeysCollection, box_keys: str, label_keys: KeysCollection, - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, pos: float = 1.0, neg: float = 1.0, num_samples: int = 1, whole_box: bool = True, - thresh_image_key: Optional[str] = None, + thresh_image_key: str | None = None, image_threshold: float = 0.0, - fg_indices_key: Optional[str] = None, - bg_indices_key: Optional[str] = None, - meta_keys: Optional[KeysCollection] = None, + fg_indices_key: str | None = None, + bg_indices_key: str | None = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, allow_smaller: bool = False, allow_missing_keys: bool = False, @@ -1050,7 +1049,7 @@ def __init__( self.box_keys = box_keys_tuple[0] self.label_keys = ensure_tuple(label_keys) - self.spatial_size_: Union[Tuple[int, ...], Sequence[int], int] = spatial_size + self.spatial_size_: tuple[int, ...] | Sequence[int] | int = spatial_size if pos < 0 or neg < 0: raise ValueError(f"pos and neg must be nonnegative, got pos={pos} neg={neg}.") @@ -1071,7 +1070,7 @@ def __init__( if len(self.image_keys) != len(self.meta_keys): raise ValueError("meta_keys should have the same length as keys.") self.meta_key_postfix = ensure_tuple_rep(meta_key_postfix, len(self.image_keys)) - self.centers: Optional[List[List[int]]] = None + self.centers: list[list[int]] | None = None self.allow_smaller = allow_smaller def generate_fg_center_boxes_np(self, boxes: NdarrayOrTensor, image_size: Sequence[int]) -> np.ndarray: @@ -1104,9 +1103,9 @@ def randomize( # type: ignore self, boxes: NdarrayOrTensor, image_size: Sequence[int], - fg_indices: Optional[NdarrayOrTensor] = None, - bg_indices: Optional[NdarrayOrTensor] = None, - thresh_image: Optional[NdarrayOrTensor] = None, + fg_indices: NdarrayOrTensor | None = None, + bg_indices: NdarrayOrTensor | None = None, + thresh_image: NdarrayOrTensor | None = None, ) -> None: if fg_indices is None or bg_indices is None: # We don't require crop center to be within the boxes. @@ -1133,7 +1132,7 @@ def randomize( # type: ignore self.allow_smaller, ) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> List[Dict[Hashable, torch.Tensor]]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> list[dict[Hashable, torch.Tensor]]: d = dict(data) image_size = d[self.image_keys[0]].shape[1:] self.spatial_size = fall_back_tuple(self.spatial_size_, image_size) @@ -1150,7 +1149,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> List[Dict[Hashable, raise ValueError("no available ROI centers to crop.") # initialize returned list with shallow copy to preserve key ordering - results: List[Dict[Hashable, torch.Tensor]] = [dict(d) for _ in range(self.num_samples)] + results: list[dict[Hashable, torch.Tensor]] = [dict(d) for _ in range(self.num_samples)] # crop images and boxes for each center. for i, center in enumerate(self.centers): @@ -1198,7 +1197,7 @@ def __init__( box_keys: KeysCollection, box_ref_image_keys: KeysCollection, k: int = 1, - spatial_axes: Tuple[int, int] = (0, 1), + spatial_axes: tuple[int, int] = (0, 1), allow_missing_keys: bool = False, ) -> None: self.image_keys = ensure_tuple(image_keys) @@ -1225,7 +1224,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Mapping[Hashable, t d[key] = self.img_rotator(d[key]) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): @@ -1270,7 +1269,7 @@ def __init__( box_ref_image_keys: KeysCollection, prob: float = 0.1, max_k: int = 3, - spatial_axes: Tuple[int, int] = (0, 1), + spatial_axes: tuple[int, int] = (0, 1), allow_missing_keys: bool = False, ) -> None: self.image_keys = ensure_tuple(image_keys) @@ -1316,7 +1315,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Mapping[Hashable, t self.push_transform(d[key], extra_info=xform) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) if self._rand_k % 4 == 0: return d diff --git a/monai/apps/detection/utils/ATSS_matcher.py b/monai/apps/detection/utils/ATSS_matcher.py index c208fcd41c..cc9e238862 100644 --- a/monai/apps/detection/utils/ATSS_matcher.py +++ b/monai/apps/detection/utils/ATSS_matcher.py @@ -72,9 +72,12 @@ 5) add support for float16 cpu """ +from __future__ import annotations + import logging from abc import ABC, abstractmethod -from typing import Callable, Sequence, Tuple, TypeVar +from collections.abc import Callable, Sequence +from typing import TypeVar import torch from torch import Tensor @@ -103,7 +106,7 @@ def __init__(self, similarity_fn: Callable[[Tensor, Tensor], Tensor] = box_iou): def __call__( self, boxes: torch.Tensor, anchors: torch.Tensor, num_anchors_per_level: Sequence[int], num_anchors_per_loc: int - ) -> Tuple[torch.Tensor, torch.Tensor]: + ) -> tuple[torch.Tensor, torch.Tensor]: """ Compute matches for a single image @@ -141,7 +144,7 @@ def __call__( @abstractmethod def compute_matches( self, boxes: torch.Tensor, anchors: torch.Tensor, num_anchors_per_level: Sequence[int], num_anchors_per_loc: int - ) -> Tuple[torch.Tensor, torch.Tensor]: + ) -> tuple[torch.Tensor, torch.Tensor]: """ Compute matches @@ -194,7 +197,7 @@ def __init__( def compute_matches( self, boxes: torch.Tensor, anchors: torch.Tensor, num_anchors_per_level: Sequence[int], num_anchors_per_loc: int - ) -> Tuple[torch.Tensor, torch.Tensor]: + ) -> tuple[torch.Tensor, torch.Tensor]: """ Compute matches according to ATTS for a single image Adapted from diff --git a/monai/apps/detection/utils/anchor_utils.py b/monai/apps/detection/utils/anchor_utils.py index 55c256248a..379c972f6d 100644 --- a/monai/apps/detection/utils/anchor_utils.py +++ b/monai/apps/detection/utils/anchor_utils.py @@ -37,7 +37,9 @@ https://github.com/pytorch/vision/blob/release/0.12/torchvision/models/detection/anchor_utils.py """ -from typing import List, Sequence, Union +from __future__ import annotations + +from typing import List, Sequence import torch from torch import Tensor, nn @@ -148,7 +150,7 @@ def generate_anchors( scales: Sequence, aspect_ratios: Sequence, dtype: torch.dtype = torch.float32, - device: Union[torch.device, None] = None, + device: torch.device | None = None, ) -> torch.Tensor: """ Compute cell anchor shapes at multiple sizes and aspect ratios for the current feature map. @@ -215,7 +217,7 @@ def num_anchors_per_location(self): """ return [c.shape[0] for c in self.cell_anchors] - def grid_anchors(self, grid_sizes: List[List[int]], strides: List[List[Tensor]]) -> List[Tensor]: + def grid_anchors(self, grid_sizes: list[list[int]], strides: list[list[Tensor]]) -> list[Tensor]: """ Every combination of (a, (g, s), i) in (self.cell_anchors, zip(grid_sizes, strides), 0:spatial_dims) corresponds to a feature map. @@ -279,7 +281,7 @@ def grid_anchors(self, grid_sizes: List[List[int]], strides: List[List[Tensor]]) return anchors - def forward(self, images: Tensor, feature_maps: List[Tensor]) -> List[Tensor]: + def forward(self, images: Tensor, feature_maps: list[Tensor]) -> list[Tensor]: """ Generate anchor boxes for each image. @@ -366,13 +368,9 @@ class AnchorGeneratorWithAnchorShape(AnchorGenerator): def __init__( self, - feature_map_scales: Union[Sequence[int], Sequence[float]] = (1, 2, 4, 8), - base_anchor_shapes: Union[Sequence[Sequence[int]], Sequence[Sequence[float]]] = ( - (32, 32, 32), - (48, 20, 20), - (20, 48, 20), - (20, 20, 48), - ), + feature_map_scales: Sequence[int] | Sequence[float] = (1, 2, 4, 8), + base_anchor_shapes: Sequence[Sequence[int]] + | Sequence[Sequence[float]] = ((32, 32, 32), (48, 20, 20), (20, 48, 20), (20, 20, 48)), indexing: str = "ij", ) -> None: @@ -389,7 +387,7 @@ def __init__( @staticmethod def generate_anchors_using_shape( - anchor_shapes: torch.Tensor, dtype: torch.dtype = torch.float32, device: Union[torch.device, None] = None + anchor_shapes: torch.Tensor, dtype: torch.dtype = torch.float32, device: torch.device | None = None ) -> torch.Tensor: """ Compute cell anchor shapes at multiple sizes and aspect ratios for the current feature map. diff --git a/monai/apps/detection/utils/box_coder.py b/monai/apps/detection/utils/box_coder.py index 6458360fcd..2c7238a475 100644 --- a/monai/apps/detection/utils/box_coder.py +++ b/monai/apps/detection/utils/box_coder.py @@ -49,8 +49,10 @@ https://github.com/pytorch/vision/blob/main/torchvision/models/detection/_utils.py """ +from __future__ import annotations + import math -from typing import Sequence, Tuple, Union +from collections.abc import Sequence import torch from torch import Tensor @@ -120,14 +122,14 @@ class BoxCoder: # We expect gt_back to be equal to gt_boxes """ - def __init__(self, weights: Tuple[float], boxes_xform_clip: Union[float, None] = None) -> None: + def __init__(self, weights: tuple[float], boxes_xform_clip: float | None = None) -> None: if boxes_xform_clip is None: boxes_xform_clip = math.log(1000.0 / 16) self.spatial_dims = look_up_option(len(weights), [4, 6]) // 2 self.weights = weights self.boxes_xform_clip = boxes_xform_clip - def encode(self, gt_boxes: Sequence[Tensor], proposals: Sequence[Tensor]) -> Tuple[Tensor]: + def encode(self, gt_boxes: Sequence[Tensor], proposals: Sequence[Tensor]) -> tuple[Tensor]: """ Encode a set of proposals with respect to some ground truth (gt) boxes. @@ -146,7 +148,7 @@ def encode(self, gt_boxes: Sequence[Tensor], proposals: Sequence[Tensor]) -> Tup concat_proposals = torch.cat(tuple(proposals), dim=0) concat_targets = self.encode_single(concat_gt_boxes, concat_proposals) # split to tuple - targets: Tuple[Tensor] = concat_targets.split(boxes_per_image, 0) + targets: tuple[Tensor] = concat_targets.split(boxes_per_image, 0) return targets def encode_single(self, gt_boxes: Tensor, proposals: Tensor) -> Tensor: diff --git a/monai/apps/detection/utils/box_selector.py b/monai/apps/detection/utils/box_selector.py index e0e82dbef7..c3da858880 100644 --- a/monai/apps/detection/utils/box_selector.py +++ b/monai/apps/detection/utils/box_selector.py @@ -37,7 +37,9 @@ https://github.com/pytorch/vision/blob/main/torchvision/models/detection/retinanet.py """ -from typing import Callable, List, Tuple, Union +from __future__ import annotations + +from collections.abc import Callable import torch from torch import Tensor @@ -100,7 +102,7 @@ def __init__( self.nms_thresh = nms_thresh self.detections_per_img = detections_per_img - def select_top_score_idx_per_level(self, logits: Tensor) -> Tuple[Tensor, Tensor, Tensor]: + def select_top_score_idx_per_level(self, logits: Tensor) -> tuple[Tensor, Tensor, Tensor]: """ Select indices with highest scores. @@ -144,8 +146,8 @@ def select_top_score_idx_per_level(self, logits: Tensor) -> Tuple[Tensor, Tensor return topk_idxs, selected_scores, selected_labels # type: ignore def select_boxes_per_image( - self, boxes_list: List[Tensor], logits_list: List[Tensor], spatial_size: Union[List[int], Tuple[int]] - ) -> Tuple[Tensor, Tensor, Tensor]: + self, boxes_list: list[Tensor], logits_list: list[Tensor], spatial_size: list[int] | tuple[int] + ) -> tuple[Tensor, Tensor, Tensor]: """ Postprocessing to generate detection result from classification logits and boxes. diff --git a/monai/apps/detection/utils/detector_utils.py b/monai/apps/detection/utils/detector_utils.py index d7693da62c..64a1643101 100644 --- a/monai/apps/detection/utils/detector_utils.py +++ b/monai/apps/detection/utils/detector_utils.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, List, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn.functional as F @@ -20,7 +22,7 @@ from monai.utils import PytorchPadMode, ensure_tuple_rep -def check_input_images(input_images: Union[List[Tensor], Tensor], spatial_dims: int) -> None: +def check_input_images(input_images: list[Tensor] | Tensor, spatial_dims: int) -> None: """ Validate the input dimensionality (raise a `ValueError` if invalid). @@ -35,7 +37,7 @@ def check_input_images(input_images: Union[List[Tensor], Tensor], spatial_dims: "When input_images is a Tensor, its need to be (spatial_dims + 2)-D." f"In this case, it should be a {(spatial_dims + 2)}-D Tensor, got Tensor shape {input_images.shape}." ) - elif isinstance(input_images, List): + elif isinstance(input_images, list): for img in input_images: if len(img.shape) != spatial_dims + 1: raise ValueError( @@ -48,8 +50,8 @@ def check_input_images(input_images: Union[List[Tensor], Tensor], spatial_dims: def check_training_targets( - input_images: Union[List[Tensor], Tensor], - targets: Union[List[Dict[str, Tensor]], None], + input_images: list[Tensor] | Tensor, + targets: list[dict[str, Tensor]] | None, spatial_dims: int, target_label_key: str, target_box_key: str, @@ -89,12 +91,12 @@ def check_training_targets( def pad_images( - input_images: Union[List[Tensor], Tensor], + input_images: list[Tensor] | Tensor, spatial_dims: int, - size_divisible: Union[int, Sequence[int]], - mode: Union[PytorchPadMode, str] = PytorchPadMode.CONSTANT, + size_divisible: int | Sequence[int], + mode: PytorchPadMode | str = PytorchPadMode.CONSTANT, **kwargs, -) -> Tuple[Tensor, List[List[int]]]: +) -> tuple[Tensor, list[list[int]]]: """ Pad the input images, so that the output spatial sizes are divisible by `size_divisible`. It pads them at the end to create a (B, C, H, W) or (B, C, H, W, D) Tensor. @@ -157,12 +159,12 @@ def pad_images( def preprocess_images( - input_images: Union[List[Tensor], Tensor], + input_images: list[Tensor] | Tensor, spatial_dims: int, - size_divisible: Union[int, Sequence[int]], - mode: Union[PytorchPadMode, str] = PytorchPadMode.CONSTANT, + size_divisible: int | Sequence[int], + mode: PytorchPadMode | str = PytorchPadMode.CONSTANT, **kwargs, -) -> Tuple[Tensor, List[List[int]]]: +) -> tuple[Tensor, list[list[int]]]: """ Preprocess the input images, including diff --git a/monai/apps/detection/utils/hard_negative_sampler.py b/monai/apps/detection/utils/hard_negative_sampler.py index ee423bb4bc..4c8dcf5d45 100644 --- a/monai/apps/detection/utils/hard_negative_sampler.py +++ b/monai/apps/detection/utils/hard_negative_sampler.py @@ -29,8 +29,9 @@ https://github.com/MIC-DKFZ/nnDetection/blob/main/nndet/core/boxes/sampler.py """ +from __future__ import annotations + import logging -from typing import List, Tuple import torch from torch import Tensor @@ -125,7 +126,7 @@ def __init__( self.positive_fraction = positive_fraction logging.info("Sampling hard negatives on a per batch basis") - def __call__(self, target_labels: List[Tensor], concat_fg_probs: Tensor) -> Tuple[List[Tensor], List[Tensor]]: + def __call__(self, target_labels: list[Tensor], concat_fg_probs: Tensor) -> tuple[list[Tensor], list[Tensor]]: """ Select positives and hard negatives from list samples per image. Hard negative sampler will be applied to each image independently. @@ -158,8 +159,8 @@ def __call__(self, target_labels: List[Tensor], concat_fg_probs: Tensor) -> Tupl return self.select_samples_img_list(target_labels, fg_probs) def select_samples_img_list( - self, target_labels: List[Tensor], fg_probs: List[Tensor] - ) -> Tuple[List[Tensor], List[Tensor]]: + self, target_labels: list[Tensor], fg_probs: list[Tensor] + ) -> tuple[list[Tensor], list[Tensor]]: """ Select positives and hard negatives from list samples per image. Hard negative sampler will be applied to each image independently. @@ -205,7 +206,7 @@ def select_samples_img_list( return pos_idx, neg_idx - def select_samples_per_img(self, labels_per_img: Tensor, fg_probs_per_img: Tensor) -> Tuple[Tensor, Tensor]: + def select_samples_per_img(self, labels_per_img: Tensor, fg_probs_per_img: Tensor) -> tuple[Tensor, Tensor]: """ Select positives and hard negatives from samples. diff --git a/monai/apps/detection/utils/predict_utils.py b/monai/apps/detection/utils/predict_utils.py index a11aa97ce7..2bfabc51ec 100644 --- a/monai/apps/detection/utils/predict_utils.py +++ b/monai/apps/detection/utils/predict_utils.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, List, Optional +from __future__ import annotations import torch from torch import Tensor @@ -17,7 +17,7 @@ from monai.inferers import SlidingWindowInferer -def ensure_dict_value_to_list_(head_outputs: Dict[str, List[Tensor]], keys: Optional[List[str]] = None) -> None: +def ensure_dict_value_to_list_(head_outputs: dict[str, list[Tensor]], keys: list[str] | None = None) -> None: """ An in-place function. We expect ``head_outputs`` to be Dict[str, List[Tensor]]. Yet if it is Dict[str, Tensor], this func converts it to Dict[str, List[Tensor]]. @@ -41,7 +41,7 @@ def ensure_dict_value_to_list_(head_outputs: Dict[str, List[Tensor]], keys: Opti raise ValueError("The output of network should be Dict[str, List[Tensor]] or Dict[str, Tensor].") -def check_dict_values_same_length(head_outputs: Dict[str, List[Tensor]], keys: Optional[List[str]] = None) -> None: +def check_dict_values_same_length(head_outputs: dict[str, list[Tensor]], keys: list[str] | None = None) -> None: """ We expect the values in ``head_outputs``: Dict[str, List[Tensor]] to have the same length. Will raise ValueError if not. @@ -54,13 +54,13 @@ def check_dict_values_same_length(head_outputs: Dict[str, List[Tensor]], keys: O if keys is None: keys = list(head_outputs.keys()) - num_output_levels_list: List[int] = [len(head_outputs[k]) for k in keys] + num_output_levels_list: list[int] = [len(head_outputs[k]) for k in keys] num_output_levels = torch.unique(torch.tensor(num_output_levels_list)) if len(num_output_levels) != 1: raise ValueError(f"The values in the input dict should have the same length, Got {num_output_levels_list}.") -def _network_sequence_output(images: Tensor, network, keys: Optional[List[str]] = None) -> List[Tensor]: +def _network_sequence_output(images: Tensor, network, keys: list[str] | None = None) -> list[Tensor]: """ Decompose the output of network (a dict) into a list. @@ -84,8 +84,8 @@ def _network_sequence_output(images: Tensor, network, keys: Optional[List[str]] def predict_with_inferer( - images: Tensor, network, keys: List[str], inferer: Optional[SlidingWindowInferer] = None -) -> Dict[str, List[Tensor]]: + images: Tensor, network, keys: list[str], inferer: SlidingWindowInferer | None = None +) -> dict[str, list[Tensor]]: """ Predict network dict output with an inferer. Compared with directly output network(images), it enables a sliding window inferer that can be used to handle large inputs. diff --git a/monai/apps/mmars/__init__.py b/monai/apps/mmars/__init__.py index 8f1448bb06..5534ed6951 100644 --- a/monai/apps/mmars/__init__.py +++ b/monai/apps/mmars/__init__.py @@ -9,5 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .mmars import download_mmar, get_model_spec, load_from_mmar from .model_desc import MODEL_DESC, RemoteMMARKeys diff --git a/monai/apps/mmars/mmars.py b/monai/apps/mmars/mmars.py index 6e1770b19e..c3077833c7 100644 --- a/monai/apps/mmars/mmars.py +++ b/monai/apps/mmars/mmars.py @@ -15,11 +15,13 @@ - https://docs.nvidia.com/clara/clara-train-sdk/pt/mmar.html """ +from __future__ import annotations + import json import os import warnings +from collections.abc import Mapping from pathlib import Path -from typing import Mapping, Optional, Union import torch @@ -35,7 +37,7 @@ __all__ = ["get_model_spec", "download_mmar", "load_from_mmar"] -def get_model_spec(idx: Union[int, str]): +def get_model_spec(idx: int | str): """get model specification by `idx`. `idx` could be index of the constant tuple of dict or the actual model ID.""" if isinstance(idx, int): return MODEL_DESC[idx] @@ -100,9 +102,7 @@ def _get_ngc_doc_url(model_name: str, model_prefix=""): return f"https://ngc.nvidia.com/catalog/models/{model_prefix}{model_name}" -def download_mmar( - item, mmar_dir: Optional[PathLike] = None, progress: bool = True, api: bool = True, version: int = -1 -): +def download_mmar(item, mmar_dir: PathLike | None = None, progress: bool = True, api: bool = True, version: int = -1): """ Download and extract Medical Model Archive (MMAR) from Nvidia Clara Train. @@ -183,7 +183,7 @@ def download_mmar( def load_from_mmar( item, - mmar_dir: Optional[PathLike] = None, + mmar_dir: PathLike | None = None, progress: bool = True, version: int = -1, map_location=None, diff --git a/monai/apps/mmars/model_desc.py b/monai/apps/mmars/model_desc.py index e0a7f26117..a3963689fb 100644 --- a/monai/apps/mmars/model_desc.py +++ b/monai/apps/mmars/model_desc.py @@ -15,8 +15,10 @@ - https://docs.nvidia.com/clara/clara-train-sdk/pt/mmar.html """ +from __future__ import annotations + import os -from typing import Any, Dict, Tuple +from typing import Any __all__ = ["MODEL_DESC", "RemoteMMARKeys"] @@ -39,7 +41,7 @@ class RemoteMMARKeys: VERSION = "version" # version of the MMAR -MODEL_DESC: Tuple[Dict[Any, Any], ...] = ( +MODEL_DESC: tuple[dict[Any, Any], ...] = ( { RemoteMMARKeys.ID: "clara_pt_spleen_ct_segmentation_1", RemoteMMARKeys.NAME: "clara_pt_spleen_ct_segmentation", diff --git a/monai/apps/nuclick/transforms.py b/monai/apps/nuclick/transforms.py index f080961e4c..a9fbaa3144 100644 --- a/monai/apps/nuclick/transforms.py +++ b/monai/apps/nuclick/transforms.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import math -from typing import Any, Optional, Tuple, Union +from typing import Any import numpy as np import torch @@ -87,7 +89,7 @@ def __init__( self, keys: KeysCollection, centroid_key: str = NuclickKeys.CENTROID, - patch_size: Union[Tuple[int, int], int] = 128, + patch_size: tuple[int, int] | int = 128, allow_missing_keys: bool = False, **kwargs: Any, ): @@ -144,7 +146,7 @@ def __init__( self, keys: KeysCollection, others: str = NuclickKeys.OTHERS, - mask_value: Optional[str] = NuclickKeys.MASK_VALUE, + mask_value: str | None = NuclickKeys.MASK_VALUE, min_area: int = 5, others_value: int = 0, to_binary_mask: bool = True, diff --git a/monai/apps/pathology/__init__.py b/monai/apps/pathology/__init__.py index da5cec7e6c..3de1c754c2 100644 --- a/monai/apps/pathology/__init__.py +++ b/monai/apps/pathology/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .data import MaskedInferenceWSIDataset, PatchWSIDataset, SmartCachePatchWSIDataset from .handlers import ProbMapProducer from .losses import HoVerNetLoss diff --git a/monai/apps/pathology/data/__init__.py b/monai/apps/pathology/data/__init__.py index e1b2ef7bd2..cfdd71cfe3 100644 --- a/monai/apps/pathology/data/__init__.py +++ b/monai/apps/pathology/data/__init__.py @@ -9,4 +9,6 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .datasets import MaskedInferenceWSIDataset, PatchWSIDataset, SmartCachePatchWSIDataset diff --git a/monai/apps/pathology/data/datasets.py b/monai/apps/pathology/data/datasets.py index 23ac9ac062..46ee9e3aa5 100644 --- a/monai/apps/pathology/data/datasets.py +++ b/monai/apps/pathology/data/datasets.py @@ -9,9 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import sys -from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union, cast +from collections.abc import Callable, Sequence +from typing import Tuple, cast import numpy as np @@ -52,11 +55,11 @@ class PatchWSIDataset(Dataset): def __init__( self, - data: List, - region_size: Union[int, Tuple[int, int]], - grid_shape: Union[int, Tuple[int, int]], - patch_size: Union[int, Tuple[int, int]], - transform: Optional[Callable] = None, + data: list, + region_size: int | tuple[int, int], + grid_shape: int | tuple[int, int], + patch_size: int | tuple[int, int], + transform: Callable | None = None, image_reader_name: str = "cuCIM", **kwargs, ): @@ -69,7 +72,7 @@ def __init__( self.image_path_list = list({x["image"] for x in self.data}) self.image_reader_name = image_reader_name.lower() self.image_reader = WSIReader(backend=image_reader_name, **kwargs) - self.wsi_object_dict: Optional[Dict] = None + self.wsi_object_dict: dict | None = None if self.image_reader_name != "openslide": # OpenSlide causes memory issue if we prefetch image objects self._fetch_wsi_objects() @@ -85,7 +88,7 @@ def __getitem__(self, index): if self.image_reader_name == "openslide": img_obj = self.image_reader.read(sample["image"]) else: - img_obj = cast(Dict, self.wsi_object_dict)[sample["image"]] + img_obj = cast(dict, self.wsi_object_dict)[sample["image"]] location = [sample["location"][i] - self.region_size[i] // 2 for i in range(len(self.region_size))] images, _ = self.image_reader.get_data( img=img_obj, @@ -140,17 +143,17 @@ class SmartCachePatchWSIDataset(SmartCacheDataset): def __init__( self, - data: List, - region_size: Union[int, Tuple[int, int]], - grid_shape: Union[int, Tuple[int, int]], - patch_size: Union[int, Tuple[int, int]], - transform: Union[Sequence[Callable], Callable], + data: list, + region_size: int | tuple[int, int], + grid_shape: int | tuple[int, int], + patch_size: int | tuple[int, int], + transform: Sequence[Callable] | Callable, image_reader_name: str = "cuCIM", replace_rate: float = 0.5, cache_num: int = sys.maxsize, cache_rate: float = 1.0, - num_init_workers: Optional[int] = 1, - num_replace_workers: Optional[int] = 1, + num_init_workers: int | None = 1, + num_replace_workers: int | None = 1, progress: bool = True, copy_cache: bool = True, as_contiguous: bool = True, @@ -201,9 +204,9 @@ class MaskedInferenceWSIDataset(Dataset): def __init__( self, - data: List[Dict["str", "str"]], - patch_size: Union[int, Tuple[int, int]], - transform: Optional[Callable] = None, + data: list[dict[str, str]], + patch_size: int | tuple[int, int], + transform: Callable | None = None, image_reader_name: str = "cuCIM", **kwargs, ) -> None: @@ -223,14 +226,14 @@ def __init__( self.num_patches = sum(self.num_patches_per_sample) self.cum_num_patches = np.cumsum([0] + self.num_patches_per_sample[:-1]) - def _prepare_data(self, input_data: List[Dict["str", "str"]]) -> List[Dict]: + def _prepare_data(self, input_data: list[dict[str, str]]) -> list[dict]: prepared_data = [] for sample in input_data: prepared_sample = self._prepare_a_sample(sample) prepared_data.append(prepared_sample) return prepared_data - def _prepare_a_sample(self, sample: Dict["str", "str"]) -> Dict: + def _prepare_a_sample(self, sample: dict[str, str]) -> dict: """ Preprocess input data to load WSIReader object and the foreground mask, and define the locations where patches need to be extracted. @@ -272,7 +275,7 @@ def _prepare_a_sample(self, sample: Dict["str", "str"]) -> Dict: "level": level, } - def _calculate_mask_level(self, image: np.ndarray, mask: np.ndarray) -> Tuple[int, float]: + def _calculate_mask_level(self, image: np.ndarray, mask: np.ndarray) -> tuple[int, float]: """ Calculate level of the mask and its ratio with respect to the whole slide image diff --git a/monai/apps/pathology/engines/__init__.py b/monai/apps/pathology/engines/__init__.py index 68c084d40d..b32c148c2e 100644 --- a/monai/apps/pathology/engines/__init__.py +++ b/monai/apps/pathology/engines/__init__.py @@ -9,4 +9,6 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .utils import PrepareBatchHoVerNet diff --git a/monai/apps/pathology/engines/utils.py b/monai/apps/pathology/engines/utils.py index 895638a01b..5fa997480d 100644 --- a/monai/apps/pathology/engines/utils.py +++ b/monai/apps/pathology/engines/utils.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, Optional, Sequence, Union +from __future__ import annotations + +from typing import Sequence import torch @@ -38,8 +40,8 @@ def __init__(self, extra_keys: Sequence[str]) -> None: def __call__( self, - batchdata: Dict[str, torch.Tensor], - device: Optional[Union[str, torch.device]] = None, + batchdata: dict[str, torch.Tensor], + device: str | torch.device | None = None, non_blocking: bool = False, **kwargs, ): diff --git a/monai/apps/pathology/handlers/__init__.py b/monai/apps/pathology/handlers/__init__.py index 0638950bd8..3f2aab272f 100644 --- a/monai/apps/pathology/handlers/__init__.py +++ b/monai/apps/pathology/handlers/__init__.py @@ -9,4 +9,6 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .prob_map_producer import ProbMapProducer diff --git a/monai/apps/pathology/handlers/prob_map_producer.py b/monai/apps/pathology/handlers/prob_map_producer.py index d5b1b50c47..7e8c4800eb 100644 --- a/monai/apps/pathology/handlers/prob_map_producer.py +++ b/monai/apps/pathology/handlers/prob_map_producer.py @@ -9,9 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import os -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING import numpy as np @@ -38,11 +40,7 @@ class ProbMapProducer: """ def __init__( - self, - output_dir: str = "./", - output_postfix: str = "", - dtype: DtypeLike = np.float64, - name: Optional[str] = None, + self, output_dir: str = "./", output_postfix: str = "", dtype: DtypeLike = np.float64, name: str | None = None ) -> None: """ Args: @@ -57,9 +55,9 @@ def __init__( self.output_dir = output_dir self.output_postfix = output_postfix self.dtype = dtype - self.prob_map: Dict[str, np.ndarray] = {} - self.level: Dict[str, int] = {} - self.counter: Dict[str, int] = {} + self.prob_map: dict[str, np.ndarray] = {} + self.level: dict[str, int] = {} + self.counter: dict[str, int] = {} self.num_done_images: int = 0 self.num_images: int = 0 diff --git a/monai/apps/pathology/handlers/utils.py b/monai/apps/pathology/handlers/utils.py index 8daac57143..777989e1ac 100644 --- a/monai/apps/pathology/handlers/utils.py +++ b/monai/apps/pathology/handlers/utils.py @@ -8,7 +8,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from typing import Hashable, Tuple + +from __future__ import annotations + +from collections.abc import Hashable from monai.config import KeysCollection from monai.utils import ensure_tuple @@ -38,7 +41,7 @@ def from_engine_hovernet(keys: KeysCollection, nested_key: str): nested_key: specified key to extract nested data from dictionary or decollated list of dictionaries. """ - _keys: Tuple[Hashable, ...] = ensure_tuple(keys) + _keys: tuple[Hashable, ...] = ensure_tuple(keys) def _wrapper(data): if isinstance(data, dict): diff --git a/monai/apps/pathology/inferers/__init__.py b/monai/apps/pathology/inferers/__init__.py index c3571c87a3..3549b8ec29 100644 --- a/monai/apps/pathology/inferers/__init__.py +++ b/monai/apps/pathology/inferers/__init__.py @@ -9,4 +9,6 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .inferer import SlidingWindowHoVerNetInferer diff --git a/monai/apps/pathology/inferers/inferer.py b/monai/apps/pathology/inferers/inferer.py index 1aacb5d99d..b776b79079 100644 --- a/monai/apps/pathology/inferers/inferer.py +++ b/monai/apps/pathology/inferers/inferer.py @@ -9,7 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Callable, Sequence +from typing import Any import numpy as np import torch @@ -73,19 +76,19 @@ class SlidingWindowHoVerNetInferer(SlidingWindowInferer): def __init__( self, - roi_size: Union[Sequence[int], int], + roi_size: Sequence[int] | int, sw_batch_size: int = 1, overlap: float = 0.25, - mode: Union[BlendMode, str] = BlendMode.CONSTANT, - sigma_scale: Union[Sequence[float], float] = 0.125, - padding_mode: Union[PytorchPadMode, str] = PytorchPadMode.CONSTANT, + mode: BlendMode | str = BlendMode.CONSTANT, + sigma_scale: Sequence[float] | float = 0.125, + padding_mode: PytorchPadMode | str = PytorchPadMode.CONSTANT, cval: float = 0.0, - sw_device: Optional[Union[torch.device, str]] = None, - device: Optional[Union[torch.device, str]] = None, + sw_device: torch.device | str | None = None, + device: torch.device | str | None = None, progress: bool = False, cache_roi_weight_map: bool = False, - cpu_thresh: Optional[int] = None, - extra_input_padding: Optional[Tuple[int]] = None, + cpu_thresh: int | None = None, + extra_input_padding: tuple[int] | None = None, ) -> None: super().__init__( roi_size=roi_size, @@ -130,10 +133,10 @@ def process_output(self, seg_prob_tuple, window_data, importance_map_): def __call__( self, inputs: torch.Tensor, - network: Callable[..., Union[torch.Tensor, Sequence[torch.Tensor], Dict[Any, torch.Tensor]]], + network: Callable[..., torch.Tensor | Sequence[torch.Tensor] | dict[Any, torch.Tensor]], *args: Any, **kwargs: Any, - ) -> Union[torch.Tensor, Tuple[torch.Tensor, ...], Dict[Any, torch.Tensor]]: + ) -> torch.Tensor | tuple[torch.Tensor, ...] | dict[Any, torch.Tensor]: """ Args: @@ -179,7 +182,7 @@ def __call__( ) if self.extra_input_padding: - extra_slicing: List[slice] = [] + extra_slicing: list[slice] = [] num_padded_dims = len(self.extra_input_padding) // 2 for sp in range(num_padded_dims): slice_dim = slice( diff --git a/monai/apps/pathology/losses/__init__.py b/monai/apps/pathology/losses/__init__.py index 5e960b34cf..09c4d43836 100644 --- a/monai/apps/pathology/losses/__init__.py +++ b/monai/apps/pathology/losses/__init__.py @@ -9,4 +9,6 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .hovernet_loss import HoVerNetLoss diff --git a/monai/apps/pathology/losses/hovernet_loss.py b/monai/apps/pathology/losses/hovernet_loss.py index 5f35d9c509..72b4d8a6ff 100644 --- a/monai/apps/pathology/losses/hovernet_loss.py +++ b/monai/apps/pathology/losses/hovernet_loss.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict +from __future__ import annotations import torch from torch.nn import CrossEntropyLoss @@ -95,7 +95,7 @@ def _mse_gradient_loss(self, prediction: torch.Tensor, target: torch.Tensor, foc return loss - def forward(self, prediction: Dict[str, torch.Tensor], target: Dict[str, torch.Tensor]) -> torch.Tensor: + def forward(self, prediction: dict[str, torch.Tensor], target: dict[str, torch.Tensor]) -> torch.Tensor: """ Args: prediction: dictionary of predicted outputs for three branches, diff --git a/monai/apps/pathology/metrics/__init__.py b/monai/apps/pathology/metrics/__init__.py index f19811dcaf..4f77d6a852 100644 --- a/monai/apps/pathology/metrics/__init__.py +++ b/monai/apps/pathology/metrics/__init__.py @@ -9,4 +9,6 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .lesion_froc import LesionFROC diff --git a/monai/apps/pathology/metrics/lesion_froc.py b/monai/apps/pathology/metrics/lesion_froc.py index 6c7965bae6..67e8a8510a 100644 --- a/monai/apps/pathology/metrics/lesion_froc.py +++ b/monai/apps/pathology/metrics/lesion_froc.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Dict, List, Tuple +from __future__ import annotations + +from typing import TYPE_CHECKING import numpy as np @@ -63,10 +65,10 @@ class LesionFROC: def __init__( self, - data: List[Dict], + data: list[dict], grow_distance: int = 75, itc_diameter: int = 200, - eval_thresholds: Tuple = (0.25, 0.5, 1, 2, 4, 8), + eval_thresholds: tuple = (0.25, 0.5, 1, 2, 4, 8), nms_sigma: float = 0.0, nms_prob_threshold: float = 0.5, nms_box_size: int = 48, @@ -80,7 +82,7 @@ def __init__( self.image_reader = WSIReader(image_reader_name) self.nms = PathologyProbNMS(sigma=nms_sigma, prob_threshold=nms_prob_threshold, box_size=nms_box_size) - def prepare_inference_result(self, sample: Dict): + def prepare_inference_result(self, sample: dict): """ Prepare the probability map for detection evaluation. diff --git a/monai/apps/pathology/transforms/__init__.py b/monai/apps/pathology/transforms/__init__.py index c30d2c1a76..af2254074a 100644 --- a/monai/apps/pathology/transforms/__init__.py +++ b/monai/apps/pathology/transforms/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .post.array import ( GenerateDistanceMap, GenerateInstanceBorder, diff --git a/monai/apps/pathology/transforms/post/__init__.py b/monai/apps/pathology/transforms/post/__init__.py index c5b928b991..dfdf7d31eb 100644 --- a/monai/apps/pathology/transforms/post/__init__.py +++ b/monai/apps/pathology/transforms/post/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .array import ( GenerateDistanceMap, GenerateInstanceBorder, diff --git a/monai/apps/pathology/transforms/post/array.py b/monai/apps/pathology/transforms/post/array.py index aa54147b3d..ba005706a5 100644 --- a/monai/apps/pathology/transforms/post/array.py +++ b/monai/apps/pathology/transforms/post/array.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union +from typing import Callable, Sequence import numpy as np import torch @@ -68,12 +70,12 @@ class Watershed(Transform): backend = [TransformBackends.NUMPY] - def __init__(self, connectivity: Optional[int] = 1, dtype: DtypeLike = np.int64) -> None: + def __init__(self, connectivity: int | None = 1, dtype: DtypeLike = np.int64) -> None: self.connectivity = connectivity self.dtype = dtype def __call__( - self, image: NdarrayOrTensor, mask: Optional[NdarrayOrTensor] = None, markers: Optional[NdarrayOrTensor] = None + self, image: NdarrayOrTensor, mask: NdarrayOrTensor | None = None, markers: NdarrayOrTensor | None = None ) -> NdarrayOrTensor: """ Args: @@ -112,8 +114,8 @@ class GenerateWatershedMask(Transform): def __init__( self, - activation: Union[str, Callable] = "softmax", - threshold: Optional[float] = None, + activation: str | Callable = "softmax", + threshold: float | None = None, min_object_size: int = 10, dtype: DtypeLike = np.uint8, ) -> None: @@ -249,7 +251,7 @@ class GenerateDistanceMap(Transform): backend = [TransformBackends.NUMPY] - def __init__(self, smooth_fn: Optional[Callable] = None, dtype: DtypeLike = np.float32) -> None: + def __init__(self, smooth_fn: Callable | None = None, dtype: DtypeLike = np.float32) -> None: self.smooth_fn = smooth_fn if smooth_fn is not None else GaussianSmooth() self.dtype = dtype @@ -304,7 +306,7 @@ def __init__( threshold: float = 0.4, radius: int = 2, min_object_size: int = 10, - postprocess_fn: Optional[Callable] = None, + postprocess_fn: Callable | None = None, dtype: DtypeLike = np.int64, ) -> None: self.threshold = threshold @@ -367,7 +369,7 @@ def __init__(self, height: int, width: int) -> None: self.height = height self.width = width - def _generate_contour_coord(self, current: np.ndarray, previous: np.ndarray) -> Tuple[int, int]: + def _generate_contour_coord(self, current: np.ndarray, previous: np.ndarray) -> tuple[int, int]: """ Generate contour coordinates. Given the previous and current coordinates of border positions, returns the int pixel that marks the extremity of the segmented pixels. @@ -394,7 +396,7 @@ def _generate_contour_coord(self, current: np.ndarray, previous: np.ndarray) -> return row, col - def _calculate_distance_from_top_left(self, sequence: Sequence[Tuple[int, int]]) -> int: + def _calculate_distance_from_top_left(self, sequence: Sequence[tuple[int, int]]) -> int: """ Each sequence of coordinates describes a boundary between foreground and background starting and ending at two sides of the bounding box. To order the sequences correctly, we compute the distance from the top-left of the bounding box @@ -419,18 +421,18 @@ def _calculate_distance_from_top_left(self, sequence: Sequence[Tuple[int, int]]) return distance - def __call__(self, contours: List[np.ndarray]) -> np.ndarray: + def __call__(self, contours: list[np.ndarray]) -> np.ndarray: """ Args: contours: list of (n, 2)-ndarrays, scipy-style clockwise line segments, with lines separating foreground/background. Each contour is an ndarray of shape (n, 2), consisting of n (row, column) coordinates along the contour. """ - pixels: List[Tuple[int, int]] = [] + pixels: list[tuple[int, int]] = [] sequences = [] corners = [False, False, False, False] for group in contours: - sequence: List[Tuple[int, int]] = [] + sequence: list[tuple[int, int]] = [] last_added = None prev = None corner = -1 @@ -544,11 +546,11 @@ class GenerateInstanceContour(Transform): backend = [TransformBackends.NUMPY] - def __init__(self, min_num_points: int = 3, contour_level: Optional[float] = None) -> None: + def __init__(self, min_num_points: int = 3, contour_level: float | None = None) -> None: self.contour_level = contour_level self.min_num_points = min_num_points - def __call__(self, inst_mask: NdarrayOrTensor, offset: Optional[Sequence[int]] = (0, 0)) -> Optional[np.ndarray]: + def __call__(self, inst_mask: NdarrayOrTensor, offset: Sequence[int] | None = (0, 0)) -> np.ndarray | None: """ Args: inst_mask: segmentation mask for a single instance. Shape should be [1, H, W, [D]] @@ -587,10 +589,10 @@ class GenerateInstanceCentroid(Transform): backend = [TransformBackends.NUMPY] - def __init__(self, dtype: Optional[DtypeLike] = int) -> None: + def __init__(self, dtype: DtypeLike | None = int) -> None: self.dtype = dtype - def __call__(self, inst_mask: NdarrayOrTensor, offset: Union[Sequence[int], int] = 0) -> NdarrayOrTensor: + def __call__(self, inst_mask: NdarrayOrTensor, offset: Sequence[int] | int = 0) -> NdarrayOrTensor: """ Args: inst_mask: segmentation mask for a single instance. Shape should be [1, H, W, [D]] @@ -618,7 +620,7 @@ class GenerateInstanceType(Transform): def __call__( # type: ignore self, type_pred: NdarrayOrTensor, seg_pred: NdarrayOrTensor, bbox: np.ndarray, instance_id: int - ) -> Tuple[int, float]: + ) -> tuple[int, float]: """ Args: type_pred: pixel-level type prediction map after activation function. @@ -674,17 +676,17 @@ class HoVerNetInstanceMapPostProcessing(Transform): def __init__( self, - activation: Union[str, Callable] = "softmax", - mask_threshold: Optional[float] = None, + activation: str | Callable = "softmax", + mask_threshold: float | None = None, min_object_size: int = 10, sobel_kernel_size: int = 5, - distance_smooth_fn: Optional[Callable] = None, + distance_smooth_fn: Callable | None = None, marker_threshold: float = 0.4, marker_radius: int = 2, - marker_postprocess_fn: Optional[Callable] = None, - watershed_connectivity: Optional[int] = 1, + marker_postprocess_fn: Callable | None = None, + watershed_connectivity: int | None = 1, min_num_points: int = 3, - contour_level: Optional[float] = None, + contour_level: float | None = None, ) -> None: super().__init__() @@ -707,7 +709,7 @@ def __init__( def __call__( # type: ignore self, nuclear_prediction: NdarrayOrTensor, hover_map: NdarrayOrTensor - ) -> Tuple[Dict, NdarrayOrTensor]: + ) -> tuple[dict, NdarrayOrTensor]: """post-process instance segmentation branches (NP and HV) to generate instance segmentation map. Args: @@ -761,10 +763,7 @@ class HoVerNetNuclearTypePostProcessing(Transform): """ def __init__( - self, - activation: Union[str, Callable] = "softmax", - threshold: Optional[float] = None, - return_type_map: bool = True, + self, activation: str | Callable = "softmax", threshold: float | None = None, return_type_map: bool = True ) -> None: super().__init__() self.return_type_map = return_type_map @@ -795,8 +794,8 @@ def __init__( self.as_discrete = AsDiscrete(threshold=threshold, argmax=use_softmax) def __call__( # type: ignore - self, type_prediction: NdarrayOrTensor, instance_info: Dict[int, Dict], instance_map: NdarrayOrTensor - ) -> Tuple[Dict, Optional[NdarrayOrTensor]]: + self, type_prediction: NdarrayOrTensor, instance_info: dict[int, dict], instance_map: NdarrayOrTensor + ) -> tuple[dict, NdarrayOrTensor | None]: """Process NC (type prediction) branch and combine it with instance segmentation It updates the instance_info with instance type and associated probability, and generate instance type map. diff --git a/monai/apps/pathology/transforms/post/dictionary.py b/monai/apps/pathology/transforms/post/dictionary.py index ee9654eaca..ef6de1b596 100644 --- a/monai/apps/pathology/transforms/post/dictionary.py +++ b/monai/apps/pathology/transforms/post/dictionary.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Dict, Hashable, Mapping, Optional, Union +from __future__ import annotations + +from collections.abc import Callable, Hashable, Mapping import numpy as np @@ -100,9 +102,9 @@ class Watershedd(MapTransform): def __init__( self, keys: KeysCollection, - mask_key: Optional[str] = "mask", - markers_key: Optional[str] = None, - connectivity: Optional[int] = 1, + mask_key: str | None = "mask", + markers_key: str | None = None, + connectivity: int | None = 1, dtype: DtypeLike = np.uint8, allow_missing_keys: bool = False, ) -> None: @@ -111,7 +113,7 @@ def __init__( self.markers_key = markers_key self.transform = Watershed(connectivity=connectivity, dtype=dtype) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) markers = d[self.markers_key] if self.markers_key else None mask = d[self.mask_key] if self.mask_key else None @@ -144,8 +146,8 @@ def __init__( self, keys: KeysCollection, mask_key: str = "mask", - activation: Union[str, Callable] = "softmax", - threshold: Optional[float] = None, + activation: str | Callable = "softmax", + threshold: float | None = None, min_object_size: int = 10, dtype: DtypeLike = np.uint8, allow_missing_keys: bool = False, @@ -156,7 +158,7 @@ def __init__( activation=activation, threshold=threshold, min_object_size=min_object_size, dtype=dtype ) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): mask = self.transform(d[key]) @@ -199,7 +201,7 @@ def __init__( self.border_key = border_key self.transform = GenerateInstanceBorder(kernel_size=kernel_size, dtype=dtype) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) if self.border_key in d: raise KeyError(f"The key '{self.border_key}' for instance border map already exists.") @@ -227,7 +229,7 @@ def __init__( mask_key: str = "mask", border_key: str = "border", dist_map_key: str = "dist_map", - smooth_fn: Optional[Callable] = None, + smooth_fn: Callable | None = None, dtype: DtypeLike = np.float32, ) -> None: self.mask_key = mask_key @@ -235,7 +237,7 @@ def __init__( self.dist_map_key = dist_map_key self.transform = GenerateDistanceMap(smooth_fn=smooth_fn, dtype=dtype) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) if self.dist_map_key in d: raise KeyError(f"The key '{self.dist_map_key}' for distance map already exists.") @@ -270,7 +272,7 @@ def __init__( threshold: float = 0.4, radius: int = 2, min_object_size: int = 10, - postprocess_fn: Optional[Callable] = None, + postprocess_fn: Callable | None = None, dtype: DtypeLike = np.uint8, ) -> None: self.mask_key = mask_key @@ -284,7 +286,7 @@ def __init__( dtype=dtype, ) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) if self.markers_key in d: raise KeyError(f"The key '{self.markers_key}' for markers already exists.") @@ -345,9 +347,9 @@ def __init__( self, keys: KeysCollection, contour_key_postfix: str = "contour", - offset_key: Optional[str] = None, + offset_key: str | None = None, min_num_points: int = 3, - level: Optional[float] = None, + level: float | None = None, allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) @@ -388,8 +390,8 @@ def __init__( self, keys: KeysCollection, centroid_key_postfix: str = "centroid", - offset_key: Optional[str] = None, - dtype: Optional[DtypeLike] = int, + offset_key: str | None = None, + dtype: DtypeLike | None = int, allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) @@ -494,17 +496,17 @@ def __init__( hover_map_key: str = HoVerNetBranch.HV.value, instance_info_key: str = "instance_info", instance_map_key: str = "instance_map", - activation: Union[str, Callable] = "softmax", - mask_threshold: Optional[float] = None, + activation: str | Callable = "softmax", + mask_threshold: float | None = None, min_object_size: int = 10, sobel_kernel_size: int = 5, - distance_smooth_fn: Optional[Callable] = None, + distance_smooth_fn: Callable | None = None, marker_threshold: float = 0.4, marker_radius: int = 2, - marker_postprocess_fn: Optional[Callable] = None, - watershed_connectivity: Optional[int] = 1, + marker_postprocess_fn: Callable | None = None, + watershed_connectivity: int | None = 1, min_num_points: int = 3, - contour_level: Optional[float] = None, + contour_level: float | None = None, ) -> None: super().__init__() self.instance_map_post_process = HoVerNetInstanceMapPostProcessing( @@ -561,8 +563,8 @@ def __init__( instance_info_key: str = "instance_info", instance_map_key: str = "instance_map", type_map_key: str = "type_map", - activation: Union[str, Callable] = "softmax", - threshold: Optional[float] = None, + activation: str | Callable = "softmax", + threshold: float | None = None, return_type_map: bool = True, ) -> None: super().__init__() diff --git a/monai/apps/pathology/transforms/spatial/__init__.py b/monai/apps/pathology/transforms/spatial/__init__.py index eed111d2b6..7e0f6c75d0 100644 --- a/monai/apps/pathology/transforms/spatial/__init__.py +++ b/monai/apps/pathology/transforms/spatial/__init__.py @@ -9,5 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .array import SplitOnGrid, TileOnGrid from .dictionary import SplitOnGridd, SplitOnGridD, SplitOnGridDict, TileOnGridd, TileOnGridD, TileOnGridDict diff --git a/monai/apps/pathology/transforms/spatial/array.py b/monai/apps/pathology/transforms/spatial/array.py index ce22b86d49..9733156c85 100644 --- a/monai/apps/pathology/transforms/spatial/array.py +++ b/monai/apps/pathology/transforms/spatial/array.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import numpy as np import torch @@ -41,9 +43,7 @@ class SplitOnGrid(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__( - self, grid_size: Union[int, Tuple[int, int]] = (2, 2), patch_size: Optional[Union[int, Tuple[int, int]]] = None - ): + def __init__(self, grid_size: int | tuple[int, int] = (2, 2), patch_size: int | tuple[int, int] | None = None): # Grid size if isinstance(grid_size, int): self.grid_size = (grid_size, grid_size) @@ -137,9 +137,9 @@ class TileOnGrid(Randomizable, Transform): def __init__( self, - tile_count: Optional[int] = None, + tile_count: int | None = None, tile_size: int = 256, - step: Optional[int] = None, + step: int | None = None, random_offset: bool = False, pad_full: bool = False, background_val: int = 255, diff --git a/monai/apps/pathology/transforms/spatial/dictionary.py b/monai/apps/pathology/transforms/spatial/dictionary.py index 022d82a053..a304ea6d62 100644 --- a/monai/apps/pathology/transforms/spatial/dictionary.py +++ b/monai/apps/pathology/transforms/spatial/dictionary.py @@ -9,8 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import copy -from typing import Any, Dict, Hashable, List, Mapping, Optional, Tuple, Union +from collections.abc import Hashable, Mapping +from typing import Any from monai.config import KeysCollection from monai.config.type_definitions import NdarrayOrTensor @@ -43,14 +46,14 @@ class SplitOnGridd(MapTransform): def __init__( self, keys: KeysCollection, - grid_size: Union[int, Tuple[int, int]] = (2, 2), - patch_size: Optional[Union[int, Tuple[int, int]]] = None, + grid_size: int | tuple[int, int] = (2, 2), + patch_size: int | tuple[int, int] | None = None, allow_missing_keys: bool = False, ): super().__init__(keys, allow_missing_keys) self.splitter = SplitOnGrid(grid_size=grid_size, patch_size=patch_size) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.splitter(d[key]) @@ -87,9 +90,9 @@ class TileOnGridd(Randomizable, MapTransform): def __init__( self, keys: KeysCollection, - tile_count: Optional[int] = None, + tile_count: int | None = None, tile_size: int = 256, - step: Optional[int] = None, + step: int | None = None, random_offset: bool = False, pad_full: bool = False, background_val: int = 255, @@ -117,7 +120,7 @@ def randomize(self, data: Any = None) -> None: def __call__( self, data: Mapping[Hashable, NdarrayOrTensor] - ) -> Union[Dict[Hashable, NdarrayOrTensor], List[Dict[Hashable, NdarrayOrTensor]]]: + ) -> dict[Hashable, NdarrayOrTensor] | list[dict[Hashable, NdarrayOrTensor]]: self.randomize() diff --git a/monai/apps/pathology/transforms/stain/__init__.py b/monai/apps/pathology/transforms/stain/__init__.py index dfa235de55..3239fcbce3 100644 --- a/monai/apps/pathology/transforms/stain/__init__.py +++ b/monai/apps/pathology/transforms/stain/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .array import ExtractHEStains, NormalizeHEStains from .dictionary import ( ExtractHEStainsd, diff --git a/monai/apps/pathology/transforms/stain/array.py b/monai/apps/pathology/transforms/stain/array.py index 3b3a293451..5df9ad7ef3 100644 --- a/monai/apps/pathology/transforms/stain/array.py +++ b/monai/apps/pathology/transforms/stain/array.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Union +from __future__ import annotations import numpy as np @@ -37,11 +37,7 @@ class ExtractHEStains(Transform): """ def __init__( - self, - tli: float = 240, - alpha: float = 1, - beta: float = 0.15, - max_cref: Union[tuple, np.ndarray] = (1.9705, 1.0308), + self, tli: float = 240, alpha: float = 1, beta: float = 0.15, max_cref: tuple | np.ndarray = (1.9705, 1.0308) ) -> None: self.tli = tli self.alpha = alpha @@ -145,8 +141,8 @@ def __init__( tli: float = 240, alpha: float = 1, beta: float = 0.15, - target_he: Union[tuple, np.ndarray] = ((0.5626, 0.2159), (0.7201, 0.8012), (0.4062, 0.5581)), - max_cref: Union[tuple, np.ndarray] = (1.9705, 1.0308), + target_he: tuple | np.ndarray = ((0.5626, 0.2159), (0.7201, 0.8012), (0.4062, 0.5581)), + max_cref: tuple | np.ndarray = (1.9705, 1.0308), ) -> None: self.tli = tli self.target_he = np.array(target_he) diff --git a/monai/apps/pathology/transforms/stain/dictionary.py b/monai/apps/pathology/transforms/stain/dictionary.py index eb8eba43f8..aa77301cb6 100644 --- a/monai/apps/pathology/transforms/stain/dictionary.py +++ b/monai/apps/pathology/transforms/stain/dictionary.py @@ -15,7 +15,9 @@ Class names are ended with 'd' to denote dictionary-based transforms. """ -from typing import Dict, Hashable, Mapping, Union +from __future__ import annotations + +from collections.abc import Hashable, Mapping import numpy as np @@ -48,13 +50,13 @@ def __init__( tli: float = 240, alpha: float = 1, beta: float = 0.15, - max_cref: Union[tuple, np.ndarray] = (1.9705, 1.0308), + max_cref: tuple | np.ndarray = (1.9705, 1.0308), allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) self.extractor = ExtractHEStains(tli=tli, alpha=alpha, beta=beta, max_cref=max_cref) - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: d = dict(data) for key in self.key_iterator(d): d[key] = self.extractor(d[key]) @@ -93,14 +95,14 @@ def __init__( tli: float = 240, alpha: float = 1, beta: float = 0.15, - target_he: Union[tuple, np.ndarray] = ((0.5626, 0.2159), (0.7201, 0.8012), (0.4062, 0.5581)), - max_cref: Union[tuple, np.ndarray] = (1.9705, 1.0308), + target_he: tuple | np.ndarray = ((0.5626, 0.2159), (0.7201, 0.8012), (0.4062, 0.5581)), + max_cref: tuple | np.ndarray = (1.9705, 1.0308), allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) self.normalizer = NormalizeHEStains(tli=tli, alpha=alpha, beta=beta, target_he=target_he, max_cref=max_cref) - def __call__(self, data: Mapping[Hashable, np.ndarray]) -> Dict[Hashable, np.ndarray]: + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: d = dict(data) for key in self.key_iterator(d): d[key] = self.normalizer(d[key]) diff --git a/monai/apps/pathology/utils.py b/monai/apps/pathology/utils.py index 5a57364a11..7ca890db65 100644 --- a/monai/apps/pathology/utils.py +++ b/monai/apps/pathology/utils.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Union +from __future__ import annotations import numpy as np import torch @@ -40,7 +40,7 @@ def compute_multi_instance_mask(mask: np.ndarray, threshold: float): return multi_instance_mask -def compute_isolated_tumor_cells(tumor_mask: np.ndarray, threshold: float) -> List[int]: +def compute_isolated_tumor_cells(tumor_mask: np.ndarray, threshold: float) -> list[int]: """ This method computes identifies Isolated Tumor Cells (ITC) and return their labels. @@ -62,7 +62,7 @@ class PathologyProbNMS(ProbNMS): Pathology. """ - def __call__(self, probs_map: Union[np.ndarray, torch.Tensor], resolution_level: int = 0): + def __call__(self, probs_map: np.ndarray | torch.Tensor, resolution_level: int = 0): """ probs_map: the input probabilities map, it must have shape (H[, W, ...]). resolution_level: the level at which the probabilities map is made. diff --git a/monai/apps/reconstruction/complex_utils.py b/monai/apps/reconstruction/complex_utils.py index 0a5cdccd0d..c98ed1ac92 100644 --- a/monai/apps/reconstruction/complex_utils.py +++ b/monai/apps/reconstruction/complex_utils.py @@ -12,8 +12,9 @@ This script contains utility functions for complex-value PyTorch tensor. """ +from __future__ import annotations + import re -from typing import Optional import numpy as np import torch @@ -25,8 +26,8 @@ def convert_to_tensor_complex( data, - dtype: Optional[torch.dtype] = None, - device: Optional[torch.device] = None, + dtype: torch.dtype | None = None, + device: torch.device | None = None, wrap_sequence: bool = True, track_meta: bool = False, ) -> Tensor: diff --git a/monai/apps/reconstruction/fastmri_reader.py b/monai/apps/reconstruction/fastmri_reader.py index 52bd5c0db3..45391c68d0 100644 --- a/monai/apps/reconstruction/fastmri_reader.py +++ b/monai/apps/reconstruction/fastmri_reader.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os -from typing import Dict, Sequence, Tuple, Union +from collections.abc import Sequence import numpy as np from numpy import ndarray @@ -41,7 +43,7 @@ class FastMRIReader(ImageReader): - patient_id (str): the patient's id whose measurements were recorded """ - def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: + def verify_suffix(self, filename: Sequence[PathLike] | PathLike) -> bool: """ Verify whether the specified file format is supported by h5py reader. @@ -51,7 +53,7 @@ def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: suffixes: Sequence[str] = [".h5"] return has_h5py and is_supported_format(filename, suffixes) - def read(self, data: Union[Sequence[PathLike], PathLike]) -> Dict: # type: ignore + def read(self, data: Sequence[PathLike] | PathLike) -> dict: # type: ignore """ Read data from specified h5 file. Note that the returned object is a dictionary. @@ -73,7 +75,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike]) -> Dict: # type: igno return dat - def get_data(self, dat: Dict) -> Tuple[ndarray, dict]: + def get_data(self, dat: dict) -> tuple[ndarray, dict]: """ Extract data array and metadata from the loaded data and return them. This function returns two objects, first is numpy array of image data, second is dict of metadata. @@ -90,7 +92,7 @@ def get_data(self, dat: Dict) -> Tuple[ndarray, dict]: ) return data, header - def _get_meta_dict(self, dat) -> Dict: + def _get_meta_dict(self, dat) -> dict: """ Get all the metadata of the loaded dict and return the meta dict. diff --git a/monai/apps/reconstruction/mri_utils.py b/monai/apps/reconstruction/mri_utils.py index 9c06b492d5..f51040509e 100644 --- a/monai/apps/reconstruction/mri_utils.py +++ b/monai/apps/reconstruction/mri_utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from torch import Tensor from monai.config.type_definitions import NdarrayOrTensor diff --git a/monai/apps/reconstruction/networks/blocks/varnetblock.py b/monai/apps/reconstruction/networks/blocks/varnetblock.py index daaa3efbf3..289505a057 100644 --- a/monai/apps/reconstruction/networks/blocks/varnetblock.py +++ b/monai/apps/reconstruction/networks/blocks/varnetblock.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import torch import torch.nn as nn from torch import Tensor diff --git a/monai/apps/reconstruction/networks/nets/coil_sensitivity_model.py b/monai/apps/reconstruction/networks/nets/coil_sensitivity_model.py index 94568db90f..91a9f3d8d3 100644 --- a/monai/apps/reconstruction/networks/nets/coil_sensitivity_model.py +++ b/monai/apps/reconstruction/networks/nets/coil_sensitivity_model.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -54,13 +56,13 @@ def __init__( self, spatial_dims: int = 2, features: Sequence[int] = (32, 32, 64, 128, 256, 32), - act: Union[str, tuple] = ("LeakyReLU", {"negative_slope": 0.1, "inplace": True}), - norm: Union[str, tuple] = ("instance", {"affine": True}), + act: str | tuple = ("LeakyReLU", {"negative_slope": 0.1, "inplace": True}), + norm: str | tuple = ("instance", {"affine": True}), bias: bool = True, - dropout: Union[float, tuple] = 0.0, + dropout: float | tuple = 0.0, upsample: str = "deconv", coil_dim: int = 1, - conv_net: Optional[nn.Module] = None, + conv_net: nn.Module | None = None, ): super().__init__() if conv_net is None: @@ -83,7 +85,7 @@ def __init__( self.spatial_dims = spatial_dims self.coil_dim = coil_dim - def get_fully_sampled_region(self, mask: Tensor) -> Tuple[int, int]: + def get_fully_sampled_region(self, mask: Tensor) -> tuple[int, int]: """ Extracts the size of the fully-sampled part of the kspace. Note that when a kspace is under-sampled, a part of its center is fully sampled. This part is called the Auto diff --git a/monai/apps/reconstruction/networks/nets/complex_unet.py b/monai/apps/reconstruction/networks/nets/complex_unet.py index 3747669174..1ca5fd5eec 100644 --- a/monai/apps/reconstruction/networks/nets/complex_unet.py +++ b/monai/apps/reconstruction/networks/nets/complex_unet.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import torch.nn as nn from torch import Tensor @@ -56,13 +58,13 @@ def __init__( self, spatial_dims: int = 2, features: Sequence[int] = (32, 32, 64, 128, 256, 32), - act: Union[str, tuple] = ("LeakyReLU", {"negative_slope": 0.1, "inplace": True}), - norm: Union[str, tuple] = ("instance", {"affine": True}), + act: str | tuple = ("LeakyReLU", {"negative_slope": 0.1, "inplace": True}), + norm: str | tuple = ("instance", {"affine": True}), bias: bool = True, - dropout: Union[float, tuple] = 0.0, + dropout: float | tuple = 0.0, upsample: str = "deconv", pad_factor: int = 16, - conv_net: Optional[nn.Module] = None, + conv_net: nn.Module | None = None, ): super().__init__() self.unet: nn.Module diff --git a/monai/apps/reconstruction/networks/nets/utils.py b/monai/apps/reconstruction/networks/nets/utils.py index b97cdab786..419ef5e6ff 100644 --- a/monai/apps/reconstruction/networks/nets/utils.py +++ b/monai/apps/reconstruction/networks/nets/utils.py @@ -12,8 +12,9 @@ This script contains utility functions for developing new networks/blocks in PyTorch. """ +from __future__ import annotations + import math -from typing import Tuple from torch import Tensor from torch.nn import functional as F @@ -75,7 +76,7 @@ def reshape_channel_complex_to_last_dim(x: Tensor) -> Tensor: raise ValueError(f"only 2D (B,C*2,H,W) and 3D (B,C*2,H,W,D) data are supported but x has shape {x.shape}") -def reshape_channel_to_batch_dim(x: Tensor) -> Tuple[Tensor, int]: +def reshape_channel_to_batch_dim(x: Tensor) -> tuple[Tensor, int]: """ Combines batch and channel dimensions. @@ -125,7 +126,7 @@ def reshape_batch_channel_to_channel_dim(x: Tensor, batch_size: int) -> Tensor: raise ValueError(f"only 2D (B*C,1,H,W,2) and 3D (B*C,1,H,W,D,2) data are supported but x has shape {x.shape}") -def complex_normalize(x: Tensor) -> Tuple[Tensor, Tensor, Tensor]: +def complex_normalize(x: Tensor) -> tuple[Tensor, Tensor, Tensor]: """ Performs layer mean-std normalization for complex data. Normalization is done for each batch member along each part (part refers to real and imaginary parts), separately. @@ -167,7 +168,7 @@ def complex_normalize(x: Tensor) -> Tuple[Tensor, Tensor, Tensor]: def divisible_pad_t( x: Tensor, k: int = 16 -) -> Tuple[Tensor, Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, int], int, int, int]]: +) -> tuple[Tensor, tuple[tuple[int, int], tuple[int, int], tuple[int, int], int, int, int]]: """ Pad input to feed into the network (torch script compatible) @@ -228,7 +229,7 @@ def divisible_pad_t( def inverse_divisible_pad_t( - x: Tensor, pad_sizes: Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, int], int, int, int] + x: Tensor, pad_sizes: tuple[tuple[int, int], tuple[int, int], tuple[int, int], int, int, int] ) -> Tensor: """ De-pad network output to match its original shape @@ -252,7 +253,7 @@ def inverse_divisible_pad_t( raise ValueError(f"only 2D (B,C,H,W) and 3D (B,C,H,W,D) data are supported but x has shape {x.shape}") -def floor_ceil(n: float) -> Tuple[int, int]: +def floor_ceil(n: float) -> tuple[int, int]: """ Returns floor and ceil of the input diff --git a/monai/apps/reconstruction/networks/nets/varnet.py b/monai/apps/reconstruction/networks/nets/varnet.py index 33b93b3d82..de4deb9afc 100644 --- a/monai/apps/reconstruction/networks/nets/varnet.py +++ b/monai/apps/reconstruction/networks/nets/varnet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import copy import torch.nn as nn diff --git a/monai/apps/reconstruction/transforms/array.py b/monai/apps/reconstruction/transforms/array.py index cd2936de41..e8a99cdac6 100644 --- a/monai/apps/reconstruction/transforms/array.py +++ b/monai/apps/reconstruction/transforms/array.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from abc import abstractmethod -from typing import Sequence +from collections.abc import Sequence import numpy as np from torch import Tensor diff --git a/monai/apps/reconstruction/transforms/dictionary.py b/monai/apps/reconstruction/transforms/dictionary.py index 4f3a2e03cf..f475b9870d 100644 --- a/monai/apps/reconstruction/transforms/dictionary.py +++ b/monai/apps/reconstruction/transforms/dictionary.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, Hashable, Mapping, Optional, Sequence +from __future__ import annotations + +from collections.abc import Hashable, Mapping, Sequence import numpy as np from numpy import ndarray @@ -46,7 +48,7 @@ def __init__(self, keys: KeysCollection, meta_key: str, allow_missing_keys: bool MapTransform.__init__(self, keys, allow_missing_keys) self.meta_key = meta_key - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, Tensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, Tensor]: """ Args: data: is a dictionary containing (key,value) pairs from the @@ -113,13 +115,13 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandomKspaceMaskd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandomKspaceMaskd: super().set_random_state(seed, state) self.masker.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, Tensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, Tensor]: """ Args: data: is a dictionary containing (key,value) pairs from the @@ -181,8 +183,8 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "EquispacedKspaceMaskd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> EquispacedKspaceMaskd: super().set_random_state(seed, state) self.masker.set_random_state(seed, state) return self @@ -215,7 +217,7 @@ def __init__(self, keys: KeysCollection, ref_key: str, allow_missing_keys: bool super().__init__(keys, cropper=None, allow_missing_keys=allow_missing_keys) # type: ignore self.ref_key = ref_key - def __call__(self, data: Mapping[Hashable, Tensor]) -> Dict[Hashable, Tensor]: + def __call__(self, data: Mapping[Hashable, Tensor]) -> dict[Hashable, Tensor]: """ This transform can support to crop ND spatial (channel-first) data. It also supports pseudo ND spatial data (e.g., (C,H,W) is a pseudo-3D @@ -277,8 +279,8 @@ def __init__( self, keys: KeysCollection, ref_key: str, - subtrahend: Optional[NdarrayOrTensor] = None, - divisor: Optional[NdarrayOrTensor] = None, + subtrahend: NdarrayOrTensor | None = None, + divisor: NdarrayOrTensor | None = None, nonzero: bool = False, channel_wise: bool = False, dtype: DtypeLike = np.float32, @@ -288,7 +290,7 @@ def __init__( self.default_normalizer = NormalizeIntensity(subtrahend, divisor, nonzero, channel_wise, dtype) self.ref_key = ref_key - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: """ This transform can support to normalize ND spatial (channel-first) data. It also supports pseudo ND spatial data (e.g., (C,H,W) is a pseudo-3D diff --git a/monai/apps/tcia/__init__.py b/monai/apps/tcia/__init__.py index bd266704b8..af3d44fd14 100644 --- a/monai/apps/tcia/__init__.py +++ b/monai/apps/tcia/__init__.py @@ -9,5 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .label_desc import TCIA_LABEL_DICT from .utils import download_tcia_series_instance, get_tcia_metadata, get_tcia_ref_uid, match_tcia_ref_uid_in_study diff --git a/monai/apps/tcia/label_desc.py b/monai/apps/tcia/label_desc.py index e3875e4095..29ae1fad1f 100644 --- a/monai/apps/tcia/label_desc.py +++ b/monai/apps/tcia/label_desc.py @@ -9,11 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict +from __future__ import annotations __all__ = ["TCIA_LABEL_DICT"] -TCIA_LABEL_DICT: Dict[str, Dict[str, int]] = { +TCIA_LABEL_DICT: dict[str, dict[str, int]] = { "C4KC-KiTS": {"Kidney": 0, "Renal Tumor": 1}, "NSCLC-Radiomics": { "Esophagus": 0, diff --git a/monai/apps/tcia/utils.py b/monai/apps/tcia/utils.py index ad95596223..515793ded4 100644 --- a/monai/apps/tcia/utils.py +++ b/monai/apps/tcia/utils.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os -from typing import List, Optional import monai from monai.config.type_definitions import PathLike @@ -24,7 +25,7 @@ BASE_URL = "https://services.cancerimagingarchive.net/nbia-api/services/v1/" -def get_tcia_metadata(query: str, attribute: Optional[str] = None): +def get_tcia_metadata(query: str, attribute: str | None = None): """ Achieve metadata of a public The Cancer Imaging Archive (TCIA) dataset. @@ -51,7 +52,7 @@ def get_tcia_metadata(query: str, attribute: Optional[str] = None): full_url = f"{BASE_URL}{query}" resp = requests_get(full_url) resp.raise_for_status() - metadata_list: List = [] + metadata_list: list = [] if len(resp.text) == 0: return metadata_list for d in resp.json(): diff --git a/monai/apps/utils.py b/monai/apps/utils.py index cbfdcd7423..84b8a5bf2a 100644 --- a/monai/apps/utils.py +++ b/monai/apps/utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import hashlib import logging import os @@ -19,7 +21,7 @@ import warnings import zipfile from pathlib import Path -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from urllib.error import ContentTooShortError, HTTPError, URLError from urllib.parse import urlparse from urllib.request import urlretrieve @@ -45,8 +47,8 @@ def get_logger( module_name: str = "monai.apps", fmt: str = DEFAULT_FMT, - datefmt: Optional[str] = None, - logger_handler: Optional[logging.Handler] = None, + datefmt: str | None = None, + logger_handler: logging.Handler | None = None, ): """ Get a `module_name` logger with the specified format and date format. @@ -92,7 +94,7 @@ class TqdmUpTo(tqdm): Inspired by the example in https://github.com/tqdm/tqdm. """ - def update_to(self, b: int = 1, bsize: int = 1, tsize: Optional[int] = None): + def update_to(self, b: int = 1, bsize: int = 1, tsize: int | None = None): """ Args: b: number of blocks transferred so far, default: 1. @@ -114,7 +116,7 @@ def update_to(self, b: int = 1, bsize: int = 1, tsize: Optional[int] = None): raise e -def check_hash(filepath: PathLike, val: Optional[str] = None, hash_type: str = "md5") -> bool: +def check_hash(filepath: PathLike, val: str | None = None, hash_type: str = "md5") -> bool: """ Verify hash signature of specified file. @@ -149,7 +151,7 @@ def check_hash(filepath: PathLike, val: Optional[str] = None, hash_type: str = " def download_url( url: str, filepath: PathLike = "", - hash_val: Optional[str] = None, + hash_val: str | None = None, hash_type: str = "md5", progress: bool = True, **gdown_kwargs, @@ -222,7 +224,7 @@ def download_url( def extractall( filepath: PathLike, output_dir: PathLike = ".", - hash_val: Optional[str] = None, + hash_val: str | None = None, hash_type: str = "md5", file_type: str = "", has_base: bool = True, @@ -282,7 +284,7 @@ def download_and_extract( url: str, filepath: PathLike = "", output_dir: PathLike = ".", - hash_val: Optional[str] = None, + hash_val: str | None = None, hash_type: str = "md5", file_type: str = "", has_base: bool = True, diff --git a/monai/auto3dseg/__init__.py b/monai/auto3dseg/__init__.py index 9d35026045..4e5d15613b 100644 --- a/monai/auto3dseg/__init__.py +++ b/monai/auto3dseg/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .algo_gen import Algo, AlgoGen from .analyzer import ( Analyzer, diff --git a/monai/auto3dseg/algo_gen.py b/monai/auto3dseg/algo_gen.py index ad185117a4..5ebe479aec 100644 --- a/monai/auto3dseg/algo_gen.py +++ b/monai/auto3dseg/algo_gen.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from monai.transforms import Randomizable diff --git a/monai/auto3dseg/analyzer.py b/monai/auto3dseg/analyzer.py index 386ca2f99e..af24b5c907 100644 --- a/monai/auto3dseg/analyzer.py +++ b/monai/auto3dseg/analyzer.py @@ -9,10 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import time from abc import ABC, abstractmethod +from collections.abc import Hashable, Mapping from copy import deepcopy -from typing import Any, Dict, Hashable, List, Mapping, Optional, Union +from typing import Any import numpy as np import torch @@ -371,13 +374,13 @@ class LabelStats(Analyzer): """ - def __init__(self, image_key: str, label_key: str, stats_name: str = "label_stats", do_ccp: Optional[bool] = True): + def __init__(self, image_key: str, label_key: str, stats_name: str = "label_stats", do_ccp: bool | None = True): self.image_key = image_key self.label_key = label_key self.do_ccp = do_ccp - report_format: Dict[LabelStatsKeys, Any] = { + report_format: dict[LabelStatsKeys, Any] = { LabelStatsKeys.LABEL_UID: None, LabelStatsKeys.IMAGE_INTST: None, LabelStatsKeys.LABEL: [{LabelStatsKeys.PIXEL_PCT: None, LabelStatsKeys.IMAGE_INTST: None}], @@ -394,7 +397,7 @@ def __init__(self, image_key: str, label_key: str, stats_name: str = "label_stat id_seq = ID_SEP_KEY.join([LabelStatsKeys.LABEL, "0", LabelStatsKeys.IMAGE_INTST]) self.update_ops_nested_label(id_seq, SampleOperations()) - def __call__(self, data: Mapping[Hashable, MetaTensor]) -> Dict[Hashable, MetaTensor]: + def __call__(self, data: Mapping[Hashable, MetaTensor]) -> dict[Hashable, MetaTensor]: """ Callable to execute the pre-defined functions. @@ -442,7 +445,7 @@ def __call__(self, data: Mapping[Hashable, MetaTensor]) -> Dict[Hashable, MetaTe The stats operation uses numpy and torch to compute max, min, and other functions. If the input has nan/inf, the stats results will be nan/inf. """ - d: Dict[Hashable, MetaTensor] = dict(data) + d: dict[Hashable, MetaTensor] = dict(data) start = time.time() if isinstance(d[self.image_key], (torch.Tensor, MetaTensor)) and d[self.image_key].device.type == "cuda": using_cuda = True @@ -451,13 +454,13 @@ def __call__(self, data: Mapping[Hashable, MetaTensor]) -> Dict[Hashable, MetaTe restore_grad_state = torch.is_grad_enabled() torch.set_grad_enabled(False) - ndas: List[MetaTensor] = [d[self.image_key][i] for i in range(d[self.image_key].shape[0])] # type: ignore + ndas: list[MetaTensor] = [d[self.image_key][i] for i in range(d[self.image_key].shape[0])] # type: ignore ndas_label: MetaTensor = d[self.label_key] # (H,W,D) if ndas_label.shape != ndas[0].shape: raise ValueError(f"Label shape {ndas_label.shape} is different from image shape {ndas[0].shape}") - nda_foregrounds: List[torch.Tensor] = [get_foreground_label(nda, ndas_label) for nda in ndas] + nda_foregrounds: list[torch.Tensor] = [get_foreground_label(nda, ndas_label) for nda in ndas] nda_foregrounds = [nda if nda.numel() > 0 else torch.Tensor([0]) for nda in nda_foregrounds] unique_label = unique(ndas_label) @@ -471,7 +474,7 @@ def __call__(self, data: Mapping[Hashable, MetaTensor]) -> Dict[Hashable, MetaTe pixel_arr = [] for index in unique_label: start_label = time.time() - label_dict: Dict[str, Any] = {} + label_dict: dict[str, Any] = {} mask_index = ndas_label == index nda_masks = [nda[mask_index] for nda in ndas] @@ -527,7 +530,7 @@ class ImageStatsSumm(Analyzer): """ - def __init__(self, stats_name: str = "image_stats", average: Optional[bool] = True): + def __init__(self, stats_name: str = "image_stats", average: bool | None = True): self.summary_average = average report_format = { ImageStatsKeys.SHAPE: None, @@ -544,7 +547,7 @@ def __init__(self, stats_name: str = "image_stats", average: Optional[bool] = Tr self.update_ops(ImageStatsKeys.SPACING, SampleOperations()) self.update_ops(ImageStatsKeys.INTENSITY, SummaryOperations()) - def __call__(self, data: List[Dict]): + def __call__(self, data: list[dict]): """ Callable to execute the pre-defined functions @@ -608,14 +611,14 @@ class FgImageStatsSumm(Analyzer): """ - def __init__(self, stats_name: str = "image_foreground_stats", average: Optional[bool] = True): + def __init__(self, stats_name: str = "image_foreground_stats", average: bool | None = True): self.summary_average = average report_format = {ImageStatsKeys.INTENSITY: None} super().__init__(stats_name, report_format) self.update_ops(ImageStatsKeys.INTENSITY, SummaryOperations()) - def __call__(self, data: List[Dict]): + def __call__(self, data: list[dict]): """ Callable to execute the pre-defined functions. @@ -672,11 +675,11 @@ class LabelStatsSumm(Analyzer): """ - def __init__(self, stats_name: str = "label_stats", average: Optional[bool] = True, do_ccp: Optional[bool] = True): + def __init__(self, stats_name: str = "label_stats", average: bool | None = True, do_ccp: bool | None = True): self.summary_average = average self.do_ccp = do_ccp - report_format: Dict[str, Any] = { + report_format: dict[str, Any] = { LabelStatsKeys.LABEL_UID: None, LabelStatsKeys.IMAGE_INTST: None, LabelStatsKeys.LABEL: [{LabelStatsKeys.PIXEL_PCT: None, LabelStatsKeys.IMAGE_INTST: None}], @@ -702,7 +705,7 @@ def __init__(self, stats_name: str = "label_stats", average: Optional[bool] = Tr id_seq = ID_SEP_KEY.join([LabelStatsKeys.LABEL, "0", LabelStatsKeys.LABEL_NCOMP]) self.update_ops_nested_label(id_seq, SampleOperations()) - def __call__(self, data: List[Dict]): + def __call__(self, data: list[dict]): """ Callable to execute the pre-defined functions @@ -800,7 +803,7 @@ class FilenameStats(Analyzer): """ - def __init__(self, key: Optional[str], stats_name: str) -> None: + def __init__(self, key: str | None, stats_name: str) -> None: self.key = key super().__init__(stats_name, {}) @@ -851,14 +854,14 @@ def __init__( self, image_key: str, stats_name: str = DataStatsKeys.IMAGE_HISTOGRAM, - hist_bins: Union[List[int], int, None] = None, - hist_range: Optional[list] = None, + hist_bins: list[int] | int | None = None, + hist_range: list | None = None, ): self.image_key = image_key # set defaults - self.hist_bins: List[int] = ( + self.hist_bins: list[int] = ( [100] if hist_bins is None else hist_bins if isinstance(hist_bins, list) else [hist_bins] ) self.hist_range: list = [-500, 500] if hist_range is None else hist_range @@ -949,14 +952,14 @@ class ImageHistogramSumm(Analyzer): """ - def __init__(self, stats_name: str = DataStatsKeys.IMAGE_HISTOGRAM, average: Optional[bool] = True): + def __init__(self, stats_name: str = DataStatsKeys.IMAGE_HISTOGRAM, average: bool | None = True): self.summary_average = average report_format = {ImageStatsKeys.HISTOGRAM: None} super().__init__(stats_name, report_format) self.update_ops(ImageStatsKeys.HISTOGRAM, SummaryOperations()) - def __call__(self, data: List[Dict]): + def __call__(self, data: list[dict]): """ Callable to execute the pre-defined functions @@ -991,7 +994,7 @@ def __call__(self, data: List[Dict]): if self.stats_name not in data[0]: return KeyError(f"{self.stats_name} is not in input data") - summ_histogram: Dict = {} + summ_histogram: dict = {} for d in data: if not summ_histogram: diff --git a/monai/auto3dseg/operations.py b/monai/auto3dseg/operations.py index 45294549ef..30b0fbc8ff 100644 --- a/monai/auto3dseg/operations.py +++ b/monai/auto3dseg/operations.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from collections import UserDict from functools import partial from typing import Any diff --git a/monai/auto3dseg/seg_summarizer.py b/monai/auto3dseg/seg_summarizer.py index 6f8093a40e..22db58f76e 100644 --- a/monai/auto3dseg/seg_summarizer.py +++ b/monai/auto3dseg/seg_summarizer.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Dict, List, Optional, Union +from __future__ import annotations + +from typing import Any from monai.auto3dseg.analyzer import ( FgImageStats, @@ -80,22 +82,22 @@ class SegSummarizer(Compose): def __init__( self, image_key: str, - label_key: Optional[str], + label_key: str | None, average=True, do_ccp: bool = True, - hist_bins: Union[List[int], int, None] = None, - hist_range: Optional[list] = None, + hist_bins: list[int] | int | None = None, + hist_range: list | None = None, histogram_only: bool = False, ) -> None: self.image_key = image_key self.label_key = label_key # set defaults - self.hist_bins: Union[List[int], int] = [100] if hist_bins is None else hist_bins + self.hist_bins: list[int] | int = [100] if hist_bins is None else hist_bins self.hist_range: list = [-500, 500] if hist_range is None else hist_range self.histogram_only = histogram_only - self.summary_analyzers: List[Any] = [] + self.summary_analyzers: list[Any] = [] super().__init__() if not self.histogram_only: @@ -167,7 +169,7 @@ def __call__(self, data): self.transforms += (case_analyzer,) self.summary_analyzers.append(summary_analyzer) - def summarize(self, data: List[Dict]): + def summarize(self, data: list[dict]): """ Summarize the input list of data and generates a report ready for json/yaml export. @@ -196,7 +198,7 @@ def summarize(self, data: List[Dict]): if not isinstance(data, list): raise ValueError(f"{self.__class__} summarize function needs input to be a list of dict") - report: Dict[str, Dict] = {} + report: dict[str, dict] = {} if len(data) == 0: return report diff --git a/monai/auto3dseg/utils.py b/monai/auto3dseg/utils.py index 78593f8369..6aebd1c94b 100644 --- a/monai/auto3dseg/utils.py +++ b/monai/auto3dseg/utils.py @@ -9,13 +9,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import pickle import sys import warnings from copy import deepcopy from numbers import Number -from typing import Any, Dict, Iterable, List, Optional, Tuple, Union, cast +from typing import Any import numpy as np import torch @@ -80,7 +82,7 @@ def get_foreground_label(image: MetaTensor, label: MetaTensor) -> MetaTensor: return label_foreground -def get_label_ccp(mask_index: MetaTensor, use_gpu: bool = True) -> Tuple[List[Any], int]: +def get_label_ccp(mask_index: MetaTensor, use_gpu: bool = True) -> tuple[list[Any], int]: """ Find all connected components and their bounding shape. Backend can be cuPy/cuCIM or Numpy depending on the hardware. @@ -124,10 +126,10 @@ def get_label_ccp(mask_index: MetaTensor, use_gpu: bool = True) -> Tuple[List[An def concat_val_to_np( - data_list: List[Dict], - fixed_keys: List[Union[str, int]], - ragged: Optional[bool] = False, - allow_missing: Optional[bool] = False, + data_list: list[dict], + fixed_keys: list[str | int], + ragged: bool | None = False, + allow_missing: bool | None = False, **kwargs, ): """ @@ -144,14 +146,14 @@ def concat_val_to_np( """ - np_list: List[Optional[np.ndarray]] = [] + np_list: list[np.ndarray | None] = [] for data in data_list: parser = ConfigParser(data) for i, key in enumerate(fixed_keys): fixed_keys[i] = str(key) val: Any - val = parser.get(ID_SEP_KEY.join(cast(Iterable[str], fixed_keys))) + val = parser.get(ID_SEP_KEY.join(fixed_keys)) # type: ignore if val is None: if allow_missing: @@ -181,7 +183,7 @@ def concat_val_to_np( def concat_multikeys_to_dict( - data_list: List[Dict], fixed_keys: List[Union[str, int]], keys: List[str], zero_insert: bool = True, **kwargs + data_list: list[dict], fixed_keys: list[str | int], keys: list[str], zero_insert: bool = True, **kwargs ): """ Get the nested value in a list of dictionary that shares the same structure iteratively on all keys. @@ -200,14 +202,14 @@ def concat_multikeys_to_dict( ret_dict = {} for key in keys: - addon: List[Union[str, int]] = [0, key] if zero_insert else [key] + addon: list[str | int] = [0, key] if zero_insert else [key] val = concat_val_to_np(data_list, fixed_keys + addon, **kwargs) ret_dict.update({key: val}) return ret_dict -def datafold_read(datalist: Union[str, Dict], basedir: str, fold: int = 0, key: str = "training") -> Tuple[List, List]: +def datafold_read(datalist: str | dict, basedir: str, fold: int = 0, key: str = "training") -> tuple[list, list]: """ Read a list of data dictionary `datalist` diff --git a/monai/bundle/__init__.py b/monai/bundle/__init__.py index c5cb0da978..f172f1a824 100644 --- a/monai/bundle/__init__.py +++ b/monai/bundle/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .config_item import ComponentLocator, ConfigComponent, ConfigExpression, ConfigItem, Instantiable from .config_parser import ConfigParser from .reference_resolver import ReferenceResolver diff --git a/monai/bundle/__main__.py b/monai/bundle/__main__.py index a9671fe385..b5142ca781 100644 --- a/monai/bundle/__main__.py +++ b/monai/bundle/__main__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from monai.bundle.scripts import ckpt_export, download, init_bundle, run, verify_metadata, verify_net_in_out if __name__ == "__main__": diff --git a/monai/bundle/config_item.py b/monai/bundle/config_item.py index 75a20b5e6a..acd664e725 100644 --- a/monai/bundle/config_item.py +++ b/monai/bundle/config_item.py @@ -9,13 +9,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import ast import inspect import sys import warnings from abc import ABC, abstractmethod +from collections.abc import Mapping, Sequence from importlib import import_module -from typing import Any, Dict, List, Mapping, Optional, Sequence, Union +from typing import Any from monai.bundle.utils import EXPR_KEY from monai.utils import ensure_tuple, first, instantiate, optional_import, run_debug, run_eval @@ -55,18 +58,18 @@ class ComponentLocator: MOD_START = "monai" - def __init__(self, excludes: Optional[Union[Sequence[str], str]] = None): + def __init__(self, excludes: Sequence[str] | str | None = None): self.excludes = [] if excludes is None else ensure_tuple(excludes) - self._components_table: Optional[Dict[str, List]] = None + self._components_table: dict[str, list] | None = None - def _find_module_names(self) -> List[str]: + def _find_module_names(self) -> list[str]: """ Find all the modules start with MOD_START and don't contain any of `excludes`. """ return [m for m in sys.modules if m.startswith(self.MOD_START) and all(s not in m for s in self.excludes)] - def _find_classes_or_functions(self, modnames: Union[Sequence[str], str]) -> Dict[str, List]: + def _find_classes_or_functions(self, modnames: Sequence[str] | str) -> dict[str, list]: """ Find all the classes and functions in the modules with specified `modnames`. @@ -74,7 +77,7 @@ def _find_classes_or_functions(self, modnames: Union[Sequence[str], str]) -> Dic modnames: names of the target modules to find all the classes and functions. """ - table: Dict[str, List] = {} + table: dict[str, list] = {} # all the MONAI modules are already loaded by `load_submodules` for modname in ensure_tuple(modnames): try: @@ -89,7 +92,7 @@ def _find_classes_or_functions(self, modnames: Union[Sequence[str], str]) -> Dic pass return table - def get_component_module_name(self, name: str) -> Optional[Union[List[str], str]]: + def get_component_module_name(self, name: str) -> list[str] | str | None: """ Get the full module name of the class or function with specified ``name``. If target component name exists in multiple packages or modules, return a list of full module names. @@ -104,7 +107,7 @@ def get_component_module_name(self, name: str) -> Optional[Union[List[str], str] # init component and module mapping table self._components_table = self._find_classes_or_functions(self._find_module_names()) - mods: Optional[Union[List[str], str]] = self._components_table.get(name) + mods: list[str] | str | None = self._components_table.get(name) if isinstance(mods, list) and len(mods) == 1: mods = mods[0] return mods @@ -207,8 +210,8 @@ def __init__( self, config: Any, id: str = "", - locator: Optional[ComponentLocator] = None, - excludes: Optional[Union[Sequence[str], str]] = None, + locator: ComponentLocator | None = None, + excludes: Sequence[str] | str | None = None, ) -> None: super().__init__(config=config, id=id) self.locator = ComponentLocator(excludes=excludes) if locator is None else locator @@ -315,7 +318,7 @@ class ConfigExpression(ConfigItem): prefix = EXPR_KEY run_eval = run_eval - def __init__(self, config: Any, id: str = "", globals: Optional[Dict] = None) -> None: + def __init__(self, config: Any, id: str = "", globals: dict | None = None) -> None: super().__init__(config=config, id=id) self.globals = globals if globals is not None else {} @@ -338,7 +341,7 @@ def _parse_import_string(self, import_string: str): return self.globals[asname] return None - def evaluate(self, globals: Optional[Dict] = None, locals: Optional[Dict] = None): + def evaluate(self, globals: dict | None = None, locals: dict | None = None): """ Execute the current config content and return the result if it is expression, based on Python `eval()`. For more details: https://docs.python.org/3/library/functions.html#eval. @@ -373,7 +376,7 @@ def evaluate(self, globals: Optional[Dict] = None, locals: Optional[Dict] = None return pdb.run(value[len(self.prefix) :], globals_, locals) @classmethod - def is_expression(cls, config: Union[Dict, List, str]) -> bool: + def is_expression(cls, config: dict | list | str) -> bool: """ Check whether the config is an executable expression string. Currently, a string starts with ``"$"`` character is interpreted as an expression. @@ -385,7 +388,7 @@ def is_expression(cls, config: Union[Dict, List, str]) -> bool: return isinstance(config, str) and config.startswith(cls.prefix) @classmethod - def is_import_statement(cls, config: Union[Dict, List, str]) -> bool: + def is_import_statement(cls, config: dict | list | str) -> bool: """ Check whether the config is an import statement (a special case of expression). diff --git a/monai/bundle/config_parser.py b/monai/bundle/config_parser.py index f1970b3758..65d733ad8b 100644 --- a/monai/bundle/config_parser.py +++ b/monai/bundle/config_parser.py @@ -9,11 +9,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import json import re +from collections.abc import Sequence from copy import deepcopy from pathlib import Path -from typing import Any, Dict, Optional, Sequence, Tuple, Union +from typing import Any from monai.bundle.config_item import ComponentLocator, ConfigComponent, ConfigExpression, ConfigItem from monai.bundle.reference_resolver import ReferenceResolver @@ -95,11 +98,11 @@ class ConfigParser: def __init__( self, config: Any = None, - excludes: Optional[Union[Sequence[str], str]] = None, - globals: Union[Dict[str, Any], None, bool] = None, + excludes: Sequence[str] | str | None = None, + globals: dict[str, Any] | None | bool = None, ): self.config = None - self.globals: Dict[str, Any] = {} + self.globals: dict[str, Any] = {} _globals = _default_globals.copy() if isinstance(_globals, dict) and globals not in (None, False): _globals.update(globals) # type: ignore @@ -129,7 +132,7 @@ def __getattr__(self, id): """ return self.get_parsed_content(id) - def __getitem__(self, id: Union[str, int]): + def __getitem__(self, id: str | int): """ Get the config by id. @@ -154,7 +157,7 @@ def __getitem__(self, id: Union[str, int]): raise KeyError(f"query key: {k}") from e return config - def __setitem__(self, id: Union[str, int], config: Any): + def __setitem__(self, id: str | int, config: Any): """ Set config by ``id``. Note that this method should be used before ``parse()`` or ``get_parsed_content()`` to ensure the updates are included in the parsed content. @@ -181,7 +184,7 @@ def __setitem__(self, id: Union[str, int], config: Any): self.ref_resolver.reset() return - def get(self, id: str = "", default: Optional[Any] = None): + def get(self, id: str = "", default: Any | None = None): """ Get the config by id. @@ -217,7 +220,7 @@ def set(self, config: Any, id: str = "", recursive: bool = True): conf_ = conf_[k if isinstance(conf_, dict) else int(k)] self[id] = config - def update(self, pairs: Dict[str, Any]): + def update(self, pairs: dict[str, Any]): """ Set the ``id`` and the corresponding config content in pairs, see also :py:meth:`__setitem__`. For example, ``parser.update({"train#epoch": 100, "train#lr": 0.02})`` @@ -229,7 +232,7 @@ def update(self, pairs: Dict[str, Any]): for k, v in pairs.items(): self[k] = v - def __contains__(self, id: Union[str, int]) -> bool: + def __contains__(self, id: str | int) -> bool: """ Returns True if `id` is stored in this configuration. @@ -283,7 +286,7 @@ def get_parsed_content(self, id: str = "", **kwargs): self.parse(reset=not kwargs.get("lazy", True)) return self.ref_resolver.get_resolved_content(id=id, **kwargs) - def read_meta(self, f: Union[PathLike, Sequence[PathLike], Dict], **kwargs): + def read_meta(self, f: PathLike | Sequence[PathLike] | dict, **kwargs): """ Read the metadata from specified JSON or YAML file. The metadata as a dictionary will be stored at ``self.config["_meta_"]``. @@ -297,7 +300,7 @@ def read_meta(self, f: Union[PathLike, Sequence[PathLike], Dict], **kwargs): """ self.set(self.load_config_files(f, **kwargs), self.meta_key) - def read_config(self, f: Union[PathLike, Sequence[PathLike], Dict], **kwargs): + def read_config(self, f: PathLike | Sequence[PathLike] | dict, **kwargs): """ Read the config from specified JSON or YAML file. The config content in the `self.config` dictionary. @@ -400,7 +403,7 @@ def load_config_file(cls, filepath: PathLike, **kwargs): raise ValueError(f"only support JSON or YAML config file so far, got name {_filepath}.") @classmethod - def load_config_files(cls, files: Union[PathLike, Sequence[PathLike], dict], **kwargs) -> Dict: + def load_config_files(cls, files: PathLike | Sequence[PathLike] | dict, **kwargs) -> dict: """ Load config files into a single config dict. The latter config file in the list will override or add the former config file. @@ -420,7 +423,7 @@ def load_config_files(cls, files: Union[PathLike, Sequence[PathLike], dict], **k return parser.get() # type: ignore @classmethod - def export_config_file(cls, config: Dict, filepath: PathLike, fmt="json", **kwargs): + def export_config_file(cls, config: dict, filepath: PathLike, fmt="json", **kwargs): """ Export the config content to the specified file path (currently support JSON and YAML files). @@ -442,7 +445,7 @@ def export_config_file(cls, config: Dict, filepath: PathLike, fmt="json", **kwar raise ValueError(f"only support JSON or YAML config file so far, got {writer}.") @classmethod - def split_path_id(cls, src: str) -> Tuple[str, str]: + def split_path_id(cls, src: str) -> tuple[str, str]: """ Split `src` string into two parts: a config file path and component id. The file path should end with `(json|yaml|yml)`. The component id should be separated by `#` if it exists. diff --git a/monai/bundle/reference_resolver.py b/monai/bundle/reference_resolver.py index ff2eaf4053..710bb399ae 100644 --- a/monai/bundle/reference_resolver.py +++ b/monai/bundle/reference_resolver.py @@ -9,9 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import re import warnings -from typing import Any, Dict, Optional, Sequence, Set +from collections.abc import Sequence +from typing import Any from monai.bundle.config_item import ConfigComponent, ConfigExpression, ConfigItem from monai.bundle.utils import ID_REF_KEY, ID_SEP_KEY @@ -54,10 +57,10 @@ class ReferenceResolver: # if `allow_missing_reference` and can't find a reference ID, will just raise a warning and don't update the config allow_missing_reference = allow_missing_reference - def __init__(self, items: Optional[Sequence[ConfigItem]] = None): + def __init__(self, items: Sequence[ConfigItem] | None = None): # save the items in a dictionary with the `ConfigItem.id` as key - self.items: Dict[str, Any] = {} if items is None else {i.get_id(): i for i in items} - self.resolved_content: Dict[str, Any] = {} + self.items: dict[str, Any] = {} if items is None else {i.get_id(): i for i in items} + self.resolved_content: dict[str, Any] = {} def reset(self): """ @@ -100,7 +103,7 @@ def get_item(self, id: str, resolve: bool = False, **kwargs): self._resolve_one_item(id=id, **kwargs) return self.items.get(id) - def _resolve_one_item(self, id: str, waiting_list: Optional[Set[str]] = None, **kwargs): + def _resolve_one_item(self, id: str, waiting_list: set[str] | None = None, **kwargs): """ Resolve and return one ``ConfigItem`` of ``id``, cache the resolved result in ``resolved_content``. If it has unresolved references, recursively resolve the referring items first. @@ -185,7 +188,7 @@ def get_resolved_content(self, id: str, **kwargs): return self._resolve_one_item(id=id, **kwargs) @classmethod - def match_refs_pattern(cls, value: str) -> Dict[str, int]: + def match_refs_pattern(cls, value: str) -> dict[str, int]: """ Match regular expression for the input string to find the references. The reference string starts with ``"@"``, like: ``"@XXX#YYY#ZZZ"``. @@ -194,7 +197,7 @@ def match_refs_pattern(cls, value: str) -> Dict[str, int]: value: input value to match regular expression. """ - refs: Dict[str, int] = {} + refs: dict[str, int] = {} # regular expression pattern to match "@XXX" or "@XXX#YYY" result = cls.id_matcher.findall(value) value_is_expr = ConfigExpression.is_expression(value) @@ -206,7 +209,7 @@ def match_refs_pattern(cls, value: str) -> Dict[str, int]: return refs @classmethod - def update_refs_pattern(cls, value: str, refs: Dict) -> str: + def update_refs_pattern(cls, value: str, refs: dict) -> str: """ Match regular expression for the input string to update content with the references. The reference part starts with ``"@"``, like: ``"@XXX#YYY#ZZZ"``. @@ -241,7 +244,7 @@ def update_refs_pattern(cls, value: str, refs: Dict) -> str: return value @classmethod - def find_refs_in_config(cls, config, id: str, refs: Optional[Dict[str, int]] = None) -> Dict[str, int]: + def find_refs_in_config(cls, config, id: str, refs: dict[str, int] | None = None) -> dict[str, int]: """ Recursively search all the content of input config item to get the ids of references. References mean: the IDs of other config items (``"@XXX"`` in this config item), or the @@ -254,7 +257,7 @@ def find_refs_in_config(cls, config, id: str, refs: Optional[Dict[str, int]] = N refs: dict of the ID name and count of found references, default to `None`. """ - refs_: Dict[str, int] = refs or {} + refs_: dict[str, int] = refs or {} if isinstance(config, str): for id, count in cls.match_refs_pattern(value=config).items(): refs_[id] = refs_.get(id, 0) + count @@ -268,7 +271,7 @@ def find_refs_in_config(cls, config, id: str, refs: Optional[Dict[str, int]] = N return refs_ @classmethod - def update_config_with_refs(cls, config, id: str, refs: Optional[Dict] = None): + def update_config_with_refs(cls, config, id: str, refs: dict | None = None): """ With all the references in ``refs``, update the input config content with references and return the new config. @@ -279,7 +282,7 @@ def update_config_with_refs(cls, config, id: str, refs: Optional[Dict] = None): refs: all the referring content with ids, default to `None`. """ - refs_: Dict = refs or {} + refs_: dict = refs or {} if isinstance(config, str): return cls.update_refs_pattern(config, refs_) if not isinstance(config, (list, dict)): diff --git a/monai/bundle/scripts.py b/monai/bundle/scripts.py index 4b3e587ffe..d9ddfe00e0 100644 --- a/monai/bundle/scripts.py +++ b/monai/bundle/scripts.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import ast import json import os @@ -16,11 +18,11 @@ import re import time import warnings +from collections.abc import Mapping, Sequence from logging.config import fileConfig from pathlib import Path from shutil import copyfile from textwrap import dedent -from typing import Dict, Mapping, Optional, Sequence, Tuple, Union import torch from torch.cuda import is_available @@ -47,7 +49,7 @@ download_source = os.environ.get("BUNDLE_DOWNLOAD_SRC", "github") -def _update_args(args: Optional[Union[str, Dict]] = None, ignore_none: bool = True, **kwargs) -> Dict: +def _update_args(args: str | dict | None = None, ignore_none: bool = True, **kwargs) -> dict: """ Update the `args` with the input `kwargs`. For dict data, recursively update the content based on the keys. @@ -58,7 +60,7 @@ def _update_args(args: Optional[Union[str, Dict]] = None, ignore_none: bool = Tr kwargs: destination args to update. """ - args_: Dict = args if isinstance(args, dict) else {} + args_: dict = args if isinstance(args, dict) else {} if isinstance(args, str): # args are defined in a structured file args_ = ConfigParser.load_config_file(args) @@ -74,7 +76,7 @@ def _update_args(args: Optional[Union[str, Dict]] = None, ignore_none: bool = Tr return args_ -def _pop_args(src: Dict, *args, **kwargs): +def _pop_args(src: dict, *args, **kwargs): """ Pop args from the `src` dictionary based on specified keys in `args` and (key, default value) pairs in `kwargs`. @@ -82,7 +84,7 @@ def _pop_args(src: Dict, *args, **kwargs): return tuple([src.pop(i) for i in args] + [src.pop(k, v) for k, v in kwargs.items()]) -def _log_input_summary(tag, args: Dict): +def _log_input_summary(tag, args: dict): logger.info(f"--- input summary of monai.bundle.scripts.{tag} ---") for name, val in args.items(): logger.info(f"> {name}: {pprint.pformat(val)}") @@ -101,7 +103,7 @@ def _get_var_names(expr: str): return [m.id for m in ast.walk(tree) if isinstance(m, ast.Name)] -def _get_fake_spatial_shape(shape: Sequence[Union[str, int]], p: int = 1, n: int = 1, any: int = 1) -> Tuple: +def _get_fake_spatial_shape(shape: Sequence[str | int], p: int = 1, n: int = 1, any: int = 1) -> tuple: """ Get spatial shape for fake data according to the specified shape pattern. It supports `int` number and `string` with formats like: "32", "32 * n", "32 ** p", "32 ** p *n". @@ -160,7 +162,7 @@ def _remove_ngc_prefix(name: str, prefix: str = "monai_"): return name -def _download_from_ngc(download_path: Path, filename: str, version: str, remove_prefix: Optional[str], progress: bool): +def _download_from_ngc(download_path: Path, filename: str, version: str, remove_prefix: str | None, progress: bool): # ensure prefix is contained filename = _add_ngc_prefix(filename) url = _get_ngc_bundle_url(model_name=filename, version=version) @@ -187,7 +189,7 @@ def _get_latest_bundle_version(source: str, name: str, repo: str): raise ValueError(f"To get the latest bundle version, source should be 'github' or 'ngc', got {source}.") -def _process_bundle_dir(bundle_dir: Optional[PathLike] = None): +def _process_bundle_dir(bundle_dir: PathLike | None = None): if bundle_dir is None: get_dir, has_home = optional_import("torch.hub", name="get_dir") if has_home: @@ -198,15 +200,15 @@ def _process_bundle_dir(bundle_dir: Optional[PathLike] = None): def download( - name: Optional[str] = None, - version: Optional[str] = None, - bundle_dir: Optional[PathLike] = None, + name: str | None = None, + version: str | None = None, + bundle_dir: PathLike | None = None, source: str = download_source, - repo: Optional[str] = None, - url: Optional[str] = None, - remove_prefix: Optional[str] = "monai_", + repo: str | None = None, + url: str | None = None, + remove_prefix: str | None = "monai_", progress: bool = True, - args_file: Optional[str] = None, + args_file: str | None = None, ): """ download bundle from the specified source or url. The bundle should be a zip file and it @@ -320,18 +322,18 @@ def download( def load( name: str, - version: Optional[str] = None, - model_file: Optional[str] = None, + version: str | None = None, + model_file: str | None = None, load_ts_module: bool = False, - bundle_dir: Optional[PathLike] = None, + bundle_dir: PathLike | None = None, source: str = download_source, - repo: Optional[str] = None, - remove_prefix: Optional[str] = "monai_", + repo: str | None = None, + remove_prefix: str | None = "monai_", progress: bool = True, - device: Optional[str] = None, - key_in_ckpt: Optional[str] = None, + device: str | None = None, + key_in_ckpt: str | None = None, config_files: Sequence[str] = (), - net_name: Optional[str] = None, + net_name: str | None = None, **net_kwargs, ): """ @@ -422,7 +424,7 @@ def load( def _get_all_bundles_info( - repo: str = "Project-MONAI/model-zoo", tag: str = "hosting_storage_v1", auth_token: Optional[str] = None + repo: str = "Project-MONAI/model-zoo", tag: str = "hosting_storage_v1", auth_token: str | None = None ): if has_requests: request_url = f"https://api.github.com/repos/{repo}/releases" @@ -436,7 +438,7 @@ def _get_all_bundles_info( raise ValueError("requests package is required, please install it.") releases_list = json.loads(resp.text) bundle_name_pattern = re.compile(r"_v\d*.") - bundles_info: Dict = {} + bundles_info: dict = {} for release in releases_list: if release["tag_name"] == tag: @@ -459,7 +461,7 @@ def _get_all_bundles_info( def get_all_bundles_list( - repo: str = "Project-MONAI/model-zoo", tag: str = "hosting_storage_v1", auth_token: Optional[str] = None + repo: str = "Project-MONAI/model-zoo", tag: str = "hosting_storage_v1", auth_token: str | None = None ): """ Get all bundles names (and the latest versions) that are stored in the release of specified repository @@ -494,7 +496,7 @@ def get_bundle_versions( bundle_name: str, repo: str = "Project-MONAI/model-zoo", tag: str = "hosting_storage_v1", - auth_token: Optional[str] = None, + auth_token: str | None = None, ): """ Get the latest version, as well as all existing versions of a bundle that is stored in the release of specified @@ -528,10 +530,10 @@ def get_bundle_versions( def get_bundle_info( bundle_name: str, - version: Optional[str] = None, + version: str | None = None, repo: str = "Project-MONAI/model-zoo", tag: str = "hosting_storage_v1", - auth_token: Optional[str] = None, + auth_token: str | None = None, ): """ Get all information @@ -603,12 +605,12 @@ def patch_bundle_tracking(parser: ConfigParser, settings: dict): def run( - runner_id: Optional[Union[str, Sequence[str]]] = None, - meta_file: Optional[Union[str, Sequence[str]]] = None, - config_file: Optional[Union[str, Sequence[str]]] = None, - logging_file: Optional[str] = None, - tracking: Optional[Union[str, dict]] = None, - args_file: Optional[str] = None, + runner_id: str | Sequence[str] | None = None, + meta_file: str | Sequence[str] | None = None, + config_file: str | Sequence[str] | None = None, + logging_file: str | None = None, + tracking: str | dict | None = None, + args_file: str | None = None, **override, ): """ @@ -743,12 +745,12 @@ def run( def verify_metadata( - meta_file: Optional[Union[str, Sequence[str]]] = None, - filepath: Optional[PathLike] = None, - create_dir: Optional[bool] = None, - hash_val: Optional[str] = None, - hash_type: Optional[str] = None, - args_file: Optional[str] = None, + meta_file: str | Sequence[str] | None = None, + filepath: PathLike | None = None, + create_dir: bool | None = None, + hash_val: str | None = None, + hash_type: str | None = None, + args_file: str | None = None, **kwargs, ): """ @@ -804,14 +806,14 @@ def verify_metadata( def verify_net_in_out( - net_id: Optional[str] = None, - meta_file: Optional[Union[str, Sequence[str]]] = None, - config_file: Optional[Union[str, Sequence[str]]] = None, - device: Optional[str] = None, - p: Optional[int] = None, - n: Optional[int] = None, - any: Optional[int] = None, - args_file: Optional[str] = None, + net_id: str | None = None, + meta_file: str | Sequence[str] | None = None, + config_file: str | Sequence[str] | None = None, + device: str | None = None, + p: int | None = None, + n: int | None = None, + any: int | None = None, + args_file: str | None = None, **override, ): """ @@ -903,13 +905,13 @@ def verify_net_in_out( def ckpt_export( - net_id: Optional[str] = None, - filepath: Optional[PathLike] = None, - ckpt_file: Optional[str] = None, - meta_file: Optional[Union[str, Sequence[str]]] = None, - config_file: Optional[Union[str, Sequence[str]]] = None, - key_in_ckpt: Optional[str] = None, - args_file: Optional[str] = None, + net_id: str | None = None, + filepath: PathLike | None = None, + ckpt_file: str | None = None, + meta_file: str | Sequence[str] | None = None, + config_file: str | Sequence[str] | None = None, + key_in_ckpt: str | None = None, + args_file: str | None = None, **override, ): """ @@ -974,7 +976,7 @@ def ckpt_export( # convert to TorchScript model and save with metadata, config content net = convert_to_torchscript(model=net) - extra_files: Dict = {} + extra_files: dict = {} for i in ensure_tuple(config_file_): # split the filename and directory filename = os.path.basename(i) @@ -1002,11 +1004,11 @@ def ckpt_export( def init_bundle( bundle_dir: PathLike, - ckpt_file: Optional[PathLike] = None, - network: Optional[torch.nn.Module] = None, + ckpt_file: PathLike | None = None, + network: torch.nn.Module | None = None, dataset_license: bool = False, - metadata_str: Union[Dict, str, None] = None, - inference_str: Union[Dict, str, None] = None, + metadata_str: dict | str | None = None, + inference_str: dict | str | None = None, ): """ Initialise a new bundle directory with some default configuration files and optionally network weights. diff --git a/monai/bundle/utils.py b/monai/bundle/utils.py index 33ff3ff28f..c5ffe72b65 100644 --- a/monai/bundle/utils.py +++ b/monai/bundle/utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import json import os import zipfile diff --git a/monai/config/__init__.py b/monai/config/__init__.py index f494202a56..c814e1f8eb 100644 --- a/monai/config/__init__.py +++ b/monai/config/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .deviceconfig import ( USE_COMPILED, USE_META_DICT, diff --git a/monai/config/deviceconfig.py b/monai/config/deviceconfig.py index 87d46895aa..9841bde9d1 100644 --- a/monai/config/deviceconfig.py +++ b/monai/config/deviceconfig.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import platform import re diff --git a/monai/config/type_definitions.py b/monai/config/type_definitions.py index 5c360b5536..57454a94e1 100644 --- a/monai/config/type_definitions.py +++ b/monai/config/type_definitions.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os from typing import Collection, Hashable, Iterable, Sequence, TypeVar, Union diff --git a/monai/data/__init__.py b/monai/data/__init__.py index 65ee8c377f..8a560e5f2f 100644 --- a/monai/data/__init__.py +++ b/monai/data/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import contextlib from .box_utils import ( diff --git a/monai/data/box_utils.py b/monai/data/box_utils.py index 162c7cae26..e258696d2e 100644 --- a/monai/data/box_utils.py +++ b/monai/data/box_utils.py @@ -18,11 +18,13 @@ the rest of the detection pipelines mainly assumes boxes in `StandardMode`. """ +from __future__ import annotations + import inspect import warnings from abc import ABC, abstractmethod +from collections.abc import Callable, Sequence from copy import deepcopy -from typing import Callable, Dict, Sequence, Tuple, Type, Union import numpy as np import torch @@ -77,7 +79,7 @@ class BoxMode(ABC): """ # a dictionary that maps spatial_dims to monai.utils.enums.BoxModeName. - name: Dict[int, BoxModeName] = {} + name: dict[int, BoxModeName] = {} @classmethod def get_name(cls, spatial_dims: int) -> str: @@ -93,7 +95,7 @@ def get_name(cls, spatial_dims: int) -> str: return cls.name[spatial_dims].value @abstractmethod - def boxes_to_corners(self, boxes: torch.Tensor) -> Tuple: + def boxes_to_corners(self, boxes: torch.Tensor) -> tuple: """ Convert the bounding boxes of the current mode to corners. @@ -101,7 +103,7 @@ def boxes_to_corners(self, boxes: torch.Tensor) -> Tuple: boxes: bounding boxes, Nx4 or Nx6 torch tensor Returns: - ``Tuple``: corners of boxes, 4-element or 6-element tuple, each element is a Nx1 torch tensor. + ``tuple``: corners of boxes, 4-element or 6-element tuple, each element is a Nx1 torch tensor. It represents (xmin, ymin, xmax, ymax) or (xmin, ymin, zmin, xmax, ymax, zmax) Example: @@ -151,8 +153,8 @@ class CornerCornerModeTypeA(BoxMode): name = {2: BoxModeName.XYXY, 3: BoxModeName.XYZXYZ} - def boxes_to_corners(self, boxes: torch.Tensor) -> Tuple: - corners: Tuple + def boxes_to_corners(self, boxes: torch.Tensor) -> tuple: + corners: tuple corners = boxes.split(1, dim=-1) return corners @@ -178,8 +180,8 @@ class CornerCornerModeTypeB(BoxMode): name = {2: BoxModeName.XXYY, 3: BoxModeName.XXYYZZ} - def boxes_to_corners(self, boxes: torch.Tensor) -> Tuple: - corners: Tuple + def boxes_to_corners(self, boxes: torch.Tensor) -> tuple: + corners: tuple spatial_dims = get_spatial_dims(boxes=boxes) if spatial_dims == 3: xmin, xmax, ymin, ymax, zmin, zmax = boxes.split(1, dim=-1) @@ -215,8 +217,8 @@ class CornerCornerModeTypeC(BoxMode): name = {2: BoxModeName.XYXY, 3: BoxModeName.XYXYZZ} - def boxes_to_corners(self, boxes: torch.Tensor) -> Tuple: - corners: Tuple + def boxes_to_corners(self, boxes: torch.Tensor) -> tuple: + corners: tuple spatial_dims = get_spatial_dims(boxes=boxes) if spatial_dims == 3: xmin, ymin, xmax, ymax, zmin, zmax = boxes.split(1, dim=-1) @@ -251,8 +253,8 @@ class CornerSizeMode(BoxMode): name = {2: BoxModeName.XYWH, 3: BoxModeName.XYZWHD} - def boxes_to_corners(self, boxes: torch.Tensor) -> Tuple: - corners: Tuple + def boxes_to_corners(self, boxes: torch.Tensor) -> tuple: + corners: tuple # convert to float32 when computing torch.clamp, which does not support float16 box_dtype = boxes.dtype @@ -300,8 +302,8 @@ class CenterSizeMode(BoxMode): name = {2: BoxModeName.CCWH, 3: BoxModeName.CCCWHD} - def boxes_to_corners(self, boxes: torch.Tensor) -> Tuple: - corners: Tuple + def boxes_to_corners(self, boxes: torch.Tensor) -> tuple: + corners: tuple # convert to float32 when computing torch.clamp, which does not support float16 box_dtype = boxes.dtype @@ -361,10 +363,10 @@ def corners_to_boxes(self, corners: Sequence) -> torch.Tensor: def get_spatial_dims( - boxes: Union[torch.Tensor, np.ndarray, None] = None, - points: Union[torch.Tensor, np.ndarray, None] = None, - corners: Union[Sequence, None] = None, - spatial_size: Union[Sequence[int], torch.Tensor, np.ndarray, None] = None, + boxes: torch.Tensor | np.ndarray | None = None, + points: torch.Tensor | np.ndarray | None = None, + corners: Sequence | None = None, + spatial_size: Sequence[int] | torch.Tensor | np.ndarray | None = None, ) -> int: """ Get spatial dimension for the giving setting and check the validity of them. @@ -430,7 +432,7 @@ def get_spatial_dims( raise ValueError("The dimensions of multiple inputs should match with each other.") -def get_boxmode(mode: Union[str, BoxMode, Type[BoxMode], None] = None, *args, **kwargs) -> BoxMode: +def get_boxmode(mode: str | BoxMode | type[BoxMode] | None = None, *args, **kwargs) -> BoxMode: """ This function that return a :class:`~monai.data.box_utils.BoxMode` object giving a representation of box mode @@ -494,8 +496,8 @@ def get_boxmode(mode: Union[str, BoxMode, Type[BoxMode], None] = None, *args, ** def convert_box_mode( boxes: NdarrayOrTensor, - src_mode: Union[str, BoxMode, Type[BoxMode], None] = None, - dst_mode: Union[str, BoxMode, Type[BoxMode], None] = None, + src_mode: str | BoxMode | type[BoxMode] | None = None, + dst_mode: str | BoxMode | type[BoxMode] | None = None, ) -> NdarrayOrTensor: """ This function converts the boxes in src_mode to the dst_mode. @@ -549,7 +551,7 @@ def convert_box_mode( def convert_box_to_standard_mode( - boxes: NdarrayOrTensor, mode: Union[str, BoxMode, Type[BoxMode], None] = None + boxes: NdarrayOrTensor, mode: str | BoxMode | type[BoxMode] | None = None ) -> NdarrayOrTensor: """ Convert given boxes to standard mode. @@ -624,7 +626,7 @@ def centers_in_boxes(centers: NdarrayOrTensor, boxes: NdarrayOrTensor, eps: floa def boxes_center_distance( boxes1: NdarrayOrTensor, boxes2: NdarrayOrTensor, euclidean: bool = True -) -> Tuple[NdarrayOrTensor, NdarrayOrTensor, NdarrayOrTensor]: +) -> tuple[NdarrayOrTensor, NdarrayOrTensor, NdarrayOrTensor]: """ Distance of center points between two sets of boxes @@ -726,7 +728,7 @@ def box_area(boxes: NdarrayOrTensor) -> NdarrayOrTensor: def _box_inter_union( boxes1_t: torch.Tensor, boxes2_t: torch.Tensor, compute_dtype: torch.dtype = torch.float32 -) -> Tuple[torch.Tensor, torch.Tensor]: +) -> tuple[torch.Tensor, torch.Tensor]: """ This internal function computes the intersection and union area of two set of boxes. @@ -938,10 +940,10 @@ def box_pair_giou(boxes1: NdarrayOrTensor, boxes2: NdarrayOrTensor) -> NdarrayOr def spatial_crop_boxes( boxes: NdarrayOrTensor, - roi_start: Union[Sequence[int], NdarrayOrTensor], - roi_end: Union[Sequence[int], NdarrayOrTensor], + roi_start: Sequence[int] | NdarrayOrTensor, + roi_end: Sequence[int] | NdarrayOrTensor, remove_empty: bool = True, -) -> Tuple[NdarrayOrTensor, NdarrayOrTensor]: +) -> tuple[NdarrayOrTensor, NdarrayOrTensor]: """ This function generate the new boxes when the corresponding image is cropped to the given ROI. When ``remove_empty=True``, it makes sure the bounding boxes are within the new cropped image. @@ -994,8 +996,8 @@ def spatial_crop_boxes( def clip_boxes_to_image( - boxes: NdarrayOrTensor, spatial_size: Union[Sequence[int], NdarrayOrTensor], remove_empty: bool = True -) -> Tuple[NdarrayOrTensor, NdarrayOrTensor]: + boxes: NdarrayOrTensor, spatial_size: Sequence[int] | NdarrayOrTensor, remove_empty: bool = True +) -> tuple[NdarrayOrTensor, NdarrayOrTensor]: """ This function clips the ``boxes`` to makes sure the bounding boxes are within the image. diff --git a/monai/data/csv_saver.py b/monai/data/csv_saver.py index 36d159d4be..f2b483765f 100644 --- a/monai/data/csv_saver.py +++ b/monai/data/csv_saver.py @@ -9,11 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import warnings from collections import OrderedDict from pathlib import Path -from typing import Dict, Optional, Union import numpy as np import torch @@ -81,7 +82,7 @@ def finalize(self) -> None: # clear cache content after writing self.reset_cache() - def save(self, data: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] = None) -> None: + def save(self, data: torch.Tensor | np.ndarray, meta_data: dict | None = None) -> None: """Save data into the cache dictionary. The metadata should have the following key: - ``'filename_or_obj'`` -- save the data corresponding to file name or object. If meta_data is None, use the default index from 0 to save data instead. @@ -97,7 +98,7 @@ def save(self, data: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] data = data.detach().cpu().numpy() self._cache_dict[save_key] = np.asarray(data, dtype=float) - def save_batch(self, batch_data: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] = None) -> None: + def save_batch(self, batch_data: torch.Tensor | np.ndarray, meta_data: dict | None = None) -> None: """Save a batch of data into the cache dictionary. Args: diff --git a/monai/data/dataloader.py b/monai/data/dataloader.py index f43211f184..9de0d28b96 100644 --- a/monai/data/dataloader.py +++ b/monai/data/dataloader.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import torch from torch.utils.data import DataLoader as _TorchDataLoader from torch.utils.data import Dataset diff --git a/monai/data/dataset.py b/monai/data/dataset.py index 1e9d67f358..17df33baa6 100644 --- a/monai/data/dataset.py +++ b/monai/data/dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import collections.abc import math import pickle @@ -18,11 +20,12 @@ import threading import time import warnings +from collections.abc import Callable, Sequence from copy import copy, deepcopy from multiprocessing.managers import ListProxy from multiprocessing.pool import ThreadPool from pathlib import Path -from typing import IO, TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence, Union +from typing import IO, TYPE_CHECKING, Any import numpy as np import torch @@ -72,7 +75,7 @@ class Dataset(_TorchDataset): }, }, }] """ - def __init__(self, data: Sequence, transform: Optional[Callable] = None) -> None: + def __init__(self, data: Sequence, transform: Callable | None = None) -> None: """ Args: data: input data to load and transform to generate dataset for model. @@ -92,7 +95,7 @@ def _transform(self, index: int): data_i = self.data[index] return apply_transform(self.transform, data_i) if self.transform is not None else data_i - def __getitem__(self, index: Union[int, slice, Sequence[int]]): + def __getitem__(self, index: int | slice | Sequence[int]): """ Returns a `Subset` if `index` is a slice or Sequence, a data item otherwise. """ @@ -143,7 +146,7 @@ def __init__(self, data: Any, func: Callable, **kwargs) -> None: self.kwargs = kwargs self.reset() - def reset(self, data: Optional[Any] = None, func: Optional[Callable] = None, **kwargs): + def reset(self, data: Any | None = None, func: Callable | None = None, **kwargs): """ Reset the dataset items with specified `func`. @@ -211,12 +214,12 @@ class PersistentDataset(Dataset): def __init__( self, data: Sequence, - transform: Union[Sequence[Callable], Callable], - cache_dir: Optional[Union[Path, str]], + transform: Sequence[Callable] | Callable, + cache_dir: Path | str | None, hash_func: Callable[..., bytes] = pickle_hashing, pickle_module: str = "pickle", pickle_protocol: int = DEFAULT_PROTOCOL, - hash_transform: Optional[Callable[..., bytes]] = None, + hash_transform: Callable[..., bytes] | None = None, reset_ops_id: bool = True, ) -> None: """ @@ -420,13 +423,13 @@ class CacheNTransDataset(PersistentDataset): def __init__( self, data: Sequence, - transform: Union[Sequence[Callable], Callable], + transform: Sequence[Callable] | Callable, cache_n_trans: int, - cache_dir: Optional[Union[Path, str]], + cache_dir: Path | str | None, hash_func: Callable[..., bytes] = pickle_hashing, pickle_module: str = "pickle", pickle_protocol: int = DEFAULT_PROTOCOL, - hash_transform: Optional[Callable[..., bytes]] = None, + hash_transform: Callable[..., bytes] | None = None, reset_ops_id: bool = True, ) -> None: """ @@ -534,15 +537,15 @@ class LMDBDataset(PersistentDataset): def __init__( self, data: Sequence, - transform: Union[Sequence[Callable], Callable], - cache_dir: Union[Path, str] = "cache", + transform: Sequence[Callable] | Callable, + cache_dir: Path | str = "cache", hash_func: Callable[..., bytes] = pickle_hashing, db_name: str = "monai_cache", progress: bool = True, pickle_protocol=pickle.HIGHEST_PROTOCOL, - hash_transform: Optional[Callable[..., bytes]] = None, + hash_transform: Callable[..., bytes] | None = None, reset_ops_id: bool = True, - lmdb_kwargs: Optional[dict] = None, + lmdb_kwargs: dict | None = None, ) -> None: """ Args: @@ -589,7 +592,7 @@ def __init__( self.lmdb_kwargs["map_size"] = 1024**4 # default map_size # lmdb is single-writer multi-reader by default # the cache is created without multi-threading - self._read_env: Optional[Any] = None + self._read_env: Any | None = None # this runs on the primary thread/process self._fill_cache_start_reader(show_progress=self.progress) print(f"Accessing lmdb file: {self.db_file.absolute()}.") @@ -742,16 +745,16 @@ class CacheDataset(Dataset): def __init__( self, data: Sequence, - transform: Optional[Union[Sequence[Callable], Callable]] = None, + transform: Sequence[Callable] | Callable | None = None, cache_num: int = sys.maxsize, cache_rate: float = 1.0, - num_workers: Optional[int] = 1, + num_workers: int | None = 1, progress: bool = True, copy_cache: bool = True, as_contiguous: bool = True, hash_as_key: bool = False, hash_func: Callable[..., bytes] = pickle_hashing, - runtime_cache: Union[bool, str, List, ListProxy] = False, + runtime_cache: bool | str | list | ListProxy = False, ) -> None: """ Args: @@ -809,8 +812,8 @@ def __init__( self.num_workers = max(int(self.num_workers), 1) self.runtime_cache = runtime_cache self.cache_num = 0 - self._cache: Union[List, ListProxy] = [] - self._hash_keys: List = [] + self._cache: list | ListProxy = [] + self._hash_keys: list = [] self.set_data(data) def set_data(self, data: Sequence) -> None: @@ -850,7 +853,7 @@ def _compute_cache_num(data_len: int): self._cache = self.runtime_cache # type: ignore return - def _fill_cache(self, indices=None) -> List: + def _fill_cache(self, indices=None) -> list: """ Compute and fill the cache content from data source. @@ -999,12 +1002,12 @@ class SmartCacheDataset(Randomizable, CacheDataset): def __init__( self, data: Sequence, - transform: Optional[Union[Sequence[Callable], Callable]] = None, + transform: Sequence[Callable] | Callable | None = None, replace_rate: float = 0.1, cache_num: int = sys.maxsize, cache_rate: float = 1.0, - num_init_workers: Optional[int] = 1, - num_replace_workers: Optional[int] = 1, + num_init_workers: int | None = 1, + num_replace_workers: int | None = 1, progress: bool = True, shuffle: bool = True, seed: int = 0, @@ -1020,7 +1023,7 @@ def __init__( self._update_lock: threading.Lock = threading.Lock() self._round: int = 1 self._replace_done: bool = False - self._replace_mgr: Optional[threading.Thread] = None + self._replace_mgr: threading.Thread | None = None if runtime_cache is not False: raise NotImplementedError("Options other than `runtime_cache=False` is not implemented yet.") @@ -1044,14 +1047,14 @@ def __init__( if replace_rate <= 0: raise ValueError("replace_rate must be greater than 0, otherwise, please use monai.data.CacheDataset.") - self.num_replace_workers: Optional[int] = num_replace_workers + self.num_replace_workers: int | None = num_replace_workers if self.num_replace_workers is not None: self.num_replace_workers = max(int(self.num_replace_workers), 1) self._total_num: int = len(data) self._replace_num: int = min(math.ceil(self.cache_num * replace_rate), len(data) - self.cache_num) - self._replacements: List[Any] = [None for _ in range(self._replace_num)] - self._replace_data_idx: List[int] = list(range(self._replace_num)) + self._replacements: list[Any] = [None for _ in range(self._replace_num)] + self._replace_data_idx: list[int] = list(range(self._replace_num)) self._compute_data_idx() def set_data(self, data: Sequence): @@ -1250,7 +1253,7 @@ class ZipDataset(Dataset): """ - def __init__(self, datasets: Sequence, transform: Optional[Callable] = None) -> None: + def __init__(self, datasets: Sequence, transform: Callable | None = None) -> None: """ Args: datasets: list of datasets to zip together. @@ -1327,11 +1330,11 @@ def __call__(self, input_): def __init__( self, img: Sequence, - img_transform: Optional[Callable] = None, - seg: Optional[Sequence] = None, - seg_transform: Optional[Callable] = None, - labels: Optional[Sequence] = None, - label_transform: Optional[Callable] = None, + img_transform: Callable | None = None, + seg: Sequence | None = None, + seg_transform: Callable | None = None, + labels: Sequence | None = None, + label_transform: Callable | None = None, ) -> None: """ Initializes the dataset with the filename lists. The transform `img_transform` is applied @@ -1356,7 +1359,7 @@ def __init__( def __len__(self) -> int: return len(self.dataset) - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: self._seed = self.R.randint(MAX_SEED, dtype="uint32") def __getitem__(self, index: int): @@ -1390,13 +1393,13 @@ class NPZDictItemDataset(Dataset): def __init__( self, - npzfile: Union[str, IO], - keys: Dict[str, str], - transform: Optional[Callable[..., Dict[str, Any]]] = None, - other_keys: Optional[Sequence[str]] = (), + npzfile: str | IO, + keys: dict[str, str], + transform: Callable[..., dict[str, Any]] | None = None, + other_keys: Sequence[str] | None = (), ): - self.npzfile: Union[str, IO] = npzfile if isinstance(npzfile, str) else "STREAM" - self.keys: Dict[str, str] = dict(keys) + self.npzfile: str | IO = npzfile if isinstance(npzfile, str) else "STREAM" + self.keys: dict[str, str] = dict(keys) dat = np.load(npzfile) self.arrays = {storedk: dat[datak] for datak, storedk in self.keys.items()} @@ -1483,17 +1486,17 @@ class CSVDataset(Dataset): @deprecated_arg(name="filename", new_name="src", since="0.8", msg_suffix="please use `src` instead.") def __init__( self, - src: Optional[Union[str, Sequence[str]]] = None, # also can be `DataFrame` or a sequence of `DataFrame` - row_indices: Optional[Sequence[Union[int, str]]] = None, - col_names: Optional[Sequence[str]] = None, - col_types: Optional[Dict[str, Optional[Dict[str, Any]]]] = None, - col_groups: Optional[Dict[str, Sequence[str]]] = None, - transform: Optional[Callable] = None, - kwargs_read_csv: Optional[Dict] = None, + src: str | Sequence[str] | None = None, # also can be `DataFrame` or a sequence of `DataFrame` + row_indices: Sequence[int | str] | None = None, + col_names: Sequence[str] | None = None, + col_types: dict[str, dict[str, Any] | None] | None = None, + col_groups: dict[str, Sequence[str]] | None = None, + transform: Callable | None = None, + kwargs_read_csv: dict | None = None, **kwargs, ): srcs = (src,) if not isinstance(src, (tuple, list)) else src - dfs: List = [] + dfs: list = [] for i in srcs: if isinstance(i, str): dfs.append(pd.read_csv(i, **kwargs_read_csv) if kwargs_read_csv else pd.read_csv(i)) diff --git a/monai/data/dataset_summary.py b/monai/data/dataset_summary.py index 785e8c7b88..769ae33b46 100644 --- a/monai/data/dataset_summary.py +++ b/monai/data/dataset_summary.py @@ -9,9 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings from itertools import chain -from typing import List, Optional import numpy as np import torch @@ -44,9 +45,9 @@ class DatasetSummary: def __init__( self, dataset: Dataset, - image_key: Optional[str] = "image", - label_key: Optional[str] = "label", - meta_key: Optional[KeysCollection] = None, + image_key: str | None = "image", + label_key: str | None = "label", + meta_key: KeysCollection | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, num_workers: int = 0, **kwargs, @@ -75,7 +76,7 @@ def __init__( self.image_key = image_key self.label_key = label_key self.meta_key = meta_key or f"{image_key}_{meta_key_postfix}" - self.all_meta_data: List = [] + self.all_meta_data: list = [] def collect_meta_data(self): """ diff --git a/monai/data/decathlon_datalist.py b/monai/data/decathlon_datalist.py index c1bbabceb9..6f163f972e 100644 --- a/monai/data/decathlon_datalist.py +++ b/monai/data/decathlon_datalist.py @@ -9,11 +9,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import json import os import warnings +from collections.abc import Sequence from pathlib import Path -from typing import Dict, List, Optional, Sequence, Union, overload +from typing import overload from monai.config import KeysCollection, PathLike from monai.data.utils import partition_dataset, select_cross_validation_folds @@ -26,7 +29,7 @@ def _compute_path(base_dir: PathLike, element: PathLike, check_path: bool = Fals @overload -def _compute_path(base_dir: PathLike, element: List[PathLike], check_path: bool = False) -> List[str]: +def _compute_path(base_dir: PathLike, element: list[PathLike], check_path: bool = False) -> list[str]: ... @@ -60,7 +63,7 @@ def _join_path(base_dir: PathLike, item: PathLike): return element -def _append_paths(base_dir: PathLike, is_segmentation: bool, items: List[Dict]) -> List[Dict]: +def _append_paths(base_dir: PathLike, is_segmentation: bool, items: list[dict]) -> list[dict]: """ Args: base_dir: the base directory of the dataset. @@ -87,8 +90,8 @@ def load_decathlon_datalist( data_list_file_path: PathLike, is_segmentation: bool = True, data_list_key: str = "training", - base_dir: Optional[PathLike] = None, -) -> List[Dict]: + base_dir: PathLike | None = None, +) -> list[dict]: """Load image/label paths of decathlon challenge from JSON file Json file is similar to what you get from http://medicaldecathlon.com/ @@ -132,7 +135,7 @@ def load_decathlon_datalist( return _append_paths(base_dir, is_segmentation, expected_data) -def load_decathlon_properties(data_property_file_path: PathLike, property_keys: Union[Sequence[str], str]) -> Dict: +def load_decathlon_properties(data_property_file_path: PathLike, property_keys: Sequence[str] | str) -> dict: """Load the properties from the JSON file contains data property with specified `property_keys`. Args: @@ -158,7 +161,7 @@ def load_decathlon_properties(data_property_file_path: PathLike, property_keys: def check_missing_files( - datalist: List[Dict], keys: KeysCollection, root_dir: Optional[PathLike] = None, allow_missing_keys: bool = False + datalist: list[dict], keys: KeysCollection, root_dir: PathLike | None = None, allow_missing_keys: bool = False ): """Checks whether some files in the Decathlon datalist are missing. It would be helpful to check missing files before a heavy training run. @@ -196,18 +199,18 @@ def check_missing_files( def create_cross_validation_datalist( - datalist: List[Dict], + datalist: list[dict], nfolds: int, - train_folds: Union[Sequence[int], int], - val_folds: Union[Sequence[int], int], + train_folds: Sequence[int] | int, + val_folds: Sequence[int] | int, train_key: str = "training", val_key: str = "validation", - filename: Optional[Union[Path, str]] = None, + filename: Path | str | None = None, shuffle: bool = True, seed: int = 0, check_missing: bool = False, - keys: Optional[KeysCollection] = None, - root_dir: Optional[str] = None, + keys: KeysCollection | None = None, + root_dir: str | None = None, allow_missing_keys: bool = False, raise_error: bool = True, ): diff --git a/monai/data/fft_utils.py b/monai/data/fft_utils.py index 19083aa711..d26a31d656 100644 --- a/monai/data/fft_utils.py +++ b/monai/data/fft_utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import torch from monai.config.type_definitions import NdarrayOrTensor diff --git a/monai/data/folder_layout.py b/monai/data/folder_layout.py index 2da9543e7d..7aa6effeb0 100644 --- a/monai/data/folder_layout.py +++ b/monai/data/folder_layout.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import monai from monai.config import PathLike from monai.data.utils import create_file_basename diff --git a/monai/data/grid_dataset.py b/monai/data/grid_dataset.py index 2b28949419..6c6ee293e2 100644 --- a/monai/data/grid_dataset.py +++ b/monai/data/grid_dataset.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + +from collections.abc import Callable, Hashable, Iterable, Mapping, Sequence from copy import deepcopy -from typing import Callable, Dict, Hashable, Iterable, Mapping, Optional, Sequence, Union import numpy as np @@ -32,7 +34,7 @@ class PatchIter: """ def __init__( - self, patch_size: Sequence[int], start_pos: Sequence[int] = (), mode: str = NumpyPadMode.WRAP, **pad_opts: Dict + self, patch_size: Sequence[int], start_pos: Sequence[int] = (), mode: str = NumpyPadMode.WRAP, **pad_opts: dict ): """ @@ -176,9 +178,9 @@ class GridPatchDataset(IterableDataset): @deprecated_arg(name="dataset", new_name="data", since="0.8", msg_suffix="please use `data` instead.") def __init__( self, - data: Union[Iterable, Sequence], + data: Iterable | Sequence, patch_iter: Callable, - transform: Optional[Callable] = None, + transform: Callable | None = None, with_coordinates: bool = True, ) -> None: super().__init__(data=data, transform=None) @@ -244,7 +246,7 @@ class PatchDataset(Dataset): @deprecated_arg(name="dataset", new_name="data", since="0.8", msg_suffix="please use `data` instead.") def __init__( - self, data: Sequence, patch_func: Callable, samples_per_image: int = 1, transform: Optional[Callable] = None + self, data: Sequence, patch_func: Callable, samples_per_image: int = 1, transform: Callable | None = None ) -> None: """ Args: diff --git a/monai/data/image_dataset.py b/monai/data/image_dataset.py index 89694a4bb8..6c8ddcf8de 100644 --- a/monai/data/image_dataset.py +++ b/monai/data/image_dataset.py @@ -9,7 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Callable, Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Callable, Sequence +from typing import Any import numpy as np from torch.utils.data import Dataset @@ -33,15 +36,15 @@ class ImageDataset(Dataset, Randomizable): def __init__( self, image_files: Sequence[str], - seg_files: Optional[Sequence[str]] = None, - labels: Optional[Sequence[float]] = None, - transform: Optional[Callable] = None, - seg_transform: Optional[Callable] = None, - label_transform: Optional[Callable] = None, + seg_files: Sequence[str] | None = None, + labels: Sequence[float] | None = None, + transform: Callable | None = None, + seg_transform: Callable | None = None, + label_transform: Callable | None = None, image_only: bool = True, transform_with_metadata: bool = False, dtype: DtypeLike = np.float32, - reader: Optional[Union[ImageReader, str]] = None, + reader: ImageReader | str | None = None, *args, **kwargs, ) -> None: @@ -93,7 +96,7 @@ def __init__( def __len__(self) -> int: return len(self.image_files) - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: self._seed = self.R.randint(MAX_SEED, dtype="uint32") def __getitem__(self, index: int): diff --git a/monai/data/image_reader.py b/monai/data/image_reader.py index 6919c10ffe..c1cfcfd8ca 100644 --- a/monai/data/image_reader.py +++ b/monai/data/image_reader.py @@ -9,13 +9,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import glob import os import warnings from abc import ABC, abstractmethod +from collections.abc import Callable, Iterable, Iterator, Sequence from dataclasses import dataclass from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional, Sequence, Tuple, Union +from typing import TYPE_CHECKING, Any import numpy as np from torch.utils.data._utils.collate import np_str_obj_array_pattern @@ -93,7 +96,7 @@ class ImageReader(ABC): """ @abstractmethod - def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: + def verify_suffix(self, filename: Sequence[PathLike] | PathLike) -> bool: """ Verify whether the specified `filename` is supported by the current reader. This method should return True if the reader is able to read the format suggested by the @@ -107,7 +110,7 @@ def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") @abstractmethod - def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs) -> Union[Sequence[Any], Any]: + def read(self, data: Sequence[PathLike] | PathLike, **kwargs) -> Sequence[Any] | Any: """ Read image data from specified file or files. Note that it returns a data object or a sequence of data objects. @@ -120,7 +123,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs) -> Union[Seq raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") @abstractmethod - def get_data(self, img) -> Tuple[np.ndarray, Dict]: + def get_data(self, img) -> tuple[np.ndarray, dict]: """ Extract data array and metadata from loaded image and return them. This function must return two objects, the first is a numpy array of image data, @@ -133,7 +136,7 @@ def get_data(self, img) -> Tuple[np.ndarray, Dict]: raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") -def _copy_compatible_dict(from_dict: Dict, to_dict: Dict): +def _copy_compatible_dict(from_dict: dict, to_dict: dict): if not isinstance(to_dict, dict): raise ValueError(f"to_dict must be a Dict, got {type(to_dict)}.") if not to_dict: @@ -156,7 +159,7 @@ def _copy_compatible_dict(from_dict: Dict, to_dict: Dict): ) -def _stack_images(image_list: List, meta_dict: Dict): +def _stack_images(image_list: list, meta_dict: dict): if len(image_list) <= 1: return image_list[0] if meta_dict.get(MetaKeys.ORIGINAL_CHANNEL_DIM, None) not in ("no_channel", None): @@ -201,7 +204,7 @@ class ITKReader(ImageReader): def __init__( self, - channel_dim: Optional[int] = None, + channel_dim: int | None = None, series_name: str = "", reverse_indexing: bool = False, series_meta: bool = False, @@ -216,7 +219,7 @@ def __init__( self.series_meta = series_meta self.affine_lps_to_ras = affine_lps_to_ras - def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: + def verify_suffix(self, filename: Sequence[PathLike] | PathLike) -> bool: """ Verify whether the specified file or files format is supported by ITK reader. @@ -227,7 +230,7 @@ def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: """ return has_itk - def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs): + def read(self, data: Sequence[PathLike] | PathLike, **kwargs): """ Read image data from specified file or files, it can read a list of images and stack them together as multi-channel data in `get_data()`. @@ -277,7 +280,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs): img_.append(itk.imread(name, **kwargs_)) return img_ if len(filenames) > 1 else img_[0] - def get_data(self, img) -> Tuple[np.ndarray, Dict]: + def get_data(self, img) -> tuple[np.ndarray, dict]: """ Extract data array and metadata from loaded image and return them. This function returns two objects, first is numpy array of image data, second is dict of metadata. @@ -289,8 +292,8 @@ def get_data(self, img) -> Tuple[np.ndarray, Dict]: img: an ITK image object loaded from an image file or a list of ITK image objects. """ - img_array: List[np.ndarray] = [] - compatible_meta: Dict = {} + img_array: list[np.ndarray] = [] + compatible_meta: dict = {} for i in ensure_tuple(img): data = self._get_array_data(i) @@ -310,7 +313,7 @@ def get_data(self, img) -> Tuple[np.ndarray, Dict]: return _stack_images(img_array, compatible_meta), compatible_meta - def _get_meta_dict(self, img) -> Dict: + def _get_meta_dict(self, img) -> dict: """ Get all the metadata of the image and convert to dict type. @@ -432,11 +435,11 @@ class PydicomReader(ImageReader): def __init__( self, - channel_dim: Optional[int] = None, + channel_dim: int | None = None, affine_lps_to_ras: bool = True, swap_ij: bool = True, prune_metadata: bool = True, - label_dict: Optional[Dict] = None, + label_dict: dict | None = None, **kwargs, ): super().__init__() @@ -447,7 +450,7 @@ def __init__( self.prune_metadata = prune_metadata self.label_dict = label_dict - def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: + def verify_suffix(self, filename: Sequence[PathLike] | PathLike) -> bool: """ Verify whether the specified file or files format is supported by Pydicom reader. @@ -458,7 +461,7 @@ def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: """ return has_pydicom - def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs): + def read(self, data: Sequence[PathLike] | PathLike, **kwargs): """ Read image data from specified file or files, it can read a list of images and stack them together as multi-channel data in `get_data()`. @@ -515,7 +518,7 @@ def _combine_dicom_series(self, data: Iterable): Returns: a tuple that consisted with data array and metadata. """ - slices: List = [] + slices: list = [] # for a dicom series for slc_ds in data: if hasattr(slc_ds, "InstanceNumber"): @@ -564,7 +567,7 @@ def _combine_dicom_series(self, data: Iterable): return stack_array, stack_metadata - def get_data(self, data) -> Tuple[np.ndarray, Dict]: + def get_data(self, data) -> tuple[np.ndarray, dict]: """ Extract data array and metadata from loaded image and return them. This function returns two objects, first is numpy array of image data, second is dict of metadata. @@ -591,7 +594,7 @@ def get_data(self, data) -> Tuple[np.ndarray, Dict]: # combine dicom series if exists if self.has_series is True: # a list, all objects within a list belong to one dicom series - if not isinstance(data[0], List): + if not isinstance(data[0], list): dicom_data.append(self._combine_dicom_series(data)) # a list of list, each inner list represents a dicom series else: @@ -599,7 +602,7 @@ def get_data(self, data) -> Tuple[np.ndarray, Dict]: dicom_data.append(self._combine_dicom_series(series)) else: # a single pydicom dataset object - if not isinstance(data, List): + if not isinstance(data, list): data = [data] for d in data: if hasattr(d, "SegmentSequence"): @@ -610,8 +613,8 @@ def get_data(self, data) -> Tuple[np.ndarray, Dict]: metadata[MetaKeys.SPATIAL_SHAPE] = data_array.shape dicom_data.append((data_array, metadata)) - img_array: List[np.ndarray] = [] - compatible_meta: Dict = {} + img_array: list[np.ndarray] = [] + compatible_meta: dict = {} for (data_array, metadata) in ensure_tuple(dicom_data): img_array.append(np.ascontiguousarray(np.swapaxes(data_array, 0, 1) if self.swap_ij else data_array)) @@ -638,7 +641,7 @@ def get_data(self, data) -> Tuple[np.ndarray, Dict]: return _stack_images(img_array, compatible_meta), compatible_meta - def _get_meta_dict(self, img) -> Dict: + def _get_meta_dict(self, img) -> dict: """ Get all the metadata of the image and convert to dict type. @@ -664,7 +667,7 @@ def _get_meta_dict(self, img) -> Dict: return metadata # type: ignore - def _get_affine(self, metadata: Dict, lps_to_ras: bool = True): + def _get_affine(self, metadata: dict, lps_to_ras: bool = True): """ Get or construct the affine matrix of the image, it can be used to correct spacing, orientation or execute spatial transforms. @@ -880,7 +883,7 @@ class NibabelReader(ImageReader): @deprecated_arg("dtype", since="1.0", msg_suffix="please modify dtype of the returned by ``get_data`` instead.") def __init__( self, - channel_dim: Optional[int] = None, + channel_dim: int | None = None, as_closest_canonical: bool = False, squeeze_non_spatial_dims: bool = False, dtype: DtypeLike = np.float32, @@ -893,7 +896,7 @@ def __init__( self.dtype = dtype # deprecated self.kwargs = kwargs - def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: + def verify_suffix(self, filename: Sequence[PathLike] | PathLike) -> bool: """ Verify whether the specified file or files format is supported by Nibabel reader. @@ -905,7 +908,7 @@ def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: suffixes: Sequence[str] = ["nii", "nii.gz"] return has_nib and is_supported_format(filename, suffixes) - def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs): + def read(self, data: Sequence[PathLike] | PathLike, **kwargs): """ Read image data from specified file or files, it can read a list of images and stack them together as multi-channel data in `get_data()`. @@ -918,7 +921,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs): https://github.com/nipy/nibabel/blob/master/nibabel/loadsave.py """ - img_: List[Nifti1Image] = [] + img_: list[Nifti1Image] = [] filenames: Sequence[PathLike] = ensure_tuple(data) kwargs_ = self.kwargs.copy() @@ -929,7 +932,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs): img_.append(img) return img_ if len(filenames) > 1 else img_[0] - def get_data(self, img) -> Tuple[np.ndarray, Dict]: + def get_data(self, img) -> tuple[np.ndarray, dict]: """ Extract data array and metadata from loaded image and return them. This function returns two objects, first is numpy array of image data, second is dict of metadata. @@ -941,8 +944,8 @@ def get_data(self, img) -> Tuple[np.ndarray, Dict]: img: a Nibabel image object loaded from an image file or a list of Nibabel image objects. """ - img_array: List[np.ndarray] = [] - compatible_meta: Dict = {} + img_array: list[np.ndarray] = [] + compatible_meta: dict = {} for i in ensure_tuple(img): header = self._get_meta_dict(i) @@ -970,7 +973,7 @@ def get_data(self, img) -> Tuple[np.ndarray, Dict]: return _stack_images(img_array, compatible_meta), compatible_meta - def _get_meta_dict(self, img) -> Dict: + def _get_meta_dict(self, img) -> dict: """ Get the all the metadata of the image and convert to dict type. @@ -1046,7 +1049,7 @@ class NumpyReader(ImageReader): """ - def __init__(self, npz_keys: Optional[KeysCollection] = None, channel_dim: Optional[int] = None, **kwargs): + def __init__(self, npz_keys: KeysCollection | None = None, channel_dim: int | None = None, **kwargs): super().__init__() if npz_keys is not None: npz_keys = ensure_tuple(npz_keys) @@ -1054,7 +1057,7 @@ def __init__(self, npz_keys: Optional[KeysCollection] = None, channel_dim: Optio self.channel_dim = channel_dim self.kwargs = kwargs - def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: + def verify_suffix(self, filename: Sequence[PathLike] | PathLike) -> bool: """ Verify whether the specified file or files format is supported by Numpy reader. @@ -1065,7 +1068,7 @@ def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: suffixes: Sequence[str] = ["npz", "npy"] return is_supported_format(filename, suffixes) - def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs): + def read(self, data: Sequence[PathLike] | PathLike, **kwargs): """ Read image data from specified file or files, it can read a list of data files and stack them together as multi-channel data in `get_data()`. @@ -1078,7 +1081,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs): https://numpy.org/doc/stable/reference/generated/numpy.load.html """ - img_: List[Nifti1Image] = [] + img_: list[Nifti1Image] = [] filenames: Sequence[PathLike] = ensure_tuple(data) kwargs_ = self.kwargs.copy() @@ -1095,7 +1098,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs): return img_ if len(img_) > 1 else img_[0] - def get_data(self, img) -> Tuple[np.ndarray, Dict]: + def get_data(self, img) -> tuple[np.ndarray, dict]: """ Extract data array and metadata from loaded image and return them. This function returns two objects, first is numpy array of image data, second is dict of metadata. @@ -1107,13 +1110,13 @@ def get_data(self, img) -> Tuple[np.ndarray, Dict]: img: a Numpy array loaded from a file or a list of Numpy arrays. """ - img_array: List[np.ndarray] = [] - compatible_meta: Dict = {} + img_array: list[np.ndarray] = [] + compatible_meta: dict = {} if isinstance(img, np.ndarray): img = (img,) for i in ensure_tuple(img): - header: Dict[MetaKeys, Any] = {} + header: dict[MetaKeys, Any] = {} if isinstance(i, np.ndarray): # if `channel_dim` is None, can not detect the channel dim, use all the dims as spatial_shape spatial_shape = np.asarray(i.shape) @@ -1142,12 +1145,12 @@ class PILReader(ImageReader): https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.open """ - def __init__(self, converter: Optional[Callable] = None, **kwargs): + def __init__(self, converter: Callable | None = None, **kwargs): super().__init__() self.converter = converter self.kwargs = kwargs - def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: + def verify_suffix(self, filename: Sequence[PathLike] | PathLike) -> bool: """ Verify whether the specified file or files format is supported by PIL reader. @@ -1158,7 +1161,7 @@ def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: suffixes: Sequence[str] = ["png", "jpg", "jpeg", "bmp"] return has_pil and is_supported_format(filename, suffixes) - def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): + def read(self, data: Sequence[PathLike] | PathLike | np.ndarray, **kwargs): """ Read image data from specified file or files, it can read a list of images and stack them together as multi-channel data in `get_data()`. @@ -1171,7 +1174,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.open """ - img_: List[PILImage.Image] = [] + img_: list[PILImage.Image] = [] filenames: Sequence[PathLike] = ensure_tuple(data) kwargs_ = self.kwargs.copy() @@ -1184,7 +1187,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): return img_ if len(filenames) > 1 else img_[0] - def get_data(self, img) -> Tuple[np.ndarray, Dict]: + def get_data(self, img) -> tuple[np.ndarray, dict]: """ Extract data array and metadata from loaded image and return them. This function returns two objects, first is numpy array of image data, second is dict of metadata. @@ -1198,8 +1201,8 @@ def get_data(self, img) -> Tuple[np.ndarray, Dict]: img: a PIL Image object loaded from a file or a list of PIL Image objects. """ - img_array: List[np.ndarray] = [] - compatible_meta: Dict = {} + img_array: list[np.ndarray] = [] + compatible_meta: dict = {} for i in ensure_tuple(img): header = self._get_meta_dict(i) @@ -1213,7 +1216,7 @@ def get_data(self, img) -> Tuple[np.ndarray, Dict]: return _stack_images(img_array, compatible_meta), compatible_meta - def _get_meta_dict(self, img) -> Dict: + def _get_meta_dict(self, img) -> dict: """ Get the all the metadata of the image and convert to dict type. Args: @@ -1271,7 +1274,7 @@ def _set_reader(backend: str): return TiffFile raise ValueError("`backend` should be 'cuCIM', 'OpenSlide' or 'TiffFile'.") - def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: + def verify_suffix(self, filename: Sequence[PathLike] | PathLike) -> bool: """ Verify whether the specified file or files format is supported by WSI reader. @@ -1281,7 +1284,7 @@ def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: """ return is_supported_format(filename, ["tif", "tiff"]) - def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): + def read(self, data: Sequence[PathLike] | PathLike | np.ndarray, **kwargs): """ Read image data from given file or list of files. @@ -1297,7 +1300,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): image object or list of image objects """ - img_: List = [] + img_: list = [] filenames: Sequence[PathLike] = ensure_tuple(data) kwargs_ = self.kwargs.copy() @@ -1313,12 +1316,12 @@ def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): def get_data( self, img, - location: Tuple[int, int] = (0, 0), - size: Optional[Tuple[int, int]] = None, - level: Optional[int] = None, + location: tuple[int, int] = (0, 0), + size: tuple[int, int] | None = None, + level: int | None = None, dtype: DtypeLike = np.uint8, - grid_shape: Tuple[int, int] = (1, 1), - patch_size: Optional[Union[int, Tuple[int, int]]] = None, + grid_shape: tuple[int, int] = (1, 1), + patch_size: int | tuple[int, int] | None = None, ): """ Extract regions as numpy array from WSI image and return them. @@ -1345,7 +1348,7 @@ def get_data( region = self._extract_region(img, location=location, size=size, level=level, dtype=dtype) # Add necessary metadata - metadata: Dict = {} + metadata: dict = {} metadata[MetaKeys.SPATIAL_SHAPE] = np.asarray(region.shape[:-1]) metadata[MetaKeys.ORIGINAL_CHANNEL_DIM] = -1 @@ -1403,8 +1406,8 @@ def _get_image_size(self, img, size, level, location): def _extract_region( self, img_obj, - size: Optional[Tuple[int, int]], - location: Tuple[int, int] = (0, 0), + size: tuple[int, int] | None, + location: tuple[int, int] = (0, 0), level: int = 0, dtype: DtypeLike = np.uint8, ): @@ -1464,8 +1467,8 @@ def convert_to_rgb_array(self, raw_region, dtype: DtypeLike = np.uint8): def _extract_patches( self, region: np.ndarray, - grid_shape: Tuple[int, int] = (1, 1), - patch_size: Optional[Tuple[int, int]] = None, + grid_shape: tuple[int, int] = (1, 1), + patch_size: tuple[int, int] | None = None, dtype: DtypeLike = np.uint8, ): if patch_size is None and grid_shape == (1, 1): @@ -1522,8 +1525,8 @@ class NrrdReader(ImageReader): def __init__( self, - channel_dim: Optional[int] = None, - dtype: Union[np.dtype, type, str, None] = np.float32, + channel_dim: int | None = None, + dtype: np.dtype | type | str | None = np.float32, index_order: str = "F", **kwargs, ): @@ -1532,7 +1535,7 @@ def __init__( self.index_order = index_order self.kwargs = kwargs - def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: + def verify_suffix(self, filename: Sequence[PathLike] | PathLike) -> bool: """ Verify whether the specified `filename` is supported by pynrrd reader. @@ -1544,7 +1547,7 @@ def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: suffixes: Sequence[str] = ["nrrd", "seg.nrrd"] return has_nrrd and is_supported_format(filename, suffixes) - def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs) -> Union[Sequence[Any], Any]: + def read(self, data: Sequence[PathLike] | PathLike, **kwargs) -> Sequence[Any] | Any: """ Read image data from specified file or files. Note that it returns a data object or a sequence of data objects. @@ -1554,7 +1557,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs) -> Union[Seq kwargs: additional args for actual `read` API of 3rd party libs. """ - img_: List = [] + img_: list = [] filenames: Sequence[PathLike] = ensure_tuple(data) kwargs_ = self.kwargs.copy() kwargs_.update(kwargs) @@ -1563,7 +1566,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs) -> Union[Seq img_.append(nrrd_image) return img_ if len(filenames) > 1 else img_[0] - def get_data(self, img: Union[NrrdImage, List[NrrdImage]]) -> Tuple[np.ndarray, Dict]: + def get_data(self, img: NrrdImage | list[NrrdImage]) -> tuple[np.ndarray, dict]: """ Extract data array and metadata from loaded image and return them. This function must return two objects, the first is a numpy array of image data, @@ -1573,8 +1576,8 @@ def get_data(self, img: Union[NrrdImage, List[NrrdImage]]) -> Tuple[np.ndarray, img: a `NrrdImage` loaded from an image file or a list of image objects. """ - img_array: List[np.ndarray] = [] - compatible_meta: Dict = {} + img_array: list[np.ndarray] = [] + compatible_meta: dict = {} for i in ensure_tuple(img): data = i.array.astype(self.dtype) diff --git a/monai/data/image_writer.py b/monai/data/image_writer.py index 8d42d032c9..20e9e08c6a 100644 --- a/monai/data/image_writer.py +++ b/monai/data/image_writer.py @@ -9,7 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional, Sequence, Union, cast +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING, Any, cast import numpy as np @@ -58,7 +61,7 @@ "logger", ] -SUPPORTED_WRITERS: Dict = {} +SUPPORTED_WRITERS: dict = {} def register_writer(ext_name, *im_writers): @@ -178,14 +181,14 @@ def __init__(self, **kwargs): The current member in the base class is ``self.data_obj``, the subclasses can add more members, so that necessary meta information can be stored in the object and shared among the class methods. """ - self.data_obj: Union[Any, NdarrayOrTensor] = None + self.data_obj: Any | NdarrayOrTensor = None for k, v in kwargs.items(): setattr(self, k, v) def set_data_array(self, data_array, **kwargs): raise NotImplementedError(f"Subclasses of {self.__class__.__name__} must implement this method.") - def set_metadata(self, meta_dict: Optional[Mapping], **options): + def set_metadata(self, meta_dict: Mapping | None, **options): raise NotImplementedError(f"Subclasses of {self.__class__.__name__} must implement this method.") def write(self, filename: PathLike, verbose: bool = True, **kwargs): @@ -205,9 +208,9 @@ def create_backend_obj(cls, data_array: NdarrayOrTensor, **kwargs) -> np.ndarray def resample_if_needed( cls, data_array: NdarrayOrTensor, - affine: Optional[NdarrayOrTensor] = None, - target_affine: Optional[NdarrayOrTensor] = None, - output_spatial_shape: Union[Sequence[int], int, None] = None, + affine: NdarrayOrTensor | None = None, + target_affine: NdarrayOrTensor | None = None, + output_spatial_shape: Sequence[int] | int | None = None, mode: str = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, @@ -279,9 +282,9 @@ def resample_if_needed( def convert_to_channel_last( cls, data: NdarrayOrTensor, - channel_dim: Union[None, int, Sequence[int]] = 0, + channel_dim: None | int | Sequence[int] = 0, squeeze_end_dims: bool = True, - spatial_ndim: Optional[int] = 3, + spatial_ndim: int | None = 3, contiguous: bool = False, ): """ @@ -325,7 +328,7 @@ def convert_to_channel_last( return data @classmethod - def get_meta_info(cls, metadata: Optional[Mapping] = None): + def get_meta_info(cls, metadata: Mapping | None = None): """ Extracts relevant meta information from the metadata object (using ``.get``). Optional keys are ``"spatial_shape"``, ``MetaKeys.AFFINE``, ``"original_affine"``. @@ -366,7 +369,7 @@ class ITKWriter(ImageWriter): """ output_dtype: DtypeLike = None - channel_dim: Optional[int] + channel_dim: int | None def __init__(self, output_dtype: DtypeLike = np.float32, affine_lps_to_ras: bool = True, **kwargs): """ @@ -388,7 +391,7 @@ def __init__(self, output_dtype: DtypeLike = np.float32, affine_lps_to_ras: bool ) def set_data_array( - self, data_array: NdarrayOrTensor, channel_dim: Optional[int] = 0, squeeze_end_dims: bool = True, **kwargs + self, data_array: NdarrayOrTensor, channel_dim: int | None = 0, squeeze_end_dims: bool = True, **kwargs ): """ Convert ``data_array`` into 'channel-last' numpy ndarray. @@ -413,7 +416,7 @@ def set_data_array( channel_dim if self.data_obj is not None and len(self.data_obj.shape) >= _r else None ) # channel dim is at the end - def set_metadata(self, meta_dict: Optional[Mapping] = None, resample: bool = True, **options): + def set_metadata(self, meta_dict: Mapping | None = None, resample: bool = True, **options): """ Resample ``self.dataobj`` if needed. This method assumes ``self.data_obj`` is a 'channel-last' ndarray. @@ -470,8 +473,8 @@ def write(self, filename: PathLike, verbose: bool = False, **kwargs): def create_backend_obj( cls, data_array: NdarrayOrTensor, - channel_dim: Optional[int] = 0, - affine: Optional[NdarrayOrTensor] = None, + channel_dim: int | None = 0, + affine: NdarrayOrTensor | None = None, dtype: DtypeLike = np.float32, affine_lps_to_ras: bool = True, **kwargs, @@ -551,7 +554,7 @@ def __init__(self, output_dtype: DtypeLike = np.float32, **kwargs): super().__init__(output_dtype=output_dtype, affine=None, **kwargs) def set_data_array( - self, data_array: NdarrayOrTensor, channel_dim: Optional[int] = 0, squeeze_end_dims: bool = True, **kwargs + self, data_array: NdarrayOrTensor, channel_dim: int | None = 0, squeeze_end_dims: bool = True, **kwargs ): """ Convert ``data_array`` into 'channel-last' numpy ndarray. @@ -571,7 +574,7 @@ def set_data_array( spatial_ndim=kwargs.pop("spatial_ndim", 3), ) - def set_metadata(self, meta_dict: Optional[Mapping], resample: bool = True, **options): + def set_metadata(self, meta_dict: Mapping | None, resample: bool = True, **options): """ Resample ``self.dataobj`` if needed. This method assumes ``self.data_obj`` is a 'channel-last' ndarray. @@ -626,7 +629,7 @@ def write(self, filename: PathLike, verbose: bool = False, **obj_kwargs): @classmethod def create_backend_obj( - cls, data_array: NdarrayOrTensor, affine: Optional[NdarrayOrTensor] = None, dtype: DtypeLike = None, **kwargs + cls, data_array: NdarrayOrTensor, affine: NdarrayOrTensor | None = None, dtype: DtypeLike = None, **kwargs ): """ Create an Nifti1Image object from ``data_array``. This method assumes a 'channel-last' ``data_array``. @@ -678,11 +681,11 @@ class PILWriter(ImageWriter): """ output_dtype: DtypeLike - channel_dim: Optional[int] - scale: Optional[int] + channel_dim: int | None + scale: int | None def __init__( - self, output_dtype: DtypeLike = np.float32, channel_dim: Optional[int] = 0, scale: Optional[int] = 255, **kwargs + self, output_dtype: DtypeLike = np.float32, channel_dim: int | None = 0, scale: int | None = 255, **kwargs ): """ Args: @@ -698,7 +701,7 @@ def __init__( def set_data_array( self, data_array: NdarrayOrTensor, - channel_dim: Optional[int] = 0, + channel_dim: int | None = 0, squeeze_end_dims: bool = True, contiguous: bool = False, **kwargs, @@ -723,7 +726,7 @@ def set_data_array( contiguous=contiguous, ) - def set_metadata(self, meta_dict: Optional[Mapping] = None, resample: bool = True, **options): + def set_metadata(self, meta_dict: Mapping | None = None, resample: bool = True, **options): """ Resample ``self.dataobj`` if needed. This method assumes ``self.data_obj`` is a 'channel-last' ndarray. @@ -769,14 +772,14 @@ def write(self, filename: PathLike, verbose: bool = False, **kwargs): self.data_obj.save(filename, **kwargs) @classmethod - def get_meta_info(cls, metadata: Optional[Mapping] = None): + def get_meta_info(cls, metadata: Mapping | None = None): return None if not metadata else metadata.get(MetaKeys.SPATIAL_SHAPE) @classmethod def resample_and_clip( cls, data_array: NdarrayOrTensor, - output_spatial_shape: Optional[Sequence[int]] = None, + output_spatial_shape: Sequence[int] | None = None, mode: str = InterpolateMode.BICUBIC, ) -> np.ndarray: """ @@ -810,7 +813,7 @@ def create_backend_obj( cls, data_array: NdarrayOrTensor, dtype: DtypeLike = None, - scale: Optional[int] = 255, + scale: int | None = 255, reverse_indexing: bool = True, **kwargs, ): diff --git a/monai/data/iterable_dataset.py b/monai/data/iterable_dataset.py index 1d6b4b06b2..d191a7c812 100644 --- a/monai/data/iterable_dataset.py +++ b/monai/data/iterable_dataset.py @@ -9,7 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Callable, Iterable, Iterator, Sequence +from typing import Any from torch.utils.data import IterableDataset as _TorchIterableDataset from torch.utils.data import get_worker_info @@ -37,7 +40,7 @@ class IterableDataset(_TorchIterableDataset): """ - def __init__(self, data: Iterable[Any], transform: Optional[Callable] = None) -> None: + def __init__(self, data: Iterable[Any], transform: Callable | None = None) -> None: """ Args: data: input data source to load and transform to generate dataset for model. @@ -45,7 +48,7 @@ def __init__(self, data: Iterable[Any], transform: Optional[Callable] = None) -> """ self.data = data self.transform = transform - self.source: Optional[Iterator[Any]] = None + self.source: Iterator[Any] | None = None def __iter__(self): info = get_worker_info() @@ -110,7 +113,7 @@ def randomized_pop(self, buffer): def generate_item(self): """Fill a `buffer` list up to `self.size`, then generate randomly popped items.""" - buffer: List[Any] = [] + buffer: list[Any] = [] for item in iter(self.data): if len(buffer) >= self.size: yield self.randomized_pop(buffer) @@ -200,16 +203,16 @@ class CSVIterableDataset(IterableDataset): @deprecated_arg(name="filename", new_name="src", since="0.8", msg_suffix="please use `src` instead.") def __init__( self, - src: Union[Union[str, Sequence[str]], Union[Iterable, Sequence[Iterable]]], + src: str | Sequence[str] | Iterable | Sequence[Iterable], chunksize: int = 1000, - buffer_size: Optional[int] = None, - col_names: Optional[Sequence[str]] = None, - col_types: Optional[Dict[str, Optional[Dict[str, Any]]]] = None, - col_groups: Optional[Dict[str, Sequence[str]]] = None, - transform: Optional[Callable] = None, + buffer_size: int | None = None, + col_names: Sequence[str] | None = None, + col_types: dict[str, dict[str, Any] | None] | None = None, + col_groups: dict[str, Sequence[str]] | None = None, + transform: Callable | None = None, shuffle: bool = False, seed: int = 0, - kwargs_read_csv: Optional[Dict] = None, + kwargs_read_csv: dict | None = None, **kwargs, ): self.src = src @@ -225,11 +228,11 @@ def __init__( kwargs.pop("filename", None) self.kwargs = kwargs - self.iters: List[Iterable] = self.reset() + self.iters: list[Iterable] = self.reset() super().__init__(data=None, transform=transform) # type: ignore @deprecated_arg(name="filename", new_name="src", since="0.8", msg_suffix="please use `src` instead.") - def reset(self, src: Optional[Union[Union[str, Sequence[str]], Union[Iterable, Sequence[Iterable]]]] = None): + def reset(self, src: str | Sequence[str] | Iterable | Sequence[Iterable] | None = None): """ Reset the pandas `TextFileReader` iterable object to read data. For more details, please check: https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html?#iteration. diff --git a/monai/data/nifti_saver.py b/monai/data/nifti_saver.py index ddc5e10f63..293b3531ac 100644 --- a/monai/data/nifti_saver.py +++ b/monai/data/nifti_saver.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, Optional, Union +from __future__ import annotations import numpy as np import torch @@ -110,7 +110,7 @@ def __init__( self.separate_folder = separate_folder self.print_log = print_log - def save(self, data: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] = None) -> None: + def save(self, data: torch.Tensor | np.ndarray, meta_data: dict | None = None) -> None: """ Save data into a NIfTI file. The meta_data could optionally have the following keys: @@ -181,7 +181,7 @@ def save(self, data: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] if self.print_log: print(f"file written: {path}.") - def save_batch(self, batch_data: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] = None) -> None: + def save_batch(self, batch_data: torch.Tensor | np.ndarray, meta_data: dict | None = None) -> None: """ Save a batch of data into NIfTI format files. diff --git a/monai/data/nifti_writer.py b/monai/data/nifti_writer.py index 234f5b0a22..efaeecb24d 100644 --- a/monai/data/nifti_writer.py +++ b/monai/data/nifti_writer.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import numpy as np import torch @@ -29,10 +31,10 @@ def write_nifti( data: NdarrayOrTensor, file_name: str, - affine: Optional[NdarrayOrTensor] = None, - target_affine: Optional[np.ndarray] = None, + affine: NdarrayOrTensor | None = None, + target_affine: np.ndarray | None = None, resample: bool = True, - output_spatial_shape: Union[Sequence[int], np.ndarray, None] = None, + output_spatial_shape: Sequence[int] | np.ndarray | None = None, mode: str = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, diff --git a/monai/data/png_saver.py b/monai/data/png_saver.py index 5b6e3b5a30..edeb1fe200 100644 --- a/monai/data/png_saver.py +++ b/monai/data/png_saver.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, Optional, Union +from __future__ import annotations import numpy as np import torch @@ -43,7 +43,7 @@ def __init__( output_ext: str = ".png", resample: bool = True, mode: str = InterpolateMode.NEAREST, - scale: Optional[int] = None, + scale: int | None = None, data_root_dir: PathLike = "", separate_folder: bool = True, print_log: bool = True, @@ -87,7 +87,7 @@ def __init__( self._data_index = 0 - def save(self, data: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] = None) -> None: + def save(self, data: torch.Tensor | np.ndarray, meta_data: dict | None = None) -> None: """ Save data into a png file. The meta_data could optionally have the following keys: @@ -144,7 +144,7 @@ def save(self, data: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] if self.print_log: print(f"file written: {path}.") - def save_batch(self, batch_data: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] = None) -> None: + def save_batch(self, batch_data: torch.Tensor | np.ndarray, meta_data: dict | None = None) -> None: """Save a batch of data into png format files. Args: diff --git a/monai/data/png_writer.py b/monai/data/png_writer.py index 8c49944843..932ded7111 100644 --- a/monai/data/png_writer.py +++ b/monai/data/png_writer.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence +from __future__ import annotations + +from collections.abc import Sequence import numpy as np @@ -30,9 +32,9 @@ def write_png( data: np.ndarray, file_name: str, - output_spatial_shape: Optional[Sequence[int]] = None, + output_spatial_shape: Sequence[int] | None = None, mode: str = InterpolateMode.BICUBIC, - scale: Optional[int] = None, + scale: int | None = None, ) -> None: """ Write numpy data into png files to disk. diff --git a/monai/data/samplers.py b/monai/data/samplers.py index 9392ba562d..12cb7cf584 100644 --- a/monai/data/samplers.py +++ b/monai/data/samplers.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence +from __future__ import annotations + +from collections.abc import Sequence import torch from torch.utils.data import Dataset @@ -42,8 +44,8 @@ def __init__( self, dataset: Dataset, even_divisible: bool = True, - num_replicas: Optional[int] = None, - rank: Optional[int] = None, + num_replicas: int | None = None, + rank: int | None = None, shuffle: bool = True, **kwargs, ): @@ -88,11 +90,11 @@ def __init__( self, dataset: Dataset, weights: Sequence[float], - num_samples_per_rank: Optional[int] = None, - generator: Optional[torch.Generator] = None, + num_samples_per_rank: int | None = None, + generator: torch.Generator | None = None, even_divisible: bool = True, - num_replicas: Optional[int] = None, - rank: Optional[int] = None, + num_replicas: int | None = None, + rank: int | None = None, shuffle: bool = True, **kwargs, ): diff --git a/monai/data/synthetic.py b/monai/data/synthetic.py index a1a85338fe..0ecdc19f89 100644 --- a/monai/data/synthetic.py +++ b/monai/data/synthetic.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Tuple +from __future__ import annotations import numpy as np @@ -26,9 +26,9 @@ def create_test_image_2d( rad_min: int = 5, noise_max: float = 0.0, num_seg_classes: int = 5, - channel_dim: Optional[int] = None, - random_state: Optional[np.random.RandomState] = None, -) -> Tuple[np.ndarray, np.ndarray]: + channel_dim: int | None = None, + random_state: np.random.RandomState | None = None, +) -> tuple[np.ndarray, np.ndarray]: """ Return a noisy 2D image with `num_objs` circles and a 2D mask image. The maximum and minimum radii of the circles are given as `rad_max` and `rad_min`. The mask will have `num_seg_classes` number of classes for segmentations labeled @@ -103,9 +103,9 @@ def create_test_image_3d( rad_min: int = 5, noise_max: float = 0.0, num_seg_classes: int = 5, - channel_dim: Optional[int] = None, - random_state: Optional[np.random.RandomState] = None, -) -> Tuple[np.ndarray, np.ndarray]: + channel_dim: int | None = None, + random_state: np.random.RandomState | None = None, +) -> tuple[np.ndarray, np.ndarray]: """ Return a noisy 3D image and segmentation. diff --git a/monai/data/test_time_augmentation.py b/monai/data/test_time_augmentation.py index fdff86b745..23572dcef4 100644 --- a/monai/data/test_time_augmentation.py +++ b/monai/data/test_time_augmentation.py @@ -9,9 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings +from collections.abc import Callable from copy import deepcopy -from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Any import numpy as np import torch @@ -109,14 +112,14 @@ def __init__( batch_size: int, num_workers: int = 0, inferrer_fn: Callable = _identity, - device: Union[str, torch.device] = "cpu", + device: str | torch.device = "cpu", image_key=CommonKeys.IMAGE, orig_key=CommonKeys.LABEL, nearest_interp: bool = True, - orig_meta_keys: Optional[str] = None, + orig_meta_keys: str | None = None, meta_key_postfix=DEFAULT_POST_FIX, to_tensor: bool = True, - output_device: Union[str, torch.device] = "cpu", + output_device: str | torch.device = "cpu", post_func: Callable = _identity, return_full_data: bool = False, progress: bool = True, @@ -163,8 +166,8 @@ def _check_transforms(self): ) def __call__( - self, data: Dict[str, Any], num_examples: int = 10 - ) -> Union[Tuple[NdarrayOrTensor, NdarrayOrTensor, NdarrayOrTensor, float], NdarrayOrTensor]: + self, data: dict[str, Any], num_examples: int = 10 + ) -> tuple[NdarrayOrTensor, NdarrayOrTensor, NdarrayOrTensor, float] | NdarrayOrTensor: """ Args: data: dictionary data to be processed. @@ -189,7 +192,7 @@ def __call__( ds = Dataset(data_in, self.transform) dl = DataLoader(ds, num_workers=self.num_workers, batch_size=self.batch_size, collate_fn=pad_list_data_collate) - outs: List = [] + outs: list = [] for b in tqdm(dl) if has_tqdm and self.progress else dl: # do model forward pass diff --git a/monai/data/thread_buffer.py b/monai/data/thread_buffer.py index 30bccd1ec9..e86a4043a2 100644 --- a/monai/data/thread_buffer.py +++ b/monai/data/thread_buffer.py @@ -9,10 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from multiprocessing.context import SpawnContext from queue import Empty, Full, Queue from threading import Thread -from typing import Optional import torch @@ -41,7 +42,7 @@ def __init__(self, src, buffer_size: int = 1, timeout: float = 0.01): self.buffer_size = buffer_size self.timeout = timeout self.buffer: Queue = Queue(self.buffer_size) - self.gen_thread: Optional[Thread] = None + self.gen_thread: Thread | None = None self.is_running = False def enqueue_values(self): diff --git a/monai/data/torchscript_utils.py b/monai/data/torchscript_utils.py index ca46dd9dc4..cabf06ce89 100644 --- a/monai/data/torchscript_utils.py +++ b/monai/data/torchscript_utils.py @@ -9,10 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import datetime import json import os -from typing import IO, Any, Mapping, Optional, Sequence, Tuple, Union +from collections.abc import Mapping, Sequence +from typing import IO, Any import torch @@ -24,11 +27,11 @@ def save_net_with_metadata( jit_obj: torch.nn.Module, - filename_prefix_or_stream: Union[str, IO[Any]], + filename_prefix_or_stream: str | IO[Any], include_config_vals: bool = True, append_timestamp: bool = False, - meta_values: Optional[Mapping[str, Any]] = None, - more_extra_files: Optional[Mapping[str, bytes]] = None, + meta_values: Mapping[str, Any] | None = None, + more_extra_files: Mapping[str, bytes] | None = None, ) -> None: """ Save the JIT object (script or trace produced object) `jit_obj` to the given file or stream with metadata @@ -98,10 +101,10 @@ def save_net_with_metadata( def load_net_with_metadata( - filename_prefix_or_stream: Union[str, IO[Any]], - map_location: Optional[torch.device] = None, + filename_prefix_or_stream: str | IO[Any], + map_location: torch.device | None = None, more_extra_files: Sequence[str] = (), -) -> Tuple[torch.nn.Module, dict, dict]: +) -> tuple[torch.nn.Module, dict, dict]: """ Load the module object from the given Torchscript filename or stream, and convert the stored JSON metadata back to a dict object. This will produce an empty dict if the metadata file is not present. diff --git a/monai/data/utils.py b/monai/data/utils.py index ce566d0c31..96e3e15d95 100644 --- a/monai/data/utils.py +++ b/monai/data/utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import hashlib import json import logging @@ -17,11 +19,12 @@ import pickle import warnings from collections import abc, defaultdict +from collections.abc import Generator, Iterable, Mapping, Sequence, Sized from copy import deepcopy from functools import reduce from itertools import product, starmap, zip_longest from pathlib import PurePath -from typing import Any, Dict, Generator, Iterable, List, Mapping, Optional, Sequence, Sized, Tuple, Union +from typing import Any import numpy as np import torch @@ -100,8 +103,8 @@ def get_random_patch( - dims: Sequence[int], patch_size: Sequence[int], rand_state: Optional[np.random.RandomState] = None -) -> Tuple[slice, ...]: + dims: Sequence[int], patch_size: Sequence[int], rand_state: np.random.RandomState | None = None +) -> tuple[slice, ...]: """ Returns a tuple of slices to define a random patch in an array of shape `dims` with size `patch_size` or the as close to it as possible within the given dimension. It is expected that `patch_size` is a valid patch for a source @@ -126,11 +129,11 @@ def get_random_patch( def iter_patch_slices( image_size: Sequence[int], - patch_size: Union[Sequence[int], int], + patch_size: Sequence[int] | int, start_pos: Sequence[int] = (), - overlap: Union[Sequence[float], float] = 0.0, + overlap: Sequence[float] | float = 0.0, padded: bool = True, -) -> Generator[Tuple[slice, ...], None, None]: +) -> Generator[tuple[slice, ...], None, None]: """ Yield successive tuples of slices defining patches of size `patch_size` from an array of dimensions `image_size`. The iteration starts from position `start_pos` in the array, or starting at the origin if this isn't provided. Each @@ -160,7 +163,7 @@ def iter_patch_slices( def dense_patch_slices( image_size: Sequence[int], patch_size: Sequence[int], scan_interval: Sequence[int] -) -> List[Tuple[slice, ...]]: +) -> list[tuple[slice, ...]]: """ Enumerate all slices defining ND patches of size `patch_size` from an `image_size` input image. @@ -200,9 +203,9 @@ def dense_patch_slices( def iter_patch_position( image_size: Sequence[int], - patch_size: Union[Sequence[int], int, np.ndarray], + patch_size: Sequence[int] | int | np.ndarray, start_pos: Sequence[int] = (), - overlap: Union[Sequence[float], float] = 0.0, + overlap: Sequence[float] | float = 0.0, padded: bool = False, ): """ @@ -243,12 +246,12 @@ def iter_patch_position( def iter_patch( arr: np.ndarray, - patch_size: Union[Sequence[int], int] = 0, + patch_size: Sequence[int] | int = 0, start_pos: Sequence[int] = (), - overlap: Union[Sequence[float], float] = 0.0, + overlap: Sequence[float] | float = 0.0, copy_back: bool = True, - mode: Optional[str] = NumpyPadMode.WRAP, - **pad_opts: Dict, + mode: str | None = NumpyPadMode.WRAP, + **pad_opts: dict, ): """ Yield successive patches from `arr` of size `patch_size`. The iteration can start from position `start_pos` in `arr` @@ -317,9 +320,7 @@ def iter_patch( arr[...] = arrpad[slices] -def get_valid_patch_size( - image_size: Sequence[int], patch_size: Union[Sequence[int], int, np.ndarray] -) -> Tuple[int, ...]: +def get_valid_patch_size(image_size: Sequence[int], patch_size: Sequence[int] | int | np.ndarray) -> tuple[int, ...]: """ Given an image of dimensions `image_size`, return a patch size tuple taking the dimension from `patch_size` if this is not 0/None. Otherwise, or if `patch_size` is shorter than `image_size`, the dimension from `image_size` is taken. This ensures @@ -495,14 +496,14 @@ def list_data_collate(batch: Sequence): raise TypeError(re_str) from re -def _non_zipping_check(batch_data: Union[Mapping, Iterable], detach: bool, pad: bool, fill_value): +def _non_zipping_check(batch_data: Mapping | Iterable, detach: bool, pad: bool, fill_value): """ Utility function based on `decollate_batch`, to identify the largest batch size from the collated data. returns batch_size, the list of non-iterable items, and the dictionary or list with their items decollated. See `decollate_batch` for more details. """ - _deco: Union[Mapping, Sequence] + _deco: Mapping | Sequence if isinstance(batch_data, Mapping): _deco = {key: decollate_batch(batch_data[key], detach, pad=pad, fill_value=fill_value) for key in batch_data} elif isinstance(batch_data, Iterable): @@ -779,7 +780,7 @@ def rectify_header_sform_qform(img_nii): return img_nii -def zoom_affine(affine: np.ndarray, scale: Union[np.ndarray, Sequence[float]], diagonal: bool = True): +def zoom_affine(affine: np.ndarray, scale: np.ndarray | Sequence[float], diagonal: bool = True): """ To make column norm of `affine` the same as `scale`. If diagonal is False, returns an affine that combines orthogonal rotation and the new scale. @@ -832,11 +833,11 @@ def zoom_affine(affine: np.ndarray, scale: Union[np.ndarray, Sequence[float]], d def compute_shape_offset( - spatial_shape: Union[np.ndarray, Sequence[int]], + spatial_shape: np.ndarray | Sequence[int], in_affine: NdarrayOrTensor, out_affine: NdarrayOrTensor, scale_extent: bool = False, -) -> Tuple[np.ndarray, np.ndarray]: +) -> tuple[np.ndarray, np.ndarray]: """ Given input and output affine, compute appropriate shapes in the output space based on the input array's shape. @@ -895,7 +896,7 @@ def compute_shape_offset( return out_shape.astype(int, copy=False), offset # type: ignore -def to_affine_nd(r: Union[np.ndarray, int], affine: NdarrayTensor, dtype=np.float64) -> NdarrayTensor: +def to_affine_nd(r: np.ndarray | int, affine: NdarrayTensor, dtype=np.float64) -> NdarrayTensor: """ Using elements from affine, to create a new affine matrix by assigning the rotation/zoom/scaling matrix and the translation vector. @@ -943,7 +944,7 @@ def to_affine_nd(r: Union[np.ndarray, int], affine: NdarrayTensor, dtype=np.floa def reorient_spatial_axes( data_shape: Sequence[int], init_affine: NdarrayOrTensor, target_affine: NdarrayOrTensor -) -> Tuple[np.ndarray, NdarrayOrTensor]: +) -> tuple[np.ndarray, NdarrayOrTensor]: """ Given the input ``init_affine``, compute the orientation transform between it and ``target_affine`` by rearranging/flipping the axes. @@ -1041,10 +1042,10 @@ def create_file_basename( def compute_importance_map( - patch_size: Tuple[int, ...], - mode: Union[BlendMode, str] = BlendMode.CONSTANT, - sigma_scale: Union[Sequence[float], float] = 0.125, - device: Union[torch.device, int, str] = "cpu", + patch_size: tuple[int, ...], + mode: BlendMode | str = BlendMode.CONSTANT, + sigma_scale: Sequence[float] | float = 0.125, + device: torch.device | int | str = "cpu", ) -> torch.Tensor: """Get importance map for different weight modes. @@ -1089,7 +1090,7 @@ def compute_importance_map( return importance_map -def is_supported_format(filename: Union[Sequence[PathLike], PathLike], suffixes: Sequence[str]) -> bool: +def is_supported_format(filename: Sequence[PathLike] | PathLike, suffixes: Sequence[str]) -> bool: """ Verify whether the specified file or files format match supported suffixes. If supported suffixes is None, skip the verification and return True. @@ -1111,8 +1112,8 @@ def is_supported_format(filename: Union[Sequence[PathLike], PathLike], suffixes: def partition_dataset( data: Sequence, - ratios: Optional[Sequence[float]] = None, - num_partitions: Optional[int] = None, + ratios: Sequence[float] | None = None, + num_partitions: int | None = None, shuffle: bool = False, seed: int = 0, drop_last: bool = False, @@ -1222,8 +1223,8 @@ def partition_dataset( def partition_dataset_classes( data: Sequence, classes: Sequence[int], - ratios: Optional[Sequence[float]] = None, - num_partitions: Optional[int] = None, + ratios: Sequence[float] | None = None, + num_partitions: int | None = None, shuffle: bool = False, seed: int = 0, drop_last: bool = False, @@ -1261,7 +1262,7 @@ def partition_dataset_classes( for i, c in enumerate(classes): class_indices[c].append(i) - class_partition_indices: List[Sequence] = [] + class_partition_indices: list[Sequence] = [] for _, per_class_indices in sorted(class_indices.items()): per_class_partition_indices = partition_dataset( data=per_class_indices, @@ -1302,7 +1303,7 @@ def resample_datalist(data: Sequence, factor: float, random_pick: bool = False, """ scale, repeats = math.modf(factor) - ret: List = list() + ret: list = list() for _ in range(int(repeats)): ret.extend(list(deepcopy(data))) @@ -1312,7 +1313,7 @@ def resample_datalist(data: Sequence, factor: float, random_pick: bool = False, return ret -def select_cross_validation_folds(partitions: Sequence[Iterable], folds: Union[Sequence[int], int]) -> List: +def select_cross_validation_folds(partitions: Sequence[Iterable], folds: Sequence[int] | int) -> list: """ Select cross validation data based on data partitions and specified fold index. if a list of fold indices is provided, concatenate the partitions of these folds. @@ -1375,12 +1376,12 @@ def sorted_dict(item, key=None, reverse=False): def convert_tables_to_dicts( dfs, - row_indices: Optional[Sequence[Union[int, str]]] = None, - col_names: Optional[Sequence[str]] = None, - col_types: Optional[Dict[str, Optional[Dict[str, Any]]]] = None, - col_groups: Optional[Dict[str, Sequence[str]]] = None, + row_indices: Sequence[int | str] | None = None, + col_names: Sequence[str] | None = None, + col_types: dict[str, dict[str, Any] | None] | None = None, + col_groups: dict[str, Sequence[str]] | None = None, **kwargs, -) -> List[Dict[str, Any]]: +) -> list[dict[str, Any]]: """ Utility to join pandas tables, select rows, columns and generate groups. Will return a list of dictionaries, every dictionary maps to a row of data in tables. @@ -1414,7 +1415,7 @@ def convert_tables_to_dicts( """ df = reduce(lambda l, r: pd.merge(l, r, **kwargs), ensure_tuple(dfs)) # parse row indices - rows: List[Union[int, str]] = [] + rows: list[int | str] = [] if row_indices is None: rows = slice(df.shape[0]) # type: ignore else: @@ -1437,11 +1438,11 @@ def convert_tables_to_dicts( types = {k: v["type"] for k, v in col_types.items() if v is not None and "type" in v} if types: data_ = data_.astype(dtype=types, copy=False) - data: List[Dict] = data_.to_dict(orient="records") + data: list[dict] = data_.to_dict(orient="records") # group columns to generate new column if col_groups is not None: - groups: Dict[str, List] = {} + groups: dict[str, list] = {} for name, cols in col_groups.items(): groups[name] = df.loc[rows, cols].values # invert items of groups to every row of data @@ -1466,7 +1467,7 @@ def orientation_ras_lps(affine: NdarrayTensor) -> NdarrayTensor: return np.diag(flip_diag).astype(affine.dtype) @ affine # type: ignore -def remove_keys(data: dict, keys: List[str]) -> None: +def remove_keys(data: dict, keys: list[str]) -> None: """ Remove keys from a dictionary. Operates in-place so nothing is returned. @@ -1495,7 +1496,7 @@ def remove_extra_metadata(meta: dict) -> None: remove_keys(data=meta, keys=keys) -def get_extra_metadata_keys() -> List[str]: +def get_extra_metadata_keys() -> list[str]: """ Get a list of unnecessary keys for metadata that can be removed. diff --git a/monai/data/video_dataset.py b/monai/data/video_dataset.py index fda262b398..be3bcf5bd5 100644 --- a/monai/data/video_dataset.py +++ b/monai/data/video_dataset.py @@ -9,10 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import sys import tempfile -from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union +from collections.abc import Callable +from typing import TYPE_CHECKING, Any import numpy as np from torch.utils.data import Dataset, IterableDataset @@ -62,9 +65,9 @@ class VideoDataset: def __init__( self, - video_source: Union[str, int], - transform: Optional[Callable] = None, - max_num_frames: Optional[int] = None, + video_source: str | int, + transform: Callable | None = None, + max_num_frames: int | None = None, color_order: str = ColorOrder.RGB, multiprocessing: bool = False, channel_dim: int = 0, @@ -107,7 +110,7 @@ def __init__( self.max_num_frames = max_num_frames @staticmethod - def open_video(video_source: Union[str, int]): + def open_video(video_source: str | int): """ Use OpenCV to open a video source from either file or capture device. @@ -162,7 +165,7 @@ def __init__(self, *args, **kwargs) -> None: self.max_num_frames = num_frames @staticmethod - def get_available_codecs() -> Dict[str, str]: + def get_available_codecs() -> dict[str, str]: """Try different codecs, see which are available. Returns a dictionary with of available codecs with codecs as keys and file extensions as values.""" if not has_cv2: diff --git a/monai/data/wsi_datasets.py b/monai/data/wsi_datasets.py index d4b70f7f0a..7689a5c5b3 100644 --- a/monai/data/wsi_datasets.py +++ b/monai/data/wsi_datasets.py @@ -9,9 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import inspect import os -from typing import Callable, Dict, Optional, Sequence, Tuple, Union +from collections.abc import Callable, Sequence import numpy as np import torch @@ -67,12 +69,12 @@ class PatchWSIDataset(Dataset): def __init__( self, data: Sequence, - patch_size: Optional[Union[int, Tuple[int, int]]] = None, - patch_level: Optional[int] = None, - transform: Optional[Callable] = None, + patch_size: int | tuple[int, int] | None = None, + patch_level: int | None = None, + transform: Callable | None = None, include_label: bool = True, center_location: bool = True, - additional_meta_keys: Optional[Sequence[str]] = None, + additional_meta_keys: Sequence[str] | None = None, reader="cuCIM", **kwargs, ): @@ -91,7 +93,7 @@ def __init__( patch_level = 0 # Setup the WSI reader - self.wsi_reader: Union[WSIReader, BaseWSIReader] + self.wsi_reader: WSIReader | BaseWSIReader if isinstance(reader, str): self.wsi_reader = WSIReader(backend=reader, level=patch_level, **kwargs) elif inspect.isclass(reader) and issubclass(reader, BaseWSIReader): @@ -107,35 +109,35 @@ def __init__( self.additional_meta_keys = additional_meta_keys or [] # Initialized an empty whole slide image object dict - self.wsi_object_dict: Dict = {} + self.wsi_object_dict: dict = {} - def _get_wsi_object(self, sample: Dict): + def _get_wsi_object(self, sample: dict): image_path = sample[CommonKeys.IMAGE] if image_path not in self.wsi_object_dict: self.wsi_object_dict[image_path] = self.wsi_reader.read(image_path) return self.wsi_object_dict[image_path] - def _get_label(self, sample: Dict): + def _get_label(self, sample: dict): return torch.tensor(sample[CommonKeys.LABEL], dtype=torch.float32) - def _get_location(self, sample: Dict): + def _get_location(self, sample: dict): if self.center_location: size = self._get_size(sample) return [sample[WSIPatchKeys.LOCATION][i] - size[i] // 2 for i in range(len(size))] else: return sample[WSIPatchKeys.LOCATION] - def _get_level(self, sample: Dict): + def _get_level(self, sample: dict): if self.patch_level is None: return sample.get(WSIPatchKeys.LEVEL, 0) return self.patch_level - def _get_size(self, sample: Dict): + def _get_size(self, sample: dict): if self.patch_size is None: return ensure_tuple_rep(sample.get(WSIPatchKeys.SIZE), 2) return self.patch_size - def _get_data(self, sample: Dict): + def _get_data(self, sample: dict): # Don't store OpenSlide objects to avoid issues with OpenSlide internal cache if self.backend == "openslide": self.wsi_object_dict = {} @@ -147,7 +149,7 @@ def _get_data(self, sample: Dict): def _transform(self, index: int): # Get a single entry of data - sample: Dict = self.data[index] + sample: dict = self.data[index] # Extract patch image and associated metadata image, metadata = self._get_data(sample) @@ -207,13 +209,13 @@ class SlidingPatchWSIDataset(Randomizable, PatchWSIDataset): def __init__( self, data: Sequence, - patch_size: Optional[Union[int, Tuple[int, int]]] = None, - patch_level: Optional[int] = None, + patch_size: int | tuple[int, int] | None = None, + patch_level: int | None = None, mask_level: int = 0, - overlap: Union[Tuple[float, float], float] = 0.0, - offset: Union[Tuple[int, int], int, str] = (0, 0), - offset_limits: Optional[Union[Tuple[Tuple[int, int], Tuple[int, int]], Tuple[int, int]]] = None, - transform: Optional[Callable] = None, + overlap: tuple[float, float] | float = 0.0, + offset: tuple[int, int] | int | str = (0, 0), + offset_limits: tuple[tuple[int, int], tuple[int, int]] | tuple[int, int] | None = None, + transform: Callable | None = None, include_label: bool = False, center_location: bool = False, additional_meta_keys: Sequence[str] = (ProbMapKeys.LOCATION, ProbMapKeys.SIZE, ProbMapKeys.COUNT), @@ -240,7 +242,7 @@ def __init__( if isinstance(offset, str): if offset == "random": self.random_offset = True - self.offset_limits: Optional[Tuple[Tuple[int, int], Tuple[int, int]]] + self.offset_limits: tuple[tuple[int, int], tuple[int, int]] | None if offset_limits is None: self.offset_limits = None elif isinstance(offset_limits, tuple): @@ -350,10 +352,10 @@ class MaskedPatchWSIDataset(PatchWSIDataset): def __init__( self, data: Sequence, - patch_size: Optional[Union[int, Tuple[int, int]]] = None, - patch_level: Optional[int] = None, + patch_size: int | tuple[int, int] | None = None, + patch_level: int | None = None, mask_level: int = 7, - transform: Optional[Callable] = None, + transform: Callable | None = None, include_label: bool = False, center_location: bool = False, additional_meta_keys: Sequence[str] = (ProbMapKeys.LOCATION, ProbMapKeys.NAME), diff --git a/monai/data/wsi_reader.py b/monai/data/wsi_reader.py index d18d40935a..616a306f58 100644 --- a/monai/data/wsi_reader.py +++ b/monai/data/wsi_reader.py @@ -9,9 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from abc import abstractmethod +from collections.abc import Sequence from os.path import abspath -from typing import Any, Dict, List, Optional, Sequence, Tuple, Union +from typing import Any import numpy as np @@ -53,7 +56,7 @@ class BaseWSIReader(ImageReader): """ - supported_suffixes: List[str] = [] + supported_suffixes: list[str] = [] backend = "" def __init__(self, level: int = 0, channel_dim: int = 0, **kwargs): @@ -61,10 +64,10 @@ def __init__(self, level: int = 0, channel_dim: int = 0, **kwargs): self.level = level self.channel_dim = channel_dim self.kwargs = kwargs - self.metadata: Dict[Any, Any] = {} + self.metadata: dict[Any, Any] = {} @abstractmethod - def get_size(self, wsi, level: Optional[int] = None) -> Tuple[int, int]: + def get_size(self, wsi, level: int | None = None) -> tuple[int, int]: """ Returns the size (height, width) of the whole slide image at a given level. @@ -87,7 +90,7 @@ def get_level_count(self, wsi) -> int: raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") @abstractmethod - def get_downsample_ratio(self, wsi, level: Optional[int] = None) -> float: + def get_downsample_ratio(self, wsi, level: int | None = None) -> float: """ Returns the down-sampling ratio of the whole slide image at a given level. @@ -105,7 +108,7 @@ def get_file_path(self, wsi) -> str: raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") @abstractmethod - def get_mpp(self, wsi, level: Optional[int] = None) -> Tuple[float, float]: + def get_mpp(self, wsi, level: int | None = None) -> tuple[float, float]: """ Returns the micro-per-pixel resolution of the whole slide image at a given level. @@ -118,7 +121,7 @@ def get_mpp(self, wsi, level: Optional[int] = None) -> Tuple[float, float]: @abstractmethod def _get_patch( - self, wsi, location: Tuple[int, int], size: Tuple[int, int], level: int, dtype: DtypeLike, mode: str + self, wsi, location: tuple[int, int], size: tuple[int, int], level: int, dtype: DtypeLike, mode: str ) -> np.ndarray: """ Extracts and returns a patch image form the whole slide image. @@ -136,8 +139,8 @@ def _get_patch( raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") def _get_metadata( - self, wsi, patch: np.ndarray, location: Tuple[int, int], size: Tuple[int, int], level: int - ) -> Dict: + self, wsi, patch: np.ndarray, location: tuple[int, int], size: tuple[int, int], level: int + ) -> dict: """ Returns metadata of the extracted patch from the whole slide image. @@ -155,7 +158,7 @@ def _get_metadata( f"The desired channel_dim ({self.channel_dim}) is out of bound for image shape: {patch.shape}" ) channel_dim: int = self.channel_dim + (len(patch.shape) if self.channel_dim < 0 else 0) - metadata: Dict = { + metadata: dict = { "backend": self.backend, "original_channel_dim": channel_dim, "spatial_shape": np.array(patch.shape[:channel_dim] + patch.shape[channel_dim + 1 :]), @@ -170,12 +173,12 @@ def _get_metadata( def get_data( self, wsi, - location: Tuple[int, int] = (0, 0), - size: Optional[Tuple[int, int]] = None, - level: Optional[int] = None, + location: tuple[int, int] = (0, 0), + size: tuple[int, int] | None = None, + level: int | None = None, dtype: DtypeLike = np.uint8, mode: str = "RGB", - ) -> Tuple[np.ndarray, Dict]: + ) -> tuple[np.ndarray, dict]: """ Verifies inputs, extracts patches from WSI image and generates metadata, and return them. @@ -192,10 +195,10 @@ def get_data( a tuples, where the first element is an image patch [CxHxW] or stack of patches, and second element is a dictionary of metadata """ - patch_list: List = [] - metadata_list: List = [] + patch_list: list = [] + metadata_list: list = [] # CuImage object is iterable, so ensure_tuple won't work on single object - if not isinstance(wsi, List): + if not isinstance(wsi, list): wsi = [wsi] for each_wsi in ensure_tuple(wsi): # Verify magnification level @@ -257,7 +260,7 @@ def get_data( metadata[key] = [m[key] for m in metadata_list] return _stack_images(patch_list, metadata), metadata - def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: + def verify_suffix(self, filename: Sequence[PathLike] | PathLike) -> bool: """ Verify whether the specified file or files format is supported by WSI reader. @@ -286,7 +289,7 @@ class WSIReader(BaseWSIReader): def __init__(self, backend="cucim", level: int = 0, channel_dim: int = 0, **kwargs): super().__init__(level, channel_dim, **kwargs) self.backend = backend.lower() - self.reader: Union[CuCIMWSIReader, OpenSlideWSIReader, TiffFileWSIReader] + self.reader: CuCIMWSIReader | OpenSlideWSIReader | TiffFileWSIReader if self.backend == "cucim": self.reader = CuCIMWSIReader(level=level, channel_dim=channel_dim, **kwargs) elif self.backend == "openslide": @@ -309,7 +312,7 @@ def get_level_count(self, wsi) -> int: """ return self.reader.get_level_count(wsi) - def get_size(self, wsi, level: Optional[int] = None) -> Tuple[int, int]: + def get_size(self, wsi, level: int | None = None) -> tuple[int, int]: """ Returns the size (height, width) of the whole slide image at a given level. @@ -324,7 +327,7 @@ def get_size(self, wsi, level: Optional[int] = None) -> Tuple[int, int]: return self.reader.get_size(wsi, level) - def get_downsample_ratio(self, wsi, level: Optional[int] = None) -> float: + def get_downsample_ratio(self, wsi, level: int | None = None) -> float: """ Returns the down-sampling ratio of the whole slide image at a given level. @@ -343,7 +346,7 @@ def get_file_path(self, wsi) -> str: """Return the file path for the WSI object""" return self.reader.get_file_path(wsi) - def get_mpp(self, wsi, level: Optional[int] = None) -> Tuple[float, float]: + def get_mpp(self, wsi, level: int | None = None) -> tuple[float, float]: """ Returns the micro-per-pixel resolution of the whole slide image at a given level. @@ -359,7 +362,7 @@ def get_mpp(self, wsi, level: Optional[int] = None) -> Tuple[float, float]: return self.reader.get_mpp(wsi, level) def _get_patch( - self, wsi, location: Tuple[int, int], size: Tuple[int, int], level: int, dtype: DtypeLike, mode: str + self, wsi, location: tuple[int, int], size: tuple[int, int], level: int, dtype: DtypeLike, mode: str ) -> np.ndarray: """ Extracts and returns a patch image form the whole slide image. @@ -376,7 +379,7 @@ def _get_patch( """ return self.reader._get_patch(wsi=wsi, location=location, size=size, level=level, dtype=dtype, mode=mode) - def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): + def read(self, data: Sequence[PathLike] | PathLike | np.ndarray, **kwargs): """ Read whole slide image objects from given file or list of files. @@ -424,7 +427,7 @@ def get_level_count(wsi) -> int: """ return wsi.resolutions["level_count"] # type: ignore - def get_size(self, wsi, level: Optional[int] = None) -> Tuple[int, int]: + def get_size(self, wsi, level: int | None = None) -> tuple[int, int]: """ Returns the size (height, width) of the whole slide image at a given level. @@ -439,7 +442,7 @@ def get_size(self, wsi, level: Optional[int] = None) -> Tuple[int, int]: return (wsi.resolutions["level_dimensions"][level][1], wsi.resolutions["level_dimensions"][level][0]) - def get_downsample_ratio(self, wsi, level: Optional[int] = None) -> float: + def get_downsample_ratio(self, wsi, level: int | None = None) -> float: """ Returns the down-sampling ratio of the whole slide image at a given level. @@ -459,7 +462,7 @@ def get_file_path(wsi) -> str: """Return the file path for the WSI object""" return str(abspath(wsi.path)) - def get_mpp(self, wsi, level: Optional[int] = None) -> Tuple[float, float]: + def get_mpp(self, wsi, level: int | None = None) -> tuple[float, float]: """ Returns the micro-per-pixel resolution of the whole slide image at a given level. @@ -475,7 +478,7 @@ def get_mpp(self, wsi, level: Optional[int] = None) -> Tuple[float, float]: factor = float(wsi.resolutions["level_downsamples"][level]) return (wsi.metadata["cucim"]["spacing"][1] * factor, wsi.metadata["cucim"]["spacing"][0] * factor) - def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): + def read(self, data: Sequence[PathLike] | PathLike | np.ndarray, **kwargs): """ Read whole slide image objects from given file or list of files. @@ -488,7 +491,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): whole slide image object or list of such objects """ - wsi_list: List = [] + wsi_list: list = [] filenames: Sequence[PathLike] = ensure_tuple(data) kwargs_ = self.kwargs.copy() @@ -500,7 +503,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): return wsi_list if len(filenames) > 1 else wsi_list[0] def _get_patch( - self, wsi, location: Tuple[int, int], size: Tuple[int, int], level: int, dtype: DtypeLike, mode: str + self, wsi, location: tuple[int, int], size: tuple[int, int], level: int, dtype: DtypeLike, mode: str ) -> np.ndarray: """ Extracts and returns a patch image form the whole slide image. @@ -566,7 +569,7 @@ def get_level_count(wsi) -> int: """ return wsi.level_count # type: ignore - def get_size(self, wsi, level: Optional[int] = None) -> Tuple[int, int]: + def get_size(self, wsi, level: int | None = None) -> tuple[int, int]: """ Returns the size (height, width) of the whole slide image at a given level. @@ -581,7 +584,7 @@ def get_size(self, wsi, level: Optional[int] = None) -> Tuple[int, int]: return (wsi.level_dimensions[level][1], wsi.level_dimensions[level][0]) - def get_downsample_ratio(self, wsi, level: Optional[int] = None) -> float: + def get_downsample_ratio(self, wsi, level: int | None = None) -> float: """ Returns the down-sampling ratio of the whole slide image at a given level. @@ -601,7 +604,7 @@ def get_file_path(wsi) -> str: """Return the file path for the WSI object""" return str(abspath(wsi._filename)) - def get_mpp(self, wsi, level: Optional[int] = None) -> Tuple[float, float]: + def get_mpp(self, wsi, level: int | None = None) -> tuple[float, float]: """ Returns the micro-per-pixel resolution of the whole slide image at a given level. @@ -628,7 +631,7 @@ def get_mpp(self, wsi, level: Optional[int] = None) -> Tuple[float, float]: factor *= wsi.level_downsamples[level] return (factor / float(wsi.properties["tiff.YResolution"]), factor / float(wsi.properties["tiff.XResolution"])) - def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): + def read(self, data: Sequence[PathLike] | PathLike | np.ndarray, **kwargs): """ Read whole slide image objects from given file or list of files. @@ -640,7 +643,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): whole slide image object or list of such objects """ - wsi_list: List = [] + wsi_list: list = [] filenames: Sequence[PathLike] = ensure_tuple(data) kwargs_ = self.kwargs.copy() @@ -652,7 +655,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): return wsi_list if len(filenames) > 1 else wsi_list[0] def _get_patch( - self, wsi, location: Tuple[int, int], size: Tuple[int, int], level: int, dtype: DtypeLike, mode: str + self, wsi, location: tuple[int, int], size: tuple[int, int], level: int, dtype: DtypeLike, mode: str ) -> np.ndarray: """ Extracts and returns a patch image form the whole slide image. @@ -710,7 +713,7 @@ def get_level_count(wsi) -> int: """ return len(wsi.pages) - def get_size(self, wsi, level: Optional[int] = None) -> Tuple[int, int]: + def get_size(self, wsi, level: int | None = None) -> tuple[int, int]: """ Returns the size (height, width) of the whole slide image at a given level. @@ -725,7 +728,7 @@ def get_size(self, wsi, level: Optional[int] = None) -> Tuple[int, int]: return (wsi.pages[level].imagelength, wsi.pages[level].imagewidth) - def get_downsample_ratio(self, wsi, level: Optional[int] = None) -> float: + def get_downsample_ratio(self, wsi, level: int | None = None) -> float: """ Returns the down-sampling ratio of the whole slide image at a given level. @@ -745,7 +748,7 @@ def get_file_path(wsi) -> str: """Return the file path for the WSI object""" return str(abspath(wsi.filehandle.path)) - def get_mpp(self, wsi, level: Optional[int] = None) -> Tuple[float, float]: + def get_mpp(self, wsi, level: int | None = None) -> tuple[float, float]: """ Returns the micro-per-pixel resolution of the whole slide image at a given level. @@ -775,7 +778,7 @@ def get_mpp(self, wsi, level: Optional[int] = None) -> Tuple[float, float]: xres = wsi.pages[level].tags["XResolution"].value return (factor * yres[1] / yres[0], factor * xres[1] / xres[0]) - def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): + def read(self, data: Sequence[PathLike] | PathLike | np.ndarray, **kwargs): """ Read whole slide image objects from given file or list of files. @@ -787,7 +790,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): whole slide image object or list of such objects """ - wsi_list: List = [] + wsi_list: list = [] filenames: Sequence[PathLike] = ensure_tuple(data) kwargs_ = self.kwargs.copy() @@ -799,7 +802,7 @@ def read(self, data: Union[Sequence[PathLike], PathLike, np.ndarray], **kwargs): return wsi_list if len(filenames) > 1 else wsi_list[0] def _get_patch( - self, wsi, location: Tuple[int, int], size: Tuple[int, int], level: int, dtype: DtypeLike, mode: str + self, wsi, location: tuple[int, int], size: tuple[int, int], level: int, dtype: DtypeLike, mode: str ) -> np.ndarray: """ Extracts and returns a patch image form the whole slide image. diff --git a/monai/engines/__init__.py b/monai/engines/__init__.py index b6e54a6c4e..9e425ccbe2 100644 --- a/monai/engines/__init__.py +++ b/monai/engines/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .evaluator import EnsembleEvaluator, Evaluator, SupervisedEvaluator from .multi_gpu_supervised_trainer import create_multigpu_supervised_evaluator, create_multigpu_supervised_trainer from .trainer import GanTrainer, SupervisedTrainer, Trainer diff --git a/monai/engines/multi_gpu_supervised_trainer.py b/monai/engines/multi_gpu_supervised_trainer.py index 71ba40dd20..9b7cf04baf 100644 --- a/monai/engines/multi_gpu_supervised_trainer.py +++ b/monai/engines/multi_gpu_supervised_trainer.py @@ -9,9 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Callable, Dict, Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING -import torch import torch.nn from torch.nn.parallel import DataParallel, DistributedDataParallel from torch.optim.optimizer import Optimizer @@ -47,7 +49,7 @@ def _default_transform(_x: torch.Tensor, _y: torch.Tensor, _y_pred: torch.Tensor def _default_eval_transform( x: torch.Tensor, y: torch.Tensor, y_pred: torch.Tensor -) -> Tuple[torch.Tensor, torch.Tensor]: +) -> tuple[torch.Tensor, torch.Tensor]: return y_pred, y @@ -55,7 +57,7 @@ def create_multigpu_supervised_trainer( net: torch.nn.Module, optimizer: Optimizer, loss_fn: Callable, - devices: Optional[Sequence[Union[str, torch.device]]] = None, + devices: Sequence[str | torch.device] | None = None, non_blocking: bool = False, prepare_batch: Callable = _prepare_batch, output_transform: Callable = _default_transform, @@ -104,8 +106,8 @@ def create_multigpu_supervised_trainer( def create_multigpu_supervised_evaluator( net: torch.nn.Module, - metrics: Optional[Dict[str, Metric]] = None, - devices: Optional[Sequence[Union[str, torch.device]]] = None, + metrics: dict[str, Metric] | None = None, + devices: Sequence[str | torch.device] | None = None, non_blocking: bool = False, prepare_batch: Callable = _prepare_batch, output_transform: Callable = _default_eval_transform, diff --git a/monai/engines/utils.py b/monai/engines/utils.py index 9d19737ab5..661d98e2dc 100644 --- a/monai/engines/utils.py +++ b/monai/engines/utils.py @@ -9,8 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence, Tuple, Union +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any import torch @@ -60,7 +63,7 @@ class IterationEvents(EventEnum): INNER_ITERATION_COMPLETED = "inner_iteration_completed" -def get_devices_spec(devices: Optional[Sequence[Union[torch.device, str]]] = None) -> List[torch.device]: +def get_devices_spec(devices: Sequence[torch.device | str] | None = None) -> list[torch.device]: """ Get a valid specification for one or more devices. If `devices` is None get devices for all CUDA devices available. If `devices` is and zero-length structure a single CPU compute device is returned. In any other cases `devices` is @@ -93,11 +96,11 @@ def get_devices_spec(devices: Optional[Sequence[Union[torch.device, str]]] = Non def default_prepare_batch( - batchdata: Union[Dict[str, torch.Tensor], torch.Tensor, Sequence[torch.Tensor]], - device: Optional[Union[str, torch.device]] = None, + batchdata: dict[str, torch.Tensor] | torch.Tensor | Sequence[torch.Tensor], + device: str | torch.device | None = None, non_blocking: bool = False, **kwargs, -) -> Union[Tuple[torch.Tensor, Optional[torch.Tensor]], torch.Tensor]: +) -> tuple[torch.Tensor, torch.Tensor | None] | torch.Tensor: """ Default function to prepare the data for current iteration. @@ -156,8 +159,8 @@ class PrepareBatch(ABC): @abstractmethod def __call__( self, - batchdata: Dict[str, torch.Tensor], - device: Optional[Union[str, torch.device]] = None, + batchdata: dict[str, torch.Tensor], + device: str | torch.device | None = None, non_blocking: bool = False, **kwargs, ): @@ -171,8 +174,8 @@ class PrepareBatchDefault(PrepareBatch): def __call__( self, - batchdata: Union[Dict[str, torch.Tensor], torch.Tensor, Sequence[torch.Tensor]], - device: Optional[Union[str, torch.device]] = None, + batchdata: dict[str, torch.Tensor] | torch.Tensor | Sequence[torch.Tensor], + device: str | torch.device | None = None, non_blocking: bool = False, **kwargs, ): @@ -198,13 +201,13 @@ class PrepareBatchExtraInput(PrepareBatch): dictionary keyed to `v`. """ - def __init__(self, extra_keys: Union[str, Sequence[str], Dict[str, str]]) -> None: + def __init__(self, extra_keys: str | Sequence[str] | dict[str, str]) -> None: self.extra_keys = extra_keys def __call__( self, - batchdata: Dict[str, torch.Tensor], - device: Optional[Union[str, torch.device]] = None, + batchdata: dict[str, torch.Tensor], + device: str | torch.device | None = None, non_blocking: bool = False, **kwargs, ): @@ -236,16 +239,12 @@ def _get_data(key: str): def default_make_latent( - num_latents: int, - latent_size: int, - device: Optional[Union[str, torch.device]] = None, - non_blocking: bool = False, - **kwargs, + num_latents: int, latent_size: int, device: str | torch.device | None = None, non_blocking: bool = False, **kwargs ) -> torch.Tensor: return torch.randn(num_latents, latent_size).to(device=device, non_blocking=non_blocking, **kwargs) -def engine_apply_transform(batch: Any, output: Any, transform: Callable[..., Dict]): +def engine_apply_transform(batch: Any, output: Any, transform: Callable[..., dict]): """ Apply transform on `batch` and `output`. If `batch` and `output` are dictionaries, temporarily combine them for the transform, diff --git a/monai/engines/workflow.py b/monai/engines/workflow.py index 8123d14fac..96a24aac60 100644 --- a/monai/engines/workflow.py +++ b/monai/engines/workflow.py @@ -9,8 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, Sequence, Type, Union +from collections.abc import Callable, Iterable, Sequence +from typing import TYPE_CHECKING, Any import torch import torch.distributed as dist @@ -100,24 +103,24 @@ class Workflow(Engine): def __init__( self, - device: Union[torch.device, str], + device: torch.device | str, max_epochs: int, - data_loader: Union[Iterable, DataLoader], - epoch_length: Optional[int] = None, + data_loader: Iterable | DataLoader, + epoch_length: int | None = None, non_blocking: bool = False, prepare_batch: Callable = default_prepare_batch, - iteration_update: Optional[Callable[[Engine, Any], Any]] = None, - postprocessing: Optional[Callable] = None, - key_metric: Optional[Dict[str, Metric]] = None, - additional_metrics: Optional[Dict[str, Metric]] = None, + iteration_update: Callable[[Engine, Any], Any] | None = None, + postprocessing: Callable | None = None, + key_metric: dict[str, Metric] | None = None, + additional_metrics: dict[str, Metric] | None = None, metric_cmp_fn: Callable = default_metric_cmp_fn, - handlers: Optional[Sequence] = None, + handlers: Sequence | None = None, amp: bool = False, - event_names: Optional[List[Union[str, EventEnum, Type[EventEnum]]]] = None, - event_to_attr: Optional[dict] = None, + event_names: list[str | EventEnum | type[EventEnum]] | None = None, + event_to_attr: dict | None = None, decollate: bool = True, - to_kwargs: Optional[Dict] = None, - amp_kwargs: Optional[Dict] = None, + to_kwargs: dict | None = None, + amp_kwargs: dict | None = None, ) -> None: if iteration_update is not None: super().__init__(iteration_update) @@ -163,7 +166,7 @@ def set_sampler_epoch(engine: Engine): self.amp = amp self.to_kwargs = {} if to_kwargs is None else to_kwargs self.amp_kwargs = {} if amp_kwargs is None else amp_kwargs - self.scaler: Optional[torch.cuda.amp.GradScaler] = None + self.scaler: torch.cuda.amp.GradScaler | None = None if event_names is None: event_names = [IterationEvents] @@ -222,7 +225,7 @@ def _run_postprocessing(engine: Engine) -> None: for i, (b, o) in enumerate(zip(engine.state.batch, engine.state.output)): engine.state.batch[i], engine.state.output[i] = engine_apply_transform(b, o, posttrans) - def _register_metrics(self, k_metric: Dict, add_metrics: Optional[Dict] = None): + def _register_metrics(self, k_metric: dict, add_metrics: dict | None = None): """ Register the key metric and additional metrics to the engine, supports ignite Metrics. @@ -279,7 +282,7 @@ def run(self) -> None: # type: ignore[override] return super().run(data=self.data_loader, max_epochs=self.state.max_epochs) - def _iteration(self, engine, batchdata: Dict[str, torch.Tensor]): + def _iteration(self, engine, batchdata: dict[str, torch.Tensor]): """ Abstract callback function for the processing logic of 1 iteration in Ignite Engine. Need subclass to implement different logics, like SupervisedTrainer/Evaluator, GANTrainer, etc. diff --git a/monai/fl/client/__init__.py b/monai/fl/client/__init__.py index 7acb82c635..e9f1ab8601 100644 --- a/monai/fl/client/__init__.py +++ b/monai/fl/client/__init__.py @@ -9,5 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .client_algo import BaseClient, ClientAlgo, ClientAlgoStats from .monai_algo import MonaiAlgo, MonaiAlgoStats diff --git a/monai/fl/client/client_algo.py b/monai/fl/client/client_algo.py index 9c54f2891b..daac8336f4 100644 --- a/monai/fl/client/client_algo.py +++ b/monai/fl/client/client_algo.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional +from __future__ import annotations from monai.fl.utils.exchange_object import ExchangeObject @@ -27,7 +27,7 @@ class BaseClient: to help with lifecycle management of the class object. """ - def initialize(self, extra: Optional[dict] = None): + def initialize(self, extra: dict | None = None): """ Call to initialize the ClientAlgo class. @@ -36,7 +36,7 @@ def initialize(self, extra: Optional[dict] = None): """ pass - def finalize(self, extra: Optional[dict] = None): + def finalize(self, extra: dict | None = None): """ Call to finalize the ClientAlgo class. @@ -45,7 +45,7 @@ def finalize(self, extra: Optional[dict] = None): """ pass - def abort(self, extra: Optional[dict] = None): + def abort(self, extra: dict | None = None): """ Call to abort the ClientAlgo training or evaluation. @@ -57,7 +57,7 @@ def abort(self, extra: Optional[dict] = None): class ClientAlgoStats(BaseClient): - def get_data_stats(self, extra: Optional[dict] = None) -> ExchangeObject: + def get_data_stats(self, extra: dict | None = None) -> ExchangeObject: """ Get summary statistics about the local data. @@ -102,7 +102,7 @@ class ClientAlgo(ClientAlgoStats): to help with lifecycle management of the class object. """ - def train(self, data: ExchangeObject, extra: Optional[dict] = None) -> None: + def train(self, data: ExchangeObject, extra: dict | None = None) -> None: """ Train network and produce new network from train data. @@ -115,7 +115,7 @@ def train(self, data: ExchangeObject, extra: Optional[dict] = None) -> None: """ raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") - def get_weights(self, extra: Optional[dict] = None) -> ExchangeObject: + def get_weights(self, extra: dict | None = None) -> ExchangeObject: """ Get current local weights or weight differences. @@ -138,7 +138,7 @@ def get_weights(self, extra: Optional[dict] = None) -> ExchangeObject: """ raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") - def evaluate(self, data: ExchangeObject, extra: Optional[dict] = None) -> ExchangeObject: + def evaluate(self, data: ExchangeObject, extra: dict | None = None) -> ExchangeObject: """ Get evaluation metrics on test data. diff --git a/monai/fl/client/monai_algo.py b/monai/fl/client/monai_algo.py index a424b3afc3..8e6d8410e4 100644 --- a/monai/fl/client/monai_algo.py +++ b/monai/fl/client/monai_algo.py @@ -9,10 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import os import sys -from typing import Any, Dict, Mapping, MutableMapping, Optional, Union, cast +from collections.abc import Mapping, MutableMapping +from typing import Any, cast import torch import torch.distributed as dist @@ -106,11 +109,11 @@ class MonaiAlgoStats(ClientAlgoStats): def __init__( self, bundle_root: str, - config_train_filename: Optional[Union[str, list]] = "configs/train.json", - config_filters_filename: Optional[Union[str, list]] = None, - train_data_key: Optional[str] = BundleKeys.TRAIN_DATA, - eval_data_key: Optional[str] = BundleKeys.VALID_DATA, - data_stats_transform_list: Optional[list] = None, + config_train_filename: str | list | None = "configs/train.json", + config_filters_filename: str | list | None = None, + train_data_key: str | None = BundleKeys.TRAIN_DATA, + eval_data_key: str | None = BundleKeys.VALID_DATA, + data_stats_transform_list: list | None = None, histogram_only: bool = False, ): self.logger = logging.getLogger(self.__class__.__name__) @@ -122,10 +125,10 @@ def __init__( self.data_stats_transform_list = data_stats_transform_list self.histogram_only = histogram_only - self.client_name: Optional[str] = None + self.client_name: str | None = None self.app_root: str = "" - self.train_parser: Optional[ConfigParser] = None - self.filter_parser: Optional[ConfigParser] = None + self.train_parser: ConfigParser | None = None + self.filter_parser: ConfigParser | None = None self.post_statistics_filters: Any = None self.phase = FlPhase.IDLE self.dataset_root: Any = None @@ -177,7 +180,7 @@ def initialize(self, extra=None): self.logger.info(f"Initialized {self.client_name}.") - def get_data_stats(self, extra: Optional[dict] = None) -> ExchangeObject: + def get_data_stats(self, extra: dict | None = None) -> ExchangeObject: """ Returns summary statistics about the local data. @@ -385,22 +388,22 @@ def __init__( bundle_root: str, local_epochs: int = 1, send_weight_diff: bool = True, - config_train_filename: Optional[Union[str, list]] = "configs/train.json", - config_evaluate_filename: Optional[Union[str, list]] = "default", - config_filters_filename: Optional[Union[str, list]] = None, + config_train_filename: str | list | None = "configs/train.json", + config_evaluate_filename: str | list | None = "default", + config_filters_filename: str | list | None = None, disable_ckpt_loading: bool = True, - best_model_filepath: Optional[str] = "models/model.pt", - final_model_filepath: Optional[str] = "models/model_final.pt", - save_dict_key: Optional[str] = "model", - seed: Optional[int] = None, + best_model_filepath: str | None = "models/model.pt", + final_model_filepath: str | None = "models/model_final.pt", + save_dict_key: str | None = "model", + seed: int | None = None, benchmark: bool = True, multi_gpu: bool = False, backend: str = "nccl", init_method: str = "env://", - train_data_key: Optional[str] = BundleKeys.TRAIN_DATA, - eval_data_key: Optional[str] = BundleKeys.VALID_DATA, - data_stats_transform_list: Optional[list] = None, - tracking: Optional[Union[str, dict]] = None, + train_data_key: str | None = BundleKeys.TRAIN_DATA, + eval_data_key: str | None = BundleKeys.VALID_DATA, + data_stats_transform_list: list | None = None, + tracking: str | dict | None = None, ): self.logger = logging.getLogger(self.__class__.__name__) if config_evaluate_filename == "default": @@ -426,16 +429,16 @@ def __init__( self.tracking = tracking self.app_root = "" - self.train_parser: Optional[ConfigParser] = None - self.eval_parser: Optional[ConfigParser] = None - self.filter_parser: Optional[ConfigParser] = None - self.trainer: Optional[SupervisedTrainer] = None - self.evaluator: Optional[Any] = None + self.train_parser: ConfigParser | None = None + self.eval_parser: ConfigParser | None = None + self.filter_parser: ConfigParser | None = None + self.trainer: SupervisedTrainer | None = None + self.evaluator: Any | None = None self.pre_filters = None self.post_weight_filters = None self.post_evaluate_filters = None self.iter_of_start_time = 0 - self.global_weights: Optional[Mapping] = None + self.global_weights: Mapping | None = None self.rank = 0 self.phase = FlPhase.IDLE @@ -621,8 +624,8 @@ def get_weights(self, extra=None): # if weights contain several state dicts, use the one defined by `save_dict_key` if isinstance(weights, dict) and self.save_dict_key in weights: weights = weights.get(self.save_dict_key) - weigh_type: Optional[WeightType] = WeightType.WEIGHTS - stats: Dict = {} + weigh_type: WeightType | None = WeightType.WEIGHTS + stats: dict = {} self.logger.info(f"Returning {model_type} checkpoint weights from {model_path}.") else: raise ValueError( diff --git a/monai/fl/utils/constants.py b/monai/fl/utils/constants.py index cd24e6093d..fbd18b364c 100644 --- a/monai/fl/utils/constants.py +++ b/monai/fl/utils/constants.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from monai.utils.enums import StrEnum diff --git a/monai/fl/utils/exchange_object.py b/monai/fl/utils/exchange_object.py index 3fba895101..8325e0d291 100644 --- a/monai/fl/utils/exchange_object.py +++ b/monai/fl/utils/exchange_object.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, Optional +from __future__ import annotations from monai.fl.utils.constants import WeightType @@ -30,9 +30,9 @@ def __init__( self, weights=None, optim=None, - metrics: Optional[Dict] = None, - weight_type: Optional[WeightType] = None, - statistics: Optional[Dict] = None, + metrics: dict | None = None, + weight_type: WeightType | None = None, + statistics: dict | None = None, ): super().__init__() self.weights = weights @@ -40,7 +40,7 @@ def __init__( self.metrics = metrics self.weight_type = weight_type self.statistics = statistics - self._summary: Dict = {} + self._summary: dict = {} @property def metrics(self): diff --git a/monai/fl/utils/filters.py b/monai/fl/utils/filters.py index b205ffe668..dd0273e732 100644 --- a/monai/fl/utils/filters.py +++ b/monai/fl/utils/filters.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import abc from monai.fl.utils.exchange_object import ExchangeObject diff --git a/monai/handlers/__init__.py b/monai/handlers/__init__.py index 9880e39817..2e996112f8 100644 --- a/monai/handlers/__init__.py +++ b/monai/handlers/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .checkpoint_loader import CheckpointLoader from .checkpoint_saver import CheckpointSaver from .classification_saver import ClassificationSaver diff --git a/monai/handlers/checkpoint_loader.py b/monai/handlers/checkpoint_loader.py index 91cfca354a..5b05e7055c 100644 --- a/monai/handlers/checkpoint_loader.py +++ b/monai/handlers/checkpoint_loader.py @@ -9,9 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import warnings -from typing import TYPE_CHECKING, Dict, List, Optional +from typing import TYPE_CHECKING import torch @@ -69,9 +71,9 @@ class CheckpointLoader: def __init__( self, load_path: str, - load_dict: Dict, - name: Optional[str] = None, - map_location: Optional[Dict] = None, + load_dict: dict, + name: str | None = None, + map_location: dict | None = None, strict: bool = True, strict_shape: bool = True, ) -> None: @@ -112,7 +114,7 @@ def __call__(self, engine: Engine) -> None: checkpoint = {k: checkpoint} if not self.strict_shape: - pop_items: List[str] = [] + pop_items: list[str] = [] for k, obj in self.load_dict.items(): if isinstance(obj, torch.nn.Module): # skip items that don't match key name or data shape diff --git a/monai/handlers/checkpoint_saver.py b/monai/handlers/checkpoint_saver.py index 76f6458f3d..656f417709 100644 --- a/monai/handlers/checkpoint_saver.py +++ b/monai/handlers/checkpoint_saver.py @@ -9,9 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import warnings -from typing import TYPE_CHECKING, Dict, Mapping, Optional +from collections.abc import Mapping +from typing import TYPE_CHECKING from monai.config import IgniteInfo from monai.utils import is_scalar, min_version, optional_import @@ -86,21 +89,21 @@ class CheckpointSaver: def __init__( self, save_dir: str, - save_dict: Dict, - name: Optional[str] = None, + save_dict: dict, + name: str | None = None, file_prefix: str = "", save_final: bool = False, - final_filename: Optional[str] = None, + final_filename: str | None = None, save_key_metric: bool = False, - key_metric_name: Optional[str] = None, + key_metric_name: str | None = None, key_metric_n_saved: int = 1, - key_metric_filename: Optional[str] = None, + key_metric_filename: str | None = None, key_metric_save_state: bool = False, key_metric_greater_or_equal: bool = False, key_metric_negative_sign: bool = False, epoch_level: bool = True, save_interval: int = 0, - n_saved: Optional[int] = None, + n_saved: int | None = None, ) -> None: if save_dir is None: raise AssertionError("must provide directory to save the checkpoints.") @@ -111,9 +114,9 @@ def __init__( self.logger = logging.getLogger(name) self.epoch_level = epoch_level self.save_interval = save_interval - self._final_checkpoint: Optional[Checkpoint] = None - self._key_metric_checkpoint: Optional[Checkpoint] = None - self._interval_checkpoint: Optional[Checkpoint] = None + self._final_checkpoint: Checkpoint | None = None + self._key_metric_checkpoint: Checkpoint | None = None + self._interval_checkpoint: Checkpoint | None = None self._name = name class _DiskSaver(DiskSaver): @@ -122,13 +125,13 @@ class _DiskSaver(DiskSaver): """ - def __init__(self, dirname: str, filename: Optional[str] = None): + def __init__(self, dirname: str, filename: str | None = None): # set `atomic=False` as `atomic=True` only gives read/write permission to the user who saved the file, # without group/others read permission super().__init__(dirname=dirname, require_empty=False, atomic=False) self.filename = filename - def __call__(self, checkpoint: Mapping, filename: str, metadata: Optional[Mapping] = None) -> None: + def __call__(self, checkpoint: Mapping, filename: str, metadata: Mapping | None = None) -> None: if self.filename is not None: filename = self.filename super().__call__(checkpoint=checkpoint, filename=filename, metadata=metadata) @@ -200,7 +203,7 @@ def _interval_func(engine: Engine): n_saved=n_saved, ) - def load_state_dict(self, state_dict: Dict) -> None: + def load_state_dict(self, state_dict: dict) -> None: """ Utility to resume the internal state of key metric tracking list if configured to save checkpoints based on the key metric value. diff --git a/monai/handlers/classification_saver.py b/monai/handlers/classification_saver.py index 75fb394177..831808f4fb 100644 --- a/monai/handlers/classification_saver.py +++ b/monai/handlers/classification_saver.py @@ -9,9 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import warnings -from typing import TYPE_CHECKING, Callable, List, Optional +from collections.abc import Callable +from typing import TYPE_CHECKING import torch @@ -43,9 +46,9 @@ def __init__( overwrite: bool = True, batch_transform: Callable = lambda x: x, output_transform: Callable = lambda x: x, - name: Optional[str] = None, + name: str | None = None, save_rank: int = 0, - saver: Optional[CSVSaver] = None, + saver: CSVSaver | None = None, ) -> None: """ Args: @@ -85,8 +88,8 @@ def __init__( self.logger = logging.getLogger(name) self._name = name - self._outputs: List[torch.Tensor] = [] - self._filenames: List[str] = [] + self._outputs: list[torch.Tensor] = [] + self._filenames: list[str] = [] def attach(self, engine: Engine) -> None: """ diff --git a/monai/handlers/confusion_matrix.py b/monai/handlers/confusion_matrix.py index e3fc4bfbf1..0684c453a1 100644 --- a/monai/handlers/confusion_matrix.py +++ b/monai/handlers/confusion_matrix.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Union +from __future__ import annotations + +from collections.abc import Callable from monai.handlers.ignite_metric import IgniteMetric from monai.metrics import ConfusionMatrixMetric @@ -26,7 +28,7 @@ def __init__( include_background: bool = True, metric_name: str = "hit_rate", compute_sample: bool = False, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, output_transform: Callable = lambda x: x, save_details: bool = True, ) -> None: diff --git a/monai/handlers/decollate_batch.py b/monai/handlers/decollate_batch.py index a0d0ef3ad2..ac3aa94145 100644 --- a/monai/handlers/decollate_batch.py +++ b/monai/handlers/decollate_batch.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Optional +from __future__ import annotations + +from typing import TYPE_CHECKING from monai.config import IgniteInfo, KeysCollection from monai.engines.utils import IterationEvents @@ -51,9 +53,9 @@ def __init__( event: str = "MODEL_COMPLETED", detach: bool = True, decollate_batch: bool = True, - batch_keys: Optional[KeysCollection] = None, + batch_keys: KeysCollection | None = None, decollate_output: bool = True, - output_keys: Optional[KeysCollection] = None, + output_keys: KeysCollection | None = None, allow_missing_keys: bool = False, ): event = event.upper() diff --git a/monai/handlers/earlystop_handler.py b/monai/handlers/earlystop_handler.py index 4f61fa3e00..43a5b720b8 100644 --- a/monai/handlers/earlystop_handler.py +++ b/monai/handlers/earlystop_handler.py @@ -9,7 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Callable, Optional +from __future__ import annotations + +from collections.abc import Callable +from typing import TYPE_CHECKING from monai.config import IgniteInfo from monai.utils import min_version, optional_import @@ -55,7 +58,7 @@ def __init__( self, patience: int, score_function: Callable, - trainer: Optional[Engine] = None, + trainer: Engine | None = None, min_delta: float = 0.0, cumulative_delta: bool = False, epoch_level: bool = True, diff --git a/monai/handlers/garbage_collector.py b/monai/handlers/garbage_collector.py index 74ccac8a72..858c78095a 100644 --- a/monai/handlers/garbage_collector.py +++ b/monai/handlers/garbage_collector.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import gc from typing import TYPE_CHECKING diff --git a/monai/handlers/hausdorff_distance.py b/monai/handlers/hausdorff_distance.py index 739c9e9935..ef4136906c 100644 --- a/monai/handlers/hausdorff_distance.py +++ b/monai/handlers/hausdorff_distance.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Optional, Union +from __future__ import annotations + +from collections.abc import Callable from monai.handlers.ignite_metric import IgniteMetric from monai.metrics import HausdorffDistanceMetric @@ -25,9 +27,9 @@ def __init__( self, include_background: bool = False, distance_metric: str = "euclidean", - percentile: Optional[float] = None, + percentile: float | None = None, directed: bool = False, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, output_transform: Callable = lambda x: x, save_details: bool = True, ) -> None: diff --git a/monai/handlers/ignite_metric.py b/monai/handlers/ignite_metric.py index 67243412b9..822da0aa18 100644 --- a/monai/handlers/ignite_metric.py +++ b/monai/handlers/ignite_metric.py @@ -9,8 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import TYPE_CHECKING, Any, Callable, List, Optional, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any import torch @@ -58,9 +61,9 @@ def __init__( self._is_reduced: bool = False self.metric_fn = metric_fn self.save_details = save_details - self._scores: List = [] - self._engine: Optional[Engine] = None - self._name: Optional[str] = None + self._scores: list = [] + self._engine: Engine | None = None + self._name: str | None = None super().__init__(output_transform) @reinit__is_reduced diff --git a/monai/handlers/logfile_handler.py b/monai/handlers/logfile_handler.py index 73c58431a9..df6ebd34a7 100644 --- a/monai/handlers/logfile_handler.py +++ b/monai/handlers/logfile_handler.py @@ -9,9 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import os -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from monai.config import IgniteInfo from monai.utils import min_version, optional_import @@ -58,8 +60,8 @@ def __init__( self.loglevel: int = loglevel self.formatter: str = formatter self.create_dir: bool = create_dir - self.logger: Optional[logging.Logger] = None - self.handler: Optional[logging.FileHandler] = None + self.logger: logging.Logger | None = None + self.handler: logging.FileHandler | None = None def attach(self, engine: Engine) -> None: self.logger = engine.logger diff --git a/monai/handlers/lr_schedule_handler.py b/monai/handlers/lr_schedule_handler.py index 66059bba95..a79722517d 100644 --- a/monai/handlers/lr_schedule_handler.py +++ b/monai/handlers/lr_schedule_handler.py @@ -9,8 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging -from typing import TYPE_CHECKING, Any, Callable, Optional, Union +from collections.abc import Callable +from typing import TYPE_CHECKING, Any from torch.optim.lr_scheduler import ReduceLROnPlateau, _LRScheduler @@ -33,9 +36,9 @@ class LrScheduleHandler: def __init__( self, - lr_scheduler: Union[_LRScheduler, ReduceLROnPlateau], + lr_scheduler: _LRScheduler | ReduceLROnPlateau, print_lr: bool = True, - name: Optional[str] = None, + name: str | None = None, epoch_level: bool = True, step_transform: Callable[[Engine], Any] = lambda engine: (), ) -> None: diff --git a/monai/handlers/mean_dice.py b/monai/handlers/mean_dice.py index c5609c6746..aa3b5b0763 100644 --- a/monai/handlers/mean_dice.py +++ b/monai/handlers/mean_dice.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Union +from __future__ import annotations + +from collections.abc import Callable from monai.handlers.ignite_metric import IgniteMetric from monai.metrics import DiceMetric @@ -24,7 +26,7 @@ class MeanDice(IgniteMetric): def __init__( self, include_background: bool = True, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, output_transform: Callable = lambda x: x, save_details: bool = True, ) -> None: diff --git a/monai/handlers/mean_iou.py b/monai/handlers/mean_iou.py index ee4602e6a7..2fc0d5f8ab 100644 --- a/monai/handlers/mean_iou.py +++ b/monai/handlers/mean_iou.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Union +from __future__ import annotations + +from collections.abc import Callable from monai.handlers.ignite_metric import IgniteMetric from monai.metrics import MeanIoU @@ -24,7 +26,7 @@ class MeanIoUHandler(IgniteMetric): def __init__( self, include_background: bool = True, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, output_transform: Callable = lambda x: x, save_details: bool = True, ) -> None: diff --git a/monai/handlers/metric_logger.py b/monai/handlers/metric_logger.py index 334f631b88..fac046946e 100644 --- a/monai/handlers/metric_logger.py +++ b/monai/handlers/metric_logger.py @@ -9,10 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from collections import defaultdict +from collections.abc import Callable from enum import Enum from threading import RLock -from typing import TYPE_CHECKING, Callable, DefaultDict, List, Optional +from typing import TYPE_CHECKING from monai.config import IgniteInfo from monai.utils import min_version, optional_import @@ -70,12 +73,12 @@ def __init__( self, loss_transform: Callable = _get_loss_from_output, metric_transform: Callable = lambda x: x, - evaluator: Optional[Engine] = None, + evaluator: Engine | None = None, ) -> None: self.loss_transform = loss_transform self.metric_transform = metric_transform - self.loss: List = [] - self.metrics: DefaultDict = defaultdict(list) + self.loss: list = [] + self.metrics: defaultdict = defaultdict(list) self.iteration = 0 self.lock = RLock() diff --git a/monai/handlers/metrics_saver.py b/monai/handlers/metrics_saver.py index 0fc62c68ea..88a0926b91 100644 --- a/monai/handlers/metrics_saver.py +++ b/monai/handlers/metrics_saver.py @@ -9,7 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Callable, List, Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING from monai.config import IgniteInfo from monai.data import decollate_batch @@ -79,10 +82,10 @@ class mean median max 5percentile 95percentile notnans def __init__( self, save_dir: str, - metrics: Optional[Union[str, Sequence[str]]] = "*", - metric_details: Optional[Union[str, Sequence[str]]] = None, + metrics: str | Sequence[str] | None = "*", + metric_details: str | Sequence[str] | None = None, batch_transform: Callable = lambda x: x, - summary_ops: Optional[Union[str, Sequence[str]]] = None, + summary_ops: str | Sequence[str] | None = None, save_rank: int = 0, delimiter: str = ",", output_type: str = "csv", @@ -95,7 +98,7 @@ def __init__( self.save_rank = save_rank self.deli = delimiter self.output_type = output_type - self._filenames: List[str] = [] + self._filenames: list[str] = [] def attach(self, engine: Engine) -> None: """ diff --git a/monai/handlers/mlflow_handler.py b/monai/handlers/mlflow_handler.py index 340138a372..d8871d2d87 100644 --- a/monai/handlers/mlflow_handler.py +++ b/monai/handlers/mlflow_handler.py @@ -9,10 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import time +from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Sequence, Union +from typing import TYPE_CHECKING, Any import torch @@ -94,20 +97,20 @@ class MLFlowHandler: def __init__( self, - tracking_uri: Optional[str] = None, + tracking_uri: str | None = None, iteration_log: bool = True, epoch_log: bool = True, - epoch_logger: Optional[Callable[[Engine], Any]] = None, - iteration_logger: Optional[Callable[[Engine], Any]] = None, + epoch_logger: Callable[[Engine], Any] | None = None, + iteration_logger: Callable[[Engine], Any] | None = None, output_transform: Callable = lambda x: x[0], global_epoch_transform: Callable = lambda x: x, - state_attributes: Optional[Sequence[str]] = None, + state_attributes: Sequence[str] | None = None, tag_name: str = DEFAULT_TAG, experiment_name: str = "monai_experiment", - run_name: Optional[str] = None, - experiment_param: Optional[Dict] = None, - artifacts: Optional[Union[str, Sequence[Path]]] = None, - optimizer_param_names: Union[str, Sequence[str]] = "lr", + run_name: str | None = None, + experiment_param: dict | None = None, + artifacts: str | Sequence[Path] | None = None, + optimizer_param_names: str | Sequence[str] = "lr", close_on_complete: bool = False, ) -> None: self.iteration_log = iteration_log @@ -128,7 +131,7 @@ def __init__( self.experiment = None self.cur_run = None - def _delete_exist_param_in_dict(self, param_dict: Dict) -> None: + def _delete_exist_param_in_dict(self, param_dict: dict) -> None: """ Delete parameters in given dict, if they are already logged by current mlflow run. @@ -201,13 +204,13 @@ def _set_experiment(self): raise ValueError(f"Cannot set a deleted experiment '{self.experiment_name}' as the active experiment") self.experiment = experiment - def _log_params(self, params: Dict[str, Any]) -> None: + def _log_params(self, params: dict[str, Any]) -> None: if not self.cur_run: raise ValueError("Current Run is not Active to log params") params_arr = [mlflow.entities.Param(key, str(value)) for key, value in params.items()] self.client.log_batch(run_id=self.cur_run.info.run_id, metrics=[], params=params_arr, tags=[]) - def _log_metrics(self, metrics: Dict[str, Any], step: Optional[int] = None) -> None: + def _log_metrics(self, metrics: dict[str, Any], step: int | None = None) -> None: if not self.cur_run: raise ValueError("Current Run is not Active to log metrics") diff --git a/monai/handlers/nvtx_handlers.py b/monai/handlers/nvtx_handlers.py index 19f1b2e2bb..38eef6f05b 100644 --- a/monai/handlers/nvtx_handlers.py +++ b/monai/handlers/nvtx_handlers.py @@ -12,7 +12,9 @@ Wrapper around NVIDIA Tools Extension for profiling MONAI ignite workflow """ -from typing import TYPE_CHECKING, Optional, Tuple, Union +from __future__ import annotations + +from typing import TYPE_CHECKING from monai.config import IgniteInfo from monai.utils import ensure_tuple, min_version, optional_import @@ -52,9 +54,7 @@ class RangeHandler: If not provided, the name of first event will be assigned to the NVTX range. """ - def __init__( - self, events: Union[str, Tuple[Union[str, Events], Union[str, Events]]], msg: Optional[str] = None - ) -> None: + def __init__(self, events: str | tuple[str | Events, str | Events], msg: str | None = None) -> None: self.events = self.resolve_events(events) if msg is None: if isinstance(events, str): @@ -66,7 +66,7 @@ def __init__( self.msg = msg self.depth = None - def resolve_events(self, events: Union[str, Tuple]) -> Tuple[Events, Events]: + def resolve_events(self, events: str | tuple) -> tuple[Events, Events]: """ Resolve the input events to create a pair of Ignite events """ @@ -77,7 +77,7 @@ def resolve_events(self, events: Union[str, Tuple]) -> Tuple[Events, Events]: return self.get_event(events[0]), self.get_event(events[1]) raise ValueError(f"Exactly two Ignite events should be provided [received {len(events)}].") - def create_paired_events(self, event: str) -> Tuple[Events, Events]: + def create_paired_events(self, event: str) -> tuple[Events, Events]: """ Create pair of Ignite events from a event prefix name """ @@ -85,7 +85,7 @@ def create_paired_events(self, event: str) -> Tuple[Events, Events]: event_prefix = {"": "", "ENGINE": "", "EPOCH": "EPOCH_", "ITERATION": "ITERATION_", "BATCH": "GET_BATCH_"} return self.get_event(event_prefix[event] + "STARTED"), self.get_event(event_prefix[event] + "COMPLETED") - def get_event(self, event: Union[str, Events]) -> Events: + def get_event(self, event: str | Events) -> Events: return Events[event.upper()] if isinstance(event, str) else event def attach(self, engine: Engine) -> None: @@ -113,7 +113,7 @@ class RangePushHandler: msg: ASCII message to associate with range """ - def __init__(self, event: Union[str, Events], msg: Optional[str] = None) -> None: + def __init__(self, event: str | Events, msg: str | None = None) -> None: self.event = Events[event.upper()] if isinstance(event, str) else event if msg is None: msg = self.event.name @@ -141,7 +141,7 @@ class RangePopHandler: msg: ASCII message to associate with range """ - def __init__(self, event: Union[str, Events]) -> None: + def __init__(self, event: str | Events) -> None: self.event = Events[event.upper()] if isinstance(event, str) else event def attach(self, engine: Engine) -> None: @@ -164,7 +164,7 @@ class MarkHandler: msg: ASCII message to associate with range """ - def __init__(self, event: Union[str, Events], msg: Optional[str] = None) -> None: + def __init__(self, event: str | Events, msg: str | None = None) -> None: self.event = Events[event.upper()] if isinstance(event, str) else event if msg is None: msg = self.event.name diff --git a/monai/handlers/panoptic_quality.py b/monai/handlers/panoptic_quality.py index ffa0aee03a..4bf561826c 100644 --- a/monai/handlers/panoptic_quality.py +++ b/monai/handlers/panoptic_quality.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Union +from __future__ import annotations + +from collections.abc import Callable from monai.handlers.ignite_metric import IgniteMetric from monai.metrics import PanopticQualityMetric @@ -25,7 +27,7 @@ def __init__( self, num_classes: int, metric_name: str = "pq", - reduction: Union[MetricReduction, str] = MetricReduction.MEAN_BATCH, + reduction: MetricReduction | str = MetricReduction.MEAN_BATCH, match_iou_threshold: float = 0.5, smooth_numerator: float = 1e-6, output_transform: Callable = lambda x: x, diff --git a/monai/handlers/parameter_scheduler.py b/monai/handlers/parameter_scheduler.py index 233abca2e0..670c079d77 100644 --- a/monai/handlers/parameter_scheduler.py +++ b/monai/handlers/parameter_scheduler.py @@ -9,9 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging from bisect import bisect_right -from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Union +from collections.abc import Callable +from typing import TYPE_CHECKING from monai.config import IgniteInfo from monai.utils import min_version, optional_import @@ -41,10 +44,10 @@ class ParamSchedulerHandler: def __init__( self, parameter_setter: Callable, - value_calculator: Union[str, Callable], - vc_kwargs: Dict, + value_calculator: str | Callable, + vc_kwargs: dict, epoch_level: bool = False, - name: Optional[str] = None, + name: str | None = None, event=None, ): self.epoch_level = epoch_level @@ -156,7 +159,7 @@ def _step(initial_value: float, gamma: float, step_size: int, current_step: int) return initial_value * gamma ** (current_step // step_size) @staticmethod - def _multistep(initial_value: float, gamma: float, milestones: List[int], current_step: int) -> float: + def _multistep(initial_value: float, gamma: float, milestones: list[int], current_step: int) -> float: """ Decays the parameter value by gamma once the number of steps reaches one of the milestones. diff --git a/monai/handlers/postprocessing.py b/monai/handlers/postprocessing.py index 4a89c86f47..c698c84338 100644 --- a/monai/handlers/postprocessing.py +++ b/monai/handlers/postprocessing.py @@ -9,7 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Callable +from __future__ import annotations + +from collections.abc import Callable +from typing import TYPE_CHECKING from monai.config import IgniteInfo from monai.engines.utils import IterationEvents, engine_apply_transform diff --git a/monai/handlers/probability_maps.py b/monai/handlers/probability_maps.py index d39b640722..1d21a18f7d 100644 --- a/monai/handlers/probability_maps.py +++ b/monai/handlers/probability_maps.py @@ -9,9 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import threading -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING import numpy as np @@ -41,7 +43,7 @@ def __init__( output_postfix: str = "", prob_key: str = "pred", dtype: DtypeLike = np.float64, - name: Optional[str] = None, + name: str | None = None, ) -> None: """ Args: @@ -65,8 +67,8 @@ def __init__( self._name = name self.prob_key = prob_key self.dtype = dtype - self.prob_map: Dict[str, np.ndarray] = {} - self.counter: Dict[str, int] = {} + self.prob_map: dict[str, np.ndarray] = {} + self.counter: dict[str, int] = {} self.num_done_images: int = 0 self.num_images: int = 0 self.lock = threading.Lock() diff --git a/monai/handlers/regression_metrics.py b/monai/handlers/regression_metrics.py index bf4ac3af1d..fee7238491 100644 --- a/monai/handlers/regression_metrics.py +++ b/monai/handlers/regression_metrics.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Union +from __future__ import annotations + +from collections.abc import Callable from monai.handlers.ignite_metric import IgniteMetric from monai.metrics import MAEMetric, MSEMetric, PSNRMetric, RMSEMetric @@ -23,7 +25,7 @@ class MeanSquaredError(IgniteMetric): def __init__( self, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, output_transform: Callable = lambda x: x, save_details: bool = True, ) -> None: @@ -56,7 +58,7 @@ class MeanAbsoluteError(IgniteMetric): def __init__( self, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, output_transform: Callable = lambda x: x, save_details: bool = True, ) -> None: @@ -89,7 +91,7 @@ class RootMeanSquaredError(IgniteMetric): def __init__( self, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, output_transform: Callable = lambda x: x, save_details: bool = True, ) -> None: @@ -122,8 +124,8 @@ class PeakSignalToNoiseRatio(IgniteMetric): def __init__( self, - max_val: Union[int, float], - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + max_val: int | float, + reduction: MetricReduction | str = MetricReduction.MEAN, output_transform: Callable = lambda x: x, save_details: bool = True, ) -> None: diff --git a/monai/handlers/roc_auc.py b/monai/handlers/roc_auc.py index 68cf2e655e..a521a4cc06 100644 --- a/monai/handlers/roc_auc.py +++ b/monai/handlers/roc_auc.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Union +from __future__ import annotations + +from collections.abc import Callable from monai.handlers.ignite_metric import IgniteMetric from monai.metrics import ROCAUCMetric @@ -46,6 +48,6 @@ class ROCAUC(IgniteMetric): """ - def __init__(self, average: Union[Average, str] = Average.MACRO, output_transform: Callable = lambda x: x) -> None: + def __init__(self, average: Average | str = Average.MACRO, output_transform: Callable = lambda x: x) -> None: metric_fn = ROCAUCMetric(average=Average(average)) super().__init__(metric_fn=metric_fn, output_transform=output_transform, save_details=False) diff --git a/monai/handlers/smartcache_handler.py b/monai/handlers/smartcache_handler.py index 56fee78b1d..ee043635db 100644 --- a/monai/handlers/smartcache_handler.py +++ b/monai/handlers/smartcache_handler.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from typing import TYPE_CHECKING from monai.config import IgniteInfo diff --git a/monai/handlers/stats_handler.py b/monai/handlers/stats_handler.py index b8acb4d0ad..58917e666b 100644 --- a/monai/handlers/stats_handler.py +++ b/monai/handlers/stats_handler.py @@ -9,9 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import warnings -from typing import TYPE_CHECKING, Any, Callable, Optional, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any import torch @@ -65,12 +68,12 @@ def __init__( self, iteration_log: bool = True, epoch_log: bool = True, - epoch_print_logger: Optional[Callable[[Engine], Any]] = None, - iteration_print_logger: Optional[Callable[[Engine], Any]] = None, + epoch_print_logger: Callable[[Engine], Any] | None = None, + iteration_print_logger: Callable[[Engine], Any] | None = None, output_transform: Callable = lambda x: x[0], global_epoch_transform: Callable = lambda x: x, - state_attributes: Optional[Sequence[str]] = None, - name: Optional[str] = None, + state_attributes: Sequence[str] | None = None, + name: str | None = None, tag_name: str = DEFAULT_TAG, key_var_format: str = DEFAULT_KEY_VAL_FORMAT, ) -> None: diff --git a/monai/handlers/surface_distance.py b/monai/handlers/surface_distance.py index 77f0debfe9..eb80b41a07 100644 --- a/monai/handlers/surface_distance.py +++ b/monai/handlers/surface_distance.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Union +from __future__ import annotations + +from collections.abc import Callable from monai.handlers.ignite_metric import IgniteMetric from monai.metrics import SurfaceDistanceMetric @@ -26,7 +28,7 @@ def __init__( include_background: bool = False, symmetric: bool = False, distance_metric: str = "euclidean", - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, output_transform: Callable = lambda x: x, save_details: bool = True, ) -> None: diff --git a/monai/handlers/tensorboard_handlers.py b/monai/handlers/tensorboard_handlers.py index 14701e79d9..e6a8eb78ee 100644 --- a/monai/handlers/tensorboard_handlers.py +++ b/monai/handlers/tensorboard_handlers.py @@ -9,8 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import TYPE_CHECKING, Any, Callable, Optional, Sequence +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any import numpy as np import torch @@ -87,13 +90,13 @@ def __init__( log_dir: str = "./runs", iteration_log: bool = True, epoch_log: bool = True, - epoch_event_writer: Optional[Callable[[Engine, Any], Any]] = None, + epoch_event_writer: Callable[[Engine, Any], Any] | None = None, epoch_interval: int = 1, - iteration_event_writer: Optional[Callable[[Engine, Any], Any]] = None, + iteration_event_writer: Callable[[Engine, Any], Any] | None = None, iteration_interval: int = 1, output_transform: Callable = lambda x: x[0], global_epoch_transform: Callable = lambda x: x, - state_attributes: Optional[Sequence[str]] = None, + state_attributes: Sequence[str] | None = None, tag_name: str = DEFAULT_TAG, ) -> None: """ diff --git a/monai/handlers/utils.py b/monai/handlers/utils.py index 9b2c4f716e..a79c56bcb9 100644 --- a/monai/handlers/utils.py +++ b/monai/handlers/utils.py @@ -9,9 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os from collections import OrderedDict -from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Union +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any import numpy as np import torch @@ -52,10 +55,10 @@ def stopping_fn(engine: Engine): def write_metrics_reports( save_dir: PathLike, - images: Optional[Sequence[str]], - metrics: Optional[Dict[str, Union[torch.Tensor, np.ndarray]]], - metric_details: Optional[Dict[str, Union[torch.Tensor, np.ndarray]]], - summary_ops: Optional[Union[str, Sequence[str]]], + images: Sequence[str] | None, + metrics: dict[str, torch.Tensor | np.ndarray] | None, + metric_details: dict[str, torch.Tensor | np.ndarray] | None, + summary_ops: str | Sequence[str] | None, deli: str = ",", output_type: str = "csv", ): diff --git a/monai/handlers/validation_handler.py b/monai/handlers/validation_handler.py index 171c901fbb..5187fe67f7 100644 --- a/monai/handlers/validation_handler.py +++ b/monai/handlers/validation_handler.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Optional +from __future__ import annotations + +from typing import TYPE_CHECKING from monai.config import IgniteInfo from monai.engines.evaluator import Evaluator @@ -29,7 +31,7 @@ class ValidationHandler: """ - def __init__(self, interval: int, validator: Optional[Evaluator] = None, epoch_level: bool = True) -> None: + def __init__(self, interval: int, validator: Evaluator | None = None, epoch_level: bool = True) -> None: """ Args: interval: do validation every N epochs or every N iterations during training. diff --git a/monai/inferers/__init__.py b/monai/inferers/__init__.py index 3447782be9..67d13a28db 100644 --- a/monai/inferers/__init__.py +++ b/monai/inferers/__init__.py @@ -9,5 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .inferer import Inferer, SaliencyInferer, SimpleInferer, SliceInferer, SlidingWindowInferer from .utils import sliding_window_inference diff --git a/monai/inferers/inferer.py b/monai/inferers/inferer.py index d0d2f932b5..15b4927576 100644 --- a/monai/inferers/inferer.py +++ b/monai/inferers/inferer.py @@ -9,9 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings from abc import ABC, abstractmethod -from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Tuple, Union +from collections.abc import Callable, Mapping, Sequence +from typing import Any import torch import torch.nn as nn @@ -134,18 +137,18 @@ class SlidingWindowInferer(Inferer): def __init__( self, - roi_size: Union[Sequence[int], int], + roi_size: Sequence[int] | int, sw_batch_size: int = 1, overlap: float = 0.25, - mode: Union[BlendMode, str] = BlendMode.CONSTANT, - sigma_scale: Union[Sequence[float], float] = 0.125, - padding_mode: Union[PytorchPadMode, str] = PytorchPadMode.CONSTANT, + mode: BlendMode | str = BlendMode.CONSTANT, + sigma_scale: Sequence[float] | float = 0.125, + padding_mode: PytorchPadMode | str = PytorchPadMode.CONSTANT, cval: float = 0.0, - sw_device: Union[torch.device, str, None] = None, - device: Union[torch.device, str, None] = None, + sw_device: torch.device | str | None = None, + device: torch.device | str | None = None, progress: bool = False, cache_roi_weight_map: bool = False, - cpu_thresh: Optional[int] = None, + cpu_thresh: int | None = None, ) -> None: super().__init__() self.roi_size = roi_size @@ -180,10 +183,10 @@ def __init__( def __call__( self, inputs: torch.Tensor, - network: Callable[..., Union[torch.Tensor, Sequence[torch.Tensor], Dict[Any, torch.Tensor]]], + network: Callable[..., torch.Tensor | Sequence[torch.Tensor] | dict[Any, torch.Tensor]], *args: Any, **kwargs: Any, - ) -> Union[torch.Tensor, Tuple[torch.Tensor, ...], Dict[Any, torch.Tensor]]: + ) -> torch.Tensor | tuple[torch.Tensor, ...] | dict[Any, torch.Tensor]: """ Args: @@ -232,7 +235,7 @@ class SaliencyInferer(Inferer): """ - def __init__(self, cam_name: str, target_layers: str, class_idx: Optional[int] = None, *args, **kwargs) -> None: + def __init__(self, cam_name: str, target_layers: str, class_idx: int | None = None, *args, **kwargs) -> None: Inferer.__init__(self) if cam_name.lower() not in ("cam", "gradcam", "gradcampp"): raise ValueError("cam_name should be: 'CAM', 'GradCAM' or 'GradCAMpp'.") @@ -253,7 +256,7 @@ def __call__(self, inputs: torch.Tensor, network: nn.Module, *args: Any, **kwarg kwargs: other optional keyword args to be passed to `__call__` of cam. """ - cam: Union[CAM, GradCAM, GradCAMpp] + cam: CAM | GradCAM | GradCAMpp if self.cam_name == "cam": cam = CAM(network, self.target_layers, *self.args, **self.kwargs) elif self.cam_name == "gradcam": @@ -294,10 +297,10 @@ def __init__(self, spatial_dim: int = 0, *args, **kwargs) -> None: def __call__( self, inputs: torch.Tensor, - network: Callable[..., Union[torch.Tensor, Sequence[torch.Tensor], Dict[Any, torch.Tensor]]], + network: Callable[..., torch.Tensor | Sequence[torch.Tensor] | dict[Any, torch.Tensor]], *args: Any, **kwargs: Any, - ) -> Union[torch.Tensor, Tuple[torch.Tensor, ...], Dict[Any, torch.Tensor]]: + ) -> torch.Tensor | tuple[torch.Tensor, ...] | dict[Any, torch.Tensor]: """ Args: inputs: 3D input for inference @@ -322,11 +325,11 @@ def __call__( def network_wrapper( self, - network: Callable[..., Union[torch.Tensor, Sequence[torch.Tensor], Dict[Any, torch.Tensor]]], + network: Callable[..., torch.Tensor | Sequence[torch.Tensor] | dict[Any, torch.Tensor]], x: torch.Tensor, *args, **kwargs, - ) -> Union[torch.Tensor, Tuple[torch.Tensor, ...], Dict[Any, torch.Tensor]]: + ) -> torch.Tensor | tuple[torch.Tensor, ...] | dict[Any, torch.Tensor]: """ Wrapper handles inference for 2D models over 3D volume inputs. """ diff --git a/monai/inferers/utils.py b/monai/inferers/utils.py index fe83c416d6..14b1a86220 100644 --- a/monai/inferers/utils.py +++ b/monai/inferers/utils.py @@ -9,8 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Union +from collections.abc import Callable, Mapping, Sequence +from typing import Any import torch import torch.nn.functional as F @@ -36,22 +39,22 @@ def sliding_window_inference( inputs: torch.Tensor, - roi_size: Union[Sequence[int], int], + roi_size: Sequence[int] | int, sw_batch_size: int, - predictor: Callable[..., Union[torch.Tensor, Sequence[torch.Tensor], Dict[Any, torch.Tensor]]], + predictor: Callable[..., torch.Tensor | Sequence[torch.Tensor] | dict[Any, torch.Tensor]], overlap: float = 0.25, - mode: Union[BlendMode, str] = BlendMode.CONSTANT, - sigma_scale: Union[Sequence[float], float] = 0.125, - padding_mode: Union[PytorchPadMode, str] = PytorchPadMode.CONSTANT, + mode: BlendMode | str = BlendMode.CONSTANT, + sigma_scale: Sequence[float] | float = 0.125, + padding_mode: PytorchPadMode | str = PytorchPadMode.CONSTANT, cval: float = 0.0, - sw_device: Union[torch.device, str, None] = None, - device: Union[torch.device, str, None] = None, + sw_device: torch.device | str | None = None, + device: torch.device | str | None = None, progress: bool = False, - roi_weight_map: Optional[torch.Tensor] = None, - process_fn: Optional[Callable] = None, + roi_weight_map: torch.Tensor | None = None, + process_fn: Callable | None = None, *args: Any, **kwargs: Any, -) -> Union[torch.Tensor, Tuple[torch.Tensor, ...], Dict[Any, torch.Tensor]]: +) -> torch.Tensor | tuple[torch.Tensor, ...] | dict[Any, torch.Tensor]: """ Sliding window inference on `inputs` with `predictor`. @@ -185,7 +188,7 @@ def sliding_window_inference( seg_prob_out = predictor(window_data, *args, **kwargs) # batched patch segmentation # convert seg_prob_out to tuple seg_prob_tuple, this does not allocate new memory. - seg_prob_tuple: Tuple[torch.Tensor, ...] + seg_prob_tuple: tuple[torch.Tensor, ...] if isinstance(seg_prob_out, torch.Tensor): seg_prob_tuple = (seg_prob_out,) elif isinstance(seg_prob_out, Mapping): @@ -270,7 +273,7 @@ def sliding_window_inference( seg_prob_map_shape_d / roi_size_d for seg_prob_map_shape_d, roi_size_d in zip(output_i.shape[2:], roi_size) ] - final_slicing: List[slice] = [] + final_slicing: list[slice] = [] for sp in range(num_spatial_dims): slice_dim = slice(pad_size[sp * 2], image_size_[num_spatial_dims - sp - 1] + pad_size[sp * 2]) slice_dim = slice( @@ -295,7 +298,7 @@ def sliding_window_inference( def _get_scan_interval( image_size: Sequence[int], roi_size: Sequence[int], num_spatial_dims: int, overlap: float -) -> Tuple[int, ...]: +) -> tuple[int, ...]: """ Compute scan interval according to the image size, roi size and overlap. Scan interval will be `int((1 - overlap) * roi_size)`, if interval is 0, diff --git a/monai/losses/__init__.py b/monai/losses/__init__.py index a3c4bf1c5c..9e09b0b123 100644 --- a/monai/losses/__init__.py +++ b/monai/losses/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .contrastive import ContrastiveLoss from .deform import BendingEnergyLoss from .dice import ( diff --git a/monai/losses/contrastive.py b/monai/losses/contrastive.py index e0ed4ceab0..b066836143 100644 --- a/monai/losses/contrastive.py +++ b/monai/losses/contrastive.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from warnings import warn import torch diff --git a/monai/losses/deform.py b/monai/losses/deform.py index 0f5e263a53..dd03a8eb3d 100644 --- a/monai/losses/deform.py +++ b/monai/losses/deform.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Union +from __future__ import annotations import torch from torch.nn.modules.loss import _Loss @@ -52,7 +52,7 @@ class BendingEnergyLoss(_Loss): DeepReg (https://github.com/DeepRegNet/DeepReg) """ - def __init__(self, normalize: bool = False, reduction: Union[LossReduction, str] = LossReduction.MEAN) -> None: + def __init__(self, normalize: bool = False, reduction: LossReduction | str = LossReduction.MEAN) -> None: """ Args: normalize: diff --git a/monai/losses/dice.py b/monai/losses/dice.py index 1af4c519b3..5eff2fc169 100644 --- a/monai/losses/dice.py +++ b/monai/losses/dice.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Callable, List, Optional, Sequence, Union +from collections.abc import Callable, Sequence import numpy as np import torch @@ -47,10 +49,10 @@ def __init__( to_onehot_y: bool = False, sigmoid: bool = False, softmax: bool = False, - other_act: Optional[Callable] = None, + other_act: Callable | None = None, squared_pred: bool = False, jaccard: bool = False, - reduction: Union[LossReduction, str] = LossReduction.MEAN, + reduction: LossReduction | str = LossReduction.MEAN, smooth_nr: float = 1e-5, smooth_dr: float = 1e-5, batch: bool = False, @@ -157,7 +159,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: raise AssertionError(f"ground truth has different shape ({target.shape}) from input ({input.shape})") # reducing only spatial dimensions (not batch nor channels) - reduce_axis: List[int] = torch.arange(2, len(input.shape)).tolist() + reduce_axis: list[int] = torch.arange(2, len(input.shape)).tolist() if self.batch: # reducing spatial dimensions and batch reduce_axis = [0] + reduce_axis @@ -210,7 +212,7 @@ def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.spatial_weighted = MaskedLoss(loss=super().forward) - def forward(self, input: torch.Tensor, target: torch.Tensor, mask: Optional[torch.Tensor] = None): + def forward(self, input: torch.Tensor, target: torch.Tensor, mask: torch.Tensor | None = None): """ Args: input: the shape should be BNH[WD]. @@ -237,9 +239,9 @@ def __init__( to_onehot_y: bool = False, sigmoid: bool = False, softmax: bool = False, - other_act: Optional[Callable] = None, - w_type: Union[Weight, str] = Weight.SQUARE, - reduction: Union[LossReduction, str] = LossReduction.MEAN, + other_act: Callable | None = None, + w_type: Weight | str = Weight.SQUARE, + reduction: LossReduction | str = LossReduction.MEAN, smooth_nr: float = 1e-5, smooth_dr: float = 1e-5, batch: bool = False, @@ -337,7 +339,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: raise AssertionError(f"ground truth has differing shape ({target.shape}) from input ({input.shape})") # reducing only spatial dimensions (not batch nor channels) - reduce_axis: List[int] = torch.arange(2, len(input.shape)).tolist() + reduce_axis: list[int] = torch.arange(2, len(input.shape)).tolist() if self.batch: reduce_axis = [0] + reduce_axis intersection = torch.sum(target * input, reduce_axis) @@ -395,9 +397,9 @@ class GeneralizedWassersteinDiceLoss(_Loss): def __init__( self, - dist_matrix: Union[np.ndarray, torch.Tensor], + dist_matrix: np.ndarray | torch.Tensor, weighting_mode: str = "default", - reduction: Union[LossReduction, str] = LossReduction.MEAN, + reduction: LossReduction | str = LossReduction.MEAN, smooth_nr: float = 1e-5, smooth_dr: float = 1e-5, ) -> None: @@ -621,14 +623,14 @@ def __init__( to_onehot_y: bool = False, sigmoid: bool = False, softmax: bool = False, - other_act: Optional[Callable] = None, + other_act: Callable | None = None, squared_pred: bool = False, jaccard: bool = False, reduction: str = "mean", smooth_nr: float = 1e-5, smooth_dr: float = 1e-5, batch: bool = False, - ce_weight: Optional[torch.Tensor] = None, + ce_weight: torch.Tensor | None = None, lambda_dice: float = 1.0, lambda_ce: float = 1.0, ) -> None: @@ -757,7 +759,7 @@ def __init__( to_onehot_y: bool = False, sigmoid: bool = False, softmax: bool = False, - other_act: Optional[Callable] = None, + other_act: Callable | None = None, squared_pred: bool = False, jaccard: bool = False, reduction: str = "mean", @@ -765,7 +767,7 @@ def __init__( smooth_dr: float = 1e-5, batch: bool = False, gamma: float = 2.0, - focal_weight: Optional[Union[Sequence[float], float, int, torch.Tensor]] = None, + focal_weight: Sequence[float] | float | int | torch.Tensor | None = None, lambda_dice: float = 1.0, lambda_focal: float = 1.0, ) -> None: @@ -907,14 +909,14 @@ def __init__( to_onehot_y: bool = False, sigmoid: bool = False, softmax: bool = False, - other_act: Optional[Callable] = None, - w_type: Union[Weight, str] = Weight.SQUARE, - reduction: Union[LossReduction, str] = LossReduction.MEAN, + other_act: Callable | None = None, + w_type: Weight | str = Weight.SQUARE, + reduction: LossReduction | str = LossReduction.MEAN, smooth_nr: float = 1e-5, smooth_dr: float = 1e-5, batch: bool = False, gamma: float = 2.0, - focal_weight: Optional[Union[Sequence[float], float, int, torch.Tensor]] = None, + focal_weight: Sequence[float] | float | int | torch.Tensor | None = None, lambda_gdl: float = 1.0, lambda_focal: float = 1.0, ) -> None: diff --git a/monai/losses/ds_loss.py b/monai/losses/ds_loss.py index c0425ffdde..f6b6bcc14b 100644 --- a/monai/losses/ds_loss.py +++ b/monai/losses/ds_loss.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Optional, Union +from __future__ import annotations import torch import torch.nn.functional as F @@ -24,7 +24,7 @@ class DeepSupervisionLoss(_Loss): supervised networks. The final loss is computed as the sum of weighted losses for each of deep supervision levels. """ - def __init__(self, loss: _Loss, weight_mode: str = "exp", weights: Optional[List[float]] = None) -> None: + def __init__(self, loss: _Loss, weight_mode: str = "exp", weights: list[float] | None = None) -> None: """ Args: loss: main loss instance, e.g DiceLoss(). @@ -42,7 +42,7 @@ def __init__(self, loss: _Loss, weight_mode: str = "exp", weights: Optional[List self.weights = weights self.interp_mode = "nearest-exact" if pytorch_after(1, 11) else "nearest" - def get_weights(self, levels: int = 1) -> List[float]: + def get_weights(self, levels: int = 1) -> list[float]: """ Calculates weights for a given number of scale levels """ @@ -70,7 +70,7 @@ def get_loss(self, input: torch.Tensor, target: torch.Tensor): target = F.interpolate(target, size=input.shape[2:], mode=self.interp_mode) return self.loss(input, target) - def forward(self, input: Union[torch.Tensor, List[torch.Tensor]], target: torch.Tensor): + def forward(self, input: torch.Tensor | list[torch.Tensor], target: torch.Tensor): if isinstance(input, (list, tuple)): weights = self.get_weights(levels=len(input)) diff --git a/monai/losses/focal_loss.py b/monai/losses/focal_loss.py index bf31682748..80c01c7b7f 100644 --- a/monai/losses/focal_loss.py +++ b/monai/losses/focal_loss.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Optional, Sequence, Union +from collections.abc import Sequence import torch import torch.nn.functional as F @@ -67,8 +69,8 @@ def __init__( include_background: bool = True, to_onehot_y: bool = False, gamma: float = 2.0, - weight: Optional[Union[Sequence[float], float, int, torch.Tensor]] = None, - reduction: Union[LossReduction, str] = LossReduction.MEAN, + weight: Sequence[float] | float | int | torch.Tensor | None = None, + reduction: LossReduction | str = LossReduction.MEAN, ) -> None: """ Args: @@ -100,7 +102,7 @@ def __init__( self.include_background = include_background self.to_onehot_y = to_onehot_y self.gamma = gamma - self.weight: Optional[Union[Sequence[float], float, int, torch.Tensor]] = weight + self.weight: Sequence[float] | float | int | torch.Tensor | None = weight def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: """ @@ -152,7 +154,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: ce = i - i * t + max_val + ((-max_val).exp() + (-i - max_val).exp()).log() if self.weight is not None: - class_weight: Optional[torch.Tensor] = None + class_weight: torch.Tensor | None = None if isinstance(self.weight, (float, int)): class_weight = torch.as_tensor([self.weight] * i.size(1)) else: diff --git a/monai/losses/giou_loss.py b/monai/losses/giou_loss.py index 623e55921b..7940660a08 100644 --- a/monai/losses/giou_loss.py +++ b/monai/losses/giou_loss.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Union +from __future__ import annotations import torch from torch.nn.modules.loss import _Loss @@ -33,7 +33,7 @@ class BoxGIoULoss(_Loss): - ``"sum"``: the output will be summed. """ - def __init__(self, reduction: Union[LossReduction, str] = LossReduction.MEAN) -> None: + def __init__(self, reduction: LossReduction | str = LossReduction.MEAN) -> None: super().__init__(reduction=LossReduction(reduction).value) def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: diff --git a/monai/losses/image_dissimilarity.py b/monai/losses/image_dissimilarity.py index 4351199aee..39219e059a 100644 --- a/monai/losses/image_dissimilarity.py +++ b/monai/losses/image_dissimilarity.py @@ -8,7 +8,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from typing import Tuple, Union + +from __future__ import annotations import torch from torch.nn import functional as F @@ -65,7 +66,7 @@ def __init__( spatial_dims: int = 3, kernel_size: int = 3, kernel_type: str = "rectangular", - reduction: Union[LossReduction, str] = LossReduction.MEAN, + reduction: LossReduction | str = LossReduction.MEAN, smooth_nr: float = 0.0, smooth_dr: float = 1e-5, ) -> None: @@ -175,7 +176,7 @@ def __init__( kernel_type: str = "gaussian", num_bins: int = 23, sigma_ratio: float = 0.5, - reduction: Union[LossReduction, str] = LossReduction.MEAN, + reduction: LossReduction | str = LossReduction.MEAN, smooth_nr: float = 1e-7, smooth_dr: float = 1e-7, ) -> None: @@ -221,7 +222,7 @@ def __init__( def parzen_windowing( self, pred: torch.Tensor, target: torch.Tensor - ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: + ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: if self.kernel_type == "gaussian": pred_weight, pred_probability = self.parzen_windowing_gaussian(pred) target_weight, target_probability = self.parzen_windowing_gaussian(target) @@ -234,7 +235,7 @@ def parzen_windowing( raise ValueError return pred_weight, pred_probability, target_weight, target_probability - def parzen_windowing_b_spline(self, img: torch.Tensor, order: int) -> Tuple[torch.Tensor, torch.Tensor]: + def parzen_windowing_b_spline(self, img: torch.Tensor, order: int) -> tuple[torch.Tensor, torch.Tensor]: """ Parzen windowing with b-spline kernel (adapted from ITK) @@ -287,7 +288,7 @@ def parzen_windowing_b_spline(self, img: torch.Tensor, order: int) -> Tuple[torc probability = torch.mean(weight, dim=-2, keepdim=True) # (batch, 1, num_bins) return weight, probability - def parzen_windowing_gaussian(self, img: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: + def parzen_windowing_gaussian(self, img: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: """ Parzen windowing with gaussian kernel (adapted from DeepReg implementation) Note: the input is expected to range between 0 and 1 diff --git a/monai/losses/multi_scale.py b/monai/losses/multi_scale.py index bef1ae1a5d..7119f51042 100644 --- a/monai/losses/multi_scale.py +++ b/monai/losses/multi_scale.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Optional, Union +from __future__ import annotations import torch from torch.nn.modules.loss import _Loss @@ -49,9 +49,9 @@ class MultiScaleLoss(_Loss): def __init__( self, loss: _Loss, - scales: Optional[List] = None, + scales: list | None = None, kernel: str = "gaussian", - reduction: Union[LossReduction, str] = LossReduction.MEAN, + reduction: LossReduction | str = LossReduction.MEAN, ) -> None: """ Args: diff --git a/monai/losses/spatial_mask.py b/monai/losses/spatial_mask.py index aa232f882e..5c0430b264 100644 --- a/monai/losses/spatial_mask.py +++ b/monai/losses/spatial_mask.py @@ -9,9 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import inspect import warnings -from typing import Callable, Optional, Union +from collections.abc import Callable import torch from torch.nn.modules.loss import _Loss @@ -28,7 +30,7 @@ class MaskedLoss(_Loss): - :py:class:`monai.losses.MaskedDiceLoss` """ - def __init__(self, loss: Union[Callable, _Loss], *loss_args, **loss_kwargs) -> None: + def __init__(self, loss: Callable | _Loss, *loss_args, **loss_kwargs) -> None: """ Args: loss: loss function to be wrapped, this could be a loss class or an instance of a loss class. @@ -40,7 +42,7 @@ def __init__(self, loss: Union[Callable, _Loss], *loss_args, **loss_kwargs) -> N 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): + def forward(self, input: torch.Tensor, target: torch.Tensor, mask: torch.Tensor | None = None): """ Args: input: the shape should be BNH[WD]. diff --git a/monai/losses/ssim_loss.py b/monai/losses/ssim_loss.py index 2dc0289a29..e8a984013b 100644 --- a/monai/losses/ssim_loss.py +++ b/monai/losses/ssim_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import torch from torch.nn.modules.loss import _Loss diff --git a/monai/losses/tversky.py b/monai/losses/tversky.py index a0735c24e0..4f22bf84b4 100644 --- a/monai/losses/tversky.py +++ b/monai/losses/tversky.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Callable, List, Optional, Union +from collections.abc import Callable import torch from torch.nn.modules.loss import _Loss @@ -37,10 +39,10 @@ def __init__( to_onehot_y: bool = False, sigmoid: bool = False, softmax: bool = False, - other_act: Optional[Callable] = None, + other_act: Callable | None = None, alpha: float = 0.5, beta: float = 0.5, - reduction: Union[LossReduction, str] = LossReduction.MEAN, + reduction: LossReduction | str = LossReduction.MEAN, smooth_nr: float = 1e-5, smooth_dr: float = 1e-5, batch: bool = False, @@ -138,7 +140,7 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: g1 = 1 - g0 # reducing only spatial dimensions (not batch nor channels) - reduce_axis: List[int] = torch.arange(2, len(input.shape)).tolist() + reduce_axis: list[int] = torch.arange(2, len(input.shape)).tolist() if self.batch: # reducing spatial dimensions and batch reduce_axis = [0] + reduce_axis diff --git a/monai/losses/unified_focal_loss.py b/monai/losses/unified_focal_loss.py index 1e2bdae725..8484eb67ed 100644 --- a/monai/losses/unified_focal_loss.py +++ b/monai/losses/unified_focal_loss.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Union import torch from torch.nn.modules.loss import _Loss @@ -37,7 +38,7 @@ def __init__( delta: float = 0.7, gamma: float = 0.75, epsilon: float = 1e-7, - reduction: Union[LossReduction, str] = LossReduction.MEAN, + reduction: LossReduction | str = LossReduction.MEAN, ) -> None: """ Args: @@ -101,7 +102,7 @@ def __init__( delta: float = 0.7, gamma: float = 2, epsilon: float = 1e-7, - reduction: Union[LossReduction, str] = LossReduction.MEAN, + reduction: LossReduction | str = LossReduction.MEAN, ): """ Args: @@ -160,7 +161,7 @@ def __init__( weight: float = 0.5, gamma: float = 0.5, delta: float = 0.7, - reduction: Union[LossReduction, str] = LossReduction.MEAN, + reduction: LossReduction | str = LossReduction.MEAN, ): """ Args: diff --git a/monai/metrics/__init__.py b/monai/metrics/__init__.py index d04e8461c4..ea6d22c014 100644 --- a/monai/metrics/__init__.py +++ b/monai/metrics/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .active_learning_metrics import LabelQualityScore, VarianceMetric, compute_variance, label_quality_score from .confusion_matrix import ConfusionMatrixMetric, compute_confusion_matrix_metric, get_confusion_matrix from .cumulative_average import CumulativeAverage diff --git a/monai/metrics/active_learning_metrics.py b/monai/metrics/active_learning_metrics.py index eddc82e87a..6468d56492 100644 --- a/monai/metrics/active_learning_metrics.py +++ b/monai/metrics/active_learning_metrics.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings from typing import Any diff --git a/monai/metrics/confusion_matrix.py b/monai/metrics/confusion_matrix.py index 872819f1a9..e88a5ec4cb 100644 --- a/monai/metrics/confusion_matrix.py +++ b/monai/metrics/confusion_matrix.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Sequence, Union +from collections.abc import Sequence import torch @@ -63,9 +65,9 @@ class ConfusionMatrixMetric(CumulativeIterationMetric): def __init__( self, include_background: bool = True, - metric_name: Union[Sequence[str], str] = "hit_rate", + metric_name: Sequence[str] | str = "hit_rate", compute_sample: bool = False, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False, ) -> None: super().__init__() @@ -100,7 +102,7 @@ def _compute_tensor(self, y_pred: torch.Tensor, y: torch.Tensor): # type: ignor return get_confusion_matrix(y_pred=y_pred, y=y, include_background=self.include_background) - def aggregate(self, compute_sample: bool = False, reduction: Union[MetricReduction, str, None] = None): + def aggregate(self, compute_sample: bool = False, reduction: MetricReduction | str | None = None): """ Execute reduction for the confusion matrix values. @@ -215,7 +217,7 @@ def compute_confusion_matrix_metric(metric_name: str, confusion_matrix: torch.Te n = fp + tn # calculate metric numerator: torch.Tensor - denominator: Union[torch.Tensor, float] + denominator: torch.Tensor | float nan_tensor = torch.tensor(float("nan"), device=confusion_matrix.device) if metric == "tpr": numerator, denominator = tp, p diff --git a/monai/metrics/cumulative_average.py b/monai/metrics/cumulative_average.py index b099cdc2a4..e55e7b8576 100644 --- a/monai/metrics/cumulative_average.py +++ b/monai/metrics/cumulative_average.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Any, Optional +from typing import Any import torch import torch.distributed as dist @@ -100,7 +102,7 @@ def aggregate(self, to_numpy: bool = True) -> NdarrayOrTensor: val = val.cpu().numpy() return val - def append(self, val: Any, count: Optional[Any] = 1) -> None: + def append(self, val: Any, count: Any | None = 1) -> None: """ Append with a new value, and an optional count. Any data type is supported that is convertable with torch.as_tensor() e.g. number, list, numpy array, or Tensor. diff --git a/monai/metrics/f_beta_score.py b/monai/metrics/f_beta_score.py index 067c09e0d2..9884b54e82 100644 --- a/monai/metrics/f_beta_score.py +++ b/monai/metrics/f_beta_score.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Union +from __future__ import annotations import torch @@ -24,7 +24,7 @@ def __init__( self, beta: float = 1.0, include_background: bool = True, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False, ) -> None: super().__init__() @@ -42,7 +42,7 @@ def _compute_tensor(self, y_pred: torch.Tensor, y: torch.Tensor): # type: ignor return get_f_beta_score(y_pred=y_pred, y=y, include_background=self.include_background) - def aggregate(self, compute_sample: bool = False, reduction: Union[MetricReduction, str, None] = None): + def aggregate(self, compute_sample: bool = False, reduction: MetricReduction | str | None = None): data = self.get_buffer() if not isinstance(data, torch.Tensor): raise ValueError("the data to aggregate must be PyTorch Tensor.") diff --git a/monai/metrics/froc.py b/monai/metrics/froc.py index 56e0755b99..9cd576d2ca 100644 --- a/monai/metrics/froc.py +++ b/monai/metrics/froc.py @@ -9,18 +9,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Optional, Tuple, Union +from __future__ import annotations import numpy as np import torch def compute_fp_tp_probs( - probs: Union[np.ndarray, torch.Tensor], - y_coord: Union[np.ndarray, torch.Tensor], - x_coord: Union[np.ndarray, torch.Tensor], - evaluation_mask: Union[np.ndarray, torch.Tensor], - labels_to_exclude: Optional[List] = None, + probs: np.ndarray | torch.Tensor, + y_coord: np.ndarray | torch.Tensor, + x_coord: np.ndarray | torch.Tensor, + evaluation_mask: np.ndarray | torch.Tensor, + labels_to_exclude: list | None = None, resolution_level: int = 0, ): """ @@ -78,10 +78,7 @@ def compute_fp_tp_probs( def compute_froc_curve_data( - fp_probs: Union[np.ndarray, torch.Tensor], - tp_probs: Union[np.ndarray, torch.Tensor], - num_targets: int, - num_images: int, + fp_probs: np.ndarray | torch.Tensor, tp_probs: np.ndarray | torch.Tensor, num_targets: int, num_images: int ): """ This function is modified from the official evaluation code of @@ -117,7 +114,7 @@ def compute_froc_curve_data( def compute_froc_score( - fps_per_image: np.ndarray, total_sensitivity: np.ndarray, eval_thresholds: Tuple = (0.25, 0.5, 1, 2, 4, 8) + fps_per_image: np.ndarray, total_sensitivity: np.ndarray, eval_thresholds: tuple = (0.25, 0.5, 1, 2, 4, 8) ): """ This function is modified from the official evaluation code of diff --git a/monai/metrics/generalized_dice.py b/monai/metrics/generalized_dice.py index 7fdfc61a14..b1e16694f3 100644 --- a/monai/metrics/generalized_dice.py +++ b/monai/metrics/generalized_dice.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Union +from __future__ import annotations import torch @@ -46,8 +46,8 @@ class GeneralizedDiceScore(CumulativeIterationMetric): def __init__( self, include_background: bool = True, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN_BATCH, - weight_type: Union[Weight, str] = Weight.SQUARE, + reduction: MetricReduction | str = MetricReduction.MEAN_BATCH, + weight_type: Weight | str = Weight.SQUARE, ) -> None: super().__init__() self.include_background = include_background @@ -80,7 +80,7 @@ def _compute_tensor(self, y_pred: torch.Tensor, y: torch.Tensor): # type: ignor y_pred=y_pred, y=y, include_background=self.include_background, weight_type=self.weight_type ) - def aggregate(self, reduction: Union[MetricReduction, str, None] = None): + def aggregate(self, reduction: MetricReduction | str | None = None): """ Execute reduction logic for the output of `compute_generalized_dice`. @@ -106,10 +106,7 @@ def aggregate(self, reduction: Union[MetricReduction, str, None] = None): def compute_generalized_dice( - y_pred: torch.Tensor, - y: torch.Tensor, - include_background: bool = True, - weight_type: Union[Weight, str] = Weight.SQUARE, + y_pred: torch.Tensor, y: torch.Tensor, include_background: bool = True, weight_type: Weight | str = Weight.SQUARE ) -> torch.Tensor: """Computes the Generalized Dice Score and returns a tensor with its per image values. diff --git a/monai/metrics/hausdorff_distance.py b/monai/metrics/hausdorff_distance.py index 54de8b1d4d..610cf0b01a 100644 --- a/monai/metrics/hausdorff_distance.py +++ b/monai/metrics/hausdorff_distance.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Optional, Union import numpy as np import torch @@ -62,9 +63,9 @@ def __init__( self, include_background: bool = False, distance_metric: str = "euclidean", - percentile: Optional[float] = None, + percentile: float | None = None, directed: bool = False, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False, ) -> None: super().__init__() @@ -104,7 +105,7 @@ def _compute_tensor(self, y_pred: torch.Tensor, y: torch.Tensor): # type: ignor directed=self.directed, ) - def aggregate(self, reduction: Union[MetricReduction, str, None] = None): + def aggregate(self, reduction: MetricReduction | str | None = None): """ Execute reduction logic for the output of `compute_hausdorff_distance`. @@ -124,11 +125,11 @@ def aggregate(self, reduction: Union[MetricReduction, str, None] = None): def compute_hausdorff_distance( - y_pred: Union[np.ndarray, torch.Tensor], - y: Union[np.ndarray, torch.Tensor], + y_pred: np.ndarray | torch.Tensor, + y: np.ndarray | torch.Tensor, include_background: bool = False, distance_metric: str = "euclidean", - percentile: Optional[float] = None, + percentile: float | None = None, directed: bool = False, ): """ @@ -179,7 +180,7 @@ def compute_hausdorff_distance( def compute_percent_hausdorff_distance( - edges_pred: np.ndarray, edges_gt: np.ndarray, distance_metric: str = "euclidean", percentile: Optional[float] = None + edges_pred: np.ndarray, edges_gt: np.ndarray, distance_metric: str = "euclidean", percentile: float | None = None ): """ This function is used to compute the directed Hausdorff distance. diff --git a/monai/metrics/loss_metric.py b/monai/metrics/loss_metric.py index 01db1f7af1..e74da1936f 100644 --- a/monai/metrics/loss_metric.py +++ b/monai/metrics/loss_metric.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Union +from __future__ import annotations import torch from torch.nn.modules.loss import _Loss @@ -67,14 +67,14 @@ class LossMetric(CumulativeIterationMetric): """ def __init__( - self, loss_fn: _Loss, reduction: Union[MetricReduction, str] = MetricReduction.MEAN, get_not_nans: bool = False + self, loss_fn: _Loss, reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False ) -> None: super().__init__() self.loss_fn = loss_fn self.reduction = reduction self.get_not_nans = get_not_nans - def aggregate(self, reduction: Union[MetricReduction, str, None] = None): + def aggregate(self, reduction: MetricReduction | str | None = None): """ Returns the aggregated loss value across multiple iterations. diff --git a/monai/metrics/meandice.py b/monai/metrics/meandice.py index a9d4e7182a..d63d81a0ec 100644 --- a/monai/metrics/meandice.py +++ b/monai/metrics/meandice.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Union +from __future__ import annotations import torch @@ -50,7 +50,7 @@ class DiceMetric(CumulativeIterationMetric): def __init__( self, include_background: bool = True, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False, ignore_empty: bool = True, ) -> None: @@ -84,7 +84,7 @@ def _compute_tensor(self, y_pred: torch.Tensor, y: torch.Tensor): # type: ignor y_pred=y_pred, y=y, include_background=self.include_background, ignore_empty=self.ignore_empty ) - def aggregate(self, reduction: Union[MetricReduction, str, None] = None): + def aggregate(self, reduction: MetricReduction | str | None = None): """ Execute reduction logic for the output of `compute_meandice`. diff --git a/monai/metrics/meaniou.py b/monai/metrics/meaniou.py index 55fa73e1ff..83f9591b56 100644 --- a/monai/metrics/meaniou.py +++ b/monai/metrics/meaniou.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Union +from __future__ import annotations import torch @@ -51,7 +51,7 @@ class MeanIoU(CumulativeIterationMetric): def __init__( self, include_background: bool = True, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False, ignore_empty: bool = True, ) -> None: @@ -85,7 +85,7 @@ def _compute_tensor(self, y_pred: torch.Tensor, y: torch.Tensor): # type: ignor y_pred=y_pred, y=y, include_background=self.include_background, ignore_empty=self.ignore_empty ) - def aggregate(self, reduction: Union[MetricReduction, str, None] = None): + def aggregate(self, reduction: MetricReduction | str | None = None): """ Execute reduction logic for the output of `compute_meaniou`. diff --git a/monai/metrics/metric.py b/monai/metrics/metric.py index 99aff701c1..6a00e44a23 100644 --- a/monai/metrics/metric.py +++ b/monai/metrics/metric.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from abc import ABC, abstractmethod -from typing import Any, List, Optional +from typing import Any import torch @@ -45,7 +47,7 @@ class IterationMetric(Metric): Subclasses typically implement the `_compute_tensor` function for the actual tensor computation logic. """ - def __call__(self, y_pred: TensorOrList, y: Optional[TensorOrList] = None): + def __call__(self, y_pred: TensorOrList, y: TensorOrList | None = None): """ Execute basic computation for model prediction `y_pred` and ground truth `y` (optional). It supports inputs of a list of "channel-first" Tensor and a "batch-first" Tensor. @@ -71,7 +73,7 @@ def __call__(self, y_pred: TensorOrList, y: Optional[TensorOrList] = None): return self._compute_tensor(y_pred.detach(), y_) raise ValueError("y_pred or y must be a list/tuple of `channel-first` Tensors or a `batch-first` Tensor.") - def _compute_list(self, y_pred: TensorOrList, y: Optional[TensorOrList] = None): + def _compute_list(self, y_pred: TensorOrList, y: TensorOrList | None = None): """ Execute the metric computation for `y_pred` and `y` in a list of "channel-first" tensors. @@ -99,7 +101,7 @@ def _compute_list(self, y_pred: TensorOrList, y: Optional[TensorOrList] = None): return ret @abstractmethod - def _compute_tensor(self, y_pred: torch.Tensor, y: Optional[torch.Tensor] = None): + def _compute_tensor(self, y_pred: torch.Tensor, y: torch.Tensor | None = None): """ Computation logic for `y_pred` and `y` of an iteration, the data should be "batch-first" Tensors. A subclass should implement its own computation logic. @@ -172,8 +174,8 @@ def __init__(self) -> None: `self._buffers` are local buffers, they are not usually used directly. `self._sync_buffers` are the buffers with all the results across all the nodes. """ - self._buffers: Optional[List[List[torch.Tensor]]] = None - self._synced_tensors: Optional[List[Optional[torch.Tensor]]] = None + self._buffers: list[list[torch.Tensor]] | None = None + self._synced_tensors: list[torch.Tensor | None] | None = None self._synced: bool = False self.reset() @@ -310,7 +312,7 @@ class CumulativeIterationMetric(Cumulative, IterationMetric): """ - def __call__(self, y_pred: TensorOrList, y: Optional[TensorOrList] = None): + def __call__(self, y_pred: TensorOrList, y: TensorOrList | None = None): """ Execute basic computation for model prediction and ground truth. It can support both `list of channel-first Tensor` and `batch-first Tensor`. diff --git a/monai/metrics/panoptic_quality.py b/monai/metrics/panoptic_quality.py index dc8cfb84b2..52bc67fc9e 100644 --- a/monai/metrics/panoptic_quality.py +++ b/monai/metrics/panoptic_quality.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import torch @@ -59,8 +61,8 @@ class PanopticQualityMetric(CumulativeIterationMetric): def __init__( self, num_classes: int, - metric_name: Union[Sequence[str], str] = "pq", - reduction: Union[MetricReduction, str] = MetricReduction.MEAN_BATCH, + metric_name: Sequence[str] | str = "pq", + reduction: MetricReduction | str = MetricReduction.MEAN_BATCH, match_iou_threshold: float = 0.5, smooth_numerator: float = 1e-6, ) -> None: @@ -120,7 +122,7 @@ def _compute_tensor(self, y_pred: torch.Tensor, y: torch.Tensor): # type: ignor return outputs - def aggregate(self, reduction: Union[MetricReduction, str, None] = None): + def aggregate(self, reduction: MetricReduction | str | None = None): """ Execute reduction logic for the output of `compute_panoptic_quality`. @@ -226,13 +228,13 @@ def _get_id_list(gt: torch.Tensor): return id_list -def _get_pairwise_iou(pred: torch.Tensor, gt: torch.Tensor, device: Union[str, torch.device] = "cpu"): +def _get_pairwise_iou(pred: torch.Tensor, gt: torch.Tensor, device: str | torch.device = "cpu"): pred_id_list = _get_id_list(pred) true_id_list = _get_id_list(gt) pairwise_iou = torch.zeros([len(true_id_list) - 1, len(pred_id_list) - 1], dtype=torch.float, device=device) - true_masks: List[torch.Tensor] = [] - pred_masks: List[torch.Tensor] = [] + true_masks: list[torch.Tensor] = [] + pred_masks: list[torch.Tensor] = [] for t in true_id_list[1:]: t_mask = torch.as_tensor(gt == t, device=device).int() @@ -258,9 +260,7 @@ def _get_pairwise_iou(pred: torch.Tensor, gt: torch.Tensor, device: Union[str, t return pairwise_iou, true_id_list, pred_id_list -def _get_paired_iou( - pairwise_iou: torch.Tensor, match_iou_threshold: float = 0.5, device: Union[str, torch.device] = "cpu" -): +def _get_paired_iou(pairwise_iou: torch.Tensor, match_iou_threshold: float = 0.5, device: str | torch.device = "cpu"): if match_iou_threshold >= 0.5: pairwise_iou[pairwise_iou <= match_iou_threshold] = 0.0 paired_true, paired_pred = torch.nonzero(pairwise_iou)[:, 0], torch.nonzero(pairwise_iou)[:, 1] diff --git a/monai/metrics/regression.py b/monai/metrics/regression.py index b714fede86..ecab61308b 100644 --- a/monai/metrics/regression.py +++ b/monai/metrics/regression.py @@ -9,10 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import math from abc import abstractmethod from functools import partial -from typing import Any, Tuple, Union +from typing import Any import torch import torch.nn.functional as F @@ -42,14 +44,12 @@ class RegressionMetric(CumulativeIterationMetric): """ - def __init__( - self, reduction: Union[MetricReduction, str] = MetricReduction.MEAN, get_not_nans: bool = False - ) -> None: + def __init__(self, reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False) -> None: super().__init__() self.reduction = reduction self.get_not_nans = get_not_nans - def aggregate(self, reduction: Union[MetricReduction, str, None] = None): + def aggregate(self, reduction: MetricReduction | str | None = None): """ Args: reduction: define mode of reduction to the metrics, will only apply reduction on `not-nan` values, @@ -103,9 +103,7 @@ class MSEMetric(RegressionMetric): """ - def __init__( - self, reduction: Union[MetricReduction, str] = MetricReduction.MEAN, get_not_nans: bool = False - ) -> None: + def __init__(self, reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False) -> None: super().__init__(reduction=reduction, get_not_nans=get_not_nans) self.sq_func = partial(torch.pow, exponent=2.0) @@ -137,9 +135,7 @@ class MAEMetric(RegressionMetric): """ - def __init__( - self, reduction: Union[MetricReduction, str] = MetricReduction.MEAN, get_not_nans: bool = False - ) -> None: + def __init__(self, reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False) -> None: super().__init__(reduction=reduction, get_not_nans=get_not_nans) self.abs_func = torch.abs @@ -172,9 +168,7 @@ class RMSEMetric(RegressionMetric): """ - def __init__( - self, reduction: Union[MetricReduction, str] = MetricReduction.MEAN, get_not_nans: bool = False - ) -> None: + def __init__(self, reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False) -> None: super().__init__(reduction=reduction, get_not_nans=get_not_nans) self.sq_func = partial(torch.pow, exponent=2.0) @@ -214,10 +208,7 @@ class PSNRMetric(RegressionMetric): """ def __init__( - self, - max_val: Union[int, float], - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, - get_not_nans: bool = False, + self, max_val: int | float, reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False ) -> None: super().__init__(reduction=reduction, get_not_nans=get_not_nans) self.max_val = max_val @@ -275,7 +266,7 @@ def __init__( k1: float = 0.01, k2: float = 0.03, spatial_dims: int = 2, - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False, ): super().__init__(reduction=reduction, get_not_nans=get_not_nans) @@ -286,7 +277,7 @@ def __init__( self.cov_norm = (win_size**2) / (win_size**2 - 1) self.w = torch.ones([1, 1] + [win_size for _ in range(spatial_dims)]) / win_size**spatial_dims - def _compute_intermediate_statistics(self, x: torch.Tensor, y: torch.Tensor) -> Tuple[torch.Tensor, ...]: + def _compute_intermediate_statistics(self, x: torch.Tensor, y: torch.Tensor) -> tuple[torch.Tensor, ...]: data_range = self.data_range[(None,) * (self.spatial_dims + 2)] # determine whether to work with 2D convolution or 3D @@ -354,7 +345,7 @@ def _compute_metric(self, x: torch.Tensor, y: torch.Tensor) -> torch.Tensor: return ssim_per_batch - def _compute_metric_and_contrast(self, x: torch.Tensor, y: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: + def _compute_metric_and_contrast(self, x: torch.Tensor, y: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: """ Args: x: first sample (e.g., the reference image). Its shape is (B,C,W,H) for 2D data and (B,C,W,H,D) for 3D. diff --git a/monai/metrics/rocauc.py b/monai/metrics/rocauc.py index 2bb8dc2b32..e10e7d25d9 100644 --- a/monai/metrics/rocauc.py +++ b/monai/metrics/rocauc.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Union, cast +from typing import cast import numpy as np import torch @@ -44,14 +46,14 @@ class ROCAUCMetric(CumulativeIterationMetric): """ - def __init__(self, average: Union[Average, str] = Average.MACRO) -> None: + def __init__(self, average: Average | str = Average.MACRO) -> None: super().__init__() self.average = average def _compute_tensor(self, y_pred: torch.Tensor, y: torch.Tensor): # type: ignore return y_pred, y - def aggregate(self, average: Union[Average, str, None] = None): + def aggregate(self, average: Average | str | None = None): """ Typically `y_pred` and `y` are stored in the cumulative buffers at each iteration, This function reads the buffers and computes the area under the ROC. @@ -106,7 +108,7 @@ def _calculate(y_pred: torch.Tensor, y: torch.Tensor) -> float: return auc / (nneg * (n - nneg)) -def compute_roc_auc(y_pred: torch.Tensor, y: torch.Tensor, average: Union[Average, str] = Average.MACRO): +def compute_roc_auc(y_pred: torch.Tensor, y: torch.Tensor, average: Average | str = Average.MACRO): """Computes Area Under the Receiver Operating Characteristic Curve (ROC AUC). Referring to: `sklearn.metrics.roc_auc_score `_. diff --git a/monai/metrics/surface_dice.py b/monai/metrics/surface_dice.py index 80869ce583..659cd450fd 100644 --- a/monai/metrics/surface_dice.py +++ b/monai/metrics/surface_dice.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import List, Union import numpy as np import torch @@ -53,10 +54,10 @@ class SurfaceDiceMetric(CumulativeIterationMetric): def __init__( self, - class_thresholds: List[float], + class_thresholds: list[float], include_background: bool = False, distance_metric: str = "euclidean", - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False, ) -> None: super().__init__() @@ -86,7 +87,7 @@ def _compute_tensor(self, y_pred: torch.Tensor, y: torch.Tensor): # type: ignor distance_metric=self.distance_metric, ) - def aggregate(self, reduction: Union[MetricReduction, str, None] = None): + def aggregate(self, reduction: MetricReduction | str | None = None): r""" Aggregates the output of `_compute_tensor`. @@ -111,7 +112,7 @@ def aggregate(self, reduction: Union[MetricReduction, str, None] = None): def compute_surface_dice( y_pred: torch.Tensor, y: torch.Tensor, - class_thresholds: List[float], + class_thresholds: list[float], include_background: bool = False, distance_metric: str = "euclidean", ): diff --git a/monai/metrics/surface_distance.py b/monai/metrics/surface_distance.py index 8bb688b4e0..093c92fd4b 100644 --- a/monai/metrics/surface_distance.py +++ b/monai/metrics/surface_distance.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Union import numpy as np import torch @@ -58,7 +59,7 @@ def __init__( include_background: bool = False, symmetric: bool = False, distance_metric: str = "euclidean", - reduction: Union[MetricReduction, str] = MetricReduction.MEAN, + reduction: MetricReduction | str = MetricReduction.MEAN, get_not_nans: bool = False, ) -> None: super().__init__() @@ -94,7 +95,7 @@ def _compute_tensor(self, y_pred: torch.Tensor, y: torch.Tensor): # type: ignor distance_metric=self.distance_metric, ) - def aggregate(self, reduction: Union[MetricReduction, str, None] = None): + def aggregate(self, reduction: MetricReduction | str | None = None): """ Execute reduction logic for the output of `compute_average_surface_distance`. @@ -114,8 +115,8 @@ def aggregate(self, reduction: Union[MetricReduction, str, None] = None): def compute_average_surface_distance( - y_pred: Union[np.ndarray, torch.Tensor], - y: Union[np.ndarray, torch.Tensor], + y_pred: np.ndarray | torch.Tensor, + y: np.ndarray | torch.Tensor, include_background: bool = False, symmetric: bool = False, distance_metric: str = "euclidean", diff --git a/monai/metrics/utils.py b/monai/metrics/utils.py index 6219663756..2732edf544 100644 --- a/monai/metrics/utils.py +++ b/monai/metrics/utils.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Tuple, Union import numpy as np import torch @@ -26,7 +27,7 @@ __all__ = ["ignore_background", "do_metric_reduction", "get_mask_edges", "get_surface_distance", "is_binary_tensor"] -def ignore_background(y_pred: Union[np.ndarray, torch.Tensor], y: Union[np.ndarray, torch.Tensor]): +def ignore_background(y_pred: np.ndarray | torch.Tensor, y: np.ndarray | torch.Tensor): """ This function is used to remove background (the first channel) for `y_pred` and `y`. @@ -43,7 +44,7 @@ def ignore_background(y_pred: Union[np.ndarray, torch.Tensor], y: Union[np.ndarr return y_pred, y -def do_metric_reduction(f: torch.Tensor, reduction: Union[MetricReduction, str] = MetricReduction.MEAN): +def do_metric_reduction(f: torch.Tensor, reduction: MetricReduction | str = MetricReduction.MEAN): """ This function is to do the metric reduction for calculated `not-nan` metrics of each sample's each class. The function also returns `not_nans`, which counts the number of not nans for the metric. @@ -103,7 +104,7 @@ def do_metric_reduction(f: torch.Tensor, reduction: Union[MetricReduction, str] return f, not_nans -def get_mask_edges(seg_pred, seg_gt, label_idx: int = 1, crop: bool = True) -> Tuple[np.ndarray, np.ndarray]: +def get_mask_edges(seg_pred, seg_gt, label_idx: int = 1, crop: bool = True) -> tuple[np.ndarray, np.ndarray]: """ Do binary erosion and use XOR for input to get the edges. This function is helpful to further calculate metrics such as Average Surface diff --git a/monai/networks/__init__.py b/monai/networks/__init__.py index b2dc907c18..b2a70e7ff2 100644 --- a/monai/networks/__init__.py +++ b/monai/networks/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .utils import ( convert_to_torchscript, copy_model_state, diff --git a/monai/networks/blocks/__init__.py b/monai/networks/blocks/__init__.py index 61d5fcf8f5..5db8a2f8df 100644 --- a/monai/networks/blocks/__init__.py +++ b/monai/networks/blocks/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .acti_norm import ADN from .activation import MemoryEfficientSwish, Mish, Swish from .aspp import SimpleASPP diff --git a/monai/networks/blocks/acti_norm.py b/monai/networks/blocks/acti_norm.py index 6aeaa7d275..37bbd32f95 100644 --- a/monai/networks/blocks/acti_norm.py +++ b/monai/networks/blocks/acti_norm.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Tuple, Union +from __future__ import annotations import torch.nn as nn @@ -69,12 +69,12 @@ class ADN(nn.Sequential): def __init__( self, ordering: str = "NDA", - in_channels: Optional[int] = None, - act: Optional[Union[Tuple, str]] = "RELU", - norm: Optional[Union[Tuple, str]] = None, - norm_dim: Optional[int] = None, - dropout: Optional[Union[Tuple, str, float]] = None, - dropout_dim: Optional[int] = None, + in_channels: int | None = None, + act: tuple | str | None = "RELU", + norm: tuple | str | None = None, + norm_dim: int | None = None, + dropout: tuple | str | float | None = None, + dropout_dim: int | None = None, ) -> None: super().__init__() diff --git a/monai/networks/blocks/activation.py b/monai/networks/blocks/activation.py index 1526b37056..22d3c74897 100644 --- a/monai/networks/blocks/activation.py +++ b/monai/networks/blocks/activation.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import torch from torch import nn diff --git a/monai/networks/blocks/aspp.py b/monai/networks/blocks/aspp.py index 8d43530fa7..1f6c76c3af 100644 --- a/monai/networks/blocks/aspp.py +++ b/monai/networks/blocks/aspp.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -37,8 +39,8 @@ def __init__( conv_out_channels: int, kernel_sizes: Sequence[int] = (1, 3, 3, 3), dilations: Sequence[int] = (1, 2, 4, 6), - norm_type: Optional[Union[Tuple, str]] = "BATCH", - acti_type: Optional[Union[Tuple, str]] = "LEAKYRELU", + norm_type: tuple | str | None = "BATCH", + acti_type: tuple | str | None = "LEAKYRELU", bias: bool = False, ) -> None: """ diff --git a/monai/networks/blocks/backbone_fpn_utils.py b/monai/networks/blocks/backbone_fpn_utils.py index 145a4ac2e1..824b31a83b 100644 --- a/monai/networks/blocks/backbone_fpn_utils.py +++ b/monai/networks/blocks/backbone_fpn_utils.py @@ -50,7 +50,7 @@ https://github.com/pytorch/vision/blob/release/0.12/torchvision/models/detection/backbone_utils.py """ -from typing import Dict, List, Optional, Union +from __future__ import annotations from torch import Tensor, nn @@ -89,11 +89,11 @@ class BackboneWithFPN(nn.Module): def __init__( self, backbone: nn.Module, - return_layers: Dict[str, str], - in_channels_list: List[int], + return_layers: dict[str, str], + in_channels_list: list[int], out_channels: int, - spatial_dims: Union[int, None] = None, - extra_blocks: Optional[ExtraFPNBlock] = None, + spatial_dims: int | None = None, + extra_blocks: ExtraFPNBlock | None = None, ) -> None: super().__init__() @@ -120,7 +120,7 @@ def __init__( ) self.out_channels = out_channels - def forward(self, x: Tensor) -> Dict[str, Tensor]: + def forward(self, x: Tensor) -> dict[str, Tensor]: """ Computes the resulted feature maps of the network. @@ -131,7 +131,7 @@ def forward(self, x: Tensor) -> Dict[str, Tensor]: feature maps after FPN layers. They are ordered from highest resolution first. """ x = self.body(x) # backbone - y: Dict[str, Tensor] = self.fpn(x) # FPN + y: dict[str, Tensor] = self.fpn(x) # FPN return y @@ -139,8 +139,8 @@ def _resnet_fpn_extractor( backbone: resnet.ResNet, spatial_dims: int, trainable_layers: int = 5, - returned_layers: Optional[List[int]] = None, - extra_blocks: Optional[ExtraFPNBlock] = None, + returned_layers: list[int] | None = None, + extra_blocks: ExtraFPNBlock | None = None, ) -> BackboneWithFPN: """ Same code as https://github.com/pytorch/vision/blob/release/0.12/torchvision/models/detection/backbone_utils.py diff --git a/monai/networks/blocks/convolutions.py b/monai/networks/blocks/convolutions.py index 55735e2f58..8b18614364 100644 --- a/monai/networks/blocks/convolutions.py +++ b/monai/networks/blocks/convolutions.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import numpy as np import torch @@ -98,20 +100,20 @@ def __init__( spatial_dims: int, in_channels: int, out_channels: int, - strides: Union[Sequence[int], int] = 1, - kernel_size: Union[Sequence[int], int] = 3, + strides: Sequence[int] | int = 1, + kernel_size: Sequence[int] | int = 3, adn_ordering: str = "NDA", - act: Optional[Union[Tuple, str]] = "PRELU", - norm: Optional[Union[Tuple, str]] = "INSTANCE", - dropout: Optional[Union[Tuple, str, float]] = None, - dropout_dim: Optional[int] = 1, - dilation: Union[Sequence[int], int] = 1, + act: tuple | str | None = "PRELU", + norm: tuple | str | None = "INSTANCE", + dropout: tuple | str | float | None = None, + dropout_dim: int | None = 1, + dilation: Sequence[int] | int = 1, groups: int = 1, bias: bool = True, conv_only: bool = False, is_transposed: bool = False, - padding: Optional[Union[Sequence[int], int]] = None, - output_padding: Optional[Union[Sequence[int], int]] = None, + padding: Sequence[int] | int | None = None, + output_padding: Sequence[int] | int | None = None, ) -> None: super().__init__() self.spatial_dims = spatial_dims @@ -248,18 +250,18 @@ def __init__( spatial_dims: int, in_channels: int, out_channels: int, - strides: Union[Sequence[int], int] = 1, - kernel_size: Union[Sequence[int], int] = 3, + strides: Sequence[int] | int = 1, + kernel_size: Sequence[int] | int = 3, subunits: int = 2, adn_ordering: str = "NDA", - act: Optional[Union[Tuple, str]] = "PRELU", - norm: Optional[Union[Tuple, str]] = "INSTANCE", - dropout: Optional[Union[Tuple, str, float]] = None, - dropout_dim: Optional[int] = 1, - dilation: Union[Sequence[int], int] = 1, + act: tuple | str | None = "PRELU", + norm: tuple | str | None = "INSTANCE", + dropout: tuple | str | float | None = None, + dropout_dim: int | None = 1, + dilation: Sequence[int] | int = 1, bias: bool = True, last_conv_only: bool = False, - padding: Optional[Union[Sequence[int], int]] = None, + padding: Sequence[int] | int | None = None, ) -> None: super().__init__() self.spatial_dims = spatial_dims diff --git a/monai/networks/blocks/crf.py b/monai/networks/blocks/crf.py index b6382adf5f..5bbad4dff4 100644 --- a/monai/networks/blocks/crf.py +++ b/monai/networks/blocks/crf.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional +from __future__ import annotations import torch from torch.nn.functional import softmax @@ -44,7 +44,7 @@ def __init__( bilateral_color_sigma: float = 0.5, gaussian_spatial_sigma: float = 5.0, update_factor: float = 3.0, - compatibility_matrix: Optional[torch.Tensor] = None, + compatibility_matrix: torch.Tensor | None = None, ): """ Args: diff --git a/monai/networks/blocks/denseblock.py b/monai/networks/blocks/denseblock.py index dafd8d03a6..dfb39adb34 100644 --- a/monai/networks/blocks/denseblock.py +++ b/monai/networks/blocks/denseblock.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -66,13 +68,13 @@ def __init__( spatial_dims: int, in_channels: int, channels: Sequence[int], - dilations: Optional[Sequence[int]] = None, - kernel_size: Union[Sequence[int], int] = 3, + dilations: Sequence[int] | None = None, + kernel_size: Sequence[int] | int = 3, num_res_units: int = 0, adn_ordering: str = "NDA", - act: Optional[Union[Tuple, str]] = Act.PRELU, - norm: Optional[Union[Tuple, str]] = Norm.INSTANCE, - dropout: Optional[int] = None, + act: tuple | str | None = Act.PRELU, + norm: tuple | str | None = Norm.INSTANCE, + dropout: int | None = None, bias: bool = True, ): diff --git a/monai/networks/blocks/dints_block.py b/monai/networks/blocks/dints_block.py index 1823845adf..d3ac7cf04b 100644 --- a/monai/networks/blocks/dints_block.py +++ b/monai/networks/blocks/dints_block.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Tuple, Union +from __future__ import annotations import torch @@ -29,8 +29,8 @@ def __init__( in_channel: int, out_channel: int, spatial_dims: int = 3, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), ): """ Args: @@ -80,8 +80,8 @@ def __init__( in_channel: int, out_channel: int, spatial_dims: int = 3, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), ): """ Args: @@ -148,8 +148,8 @@ def __init__( kernel_size: int, padding: int, mode: int = 0, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), ): """ Args: @@ -233,8 +233,8 @@ def __init__( kernel_size: int = 3, padding: int = 1, spatial_dims: int = 3, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), ): """ Args: diff --git a/monai/networks/blocks/downsample.py b/monai/networks/blocks/downsample.py index 9b0d5dd4b9..2a6a60ff8a 100644 --- a/monai/networks/blocks/downsample.py +++ b/monai/networks/blocks/downsample.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -27,9 +29,9 @@ class MaxAvgPool(nn.Module): def __init__( self, spatial_dims: int, - kernel_size: Union[Sequence[int], int], - stride: Optional[Union[Sequence[int], int]] = None, - padding: Union[Sequence[int], int] = 0, + kernel_size: Sequence[int] | int, + stride: Sequence[int] | int | None = None, + padding: Sequence[int] | int = 0, ceil_mode: bool = False, ) -> None: """ diff --git a/monai/networks/blocks/dynunet_block.py b/monai/networks/blocks/dynunet_block.py index 894686b50a..6b689cd0ab 100644 --- a/monai/networks/blocks/dynunet_block.py +++ b/monai/networks/blocks/dynunet_block.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import numpy as np import torch @@ -43,11 +45,11 @@ def __init__( spatial_dims: int, in_channels: int, out_channels: int, - kernel_size: Union[Sequence[int], int], - stride: Union[Sequence[int], int], - norm_name: Union[Tuple, str], - act_name: Union[Tuple, str] = ("leakyrelu", {"inplace": True, "negative_slope": 0.01}), - dropout: Optional[Union[Tuple, str, float]] = None, + kernel_size: Sequence[int] | int, + stride: Sequence[int] | int, + norm_name: tuple | str, + act_name: tuple | str = ("leakyrelu", {"inplace": True, "negative_slope": 0.01}), + dropout: tuple | str | float | None = None, ): super().__init__() self.conv1 = get_conv_layer( @@ -132,11 +134,11 @@ def __init__( spatial_dims: int, in_channels: int, out_channels: int, - kernel_size: Union[Sequence[int], int], - stride: Union[Sequence[int], int], - norm_name: Union[Tuple, str], - act_name: Union[Tuple, str] = ("leakyrelu", {"inplace": True, "negative_slope": 0.01}), - dropout: Optional[Union[Tuple, str, float]] = None, + kernel_size: Sequence[int] | int, + stride: Sequence[int] | int, + norm_name: tuple | str, + act_name: tuple | str = ("leakyrelu", {"inplace": True, "negative_slope": 0.01}), + dropout: tuple | str | float | None = None, ): super().__init__() self.conv1 = get_conv_layer( @@ -200,12 +202,12 @@ def __init__( spatial_dims: int, in_channels: int, out_channels: int, - kernel_size: Union[Sequence[int], int], - stride: Union[Sequence[int], int], - upsample_kernel_size: Union[Sequence[int], int], - norm_name: Union[Tuple, str], - act_name: Union[Tuple, str] = ("leakyrelu", {"inplace": True, "negative_slope": 0.01}), - dropout: Optional[Union[Tuple, str, float]] = None, + kernel_size: Sequence[int] | int, + stride: Sequence[int] | int, + upsample_kernel_size: Sequence[int] | int, + norm_name: tuple | str, + act_name: tuple | str = ("leakyrelu", {"inplace": True, "negative_slope": 0.01}), + dropout: tuple | str | float | None = None, trans_bias: bool = False, ): super().__init__() @@ -244,7 +246,7 @@ def forward(self, inp, skip): class UnetOutBlock(nn.Module): def __init__( - self, spatial_dims: int, in_channels: int, out_channels: int, dropout: Optional[Union[Tuple, str, float]] = None + self, spatial_dims: int, in_channels: int, out_channels: int, dropout: tuple | str | float | None = None ): super().__init__() self.conv = get_conv_layer( @@ -268,11 +270,11 @@ def get_conv_layer( spatial_dims: int, in_channels: int, out_channels: int, - kernel_size: Union[Sequence[int], int] = 3, - stride: Union[Sequence[int], int] = 1, - act: Optional[Union[Tuple, str]] = Act.PRELU, - norm: Optional[Union[Tuple, str]] = Norm.INSTANCE, - dropout: Optional[Union[Tuple, str, float]] = None, + kernel_size: Sequence[int] | int = 3, + stride: Sequence[int] | int = 1, + act: tuple | str | None = Act.PRELU, + norm: tuple | str | None = Norm.INSTANCE, + dropout: tuple | str | float | None = None, bias: bool = False, conv_only: bool = True, is_transposed: bool = False, @@ -298,9 +300,7 @@ def get_conv_layer( ) -def get_padding( - kernel_size: Union[Sequence[int], int], stride: Union[Sequence[int], int] -) -> Union[Tuple[int, ...], int]: +def get_padding(kernel_size: Sequence[int] | int, stride: Sequence[int] | int) -> tuple[int, ...] | int: kernel_size_np = np.atleast_1d(kernel_size) stride_np = np.atleast_1d(stride) @@ -313,8 +313,8 @@ def get_padding( def get_output_padding( - kernel_size: Union[Sequence[int], int], stride: Union[Sequence[int], int], padding: Union[Sequence[int], int] -) -> Union[Tuple[int, ...], int]: + kernel_size: Sequence[int] | int, stride: Sequence[int] | int, padding: Sequence[int] | int +) -> tuple[int, ...] | int: kernel_size_np = np.atleast_1d(kernel_size) stride_np = np.atleast_1d(stride) padding_np = np.atleast_1d(padding) diff --git a/monai/networks/blocks/encoder.py b/monai/networks/blocks/encoder.py index 9d4dac8f57..419afec838 100644 --- a/monai/networks/blocks/encoder.py +++ b/monai/networks/blocks/encoder.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from abc import ABCMeta, abstractmethod -from typing import Dict, List, Tuple __all__ = ["BaseEncoder"] @@ -28,7 +29,7 @@ class BaseEncoder(metaclass=ABCMeta): @classmethod @abstractmethod - def get_encoder_parameters(cls) -> List[Dict]: + def get_encoder_parameters(cls) -> list[dict]: """ Get parameter list to initialize encoder networks. Each parameter dict must have `spatial_dims`, `in_channels` @@ -42,7 +43,7 @@ def get_encoder_parameters(cls) -> List[Dict]: @classmethod @abstractmethod - def num_channels_per_output(cls) -> List[Tuple[int, ...]]: + def num_channels_per_output(cls) -> list[tuple[int, ...]]: """ Get number of output features' channels. The reason that this function should return a list is that a @@ -56,7 +57,7 @@ def num_channels_per_output(cls) -> List[Tuple[int, ...]]: @classmethod @abstractmethod - def num_outputs(cls) -> List[int]: + def num_outputs(cls) -> list[int]: """ Get number of outputs of encoder. The reason that this function should return a list is that a @@ -70,7 +71,7 @@ def num_outputs(cls) -> List[int]: @classmethod @abstractmethod - def get_encoder_names(cls) -> List[str]: + def get_encoder_names(cls) -> list[str]: """ Get the name string of encoders which will be used to initialize flexible unet. diff --git a/monai/networks/blocks/fcn.py b/monai/networks/blocks/fcn.py index 5833d4a262..b44ea5f99a 100644 --- a/monai/networks/blocks/fcn.py +++ b/monai/networks/blocks/fcn.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Type +from __future__ import annotations import torch import torch.nn as nn @@ -38,7 +38,7 @@ def __init__(self, inplanes: int, planes: int, ks: int = 7): """ super().__init__() - conv2d_type: Type[nn.Conv2d] = Conv[Conv.CONV, 2] + conv2d_type: type[nn.Conv2d] = Conv[Conv.CONV, 2] self.conv_l1 = conv2d_type(in_channels=inplanes, out_channels=planes, kernel_size=(ks, 1), padding=(ks // 2, 0)) self.conv_l2 = conv2d_type(in_channels=planes, out_channels=planes, kernel_size=(1, ks), padding=(0, ks // 2)) self.conv_r1 = conv2d_type(in_channels=inplanes, out_channels=planes, kernel_size=(1, ks), padding=(0, ks // 2)) @@ -69,9 +69,9 @@ def __init__(self, planes: int): """ super().__init__() - relu_type: Type[nn.ReLU] = Act[Act.RELU] - conv2d_type: Type[nn.Conv2d] = Conv[Conv.CONV, 2] - norm2d_type: Type[nn.BatchNorm2d] = Norm[Norm.BATCH, 2] + relu_type: type[nn.ReLU] = Act[Act.RELU] + conv2d_type: type[nn.Conv2d] = Conv[Conv.CONV, 2] + norm2d_type: type[nn.BatchNorm2d] = Norm[Norm.BATCH, 2] self.bn = norm2d_type(num_features=planes) self.relu = relu_type(inplace=True) @@ -118,7 +118,7 @@ def __init__( ): super().__init__() - conv2d_type: Type[nn.Conv2d] = Conv[Conv.CONV, 2] + conv2d_type: type[nn.Conv2d] = Conv[Conv.CONV, 2] self.upsample_mode = upsample_mode self.conv2d_type = conv2d_type diff --git a/monai/networks/blocks/feature_pyramid_network.py b/monai/networks/blocks/feature_pyramid_network.py index adca3df6b4..cca7342078 100644 --- a/monai/networks/blocks/feature_pyramid_network.py +++ b/monai/networks/blocks/feature_pyramid_network.py @@ -50,8 +50,10 @@ https://github.com/pytorch/vision/blob/release/0.12/torchvision/ops/feature_pyramid_network.py """ +from __future__ import annotations + from collections import OrderedDict -from typing import Callable, Dict, List, Optional, Tuple, Type, Union +from collections.abc import Callable import torch.nn.functional as F from torch import Tensor, nn @@ -68,7 +70,7 @@ class ExtraFPNBlock(nn.Module): Same code as https://github.com/pytorch/vision/blob/release/0.12/torchvision/ops/feature_pyramid_network.py """ - def forward(self, results: List[Tensor], x: List[Tensor], names: List[str]): + def forward(self, results: list[Tensor], x: list[Tensor], names: list[str]): """ Compute extended set of results of the FPN and their names. @@ -92,10 +94,10 @@ class LastLevelMaxPool(ExtraFPNBlock): def __init__(self, spatial_dims: int): super().__init__() - pool_type: Type[Union[nn.MaxPool1d, nn.MaxPool2d, nn.MaxPool3d]] = Pool[Pool.MAX, spatial_dims] + pool_type: type[nn.MaxPool1d | nn.MaxPool2d | nn.MaxPool3d] = Pool[Pool.MAX, spatial_dims] self.maxpool = pool_type(kernel_size=1, stride=2, padding=0) - def forward(self, results: List[Tensor], x: List[Tensor], names: List[str]) -> Tuple[List[Tensor], List[str]]: + def forward(self, results: list[Tensor], x: list[Tensor], names: list[str]) -> tuple[list[Tensor], list[str]]: names.append("pool") results.append(self.maxpool(results[-1])) return results, names @@ -118,7 +120,7 @@ def __init__(self, spatial_dims: int, in_channels: int, out_channels: int): nn.init.constant_(module.bias, 0) self.use_P5 = in_channels == out_channels - def forward(self, results: List[Tensor], x: List[Tensor], names: List[str]) -> Tuple[List[Tensor], List[str]]: + def forward(self, results: list[Tensor], x: list[Tensor], names: list[str]) -> tuple[list[Tensor], list[str]]: p5, c5 = results[-1], x[-1] x5 = p5 if self.use_P5 else c5 p6 = self.p6(x5) @@ -170,9 +172,9 @@ class FeaturePyramidNetwork(nn.Module): def __init__( self, spatial_dims: int, - in_channels_list: List[int], + in_channels_list: list[int], out_channels: int, - extra_blocks: Optional[ExtraFPNBlock] = None, + extra_blocks: ExtraFPNBlock | None = None, ): super().__init__() @@ -189,7 +191,7 @@ def __init__( self.layer_blocks.append(layer_block_module) # initialize parameters now to avoid modifying the initialization of top_blocks - conv_type_: Type[nn.Module] = Conv[Conv.CONV, spatial_dims] + conv_type_: type[nn.Module] = Conv[Conv.CONV, spatial_dims] for m in self.modules(): if isinstance(m, conv_type_): nn.init.kaiming_uniform_(m.weight, a=1) # type: ignore @@ -228,7 +230,7 @@ def get_result_from_layer_blocks(self, x: Tensor, idx: int) -> Tensor: out = module(x) return out - def forward(self, x: Dict[str, Tensor]) -> Dict[str, Tensor]: + def forward(self, x: dict[str, Tensor]) -> dict[str, Tensor]: """ Computes the FPN for a set of feature maps. @@ -240,7 +242,7 @@ def forward(self, x: Dict[str, Tensor]) -> Dict[str, Tensor]: """ # unpack OrderedDict into two lists for easier handling names = list(x.keys()) - x_values: List[Tensor] = list(x.values()) + x_values: list[Tensor] = list(x.values()) last_inner = self.get_result_from_inner_blocks(x_values[-1], -1) results = [] diff --git a/monai/networks/blocks/fft_utils_t.py b/monai/networks/blocks/fft_utils_t.py index 1283f05c6b..2aa4054f64 100644 --- a/monai/networks/blocks/fft_utils_t.py +++ b/monai/networks/blocks/fft_utils_t.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List +from __future__ import annotations import torch from torch import Tensor @@ -42,7 +42,7 @@ def roll_1d(x: Tensor, shift: int, shift_dim: int) -> Tensor: return torch.cat((right, left), dim=shift_dim) -def roll(x: Tensor, shift: List[int], shift_dims: List[int]) -> Tensor: +def roll(x: Tensor, shift: list[int], shift_dims: list[int]) -> Tensor: """ Similar to np.roll but applies to PyTorch Tensors @@ -66,7 +66,7 @@ def roll(x: Tensor, shift: List[int], shift_dims: List[int]) -> Tensor: return x -def fftshift(x: Tensor, shift_dims: List[int]) -> Tensor: +def fftshift(x: Tensor, shift_dims: list[int]) -> Tensor: """ Similar to np.fft.fftshift but applies to PyTorch Tensors @@ -88,7 +88,7 @@ def fftshift(x: Tensor, shift_dims: List[int]) -> Tensor: return roll(x, shift, shift_dims) -def ifftshift(x: Tensor, shift_dims: List[int]) -> Tensor: +def ifftshift(x: Tensor, shift_dims: list[int]) -> Tensor: """ Similar to np.fft.ifftshift but applies to PyTorch Tensors diff --git a/monai/networks/blocks/localnet_block.py b/monai/networks/blocks/localnet_block.py index 9cdd0cecc2..11808eabf7 100644 --- a/monai/networks/blocks/localnet_block.py +++ b/monai/networks/blocks/localnet_block.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Tuple, Type, Union +from __future__ import annotations + +from collections.abc import Sequence import torch from torch import nn @@ -24,9 +26,9 @@ def get_conv_block( spatial_dims: int, in_channels: int, out_channels: int, - kernel_size: Union[Sequence[int], int] = 3, - act: Optional[Union[Tuple, str]] = "RELU", - norm: Optional[Union[Tuple, str]] = "BATCH", + kernel_size: Sequence[int] | int = 3, + act: tuple | str | None = "RELU", + norm: tuple | str | None = "BATCH", ) -> nn.Module: padding = same_padding(kernel_size) mod: nn.Module = Convolution( @@ -44,7 +46,7 @@ def get_conv_block( def get_conv_layer( - spatial_dims: int, in_channels: int, out_channels: int, kernel_size: Union[Sequence[int], int] = 3 + spatial_dims: int, in_channels: int, out_channels: int, kernel_size: Sequence[int] | int = 3 ) -> nn.Module: padding = same_padding(kernel_size) mod: nn.Module = Convolution( @@ -71,7 +73,7 @@ def get_deconv_block(spatial_dims: int, in_channels: int, out_channels: int) -> class ResidualBlock(nn.Module): def __init__( - self, spatial_dims: int, in_channels: int, out_channels: int, kernel_size: Union[Sequence[int], int] + self, spatial_dims: int, in_channels: int, out_channels: int, kernel_size: Sequence[int] | int ) -> None: super().__init__() if in_channels != out_channels: @@ -121,7 +123,7 @@ class LocalNetDownSampleBlock(nn.Module): """ def __init__( - self, spatial_dims: int, in_channels: int, out_channels: int, kernel_size: Union[Sequence[int], int] + self, spatial_dims: int, in_channels: int, out_channels: int, kernel_size: Sequence[int] | int ) -> None: """ Args: @@ -141,7 +143,7 @@ def __init__( ) self.max_pool = Pool[Pool.MAX, spatial_dims](kernel_size=2) - def forward(self, x) -> Tuple[torch.Tensor, torch.Tensor]: + def forward(self, x) -> tuple[torch.Tensor, torch.Tensor]: """ Halves the spatial dimensions. A tuple of (x, mid) is returned: @@ -182,7 +184,7 @@ def __init__( in_channels: int, out_channels: int, mode: str = "nearest", - align_corners: Optional[bool] = None, + align_corners: bool | None = None, ) -> None: """ Args: @@ -261,7 +263,7 @@ def __init__( spatial_dims: int, in_channels: int, out_channels: int, - act: Optional[Union[Tuple, str]] = "RELU", + act: tuple | str | None = "RELU", initializer: str = "kaiming_uniform", ) -> None: """ @@ -276,7 +278,7 @@ def __init__( self.conv_block = get_conv_block( spatial_dims=spatial_dims, in_channels=in_channels, out_channels=out_channels, act=act, norm=None ) - conv_type: Type[Union[nn.Conv1d, nn.Conv2d, nn.Conv3d]] = Conv[Conv.CONV, spatial_dims] + conv_type: type[nn.Conv1d | nn.Conv2d | nn.Conv3d] = Conv[Conv.CONV, spatial_dims] for m in self.conv_block.modules(): if isinstance(m, conv_type): if initializer == "kaiming_uniform": diff --git a/monai/networks/blocks/mlp.py b/monai/networks/blocks/mlp.py index 0feeb044f3..e691112a1e 100644 --- a/monai/networks/blocks/mlp.py +++ b/monai/networks/blocks/mlp.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Tuple, Union +from __future__ import annotations import torch.nn as nn @@ -26,12 +26,7 @@ class MLPBlock(nn.Module): """ def __init__( - self, - hidden_size: int, - mlp_dim: int, - dropout_rate: float = 0.0, - act: Union[Tuple, str] = "GELU", - dropout_mode="vit", + self, hidden_size: int, mlp_dim: int, dropout_rate: float = 0.0, act: tuple | str = "GELU", dropout_mode="vit" ) -> None: """ Args: diff --git a/monai/networks/blocks/patchembedding.py b/monai/networks/blocks/patchembedding.py index a611a30b15..a0699eb108 100644 --- a/monai/networks/blocks/patchembedding.py +++ b/monai/networks/blocks/patchembedding.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Sequence, Type, Union +from __future__ import annotations + +from collections.abc import Sequence import numpy as np import torch @@ -40,8 +42,8 @@ class PatchEmbeddingBlock(nn.Module): def __init__( self, in_channels: int, - img_size: Union[Sequence[int], int], - patch_size: Union[Sequence[int], int], + img_size: Sequence[int] | int, + patch_size: Sequence[int] | int, hidden_size: int, num_heads: int, pos_embed: str, @@ -137,10 +139,10 @@ class PatchEmbed(nn.Module): def __init__( self, - patch_size: Union[Sequence[int], int] = 2, + patch_size: Sequence[int] | int = 2, in_chans: int = 1, embed_dim: int = 48, - norm_layer: Type[LayerNorm] = nn.LayerNorm, + norm_layer: type[LayerNorm] = nn.LayerNorm, spatial_dims: int = 3, ) -> None: """ diff --git a/monai/networks/blocks/regunet_block.py b/monai/networks/blocks/regunet_block.py index 306b57a827..562364373b 100644 --- a/monai/networks/blocks/regunet_block.py +++ b/monai/networks/blocks/regunet_block.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Optional, Sequence, Tuple, Type, Union +from __future__ import annotations + +from collections.abc import Sequence import torch from torch import nn @@ -23,12 +25,12 @@ def get_conv_block( spatial_dims: int, in_channels: int, out_channels: int, - kernel_size: Union[Sequence[int], int] = 3, + kernel_size: Sequence[int] | int = 3, strides: int = 1, - padding: Optional[Union[Tuple[int, ...], int]] = None, - act: Optional[Union[Tuple, str]] = "RELU", - norm: Optional[Union[Tuple, str]] = "BATCH", - initializer: Optional[str] = "kaiming_uniform", + padding: tuple[int, ...] | int | None = None, + act: tuple | str | None = "RELU", + norm: tuple | str | None = "BATCH", + initializer: str | None = "kaiming_uniform", ) -> nn.Module: if padding is None: padding = same_padding(kernel_size) @@ -44,7 +46,7 @@ def get_conv_block( conv_only=False, padding=padding, ) - conv_type: Type[Union[nn.Conv1d, nn.Conv2d, nn.Conv3d]] = Conv[Conv.CONV, spatial_dims] + conv_type: type[nn.Conv1d | nn.Conv2d | nn.Conv3d] = Conv[Conv.CONV, spatial_dims] for m in conv_block.modules(): if isinstance(m, conv_type): if initializer == "kaiming_uniform": @@ -59,7 +61,7 @@ def get_conv_block( def get_conv_layer( - spatial_dims: int, in_channels: int, out_channels: int, kernel_size: Union[Sequence[int], int] = 3 + spatial_dims: int, in_channels: int, out_channels: int, kernel_size: Sequence[int] | int = 3 ) -> nn.Module: padding = same_padding(kernel_size) mod: nn.Module = Convolution( @@ -195,13 +197,13 @@ class RegistrationExtractionBlock(nn.Module): def __init__( self, spatial_dims: int, - extract_levels: Tuple[int], - num_channels: Union[Tuple[int], List[int]], + extract_levels: tuple[int], + num_channels: tuple[int] | list[int], out_channels: int, - kernel_initializer: Optional[str] = "kaiming_uniform", - activation: Optional[str] = None, + kernel_initializer: str | None = "kaiming_uniform", + activation: str | None = None, mode: str = "nearest", - align_corners: Optional[bool] = None, + align_corners: bool | None = None, ): """ @@ -235,7 +237,7 @@ def __init__( self.mode = mode self.align_corners = align_corners - def forward(self, x: List[torch.Tensor], image_size: List[int]) -> torch.Tensor: + def forward(self, x: list[torch.Tensor], image_size: list[int]) -> torch.Tensor: """ Args: diff --git a/monai/networks/blocks/segresnet_block.py b/monai/networks/blocks/segresnet_block.py index ded270ab52..01fc907ab7 100644 --- a/monai/networks/blocks/segresnet_block.py +++ b/monai/networks/blocks/segresnet_block.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Tuple, Union +from __future__ import annotations import torch.nn as nn @@ -29,7 +29,7 @@ def get_conv_layer( def get_upsample_layer( - spatial_dims: int, in_channels: int, upsample_mode: Union[UpsampleMode, str] = "nontrainable", scale_factor: int = 2 + spatial_dims: int, in_channels: int, upsample_mode: UpsampleMode | str = "nontrainable", scale_factor: int = 2 ): return UpSample( spatial_dims=spatial_dims, @@ -53,9 +53,9 @@ def __init__( self, spatial_dims: int, in_channels: int, - norm: Union[Tuple, str], + norm: tuple | str, kernel_size: int = 3, - act: Union[Tuple, str] = ("RELU", {"inplace": True}), + act: tuple | str = ("RELU", {"inplace": True}), ) -> None: """ Args: diff --git a/monai/networks/blocks/selfattention.py b/monai/networks/blocks/selfattention.py index d0b87fda6b..519c8c7728 100644 --- a/monai/networks/blocks/selfattention.py +++ b/monai/networks/blocks/selfattention.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import torch import torch.nn as nn diff --git a/monai/networks/blocks/squeeze_and_excitation.py b/monai/networks/blocks/squeeze_and_excitation.py index a9ac57aa4f..0928cdb641 100644 --- a/monai/networks/blocks/squeeze_and_excitation.py +++ b/monai/networks/blocks/squeeze_and_excitation.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import math -from typing import Dict, Optional, Tuple, Union import torch import torch.nn as nn @@ -30,8 +31,8 @@ def __init__( spatial_dims: int, in_channels: int, r: int = 2, - acti_type_1: Union[Tuple[str, Dict], str] = ("relu", {"inplace": True}), - acti_type_2: Union[Tuple[str, Dict], str] = "sigmoid", + acti_type_1: tuple[str, dict] | str = ("relu", {"inplace": True}), + acti_type_2: tuple[str, dict] | str = "sigmoid", add_residual: bool = False, ) -> None: """ @@ -102,8 +103,8 @@ def __init__( spatial_dims: int, in_channels: int, r: int = 2, - acti_type_1: Union[Tuple[str, Dict], str] = "leakyrelu", - acti_type_2: Union[Tuple[str, Dict], str] = "relu", + acti_type_1: tuple[str, dict] | str = "leakyrelu", + acti_type_2: tuple[str, dict] | str = "relu", ) -> None: """ Args: @@ -145,14 +146,14 @@ def __init__( n_chns_1: int, n_chns_2: int, n_chns_3: int, - conv_param_1: Optional[Dict] = None, - conv_param_2: Optional[Dict] = None, - conv_param_3: Optional[Dict] = None, - project: Optional[Convolution] = None, + conv_param_1: dict | None = None, + conv_param_2: dict | None = None, + conv_param_3: dict | None = None, + project: Convolution | None = None, r: int = 2, - acti_type_1: Union[Tuple[str, Dict], str] = ("relu", {"inplace": True}), - acti_type_2: Union[Tuple[str, Dict], str] = "sigmoid", - acti_type_final: Optional[Union[Tuple[str, Dict], str]] = ("relu", {"inplace": True}), + acti_type_1: tuple[str, dict] | str = ("relu", {"inplace": True}), + acti_type_2: tuple[str, dict] | str = "sigmoid", + acti_type_final: tuple[str, dict] | str | None = ("relu", {"inplace": True}), ): """ Args: @@ -244,7 +245,7 @@ def __init__( groups: int, reduction: int, stride: int = 1, - downsample: Optional[Convolution] = None, + downsample: Convolution | None = None, ) -> None: conv_param_1 = { @@ -295,7 +296,7 @@ def __init__( groups: int, reduction: int, stride: int = 1, - downsample: Optional[Convolution] = None, + downsample: Convolution | None = None, ) -> None: conv_param_1 = { @@ -344,7 +345,7 @@ def __init__( groups: int, reduction: int, stride: int = 1, - downsample: Optional[Convolution] = None, + downsample: Convolution | None = None, base_width: int = 4, ) -> None: diff --git a/monai/networks/blocks/transformerblock.py b/monai/networks/blocks/transformerblock.py index 88b33acb09..3a4b507d69 100644 --- a/monai/networks/blocks/transformerblock.py +++ b/monai/networks/blocks/transformerblock.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import torch.nn as nn from monai.networks.blocks.mlp import MLPBlock diff --git a/monai/networks/blocks/unetr_block.py b/monai/networks/blocks/unetr_block.py index 452a535a2a..13ea0ad4c8 100644 --- a/monai/networks/blocks/unetr_block.py +++ b/monai/networks/blocks/unetr_block.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -28,9 +30,9 @@ def __init__( spatial_dims: int, in_channels: int, out_channels: int, - kernel_size: Union[Sequence[int], int], - upsample_kernel_size: Union[Sequence[int], int], - norm_name: Union[Tuple, str], + kernel_size: Sequence[int] | int, + upsample_kernel_size: Sequence[int] | int, + norm_name: tuple | str, res_block: bool = False, ) -> None: """ @@ -96,10 +98,10 @@ def __init__( in_channels: int, out_channels: int, num_layer: int, - kernel_size: Union[Sequence[int], int], - stride: Union[Sequence[int], int], - upsample_kernel_size: Union[Sequence[int], int], - norm_name: Union[Tuple, str], + kernel_size: Sequence[int] | int, + stride: Sequence[int] | int, + upsample_kernel_size: Sequence[int] | int, + norm_name: tuple | str, conv_block: bool = False, res_block: bool = False, ) -> None: @@ -215,9 +217,9 @@ def __init__( spatial_dims: int, in_channels: int, out_channels: int, - kernel_size: Union[Sequence[int], int], - stride: Union[Sequence[int], int], - norm_name: Union[Tuple, str], + kernel_size: Sequence[int] | int, + stride: Sequence[int] | int, + norm_name: tuple | str, res_block: bool = False, ) -> None: """ diff --git a/monai/networks/blocks/upsample.py b/monai/networks/blocks/upsample.py index 425cb743a5..dee9966919 100644 --- a/monai/networks/blocks/upsample.py +++ b/monai/networks/blocks/upsample.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -41,15 +43,15 @@ class UpSample(nn.Sequential): def __init__( self, spatial_dims: int, - in_channels: Optional[int] = None, - out_channels: Optional[int] = None, - scale_factor: Union[Sequence[float], float] = 2, - kernel_size: Optional[Union[Sequence[float], float]] = None, - size: Optional[Union[Tuple[int], int]] = None, - mode: Union[UpsampleMode, str] = UpsampleMode.DECONV, - pre_conv: Optional[Union[nn.Module, str]] = "default", + in_channels: int | None = None, + out_channels: int | None = None, + scale_factor: Sequence[float] | float = 2, + kernel_size: Sequence[float] | float | None = None, + size: tuple[int] | int | None = None, + mode: UpsampleMode | str = UpsampleMode.DECONV, + pre_conv: nn.Module | str | None = "default", interp_mode: str = InterpolateMode.LINEAR, - align_corners: Optional[bool] = True, + align_corners: bool | None = True, bias: bool = True, apply_pad_pool: bool = True, ) -> None: @@ -206,10 +208,10 @@ class SubpixelUpsample(nn.Module): def __init__( self, spatial_dims: int, - in_channels: Optional[int], - out_channels: Optional[int] = None, + in_channels: int | None, + out_channels: int | None = None, scale_factor: int = 2, - conv_block: Optional[Union[nn.Module, str]] = "default", + conv_block: nn.Module | str | None = "default", apply_pad_pool: bool = True, bias: bool = True, ) -> None: diff --git a/monai/networks/blocks/warp.py b/monai/networks/blocks/warp.py index 018d694407..10a115eff8 100644 --- a/monai/networks/blocks/warp.py +++ b/monai/networks/blocks/warp.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import List import torch from torch import nn @@ -122,7 +123,7 @@ def forward(self, image: torch.Tensor, ddf: torch.Tensor): if not USE_COMPILED: # pytorch native grid_sample for i, dim in enumerate(grid.shape[1:-1]): grid[..., i] = grid[..., i] * 2 / (dim - 1) - 1 - index_ordering: List[int] = list(range(spatial_dims - 1, -1, -1)) + index_ordering: list[int] = list(range(spatial_dims - 1, -1, -1)) grid = grid[..., index_ordering] # z, y, x -> x, y, z return F.grid_sample( image, grid, mode=self._interp_mode, padding_mode=f"{self._padding_mode}", align_corners=True diff --git a/monai/networks/layers/__init__.py b/monai/networks/layers/__init__.py index 5d91cf66f6..1d62a19e8a 100644 --- a/monai/networks/layers/__init__.py +++ b/monai/networks/layers/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .convutils import calculate_out_shape, gaussian_1d, polyval, same_padding, stride_minus_kernel_padding from .drop_path import DropPath from .factories import Act, Conv, Dropout, LayerFactory, Norm, Pad, Pool, split_args diff --git a/monai/networks/layers/convutils.py b/monai/networks/layers/convutils.py index fe688b24ff..fc8ea03809 100644 --- a/monai/networks/layers/convutils.py +++ b/monai/networks/layers/convutils.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import numpy as np import torch @@ -17,9 +19,7 @@ __all__ = ["same_padding", "stride_minus_kernel_padding", "calculate_out_shape", "gaussian_1d", "polyval"] -def same_padding( - kernel_size: Union[Sequence[int], int], dilation: Union[Sequence[int], int] = 1 -) -> Union[Tuple[int, ...], int]: +def same_padding(kernel_size: Sequence[int] | int, dilation: Sequence[int] | int = 1) -> tuple[int, ...] | int: """ Return the padding value needed to ensure a convolution using the given kernel size produces an output of the same shape as the input for a stride of 1, otherwise ensure a shape of the input divided by the stride rounded down. @@ -43,9 +43,7 @@ def same_padding( return padding if len(padding) > 1 else padding[0] -def stride_minus_kernel_padding( - kernel_size: Union[Sequence[int], int], stride: Union[Sequence[int], int] -) -> Union[Tuple[int, ...], int]: +def stride_minus_kernel_padding(kernel_size: Sequence[int] | int, stride: Sequence[int] | int) -> tuple[int, ...] | int: kernel_size_np = np.atleast_1d(kernel_size) stride_np = np.atleast_1d(stride) @@ -56,11 +54,11 @@ def stride_minus_kernel_padding( def calculate_out_shape( - in_shape: Union[Sequence[int], int, np.ndarray], - kernel_size: Union[Sequence[int], int], - stride: Union[Sequence[int], int], - padding: Union[Sequence[int], int], -) -> Union[Tuple[int, ...], int]: + in_shape: Sequence[int] | int | np.ndarray, + kernel_size: Sequence[int] | int, + stride: Sequence[int] | int, + padding: Sequence[int] | int, +) -> tuple[int, ...] | int: """ Calculate the output tensor shape when applying a convolution to a tensor of shape `inShape` with kernel size `kernel_size`, stride value `stride`, and input padding value `padding`. All arguments can be scalars or multiple @@ -120,7 +118,7 @@ def gaussian_1d( out = out / (2.5066282 * sigma) elif approx.lower() == "scalespace": sigma2 = sigma * sigma - out_pos: List[Optional[torch.Tensor]] = [None] * (tail + 1) + out_pos: list[torch.Tensor | None] = [None] * (tail + 1) out_pos[0] = _modified_bessel_0(sigma2) out_pos[1] = _modified_bessel_1(sigma2) for k in range(2, len(out_pos)): diff --git a/monai/networks/layers/drop_path.py b/monai/networks/layers/drop_path.py index 7bb209ed25..6073ca4402 100644 --- a/monai/networks/layers/drop_path.py +++ b/monai/networks/layers/drop_path.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import torch.nn as nn diff --git a/monai/networks/layers/factories.py b/monai/networks/layers/factories.py index b6c64c6c19..038b85dbca 100644 --- a/monai/networks/layers/factories.py +++ b/monai/networks/layers/factories.py @@ -59,8 +59,11 @@ def use_factory(fact_args): layer = use_factory( (fact.TEST, kwargs) ) """ +from __future__ import annotations + import warnings -from typing import Any, Callable, Dict, Tuple, Type, Union +from collections.abc import Callable +from typing import Any import torch import torch.nn as nn @@ -79,10 +82,10 @@ class LayerFactory: """ def __init__(self) -> None: - self.factories: Dict[str, Callable] = {} + self.factories: dict[str, Callable] = {} @property - def names(self) -> Tuple[str, ...]: + def names(self) -> tuple[str, ...]: """ Produces all factory names. """ @@ -203,7 +206,7 @@ def split_args(args): @Dropout.factory_function("dropout") -def dropout_factory(dim: int) -> Type[Union[nn.Dropout, nn.Dropout2d, nn.Dropout3d]]: +def dropout_factory(dim: int) -> type[nn.Dropout | nn.Dropout2d | nn.Dropout3d]: types = (nn.Dropout, nn.Dropout2d, nn.Dropout3d) return types[dim - 1] @@ -214,34 +217,34 @@ def alpha_dropout_factory(_dim): @Norm.factory_function("instance") -def instance_factory(dim: int) -> Type[Union[nn.InstanceNorm1d, nn.InstanceNorm2d, nn.InstanceNorm3d]]: +def instance_factory(dim: int) -> type[nn.InstanceNorm1d | nn.InstanceNorm2d | nn.InstanceNorm3d]: types = (nn.InstanceNorm1d, nn.InstanceNorm2d, nn.InstanceNorm3d) return types[dim - 1] @Norm.factory_function("batch") -def batch_factory(dim: int) -> Type[Union[nn.BatchNorm1d, nn.BatchNorm2d, nn.BatchNorm3d]]: +def batch_factory(dim: int) -> type[nn.BatchNorm1d | nn.BatchNorm2d | nn.BatchNorm3d]: types = (nn.BatchNorm1d, nn.BatchNorm2d, nn.BatchNorm3d) return types[dim - 1] @Norm.factory_function("group") -def group_factory(_dim) -> Type[nn.GroupNorm]: +def group_factory(_dim) -> type[nn.GroupNorm]: return nn.GroupNorm @Norm.factory_function("layer") -def layer_factory(_dim) -> Type[nn.LayerNorm]: +def layer_factory(_dim) -> type[nn.LayerNorm]: return nn.LayerNorm @Norm.factory_function("localresponse") -def local_response_factory(_dim) -> Type[nn.LocalResponseNorm]: +def local_response_factory(_dim) -> type[nn.LocalResponseNorm]: return nn.LocalResponseNorm @Norm.factory_function("syncbatch") -def sync_batch_factory(_dim) -> Type[nn.SyncBatchNorm]: +def sync_batch_factory(_dim) -> type[nn.SyncBatchNorm]: return nn.SyncBatchNorm @@ -318,52 +321,48 @@ def mish_factory(): @Conv.factory_function("conv") -def conv_factory(dim: int) -> Type[Union[nn.Conv1d, nn.Conv2d, nn.Conv3d]]: +def conv_factory(dim: int) -> type[nn.Conv1d | nn.Conv2d | nn.Conv3d]: types = (nn.Conv1d, nn.Conv2d, nn.Conv3d) return types[dim - 1] @Conv.factory_function("convtrans") -def convtrans_factory(dim: int) -> Type[Union[nn.ConvTranspose1d, nn.ConvTranspose2d, nn.ConvTranspose3d]]: +def convtrans_factory(dim: int) -> type[nn.ConvTranspose1d | nn.ConvTranspose2d | nn.ConvTranspose3d]: types = (nn.ConvTranspose1d, nn.ConvTranspose2d, nn.ConvTranspose3d) return types[dim - 1] @Pool.factory_function("max") -def maxpooling_factory(dim: int) -> Type[Union[nn.MaxPool1d, nn.MaxPool2d, nn.MaxPool3d]]: +def maxpooling_factory(dim: int) -> type[nn.MaxPool1d | nn.MaxPool2d | nn.MaxPool3d]: types = (nn.MaxPool1d, nn.MaxPool2d, nn.MaxPool3d) return types[dim - 1] @Pool.factory_function("adaptivemax") -def adaptive_maxpooling_factory( - dim: int, -) -> Type[Union[nn.AdaptiveMaxPool1d, nn.AdaptiveMaxPool2d, nn.AdaptiveMaxPool3d]]: +def adaptive_maxpooling_factory(dim: int) -> type[nn.AdaptiveMaxPool1d | nn.AdaptiveMaxPool2d | nn.AdaptiveMaxPool3d]: types = (nn.AdaptiveMaxPool1d, nn.AdaptiveMaxPool2d, nn.AdaptiveMaxPool3d) return types[dim - 1] @Pool.factory_function("avg") -def avgpooling_factory(dim: int) -> Type[Union[nn.AvgPool1d, nn.AvgPool2d, nn.AvgPool3d]]: +def avgpooling_factory(dim: int) -> type[nn.AvgPool1d | nn.AvgPool2d | nn.AvgPool3d]: types = (nn.AvgPool1d, nn.AvgPool2d, nn.AvgPool3d) return types[dim - 1] @Pool.factory_function("adaptiveavg") -def adaptive_avgpooling_factory( - dim: int, -) -> Type[Union[nn.AdaptiveAvgPool1d, nn.AdaptiveAvgPool2d, nn.AdaptiveAvgPool3d]]: +def adaptive_avgpooling_factory(dim: int) -> type[nn.AdaptiveAvgPool1d | nn.AdaptiveAvgPool2d | nn.AdaptiveAvgPool3d]: types = (nn.AdaptiveAvgPool1d, nn.AdaptiveAvgPool2d, nn.AdaptiveAvgPool3d) return types[dim - 1] @Pad.factory_function("replicationpad") -def replication_pad_factory(dim: int) -> Type[Union[nn.ReplicationPad1d, nn.ReplicationPad2d, nn.ReplicationPad3d]]: +def replication_pad_factory(dim: int) -> type[nn.ReplicationPad1d | nn.ReplicationPad2d | nn.ReplicationPad3d]: types = (nn.ReplicationPad1d, nn.ReplicationPad2d, nn.ReplicationPad3d) return types[dim - 1] @Pad.factory_function("constantpad") -def constant_pad_factory(dim: int) -> Type[Union[nn.ConstantPad1d, nn.ConstantPad2d, nn.ConstantPad3d]]: +def constant_pad_factory(dim: int) -> type[nn.ConstantPad1d | nn.ConstantPad2d | nn.ConstantPad3d]: types = (nn.ConstantPad1d, nn.ConstantPad2d, nn.ConstantPad3d) return types[dim - 1] diff --git a/monai/networks/layers/filtering.py b/monai/networks/layers/filtering.py index bbf925eba9..c0ddd64a87 100644 --- a/monai/networks/layers/filtering.py +++ b/monai/networks/layers/filtering.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import torch from monai.utils.module import optional_import diff --git a/monai/networks/layers/gmm.py b/monai/networks/layers/gmm.py index eb9a3f91e4..94d619bb7a 100644 --- a/monai/networks/layers/gmm.py +++ b/monai/networks/layers/gmm.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import torch from monai._extensions.loader import load_module diff --git a/monai/networks/layers/simplelayers.py b/monai/networks/layers/simplelayers.py index 8a5f00fe98..c5398b533e 100644 --- a/monai/networks/layers/simplelayers.py +++ b/monai/networks/layers/simplelayers.py @@ -9,9 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import math from copy import deepcopy -from typing import List, Optional, Sequence, Union +from typing import Sequence import torch import torch.nn.functional as F @@ -58,11 +60,7 @@ class ChannelPad(nn.Module): """ def __init__( - self, - spatial_dims: int, - in_channels: int, - out_channels: int, - mode: Union[ChannelMatching, str] = ChannelMatching.PAD, + self, spatial_dims: int, in_channels: int, out_channels: int, mode: ChannelMatching | str = ChannelMatching.PAD ): """ @@ -113,7 +111,7 @@ class SkipConnection(nn.Module): The available modes are ``"cat"``, ``"add"``, ``"mul"``. """ - def __init__(self, submodule, dim: int = 1, mode: Union[str, SkipMode] = "cat") -> None: + def __init__(self, submodule, dim: int = 1, mode: str | SkipMode = "cat") -> None: """ Args: @@ -172,11 +170,11 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: def _separable_filtering_conv( input_: torch.Tensor, - kernels: List[torch.Tensor], + kernels: list[torch.Tensor], pad_mode: str, d: int, spatial_dims: int, - paddings: List[int], + paddings: list[int], num_channels: int, ) -> torch.Tensor: if d < 0: @@ -196,8 +194,8 @@ def _separable_filtering_conv( conv_type = [F.conv1d, F.conv2d, F.conv3d][spatial_dims - 1] # translate padding for input to torch.nn.functional.pad - _reversed_padding_repeated_twice: List[List[int]] = [[p, p] for p in reversed(_padding)] - _sum_reversed_padding_repeated_twice: List[int] = sum(_reversed_padding_repeated_twice, []) + _reversed_padding_repeated_twice: list[list[int]] = [[p, p] for p in reversed(_padding)] + _sum_reversed_padding_repeated_twice: list[int] = sum(_reversed_padding_repeated_twice, []) padded_input = F.pad(input_, _sum_reversed_padding_repeated_twice, mode=pad_mode) return conv_type( @@ -207,7 +205,7 @@ def _separable_filtering_conv( ) -def separable_filtering(x: torch.Tensor, kernels: List[torch.Tensor], mode: str = "zeros") -> torch.Tensor: +def separable_filtering(x: torch.Tensor, kernels: list[torch.Tensor], mode: str = "zeros") -> torch.Tensor: """ Apply 1-D convolutions along each spatial dimension of `x`. @@ -392,7 +390,7 @@ class HilbertTransform(nn.Module): n: Number of Fourier components (i.e. FFT size). Default: ``x.shape[axis]``. """ - def __init__(self, axis: int = 2, n: Union[int, None] = None) -> None: + def __init__(self, axis: int = 2, n: int | None = None) -> None: super().__init__() self.axis = axis @@ -459,7 +457,7 @@ def median_filter( in_tensor: torch.Tensor, kernel_size: Sequence[int] = (3, 3, 3), spatial_dims: int = 3, - kernel: Optional[torch.Tensor] = None, + kernel: torch.Tensor | None = None, **kwargs, ) -> torch.Tensor: """ @@ -537,7 +535,7 @@ class MedianFilter(nn.Module): """ - def __init__(self, radius: Union[Sequence[int], int], spatial_dims: int = 3, device="cpu") -> None: + def __init__(self, radius: Sequence[int] | int, spatial_dims: int = 3, device="cpu") -> None: super().__init__() self.spatial_dims = spatial_dims self.radius: Sequence[int] = ensure_tuple_rep(radius, spatial_dims) @@ -560,7 +558,7 @@ class GaussianFilter(nn.Module): def __init__( self, spatial_dims: int, - sigma: Union[Sequence[float], float, Sequence[torch.Tensor], torch.Tensor], + sigma: Sequence[float] | float | Sequence[torch.Tensor] | torch.Tensor, truncated: float = 4.0, approx: str = "erf", requires_grad: bool = False, diff --git a/monai/networks/layers/spatial_transforms.py b/monai/networks/layers/spatial_transforms.py index d1a0fed021..ff5b0a3b89 100644 --- a/monai/networks/layers/spatial_transforms.py +++ b/monai/networks/layers/spatial_transforms.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -433,13 +435,13 @@ def grid_grad(input: torch.Tensor, grid: torch.Tensor, interpolation="linear", b class AffineTransform(nn.Module): def __init__( self, - spatial_size: Optional[Union[Sequence[int], int]] = None, + spatial_size: Sequence[int] | int | None = None, normalized: bool = False, mode: str = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.ZEROS, align_corners: bool = False, reverse_indexing: bool = True, - zero_centered: Optional[bool] = None, + zero_centered: bool | None = None, ) -> None: """ Apply affine transformations with a batch of affine matrices. @@ -493,7 +495,7 @@ def __init__( self.zero_centered = zero_centered if zero_centered is not None else False def forward( - self, src: torch.Tensor, theta: torch.Tensor, spatial_size: Optional[Union[Sequence[int], int]] = None + self, src: torch.Tensor, theta: torch.Tensor, spatial_size: Sequence[int] | int | None = None ) -> torch.Tensor: """ ``theta`` must be an affine transformation matrix with shape diff --git a/monai/networks/layers/utils.py b/monai/networks/layers/utils.py index a630a5edc7..ace1af27b6 100644 --- a/monai/networks/layers/utils.py +++ b/monai/networks/layers/utils.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Tuple, Union +from __future__ import annotations import torch.nn @@ -19,7 +19,7 @@ __all__ = ["get_norm_layer", "get_act_layer", "get_dropout_layer", "get_pool_layer"] -def get_norm_layer(name: Union[Tuple, str], spatial_dims: Optional[int] = 1, channels: Optional[int] = 1): +def get_norm_layer(name: tuple | str, spatial_dims: int | None = 1, channels: int | None = 1): """ Create a normalization layer instance. @@ -50,7 +50,7 @@ def get_norm_layer(name: Union[Tuple, str], spatial_dims: Optional[int] = 1, cha return norm_type(**kw_args) -def get_act_layer(name: Union[Tuple, str]): +def get_act_layer(name: tuple | str): """ Create an activation layer instance. @@ -73,7 +73,7 @@ def get_act_layer(name: Union[Tuple, str]): return act_type(**act_args) -def get_dropout_layer(name: Union[Tuple, str, float, int], dropout_dim: Optional[int] = 1): +def get_dropout_layer(name: tuple | str | float | int, dropout_dim: int | None = 1): """ Create a dropout layer instance. @@ -102,7 +102,7 @@ def get_dropout_layer(name: Union[Tuple, str, float, int], dropout_dim: Optional return drop_type(**drop_args) -def get_pool_layer(name: Union[Tuple, str], spatial_dims: Optional[int] = 1): +def get_pool_layer(name: tuple | str, spatial_dims: int | None = 1): """ Create a pooling layer instance. diff --git a/monai/networks/layers/weight_init.py b/monai/networks/layers/weight_init.py index b0c6fae2c2..f413e19ca3 100644 --- a/monai/networks/layers/weight_init.py +++ b/monai/networks/layers/weight_init.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import math import torch diff --git a/monai/networks/nets/__init__.py b/monai/networks/nets/__init__.py index 18a85d802a..95ddad7842 100644 --- a/monai/networks/nets/__init__.py +++ b/monai/networks/nets/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .ahnet import AHnet, Ahnet, AHNet from .attentionunet import AttentionUnet from .autoencoder import AutoEncoder diff --git a/monai/networks/nets/ahnet.py b/monai/networks/nets/ahnet.py index 65d85a2054..0080180d75 100644 --- a/monai/networks/nets/ahnet.py +++ b/monai/networks/nets/ahnet.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import math -from typing import Optional, Sequence, Type, Union +from collections.abc import Sequence import torch import torch.nn as nn @@ -31,16 +33,16 @@ def __init__( spatial_dims: int, inplanes: int, planes: int, - stride: Union[Sequence[int], int] = 1, - downsample: Optional[nn.Sequential] = None, + stride: Sequence[int] | int = 1, + downsample: nn.Sequential | None = None, ) -> None: super().__init__() conv_type = Conv[Conv.CONV, spatial_dims] - norm_type: Type[Union[nn.BatchNorm2d, nn.BatchNorm3d]] = Norm[Norm.BATCH, spatial_dims] - pool_type: Type[Union[nn.MaxPool2d, nn.MaxPool3d]] = Pool[Pool.MAX, spatial_dims] - relu_type: Type[nn.ReLU] = Act[Act.RELU] + norm_type: type[nn.BatchNorm2d | nn.BatchNorm3d] = Norm[Norm.BATCH, spatial_dims] + pool_type: type[nn.MaxPool2d | nn.MaxPool3d] = Pool[Pool.MAX, spatial_dims] + relu_type: type[nn.ReLU] = Act[Act.RELU] self.conv1 = conv_type(inplanes, planes, kernel_size=1, bias=False) self.bn1 = norm_type(planes) @@ -90,8 +92,8 @@ def __init__(self, spatial_dims: int, num_input_features: int, num_output_featur super().__init__() conv_type = Conv[Conv.CONV, spatial_dims] - norm_type: Type[Union[nn.BatchNorm2d, nn.BatchNorm3d]] = Norm[Norm.BATCH, spatial_dims] - relu_type: Type[nn.ReLU] = Act[Act.RELU] + norm_type: type[nn.BatchNorm2d | nn.BatchNorm3d] = Norm[Norm.BATCH, spatial_dims] + relu_type: type[nn.ReLU] = Act[Act.RELU] self.add_module("norm", norm_type(num_input_features)) self.add_module("relu", relu_type(inplace=True)) @@ -123,8 +125,8 @@ def __init__( super().__init__() conv_type = Conv[Conv.CONV, spatial_dims] - norm_type: Type[Union[nn.BatchNorm2d, nn.BatchNorm3d]] = Norm[Norm.BATCH, spatial_dims] - relu_type: Type[nn.ReLU] = Act[Act.RELU] + norm_type: type[nn.BatchNorm2d | nn.BatchNorm3d] = Norm[Norm.BATCH, spatial_dims] + relu_type: type[nn.ReLU] = Act[Act.RELU] self.add_module("norm", norm_type(num_input_features)) self.add_module("relu", relu_type(inplace=True)) @@ -135,7 +137,7 @@ def __init__( "up", conv_trans_type(num_output_features, num_output_features, kernel_size=2, stride=2, bias=False) ) else: - align_corners: Optional[bool] = None + align_corners: bool | None = None if upsample_mode in ["trilinear", "bilinear"]: align_corners = True self.add_module("up", nn.Upsample(scale_factor=2, mode=upsample_mode, align_corners=align_corners)) @@ -148,8 +150,8 @@ def __init__( super().__init__() conv_type = Conv[Conv.CONV, spatial_dims] - norm_type: Type[Union[nn.BatchNorm2d, nn.BatchNorm3d]] = Norm[Norm.BATCH, spatial_dims] - relu_type: Type[nn.ReLU] = Act[Act.RELU] + norm_type: type[nn.BatchNorm2d | nn.BatchNorm3d] = Norm[Norm.BATCH, spatial_dims] + relu_type: type[nn.ReLU] = Act[Act.RELU] self.add_module("norm", norm_type(num_input_features)) self.add_module("relu", relu_type(inplace=True)) @@ -170,7 +172,7 @@ def __init__( "up", conv_trans_type(num_output_features, num_output_features, kernel_size=2, stride=2, bias=False) ) else: - align_corners: Optional[bool] = None + align_corners: bool | None = None if upsample_mode in ["trilinear", "bilinear"]: align_corners = True self.add_module("up", nn.Upsample(scale_factor=2, mode=upsample_mode, align_corners=align_corners)) @@ -182,8 +184,8 @@ def __init__(self, spatial_dims: int, num_input_features: int, growth_rate: int, # 1x1x1 conv_type = Conv[Conv.CONV, spatial_dims] - norm_type: Type[Union[nn.BatchNorm2d, nn.BatchNorm3d]] = Norm[Norm.BATCH, spatial_dims] - relu_type: Type[nn.ReLU] = Act[Act.RELU] + norm_type: type[nn.BatchNorm2d | nn.BatchNorm3d] = Norm[Norm.BATCH, spatial_dims] + relu_type: type[nn.ReLU] = Act[Act.RELU] self.bn1 = norm_type(num_input_features) self.relu1 = relu_type(inplace=True) @@ -247,7 +249,7 @@ def __init__(self, spatial_dims: int, psp_block_num: int, in_ch: int, upsample_m super().__init__() self.up_modules = nn.ModuleList() conv_type = Conv[Conv.CONV, spatial_dims] - pool_type: Type[Union[nn.MaxPool2d, nn.MaxPool3d]] = Pool[Pool.MAX, spatial_dims] + pool_type: type[nn.MaxPool2d | nn.MaxPool3d] = Pool[Pool.MAX, spatial_dims] self.pool_modules = nn.ModuleList() self.project_modules = nn.ModuleList() @@ -281,7 +283,7 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: else: for (project_module, pool_module) in zip(self.project_modules, self.pool_modules): interpolate_size = x.shape[2:] - align_corners: Optional[bool] = None + align_corners: bool | None = None if self.upsample_mode in ["trilinear", "bilinear"]: align_corners = True output = F.interpolate( @@ -347,10 +349,10 @@ def __init__( conv_type = Conv[Conv.CONV, spatial_dims] conv_trans_type = Conv[Conv.CONVTRANS, spatial_dims] norm_type = Norm[Norm.BATCH, spatial_dims] - pool_type: Type[Union[nn.MaxPool2d, nn.MaxPool3d]] = Pool[Pool.MAX, spatial_dims] - relu_type: Type[nn.ReLU] = Act[Act.RELU] - conv2d_type: Type[nn.Conv2d] = Conv[Conv.CONV, 2] - norm2d_type: Type[nn.BatchNorm2d] = Norm[Norm.BATCH, 2] + pool_type: type[nn.MaxPool2d | nn.MaxPool3d] = Pool[Pool.MAX, spatial_dims] + relu_type: type[nn.ReLU] = Act[Act.RELU] + conv2d_type: type[nn.Conv2d] = Conv[Conv.CONV, 2] + norm2d_type: type[nn.BatchNorm2d] = Norm[Norm.BATCH, 2] self.conv2d_type = conv2d_type self.norm2d_type = norm2d_type @@ -437,7 +439,7 @@ def __init__( net2d = FCN(pretrained=True, progress=progress) self.copy_from(net2d) - def _make_layer(self, block: Type[Bottleneck3x3x1], planes: int, blocks: int, stride: int = 1) -> nn.Sequential: + def _make_layer(self, block: type[Bottleneck3x3x1], planes: int, blocks: int, stride: int = 1) -> nn.Sequential: downsample = None if stride != 1 or self.inplanes != planes * block.expansion: downsample = nn.Sequential( diff --git a/monai/networks/nets/attentionunet.py b/monai/networks/nets/attentionunet.py index a57b57425e..362d63d636 100644 --- a/monai/networks/nets/attentionunet.py +++ b/monai/networks/nets/attentionunet.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -200,8 +202,8 @@ def __init__( out_channels: int, channels: Sequence[int], strides: Sequence[int], - kernel_size: Union[Sequence[int], int] = 3, - up_kernel_size: Union[Sequence[int], int] = 3, + kernel_size: Sequence[int] | int = 3, + up_kernel_size: Sequence[int] | int = 3, dropout: float = 0.0, ): super().__init__() diff --git a/monai/networks/nets/autoencoder.py b/monai/networks/nets/autoencoder.py index a88d1861ad..19906f3ba8 100644 --- a/monai/networks/nets/autoencoder.py +++ b/monai/networks/nets/autoencoder.py @@ -9,7 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence +from typing import Any import torch import torch.nn as nn @@ -91,15 +94,15 @@ def __init__( out_channels: int, channels: Sequence[int], strides: Sequence[int], - kernel_size: Union[Sequence[int], int] = 3, - up_kernel_size: Union[Sequence[int], int] = 3, + kernel_size: Sequence[int] | int = 3, + up_kernel_size: Sequence[int] | int = 3, num_res_units: int = 0, - inter_channels: Optional[list] = None, - inter_dilations: Optional[list] = None, + inter_channels: list | None = None, + inter_dilations: list | None = None, num_inter_units: int = 2, - act: Optional[Union[Tuple, str]] = Act.PRELU, - norm: Union[Tuple, str] = Norm.INSTANCE, - dropout: Optional[Union[Tuple, str, float]] = None, + act: tuple | str | None = Act.PRELU, + norm: tuple | str = Norm.INSTANCE, + dropout: tuple | str | float | None = None, bias: bool = True, ) -> None: @@ -133,7 +136,7 @@ def __init__( def _get_encode_module( self, in_channels: int, channels: Sequence[int], strides: Sequence[int] - ) -> Tuple[nn.Sequential, int]: + ) -> tuple[nn.Sequential, int]: """ Returns the encode part of the network by building up a sequence of layers returned by `_get_encode_layer`. """ @@ -147,7 +150,7 @@ def _get_encode_module( return encode, layer_channels - def _get_intermediate_module(self, in_channels: int, num_inter_units: int) -> Tuple[nn.Module, int]: + def _get_intermediate_module(self, in_channels: int, num_inter_units: int) -> tuple[nn.Module, int]: """ Returns the intermediate block of the network which accepts input from the encoder and whose output goes to the decoder. @@ -198,7 +201,7 @@ def _get_intermediate_module(self, in_channels: int, num_inter_units: int) -> Tu def _get_decode_module( self, in_channels: int, channels: Sequence[int], strides: Sequence[int] - ) -> Tuple[nn.Sequential, int]: + ) -> tuple[nn.Sequential, int]: """ Returns the decode part of the network by building up a sequence of layers returned by `_get_decode_layer`. """ diff --git a/monai/networks/nets/basic_unet.py b/monai/networks/nets/basic_unet.py index 6fe77038fe..2185219b25 100644 --- a/monai/networks/nets/basic_unet.py +++ b/monai/networks/nets/basic_unet.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -29,10 +31,10 @@ def __init__( spatial_dims: int, in_chns: int, out_chns: int, - act: Union[str, tuple], - norm: Union[str, tuple], + act: str | tuple, + norm: str | tuple, bias: bool, - dropout: Union[float, tuple] = 0.0, + dropout: float | tuple = 0.0, ): """ Args: @@ -63,10 +65,10 @@ def __init__( spatial_dims: int, in_chns: int, out_chns: int, - act: Union[str, tuple], - norm: Union[str, tuple], + act: str | tuple, + norm: str | tuple, bias: bool, - dropout: Union[float, tuple] = 0.0, + dropout: float | tuple = 0.0, ): """ Args: @@ -95,14 +97,14 @@ def __init__( in_chns: int, cat_chns: int, out_chns: int, - act: Union[str, tuple], - norm: Union[str, tuple], + act: str | tuple, + norm: str | tuple, bias: bool, - dropout: Union[float, tuple] = 0.0, + dropout: float | tuple = 0.0, upsample: str = "deconv", - pre_conv: Optional[Union[nn.Module, str]] = "default", + pre_conv: nn.Module | str | None = "default", interp_mode: str = "linear", - align_corners: Optional[bool] = True, + align_corners: bool | None = True, halves: bool = True, is_pad: bool = True, ): @@ -147,7 +149,7 @@ def __init__( self.convs = TwoConv(spatial_dims, cat_chns + up_chns, out_chns, act, norm, bias, dropout) self.is_pad = is_pad - def forward(self, x: torch.Tensor, x_e: Optional[torch.Tensor]): + def forward(self, x: torch.Tensor, x_e: torch.Tensor | None): """ Args: @@ -182,12 +184,12 @@ def __init__( in_channels: int = 1, out_channels: int = 2, features: Sequence[int] = (32, 32, 64, 128, 256, 32), - act: Union[str, tuple] = ("LeakyReLU", {"negative_slope": 0.1, "inplace": True}), - norm: Union[str, tuple] = ("instance", {"affine": True}), + act: str | tuple = ("LeakyReLU", {"negative_slope": 0.1, "inplace": True}), + norm: str | tuple = ("instance", {"affine": True}), bias: bool = True, - dropout: Union[float, tuple] = 0.0, + dropout: float | tuple = 0.0, upsample: str = "deconv", - dimensions: Optional[int] = None, + dimensions: int | None = None, ): """ A UNet implementation with 1D/2D/3D supports. diff --git a/monai/networks/nets/basic_unetplusplus.py b/monai/networks/nets/basic_unetplusplus.py index 4f7d319aaa..28d4b4668a 100644 --- a/monai/networks/nets/basic_unetplusplus.py +++ b/monai/networks/nets/basic_unetplusplus.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -29,10 +31,10 @@ def __init__( out_channels: int = 2, features: Sequence[int] = (32, 32, 64, 128, 256, 32), deep_supervision: bool = False, - act: Union[str, tuple] = ("LeakyReLU", {"negative_slope": 0.1, "inplace": True}), - norm: Union[str, tuple] = ("instance", {"affine": True}), + act: str | tuple = ("LeakyReLU", {"negative_slope": 0.1, "inplace": True}), + norm: str | tuple = ("instance", {"affine": True}), bias: bool = True, - dropout: Union[float, tuple] = 0.0, + dropout: float | tuple = 0.0, upsample: str = "deconv", ): """ diff --git a/monai/networks/nets/classifier.py b/monai/networks/nets/classifier.py index 7f4e43eedb..dcedaf3e70 100644 --- a/monai/networks/nets/classifier.py +++ b/monai/networks/nets/classifier.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -46,13 +48,13 @@ def __init__( classes: int, channels: Sequence[int], strides: Sequence[int], - kernel_size: Union[Sequence[int], int] = 3, + kernel_size: Sequence[int] | int = 3, num_res_units: int = 2, act=Act.PRELU, norm=Norm.INSTANCE, - dropout: Optional[float] = None, + dropout: float | None = None, bias: bool = True, - last_act: Optional[str] = None, + last_act: str | None = None, ) -> None: super().__init__(in_shape, (classes,), channels, strides, kernel_size, num_res_units, act, norm, dropout, bias) @@ -86,11 +88,11 @@ def __init__( in_shape: Sequence[int], channels: Sequence[int], strides: Sequence[int], - kernel_size: Union[Sequence[int], int] = 3, + kernel_size: Sequence[int] | int = 3, num_res_units: int = 2, act=Act.PRELU, norm=Norm.INSTANCE, - dropout: Optional[float] = 0.25, + dropout: float | None = 0.25, bias: bool = True, last_act=Act.SIGMOID, ) -> None: @@ -120,11 +122,11 @@ def __init__( in_shape: Sequence[int], channels: Sequence[int], strides: Sequence[int], - kernel_size: Union[Sequence[int], int] = 3, + kernel_size: Sequence[int] | int = 3, num_res_units: int = 2, act=Act.PRELU, norm=Norm.INSTANCE, - dropout: Optional[float] = 0.25, + dropout: float | None = 0.25, bias: bool = True, ) -> None: super().__init__(in_shape, 1, channels, strides, kernel_size, num_res_units, act, norm, dropout, bias, None) diff --git a/monai/networks/nets/densenet.py b/monai/networks/nets/densenet.py index 2f02ecf395..40924cbc9a 100644 --- a/monai/networks/nets/densenet.py +++ b/monai/networks/nets/densenet.py @@ -9,9 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import re from collections import OrderedDict -from typing import Callable, Sequence, Type, Union +from collections.abc import Callable, Sequence import torch import torch.nn as nn @@ -47,8 +49,8 @@ def __init__( growth_rate: int, bn_size: int, dropout_prob: float, - act: Union[str, tuple] = ("relu", {"inplace": True}), - norm: Union[str, tuple] = "batch", + act: str | tuple = ("relu", {"inplace": True}), + norm: str | tuple = "batch", ) -> None: """ Args: @@ -94,8 +96,8 @@ def __init__( bn_size: int, growth_rate: int, dropout_prob: float, - act: Union[str, tuple] = ("relu", {"inplace": True}), - norm: Union[str, tuple] = "batch", + act: str | tuple = ("relu", {"inplace": True}), + norm: str | tuple = "batch", ) -> None: """ Args: @@ -122,8 +124,8 @@ def __init__( spatial_dims: int, in_channels: int, out_channels: int, - act: Union[str, tuple] = ("relu", {"inplace": True}), - norm: Union[str, tuple] = "batch", + act: str | tuple = ("relu", {"inplace": True}), + norm: str | tuple = "batch", ) -> None: """ Args: @@ -175,16 +177,16 @@ def __init__( growth_rate: int = 32, block_config: Sequence[int] = (6, 12, 24, 16), bn_size: int = 4, - act: Union[str, tuple] = ("relu", {"inplace": True}), - norm: Union[str, tuple] = "batch", + act: str | tuple = ("relu", {"inplace": True}), + norm: str | tuple = "batch", dropout_prob: float = 0.0, ) -> None: super().__init__() - conv_type: Type[Union[nn.Conv1d, nn.Conv2d, nn.Conv3d]] = Conv[Conv.CONV, spatial_dims] - pool_type: Type[Union[nn.MaxPool1d, nn.MaxPool2d, nn.MaxPool3d]] = Pool[Pool.MAX, spatial_dims] - avg_pool_type: Type[Union[nn.AdaptiveAvgPool1d, nn.AdaptiveAvgPool2d, nn.AdaptiveAvgPool3d]] = Pool[ + conv_type: type[nn.Conv1d | nn.Conv2d | nn.Conv3d] = Conv[Conv.CONV, spatial_dims] + pool_type: type[nn.MaxPool1d | nn.MaxPool2d | nn.MaxPool3d] = Pool[Pool.MAX, spatial_dims] + avg_pool_type: type[nn.AdaptiveAvgPool1d | nn.AdaptiveAvgPool2d | nn.AdaptiveAvgPool3d] = Pool[ Pool.ADAPTIVEAVG, spatial_dims ] diff --git a/monai/networks/nets/dints.py b/monai/networks/nets/dints.py index b62fe2b747..a0c3b070a0 100644 --- a/monai/networks/nets/dints.py +++ b/monai/networks/nets/dints.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import List, Optional, Tuple, Union import numpy as np import torch @@ -103,8 +104,8 @@ def __init__( kernel_size: int, padding: int, spatial_dims: int = 3, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), ): super().__init__(in_channel, out_channel, kernel_size, padding, spatial_dims, act_name, norm_name) self.ram_cost = 1 + in_channel / out_channel * 2 @@ -118,8 +119,8 @@ def __init__( kernel_size: int, padding: int, p3dmode: int = 0, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), ): super().__init__(in_channel, out_channel, kernel_size, padding, p3dmode, act_name, norm_name) # 1 in_channel (activation) + 1 in_channel (convolution) + @@ -133,8 +134,8 @@ def __init__( in_channel: int, out_channel: int, spatial_dims: int = 3, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), ): super().__init__(in_channel, out_channel, spatial_dims, act_name, norm_name) # s0 is upsampled 2x from s1, representing feature sizes at two resolutions. @@ -149,8 +150,8 @@ def __init__( in_channel: int, out_channel: int, spatial_dims: int = 3, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), ): super().__init__(in_channel, out_channel, spatial_dims, act_name, norm_name) # s0 is upsampled 2x from s1, representing feature sizes at two resolutions. @@ -244,8 +245,8 @@ def __init__( rate: int, arch_code_c=None, spatial_dims: int = 3, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), ): super().__init__() self._spatial_dims = spatial_dims @@ -357,8 +358,8 @@ def __init__( dints_space, in_channels: int, num_classes: int, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), spatial_dims: int = 3, use_downsample: bool = True, node_a=None, @@ -555,14 +556,14 @@ class TopologyConstruction(nn.Module): def __init__( self, - arch_code: Optional[list] = None, + arch_code: list | None = None, channel_mul: float = 1.0, cell=Cell, num_blocks: int = 6, num_depths: int = 3, spatial_dims: int = 3, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), use_downsample: bool = True, device: str = "cpu", ): @@ -638,8 +639,8 @@ def __init__( num_blocks: int = 6, num_depths: int = 3, spatial_dims: int = 3, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), use_downsample: bool = True, device: str = "cpu", ): @@ -662,7 +663,7 @@ def __init__( device=device, ) - def forward(self, x: List[torch.Tensor]) -> List[torch.Tensor]: + def forward(self, x: list[torch.Tensor]) -> list[torch.Tensor]: """ Args: x: input tensor. @@ -729,19 +730,19 @@ class TopologySearch(TopologyConstruction): The return value will exclude path activation of all 0. """ - node2out: List[List] - node2in: List[List] + node2out: list[list] + node2in: list[list] def __init__( self, channel_mul: float = 1.0, cell=Cell, - arch_code: Optional[list] = None, + arch_code: list | None = None, num_blocks: int = 6, num_depths: int = 3, spatial_dims: int = 3, - act_name: Union[Tuple, str] = "RELU", - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), + act_name: tuple | str = "RELU", + norm_name: tuple | str = ("INSTANCE", {"affine": True}), use_downsample: bool = True, device: str = "cpu", ): diff --git a/monai/networks/nets/dynunet.py b/monai/networks/nets/dynunet.py index 4eaba4bc42..a761f5993a 100644 --- a/monai/networks/nets/dynunet.py +++ b/monai/networks/nets/dynunet.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Optional, Sequence, Tuple, Type, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -30,7 +32,7 @@ class DynUNetSkipLayer(nn.Module): forward passes of the network. """ - heads: Optional[List[torch.Tensor]] + heads: list[torch.Tensor] | None def __init__(self, index, downsample, upsample, next_layer, heads=None, super_head=None): super().__init__() @@ -130,13 +132,13 @@ def __init__( spatial_dims: int, in_channels: int, out_channels: int, - kernel_size: Sequence[Union[Sequence[int], int]], - strides: Sequence[Union[Sequence[int], int]], - upsample_kernel_size: Sequence[Union[Sequence[int], int]], - filters: Optional[Sequence[int]] = None, - dropout: Optional[Union[Tuple, str, float]] = None, - norm_name: Union[Tuple, str] = ("INSTANCE", {"affine": True}), - act_name: Union[Tuple, str] = ("leakyrelu", {"inplace": True, "negative_slope": 0.01}), + kernel_size: Sequence[Sequence[int] | int], + strides: Sequence[Sequence[int] | int], + upsample_kernel_size: Sequence[Sequence[int] | int], + filters: Sequence[int] | None = None, + dropout: tuple | str | float | None = None, + norm_name: tuple | str = ("INSTANCE", {"affine": True}), + act_name: tuple | str = ("leakyrelu", {"inplace": True, "negative_slope": 0.01}), deep_supervision: bool = False, deep_supr_num: int = 1, res_block: bool = False, @@ -167,7 +169,7 @@ def __init__( self.deep_supervision = deep_supervision self.deep_supr_num = deep_supr_num # initialize the typed list of supervision head outputs so that Torchscript can recognize what's going on - self.heads: List[torch.Tensor] = [torch.rand(1)] * self.deep_supr_num + self.heads: list[torch.Tensor] = [torch.rand(1)] * self.deep_supr_num if self.deep_supervision: self.deep_supervision_heads = self.get_deep_supervision_heads() self.check_deep_supr_num() @@ -317,10 +319,10 @@ def get_module_list( self, in_channels: Sequence[int], out_channels: Sequence[int], - kernel_size: Sequence[Union[Sequence[int], int]], - strides: Sequence[Union[Sequence[int], int]], - conv_block: Type[nn.Module], - upsample_kernel_size: Optional[Sequence[Union[Sequence[int], int]]] = None, + kernel_size: Sequence[Sequence[int] | int], + strides: Sequence[Sequence[int] | int], + conv_block: type[nn.Module], + upsample_kernel_size: Sequence[Sequence[int] | int] | None = None, trans_bias: bool = False, ): layers = [] diff --git a/monai/networks/nets/efficientnet.py b/monai/networks/nets/efficientnet.py index eef3d68090..7c2a507fea 100644 --- a/monai/networks/nets/efficientnet.py +++ b/monai/networks/nets/efficientnet.py @@ -9,11 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import math import operator import re from functools import reduce -from typing import Dict, List, NamedTuple, Optional, Tuple, Type, Union +from typing import NamedTuple import torch from torch import nn @@ -78,12 +80,12 @@ def __init__( out_channels: int, kernel_size: int, stride: int, - image_size: List[int], + image_size: list[int], expand_ratio: int, - se_ratio: Optional[float], - id_skip: Optional[bool] = True, - norm: Union[str, tuple] = ("batch", {"eps": 1e-3, "momentum": 0.01}), - drop_connect_rate: Optional[float] = 0.2, + se_ratio: float | None, + id_skip: bool | None = True, + norm: str | tuple = ("batch", {"eps": 1e-3, "momentum": 0.01}), + drop_connect_rate: float | None = 0.2, ) -> None: """ Mobile Inverted Residual Bottleneck Block. @@ -227,7 +229,7 @@ def set_swish(self, memory_efficient: bool = True) -> None: class EfficientNet(nn.Module): def __init__( self, - blocks_args_str: List[str], + blocks_args_str: list[str], spatial_dims: int = 2, in_channels: int = 3, num_classes: int = 1000, @@ -235,7 +237,7 @@ def __init__( depth_coefficient: float = 1.0, dropout_rate: float = 0.2, image_size: int = 224, - norm: Union[str, tuple] = ("batch", {"eps": 1e-3, "momentum": 0.01}), + norm: str | tuple = ("batch", {"eps": 1e-3, "momentum": 0.01}), drop_connect_rate: float = 0.2, depth_divisor: int = 8, ) -> None: @@ -264,8 +266,8 @@ def __init__( # select the type of N-Dimensional layers to use # these are based on spatial dims and selected from MONAI factories - conv_type: Type[Union[nn.Conv1d, nn.Conv2d, nn.Conv3d]] = Conv["conv", spatial_dims] - adaptivepool_type: Type[Union[nn.AdaptiveAvgPool1d, nn.AdaptiveAvgPool2d, nn.AdaptiveAvgPool3d]] = Pool[ + conv_type: type[nn.Conv1d | nn.Conv2d | nn.Conv3d] = Conv["conv", spatial_dims] + adaptivepool_type: type[nn.AdaptiveAvgPool1d | nn.AdaptiveAvgPool2d | nn.AdaptiveAvgPool3d] = Pool[ "adaptiveavg", spatial_dims ] @@ -478,7 +480,7 @@ def __init__( spatial_dims: int = 2, in_channels: int = 3, num_classes: int = 1000, - norm: Union[str, tuple] = ("batch", {"eps": 1e-3, "momentum": 0.01}), + norm: str | tuple = ("batch", {"eps": 1e-3, "momentum": 0.01}), adv_prop: bool = False, ) -> None: """ @@ -564,7 +566,7 @@ def __init__( spatial_dims: int = 2, in_channels: int = 3, num_classes: int = 1000, - norm: Union[str, tuple] = ("batch", {"eps": 1e-3, "momentum": 0.01}), + norm: str | tuple = ("batch", {"eps": 1e-3, "momentum": 0.01}), adv_prop: bool = False, ) -> None: """ @@ -653,7 +655,7 @@ class EfficientNetEncoder(EfficientNetBNFeatures, BaseEncoder): ] @classmethod - def get_encoder_parameters(cls) -> List[Dict]: + def get_encoder_parameters(cls) -> list[dict]: """ Get the initialization parameter for efficientnet backbones. """ @@ -674,7 +676,7 @@ def get_encoder_parameters(cls) -> List[Dict]: return parameter_list @classmethod - def num_channels_per_output(cls) -> List[Tuple[int, ...]]: + def num_channels_per_output(cls) -> list[tuple[int, ...]]: """ Get number of efficientnet backbone output feature maps' channel. """ @@ -692,7 +694,7 @@ def num_channels_per_output(cls) -> List[Tuple[int, ...]]: ] @classmethod - def num_outputs(cls) -> List[int]: + def num_outputs(cls) -> list[int]: """ Get number of efficientnet backbone output feature maps. Since every backbone contains the same 5 output feature maps, @@ -701,7 +703,7 @@ def num_outputs(cls) -> List[int]: return [5] * 10 @classmethod - def get_encoder_names(cls) -> List[str]: + def get_encoder_names(cls) -> list[str]: """ Get names of efficient backbone. """ @@ -762,7 +764,7 @@ def drop_connect(inputs: torch.Tensor, p: float, training: bool) -> torch.Tensor num_dims: int = len(inputs.shape) - 2 # build dimensions for random tensor, use num_dims to populate appropriate spatial dims - random_tensor_shape: List[int] = [batch_size, 1] + [1] * num_dims + random_tensor_shape: list[int] = [batch_size, 1] + [1] * num_dims # generate binary_tensor mask according to probability (p for 0, 1-p for 1) random_tensor: torch.Tensor = torch.rand(random_tensor_shape, dtype=inputs.dtype, device=inputs.device) @@ -798,8 +800,8 @@ def _load_state_dict(model: nn.Module, arch: str, progress: bool, adv_prop: bool def _get_same_padding_conv_nd( - image_size: List[int], kernel_size: Tuple[int, ...], dilation: Tuple[int, ...], stride: Tuple[int, ...] -) -> List[int]: + image_size: list[int], kernel_size: tuple[int, ...], dilation: tuple[int, ...], stride: tuple[int, ...] +) -> list[int]: """ Helper for getting padding (nn.ConstantPadNd) to be used to get SAME padding conv operations similar to Tensorflow's SAME padding. @@ -826,20 +828,20 @@ def _get_same_padding_conv_nd( stride = stride * num_dims # equation to calculate (pad^+ + pad^-) size - _pad_size: List[int] = [ + _pad_size: list[int] = [ max((math.ceil(_i_s / _s) - 1) * _s + (_k_s - 1) * _d + 1 - _i_s, 0) for _i_s, _k_s, _d, _s in zip(image_size, kernel_size, dilation, stride) ] # distribute paddings into pad^+ and pad^- following Tensorflow's same padding strategy - _paddings: List[Tuple[int, int]] = [(_p // 2, _p - _p // 2) for _p in _pad_size] + _paddings: list[tuple[int, int]] = [(_p // 2, _p - _p // 2) for _p in _pad_size] # unroll list of tuples to tuples, and then to list # reversed as nn.ConstantPadNd expects paddings starting with last dimension - _paddings_ret: List[int] = [outer for inner in reversed(_paddings) for outer in inner] + _paddings_ret: list[int] = [outer for inner in reversed(_paddings) for outer in inner] return _paddings_ret -def _make_same_padder(conv_op: Union[nn.Conv1d, nn.Conv2d, nn.Conv3d], image_size: List[int]): +def _make_same_padder(conv_op: nn.Conv1d | nn.Conv2d | nn.Conv3d, image_size: list[int]): """ Helper for initializing ConstantPadNd with SAME padding similar to Tensorflow. Uses output of _get_same_padding_conv_nd() to get the padding size. @@ -854,7 +856,7 @@ def _make_same_padder(conv_op: Union[nn.Conv1d, nn.Conv2d, nn.Conv3d], image_siz If padding required then nn.ConstandNd() padder initialized to paddings otherwise nn.Identity() """ # calculate padding required - padding: List[int] = _get_same_padding_conv_nd(image_size, conv_op.kernel_size, conv_op.dilation, conv_op.stride) + padding: list[int] = _get_same_padding_conv_nd(image_size, conv_op.kernel_size, conv_op.dilation, conv_op.stride) # initialize and return padder padder = Pad["constantpad", len(padding) // 2] @@ -863,7 +865,7 @@ def _make_same_padder(conv_op: Union[nn.Conv1d, nn.Conv2d, nn.Conv3d], image_siz return nn.Identity() -def _round_filters(filters: int, width_coefficient: Optional[float], depth_divisor: float) -> int: +def _round_filters(filters: int, width_coefficient: float | None, depth_divisor: float) -> int: """ Calculate and round number of filters based on width coefficient multiplier and depth divisor. @@ -890,7 +892,7 @@ def _round_filters(filters: int, width_coefficient: Optional[float], depth_divis return int(new_filters) -def _round_repeats(repeats: int, depth_coefficient: Optional[float]) -> int: +def _round_repeats(repeats: int, depth_coefficient: float | None) -> int: """ Re-calculate module's repeat number of a block based on depth coefficient multiplier. @@ -908,7 +910,7 @@ def _round_repeats(repeats: int, depth_coefficient: Optional[float]) -> int: return int(math.ceil(depth_coefficient * repeats)) -def _calculate_output_image_size(input_image_size: List[int], stride: Union[int, Tuple[int]]): +def _calculate_output_image_size(input_image_size: list[int], stride: int | tuple[int]): """ Calculates the output image size when using _make_same_padder with a stride. Required for static padding. @@ -946,7 +948,7 @@ class BlockArgs(NamedTuple): input_filters: int output_filters: int id_skip: bool - se_ratio: Optional[float] = None + se_ratio: float | None = None @staticmethod def from_string(block_string: str): diff --git a/monai/networks/nets/flexible_unet.py b/monai/networks/nets/flexible_unet.py index 28e0cedaa0..6de629fc6b 100644 --- a/monai/networks/nets/flexible_unet.py +++ b/monai/networks/nets/flexible_unet.py @@ -9,9 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings +from collections.abc import Sequence from pydoc import locate -from typing import List, Optional, Sequence, Tuple, Type, Union +from typing import Any import torch from torch import nn @@ -38,7 +41,7 @@ class FlexUNetEncoderRegister: def __init__(self): self.register_dict = {} - def register_class(self, name: Union[Type, str]): + def register_class(self, name: type[Any] | str): """ Register a given class to the encoder dict. Please notice that input class must be a subclass of BaseEncoder. @@ -110,14 +113,14 @@ def __init__( spatial_dims: int, encoder_channels: Sequence[int], decoder_channels: Sequence[int], - act: Union[str, tuple], - norm: Union[str, tuple], - dropout: Union[float, tuple], + act: str | tuple, + norm: str | tuple, + dropout: float | tuple, bias: bool, upsample: str, - pre_conv: Optional[str], + pre_conv: str | None, interp_mode: str, - align_corners: Optional[bool], + align_corners: bool | None, is_pad: bool, ): @@ -153,7 +156,7 @@ def __init__( ) self.blocks = nn.ModuleList(blocks) - def forward(self, features: List[torch.Tensor], skip_connect: int = 4): + def forward(self, features: list[torch.Tensor], skip_connect: int = 4): skips = features[:-1][::-1] features = features[1:][::-1] @@ -190,7 +193,7 @@ def __init__( in_channels: int, out_channels: int, kernel_size: int = 3, - act: Optional[Union[Tuple, str]] = None, + act: tuple | str | None = None, scale_factor: float = 1.0, ): @@ -224,11 +227,11 @@ def __init__( out_channels: int, backbone: str, pretrained: bool = False, - decoder_channels: Tuple = (256, 128, 64, 32, 16), + decoder_channels: tuple = (256, 128, 64, 32, 16), spatial_dims: int = 2, - norm: Union[str, tuple] = ("batch", {"eps": 1e-3, "momentum": 0.1}), - act: Union[str, tuple] = ("relu", {"inplace": True}), - dropout: Union[float, tuple] = 0.0, + norm: str | tuple = ("batch", {"eps": 1e-3, "momentum": 0.1}), + act: str | tuple = ("relu", {"inplace": True}), + dropout: float | tuple = 0.0, decoder_bias: bool = False, upsample: str = "nontrainable", interp_mode: str = "nearest", diff --git a/monai/networks/nets/fullyconnectednet.py b/monai/networks/nets/fullyconnectednet.py index 810c07431b..fe9a8a0cd6 100644 --- a/monai/networks/nets/fullyconnectednet.py +++ b/monai/networks/nets/fullyconnectednet.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -20,9 +22,7 @@ __all__ = ["FullyConnectedNet", "VarFullyConnectedNet"] -def _get_adn_layer( - act: Optional[Union[Tuple, str]], dropout: Optional[Union[Tuple, str, float]], ordering: Optional[str] -) -> ADN: +def _get_adn_layer(act: tuple | str | None, dropout: tuple | str | float | None, ordering: str | None) -> ADN: if ordering: return ADN(act=act, dropout=dropout, dropout_dim=1, ordering=ordering) return ADN(act=act, dropout=dropout, dropout_dim=1) @@ -55,10 +55,10 @@ def __init__( in_channels: int, out_channels: int, hidden_channels: Sequence[int], - dropout: Optional[Union[Tuple, str, float]] = None, - act: Optional[Union[Tuple, str]] = Act.PRELU, + dropout: tuple | str | float | None = None, + act: tuple | str | None = Act.PRELU, bias: bool = True, - adn_ordering: Optional[str] = None, + adn_ordering: str | None = None, ) -> None: """ Defines a network accept input with `in_channels` channels, output of `out_channels` channels, and hidden layers @@ -118,10 +118,10 @@ def __init__( latent_size: int, encode_channels: Sequence[int], decode_channels: Sequence[int], - dropout: Optional[Union[Tuple, str, float]] = None, - act: Optional[Union[Tuple, str]] = Act.PRELU, + dropout: tuple | str | float | None = None, + act: tuple | str | None = Act.PRELU, bias: bool = True, - adn_ordering: Optional[str] = None, + adn_ordering: str | None = None, ) -> None: super().__init__() self.in_channels = in_channels @@ -154,7 +154,7 @@ def _get_layer(self, in_channels: int, out_channels: int, bias: bool) -> nn.Sequ seq.add_module("ADN", self.adn_layer) return seq - def encode_forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: + def encode_forward(self, x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: x = self.encode(x) x = self.flatten(x) mu = self.mu(x) @@ -179,7 +179,7 @@ def reparameterize(self, mu: torch.Tensor, logvar: torch.Tensor) -> torch.Tensor return std.add_(mu) - def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: + def forward(self, x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: mu, logvar = self.encode_forward(x) z = self.reparameterize(mu, logvar) return self.decode_forward(z), mu, logvar, z diff --git a/monai/networks/nets/generator.py b/monai/networks/nets/generator.py index a69cae4d7b..32428b2696 100644 --- a/monai/networks/nets/generator.py +++ b/monai/networks/nets/generator.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import numpy as np import torch @@ -62,11 +64,11 @@ def __init__( start_shape: Sequence[int], channels: Sequence[int], strides: Sequence[int], - kernel_size: Union[Sequence[int], int] = 3, + kernel_size: Sequence[int] | int = 3, num_res_units: int = 2, act=Act.PRELU, norm=Norm.INSTANCE, - dropout: Optional[float] = None, + dropout: float | None = None, bias: bool = True, ) -> None: super().__init__() @@ -100,14 +102,14 @@ def __init__( def _get_layer( self, in_channels: int, out_channels: int, strides: int, is_last: bool - ) -> Union[Convolution, nn.Sequential]: + ) -> Convolution | nn.Sequential: """ Returns a layer accepting inputs with `in_channels` number of channels and producing outputs of `out_channels` number of channels. The `strides` indicates upsampling factor, ie. transpose convolutional stride. If `is_last` is True this is the final layer and is not expected to include activation and normalization layers. """ - layer: Union[Convolution, nn.Sequential] + layer: Convolution | nn.Sequential layer = Convolution( in_channels=in_channels, diff --git a/monai/networks/nets/highresnet.py b/monai/networks/nets/highresnet.py index 891a65e67b..7dd99172e1 100644 --- a/monai/networks/nets/highresnet.py +++ b/monai/networks/nets/highresnet.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -40,11 +42,11 @@ def __init__( in_channels: int, out_channels: int, kernels: Sequence[int] = (3, 3), - dilation: Union[Sequence[int], int] = 1, - norm_type: Union[Tuple, str] = ("batch", {"affine": True}), - acti_type: Union[Tuple, str] = ("relu", {"inplace": True}), + dilation: Sequence[int] | int = 1, + norm_type: tuple | str = ("batch", {"affine": True}), + acti_type: tuple | str = ("relu", {"inplace": True}), bias: bool = False, - channel_matching: Union[ChannelMatching, str] = ChannelMatching.PAD, + channel_matching: ChannelMatching | str = ChannelMatching.PAD, ) -> None: """ Args: @@ -138,12 +140,12 @@ def __init__( spatial_dims: int = 3, in_channels: int = 1, out_channels: int = 1, - norm_type: Union[str, tuple] = ("batch", {"affine": True}), - acti_type: Union[str, tuple] = ("relu", {"inplace": True}), - dropout_prob: Optional[Union[Tuple, str, float]] = 0.0, + norm_type: str | tuple = ("batch", {"affine": True}), + acti_type: str | tuple = ("relu", {"inplace": True}), + dropout_prob: tuple | str | float | None = 0.0, bias: bool = False, - layer_params: Sequence[Dict] = DEFAULT_LAYER_PARAMS_3D, - channel_matching: Union[ChannelMatching, str] = ChannelMatching.PAD, + layer_params: Sequence[dict] = DEFAULT_LAYER_PARAMS_3D, + channel_matching: ChannelMatching | str = ChannelMatching.PAD, ) -> None: super().__init__() diff --git a/monai/networks/nets/hovernet.py b/monai/networks/nets/hovernet.py index f6cb491511..4ab106f674 100644 --- a/monai/networks/nets/hovernet.py +++ b/monai/networks/nets/hovernet.py @@ -27,11 +27,13 @@ # } # ========================================================================= +from __future__ import annotations + import os import re import warnings from collections import OrderedDict -from typing import Callable, Dict, List, Optional, Sequence, Type, Union +from collections.abc import Callable, Sequence import torch import torch.nn as nn @@ -53,8 +55,8 @@ def __init__( in_channels: int, out_channels: int, dropout_prob: float = 0.0, - act: Union[str, tuple] = ("relu", {"inplace": True}), - norm: Union[str, tuple] = "batch", + act: str | tuple = ("relu", {"inplace": True}), + norm: str | tuple = "batch", kernel_size: int = 3, padding: int = 0, ) -> None: @@ -109,8 +111,8 @@ def __init__( in_channels: int, out_channels: int, dropout_prob: float = 0.0, - act: Union[str, tuple] = ("relu", {"inplace": True}), - norm: Union[str, tuple] = "batch", + act: str | tuple = ("relu", {"inplace": True}), + norm: str | tuple = "batch", kernel_size: int = 3, same_padding: bool = False, ) -> None: @@ -164,8 +166,8 @@ def __init__( in_channels: int, out_channels: int, dropout_prob: float = 0.0, - act: Union[str, tuple] = ("relu", {"inplace": True}), - norm: Union[str, tuple] = "batch", + act: str | tuple = ("relu", {"inplace": True}), + norm: str | tuple = "batch", drop_first_norm_relu: int = 0, kernel_size: int = 3, ) -> None: @@ -219,7 +221,7 @@ def __init__( class _Transition(nn.Sequential): def __init__( - self, in_channels: int, act: Union[str, tuple] = ("relu", {"inplace": True}), norm: Union[str, tuple] = "batch" + self, in_channels: int, act: str | tuple = ("relu", {"inplace": True}), norm: str | tuple = "batch" ) -> None: """ Args: @@ -241,8 +243,8 @@ def __init__( in_channels: int, out_channels: int, dropout_prob: float = 0.0, - act: Union[str, tuple] = ("relu", {"inplace": True}), - norm: Union[str, tuple] = "batch", + act: str | tuple = ("relu", {"inplace": True}), + norm: str | tuple = "batch", freeze_dense_layer: bool = False, freeze_block: bool = False, ) -> None: @@ -315,8 +317,8 @@ class _DecoderBranch(nn.ModuleList): def __init__( self, decode_config: Sequence[int] = (8, 4), - act: Union[str, tuple] = ("relu", {"inplace": True}), - norm: Union[str, tuple] = "batch", + act: str | tuple = ("relu", {"inplace": True}), + norm: str | tuple = "batch", dropout_prob: float = 0.0, out_channels: int = 2, kernel_size: int = 3, @@ -385,7 +387,7 @@ def __init__( 2, scale_factor=2, mode=UpsampleMode.NONTRAINABLE, interp_mode=InterpolateMode.BILINEAR, bias=False ) - def forward(self, xin: torch.Tensor, short_cuts: List[torch.Tensor]) -> torch.Tensor: + def forward(self, xin: torch.Tensor, short_cuts: list[torch.Tensor]) -> torch.Tensor: block_number = len(short_cuts) - 1 x = xin + short_cuts[block_number] @@ -452,15 +454,15 @@ class HoVerNet(nn.Module): def __init__( self, - mode: Union[HoVerNetMode, str] = HoVerNetMode.FAST, + mode: HoVerNetMode | str = HoVerNetMode.FAST, in_channels: int = 3, np_out_channels: int = 2, out_classes: int = 0, - act: Union[str, tuple] = ("relu", {"inplace": True}), - norm: Union[str, tuple] = "batch", + act: str | tuple = ("relu", {"inplace": True}), + norm: str | tuple = "batch", decoder_padding: bool = False, dropout_prob: float = 0.0, - pretrained_url: Optional[str] = None, + pretrained_url: str | None = None, adapt_standard_resnet: bool = False, freeze_encoder: bool = False, ) -> None: @@ -496,7 +498,7 @@ def __init__( _ksize = 5 _pad = 0 - conv_type: Type[nn.Conv2d] = Conv[Conv.CONV, 2] + conv_type: type[nn.Conv2d] = Conv[Conv.CONV, 2] self.conv0 = nn.Sequential( OrderedDict( @@ -553,7 +555,7 @@ def __init__( kernel_size=_ksize, same_padding=decoder_padding, out_channels=np_out_channels ) self.horizontal_vertical = _DecoderBranch(kernel_size=_ksize, same_padding=decoder_padding) - self.type_prediction: Optional[_DecoderBranch] = ( + self.type_prediction: _DecoderBranch | None = ( _DecoderBranch(out_channels=out_classes, kernel_size=_ksize, same_padding=decoder_padding) if out_classes > 0 else None @@ -573,7 +575,7 @@ def __init__( weights = _remap_preact_resnet_model(pretrained_url) _load_pretrained_encoder(self, weights) - def forward(self, x: torch.Tensor) -> Dict[str, torch.Tensor]: + def forward(self, x: torch.Tensor) -> dict[str, torch.Tensor]: if self.mode == HoVerNetMode.ORIGINAL.value: if x.shape[-1] != 270 or x.shape[-2] != 270: @@ -604,7 +606,7 @@ def forward(self, x: torch.Tensor) -> Dict[str, torch.Tensor]: return output -def _load_pretrained_encoder(model: nn.Module, state_dict: Union[OrderedDict, Dict]): +def _load_pretrained_encoder(model: nn.Module, state_dict: OrderedDict | dict): model_dict = model.state_dict() state_dict = { diff --git a/monai/networks/nets/milmodel.py b/monai/networks/nets/milmodel.py index 88468d39a6..f6b905bfda 100644 --- a/monai/networks/nets/milmodel.py +++ b/monai/networks/nets/milmodel.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, Optional, Union, cast +from __future__ import annotations + +from typing import cast import torch import torch.nn as nn @@ -54,8 +56,8 @@ def __init__( num_classes: int, mil_mode: str = "att", pretrained: bool = True, - backbone: Optional[Union[str, nn.Module]] = None, - backbone_num_features: Optional[int] = None, + backbone: str | nn.Module | None = None, + backbone_num_features: int | None = None, trans_blocks: int = 4, trans_dropout: float = 0.0, ) -> None: @@ -70,7 +72,7 @@ def __init__( self.mil_mode = mil_mode.lower() self.attention = nn.Sequential() - self.transformer = None # type: Optional[nn.Module] + self.transformer: nn.Module | None = None if backbone is None: @@ -78,7 +80,7 @@ def __init__( nfc = net.fc.in_features # save the number of final features net.fc = torch.nn.Identity() # remove final linear layer - self.extra_outputs: Dict[str, torch.Tensor] = {} + self.extra_outputs: dict[str, torch.Tensor] = {} if mil_mode == "att_trans_pyramid": # register hooks to capture outputs of intermediate layers diff --git a/monai/networks/nets/netadapter.py b/monai/networks/nets/netadapter.py index 39112e4d54..452c31be37 100644 --- a/monai/networks/nets/netadapter.py +++ b/monai/networks/nets/netadapter.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Dict, Optional, Tuple, Union +from __future__ import annotations + +from typing import Any, Dict import torch @@ -52,9 +54,9 @@ def __init__( model: torch.nn.Module, num_classes: int = 1, dim: int = 2, - in_channels: Optional[int] = None, + in_channels: int | None = None, use_conv: bool = False, - pool: Optional[Tuple[str, Dict[str, Any]]] = ("avg", {"kernel_size": 7, "stride": 1}), + pool: tuple[str, dict[str, Any]] | None = ("avg", {"kernel_size": 7, "stride": 1}), bias: bool = True, fc_name: str = "fc", node_name: str = "", @@ -94,7 +96,7 @@ def __init__( self.pool = get_pool_layer(name=pool, spatial_dims=dim) # create new fully connected layer or kernel size 1 convolutional layer - self.fc: Union[torch.nn.Linear, torch.nn.Conv2d, torch.nn.Conv3d] + self.fc: torch.nn.Linear | torch.nn.Conv2d | torch.nn.Conv3d if use_conv: self.fc = Conv[Conv.CONV, dim](in_channels=in_channels_, out_channels=num_classes, kernel_size=1, bias=bias) else: diff --git a/monai/networks/nets/regressor.py b/monai/networks/nets/regressor.py index 0a1e6258a9..a54b926bd0 100644 --- a/monai/networks/nets/regressor.py +++ b/monai/networks/nets/regressor.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import numpy as np import torch @@ -61,11 +63,11 @@ def __init__( out_shape: Sequence[int], channels: Sequence[int], strides: Sequence[int], - kernel_size: Union[Sequence[int], int] = 3, + kernel_size: Sequence[int] | int = 3, num_res_units: int = 2, act=Act.PRELU, norm=Norm.INSTANCE, - dropout: Optional[float] = None, + dropout: float | None = None, bias: bool = True, ) -> None: super().__init__() @@ -101,14 +103,14 @@ def __init__( def _get_layer( self, in_channels: int, out_channels: int, strides: int, is_last: bool - ) -> Union[ResidualUnit, Convolution]: + ) -> ResidualUnit | Convolution: """ Returns a layer accepting inputs with `in_channels` number of channels and producing outputs of `out_channels` number of channels. The `strides` indicates downsampling factor, ie. convolutional stride. If `is_last` is True this is the final layer and is not expected to include activation and normalization layers. """ - layer: Union[ResidualUnit, Convolution] + layer: ResidualUnit | Convolution if self.num_res_units > 0: layer = ResidualUnit( diff --git a/monai/networks/nets/regunet.py b/monai/networks/nets/regunet.py index 01fd9b905f..1764c480f3 100644 --- a/monai/networks/nets/regunet.py +++ b/monai/networks/nets/regunet.py @@ -8,7 +8,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Optional, Tuple, Union + +from __future__ import annotations import torch from torch import nn @@ -46,13 +47,13 @@ def __init__( in_channels: int, num_channel_initial: int, depth: int, - out_kernel_initializer: Optional[str] = "kaiming_uniform", - out_activation: Optional[str] = None, + out_kernel_initializer: str | None = "kaiming_uniform", + out_activation: str | None = None, out_channels: int = 3, - extract_levels: Optional[Tuple[int]] = None, + extract_levels: tuple[int] | None = None, pooling: bool = True, concat_skip: bool = False, - encode_kernel_sizes: Union[int, List[int]] = 3, + encode_kernel_sizes: int | list[int] = 3, ): """ Args: @@ -90,7 +91,7 @@ def __init__( encode_kernel_sizes = [encode_kernel_sizes] * (self.depth + 1) if len(encode_kernel_sizes) != self.depth + 1: raise AssertionError - self.encode_kernel_sizes: List[int] = encode_kernel_sizes + self.encode_kernel_sizes: list[int] = encode_kernel_sizes self.num_channels = [self.num_channel_initial * (2**d) for d in range(self.depth + 1)] self.min_extract_level = min(self.extract_levels) @@ -233,7 +234,7 @@ def forward(self, x): class AffineHead(nn.Module): - def __init__(self, spatial_dims: int, image_size: List[int], decode_size: List[int], in_channels: int): + def __init__(self, spatial_dims: int, image_size: list[int], decode_size: list[int], in_channels: int): super().__init__() self.spatial_dims = spatial_dims if spatial_dims == 2: @@ -255,7 +256,7 @@ def __init__(self, spatial_dims: int, image_size: List[int], decode_size: List[i self.fc.bias.data.copy_(out_init) @staticmethod - def get_reference_grid(image_size: Union[Tuple[int], List[int]]) -> torch.Tensor: + def get_reference_grid(image_size: tuple[int] | list[int]) -> torch.Tensor: mesh_points = [torch.arange(0, dim) for dim in image_size] grid = torch.stack(meshgrid_ij(*mesh_points), dim=0) # (spatial_dims, ...) return grid.to(dtype=torch.float) @@ -273,7 +274,7 @@ def affine_transform(self, theta: torch.Tensor): raise ValueError(f"do not support spatial_dims={self.spatial_dims}") return grid_warped - def forward(self, x: List[torch.Tensor], image_size: List[int]) -> torch.Tensor: + def forward(self, x: list[torch.Tensor], image_size: list[int]) -> torch.Tensor: f = x[0] self.grid = self.grid.to(device=f.device) theta = self.fc(f.reshape(f.shape[0], -1)) @@ -294,16 +295,16 @@ class GlobalNet(RegUNet): def __init__( self, - image_size: List[int], + image_size: list[int], spatial_dims: int, in_channels: int, num_channel_initial: int, depth: int, - out_kernel_initializer: Optional[str] = "kaiming_uniform", - out_activation: Optional[str] = None, + out_kernel_initializer: str | None = "kaiming_uniform", + out_activation: str | None = None, pooling: bool = True, concat_skip: bool = False, - encode_kernel_sizes: Union[int, List[int]] = 3, + encode_kernel_sizes: int | list[int] = 3, ): for size in image_size: if size % (2**depth) != 0: @@ -343,7 +344,7 @@ def __init__( in_channels: int, out_channels: int, mode: str = "nearest", - align_corners: Optional[bool] = None, + align_corners: bool | None = None, ): super().__init__() self.deconv = get_deconv_block(spatial_dims=spatial_dims, in_channels=in_channels, out_channels=out_channels) @@ -376,15 +377,15 @@ def __init__( spatial_dims: int, in_channels: int, num_channel_initial: int, - extract_levels: Tuple[int], - out_kernel_initializer: Optional[str] = "kaiming_uniform", - out_activation: Optional[str] = None, + extract_levels: tuple[int], + out_kernel_initializer: str | None = "kaiming_uniform", + out_activation: str | None = None, out_channels: int = 3, pooling: bool = True, use_additive_sampling: bool = True, concat_skip: bool = False, mode: str = "nearest", - align_corners: Optional[bool] = None, + align_corners: bool | None = None, ): """ Args: diff --git a/monai/networks/nets/resnet.py b/monai/networks/nets/resnet.py index fca975d40e..c9e34d093c 100644 --- a/monai/networks/nets/resnet.py +++ b/monai/networks/nets/resnet.py @@ -9,8 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + +from collections.abc import Callable from functools import partial -from typing import Any, Callable, List, Tuple, Type, Union +from typing import Any import torch import torch.nn as nn @@ -51,7 +54,7 @@ def __init__( planes: int, spatial_dims: int = 3, stride: int = 1, - downsample: Union[nn.Module, partial, None] = None, + downsample: nn.Module | partial | None = None, ) -> None: """ Args: @@ -102,7 +105,7 @@ def __init__( planes: int, spatial_dims: int = 3, stride: int = 1, - downsample: Union[nn.Module, partial, None] = None, + downsample: nn.Module | partial | None = None, ) -> None: """ Args: @@ -181,13 +184,13 @@ class ResNet(nn.Module): def __init__( self, - block: Union[Type[Union[ResNetBlock, ResNetBottleneck]], str], - layers: List[int], - block_inplanes: List[int], + block: type[ResNetBlock | ResNetBottleneck] | str, + layers: list[int], + block_inplanes: list[int], spatial_dims: int = 3, n_input_channels: int = 3, - conv1_t_size: Union[Tuple[int], int] = 7, - conv1_t_stride: Union[Tuple[int], int] = 1, + conv1_t_size: tuple[int] | int = 7, + conv1_t_stride: tuple[int] | int = 1, no_max_pool: bool = False, shortcut_type: str = "B", widen_factor: float = 1.0, @@ -206,10 +209,10 @@ def __init__( else: raise ValueError("Unknown block '%s', use basic or bottleneck" % block) - conv_type: Type[Union[nn.Conv1d, nn.Conv2d, nn.Conv3d]] = Conv[Conv.CONV, spatial_dims] - norm_type: Type[Union[nn.BatchNorm1d, nn.BatchNorm2d, nn.BatchNorm3d]] = Norm[Norm.BATCH, spatial_dims] - pool_type: Type[Union[nn.MaxPool1d, nn.MaxPool2d, nn.MaxPool3d]] = Pool[Pool.MAX, spatial_dims] - avgp_type: Type[Union[nn.AdaptiveAvgPool1d, nn.AdaptiveAvgPool2d, nn.AdaptiveAvgPool3d]] = Pool[ + conv_type: type[nn.Conv1d | nn.Conv2d | nn.Conv3d] = Conv[Conv.CONV, spatial_dims] + norm_type: type[nn.BatchNorm1d | nn.BatchNorm2d | nn.BatchNorm3d] = Norm[Norm.BATCH, spatial_dims] + pool_type: type[nn.MaxPool1d | nn.MaxPool2d | nn.MaxPool3d] = Pool[Pool.MAX, spatial_dims] + avgp_type: type[nn.AdaptiveAvgPool1d | nn.AdaptiveAvgPool2d | nn.AdaptiveAvgPool3d] = Pool[ Pool.ADAPTIVEAVG, spatial_dims ] @@ -258,7 +261,7 @@ def _downsample_basic_block(self, x: torch.Tensor, planes: int, stride: int, spa def _make_layer( self, - block: Type[Union[ResNetBlock, ResNetBottleneck]], + block: type[ResNetBlock | ResNetBottleneck], planes: int, blocks: int, spatial_dims: int, @@ -269,7 +272,7 @@ def _make_layer( conv_type: Callable = Conv[Conv.CONV, spatial_dims] norm_type: Callable = Norm[Norm.BATCH, spatial_dims] - downsample: Union[nn.Module, partial, None] = None + downsample: nn.Module | partial | None = None if stride != 1 or self.in_planes != planes * block.expansion: if look_up_option(shortcut_type, {"A", "B"}) == "A": downsample = partial( @@ -325,9 +328,9 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: def _resnet( arch: str, - block: Type[Union[ResNetBlock, ResNetBottleneck]], - layers: List[int], - block_inplanes: List[int], + block: type[ResNetBlock | ResNetBottleneck], + layers: list[int], + block_inplanes: list[int], pretrained: bool, progress: bool, **kwargs: Any, diff --git a/monai/networks/nets/segresnet.py b/monai/networks/nets/segresnet.py index cc908f1640..0c1c85f04a 100644 --- a/monai/networks/nets/segresnet.py +++ b/monai/networks/nets/segresnet.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import numpy as np import torch @@ -60,15 +62,15 @@ def __init__( init_filters: int = 8, in_channels: int = 1, out_channels: int = 2, - dropout_prob: Optional[float] = None, - act: Union[Tuple, str] = ("RELU", {"inplace": True}), - norm: Union[Tuple, str] = ("GROUP", {"num_groups": 8}), + dropout_prob: float | None = None, + act: tuple | str = ("RELU", {"inplace": True}), + norm: tuple | str = ("GROUP", {"num_groups": 8}), norm_name: str = "", num_groups: int = 8, use_conv_final: bool = True, blocks_down: tuple = (1, 2, 2, 4), blocks_up: tuple = (1, 1, 1), - upsample_mode: Union[UpsampleMode, str] = UpsampleMode.NONTRAINABLE, + upsample_mode: UpsampleMode | str = UpsampleMode.NONTRAINABLE, ): super().__init__() @@ -151,7 +153,7 @@ def _make_final_conv(self, out_channels: int): get_conv_layer(self.spatial_dims, self.init_filters, out_channels, kernel_size=1, bias=True), ) - def encode(self, x: torch.Tensor) -> Tuple[torch.Tensor, List[torch.Tensor]]: + def encode(self, x: torch.Tensor) -> tuple[torch.Tensor, list[torch.Tensor]]: x = self.convInit(x) if self.dropout_prob is not None: x = self.dropout(x) @@ -164,7 +166,7 @@ def encode(self, x: torch.Tensor) -> Tuple[torch.Tensor, List[torch.Tensor]]: return x, down_x - def decode(self, x: torch.Tensor, down_x: List[torch.Tensor]) -> torch.Tensor: + def decode(self, x: torch.Tensor, down_x: list[torch.Tensor]) -> torch.Tensor: for i, (up, upl) in enumerate(zip(self.up_samples, self.up_layers)): x = up(x) + down_x[i + 1] x = upl(x) @@ -225,13 +227,13 @@ def __init__( init_filters: int = 8, in_channels: int = 1, out_channels: int = 2, - dropout_prob: Optional[float] = None, - act: Union[str, tuple] = ("RELU", {"inplace": True}), - norm: Union[Tuple, str] = ("GROUP", {"num_groups": 8}), + dropout_prob: float | None = None, + act: str | tuple = ("RELU", {"inplace": True}), + norm: tuple | str = ("GROUP", {"num_groups": 8}), use_conv_final: bool = True, blocks_down: tuple = (1, 2, 2, 4), blocks_up: tuple = (1, 1, 1), - upsample_mode: Union[UpsampleMode, str] = UpsampleMode.NONTRAINABLE, + upsample_mode: UpsampleMode | str = UpsampleMode.NONTRAINABLE, ): super().__init__( spatial_dims=spatial_dims, diff --git a/monai/networks/nets/segresnet_ds.py b/monai/networks/nets/segresnet_ds.py index 15129a7996..115bec455a 100644 --- a/monai/networks/nets/segresnet_ds.py +++ b/monai/networks/nets/segresnet_ds.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, List, Optional, Tuple, Union +from __future__ import annotations + +from collections.abc import Callable import numpy as np import torch @@ -23,7 +25,7 @@ __all__ = ["SegResNetDS"] -def scales_for_resolution(resolution: Union[Tuple, List], n_stages: Optional[int] = None): +def scales_for_resolution(resolution: tuple | list, n_stages: int | None = None): """ A helper function to compute a schedule of scale at different downsampling levels, given the input resolution. @@ -51,7 +53,7 @@ def scales_for_resolution(resolution: Union[Tuple, List], n_stages: Optional[int return scales -def aniso_kernel(scale: Union[Tuple, List]): +def aniso_kernel(scale: tuple | list): """ A helper function to compute kernel_size, padding and stride for the given scale @@ -73,9 +75,9 @@ def __init__( self, spatial_dims: int, in_channels: int, - norm: Union[Tuple, str], - kernel_size: Union[Tuple, int] = 3, - act: Union[Tuple, str] = "relu", + norm: tuple | str, + kernel_size: tuple | int = 3, + act: tuple | str = "relu", ) -> None: """ Args: @@ -144,11 +146,11 @@ def __init__( spatial_dims: int = 3, init_filters: int = 32, in_channels: int = 1, - act: Union[Tuple, str] = "relu", - norm: Union[Tuple, str] = "batch", + act: tuple | str = "relu", + norm: tuple | str = "batch", blocks_down: tuple = (1, 2, 2, 4), - head_module: Optional[nn.Module] = None, - anisotropic_scales: Optional[Tuple] = None, + head_module: nn.Module | None = None, + anisotropic_scales: tuple | None = None, ): super().__init__() @@ -212,7 +214,7 @@ def __init__( self.act = act self.spatial_dims = spatial_dims - def _forward(self, x: torch.Tensor) -> List[torch.Tensor]: + def _forward(self, x: torch.Tensor) -> list[torch.Tensor]: outputs = [] x = self.conv_init(x) @@ -227,7 +229,7 @@ def _forward(self, x: torch.Tensor) -> List[torch.Tensor]: return outputs - def forward(self, x: torch.Tensor) -> List[torch.Tensor]: + def forward(self, x: torch.Tensor) -> list[torch.Tensor]: return self._forward(x) @@ -262,14 +264,14 @@ def __init__( init_filters: int = 32, in_channels: int = 1, out_channels: int = 2, - act: Union[Tuple, str] = "relu", - norm: Union[Tuple, str] = "batch", + act: tuple | str = "relu", + norm: tuple | str = "batch", blocks_down: tuple = (1, 2, 2, 4), - blocks_up: Optional[Tuple] = None, + blocks_up: tuple | None = None, dsdepth: int = 1, - preprocess: Optional[Union[nn.Module, Callable]] = None, - upsample_mode: Union[UpsampleMode, str] = "deconv", - resolution: Optional[Tuple] = None, + preprocess: nn.Module | Callable | None = None, + upsample_mode: UpsampleMode | str = "deconv", + resolution: tuple | None = None, ): super().__init__() @@ -389,7 +391,7 @@ def is_valid_shape(self, x): a = [i % j == 0 for i, j in zip(x.shape[2:], self.shape_factor())] return all(a) - def _forward(self, x: torch.Tensor) -> Union[torch.Tensor, List[torch.Tensor]]: + def _forward(self, x: torch.Tensor) -> torch.Tensor | list[torch.Tensor]: if self.preprocess is not None: x = self.preprocess(x) @@ -405,7 +407,7 @@ def _forward(self, x: torch.Tensor) -> Union[torch.Tensor, List[torch.Tensor]]: if len(x_down) == 0: x_down = [torch.zeros(1, device=x.device, dtype=x.dtype)] - outputs: List[torch.Tensor] = [] + outputs: list[torch.Tensor] = [] i = 0 for level in self.up_layers: @@ -426,5 +428,5 @@ def _forward(self, x: torch.Tensor) -> Union[torch.Tensor, List[torch.Tensor]]: # return a list of DS outputs return outputs - def forward(self, x: torch.Tensor) -> Union[torch.Tensor, List[torch.Tensor]]: + def forward(self, x: torch.Tensor) -> torch.Tensor | list[torch.Tensor]: return self._forward(x) diff --git a/monai/networks/nets/senet.py b/monai/networks/nets/senet.py index b4c024b1f2..f8ca5fefef 100644 --- a/monai/networks/nets/senet.py +++ b/monai/networks/nets/senet.py @@ -9,9 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import re from collections import OrderedDict -from typing import Any, List, Optional, Sequence, Tuple, Type, Union +from collections.abc import Sequence +from typing import Any import torch import torch.nn as nn @@ -94,11 +97,11 @@ def __init__( self, spatial_dims: int, in_channels: int, - block: Union[Type[Union[SEBottleneck, SEResNetBottleneck, SEResNeXtBottleneck]], str], + block: type[SEBottleneck | SEResNetBottleneck | SEResNeXtBottleneck] | str, layers: Sequence[int], groups: int, reduction: int, - dropout_prob: Optional[float] = 0.2, + dropout_prob: float | None = 0.2, dropout_dim: int = 1, inplanes: int = 128, downsample_kernel_size: int = 3, @@ -120,19 +123,19 @@ def __init__( "Unknown block '%s', use se_bottleneck, se_resnet_bottleneck or se_resnetxt_bottleneck" % block ) - relu_type: Type[nn.ReLU] = Act[Act.RELU] - conv_type: Type[Union[nn.Conv1d, nn.Conv2d, nn.Conv3d]] = Conv[Conv.CONV, spatial_dims] - pool_type: Type[Union[nn.MaxPool1d, nn.MaxPool2d, nn.MaxPool3d]] = Pool[Pool.MAX, spatial_dims] - norm_type: Type[Union[nn.BatchNorm1d, nn.BatchNorm2d, nn.BatchNorm3d]] = Norm[Norm.BATCH, spatial_dims] - dropout_type: Type[Union[nn.Dropout, nn.Dropout2d, nn.Dropout3d]] = Dropout[Dropout.DROPOUT, dropout_dim] - avg_pool_type: Type[Union[nn.AdaptiveAvgPool1d, nn.AdaptiveAvgPool2d, nn.AdaptiveAvgPool3d]] = Pool[ + relu_type: type[nn.ReLU] = Act[Act.RELU] + conv_type: type[nn.Conv1d | nn.Conv2d | nn.Conv3d] = Conv[Conv.CONV, spatial_dims] + pool_type: type[nn.MaxPool1d | nn.MaxPool2d | nn.MaxPool3d] = Pool[Pool.MAX, spatial_dims] + norm_type: type[nn.BatchNorm1d | nn.BatchNorm2d | nn.BatchNorm3d] = Norm[Norm.BATCH, spatial_dims] + dropout_type: type[nn.Dropout | nn.Dropout2d | nn.Dropout3d] = Dropout[Dropout.DROPOUT, dropout_dim] + avg_pool_type: type[nn.AdaptiveAvgPool1d | nn.AdaptiveAvgPool2d | nn.AdaptiveAvgPool3d] = Pool[ Pool.ADAPTIVEAVG, spatial_dims ] self.inplanes = inplanes self.spatial_dims = spatial_dims - layer0_modules: List[Tuple[str, Any]] + layer0_modules: list[tuple[str, Any]] if input_3x3: layer0_modules = [ @@ -211,7 +214,7 @@ def __init__( def _make_layer( self, - block: Type[Union[SEBottleneck, SEResNetBottleneck, SEResNeXtBottleneck]], + block: type[SEBottleneck | SEResNetBottleneck | SEResNeXtBottleneck], planes: int, blocks: int, groups: int, @@ -358,7 +361,7 @@ def __init__( layers: Sequence[int] = (3, 4, 6, 3), groups: int = 1, reduction: int = 16, - dropout_prob: Optional[float] = None, + dropout_prob: float | None = None, inplanes: int = 64, downsample_kernel_size: int = 1, input_3x3: bool = False, @@ -456,7 +459,7 @@ def __init__( layers: Sequence[int] = (3, 4, 6, 3), groups: int = 32, reduction: int = 16, - dropout_prob: Optional[float] = None, + dropout_prob: float | None = None, inplanes: int = 64, downsample_kernel_size: int = 1, input_3x3: bool = False, @@ -490,7 +493,7 @@ def __init__( layers: Sequence[int] = (3, 4, 23, 3), groups: int = 32, reduction: int = 16, - dropout_prob: Optional[float] = None, + dropout_prob: float | None = None, inplanes: int = 64, downsample_kernel_size: int = 1, input_3x3: bool = False, diff --git a/monai/networks/nets/swin_unetr.py b/monai/networks/nets/swin_unetr.py index 3f18ac4d40..faaef88514 100644 --- a/monai/networks/nets/swin_unetr.py +++ b/monai/networks/nets/swin_unetr.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import itertools -from typing import Optional, Sequence, Tuple, Type, Union +from collections.abc import Sequence import numpy as np import torch @@ -49,13 +51,13 @@ class SwinUNETR(nn.Module): def __init__( self, - img_size: Union[Sequence[int], int], + img_size: Sequence[int] | int, in_channels: int, out_channels: int, depths: Sequence[int] = (2, 2, 2, 2), num_heads: Sequence[int] = (3, 6, 12, 24), feature_size: int = 24, - norm_name: Union[Tuple, str] = "instance", + norm_name: tuple | str = "instance", drop_rate: float = 0.0, attn_drop_rate: float = 0.0, dropout_path_rate: float = 0.0, @@ -530,7 +532,7 @@ def __init__( attn_drop: float = 0.0, drop_path: float = 0.0, act_layer: str = "GELU", - norm_layer: Type[LayerNorm] = nn.LayerNorm, + norm_layer: type[LayerNorm] = nn.LayerNorm, use_checkpoint: bool = False, ) -> None: """ @@ -684,7 +686,7 @@ class PatchMergingV2(nn.Module): https://github.com/microsoft/Swin-Transformer """ - def __init__(self, dim: int, norm_layer: Type[LayerNorm] = nn.LayerNorm, spatial_dims: int = 3) -> None: + def __init__(self, dim: int, norm_layer: type[LayerNorm] = nn.LayerNorm, spatial_dims: int = 3) -> None: """ Args: dim: number of feature channels. @@ -814,8 +816,8 @@ def __init__( qkv_bias: bool = False, drop: float = 0.0, attn_drop: float = 0.0, - norm_layer: Type[LayerNorm] = nn.LayerNorm, - downsample: Optional[nn.Module] = None, + norm_layer: type[LayerNorm] = nn.LayerNorm, + downsample: nn.Module | None = None, use_checkpoint: bool = False, ) -> None: """ @@ -916,7 +918,7 @@ def __init__( drop_rate: float = 0.0, attn_drop_rate: float = 0.0, drop_path_rate: float = 0.0, - norm_layer: Type[LayerNorm] = nn.LayerNorm, + norm_layer: type[LayerNorm] = nn.LayerNorm, patch_norm: bool = False, use_checkpoint: bool = False, spatial_dims: int = 3, diff --git a/monai/networks/nets/torchvision_fc.py b/monai/networks/nets/torchvision_fc.py index 85103a2c04..6f63a34951 100644 --- a/monai/networks/nets/torchvision_fc.py +++ b/monai/networks/nets/torchvision_fc.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Dict, Optional, Tuple +from __future__ import annotations + +from typing import Any from monai.networks.nets import NetAdapter from monai.utils import optional_import @@ -100,9 +102,9 @@ def __init__( model_name: str = "resnet18", num_classes: int = 1, dim: int = 2, - in_channels: Optional[int] = None, + in_channels: int | None = None, use_conv: bool = False, - pool: Optional[Tuple[str, Dict[str, Any]]] = ("avg", {"kernel_size": 7, "stride": 1}), + pool: tuple[str, dict[str, Any]] | None = ("avg", {"kernel_size": 7, "stride": 1}), bias: bool = True, pretrained: bool = False, fc_name: str = "fc", diff --git a/monai/networks/nets/transchex.py b/monai/networks/nets/transchex.py index f138a36d09..8f7ad33340 100644 --- a/monai/networks/nets/transchex.py +++ b/monai/networks/nets/transchex.py @@ -9,12 +9,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import math import os import shutil import tarfile import tempfile -from typing import List, Sequence, Tuple, Union +from collections.abc import Sequence import torch from torch import nn @@ -115,9 +117,9 @@ def safe_extract(tar, path=".", members=None, *, numeric_owner=False): new_keys.append(new_key) for old_key, new_key in zip(old_keys, new_keys): state_dict[new_key] = state_dict.pop(old_key) - missing_keys: List = [] - unexpected_keys: List = [] - error_msgs: List = [] + missing_keys: list = [] + unexpected_keys: list = [] + error_msgs: list = [] metadata = getattr(state_dict, "_metadata", None) state_dict = state_dict.copy() if metadata is not None: @@ -276,8 +278,8 @@ class Transchex(torch.nn.Module): def __init__( self, in_channels: int, - img_size: Union[Sequence[int], int], - patch_size: Union[int, Tuple[int, int]], + img_size: Sequence[int] | int, + patch_size: int | tuple[int, int], num_classes: int, num_language_layers: int, num_vision_layers: int, diff --git a/monai/networks/nets/unet.py b/monai/networks/nets/unet.py index 6db41ef8fb..b2446c7ff1 100644 --- a/monai/networks/nets/unet.py +++ b/monai/networks/nets/unet.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Optional, Sequence, Tuple, Union +from collections.abc import Sequence import torch import torch.nn as nn @@ -117,15 +119,15 @@ def __init__( out_channels: int, channels: Sequence[int], strides: Sequence[int], - kernel_size: Union[Sequence[int], int] = 3, - up_kernel_size: Union[Sequence[int], int] = 3, + kernel_size: Sequence[int] | int = 3, + up_kernel_size: Sequence[int] | int = 3, num_res_units: int = 0, - act: Union[Tuple, str] = Act.PRELU, - norm: Union[Tuple, str] = Norm.INSTANCE, + act: tuple | str = Act.PRELU, + norm: tuple | str = Norm.INSTANCE, dropout: float = 0.0, bias: bool = True, adn_ordering: str = "NDA", - dimensions: Optional[int] = None, + dimensions: int | None = None, ) -> None: super().__init__() @@ -271,7 +273,7 @@ def _get_up_layer(self, in_channels: int, out_channels: int, strides: int, is_to strides: convolution stride. is_top: True if this is the top block. """ - conv: Union[Convolution, nn.Sequential] + conv: Convolution | nn.Sequential conv = Convolution( self.dimensions, diff --git a/monai/networks/nets/unetr.py b/monai/networks/nets/unetr.py index f95428693a..7ad12daa89 100644 --- a/monai/networks/nets/unetr.py +++ b/monai/networks/nets/unetr.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import torch.nn as nn @@ -29,13 +31,13 @@ def __init__( self, in_channels: int, out_channels: int, - img_size: Union[Sequence[int], int], + img_size: Sequence[int] | int, feature_size: int = 16, hidden_size: int = 768, mlp_dim: int = 3072, num_heads: int = 12, pos_embed: str = "conv", - norm_name: Union[Tuple, str] = "instance", + norm_name: tuple | str = "instance", conv_block: bool = True, res_block: bool = True, dropout_rate: float = 0.0, diff --git a/monai/networks/nets/varautoencoder.py b/monai/networks/nets/varautoencoder.py index c4001c6a2a..29131281de 100644 --- a/monai/networks/nets/varautoencoder.py +++ b/monai/networks/nets/varautoencoder.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Tuple, Union +from __future__ import annotations + +from collections.abc import Sequence import numpy as np import torch @@ -77,15 +79,15 @@ def __init__( latent_size: int, channels: Sequence[int], strides: Sequence[int], - kernel_size: Union[Sequence[int], int] = 3, - up_kernel_size: Union[Sequence[int], int] = 3, + kernel_size: Sequence[int] | int = 3, + up_kernel_size: Sequence[int] | int = 3, num_res_units: int = 0, - inter_channels: Optional[list] = None, - inter_dilations: Optional[list] = None, + inter_channels: list | None = None, + inter_dilations: list | None = None, num_inter_units: int = 2, - act: Optional[Union[Tuple, str]] = Act.PRELU, - norm: Union[Tuple, str] = Norm.INSTANCE, - dropout: Optional[Union[Tuple, str, float]] = None, + act: tuple | str | None = Act.PRELU, + norm: tuple | str = Norm.INSTANCE, + dropout: tuple | str | float | None = None, bias: bool = True, use_sigmoid: bool = True, ) -> None: @@ -124,7 +126,7 @@ def __init__( self.logvar = nn.Linear(linear_size, self.latent_size) self.decodeL = nn.Linear(self.latent_size, linear_size) - def encode_forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]: + def encode_forward(self, x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: x = self.encode(x) x = self.intermediate(x) x = x.view(x.shape[0], -1) @@ -148,7 +150,7 @@ def reparameterize(self, mu: torch.Tensor, logvar: torch.Tensor) -> torch.Tensor return std.add_(mu) - def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: + def forward(self, x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: mu, logvar = self.encode_forward(x) z = self.reparameterize(mu, logvar) return self.decode_forward(z, self.use_sigmoid), mu, logvar, z diff --git a/monai/networks/nets/vit.py b/monai/networks/nets/vit.py index e4166c78b6..f3896d76c4 100644 --- a/monai/networks/nets/vit.py +++ b/monai/networks/nets/vit.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -31,8 +33,8 @@ class ViT(nn.Module): def __init__( self, in_channels: int, - img_size: Union[Sequence[int], int], - patch_size: Union[Sequence[int], int], + img_size: Sequence[int] | int, + patch_size: Sequence[int] | int, hidden_size: int = 768, mlp_dim: int = 3072, num_layers: int = 12, diff --git a/monai/networks/nets/vitautoenc.py b/monai/networks/nets/vitautoenc.py index 6197f6bd99..ff6f637118 100644 --- a/monai/networks/nets/vitautoenc.py +++ b/monai/networks/nets/vitautoenc.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence import torch import torch.nn as nn @@ -33,8 +35,8 @@ class ViTAutoEnc(nn.Module): def __init__( self, in_channels: int, - img_size: Union[Sequence[int], int], - patch_size: Union[Sequence[int], int], + img_size: Sequence[int] | int, + patch_size: Sequence[int] | int, out_channels: int = 1, deconv_chns: int = 16, hidden_size: int = 768, diff --git a/monai/networks/nets/vnet.py b/monai/networks/nets/vnet.py index 9abd2bc5e2..697547093a 100644 --- a/monai/networks/nets/vnet.py +++ b/monai/networks/nets/vnet.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, Optional, Tuple, Type, Union +from __future__ import annotations import torch import torch.nn as nn @@ -20,7 +20,7 @@ __all__ = ["VNet"] -def get_acti_layer(act: Union[Tuple[str, Dict], str], nchan: int = 0): +def get_acti_layer(act: tuple[str, dict] | str, nchan: int = 0): if act == "prelu": act = ("prelu", {"num_parameters": nchan}) act_name, act_args = split_args(act) @@ -29,7 +29,7 @@ def get_acti_layer(act: Union[Tuple[str, Dict], str], nchan: int = 0): class LUConv(nn.Module): - def __init__(self, spatial_dims: int, nchan: int, act: Union[Tuple[str, Dict], str], bias: bool = False): + def __init__(self, spatial_dims: int, nchan: int, act: tuple[str, dict] | str, bias: bool = False): super().__init__() self.act_function = get_acti_layer(act, nchan) @@ -49,7 +49,7 @@ def forward(self, x): return out -def _make_nconv(spatial_dims: int, nchan: int, depth: int, act: Union[Tuple[str, Dict], str], bias: bool = False): +def _make_nconv(spatial_dims: int, nchan: int, depth: int, act: tuple[str, dict] | str, bias: bool = False): layers = [] for _ in range(depth): layers.append(LUConv(spatial_dims, nchan, act, bias)) @@ -58,12 +58,7 @@ def _make_nconv(spatial_dims: int, nchan: int, depth: int, act: Union[Tuple[str, class InputTransition(nn.Module): def __init__( - self, - spatial_dims: int, - in_channels: int, - out_channels: int, - act: Union[Tuple[str, Dict], str], - bias: bool = False, + self, spatial_dims: int, in_channels: int, out_channels: int, act: tuple[str, dict] | str, bias: bool = False ): super().__init__() @@ -100,16 +95,16 @@ def __init__( spatial_dims: int, in_channels: int, nconvs: int, - act: Union[Tuple[str, Dict], str], - dropout_prob: Optional[float] = None, + act: tuple[str, dict] | str, + dropout_prob: float | None = None, dropout_dim: int = 3, bias: bool = False, ): super().__init__() - conv_type: Type[Union[nn.Conv2d, nn.Conv3d]] = Conv[Conv.CONV, spatial_dims] - norm_type: Type[Union[nn.BatchNorm2d, nn.BatchNorm3d]] = Norm[Norm.BATCH, spatial_dims] - dropout_type: Type[Union[nn.Dropout, nn.Dropout2d, nn.Dropout3d]] = Dropout[Dropout.DROPOUT, dropout_dim] + conv_type: type[nn.Conv2d | nn.Conv3d] = Conv[Conv.CONV, spatial_dims] + norm_type: type[nn.BatchNorm2d | nn.BatchNorm3d] = Norm[Norm.BATCH, spatial_dims] + dropout_type: type[nn.Dropout | nn.Dropout2d | nn.Dropout3d] = Dropout[Dropout.DROPOUT, dropout_dim] out_channels = 2 * in_channels self.down_conv = conv_type(in_channels, out_channels, kernel_size=2, stride=2, bias=bias) @@ -137,15 +132,15 @@ def __init__( in_channels: int, out_channels: int, nconvs: int, - act: Union[Tuple[str, Dict], str], - dropout_prob: Optional[float] = None, + act: tuple[str, dict] | str, + dropout_prob: float | None = None, dropout_dim: int = 3, ): super().__init__() - conv_trans_type: Type[Union[nn.ConvTranspose2d, nn.ConvTranspose3d]] = Conv[Conv.CONVTRANS, spatial_dims] - norm_type: Type[Union[nn.BatchNorm2d, nn.BatchNorm3d]] = Norm[Norm.BATCH, spatial_dims] - dropout_type: Type[Union[nn.Dropout, nn.Dropout2d, nn.Dropout3d]] = Dropout[Dropout.DROPOUT, dropout_dim] + conv_trans_type: type[nn.ConvTranspose2d | nn.ConvTranspose3d] = Conv[Conv.CONVTRANS, spatial_dims] + norm_type: type[nn.BatchNorm2d | nn.BatchNorm3d] = Norm[Norm.BATCH, spatial_dims] + dropout_type: type[nn.Dropout | nn.Dropout2d | nn.Dropout3d] = Dropout[Dropout.DROPOUT, dropout_dim] self.up_conv = conv_trans_type(in_channels, out_channels // 2, kernel_size=2, stride=2) self.bn1 = norm_type(out_channels // 2) @@ -170,16 +165,11 @@ def forward(self, x, skipx): class OutputTransition(nn.Module): def __init__( - self, - spatial_dims: int, - in_channels: int, - out_channels: int, - act: Union[Tuple[str, Dict], str], - bias: bool = False, + self, spatial_dims: int, in_channels: int, out_channels: int, act: tuple[str, dict] | str, bias: bool = False ): super().__init__() - conv_type: Type[Union[nn.Conv2d, nn.Conv3d]] = Conv[Conv.CONV, spatial_dims] + conv_type: type[nn.Conv2d | nn.Conv3d] = Conv[Conv.CONV, spatial_dims] self.act_function1 = get_acti_layer(act, out_channels) self.conv_block = Convolution( @@ -233,7 +223,7 @@ def __init__( spatial_dims: int = 3, in_channels: int = 1, out_channels: int = 1, - act: Union[Tuple[str, Dict], str] = ("elu", {"inplace": True}), + act: tuple[str, dict] | str = ("elu", {"inplace": True}), dropout_prob: float = 0.5, dropout_dim: int = 3, bias: bool = False, diff --git a/monai/networks/utils.py b/monai/networks/utils.py index b58a41cc0c..14944abf15 100644 --- a/monai/networks/utils.py +++ b/monai/networks/utils.py @@ -11,12 +11,16 @@ """ Utilities and types for defining networks, these depend on PyTorch. """ + +from __future__ import annotations + import re import warnings from collections import OrderedDict +from collections.abc import Callable, Mapping, Sequence from contextlib import contextmanager from copy import deepcopy -from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Union +from typing import Any import torch import torch.nn as nn @@ -162,7 +166,7 @@ def one_hot(labels: torch.Tensor, num_classes: int, dtype: torch.dtype = torch.f @deprecated(since="0.8.0", msg_suffix="use `monai.utils.misc.sample_slices` instead.") -def slice_channels(tensor: torch.Tensor, *slicevals: Optional[int]) -> torch.Tensor: +def slice_channels(tensor: torch.Tensor, *slicevals: int | None) -> torch.Tensor: """ .. deprecated:: 0.8.0 Use `monai.utils.misc.sample_slices` instead. @@ -196,8 +200,8 @@ def predict_segmentation(logits: torch.Tensor, mutually_exclusive: bool = False, def normalize_transform( shape, - device: Optional[torch.device] = None, - dtype: Optional[torch.dtype] = None, + device: torch.device | None = None, + dtype: torch.dtype | None = None, align_corners: bool = False, zero_centered: bool = False, ) -> torch.Tensor: @@ -432,7 +436,7 @@ def train_mode(*nets: nn.Module): n.eval() -def get_state_dict(obj: Union[torch.nn.Module, Mapping]): +def get_state_dict(obj: torch.nn.Module | Mapping): """ Get the state dict of input object if has `state_dict`, otherwise, return object directly. For data parallel model, automatically convert it to regular model first. @@ -447,8 +451,8 @@ def get_state_dict(obj: Union[torch.nn.Module, Mapping]): def copy_model_state( - dst: Union[torch.nn.Module, Mapping], - src: Union[torch.nn.Module, Mapping], + dst: torch.nn.Module | Mapping, + src: torch.nn.Module | Mapping, dst_prefix="", mapping=None, exclude_vars=None, @@ -522,7 +526,7 @@ def copy_model_state( return dst_dict, updated_keys, unchanged_keys -def save_state(src: Union[torch.nn.Module, Dict], path: PathLike, **kwargs): +def save_state(src: torch.nn.Module | dict, path: PathLike, **kwargs): """ Save the state dict of input source data with PyTorch `save`. It can save `nn.Module`, `state_dict`, a dictionary of `nn.Module` or `state_dict`. @@ -546,7 +550,7 @@ def save_state(src: Union[torch.nn.Module, Dict], path: PathLike, **kwargs): """ - ckpt: Dict = {} + ckpt: dict = {} if isinstance(src, dict): for k, v in src.items(): ckpt[k] = get_state_dict(v) @@ -558,11 +562,11 @@ def save_state(src: Union[torch.nn.Module, Dict], path: PathLike, **kwargs): def convert_to_torchscript( model: nn.Module, - filename_or_obj: Optional[Any] = None, - extra_files: Optional[Dict] = None, + filename_or_obj: Any | None = None, + extra_files: dict | None = None, verify: bool = False, - inputs: Optional[Sequence[Any]] = None, - device: Optional[torch.device] = None, + inputs: Sequence[Any] | None = None, + device: torch.device | None = None, rtol: float = 1e-4, atol: float = 0.0, **kwargs, @@ -638,7 +642,7 @@ def _replace_modules( parent: torch.nn.Module, name: str, new_module: torch.nn.Module, - out: List[Tuple[str, torch.nn.Module]], + out: list[tuple[str, torch.nn.Module]], strict_match: bool = True, match_device: bool = True, ) -> None: @@ -656,7 +660,7 @@ def _replace_modules( parent_name = name[:idx] parent = getattr(parent, parent_name) name = name[idx + 1 :] - _out: List[Tuple[str, torch.nn.Module]] = [] + _out: list[tuple[str, torch.nn.Module]] = [] _replace_modules(parent, name, new_module, _out) # prepend the parent name out += [(f"{parent_name}.{r[0]}", r[1]) for r in _out] @@ -678,7 +682,7 @@ def replace_modules( new_module: torch.nn.Module, strict_match: bool = True, match_device: bool = True, -) -> List[Tuple[str, torch.nn.Module]]: +) -> list[tuple[str, torch.nn.Module]]: """ Replace sub-module(s) in a parent module. @@ -704,7 +708,7 @@ def replace_modules( Raises: AttributeError: if `strict_match` is `True` and `name` is not a named module in `parent`. """ - out: List[Tuple[str, torch.nn.Module]] = [] + out: list[tuple[str, torch.nn.Module]] = [] _replace_modules(parent, name, new_module, out, strict_match, match_device) return out @@ -722,7 +726,7 @@ def replace_modules_temp( See :py:class:`monai.networks.utils.replace_modules`. """ - replaced: List[Tuple[str, torch.nn.Module]] = [] + replaced: list[tuple[str, torch.nn.Module]] = [] try: # replace _replace_modules(parent, name, new_module, replaced, strict_match, match_device) diff --git a/monai/optimizers/__init__.py b/monai/optimizers/__init__.py index 8ce5d3f925..f0a3858ced 100644 --- a/monai/optimizers/__init__.py +++ b/monai/optimizers/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .lr_finder import LearningRateFinder from .lr_scheduler import ExponentialLR, LinearLR, WarmupCosineSchedule from .novograd import Novograd diff --git a/monai/optimizers/lr_finder.py b/monai/optimizers/lr_finder.py index ce092d33ab..2a37f5de19 100644 --- a/monai/optimizers/lr_finder.py +++ b/monai/optimizers/lr_finder.py @@ -9,10 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import pickle import warnings from functools import partial -from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple, Type, Union +from typing import TYPE_CHECKING, Any, Callable import numpy as np import torch @@ -181,9 +183,9 @@ def __init__( model: nn.Module, optimizer: Optimizer, criterion: torch.nn.Module, - device: Optional[Union[str, torch.device]] = None, + device: str | torch.device | None = None, memory_cache: bool = True, - cache_dir: Optional[str] = None, + cache_dir: str | None = None, amp: bool = False, pickle_module=pickle, pickle_protocol: int = DEFAULT_PROTOCOL, @@ -222,7 +224,7 @@ def __init__( self.model = model self.criterion = criterion - self.history: Dict[str, list] = {"lr": [], "loss": []} + self.history: dict[str, list] = {"lr": [], "loss": []} self.memory_cache = memory_cache self.cache_dir = cache_dir self.amp = amp @@ -250,10 +252,10 @@ def reset(self) -> None: def range_test( self, train_loader: DataLoader, - val_loader: Optional[DataLoader] = None, + val_loader: DataLoader | None = None, image_extractor: Callable = default_image_extractor, label_extractor: Callable = default_label_extractor, - start_lr: Optional[float] = None, + start_lr: float | None = None, end_lr: int = 10, num_iter: int = 100, step_mode: str = "exp", @@ -311,7 +313,7 @@ def range_test( raise ValueError("`num_iter` must be larger than 1") # Initialize the proper learning rate policy - lr_schedule: Union[ExponentialLR, LinearLR] + lr_schedule: ExponentialLR | LinearLR if step_mode.lower() == "exp": lr_schedule = ExponentialLR(self.optimizer, end_lr, num_iter) elif step_mode.lower() == "linear": @@ -327,7 +329,7 @@ def range_test( if val_loader: val_iter = ValDataLoaderIter(val_loader, image_extractor, label_extractor) - trange: Union[partial[tqdm.trange], Type[range]] + trange: partial[tqdm.trange] | type[range] if self.verbose and has_tqdm: trange = partial(tqdm.trange, desc="Computing optimal learning rate") tprint = tqdm.tqdm.write @@ -369,7 +371,7 @@ def range_test( print("Resetting model and optimizer") self.reset() - def _set_learning_rate(self, new_lrs: Union[float, list]) -> None: + def _set_learning_rate(self, new_lrs: float | list) -> None: """Set learning rate(s) for optimizer.""" if not isinstance(new_lrs, list): new_lrs = [new_lrs] * len(self.optimizer.param_groups) @@ -437,7 +439,7 @@ def _validate(self, val_iter: ValDataLoaderIter, non_blocking_transfer: bool = T return running_loss / len(val_iter.dataset) - def get_lrs_and_losses(self, skip_start: int = 0, skip_end: int = 0) -> Tuple[list, list]: + def get_lrs_and_losses(self, skip_start: int = 0, skip_end: int = 0) -> tuple[list, list]: """Get learning rates and their corresponding losses Args: @@ -457,9 +459,7 @@ def get_lrs_and_losses(self, skip_start: int = 0, skip_end: int = 0) -> Tuple[li return lrs, losses - def get_steepest_gradient( - self, skip_start: int = 0, skip_end: int = 0 - ) -> Union[Tuple[float, float], Tuple[None, None]]: + def get_steepest_gradient(self, skip_start: int = 0, skip_end: int = 0) -> tuple[float, float] | tuple[None, None]: """Get learning rate which has steepest gradient and its corresponding loss Args: diff --git a/monai/optimizers/lr_scheduler.py b/monai/optimizers/lr_scheduler.py index dc76a3dda1..b056e06a01 100644 --- a/monai/optimizers/lr_scheduler.py +++ b/monai/optimizers/lr_scheduler.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import math from torch.optim import Optimizer diff --git a/monai/optimizers/novograd.py b/monai/optimizers/novograd.py index 07a6aff90a..2eff19f99f 100644 --- a/monai/optimizers/novograd.py +++ b/monai/optimizers/novograd.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Iterable, Optional, Tuple +from __future__ import annotations + +from typing import Callable, Iterable import torch from torch.optim import Optimizer @@ -38,7 +40,7 @@ def __init__( self, params: Iterable, lr: float = 1e-3, - betas: Tuple[float, float] = (0.9, 0.98), + betas: tuple[float, float] = (0.9, 0.98), eps: float = 1e-8, weight_decay: float = 0, grad_averaging: bool = False, @@ -65,7 +67,7 @@ def __setstate__(self, state): for group in self.param_groups: group.setdefault("amsgrad", False) - def step(self, closure: Optional[Callable] = None): + def step(self, closure: Callable | None = None): """Performs a single optimization step. Arguments: diff --git a/monai/optimizers/utils.py b/monai/optimizers/utils.py index 5444aca191..0c4b53dacd 100644 --- a/monai/optimizers/utils.py +++ b/monai/optimizers/utils.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Sequence +from __future__ import annotations + +from collections.abc import Callable, Sequence import torch diff --git a/monai/transforms/__init__.py b/monai/transforms/__init__.py index 1fa03c0317..e519be83a3 100644 --- a/monai/transforms/__init__.py +++ b/monai/transforms/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .adaptors import FunctionSignature, adaptor, apply_alias, to_kwargs from .compose import Compose, OneOf, RandomOrder from .croppad.array import ( diff --git a/monai/transforms/adaptors.py b/monai/transforms/adaptors.py index 1edbcc63e2..a2f061d13d 100644 --- a/monai/transforms/adaptors.py +++ b/monai/transforms/adaptors.py @@ -121,6 +121,8 @@ def __call__(self, img, seg): """ +from __future__ import annotations + from typing import Callable from monai.utils import export as _monai_export diff --git a/monai/transforms/compose.py b/monai/transforms/compose.py index 0ce4433218..04fb12b463 100644 --- a/monai/transforms/compose.py +++ b/monai/transforms/compose.py @@ -12,8 +12,11 @@ A collection of generic interfaces for MONAI transforms. """ +from __future__ import annotations + import warnings -from typing import Any, Callable, Mapping, Optional, Sequence, Union +from collections.abc import Callable, Mapping, Sequence +from typing import Any import numpy as np @@ -116,7 +119,7 @@ class Compose(Randomizable, InvertibleTransform): def __init__( self, - transforms: Optional[Union[Sequence[Callable], Callable]] = None, + transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, log_stats: bool = False, @@ -129,7 +132,7 @@ def __init__( self.log_stats = log_stats self.set_random_state(seed=get_seed()) - def set_random_state(self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None) -> "Compose": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Compose: super().set_random_state(seed=seed, state=state) for _transform in self.transforms: if not isinstance(_transform, Randomizable): @@ -137,7 +140,7 @@ def set_random_state(self, seed: Optional[int] = None, state: Optional[np.random _transform.set_random_state(seed=self.R.randint(MAX_SEED, dtype="uint32")) return self - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: for _transform in self.transforms: if not isinstance(_transform, Randomizable): continue @@ -206,8 +209,8 @@ class OneOf(Compose): def __init__( self, - transforms: Optional[Union[Sequence[Callable], Callable]] = None, - weights: Optional[Union[Sequence[float], float]] = None, + transforms: Sequence[Callable] | Callable | None = None, + weights: Sequence[float] | float | None = None, map_items: bool = True, unpack_items: bool = False, log_stats: bool = False, @@ -303,7 +306,7 @@ class RandomOrder(Compose): def __init__( self, - transforms: Optional[Union[Sequence[Callable], Callable]] = None, + transforms: Sequence[Callable] | Callable | None = None, map_items: bool = True, unpack_items: bool = False, log_stats: bool = False, diff --git a/monai/transforms/croppad/array.py b/monai/transforms/croppad/array.py index a2ee02c553..be0476fd95 100644 --- a/monai/transforms/croppad/array.py +++ b/monai/transforms/croppad/array.py @@ -13,9 +13,12 @@ https://github.com/Project-MONAI/MONAI/wiki/MONAI_Design """ +from __future__ import annotations + +from collections.abc import Callable, Sequence from itertools import chain from math import ceil -from typing import Any, Callable, List, Optional, Sequence, Tuple, Union +from typing import Any import numpy as np import torch @@ -102,13 +105,13 @@ class Pad(InvertibleTransform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] def __init__( - self, to_pad: Optional[List[Tuple[int, int]]] = None, mode: str = PytorchPadMode.CONSTANT, **kwargs + self, to_pad: list[tuple[int, int]] | None = None, mode: str = PytorchPadMode.CONSTANT, **kwargs ) -> None: self.to_pad = to_pad self.mode = mode self.kwargs = kwargs - def compute_pad_width(self, spatial_shape: Sequence[int]) -> List[Tuple[int, int]]: + def compute_pad_width(self, spatial_shape: Sequence[int]) -> list[tuple[int, int]]: """ dynamically compute the pad width according to the spatial shape. the output is the amount of padding for all dimensions including the channel. @@ -138,7 +141,7 @@ def _pt_pad(img: torch.Tensor, pad_width, mode, **kwargs) -> torch.Tensor: return pad_pt(img.unsqueeze(0), pt_pad_width, mode=mode, **kwargs).squeeze(0) def __call__( # type: ignore - self, img: torch.Tensor, to_pad: Optional[List[Tuple[int, int]]] = None, mode: Optional[str] = None, **kwargs + self, img: torch.Tensor, to_pad: list[tuple[int, int]] | None = None, mode: str | None = None, **kwargs ) -> torch.Tensor: """ Args: @@ -198,7 +201,7 @@ def __call__( # type: ignore self.push_transform(out, orig_size=_orig_size, extra_info={"padded": to_pad_}) return out - def update_meta(self, tensor: MetaTensor, to_pad: List[Tuple[int, int]]): + def update_meta(self, tensor: MetaTensor, to_pad: list[tuple[int, int]]): spatial_rank = max(len(tensor.affine) - 1, 1) to_shift = [-s[0] for s in to_pad[1:]] # skipping the channel pad mat = create_translate(spatial_rank, to_shift) @@ -243,7 +246,7 @@ class SpatialPad(Pad): def __init__( self, - spatial_size: Union[Sequence[int], int, Tuple[Union[Tuple[int, ...], int], ...]], + spatial_size: Sequence[int] | int | tuple[tuple[int, ...] | int, ...], method: str = Method.SYMMETRIC, mode: str = PytorchPadMode.CONSTANT, **kwargs, @@ -252,7 +255,7 @@ def __init__( self.method: Method = look_up_option(method, Method) super().__init__(mode=mode, **kwargs) - def compute_pad_width(self, spatial_shape: Sequence[int]) -> List[Tuple[int, int]]: + def compute_pad_width(self, spatial_shape: Sequence[int]) -> list[tuple[int, int]]: """ dynamically compute the pad width according to the spatial shape. @@ -297,13 +300,11 @@ class BorderPad(Pad): """ - def __init__( - self, spatial_border: Union[Sequence[int], int], mode: str = PytorchPadMode.CONSTANT, **kwargs - ) -> None: + def __init__(self, spatial_border: Sequence[int] | int, mode: str = PytorchPadMode.CONSTANT, **kwargs) -> None: self.spatial_border = spatial_border super().__init__(mode=mode, **kwargs) - def compute_pad_width(self, spatial_shape: Sequence[int]) -> List[Tuple[int, int]]: + def compute_pad_width(self, spatial_shape: Sequence[int]) -> list[tuple[int, int]]: spatial_border = ensure_tuple(self.spatial_border) if not all(isinstance(b, int) for b in spatial_border): raise ValueError(f"self.spatial_border must contain only ints, got {spatial_border}.") @@ -331,11 +332,7 @@ class DivisiblePad(Pad): backend = SpatialPad.backend def __init__( - self, - k: Union[Sequence[int], int], - mode: str = PytorchPadMode.CONSTANT, - method: str = Method.SYMMETRIC, - **kwargs, + self, k: Sequence[int] | int, mode: str = PytorchPadMode.CONSTANT, method: str = Method.SYMMETRIC, **kwargs ) -> None: """ Args: @@ -359,7 +356,7 @@ def __init__( self.method: Method = Method(method) super().__init__(mode=mode, **kwargs) - def compute_pad_width(self, spatial_shape: Sequence[int]) -> List[Tuple[int, int]]: + def compute_pad_width(self, spatial_shape: Sequence[int]) -> list[tuple[int, int]]: new_size = compute_divisible_spatial_size(spatial_shape=spatial_shape, k=self.k) spatial_pad = SpatialPad(spatial_size=new_size, method=self.method) return spatial_pad.compute_pad_width(spatial_shape) @@ -375,11 +372,11 @@ class Crop(InvertibleTransform): @staticmethod def compute_slices( - roi_center: Union[Sequence[int], NdarrayOrTensor, None] = None, - roi_size: Union[Sequence[int], NdarrayOrTensor, None] = None, - roi_start: Union[Sequence[int], NdarrayOrTensor, None] = None, - roi_end: Union[Sequence[int], NdarrayOrTensor, None] = None, - roi_slices: Optional[Sequence[slice]] = None, + roi_center: Sequence[int] | NdarrayOrTensor | None = None, + roi_size: Sequence[int] | NdarrayOrTensor | None = None, + roi_start: Sequence[int] | NdarrayOrTensor | None = None, + roi_end: Sequence[int] | NdarrayOrTensor | None = None, + roi_slices: Sequence[slice] | None = None, ): """ Compute the crop slices based on specified `center & size` or `start & end` or `slices`. @@ -425,7 +422,7 @@ def compute_slices( else: return [slice(int(s), int(e)) for s, e in zip(roi_start_t.tolist(), roi_end_t.tolist())] - def __call__(self, img: torch.Tensor, slices: Tuple[slice, ...]) -> torch.Tensor: # type: ignore + def __call__(self, img: torch.Tensor, slices: tuple[slice, ...]) -> torch.Tensor: # type: ignore """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't apply to the channel dim. @@ -450,7 +447,7 @@ def __call__(self, img: torch.Tensor, slices: Tuple[slice, ...]) -> torch.Tensor self.push_transform(img_t, orig_size=_orig_size, extra_info={"cropped": cropped}) return img_t - def update_meta(self, tensor: MetaTensor, slices: Tuple[slice, ...]): + def update_meta(self, tensor: MetaTensor, slices: tuple[slice, ...]): spatial_rank = max(len(tensor.affine) - 1, 1) to_shift = [s.start if s.start is not None else 0 for s in ensure_tuple(slices)[1:]] mat = create_translate(spatial_rank, to_shift) @@ -482,11 +479,11 @@ class SpatialCrop(Crop): def __init__( self, - roi_center: Union[Sequence[int], NdarrayOrTensor, None] = None, - roi_size: Union[Sequence[int], NdarrayOrTensor, None] = None, - roi_start: Union[Sequence[int], NdarrayOrTensor, None] = None, - roi_end: Union[Sequence[int], NdarrayOrTensor, None] = None, - roi_slices: Optional[Sequence[slice]] = None, + roi_center: Sequence[int] | NdarrayOrTensor | None = None, + roi_size: Sequence[int] | NdarrayOrTensor | None = None, + roi_start: Sequence[int] | NdarrayOrTensor | None = None, + roi_end: Sequence[int] | NdarrayOrTensor | None = None, + roi_slices: Sequence[slice] | None = None, ) -> None: """ Args: @@ -526,7 +523,7 @@ class CenterSpatialCrop(Crop): the spatial size of output data will be [32, 40, 40]. """ - def __init__(self, roi_size: Union[Sequence[int], int]) -> None: + def __init__(self, roi_size: Sequence[int] | int) -> None: self.roi_size = roi_size def compute_slices(self, spatial_size: Sequence[int]): # type: ignore @@ -553,7 +550,7 @@ class CenterScaleCrop(Crop): """ - def __init__(self, roi_scale: Union[Sequence[float], float]): + def __init__(self, roi_scale: Sequence[float] | float): self.roi_scale = roi_scale def __call__(self, img: torch.Tensor) -> torch.Tensor: # type: ignore @@ -590,8 +587,8 @@ class RandSpatialCrop(Randomizable, Crop): def __init__( self, - roi_size: Union[Sequence[int], int], - max_roi_size: Optional[Union[Sequence[int], int]] = None, + roi_size: Sequence[int] | int, + max_roi_size: Sequence[int] | int | None = None, random_center: bool = True, random_size: bool = True, ) -> None: @@ -599,8 +596,8 @@ def __init__( self.max_roi_size = max_roi_size self.random_center = random_center self.random_size = random_size - self._size: Optional[Sequence[int]] = None - self._slices: Tuple[slice, ...] + self._size: Sequence[int] | None = None + self._slices: tuple[slice, ...] def randomize(self, img_size: Sequence[int]) -> None: self._size = fall_back_tuple(self.roi_size, img_size) @@ -652,8 +649,8 @@ class RandScaleCrop(RandSpatialCrop): def __init__( self, - roi_scale: Union[Sequence[float], float], - max_roi_scale: Optional[Union[Sequence[float], float]] = None, + roi_scale: Sequence[float] | float, + max_roi_scale: Sequence[float] | float | None = None, random_center: bool = True, random_size: bool = True, ) -> None: @@ -718,9 +715,9 @@ class RandSpatialCropSamples(Randomizable, TraceableTransform): def __init__( self, - roi_size: Union[Sequence[int], int], + roi_size: Sequence[int] | int, num_samples: int, - max_roi_size: Optional[Union[Sequence[int], int]] = None, + max_roi_size: Sequence[int] | int | None = None, random_center: bool = True, random_size: bool = True, ) -> None: @@ -730,16 +727,16 @@ def __init__( self.cropper = RandSpatialCrop(roi_size, max_roi_size, random_center, random_size) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandSpatialCropSamples": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandSpatialCropSamples: super().set_random_state(seed, state) self.cropper.set_random_state(seed, state) return self - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: pass - def __call__(self, img: torch.Tensor) -> List[torch.Tensor]: + def __call__(self, img: torch.Tensor) -> list[torch.Tensor]: """ Apply the transform to `img`, assuming `img` is channel-first and cropping doesn't change the channel dim. @@ -790,11 +787,11 @@ def threshold_at_one(x): def __init__( self, select_fn: Callable = is_positive, - channel_indices: Optional[IndexSelection] = None, - margin: Union[Sequence[int], int] = 0, + channel_indices: IndexSelection | None = None, + margin: Sequence[int] | int = 0, allow_smaller: bool = True, return_coords: bool = False, - k_divisible: Union[Sequence[int], int] = 1, + k_divisible: Sequence[int] | int = 1, mode: str = PytorchPadMode.CONSTANT, **pad_kwargs, ) -> None: @@ -848,7 +845,7 @@ def compute_bounding_box(self, img: torch.Tensor): return box_start_, box_end_ def crop_pad( - self, img: torch.Tensor, box_start: np.ndarray, box_end: np.ndarray, mode: Optional[str] = None, **pad_kwargs + self, img: torch.Tensor, box_start: np.ndarray, box_end: np.ndarray, mode: str | None = None, **pad_kwargs ): """ Crop and pad based on the bounding box. @@ -869,7 +866,7 @@ def crop_pad( ret_.applied_operations[-1][TraceKeys.EXTRA_INFO]["pad_info"] = app_op return ret - def __call__(self, img: torch.Tensor, mode: Optional[str] = None, **pad_kwargs): # type: ignore + def __call__(self, img: torch.Tensor, mode: str | None = None, **pad_kwargs): # type: ignore """ Apply the transform to `img`, assuming `img` is channel-first and slicing doesn't change the channel dim. @@ -908,15 +905,12 @@ class RandWeightedCrop(Randomizable, TraceableTransform): backend = SpatialCrop.backend def __init__( - self, - spatial_size: Union[Sequence[int], int], - num_samples: int = 1, - weight_map: Optional[NdarrayOrTensor] = None, + self, spatial_size: Sequence[int] | int, num_samples: int = 1, weight_map: NdarrayOrTensor | None = None ): self.spatial_size = ensure_tuple(spatial_size) self.num_samples = int(num_samples) self.weight_map = weight_map - self.centers: List[np.ndarray] = [] + self.centers: list[np.ndarray] = [] def randomize(self, weight_map: NdarrayOrTensor) -> None: self.centers = weighted_patch_samples( @@ -924,8 +918,8 @@ def randomize(self, weight_map: NdarrayOrTensor) -> None: ) # using only the first channel as weight map def __call__( - self, img: torch.Tensor, weight_map: Optional[NdarrayOrTensor] = None, randomize: bool = True - ) -> List[torch.Tensor]: + self, img: torch.Tensor, weight_map: NdarrayOrTensor | None = None, randomize: bool = True + ) -> list[torch.Tensor]: """ Args: img: input image to sample patches from. assuming `img` is a channel-first array. @@ -947,7 +941,7 @@ def __call__( if randomize: self.randomize(weight_map) _spatial_size = fall_back_tuple(self.spatial_size, weight_map.shape[1:]) - results: List[torch.Tensor] = [] + results: list[torch.Tensor] = [] orig_size = img.shape[1:] for i, center in enumerate(self.centers): cropped = SpatialCrop(roi_center=center, roi_size=_spatial_size)(img) @@ -1019,15 +1013,15 @@ class RandCropByPosNegLabel(Randomizable, TraceableTransform): def __init__( self, - spatial_size: Union[Sequence[int], int], - label: Optional[torch.Tensor] = None, + spatial_size: Sequence[int] | int, + label: torch.Tensor | None = None, pos: float = 1.0, neg: float = 1.0, num_samples: int = 1, - image: Optional[torch.Tensor] = None, + image: torch.Tensor | None = None, image_threshold: float = 0.0, - fg_indices: Optional[NdarrayOrTensor] = None, - bg_indices: Optional[NdarrayOrTensor] = None, + fg_indices: NdarrayOrTensor | None = None, + bg_indices: NdarrayOrTensor | None = None, allow_smaller: bool = False, ) -> None: self.spatial_size = spatial_size @@ -1040,7 +1034,7 @@ def __init__( self.num_samples = num_samples self.image = image self.image_threshold = image_threshold - self.centers: Optional[List[List[int]]] = None + self.centers: list[list[int]] | None = None self.fg_indices = fg_indices self.bg_indices = bg_indices self.allow_smaller = allow_smaller @@ -1048,9 +1042,9 @@ def __init__( def randomize( self, label: torch.Tensor, - fg_indices: Optional[NdarrayOrTensor] = None, - bg_indices: Optional[NdarrayOrTensor] = None, - image: Optional[torch.Tensor] = None, + fg_indices: NdarrayOrTensor | None = None, + bg_indices: NdarrayOrTensor | None = None, + image: torch.Tensor | None = None, ) -> None: if fg_indices is None or bg_indices is None: if self.fg_indices is not None and self.bg_indices is not None: @@ -1075,12 +1069,12 @@ def randomize( def __call__( self, img: torch.Tensor, - label: Optional[torch.Tensor] = None, - image: Optional[torch.Tensor] = None, - fg_indices: Optional[NdarrayOrTensor] = None, - bg_indices: Optional[NdarrayOrTensor] = None, + label: torch.Tensor | None = None, + image: torch.Tensor | None = None, + fg_indices: NdarrayOrTensor | None = None, + bg_indices: NdarrayOrTensor | None = None, randomize: bool = True, - ) -> List[torch.Tensor]: + ) -> list[torch.Tensor]: """ Args: img: input data to crop samples from based on the pos/neg ratio of `label` and `image`. @@ -1105,7 +1099,7 @@ def __call__( if randomize: self.randomize(label, fg_indices, bg_indices, image) - results: List[torch.Tensor] = [] + results: list[torch.Tensor] = [] orig_size = img.shape[1:] if self.centers is not None: for i, center in enumerate(self.centers): @@ -1188,14 +1182,14 @@ class RandCropByLabelClasses(Randomizable, TraceableTransform): def __init__( self, - spatial_size: Union[Sequence[int], int], - ratios: Optional[List[Union[float, int]]] = None, - label: Optional[torch.Tensor] = None, - num_classes: Optional[int] = None, + spatial_size: Sequence[int] | int, + ratios: list[float | int] | None = None, + label: torch.Tensor | None = None, + num_classes: int | None = None, num_samples: int = 1, - image: Optional[torch.Tensor] = None, + image: torch.Tensor | None = None, image_threshold: float = 0.0, - indices: Optional[List[NdarrayOrTensor]] = None, + indices: list[NdarrayOrTensor] | None = None, allow_smaller: bool = False, ) -> None: self.spatial_size = spatial_size @@ -1205,12 +1199,12 @@ def __init__( self.num_samples = num_samples self.image = image self.image_threshold = image_threshold - self.centers: Optional[List[List[int]]] = None + self.centers: list[list[int]] | None = None self.indices = indices self.allow_smaller = allow_smaller def randomize( - self, label: torch.Tensor, indices: Optional[List[NdarrayOrTensor]] = None, image: Optional[torch.Tensor] = None + self, label: torch.Tensor, indices: list[NdarrayOrTensor] | None = None, image: torch.Tensor | None = None ) -> None: indices_: Sequence[NdarrayOrTensor] if indices is None: @@ -1227,11 +1221,11 @@ def randomize( def __call__( self, img: torch.Tensor, - label: Optional[torch.Tensor] = None, - image: Optional[torch.Tensor] = None, - indices: Optional[List[NdarrayOrTensor]] = None, + label: torch.Tensor | None = None, + image: torch.Tensor | None = None, + indices: list[NdarrayOrTensor] | None = None, randomize: bool = True, - ) -> List[torch.Tensor]: + ) -> list[torch.Tensor]: """ Args: img: input data to crop samples from based on the ratios of every class, assumes `img` is a @@ -1252,7 +1246,7 @@ def __call__( if randomize: self.randomize(label, indices, image) - results: List[torch.Tensor] = [] + results: list[torch.Tensor] = [] orig_size = img.shape[1:] if self.centers is not None: for i, center in enumerate(self.centers): @@ -1295,7 +1289,7 @@ class ResizeWithPadOrCrop(InvertibleTransform): def __init__( self, - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, method: str = Method.SYMMETRIC, mode: str = PytorchPadMode.CONSTANT, **pad_kwargs, @@ -1303,7 +1297,7 @@ def __init__( self.padder = SpatialPad(spatial_size=spatial_size, method=method, mode=mode, **pad_kwargs) self.cropper = CenterSpatialCrop(roi_size=spatial_size) - def __call__(self, img: torch.Tensor, mode: Optional[str] = None, **pad_kwargs) -> torch.Tensor: # type: ignore + def __call__(self, img: torch.Tensor, mode: str | None = None, **pad_kwargs) -> torch.Tensor: # type: ignore """ Args: img: data to pad or crop, assuming `img` is channel-first and diff --git a/monai/transforms/croppad/batch.py b/monai/transforms/croppad/batch.py index a3cb15144b..095edbadf6 100644 --- a/monai/transforms/croppad/batch.py +++ b/monai/transforms/croppad/batch.py @@ -13,7 +13,10 @@ https://github.com/Project-MONAI/MONAI/wiki/MONAI_Design """ -from typing import Any, Dict, Hashable, Mapping +from __future__ import annotations + +from collections.abc import Hashable, Mapping +from typing import Any import numpy as np import torch @@ -112,7 +115,7 @@ def __call__(self, batch: Any): return list_data_collate(batch) @staticmethod - def inverse(data: dict) -> Dict[Hashable, np.ndarray]: + def inverse(data: dict) -> dict[Hashable, np.ndarray]: if not isinstance(data, Mapping): raise RuntimeError("Inverse can only currently be applied on dictionaries.") @@ -125,7 +128,7 @@ def inverse(data: dict) -> Dict[Hashable, np.ndarray]: transform_key = InvertibleTransform.trace_key(key) if transform_key in d: transforms = d[transform_key] - if not transforms or not isinstance(transforms[-1], Dict): + if not transforms or not isinstance(transforms[-1], dict): continue if transforms[-1].get(TraceKeys.CLASS_NAME) == PadListDataCollate.__name__: xform = transforms.pop() diff --git a/monai/transforms/croppad/dictionary.py b/monai/transforms/croppad/dictionary.py index bae6705c22..d5d57f9e04 100644 --- a/monai/transforms/croppad/dictionary.py +++ b/monai/transforms/croppad/dictionary.py @@ -15,8 +15,11 @@ Class names are ended with 'd' to denote dictionary-based transforms. """ +from __future__ import annotations + +from collections.abc import Callable, Hashable, Mapping, Sequence from copy import deepcopy -from typing import Any, Callable, Dict, Hashable, List, Mapping, Optional, Sequence, Union +from typing import Any import numpy as np import torch @@ -141,13 +144,13 @@ def __init__( self.padder = padder self.mode = ensure_tuple_rep(mode, len(self.keys)) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key, m in self.key_iterator(d, self.mode): d[key] = self.padder(d[key], mode=m) return d - def inverse(self, data: Mapping[Hashable, MetaTensor]) -> Dict[Hashable, MetaTensor]: + def inverse(self, data: Mapping[Hashable, MetaTensor]) -> dict[Hashable, MetaTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.padder.inverse(d[key]) @@ -164,7 +167,7 @@ class SpatialPadd(Padd): def __init__( self, keys: KeysCollection, - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, method: str = Method.SYMMETRIC, mode: SequenceStr = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, @@ -208,7 +211,7 @@ class BorderPadd(Padd): def __init__( self, keys: KeysCollection, - spatial_border: Union[Sequence[int], int], + spatial_border: Sequence[int] | int, mode: SequenceStr = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, **kwargs, @@ -255,7 +258,7 @@ class DivisiblePadd(Padd): def __init__( self, keys: KeysCollection, - k: Union[Sequence[int], int], + k: Sequence[int] | int, mode: SequenceStr = PytorchPadMode.CONSTANT, method: str = Method.SYMMETRIC, allow_missing_keys: bool = False, @@ -306,13 +309,13 @@ def __init__(self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool super().__init__(keys, allow_missing_keys) self.cropper = cropper - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.cropper(d[key]) # type: ignore return d - def inverse(self, data: Mapping[Hashable, MetaTensor]) -> Dict[Hashable, MetaTensor]: + def inverse(self, data: Mapping[Hashable, MetaTensor]) -> dict[Hashable, MetaTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.cropper.inverse(d[key]) @@ -336,9 +339,7 @@ class RandCropd(Cropd, Randomizable): def __init__(self, keys: KeysCollection, cropper: Crop, allow_missing_keys: bool = False): super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandCropd": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandCropd: super().set_random_state(seed, state) if isinstance(self.cropper, Randomizable): self.cropper.set_random_state(seed, state) @@ -348,7 +349,7 @@ def randomize(self, img_size: Sequence[int]) -> None: if isinstance(self.cropper, Randomizable): self.cropper.randomize(img_size) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) # the first key must exist to execute random operations self.randomize(d[self.first_key(d)].shape[1:]) @@ -376,11 +377,11 @@ class SpatialCropd(Cropd): def __init__( self, keys: KeysCollection, - roi_center: Optional[Sequence[int]] = None, - roi_size: Optional[Sequence[int]] = None, - roi_start: Optional[Sequence[int]] = None, - roi_end: Optional[Sequence[int]] = None, - roi_slices: Optional[Sequence[slice]] = None, + roi_center: Sequence[int] | None = None, + roi_size: Sequence[int] | None = None, + roi_start: Sequence[int] | None = None, + roi_end: Sequence[int] | None = None, + roi_slices: Sequence[slice] | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -419,9 +420,7 @@ class CenterSpatialCropd(Cropd): allow_missing_keys: don't raise exception if key is missing. """ - def __init__( - self, keys: KeysCollection, roi_size: Union[Sequence[int], int], allow_missing_keys: bool = False - ) -> None: + def __init__(self, keys: KeysCollection, roi_size: Sequence[int] | int, allow_missing_keys: bool = False) -> None: cropper = CenterSpatialCrop(roi_size) super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys) @@ -441,7 +440,7 @@ class CenterScaleCropd(Cropd): """ def __init__( - self, keys: KeysCollection, roi_scale: Union[Sequence[float], float], allow_missing_keys: bool = False + self, keys: KeysCollection, roi_scale: Sequence[float] | float, allow_missing_keys: bool = False ) -> None: cropper = CenterScaleCrop(roi_scale) super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys) @@ -480,8 +479,8 @@ class RandSpatialCropd(RandCropd): def __init__( self, keys: KeysCollection, - roi_size: Union[Sequence[int], int], - max_roi_size: Optional[Union[Sequence[int], int]] = None, + roi_size: Sequence[int] | int, + max_roi_size: Sequence[int] | int | None = None, random_center: bool = True, random_size: bool = True, allow_missing_keys: bool = False, @@ -518,8 +517,8 @@ class RandScaleCropd(RandCropd): def __init__( self, keys: KeysCollection, - roi_scale: Union[Sequence[float], float], - max_roi_scale: Optional[Union[Sequence[float], float]] = None, + roi_scale: Sequence[float] | float, + max_roi_scale: Sequence[float] | float | None = None, random_center: bool = True, random_size: bool = True, allow_missing_keys: bool = False, @@ -571,23 +570,23 @@ class RandSpatialCropSamplesd(Randomizable, MapTransform): def __init__( self, keys: KeysCollection, - roi_size: Union[Sequence[int], int], + roi_size: Sequence[int] | int, num_samples: int, - max_roi_size: Optional[Union[Sequence[int], int]] = None, + max_roi_size: Sequence[int] | int | None = None, random_center: bool = True, random_size: bool = True, - meta_keys: Optional[KeysCollection] = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = "meta_dict", allow_missing_keys: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) self.cropper = RandSpatialCropSamples(roi_size, num_samples, max_roi_size, random_center, random_size) - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: self.sub_seed = self.R.randint(MAX_SEED, dtype="uint32") - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> List[Dict[Hashable, torch.Tensor]]: - ret: List[Dict[Hashable, torch.Tensor]] = [dict(data) for _ in range(self.cropper.num_samples)] + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> list[dict[Hashable, torch.Tensor]]: + ret: list[dict[Hashable, torch.Tensor]] = [dict(data) for _ in range(self.cropper.num_samples)] # deep copy all the unmodified data for i in range(self.cropper.num_samples): for key in set(data.keys()).difference(set(self.keys)): @@ -620,10 +619,10 @@ def __init__( keys: KeysCollection, source_key: str, select_fn: Callable = is_positive, - channel_indices: Optional[IndexSelection] = None, - margin: Union[Sequence[int], int] = 0, + channel_indices: IndexSelection | None = None, + margin: Sequence[int] | int = 0, allow_smaller: bool = True, - k_divisible: Union[Sequence[int], int] = 1, + k_divisible: Sequence[int] | int = 1, mode: SequenceStr = PytorchPadMode.CONSTANT, start_coord_key: str = "foreground_start_coord", end_coord_key: str = "foreground_end_coord", @@ -672,7 +671,7 @@ def __init__( super().__init__(keys, cropper=cropper, allow_missing_keys=allow_missing_keys) self.mode = ensure_tuple_rep(mode, len(self.keys)) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) self.cropper: CropForeground box_start, box_end = self.cropper.compute_bounding_box(img=d[self.source_key]) @@ -712,10 +711,10 @@ def __init__( self, keys: KeysCollection, w_key: str, - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, num_samples: int = 1, - center_coord_key: Optional[str] = None, - meta_keys: Optional[KeysCollection] = None, + center_coord_key: str | None = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = "meta_dict", allow_missing_keys: bool = False, ): @@ -724,8 +723,8 @@ def __init__( self.cropper = RandWeightedCrop(spatial_size, num_samples) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandWeightedCropd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandWeightedCropd: super().set_random_state(seed, state) self.cropper.set_random_state(seed, state) return self @@ -733,9 +732,9 @@ def set_random_state( def randomize(self, weight_map: NdarrayOrTensor) -> None: self.cropper.randomize(weight_map) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> List[Dict[Hashable, torch.Tensor]]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> list[dict[Hashable, torch.Tensor]]: # output starts as empty list of dictionaries - ret: List = [dict(data) for _ in range(self.cropper.num_samples)] + ret: list = [dict(data) for _ in range(self.cropper.num_samples)] # deep copy all the unmodified data for i in range(self.cropper.num_samples): for key in set(data.keys()).difference(set(self.keys)): @@ -808,15 +807,15 @@ def __init__( self, keys: KeysCollection, label_key: str, - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, pos: float = 1.0, neg: float = 1.0, num_samples: int = 1, - image_key: Optional[str] = None, + image_key: str | None = None, image_threshold: float = 0.0, - fg_indices_key: Optional[str] = None, - bg_indices_key: Optional[str] = None, - meta_keys: Optional[KeysCollection] = None, + fg_indices_key: str | None = None, + bg_indices_key: str | None = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = "meta_dict", allow_smaller: bool = False, allow_missing_keys: bool = False, @@ -836,8 +835,8 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandCropByPosNegLabeld": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandCropByPosNegLabeld: super().set_random_state(seed, state) self.cropper.set_random_state(seed, state) return self @@ -845,13 +844,13 @@ def set_random_state( def randomize( self, label: torch.Tensor, - fg_indices: Optional[NdarrayOrTensor] = None, - bg_indices: Optional[NdarrayOrTensor] = None, - image: Optional[torch.Tensor] = None, + fg_indices: NdarrayOrTensor | None = None, + bg_indices: NdarrayOrTensor | None = None, + image: torch.Tensor | None = None, ) -> None: self.cropper.randomize(label=label, fg_indices=fg_indices, bg_indices=bg_indices, image=image) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> List[Dict[Hashable, torch.Tensor]]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> list[dict[Hashable, torch.Tensor]]: d = dict(data) label = d[self.label_key] image = d[self.image_key] if self.image_key else None @@ -861,7 +860,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> List[Dict[Hashable, self.randomize(label, fg_indices, bg_indices, image) # initialize returned list with shallow copy to preserve key ordering - ret: List = [dict(d) for _ in range(self.cropper.num_samples)] + ret: list = [dict(d) for _ in range(self.cropper.num_samples)] # deep copy all the unmodified data for i in range(self.cropper.num_samples): for key in set(d.keys()).difference(set(self.keys)): @@ -953,14 +952,14 @@ def __init__( self, keys: KeysCollection, label_key: str, - spatial_size: Union[Sequence[int], int], - ratios: Optional[List[Union[float, int]]] = None, - num_classes: Optional[int] = None, + spatial_size: Sequence[int] | int, + ratios: list[float | int] | None = None, + num_classes: int | None = None, num_samples: int = 1, - image_key: Optional[str] = None, + image_key: str | None = None, image_threshold: float = 0.0, - indices_key: Optional[str] = None, - meta_keys: Optional[KeysCollection] = None, + indices_key: str | None = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = "meta_dict", allow_smaller: bool = False, allow_missing_keys: bool = False, @@ -979,18 +978,18 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandCropByLabelClassesd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandCropByLabelClassesd: super().set_random_state(seed, state) self.cropper.set_random_state(seed, state) return self def randomize( - self, label: torch.Tensor, indices: Optional[List[NdarrayOrTensor]] = None, image: Optional[torch.Tensor] = None + self, label: torch.Tensor, indices: list[NdarrayOrTensor] | None = None, image: torch.Tensor | None = None ) -> None: self.cropper.randomize(label=label, indices=indices, image=image) - def __call__(self, data: Mapping[Hashable, Any]) -> List[Dict[Hashable, torch.Tensor]]: + def __call__(self, data: Mapping[Hashable, Any]) -> list[dict[Hashable, torch.Tensor]]: d = dict(data) label = d[self.label_key] image = d[self.image_key] if self.image_key else None @@ -999,7 +998,7 @@ def __call__(self, data: Mapping[Hashable, Any]) -> List[Dict[Hashable, torch.Te self.randomize(label, indices, image) # initialize returned list with shallow copy to preserve key ordering - ret: List = [dict(d) for _ in range(self.cropper.num_samples)] + ret: list = [dict(d) for _ in range(self.cropper.num_samples)] # deep copy all the unmodified data for i in range(self.cropper.num_samples): for key in set(d.keys()).difference(set(self.keys)): @@ -1038,7 +1037,7 @@ class ResizeWithPadOrCropd(Padd): def __init__( self, keys: KeysCollection, - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, mode: SequenceStr = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, method: str = Method.SYMMETRIC, @@ -1074,7 +1073,7 @@ def __init__( self.bbox = BoundingRect(select_fn=select_fn) self.bbox_key_postfix = bbox_key_postfix - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: """ See also: :py:class:`monai.transforms.utils.generate_spatial_bounding_box`. """ diff --git a/monai/transforms/intensity/array.py b/monai/transforms/intensity/array.py index c136b3aa09..67afd7f3c3 100644 --- a/monai/transforms/intensity/array.py +++ b/monai/transforms/intensity/array.py @@ -13,10 +13,12 @@ https://github.com/Project-MONAI/MONAI/wiki/MONAI_Design """ +from __future__ import annotations + from abc import abstractmethod -from collections.abc import Iterable +from collections.abc import Callable, Iterable, Sequence from functools import partial -from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union +from typing import Any from warnings import warn import numpy as np @@ -96,9 +98,9 @@ def __init__(self, prob: float = 0.1, mean: float = 0.0, std: float = 0.1, dtype self.mean = mean self.std = std self.dtype = dtype - self.noise: Optional[np.ndarray] = None + self.noise: np.ndarray | None = None - def randomize(self, img: NdarrayOrTensor, mean: Optional[float] = None) -> None: + def randomize(self, img: NdarrayOrTensor, mean: float | None = None) -> None: super().randomize(None) if not self._do_transform: return None @@ -107,7 +109,7 @@ def randomize(self, img: NdarrayOrTensor, mean: Optional[float] = None) -> None: # noise is float64 array, convert to the output dtype to save memory self.noise, *_ = convert_data_type(noise, dtype=self.dtype) - def __call__(self, img: NdarrayOrTensor, mean: Optional[float] = None, randomize: bool = True) -> NdarrayOrTensor: + def __call__(self, img: NdarrayOrTensor, mean: float | None = None, randomize: bool = True) -> NdarrayOrTensor: """ Apply the transform to `img`. """ @@ -155,8 +157,8 @@ class RandRicianNoise(RandomizableTransform): def __init__( self, prob: float = 0.1, - mean: Union[Sequence[float], float] = 0.0, - std: Union[Sequence[float], float] = 1.0, + mean: Sequence[float] | float = 0.0, + std: Sequence[float] | float = 1.0, channel_wise: bool = False, relative: bool = False, sample_std: bool = True, @@ -230,7 +232,7 @@ def __init__(self, offset: float, safe: bool = False) -> None: self.offset = offset self.safe = safe - def __call__(self, img: NdarrayOrTensor, offset: Optional[float] = None) -> NdarrayOrTensor: + def __call__(self, img: NdarrayOrTensor, offset: float | None = None) -> NdarrayOrTensor: """ Apply the transform to `img`. """ @@ -250,7 +252,7 @@ class RandShiftIntensity(RandomizableTransform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, offsets: Union[Tuple[float, float], float], safe: bool = False, prob: float = 0.1) -> None: + def __init__(self, offsets: tuple[float, float] | float, safe: bool = False, prob: float = 0.1) -> None: """ Args: offsets: offset range to randomly shift. @@ -269,13 +271,13 @@ def __init__(self, offsets: Union[Tuple[float, float], float], safe: bool = Fals self._offset = self.offsets[0] self._shifter = ShiftIntensity(self._offset, safe) - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if not self._do_transform: return None self._offset = self.R.uniform(low=self.offsets[0], high=self.offsets[1]) - def __call__(self, img: NdarrayOrTensor, factor: Optional[float] = None, randomize: bool = True) -> NdarrayOrTensor: + def __call__(self, img: NdarrayOrTensor, factor: float | None = None, randomize: bool = True) -> NdarrayOrTensor: """ Apply the transform to `img`. @@ -359,7 +361,7 @@ class RandStdShiftIntensity(RandomizableTransform): def __init__( self, - factors: Union[Tuple[float, float], float], + factors: tuple[float, float] | float, prob: float = 0.1, nonzero: bool = False, channel_wise: bool = False, @@ -387,7 +389,7 @@ def __init__( self.channel_wise = channel_wise self.dtype = dtype - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if not self._do_transform: return None @@ -420,9 +422,9 @@ class ScaleIntensity(Transform): def __init__( self, - minv: Optional[float] = 0.0, - maxv: Optional[float] = 1.0, - factor: Optional[float] = None, + minv: float | None = 0.0, + maxv: float | None = 1.0, + factor: float | None = None, channel_wise: bool = False, dtype: DtypeLike = np.float32, ) -> None: @@ -473,9 +475,7 @@ class RandScaleIntensity(RandomizableTransform): backend = ScaleIntensity.backend - def __init__( - self, factors: Union[Tuple[float, float], float], prob: float = 0.1, dtype: DtypeLike = np.float32 - ) -> None: + def __init__(self, factors: tuple[float, float] | float, prob: float = 0.1, dtype: DtypeLike = np.float32) -> None: """ Args: factors: factor range to randomly scale by ``v = v * (1 + factor)``. @@ -494,7 +494,7 @@ def __init__( self.factor = self.factors[0] self.dtype = dtype - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if not self._do_transform: return None @@ -539,7 +539,7 @@ class RandBiasField(RandomizableTransform): def __init__( self, degree: int = 3, - coeff_range: Tuple[float, float] = (0.0, 0.1), + coeff_range: tuple[float, float] = (0.0, 0.1), dtype: DtypeLike = np.float32, prob: float = 0.1, ) -> None: @@ -563,7 +563,7 @@ def _generate_random_field(self, spatial_shape: Sequence[int], degree: int, coef coeff_mat[np.tril_indices(degree + 1)] = coeff return np.polynomial.legendre.leggrid2d(coords[0], coords[1], coeff_mat) if rank == 3: - pts: List[List[int]] = [[0, 0, 0]] + pts: list[list[int]] = [[0, 0, 0]] for i in range(degree + 1): for j in range(degree + 1 - i): for k in range(degree + 1 - i - j): @@ -629,8 +629,8 @@ class NormalizeIntensity(Transform): def __init__( self, - subtrahend: Union[Sequence, NdarrayOrTensor, None] = None, - divisor: Union[Sequence, NdarrayOrTensor, None] = None, + subtrahend: Sequence | NdarrayOrTensor | None = None, + divisor: Sequence | NdarrayOrTensor | None = None, nonzero: bool = False, channel_wise: bool = False, dtype: DtypeLike = np.float32, @@ -764,8 +764,8 @@ def __init__( self, a_min: float, a_max: float, - b_min: Optional[float] = None, - b_max: Optional[float] = None, + b_min: float | None = None, + b_max: float | None = None, clip: bool = False, dtype: DtypeLike = np.float32, ) -> None: @@ -841,7 +841,7 @@ class RandAdjustContrast(RandomizableTransform): backend = AdjustContrast.backend - def __init__(self, prob: float = 0.1, gamma: Union[Sequence[float], float] = (0.5, 4.5)) -> None: + def __init__(self, prob: float = 0.1, gamma: Sequence[float] | float = (0.5, 4.5)) -> None: RandomizableTransform.__init__(self, prob) if isinstance(gamma, (int, float)): @@ -855,9 +855,9 @@ def __init__(self, prob: float = 0.1, gamma: Union[Sequence[float], float] = (0. else: self.gamma = (min(gamma), max(gamma)) - self.gamma_value: Optional[float] = None + self.gamma_value: float | None = None - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if not self._do_transform: return None @@ -947,8 +947,8 @@ def __init__( self, lower: float, upper: float, - b_min: Optional[float], - b_max: Optional[float], + b_min: float | None, + b_max: float | None, clip: bool = False, relative: bool = False, channel_wise: bool = False, @@ -1020,11 +1020,11 @@ class MaskIntensity(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, mask_data: Optional[NdarrayOrTensor] = None, select_fn: Callable = is_positive) -> None: + def __init__(self, mask_data: NdarrayOrTensor | None = None, select_fn: Callable = is_positive) -> None: self.mask_data = mask_data self.select_fn = select_fn - def __call__(self, img: NdarrayOrTensor, mask_data: Optional[NdarrayOrTensor] = None) -> NdarrayOrTensor: + def __call__(self, img: NdarrayOrTensor, mask_data: NdarrayOrTensor | None = None) -> NdarrayOrTensor: """ Args: mask_data: if mask data is single channel, apply to every channel @@ -1113,7 +1113,7 @@ class DetectEnvelope(Transform): backend = [TransformBackends.TORCH] - def __init__(self, axis: int = 1, n: Union[int, None] = None) -> None: + def __init__(self, axis: int = 1, n: int | None = None) -> None: if axis < 0: raise ValueError("axis must be zero or positive.") @@ -1157,7 +1157,7 @@ class MedianSmooth(Transform): backend = [TransformBackends.TORCH] - def __init__(self, radius: Union[Sequence[int], int] = 1) -> None: + def __init__(self, radius: Sequence[int] | int = 1) -> None: self.radius = radius def __call__(self, img: NdarrayTensor) -> NdarrayTensor: @@ -1187,14 +1187,14 @@ class GaussianSmooth(Transform): backend = [TransformBackends.TORCH] - def __init__(self, sigma: Union[Sequence[float], float] = 1.0, approx: str = "erf") -> None: + def __init__(self, sigma: Sequence[float] | float = 1.0, approx: str = "erf") -> None: self.sigma = sigma self.approx = approx def __call__(self, img: NdarrayTensor) -> NdarrayTensor: img = convert_to_tensor(img, track_meta=get_track_meta()) img_t, *_ = convert_data_type(img, torch.Tensor, dtype=torch.float) - sigma: Union[Sequence[torch.Tensor], torch.Tensor] + sigma: Sequence[torch.Tensor] | torch.Tensor if isinstance(self.sigma, Sequence): sigma = [torch.as_tensor(s, device=img_t.device) for s in self.sigma] else: @@ -1224,9 +1224,9 @@ class RandGaussianSmooth(RandomizableTransform): def __init__( self, - sigma_x: Tuple[float, float] = (0.25, 1.5), - sigma_y: Tuple[float, float] = (0.25, 1.5), - sigma_z: Tuple[float, float] = (0.25, 1.5), + sigma_x: tuple[float, float] = (0.25, 1.5), + sigma_y: tuple[float, float] = (0.25, 1.5), + sigma_z: tuple[float, float] = (0.25, 1.5), prob: float = 0.1, approx: str = "erf", ) -> None: @@ -1240,7 +1240,7 @@ def __init__( self.y = self.sigma_y[0] self.z = self.sigma_z[0] - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if not self._do_transform: return None @@ -1291,8 +1291,8 @@ class GaussianSharpen(Transform): def __init__( self, - sigma1: Union[Sequence[float], float] = 3.0, - sigma2: Union[Sequence[float], float] = 1.0, + sigma1: Sequence[float] | float = 3.0, + sigma2: Sequence[float] | float = 1.0, alpha: float = 30.0, approx: str = "erf", ) -> None: @@ -1342,13 +1342,13 @@ class RandGaussianSharpen(RandomizableTransform): def __init__( self, - sigma1_x: Tuple[float, float] = (0.5, 1.0), - sigma1_y: Tuple[float, float] = (0.5, 1.0), - sigma1_z: Tuple[float, float] = (0.5, 1.0), - sigma2_x: Union[Tuple[float, float], float] = 0.5, - sigma2_y: Union[Tuple[float, float], float] = 0.5, - sigma2_z: Union[Tuple[float, float], float] = 0.5, - alpha: Tuple[float, float] = (10.0, 30.0), + sigma1_x: tuple[float, float] = (0.5, 1.0), + sigma1_y: tuple[float, float] = (0.5, 1.0), + sigma1_z: tuple[float, float] = (0.5, 1.0), + sigma2_x: tuple[float, float] | float = 0.5, + sigma2_y: tuple[float, float] | float = 0.5, + sigma2_z: tuple[float, float] | float = 0.5, + alpha: tuple[float, float] = (10.0, 30.0), approx: str = "erf", prob: float = 0.1, ) -> None: @@ -1361,15 +1361,15 @@ def __init__( self.sigma2_z = sigma2_z self.alpha = alpha self.approx = approx - self.x1: Optional[float] = None - self.y1: Optional[float] = None - self.z1: Optional[float] = None - self.x2: Optional[float] = None - self.y2: Optional[float] = None - self.z2: Optional[float] = None - self.a: Optional[float] = None - - def randomize(self, data: Optional[Any] = None) -> None: + self.x1: float | None = None + self.y1: float | None = None + self.z1: float | None = None + self.x2: float | None = None + self.y2: float | None = None + self.z2: float | None = None + self.a: float | None = None + + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if not self._do_transform: return None @@ -1412,7 +1412,7 @@ class RandHistogramShift(RandomizableTransform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, num_control_points: Union[Tuple[int, int], int] = 10, prob: float = 0.1) -> None: + def __init__(self, num_control_points: tuple[int, int] | int = 10, prob: float = 0.1) -> None: RandomizableTransform.__init__(self, prob) if isinstance(num_control_points, int): @@ -1445,7 +1445,7 @@ def interp(self, x: NdarrayOrTensor, xp: NdarrayOrTensor, fp: NdarrayOrTensor) - f[x > xp[-1]] = fp[-1] return f - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if not self._do_transform: return None @@ -1649,7 +1649,7 @@ class KSpaceSpikeNoise(Transform, Fourier): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, loc: Union[Tuple, Sequence[Tuple]], k_intensity: Optional[Union[Sequence[float], float]] = None): + def __init__(self, loc: tuple | Sequence[tuple], k_intensity: Sequence[float] | float | None = None): self.loc = ensure_tuple(loc) self.k_intensity = k_intensity @@ -1724,7 +1724,7 @@ def _check_indices(self, img) -> None: f"The index value at position {i} of one of the tuples in loc = {self.loc} is out of bounds for current image." ) - def _set_spike(self, k: NdarrayOrTensor, idx: Tuple, val: Union[Sequence[float], float]): + def _set_spike(self, k: NdarrayOrTensor, idx: tuple, val: Sequence[float] | float): """ Helper function to introduce a given intensity at given location. @@ -1781,14 +1781,14 @@ class RandKSpaceSpikeNoise(RandomizableTransform, Fourier): def __init__( self, prob: float = 0.1, - intensity_range: Optional[Sequence[Union[Sequence[float], float]]] = None, + intensity_range: Sequence[Sequence[float] | float] | None = None, channel_wise: bool = True, ): self.intensity_range = intensity_range self.channel_wise = channel_wise - self.sampled_k_intensity: List = [] - self.sampled_locs: List[Tuple] = [] + self.sampled_k_intensity: list = [] + self.sampled_locs: list[tuple] = [] if intensity_range is not None and isinstance(intensity_range[0], Sequence) and not channel_wise: raise ValueError("When channel_wise = False, intensity_range should be a 2-tuple (low, high) or None.") @@ -1909,9 +1909,9 @@ class RandCoarseTransform(RandomizableTransform): def __init__( self, holes: int, - spatial_size: Union[Sequence[int], int], - max_holes: Optional[int] = None, - max_spatial_size: Optional[Union[Sequence[int], int]] = None, + spatial_size: Sequence[int] | int, + max_holes: int | None = None, + max_spatial_size: Sequence[int] | int | None = None, prob: float = 0.1, ) -> None: RandomizableTransform.__init__(self, prob) @@ -1921,7 +1921,7 @@ def __init__( self.spatial_size = spatial_size self.max_holes = max_holes self.max_spatial_size = max_spatial_size - self.hole_coords: List = [] + self.hole_coords: list = [] def randomize(self, img_size: Sequence[int]) -> None: super().randomize(None) @@ -1993,11 +1993,11 @@ class RandCoarseDropout(RandCoarseTransform): def __init__( self, holes: int, - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, dropout_holes: bool = True, - fill_value: Optional[Union[Tuple[float, float], float]] = None, - max_holes: Optional[int] = None, - max_spatial_size: Optional[Union[Sequence[int], int]] = None, + fill_value: tuple[float, float] | float | None = None, + max_holes: int | None = None, + max_spatial_size: Sequence[int] | int | None = None, prob: float = 0.1, ) -> None: super().__init__( @@ -2098,7 +2098,7 @@ def __init__( num_bins: int = 256, min: int = 0, max: int = 255, - mask: Optional[NdarrayOrTensor] = None, + mask: NdarrayOrTensor | None = None, dtype: DtypeLike = np.float32, ) -> None: self.num_bins = num_bins @@ -2107,11 +2107,11 @@ def __init__( self.mask = mask self.dtype = dtype - def __call__(self, img: NdarrayOrTensor, mask: Optional[NdarrayOrTensor] = None) -> NdarrayOrTensor: + def __call__(self, img: NdarrayOrTensor, mask: NdarrayOrTensor | None = None) -> NdarrayOrTensor: img = convert_to_tensor(img, track_meta=get_track_meta()) img_np, *_ = convert_data_type(img, np.ndarray) mask = mask if mask is not None else self.mask - mask_np: Optional[np.ndarray] = None + mask_np: np.ndarray | None = None if mask is not None: mask_np, *_ = convert_data_type(mask, np.ndarray) @@ -2252,11 +2252,11 @@ class ForegroundMask(Transform): def __init__( self, - threshold: Union[Dict, Callable, str, float, int] = "otsu", - hsv_threshold: Optional[Union[Dict, Callable, str, float, int]] = None, + threshold: dict | Callable | str | float | int = "otsu", + hsv_threshold: dict | Callable | str | float | int | None = None, invert: bool = False, ) -> None: - self.thresholds: Dict[str, Union[Callable, float]] = {} + self.thresholds: dict[str, Callable | float] = {} if threshold is not None: if isinstance(threshold, dict): for mode, th in threshold.items(): diff --git a/monai/transforms/intensity/dictionary.py b/monai/transforms/intensity/dictionary.py index 2e087652a2..a0e33557b9 100644 --- a/monai/transforms/intensity/dictionary.py +++ b/monai/transforms/intensity/dictionary.py @@ -15,7 +15,9 @@ Class names are ended with 'd' to denote dictionary-based transforms. """ -from typing import Callable, Dict, Hashable, Mapping, Optional, Sequence, Tuple, Union +from __future__ import annotations + +from typing import Callable, Hashable, Mapping, Sequence import numpy as np @@ -195,13 +197,13 @@ def __init__( self.rand_gaussian_noise = RandGaussianNoise(mean=mean, std=std, prob=1.0, dtype=dtype) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandGaussianNoised": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandGaussianNoised: super().set_random_state(seed, state) self.rand_gaussian_noise.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -253,8 +255,8 @@ def __init__( self, keys: KeysCollection, prob: float = 0.1, - mean: Union[Sequence[float], float] = 0.0, - std: Union[Sequence[float], float] = 1.0, + mean: Sequence[float] | float = 0.0, + std: Sequence[float] | float = 1.0, channel_wise: bool = False, relative: bool = False, sample_std: bool = True, @@ -273,14 +275,12 @@ def __init__( dtype=dtype, ) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandRicianNoised": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandRicianNoised: super().set_random_state(seed, state) self.rand_rician_noise.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -305,8 +305,8 @@ def __init__( keys: KeysCollection, offset: float, safe: bool = False, - factor_key: Optional[str] = None, - meta_keys: Optional[KeysCollection] = None, + factor_key: str | None = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, allow_missing_keys: bool = False, ) -> None: @@ -341,13 +341,13 @@ def __init__( self.meta_key_postfix = ensure_tuple_rep(meta_key_postfix, len(self.keys)) self.shifter = ShiftIntensity(offset, safe) - def __call__(self, data) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key, factor_key, meta_key, meta_key_postfix in self.key_iterator( d, self.factor_key, self.meta_keys, self.meta_key_postfix ): meta_key = meta_key or f"{key}_{meta_key_postfix}" - factor: Optional[float] = d[meta_key].get(factor_key) if meta_key in d else None + factor: float | None = d[meta_key].get(factor_key) if meta_key in d else None offset = None if factor is None else self.shifter.offset * factor d[key] = self.shifter(d[key], offset=offset) return d @@ -363,10 +363,10 @@ class RandShiftIntensityd(RandomizableTransform, MapTransform): def __init__( self, keys: KeysCollection, - offsets: Union[Tuple[float, float], float], + offsets: tuple[float, float] | float, safe: bool = False, - factor_key: Optional[str] = None, - meta_keys: Optional[KeysCollection] = None, + factor_key: str | None = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, prob: float = 0.1, allow_missing_keys: bool = False, @@ -408,13 +408,13 @@ def __init__( self.shifter = RandShiftIntensity(offsets=offsets, safe=safe, prob=1.0) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandShiftIntensityd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandShiftIntensityd: super().set_random_state(seed, state) self.shifter.set_random_state(seed, state) return self - def __call__(self, data) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -428,7 +428,7 @@ def __call__(self, data) -> Dict[Hashable, NdarrayOrTensor]: d, self.factor_key, self.meta_keys, self.meta_key_postfix ): meta_key = meta_key or f"{key}_{meta_key_postfix}" - factor: Optional[float] = d[meta_key].get(factor_key) if meta_key in d else None + factor: float | None = d[meta_key].get(factor_key) if meta_key in d else None d[key] = self.shifter(d[key], factor=factor, randomize=False) return d @@ -463,7 +463,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.shifter = StdShiftIntensity(factor, nonzero, channel_wise, dtype) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.shifter(d[key]) @@ -480,7 +480,7 @@ class RandStdShiftIntensityd(RandomizableTransform, MapTransform): def __init__( self, keys: KeysCollection, - factors: Union[Tuple[float, float], float], + factors: tuple[float, float] | float, prob: float = 0.1, nonzero: bool = False, channel_wise: bool = False, @@ -506,13 +506,13 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandStdShiftIntensityd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandStdShiftIntensityd: super().set_random_state(seed, state) self.shifter.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -539,9 +539,9 @@ class ScaleIntensityd(MapTransform): def __init__( self, keys: KeysCollection, - minv: Optional[float] = 0.0, - maxv: Optional[float] = 1.0, - factor: Optional[float] = None, + minv: float | None = 0.0, + maxv: float | None = 1.0, + factor: float | None = None, channel_wise: bool = False, dtype: DtypeLike = np.float32, allow_missing_keys: bool = False, @@ -563,7 +563,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.scaler = ScaleIntensity(minv, maxv, factor, channel_wise, dtype) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.scaler(d[key]) @@ -580,7 +580,7 @@ class RandScaleIntensityd(RandomizableTransform, MapTransform): def __init__( self, keys: KeysCollection, - factors: Union[Tuple[float, float], float], + factors: tuple[float, float] | float, prob: float = 0.1, dtype: DtypeLike = np.float32, allow_missing_keys: bool = False, @@ -602,13 +602,13 @@ def __init__( self.scaler = RandScaleIntensity(factors=factors, dtype=dtype, prob=1.0) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandScaleIntensityd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandScaleIntensityd: super().set_random_state(seed, state) self.scaler.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -634,7 +634,7 @@ def __init__( self, keys: KeysCollection, degree: int = 3, - coeff_range: Tuple[float, float] = (0.0, 0.1), + coeff_range: tuple[float, float] = (0.0, 0.1), dtype: DtypeLike = np.float32, prob: float = 0.1, allow_missing_keys: bool = False, @@ -656,14 +656,12 @@ def __init__( self.rand_bias_field = RandBiasField(degree=degree, coeff_range=coeff_range, dtype=dtype, prob=1.0) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandBiasFieldd": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandBiasFieldd: super().set_random_state(seed, state) self.rand_bias_field.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -708,8 +706,8 @@ class NormalizeIntensityd(MapTransform): def __init__( self, keys: KeysCollection, - subtrahend: Optional[NdarrayOrTensor] = None, - divisor: Optional[NdarrayOrTensor] = None, + subtrahend: NdarrayOrTensor | None = None, + divisor: NdarrayOrTensor | None = None, nonzero: bool = False, channel_wise: bool = False, dtype: DtypeLike = np.float32, @@ -718,7 +716,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.normalizer = NormalizeIntensity(subtrahend, divisor, nonzero, channel_wise, dtype) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.normalizer(d[key]) @@ -751,7 +749,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.filter = ThresholdIntensity(threshold, above, cval) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.filter(d[key]) @@ -781,8 +779,8 @@ def __init__( keys: KeysCollection, a_min: float, a_max: float, - b_min: Optional[float] = None, - b_max: Optional[float] = None, + b_min: float | None = None, + b_max: float | None = None, clip: bool = False, dtype: DtypeLike = np.float32, allow_missing_keys: bool = False, @@ -790,7 +788,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.scaler = ScaleIntensityRange(a_min, a_max, b_min, b_max, clip, dtype) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.scaler(d[key]) @@ -817,7 +815,7 @@ def __init__(self, keys: KeysCollection, gamma: float, allow_missing_keys: bool super().__init__(keys, allow_missing_keys) self.adjuster = AdjustContrast(gamma) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.adjuster(d[key]) @@ -846,7 +844,7 @@ def __init__( self, keys: KeysCollection, prob: float = 0.1, - gamma: Union[Tuple[float, float], float] = (0.5, 4.5), + gamma: tuple[float, float] | float = (0.5, 4.5), allow_missing_keys: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) @@ -854,13 +852,13 @@ def __init__( self.adjuster = RandAdjustContrast(gamma=gamma, prob=1.0) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandAdjustContrastd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandAdjustContrastd: super().set_random_state(seed, state) self.adjuster.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -901,8 +899,8 @@ def __init__( keys: KeysCollection, lower: float, upper: float, - b_min: Optional[float], - b_max: Optional[float], + b_min: float | None, + b_max: float | None, clip: bool = False, relative: bool = False, channel_wise: bool = False, @@ -912,7 +910,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.scaler = ScaleIntensityRangePercentiles(lower, upper, b_min, b_max, clip, relative, channel_wise, dtype) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.scaler(d[key]) @@ -945,8 +943,8 @@ class MaskIntensityd(MapTransform): def __init__( self, keys: KeysCollection, - mask_data: Optional[NdarrayOrTensor] = None, - mask_key: Optional[str] = None, + mask_data: NdarrayOrTensor | None = None, + mask_key: str | None = None, select_fn: Callable = is_positive, allow_missing_keys: bool = False, ) -> None: @@ -954,7 +952,7 @@ def __init__( self.converter = MaskIntensity(mask_data=mask_data, select_fn=select_fn) self.mask_key = mask_key if mask_data is None else None - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key], d[self.mask_key]) if self.mask_key is not None else self.converter(d[key]) @@ -991,7 +989,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.converter = SavitzkyGolaySmooth(window_length=window_length, order=order, axis=axis, mode=mode) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -1014,13 +1012,11 @@ class MedianSmoothd(MapTransform): backend = MedianSmooth.backend - def __init__( - self, keys: KeysCollection, radius: Union[Sequence[int], int], allow_missing_keys: bool = False - ) -> None: + def __init__(self, keys: KeysCollection, radius: Sequence[int] | int, allow_missing_keys: bool = False) -> None: super().__init__(keys, allow_missing_keys) self.converter = MedianSmooth(radius) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -1048,14 +1044,14 @@ class GaussianSmoothd(MapTransform): def __init__( self, keys: KeysCollection, - sigma: Union[Sequence[float], float], + sigma: Sequence[float] | float, approx: str = "erf", allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) self.converter = GaussianSmooth(sigma, approx=approx) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -1084,9 +1080,9 @@ class RandGaussianSmoothd(RandomizableTransform, MapTransform): def __init__( self, keys: KeysCollection, - sigma_x: Tuple[float, float] = (0.25, 1.5), - sigma_y: Tuple[float, float] = (0.25, 1.5), - sigma_z: Tuple[float, float] = (0.25, 1.5), + sigma_x: tuple[float, float] = (0.25, 1.5), + sigma_y: tuple[float, float] = (0.25, 1.5), + sigma_z: tuple[float, float] = (0.25, 1.5), approx: str = "erf", prob: float = 0.1, allow_missing_keys: bool = False, @@ -1098,13 +1094,13 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandGaussianSmoothd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandGaussianSmoothd: super().set_random_state(seed, state) self.rand_smooth.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -1144,8 +1140,8 @@ class GaussianSharpend(MapTransform): def __init__( self, keys: KeysCollection, - sigma1: Union[Sequence[float], float] = 3.0, - sigma2: Union[Sequence[float], float] = 1.0, + sigma1: Sequence[float] | float = 3.0, + sigma2: Sequence[float] | float = 1.0, alpha: float = 30.0, approx: str = "erf", allow_missing_keys: bool = False, @@ -1153,7 +1149,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.converter = GaussianSharpen(sigma1, sigma2, alpha, approx=approx) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -1189,13 +1185,13 @@ class RandGaussianSharpend(RandomizableTransform, MapTransform): def __init__( self, keys: KeysCollection, - sigma1_x: Tuple[float, float] = (0.5, 1.0), - sigma1_y: Tuple[float, float] = (0.5, 1.0), - sigma1_z: Tuple[float, float] = (0.5, 1.0), - sigma2_x: Union[Tuple[float, float], float] = 0.5, - sigma2_y: Union[Tuple[float, float], float] = 0.5, - sigma2_z: Union[Tuple[float, float], float] = 0.5, - alpha: Tuple[float, float] = (10.0, 30.0), + sigma1_x: tuple[float, float] = (0.5, 1.0), + sigma1_y: tuple[float, float] = (0.5, 1.0), + sigma1_z: tuple[float, float] = (0.5, 1.0), + sigma2_x: tuple[float, float] | float = 0.5, + sigma2_y: tuple[float, float] | float = 0.5, + sigma2_z: tuple[float, float] | float = 0.5, + alpha: tuple[float, float] = (10.0, 30.0), approx: str = "erf", prob: float = 0.1, allow_missing_keys: bool = False, @@ -1215,13 +1211,13 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandGaussianSharpend": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandGaussianSharpend: super().set_random_state(seed, state) self.rand_sharpen.set_random_state(seed, state) return self - def __call__(self, data: Dict[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: dict[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -1256,7 +1252,7 @@ class RandHistogramShiftd(RandomizableTransform, MapTransform): def __init__( self, keys: KeysCollection, - num_control_points: Union[Tuple[int, int], int] = 10, + num_control_points: tuple[int, int] | int = 10, prob: float = 0.1, allow_missing_keys: bool = False, ) -> None: @@ -1265,13 +1261,13 @@ def __init__( self.shifter = RandHistogramShift(num_control_points=num_control_points, prob=1.0) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandHistogramShiftd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandHistogramShiftd: super().set_random_state(seed, state) self.shifter.set_random_state(seed, state) return self - def __call__(self, data: Dict[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: dict[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -1325,14 +1321,12 @@ def __init__( RandomizableTransform.__init__(self, prob=prob) self.rand_gibbs_noise = RandGibbsNoise(alpha=alpha, prob=1.0) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandGibbsNoised": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandGibbsNoised: super().set_random_state(seed, state) self.rand_gibbs_noise.set_random_state(seed, state) return self - def __call__(self, data: Dict[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: dict[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -1373,7 +1367,7 @@ def __init__(self, keys: KeysCollection, alpha: float = 0.5, allow_missing_keys: MapTransform.__init__(self, keys, allow_missing_keys) self.transform = GibbsNoise(alpha) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): @@ -1427,15 +1421,15 @@ class KSpaceSpikeNoised(MapTransform): def __init__( self, keys: KeysCollection, - loc: Union[Tuple, Sequence[Tuple]], - k_intensity: Optional[Union[Sequence[float], float]] = None, + loc: tuple | Sequence[tuple], + k_intensity: Sequence[float] | float | None = None, allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) self.transform = KSpaceSpikeNoise(loc, k_intensity) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: """ Args: data: Expects image/label to have dimensions (C, H, W) or @@ -1490,7 +1484,7 @@ def __init__( self, keys: KeysCollection, prob: float = 0.1, - intensity_range: Optional[Sequence[Union[Sequence[float], float]]] = None, + intensity_range: Sequence[Sequence[float] | float] | None = None, channel_wise: bool = True, allow_missing_keys: bool = False, ): @@ -1499,13 +1493,13 @@ def __init__( self.rand_noise = RandKSpaceSpikeNoise(prob=1.0, intensity_range=intensity_range, channel_wise=channel_wise) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandKSpaceSpikeNoised": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandKSpaceSpikeNoised: super().set_random_state(seed, state) self.rand_noise.set_random_state(seed, state) return self - def __call__(self, data: Dict[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: dict[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -1556,11 +1550,11 @@ def __init__( self, keys: KeysCollection, holes: int, - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, dropout_holes: bool = True, - fill_value: Optional[Union[Tuple[float, float], float]] = None, - max_holes: Optional[int] = None, - max_spatial_size: Optional[Union[Sequence[int], int]] = None, + fill_value: tuple[float, float] | float | None = None, + max_holes: int | None = None, + max_spatial_size: Sequence[int] | int | None = None, prob: float = 0.1, allow_missing_keys: bool = False, ): @@ -1577,13 +1571,13 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandCoarseDropoutd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandCoarseDropoutd: super().set_random_state(seed, state) self.dropper.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -1637,9 +1631,9 @@ def __init__( self, keys: KeysCollection, holes: int, - spatial_size: Union[Sequence[int], int], - max_holes: Optional[int] = None, - max_spatial_size: Optional[Union[Sequence[int], int]] = None, + spatial_size: Sequence[int] | int, + max_holes: int | None = None, + max_spatial_size: Sequence[int] | int | None = None, prob: float = 0.1, allow_missing_keys: bool = False, ): @@ -1650,13 +1644,13 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandCoarseShuffled": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandCoarseShuffled: super().set_random_state(seed, state) self.shuffle.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if not self._do_transform: @@ -1706,8 +1700,8 @@ def __init__( num_bins: int = 256, min: int = 0, max: int = 255, - mask: Optional[NdarrayOrTensor] = None, - mask_key: Optional[str] = None, + mask: NdarrayOrTensor | None = None, + mask_key: str | None = None, dtype: DtypeLike = np.float32, allow_missing_keys: bool = False, ) -> None: @@ -1715,7 +1709,7 @@ def __init__( self.transform = HistogramNormalize(num_bins=num_bins, min=min, max=max, mask=mask, dtype=dtype) self.mask_key = mask_key if mask is None else None - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.transform(d[key], d[self.mask_key]) if self.mask_key is not None else self.transform(d[key]) @@ -1750,17 +1744,17 @@ class ForegroundMaskd(MapTransform): def __init__( self, keys: KeysCollection, - threshold: Union[Dict, Callable, str, float] = "otsu", - hsv_threshold: Optional[Union[Dict, Callable, str, float, int]] = None, + threshold: dict | Callable | str | float = "otsu", + hsv_threshold: dict | Callable | str | float | int | None = None, invert: bool = False, - new_key_prefix: Optional[str] = None, + new_key_prefix: str | None = None, allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) self.transform = ForegroundMask(threshold=threshold, hsv_threshold=hsv_threshold, invert=invert) self.new_key_prefix = new_key_prefix - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): new_key = key if self.new_key_prefix is None else self.new_key_prefix + key @@ -1793,7 +1787,7 @@ def __init__( self.transform = ComputeHoVerMaps(dtype=dtype) self.new_key_prefix = new_key_prefix - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): new_key = key if self.new_key_prefix is None else self.new_key_prefix + key diff --git a/monai/transforms/inverse.py b/monai/transforms/inverse.py index db4653ce93..6d9060723a 100644 --- a/monai/transforms/inverse.py +++ b/monai/transforms/inverse.py @@ -9,10 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import warnings +from collections.abc import Hashable, Mapping from contextlib import contextmanager -from typing import Any, Hashable, Mapping, Optional, Tuple +from typing import Any import torch @@ -70,7 +73,7 @@ def trace_key(key: Hashable = None): return f"{key}{TraceKeys.KEY_SUFFIX}" def get_transform_info( - self, data, key: Hashable = None, extra_info: Optional[dict] = None, orig_size: Optional[Tuple] = None + self, data, key: Hashable = None, extra_info: dict | None = None, orig_size: tuple | None = None ) -> dict: """ Return a dictionary with the relevant information pertaining to an applied transform. @@ -104,7 +107,7 @@ def get_transform_info( return info def push_transform( - self, data, key: Hashable = None, extra_info: Optional[dict] = None, orig_size: Optional[Tuple] = None + self, data, key: Hashable = None, extra_info: dict | None = None, orig_size: tuple | None = None ) -> None: """ Push to a stack of applied transforms. diff --git a/monai/transforms/inverse_batch_transform.py b/monai/transforms/inverse_batch_transform.py index 3bfbb961a8..73149f1be5 100644 --- a/monai/transforms/inverse_batch_transform.py +++ b/monai/transforms/inverse_batch_transform.py @@ -9,8 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Any, Callable, Dict, List, Optional, Sequence, Union +from collections.abc import Callable, Sequence +from typing import Any from torch.utils.data import Dataset from torch.utils.data.dataloader import DataLoader as TorchDataLoader @@ -57,8 +60,8 @@ def __init__( self, transform: InvertibleTransform, loader: TorchDataLoader, - collate_fn: Optional[Callable] = no_collation, - num_workers: Optional[int] = 0, + collate_fn: Callable | None = no_collation, + num_workers: int | None = 0, detach: bool = True, pad_batch: bool = True, fill_value=None, @@ -92,7 +95,7 @@ def __init__( loader.collate_fn, PadListDataCollate ) - def __call__(self, data: Dict[str, Any]) -> Any: + def __call__(self, data: dict[str, Any]) -> Any: decollated_data = decollate_batch(data, detach=self.detach, pad=self.pad_batch, fill_value=self.fill_value) inv_ds = _BatchInverseDataset(decollated_data, self.transform, self.pad_collation_used) inv_loader = DataLoader( @@ -129,7 +132,7 @@ class Decollated(MapTransform): def __init__( self, - keys: Optional[KeysCollection] = None, + keys: KeysCollection | None = None, detach: bool = True, pad_batch: bool = True, fill_value=None, @@ -140,8 +143,8 @@ def __init__( self.pad_batch = pad_batch self.fill_value = fill_value - def __call__(self, data: Union[Dict, List]): - d: Union[Dict, List] + def __call__(self, data: dict | list): + d: dict | list if len(self.keys) == 1 and self.keys[0] is None: # it doesn't support `None` as the key d = data diff --git a/monai/transforms/io/array.py b/monai/transforms/io/array.py index 5115ace4f0..b05995a1a1 100644 --- a/monai/transforms/io/array.py +++ b/monai/transforms/io/array.py @@ -13,14 +13,16 @@ https://github.com/Project-MONAI/MONAI/wiki/MONAI_Design """ +from __future__ import annotations + import inspect import logging import sys import traceback import warnings +from collections.abc import Sequence from pathlib import Path from pydoc import locate -from typing import Dict, List, Optional, Sequence, Type, Union import numpy as np import torch @@ -121,10 +123,10 @@ def __init__( self, reader=None, image_only: bool = False, - dtype: Optional[DtypeLike] = np.float32, + dtype: DtypeLike | None = np.float32, ensure_channel_first: bool = False, simple_keys: bool = False, - prune_meta_pattern: Optional[str] = None, + prune_meta_pattern: str | None = None, prune_meta_sep: str = ".", *args, **kwargs, @@ -170,7 +172,7 @@ def __init__( self.pattern = prune_meta_pattern self.sep = prune_meta_sep - self.readers: List[ImageReader] = [] + self.readers: list[ImageReader] = [] for r in SUPPORTED_READERS: # set predefined readers as default try: self.register(SUPPORTED_READERS[r](*args, **kwargs)) @@ -220,7 +222,7 @@ def register(self, reader: ImageReader): warnings.warn(f"Preferably the reader should inherit ImageReader, but got {type(reader)}.") self.readers.append(reader) - def __call__(self, filename: Union[Sequence[PathLike], PathLike], reader: Optional[ImageReader] = None): + def __call__(self, filename: Sequence[PathLike] | PathLike, reader: ImageReader | None = None): """ Load image file and metadata from the given filename(s). If `reader` is not specified, this class automatically chooses readers based on the @@ -357,19 +359,19 @@ def __init__( output_dir: PathLike = "./", output_postfix: str = "trans", output_ext: str = ".nii.gz", - output_dtype: Optional[DtypeLike] = np.float32, + output_dtype: DtypeLike | None = np.float32, resample: bool = True, mode: str = "nearest", padding_mode: str = GridSamplePadMode.BORDER, - scale: Optional[int] = None, + scale: int | None = None, dtype: DtypeLike = np.float64, squeeze_end_dims: bool = True, data_root_dir: PathLike = "", separate_folder: bool = True, print_log: bool = True, output_format: str = "", - writer: Union[Type[image_writer.ImageWriter], str, None] = None, - channel_dim: Optional[int] = 0, + writer: type[image_writer.ImageWriter] | str | None = None, + channel_dim: int | None = 0, output_name_formatter=None, ) -> None: self.folder_layout = FolderLayout( @@ -425,7 +427,7 @@ def set_options(self, init_kwargs=None, data_kwargs=None, meta_kwargs=None, writ if write_kwargs is not None: self.write_kwargs.update(write_kwargs) - def __call__(self, img: Union[torch.Tensor, np.ndarray], meta_data: Optional[Dict] = None): + def __call__(self, img: torch.Tensor | np.ndarray, meta_data: dict | None = None): """ Args: img: target data content that save into file. The image should be channel-first, shape: `[C,H,W,[D]]`. diff --git a/monai/transforms/io/dictionary.py b/monai/transforms/io/dictionary.py index 9817a92807..b5f3429088 100644 --- a/monai/transforms/io/dictionary.py +++ b/monai/transforms/io/dictionary.py @@ -15,8 +15,9 @@ Class names are ended with 'd' to denote dictionary-based transforms. """ +from __future__ import annotations + from pathlib import Path -from typing import Optional, Type, Union import numpy as np @@ -71,15 +72,15 @@ class LoadImaged(MapTransform): def __init__( self, keys: KeysCollection, - reader: Union[Type[ImageReader], str, None] = None, + reader: type[ImageReader] | str | None = None, dtype: DtypeLike = np.float32, - meta_keys: Optional[KeysCollection] = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, overwriting: bool = False, image_only: bool = False, ensure_channel_first: bool = False, simple_keys: bool = False, - prune_meta_pattern: Optional[str] = None, + prune_meta_pattern: str | None = None, prune_meta_sep: str = ".", allow_missing_keys: bool = False, *args, @@ -143,7 +144,7 @@ def __init__( def register(self, reader: ImageReader): self._loader.register(reader) - def __call__(self, data, reader: Optional[ImageReader] = None): + def __call__(self, data, reader: ImageReader | None = None): """ Raises: KeyError: When not ``self.overwriting`` and key already exists in ``data``. @@ -241,24 +242,24 @@ class SaveImaged(MapTransform): def __init__( self, keys: KeysCollection, - meta_keys: Optional[KeysCollection] = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, - output_dir: Union[Path, str] = "./", + output_dir: Path | str = "./", output_postfix: str = "trans", output_ext: str = ".nii.gz", resample: bool = True, mode: str = "nearest", padding_mode: str = GridSamplePadMode.BORDER, - scale: Optional[int] = None, + scale: int | None = None, dtype: DtypeLike = np.float64, - output_dtype: Optional[DtypeLike] = np.float32, + output_dtype: DtypeLike | None = np.float32, allow_missing_keys: bool = False, squeeze_end_dims: bool = True, data_root_dir: str = "", separate_folder: bool = True, print_log: bool = True, output_format: str = "", - writer: Union[Type[image_writer.ImageWriter], str, None] = None, + writer: type[image_writer.ImageWriter] | str | None = None, output_name_formatter=None, ) -> None: super().__init__(keys, allow_missing_keys) diff --git a/monai/transforms/lazy/functional.py b/monai/transforms/lazy/functional.py index 5f79f7954e..13aa753a55 100644 --- a/monai/transforms/lazy/functional.py +++ b/monai/transforms/lazy/functional.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Union +from __future__ import annotations import torch @@ -26,7 +26,7 @@ __all__ = ["apply_transforms"] -def apply_transforms(data: Union[torch.Tensor, MetaTensor], pending: Optional[list] = None): +def apply_transforms(data: torch.Tensor | MetaTensor, pending: list | None = None): """ This method applies pending transforms to `data` tensors. diff --git a/monai/transforms/lazy/utils.py b/monai/transforms/lazy/utils.py index 4e37e78833..e03314d655 100644 --- a/monai/transforms/lazy/utils.py +++ b/monai/transforms/lazy/utils.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional +from __future__ import annotations import numpy as np import torch @@ -105,7 +105,7 @@ def is_compatible_apply_kwargs(kwargs_1, kwargs_2): return True -def resample(data: torch.Tensor, matrix: NdarrayOrTensor, kwargs: Optional[dict] = None): +def resample(data: torch.Tensor, matrix: NdarrayOrTensor, kwargs: dict | None = None): """ This is a minimal implementation of resample that always uses Affine. """ diff --git a/monai/transforms/meta_utility/dictionary.py b/monai/transforms/meta_utility/dictionary.py index bef228f423..ed752bb2d7 100644 --- a/monai/transforms/meta_utility/dictionary.py +++ b/monai/transforms/meta_utility/dictionary.py @@ -15,7 +15,9 @@ Class names are ended with 'd' to denote dictionary-based transforms. """ -from typing import Dict, Hashable, Mapping, Sequence, Union +from __future__ import annotations + +from collections.abc import Hashable, Mapping, Sequence import numpy as np import torch @@ -48,7 +50,7 @@ class FromMetaTensord(MapTransform, InvertibleTransform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY, TransformBackends.CUPY] def __init__( - self, keys: KeysCollection, data_type: Union[Sequence[str], str] = "tensor", allow_missing_keys: bool = False + self, keys: KeysCollection, data_type: Sequence[str] | str = "tensor", allow_missing_keys: bool = False ): """ Args: @@ -60,7 +62,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.as_tensor_output = tuple(d == "tensor" for d in ensure_tuple_rep(data_type, len(self.keys))) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key, t in self.key_iterator(d, self.as_tensor_output): im: MetaTensor = d[key] # type: ignore @@ -68,7 +70,7 @@ def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, N self.push_transform(d, key) return d - def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): # check transform @@ -94,7 +96,7 @@ class ToMetaTensord(MapTransform, InvertibleTransform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY, TransformBackends.CUPY] - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): self.push_transform(d, key) @@ -105,7 +107,7 @@ def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, N d[key] = im return d - def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): # check transform diff --git a/monai/transforms/nvtx.py b/monai/transforms/nvtx.py index b6e0b0d465..cbc3c9dafe 100644 --- a/monai/transforms/nvtx.py +++ b/monai/transforms/nvtx.py @@ -12,6 +12,8 @@ Wrapper around NVIDIA Tools Extension for profiling MONAI transformations """ +from __future__ import annotations + from monai.transforms.transform import RandomizableTrait, Transform from monai.utils import optional_import diff --git a/monai/transforms/post/array.py b/monai/transforms/post/array.py index b05f0b9d61..a514c43536 100644 --- a/monai/transforms/post/array.py +++ b/monai/transforms/post/array.py @@ -13,8 +13,10 @@ https://github.com/Project-MONAI/MONAI/wiki/MONAI_Design """ +from __future__ import annotations + import warnings -from typing import Callable, Iterable, Optional, Sequence, Tuple, Union +from collections.abc import Callable, Iterable, Sequence import numpy as np import torch @@ -76,9 +78,7 @@ class Activations(Transform): backend = [TransformBackends.TORCH] - def __init__( - self, sigmoid: bool = False, softmax: bool = False, other: Optional[Callable] = None, **kwargs - ) -> None: + def __init__(self, sigmoid: bool = False, softmax: bool = False, other: Callable | None = None, **kwargs) -> None: self.sigmoid = sigmoid self.softmax = softmax self.kwargs = kwargs @@ -89,9 +89,9 @@ def __init__( def __call__( self, img: NdarrayOrTensor, - sigmoid: Optional[bool] = None, - softmax: Optional[bool] = None, - other: Optional[Callable] = None, + sigmoid: bool | None = None, + softmax: bool | None = None, + other: Callable | None = None, ) -> NdarrayOrTensor: """ Args: @@ -171,9 +171,9 @@ class AsDiscrete(Transform): def __init__( self, argmax: bool = False, - to_onehot: Optional[int] = None, - threshold: Optional[float] = None, - rounding: Optional[str] = None, + to_onehot: int | None = None, + threshold: float | None = None, + rounding: str | None = None, **kwargs, ) -> None: self.argmax = argmax @@ -187,10 +187,10 @@ def __init__( def __call__( self, img: NdarrayOrTensor, - argmax: Optional[bool] = None, - to_onehot: Optional[int] = None, - threshold: Optional[float] = None, - rounding: Optional[str] = None, + argmax: bool | None = None, + to_onehot: int | None = None, + threshold: float | None = None, + rounding: str | None = None, ) -> NdarrayOrTensor: """ Args: @@ -282,10 +282,10 @@ class KeepLargestConnectedComponent(Transform): def __init__( self, - applied_labels: Optional[Union[Sequence[int], int]] = None, - is_onehot: Optional[bool] = None, + applied_labels: Sequence[int] | int | None = None, + is_onehot: bool | None = None, independent: bool = True, - connectivity: Optional[int] = None, + connectivity: int | None = None, num_components: int = 1, ) -> None: """ @@ -409,7 +409,7 @@ class LabelFilter(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, applied_labels: Union[Iterable[int], int]) -> None: + def __init__(self, applied_labels: Iterable[int] | int) -> None: """ Initialize the LabelFilter class with the labels to filter on. @@ -489,9 +489,7 @@ class FillHoles(Transform): backend = [TransformBackends.NUMPY] - def __init__( - self, applied_labels: Optional[Union[Iterable[int], int]] = None, connectivity: Optional[int] = None - ) -> None: + def __init__(self, applied_labels: Iterable[int] | int | None = None, connectivity: int | None = None) -> None: """ Initialize the connectivity and limit the labels for which holes are filled. @@ -582,7 +580,7 @@ def __call__(self, img: NdarrayOrTensor) -> NdarrayOrTensor: class Ensemble: @staticmethod - def get_stacked_torch(img: Union[Sequence[NdarrayOrTensor], NdarrayOrTensor]) -> torch.Tensor: + def get_stacked_torch(img: Sequence[NdarrayOrTensor] | NdarrayOrTensor) -> torch.Tensor: """Get either a sequence or single instance of np.ndarray/torch.Tensor. Return single torch.Tensor.""" if isinstance(img, Sequence) and isinstance(img[0], np.ndarray): img = [torch.as_tensor(i) for i in img] @@ -592,7 +590,7 @@ def get_stacked_torch(img: Union[Sequence[NdarrayOrTensor], NdarrayOrTensor]) -> return out @staticmethod - def post_convert(img: torch.Tensor, orig_img: Union[Sequence[NdarrayOrTensor], NdarrayOrTensor]) -> NdarrayOrTensor: + def post_convert(img: torch.Tensor, orig_img: Sequence[NdarrayOrTensor] | NdarrayOrTensor) -> NdarrayOrTensor: orig_img_ = orig_img[0] if isinstance(orig_img, Sequence) else orig_img out, *_ = convert_to_dst_type(img, orig_img_) return out @@ -623,10 +621,10 @@ class MeanEnsemble(Ensemble, Transform): backend = [TransformBackends.TORCH] - def __init__(self, weights: Optional[Union[Sequence[float], NdarrayOrTensor]] = None) -> None: + def __init__(self, weights: Sequence[float] | NdarrayOrTensor | None = None) -> None: self.weights = torch.as_tensor(weights, dtype=torch.float) if weights is not None else None - def __call__(self, img: Union[Sequence[NdarrayOrTensor], NdarrayOrTensor]) -> NdarrayOrTensor: + def __call__(self, img: Sequence[NdarrayOrTensor] | NdarrayOrTensor) -> NdarrayOrTensor: img_ = self.get_stacked_torch(img) if self.weights is not None: self.weights = self.weights.to(img_.device) @@ -663,10 +661,10 @@ class VoteEnsemble(Ensemble, Transform): backend = [TransformBackends.TORCH] - def __init__(self, num_classes: Optional[int] = None) -> None: + def __init__(self, num_classes: int | None = None) -> None: self.num_classes = num_classes - def __call__(self, img: Union[Sequence[NdarrayOrTensor], NdarrayOrTensor]) -> NdarrayOrTensor: + def __call__(self, img: Sequence[NdarrayOrTensor] | NdarrayOrTensor) -> NdarrayOrTensor: img_ = self.get_stacked_torch(img) if self.num_classes is not None: @@ -726,9 +724,9 @@ class ProbNMS(Transform): def __init__( self, spatial_dims: int = 2, - sigma: Union[Sequence[float], float, Sequence[torch.Tensor], torch.Tensor] = 0.0, + sigma: Sequence[float] | float | Sequence[torch.Tensor] | torch.Tensor = 0.0, prob_threshold: float = 0.5, - box_size: Union[int, Sequence[int]] = 48, + box_size: int | Sequence[int] = 48, ) -> None: self.sigma = sigma self.spatial_dims = spatial_dims @@ -787,11 +785,11 @@ class Invert(Transform): def __init__( self, - transform: Optional[InvertibleTransform] = None, - nearest_interp: Union[bool, Sequence[bool]] = True, - device: Union[str, torch.device, None] = None, - post_func: Optional[Callable] = None, - to_tensor: Union[bool, Sequence[bool]] = True, + transform: InvertibleTransform | None = None, + nearest_interp: bool | Sequence[bool] = True, + device: str | torch.device | None = None, + post_func: Callable | None = None, + to_tensor: bool | Sequence[bool] = True, ) -> None: """ Args: @@ -852,7 +850,7 @@ class SobelGradients(Transform): def __init__( self, kernel_size: int = 3, - spatial_axes: Optional[Union[Sequence[int], int]] = None, + spatial_axes: Sequence[int] | int | None = None, normalize_kernels: bool = True, normalize_gradients: bool = False, padding_mode: str = "reflect", @@ -865,7 +863,7 @@ def __init__( self.normalize_gradients = normalize_gradients self.kernel_diff, self.kernel_smooth = self._get_kernel(kernel_size, dtype) - def _get_kernel(self, size, dtype) -> Tuple[torch.Tensor, torch.Tensor]: + def _get_kernel(self, size, dtype) -> tuple[torch.Tensor, torch.Tensor]: if size < 3: raise ValueError(f"Sobel kernel size should be at least three. {size} was given.") if size % 2 == 0: diff --git a/monai/transforms/post/dictionary.py b/monai/transforms/post/dictionary.py index 9c554321ba..3fbfe46118 100644 --- a/monai/transforms/post/dictionary.py +++ b/monai/transforms/post/dictionary.py @@ -15,9 +15,12 @@ Class names are ended with 'd' to denote dictionary-based transforms. """ +from __future__ import annotations + import warnings +from collections.abc import Callable, Hashable, Iterable, Mapping, Sequence from copy import deepcopy -from typing import Any, Callable, Dict, Hashable, Iterable, List, Mapping, Optional, Sequence, Union +from typing import Any import numpy as np import torch @@ -104,9 +107,9 @@ class Activationsd(MapTransform): def __init__( self, keys: KeysCollection, - sigmoid: Union[Sequence[bool], bool] = False, - softmax: Union[Sequence[bool], bool] = False, - other: Optional[Union[Sequence[Callable], Callable]] = None, + sigmoid: Sequence[bool] | bool = False, + softmax: Sequence[bool] | bool = False, + other: Sequence[Callable] | Callable | None = None, allow_missing_keys: bool = False, **kwargs, ) -> None: @@ -133,7 +136,7 @@ def __init__( self.converter = Activations() self.converter.kwargs = kwargs - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key, sigmoid, softmax, other in self.key_iterator(d, self.sigmoid, self.softmax, self.other): d[key] = self.converter(d[key], sigmoid, softmax, other) @@ -150,10 +153,10 @@ class AsDiscreted(MapTransform): def __init__( self, keys: KeysCollection, - argmax: Union[Sequence[bool], bool] = False, - to_onehot: Union[Sequence[Optional[int]], Optional[int]] = None, - threshold: Union[Sequence[Optional[float]], Optional[float]] = None, - rounding: Union[Sequence[Optional[str]], Optional[str]] = None, + argmax: Sequence[bool] | bool = False, + to_onehot: Sequence[int | None] | int | None = None, + threshold: Sequence[float | None] | float | None = None, + rounding: Sequence[str | None] | str | None = None, allow_missing_keys: bool = False, **kwargs, ) -> None: @@ -194,7 +197,7 @@ def __init__( self.converter = AsDiscrete() self.converter.kwargs = kwargs - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key, argmax, to_onehot, threshold, rounding in self.key_iterator( d, self.argmax, self.to_onehot, self.threshold, self.rounding @@ -213,10 +216,10 @@ class KeepLargestConnectedComponentd(MapTransform): def __init__( self, keys: KeysCollection, - applied_labels: Optional[Union[Sequence[int], int]] = None, - is_onehot: Optional[bool] = None, + applied_labels: Sequence[int] | int | None = None, + is_onehot: bool | None = None, independent: bool = True, - connectivity: Optional[int] = None, + connectivity: int | None = None, num_components: int = 1, allow_missing_keys: bool = False, ) -> None: @@ -251,7 +254,7 @@ def __init__( num_components=num_components, ) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -286,7 +289,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.converter = RemoveSmallObjects(min_size, connectivity, independent_channels) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -301,7 +304,7 @@ class LabelFilterd(MapTransform): backend = LabelFilter.backend def __init__( - self, keys: KeysCollection, applied_labels: Union[Sequence[int], int], allow_missing_keys: bool = False + self, keys: KeysCollection, applied_labels: Sequence[int] | int, allow_missing_keys: bool = False ) -> None: """ Args: @@ -314,7 +317,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.converter = LabelFilter(applied_labels) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -331,8 +334,8 @@ class FillHolesd(MapTransform): def __init__( self, keys: KeysCollection, - applied_labels: Optional[Union[Iterable[int], int]] = None, - connectivity: Optional[int] = None, + applied_labels: Iterable[int] | int | None = None, + connectivity: int | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -351,7 +354,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.converter = FillHoles(applied_labels=applied_labels, connectivity=connectivity) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -377,7 +380,7 @@ def __init__(self, keys: KeysCollection, kernel_type: str = "Laplace", allow_mis super().__init__(keys, allow_missing_keys) self.converter = LabelToContour(kernel_type=kernel_type) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -395,8 +398,8 @@ class Ensembled(MapTransform): def __init__( self, keys: KeysCollection, - ensemble: Callable[[Union[Sequence[NdarrayOrTensor], NdarrayOrTensor]], NdarrayOrTensor], - output_key: Optional[str] = None, + ensemble: Callable[[Sequence[NdarrayOrTensor] | NdarrayOrTensor], NdarrayOrTensor], + output_key: str | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -421,9 +424,9 @@ def __init__( raise ValueError("Incompatible values: len(self.keys) > 1 and output_key=None.") self.output_key = output_key if output_key is not None else self.keys[0] - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) - items: Union[List[NdarrayOrTensor], NdarrayOrTensor] + items: list[NdarrayOrTensor] | NdarrayOrTensor if len(self.keys) == 1 and self.keys[0] in d: items = d[self.keys[0]] else: @@ -445,8 +448,8 @@ class MeanEnsembled(Ensembled): def __init__( self, keys: KeysCollection, - output_key: Optional[str] = None, - weights: Optional[Union[Sequence[float], NdarrayOrTensor]] = None, + output_key: str | None = None, + weights: Sequence[float] | NdarrayOrTensor | None = None, ) -> None: """ Args: @@ -477,9 +480,7 @@ class VoteEnsembled(Ensembled): backend = VoteEnsemble.backend - def __init__( - self, keys: KeysCollection, output_key: Optional[str] = None, num_classes: Optional[int] = None - ) -> None: + def __init__(self, keys: KeysCollection, output_key: str | None = None, num_classes: int | None = None) -> None: """ Args: keys: keys of the corresponding items to be stack and execute ensemble. @@ -531,9 +532,9 @@ def __init__( self, keys: KeysCollection, spatial_dims: int = 2, - sigma: Union[Sequence[float], float, Sequence[torch.Tensor], torch.Tensor] = 0.0, + sigma: Sequence[float] | float | Sequence[torch.Tensor] | torch.Tensor = 0.0, prob_threshold: float = 0.5, - box_size: Union[int, Sequence[int]] = 48, + box_size: int | Sequence[int] = 48, allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) @@ -581,14 +582,14 @@ def __init__( self, keys: KeysCollection, transform: InvertibleTransform, - orig_keys: Optional[KeysCollection] = None, - meta_keys: Optional[KeysCollection] = None, - orig_meta_keys: Optional[KeysCollection] = None, + orig_keys: KeysCollection | None = None, + meta_keys: KeysCollection | None = None, + orig_meta_keys: KeysCollection | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, - nearest_interp: Union[bool, Sequence[bool]] = True, - to_tensor: Union[bool, Sequence[bool]] = True, - device: Union[Union[str, torch.device], Sequence[Union[str, torch.device]], None] = None, - post_func: Union[Callable, Sequence[Callable], None] = None, + nearest_interp: bool | Sequence[bool] = True, + to_tensor: bool | Sequence[bool] = True, + device: str | torch.device | Sequence[str | torch.device] | None = None, + post_func: Callable | Sequence[Callable] | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -638,7 +639,7 @@ def __init__( self.post_func = ensure_tuple_rep(post_func, len(self.keys)) self._totensor = ToTensor() - def __call__(self, data: Mapping[Hashable, Any]) -> Dict[Hashable, Any]: + def __call__(self, data: Mapping[Hashable, Any]) -> dict[Hashable, Any]: d = dict(data) for ( key, @@ -728,9 +729,9 @@ class SaveClassificationd(MapTransform): def __init__( self, keys: KeysCollection, - meta_keys: Optional[KeysCollection] = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, - saver: Optional[CSVSaver] = None, + saver: CSVSaver | None = None, output_dir: PathLike = "./", filename: str = "predictions.csv", delimiter: str = ",", @@ -824,12 +825,12 @@ def __init__( self, keys: KeysCollection, kernel_size: int = 3, - spatial_axes: Optional[Union[Sequence[int], int]] = None, + spatial_axes: Sequence[int] | int | None = None, normalize_kernels: bool = True, normalize_gradients: bool = False, padding_mode: str = "reflect", dtype: torch.dtype = torch.float32, - new_key_prefix: Optional[str] = None, + new_key_prefix: str | None = None, allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) @@ -845,7 +846,7 @@ def __init__( self.kernel_diff = self.transform.kernel_diff self.kernel_smooth = self.transform.kernel_smooth - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): new_key = key if self.new_key_prefix is None else self.new_key_prefix + key diff --git a/monai/transforms/signal/array.py b/monai/transforms/signal/array.py index 7b619a4b39..59b267c151 100644 --- a/monai/transforms/signal/array.py +++ b/monai/transforms/signal/array.py @@ -13,8 +13,11 @@ https://github.com/Project-MONAI/MONAI/wiki/MONAI_Design """ +from __future__ import annotations + import warnings -from typing import Any, Optional, Sequence +from collections.abc import Sequence +from typing import Any import numpy as np import torch @@ -57,7 +60,7 @@ class SignalRandShift(RandomizableTransform): backend = [TransformBackends.NUMPY, TransformBackends.TORCH] def __init__( - self, mode: Optional[str] = "wrap", filling: Optional[float] = 0.0, boundaries: Sequence[float] = (-1.0, 1.0) + self, mode: str | None = "wrap", filling: float | None = 0.0, boundaries: Sequence[float] = (-1.0, 1.0) ) -> None: """ Args: @@ -390,10 +393,7 @@ class SignalRemoveFrequency(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] def __init__( - self, - frequency: Optional[float] = None, - quality_factor: Optional[float] = None, - sampling_freq: Optional[float] = None, + self, frequency: float | None = None, quality_factor: float | None = None, sampling_freq: float | None = None ) -> None: """ Args: diff --git a/monai/transforms/smooth_field/array.py b/monai/transforms/smooth_field/array.py index 13507339e1..c9df5f1dbb 100644 --- a/monai/transforms/smooth_field/array.py +++ b/monai/transforms/smooth_field/array.py @@ -10,7 +10,10 @@ # limitations under the License. """Transforms using a smooth spatial field generated by interpolating from smaller randomized fields.""" -from typing import Any, Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Sequence +from typing import Any import numpy as np import torch @@ -61,10 +64,10 @@ def __init__( low: float = -1.0, high: float = 1.0, channels: int = 1, - spatial_size: Optional[Sequence[int]] = None, + spatial_size: Sequence[int] | None = None, mode: str = InterpolateMode.AREA, - align_corners: Optional[bool] = None, - device: Optional[torch.device] = None, + align_corners: bool | None = None, + device: torch.device | None = None, ): self.rand_size = tuple(rand_size) self.pad = pad @@ -75,8 +78,8 @@ def __init__( self.align_corners = align_corners self.device = device - self.spatial_size: Optional[Sequence[int]] = None - self.spatial_zoom: Optional[Sequence[float]] = None + self.spatial_size: Sequence[int] | None = None + self.spatial_zoom: Sequence[float] | None = None if low >= high: raise ValueError("Value for `low` must be less than `high` otherwise field will be zeros") @@ -92,10 +95,10 @@ def __init__( self.set_spatial_size(spatial_size) - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: self.field[self.rand_slices] = torch.from_numpy(self.R.uniform(self.low, self.high, self.crand_size)) - def set_spatial_size(self, spatial_size: Optional[Sequence[int]]) -> None: + def set_spatial_size(self, spatial_size: Sequence[int] | None) -> None: """ Set the `spatial_size` and `spatial_zoom` attributes used for interpolating the field to the given dimension, or not interpolate at all if None. @@ -169,10 +172,10 @@ def __init__( rand_size: Sequence[int], pad: int = 0, mode: str = InterpolateMode.AREA, - align_corners: Optional[bool] = None, + align_corners: bool | None = None, prob: float = 0.1, - gamma: Union[Sequence[float], float] = (0.5, 4.5), - device: Optional[torch.device] = None, + gamma: Sequence[float] | float = (0.5, 4.5), + device: torch.device | None = None, ): super().__init__(prob) @@ -198,13 +201,13 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandSmoothFieldAdjustContrast": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandSmoothFieldAdjustContrast: super().set_random_state(seed, state) self.sfield.set_random_state(seed, state) return self - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if self._do_transform: @@ -270,10 +273,10 @@ def __init__( rand_size: Sequence[int], pad: int = 0, mode: str = InterpolateMode.AREA, - align_corners: Optional[bool] = None, + align_corners: bool | None = None, prob: float = 0.1, - gamma: Union[Sequence[float], float] = (0.1, 1.0), - device: Optional[torch.device] = None, + gamma: Sequence[float] | float = (0.1, 1.0), + device: torch.device | None = None, ): super().__init__(prob) @@ -299,13 +302,13 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandSmoothFieldAdjustIntensity": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandSmoothFieldAdjustIntensity: super().set_random_state(seed, state) self.sfield.set_random_state(seed, state) return self - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if self._do_transform: @@ -367,14 +370,14 @@ def __init__( rand_size: Sequence[int], pad: int = 0, field_mode: str = InterpolateMode.AREA, - align_corners: Optional[bool] = None, + align_corners: bool | None = None, prob: float = 0.1, - def_range: Union[Sequence[float], float] = 1.0, + def_range: Sequence[float] | float = 1.0, grid_dtype=torch.float32, grid_mode: str = GridSampleMode.NEAREST, grid_padding_mode: str = GridSamplePadMode.BORDER, - grid_align_corners: Optional[bool] = False, - device: Optional[torch.device] = None, + grid_align_corners: bool | None = False, + device: torch.device | None = None, ): super().__init__(prob) @@ -412,14 +415,12 @@ def __init__( self.grid = torch.stack(grid).unsqueeze(0).to(self.device, self.grid_dtype) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "Randomizable": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Randomizable: super().set_random_state(seed, state) self.sfield.set_random_state(seed, state) return self - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if self._do_transform: @@ -432,7 +433,7 @@ def set_grid_mode(self, mode: str) -> None: self.grid_mode = mode def __call__( - self, img: NdarrayOrTensor, randomize: bool = True, device: Optional[torch.device] = None + self, img: NdarrayOrTensor, randomize: bool = True, device: torch.device | None = None ) -> NdarrayOrTensor: img = convert_to_tensor(img, track_meta=get_track_meta()) if randomize: diff --git a/monai/transforms/smooth_field/dictionary.py b/monai/transforms/smooth_field/dictionary.py index 08fb71edb4..99d19064f8 100644 --- a/monai/transforms/smooth_field/dictionary.py +++ b/monai/transforms/smooth_field/dictionary.py @@ -9,7 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Hashable, Mapping, Optional, Sequence, Union +from __future__ import annotations + +from collections.abc import Hashable, Mapping, Sequence +from typing import Any import numpy as np import torch @@ -67,10 +70,10 @@ def __init__( rand_size: Sequence[int], pad: int = 0, mode: SequenceStr = InterpolateMode.AREA, - align_corners: Optional[bool] = None, + align_corners: bool | None = None, prob: float = 0.1, - gamma: Union[Sequence[float], float] = (0.5, 4.5), - device: Optional[torch.device] = None, + gamma: Sequence[float] | float = (0.5, 4.5), + device: torch.device | None = None, ): RandomizableTransform.__init__(self, prob) MapTransform.__init__(self, keys) @@ -89,13 +92,13 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandSmoothFieldAdjustContrastd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandSmoothFieldAdjustContrastd: super().set_random_state(seed, state) self.trans.set_random_state(seed, state) return self - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if self._do_transform: @@ -145,10 +148,10 @@ def __init__( rand_size: Sequence[int], pad: int = 0, mode: SequenceStr = InterpolateMode.AREA, - align_corners: Optional[bool] = None, + align_corners: bool | None = None, prob: float = 0.1, - gamma: Union[Sequence[float], float] = (0.1, 1.0), - device: Optional[torch.device] = None, + gamma: Sequence[float] | float = (0.1, 1.0), + device: torch.device | None = None, ): RandomizableTransform.__init__(self, prob) MapTransform.__init__(self, keys) @@ -167,13 +170,13 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandSmoothFieldAdjustIntensityd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandSmoothFieldAdjustIntensityd: super().set_random_state(seed, state) self.trans.set_random_state(seed, state) return self - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) self.trans.randomize() @@ -226,14 +229,14 @@ def __init__( rand_size: Sequence[int], pad: int = 0, field_mode: SequenceStr = InterpolateMode.AREA, - align_corners: Optional[bool] = None, + align_corners: bool | None = None, prob: float = 0.1, - def_range: Union[Sequence[float], float] = 1.0, + def_range: Sequence[float] | float = 1.0, grid_dtype=torch.float32, grid_mode: SequenceStr = GridSampleMode.NEAREST, grid_padding_mode: str = GridSamplePadMode.BORDER, - grid_align_corners: Optional[bool] = False, - device: Optional[torch.device] = None, + grid_align_corners: bool | None = False, + device: torch.device | None = None, ): RandomizableTransform.__init__(self, prob) MapTransform.__init__(self, keys) @@ -257,13 +260,13 @@ def __init__( ) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandSmoothDeformd": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandSmoothDeformd: super().set_random_state(seed, state) self.trans.set_random_state(seed, state) return self - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) self.trans.randomize() diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index d63dfd5309..db4d98de18 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -12,11 +12,15 @@ A collection of "vanilla" transforms for spatial operations https://github.com/Project-MONAI/MONAI/wiki/MONAI_Design """ + +from __future__ import annotations + import warnings +from collections.abc import Callable from copy import deepcopy from enum import Enum from itertools import zip_longest -from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union, cast +from typing import Any, Optional, Sequence, Tuple, Union, cast import numpy as np import torch @@ -120,7 +124,7 @@ class SpatialResample(InvertibleTransform): def __init__( self, - mode: Union[str, int] = GridSampleMode.BILINEAR, + mode: str | int = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, dtype: DtypeLike = np.float64, @@ -191,12 +195,12 @@ def update_meta(self, img, dst_affine): def __call__( self, img: torch.Tensor, - src_affine: Optional[NdarrayOrTensor] = None, - dst_affine: Optional[torch.Tensor] = None, - spatial_size: Optional[Union[Sequence[int], torch.Tensor, int]] = None, - mode: Union[str, int, None] = None, - padding_mode: Optional[str] = None, - align_corners: Optional[bool] = None, + src_affine: NdarrayOrTensor | None = None, + dst_affine: torch.Tensor | None = None, + spatial_size: Sequence[int] | torch.Tensor | int | None = None, + mode: str | int | None = None, + padding_mode: str | None = None, + align_corners: bool | None = None, dtype: DtypeLike = None, ) -> torch.Tensor: """ @@ -360,11 +364,11 @@ def __call__( self, img: torch.Tensor, img_dst: torch.Tensor, - src_meta: Optional[Dict] = None, - dst_meta: Optional[Dict] = None, - mode: Union[str, int, None] = None, - padding_mode: Optional[str] = None, - align_corners: Optional[bool] = None, + src_meta: dict | None = None, + dst_meta: dict | None = None, + mode: str | int | None = None, + padding_mode: str | None = None, + align_corners: bool | None = None, dtype: DtypeLike = None, ) -> torch.Tensor: """ @@ -430,16 +434,16 @@ class Spacing(InvertibleTransform): @deprecated_arg(name="image_only", since="0.9") def __init__( self, - pixdim: Union[Sequence[float], float, np.ndarray], + pixdim: Sequence[float] | float | np.ndarray, diagonal: bool = False, - mode: Union[str, int] = GridSampleMode.BILINEAR, + mode: str | int = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, dtype: DtypeLike = np.float64, scale_extent: bool = False, recompute_affine: bool = False, - min_pixdim: Union[Sequence[float], float, np.ndarray, None] = None, - max_pixdim: Union[Sequence[float], float, np.ndarray, None] = None, + min_pixdim: Sequence[float] | float | np.ndarray | None = None, + max_pixdim: Sequence[float] | float | np.ndarray | None = None, image_only: bool = False, ) -> None: """ @@ -514,13 +518,13 @@ def __init__( def __call__( self, data_array: torch.Tensor, - affine: Optional[NdarrayOrTensor] = None, - mode: Union[str, int, None] = None, - padding_mode: Optional[str] = None, - align_corners: Optional[bool] = None, + affine: NdarrayOrTensor | None = None, + mode: str | int | None = None, + padding_mode: str | None = None, + align_corners: bool | None = None, dtype: DtypeLike = None, - scale_extent: Optional[bool] = None, - output_spatial_shape: Optional[Union[Sequence[int], np.ndarray, int]] = None, + scale_extent: bool | None = None, + output_spatial_shape: Sequence[int] | np.ndarray | int | None = None, ) -> torch.Tensor: """ Args: @@ -630,9 +634,9 @@ class Orientation(InvertibleTransform): @deprecated_arg(name="image_only", since="0.9") def __init__( self, - axcodes: Optional[str] = None, + axcodes: str | None = None, as_closest_canonical: bool = False, - labels: Optional[Sequence[Tuple[str, str]]] = (("L", "R"), ("P", "A"), ("I", "S")), + labels: Sequence[tuple[str, str]] | None = (("L", "R"), ("P", "A"), ("I", "S")), image_only: bool = False, ) -> None: """ @@ -768,7 +772,7 @@ class Flip(InvertibleTransform): backend = [TransformBackends.TORCH] - def __init__(self, spatial_axis: Optional[Union[Sequence[int], int]] = None) -> None: + def __init__(self, spatial_axis: Sequence[int] | int | None = None) -> None: self.spatial_axis = spatial_axis def update_meta(self, img, shape, axes): @@ -839,12 +843,12 @@ class Resize(InvertibleTransform): def __init__( self, - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, size_mode: str = "all", mode: str = InterpolateMode.AREA, - align_corners: Optional[bool] = None, + align_corners: bool | None = None, anti_aliasing: bool = False, - anti_aliasing_sigma: Union[Sequence[float], float, None] = None, + anti_aliasing_sigma: Sequence[float] | float | None = None, ) -> None: self.size_mode = look_up_option(size_mode, ["all", "longest"]) self.spatial_size = spatial_size @@ -856,10 +860,10 @@ def __init__( def __call__( self, img: torch.Tensor, - mode: Optional[str] = None, - align_corners: Optional[bool] = None, - anti_aliasing: Optional[bool] = None, - anti_aliasing_sigma: Union[Sequence[float], float, None] = None, + mode: str | None = None, + align_corners: bool | None = None, + anti_aliasing: bool | None = None, + anti_aliasing_sigma: Sequence[float] | float | None = None, ) -> torch.Tensor: """ Args: @@ -998,12 +1002,12 @@ class Rotate(InvertibleTransform): def __init__( self, - angle: Union[Sequence[float], float], + angle: Sequence[float] | float, keep_size: bool = True, mode: str = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, - dtype: Union[DtypeLike, torch.dtype] = torch.float32, + dtype: DtypeLike | torch.dtype = torch.float32, ) -> None: self.angle = angle self.keep_size = keep_size @@ -1015,10 +1019,10 @@ def __init__( def __call__( self, img: torch.Tensor, - mode: Optional[str] = None, - padding_mode: Optional[str] = None, - align_corners: Optional[bool] = None, - dtype: Union[DtypeLike, torch.dtype] = None, + mode: str | None = None, + padding_mode: str | None = None, + align_corners: bool | None = None, + dtype: DtypeLike | torch.dtype = None, ) -> torch.Tensor: """ Args: @@ -1160,10 +1164,10 @@ class Zoom(InvertibleTransform): def __init__( self, - zoom: Union[Sequence[float], float], + zoom: Sequence[float] | float, mode: str = InterpolateMode.AREA, padding_mode: str = NumpyPadMode.EDGE, - align_corners: Optional[bool] = None, + align_corners: bool | None = None, keep_size: bool = True, **kwargs, ) -> None: @@ -1177,9 +1181,9 @@ def __init__( def __call__( self, img: torch.Tensor, - mode: Optional[str] = None, - padding_mode: Optional[str] = None, - align_corners: Optional[bool] = None, + mode: str | None = None, + padding_mode: str | None = None, + align_corners: bool | None = None, ) -> torch.Tensor: """ Args: @@ -1278,7 +1282,7 @@ class Rotate90(InvertibleTransform): backend = [TransformBackends.TORCH] - def __init__(self, k: int = 1, spatial_axes: Tuple[int, int] = (0, 1)) -> None: + def __init__(self, k: int = 1, spatial_axes: tuple[int, int] = (0, 1)) -> None: """ Args: k: number of times to rotate by 90 degrees. @@ -1287,7 +1291,7 @@ def __init__(self, k: int = 1, spatial_axes: Tuple[int, int] = (0, 1)) -> None: If axis is negative it counts from the last to the first axis. """ self.k = k - spatial_axes_: Tuple[int, int] = ensure_tuple(spatial_axes) # type: ignore + spatial_axes_: tuple[int, int] = ensure_tuple(spatial_axes) # type: ignore if len(spatial_axes_) != 2: raise ValueError("spatial_axes must be 2 int numbers to indicate the axes to rotate 90 degrees.") self.spatial_axes = spatial_axes_ @@ -1316,7 +1320,7 @@ def update_meta(self, img, spatial_size, new_spatial_size, axes, k): rot90 = to_affine_nd(r, create_rotate(sp_r, [s * np.pi / 2])) else: idx = {1, 2, 3} - set(axes) - angle: List[float] = [0, 0, 0] + angle: list[float] = [0, 0, 0] angle[idx.pop() - 1] = s * np.pi / 2 rot90 = to_affine_nd(r, create_rotate(sp_r, angle)) for _ in range(k): @@ -1345,7 +1349,7 @@ class RandRotate90(RandomizableTransform, InvertibleTransform): backend = Rotate90.backend - def __init__(self, prob: float = 0.1, max_k: int = 3, spatial_axes: Tuple[int, int] = (0, 1)) -> None: + def __init__(self, prob: float = 0.1, max_k: int = 3, spatial_axes: tuple[int, int] = (0, 1)) -> None: """ Args: prob: probability of rotating. @@ -1360,7 +1364,7 @@ def __init__(self, prob: float = 0.1, max_k: int = 3, spatial_axes: Tuple[int, i self._rand_k = 0 - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if not self._do_transform: return None @@ -1425,15 +1429,15 @@ class RandRotate(RandomizableTransform, InvertibleTransform): def __init__( self, - range_x: Union[Tuple[float, float], float] = 0.0, - range_y: Union[Tuple[float, float], float] = 0.0, - range_z: Union[Tuple[float, float], float] = 0.0, + range_x: tuple[float, float] | float = 0.0, + range_y: tuple[float, float] | float = 0.0, + range_z: tuple[float, float] | float = 0.0, prob: float = 0.1, keep_size: bool = True, mode: str = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.BORDER, align_corners: bool = False, - dtype: Union[DtypeLike, torch.dtype] = np.float32, + dtype: DtypeLike | torch.dtype = np.float32, ) -> None: RandomizableTransform.__init__(self, prob) self.range_x = ensure_tuple(range_x) @@ -1456,7 +1460,7 @@ def __init__( self.y = 0.0 self.z = 0.0 - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if not self._do_transform: return None @@ -1468,10 +1472,10 @@ def randomize(self, data: Optional[Any] = None) -> None: def __call__( self, img: torch.Tensor, - mode: Optional[str] = None, - padding_mode: Optional[str] = None, - align_corners: Optional[bool] = None, - dtype: Union[DtypeLike, torch.dtype] = None, + mode: str | None = None, + padding_mode: str | None = None, + align_corners: bool | None = None, + dtype: DtypeLike | torch.dtype = None, randomize: bool = True, get_matrix: bool = False, ): @@ -1531,7 +1535,7 @@ class RandFlip(RandomizableTransform, InvertibleTransform): backend = Flip.backend - def __init__(self, prob: float = 0.1, spatial_axis: Optional[Union[Sequence[int], int]] = None) -> None: + def __init__(self, prob: float = 0.1, spatial_axis: Sequence[int] | int | None = None) -> None: RandomizableTransform.__init__(self, prob) self.flipper = Flip(spatial_axis=spatial_axis) @@ -1573,7 +1577,7 @@ class RandAxisFlip(RandomizableTransform, InvertibleTransform): def __init__(self, prob: float = 0.1) -> None: RandomizableTransform.__init__(self, prob) - self._axis: Optional[int] = None + self._axis: int | None = None self.flipper = Flip(spatial_axis=self._axis) def randomize(self, data: NdarrayOrTensor) -> None: @@ -1651,11 +1655,11 @@ class RandZoom(RandomizableTransform, InvertibleTransform): def __init__( self, prob: float = 0.1, - min_zoom: Union[Sequence[float], float] = 0.9, - max_zoom: Union[Sequence[float], float] = 1.1, + min_zoom: Sequence[float] | float = 0.9, + max_zoom: Sequence[float] | float = 1.1, mode: str = InterpolateMode.AREA, padding_mode: str = NumpyPadMode.EDGE, - align_corners: Optional[bool] = None, + align_corners: bool | None = None, keep_size: bool = True, **kwargs, ) -> None: @@ -1687,9 +1691,9 @@ def randomize(self, img: NdarrayOrTensor) -> None: def __call__( self, img: torch.Tensor, - mode: Optional[str] = None, - padding_mode: Optional[str] = None, - align_corners: Optional[bool] = None, + mode: str | None = None, + padding_mode: str | None = None, + align_corners: bool | None = None, randomize: bool = True, ) -> torch.Tensor: """ @@ -1772,13 +1776,13 @@ class AffineGrid(Transform): def __init__( self, - rotate_params: Optional[Union[Sequence[float], float]] = None, - shear_params: Optional[Union[Sequence[float], float]] = None, - translate_params: Optional[Union[Sequence[float], float]] = None, - scale_params: Optional[Union[Sequence[float], float]] = None, - device: Optional[torch.device] = None, + rotate_params: Sequence[float] | float | None = None, + shear_params: Sequence[float] | float | None = None, + translate_params: Sequence[float] | float | None = None, + scale_params: Sequence[float] | float | None = None, + device: torch.device | None = None, dtype: DtypeLike = np.float32, - affine: Optional[NdarrayOrTensor] = None, + affine: NdarrayOrTensor | None = None, ) -> None: self.rotate_params = rotate_params self.shear_params = shear_params @@ -1789,8 +1793,8 @@ def __init__( self.affine = affine def __call__( - self, spatial_size: Optional[Sequence[int]] = None, grid: Optional[torch.Tensor] = None - ) -> Tuple[torch.Tensor, torch.Tensor]: + self, spatial_size: Sequence[int] | None = None, grid: torch.Tensor | None = None + ) -> tuple[torch.Tensor, torch.Tensor]: """ The grid can be initialized with a `spatial_size` parameter, or provided directly as `grid`. Therefore, either `spatial_size` or `grid` must be provided. @@ -1849,7 +1853,7 @@ def __init__( shear_range: RandRange = None, translate_range: RandRange = None, scale_range: RandRange = None, - device: Optional[torch.device] = None, + device: torch.device | None = None, ) -> None: """ Args: @@ -1889,13 +1893,13 @@ def __init__( self.translate_range = ensure_tuple(translate_range) self.scale_range = ensure_tuple(scale_range) - self.rotate_params: Optional[List[float]] = None - self.shear_params: Optional[List[float]] = None - self.translate_params: Optional[List[float]] = None - self.scale_params: Optional[List[float]] = None + self.rotate_params: list[float] | None = None + self.shear_params: list[float] | None = None + self.translate_params: list[float] | None = None + self.scale_params: list[float] | None = None self.device = device - self.affine: Optional[torch.Tensor] = torch.eye(4, dtype=torch.float64) + self.affine: torch.Tensor | None = torch.eye(4, dtype=torch.float64) def _get_rand_param(self, param_range, add_scalar: float = 0.0): out_param = [] @@ -1908,17 +1912,14 @@ def _get_rand_param(self, param_range, add_scalar: float = 0.0): out_param.append(self.R.uniform(-f, f) + add_scalar) return out_param - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: self.rotate_params = self._get_rand_param(self.rotate_range) self.shear_params = self._get_rand_param(self.shear_range) self.translate_params = self._get_rand_param(self.translate_range) self.scale_params = self._get_rand_param(self.scale_range, 1.0) def __call__( - self, - spatial_size: Optional[Sequence[int]] = None, - grid: Optional[NdarrayOrTensor] = None, - randomize: bool = True, + self, spatial_size: Sequence[int] | None = None, grid: NdarrayOrTensor | None = None, randomize: bool = True ) -> torch.Tensor: """ Args: @@ -1942,7 +1943,7 @@ def __call__( _grid, self.affine = affine_grid(spatial_size, grid) # type: ignore return _grid - def get_transformation_matrix(self) -> Optional[torch.Tensor]: + def get_transformation_matrix(self) -> torch.Tensor | None: """Get the most recently applied transformation matrix""" return self.affine @@ -1957,10 +1958,10 @@ class RandDeformGrid(Randomizable, Transform): @deprecated_arg(name="as_tensor_output", since="0.8") def __init__( self, - spacing: Union[Sequence[float], float], - magnitude_range: Tuple[float, float], + spacing: Sequence[float] | float, + magnitude_range: tuple[float, float], as_tensor_output: bool = True, - device: Optional[torch.device] = None, + device: torch.device | None = None, ) -> None: """ Args: @@ -2002,10 +2003,10 @@ class Resample(Transform): def __init__( self, - mode: Union[str, int] = GridSampleMode.BILINEAR, + mode: str | int = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.BORDER, norm_coords: bool = True, - device: Optional[torch.device] = None, + device: torch.device | None = None, dtype: DtypeLike = np.float64, ) -> None: """ @@ -2049,9 +2050,9 @@ def __init__( def __call__( self, img: torch.Tensor, - grid: Optional[torch.Tensor] = None, - mode: Union[str, int, None] = None, - padding_mode: Optional[str] = None, + grid: torch.Tensor | None = None, + mode: str | int | None = None, + padding_mode: str | None = None, dtype: DtypeLike = None, ) -> torch.Tensor: """ @@ -2141,7 +2142,7 @@ def __call__( if self.norm_coords: for i, dim in enumerate(img_t.shape[1 : 1 + sr]): grid_t[i] = 2.0 / (max(2, dim) - 1.0) * grid_t[i] / grid_t[-1:] - index_ordering: List[int] = list(range(sr - 1, -1, -1)) + index_ordering: list[int] = list(range(sr - 1, -1, -1)) grid_t = moveaxis(grid_t[index_ordering], 0, -1) # type: ignore out = torch.nn.functional.grid_sample( img_t.unsqueeze(0), @@ -2166,17 +2167,17 @@ class Affine(InvertibleTransform): @deprecated_arg(name="norm_coords", since="0.8") def __init__( self, - rotate_params: Optional[Union[Sequence[float], float]] = None, - shear_params: Optional[Union[Sequence[float], float]] = None, - translate_params: Optional[Union[Sequence[float], float]] = None, - scale_params: Optional[Union[Sequence[float], float]] = None, - affine: Optional[NdarrayOrTensor] = None, - spatial_size: Optional[Union[Sequence[int], int]] = None, - mode: Union[str, int] = GridSampleMode.BILINEAR, + rotate_params: Sequence[float] | float | None = None, + shear_params: Sequence[float] | float | None = None, + translate_params: Sequence[float] | float | None = None, + scale_params: Sequence[float] | float | None = None, + affine: NdarrayOrTensor | None = None, + spatial_size: Sequence[int] | int | None = None, + mode: str | int = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.REFLECTION, normalized: bool = False, norm_coords: bool = True, - device: Optional[torch.device] = None, + device: torch.device | None = None, dtype: DtypeLike = np.float32, image_only: bool = False, ) -> None: @@ -2256,10 +2257,10 @@ def __init__( def __call__( self, img: torch.Tensor, - spatial_size: Optional[Union[Sequence[int], int]] = None, - mode: Union[str, int, None] = None, - padding_mode: Optional[str] = None, - ) -> Union[torch.Tensor, Tuple[torch.Tensor, NdarrayOrTensor]]: + spatial_size: Sequence[int] | int | None = None, + mode: str | int | None = None, + padding_mode: str | None = None, + ) -> torch.Tensor | tuple[torch.Tensor, NdarrayOrTensor]: """ Args: img: shape must be (num_channels, H, W[, D]), @@ -2348,11 +2349,11 @@ def __init__( shear_range: RandRange = None, translate_range: RandRange = None, scale_range: RandRange = None, - spatial_size: Optional[Union[Sequence[int], int]] = None, - mode: Union[str, int] = GridSampleMode.BILINEAR, + spatial_size: Sequence[int] | int | None = None, + mode: str | int = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.REFLECTION, cache_grid: bool = False, - device: Optional[torch.device] = None, + device: torch.device | None = None, ) -> None: """ Args: @@ -2465,14 +2466,12 @@ def get_identity_grid(self, spatial_size: Sequence[int]): else self._cached_grid ) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandAffine": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAffine: self.rand_affine_grid.set_random_state(seed, state) super().set_random_state(seed, state) return self - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: super().randomize(None) if not self._do_transform: return None @@ -2481,9 +2480,9 @@ def randomize(self, data: Optional[Any] = None) -> None: def __call__( self, img: torch.Tensor, - spatial_size: Optional[Union[Sequence[int], int]] = None, - mode: Union[str, int, None] = None, - padding_mode: Optional[str] = None, + spatial_size: Sequence[int] | int | None = None, + mode: str | int | None = None, + padding_mode: str | None = None, randomize: bool = True, grid=None, ) -> torch.Tensor: @@ -2584,17 +2583,17 @@ class Rand2DElastic(RandomizableTransform): def __init__( self, - spacing: Union[Tuple[float, float], float], - magnitude_range: Tuple[float, float], + spacing: tuple[float, float] | float, + magnitude_range: tuple[float, float], prob: float = 0.1, rotate_range: RandRange = None, shear_range: RandRange = None, translate_range: RandRange = None, scale_range: RandRange = None, - spatial_size: Optional[Union[Tuple[int, int], int]] = None, - mode: Union[str, int] = GridSampleMode.BILINEAR, + spatial_size: tuple[int, int] | int | None = None, + mode: str | int = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.REFLECTION, - device: Optional[torch.device] = None, + device: torch.device | None = None, ) -> None: """ Args: @@ -2664,9 +2663,7 @@ def __init__( self.mode = mode self.padding_mode: str = padding_mode - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "Rand2DElastic": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Rand2DElastic: self.deform_grid.set_random_state(seed, state) self.rand_affine_grid.set_random_state(seed, state) super().set_random_state(seed, state) @@ -2688,9 +2685,9 @@ def randomize(self, spatial_size: Sequence[int]) -> None: def __call__( self, img: torch.Tensor, - spatial_size: Optional[Union[Tuple[int, int], int]] = None, - mode: Union[str, int, None] = None, - padding_mode: Optional[str] = None, + spatial_size: tuple[int, int] | int | None = None, + mode: str | int | None = None, + padding_mode: str | None = None, randomize: bool = True, ) -> torch.Tensor: """ @@ -2751,17 +2748,17 @@ class Rand3DElastic(RandomizableTransform): def __init__( self, - sigma_range: Tuple[float, float], - magnitude_range: Tuple[float, float], + sigma_range: tuple[float, float], + magnitude_range: tuple[float, float], prob: float = 0.1, rotate_range: RandRange = None, shear_range: RandRange = None, translate_range: RandRange = None, scale_range: RandRange = None, - spatial_size: Optional[Union[Tuple[int, int, int], int]] = None, - mode: Union[str, int] = GridSampleMode.BILINEAR, + spatial_size: tuple[int, int, int] | int | None = None, + mode: str | int = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.REFLECTION, - device: Optional[torch.device] = None, + device: torch.device | None = None, ) -> None: """ Args: @@ -2839,9 +2836,7 @@ def __init__( self.magnitude = 1.0 self.sigma = 1.0 - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "Rand3DElastic": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Rand3DElastic: self.rand_affine_grid.set_random_state(seed, state) super().set_random_state(seed, state) return self @@ -2863,9 +2858,9 @@ def randomize(self, grid_size: Sequence[int]) -> None: def __call__( self, img: torch.Tensor, - spatial_size: Optional[Union[Tuple[int, int, int], int]] = None, - mode: Union[str, int, None] = None, - padding_mode: Optional[str] = None, + spatial_size: tuple[int, int, int] | int | None = None, + mode: str | int | None = None, + padding_mode: str | None = None, randomize: bool = True, ) -> torch.Tensor: """ @@ -2916,11 +2911,11 @@ class GridDistortion(Transform): def __init__( self, - num_cells: Union[Tuple[int], int], + num_cells: tuple[int] | int, distort_steps: Sequence[Sequence[float]], - mode: Union[str, int] = GridSampleMode.BILINEAR, + mode: str | int = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.BORDER, - device: Optional[torch.device] = None, + device: torch.device | None = None, ) -> None: """ Grid distortion transform. Refer to: @@ -2954,9 +2949,9 @@ def __init__( def __call__( self, img: torch.Tensor, - distort_steps: Optional[Sequence[Sequence]] = None, - mode: Optional[str] = None, - padding_mode: Optional[str] = None, + distort_steps: Sequence[Sequence] | None = None, + mode: str | None = None, + padding_mode: str | None = None, ) -> torch.Tensor: """ Args: @@ -3014,12 +3009,12 @@ class RandGridDistortion(RandomizableTransform): def __init__( self, - num_cells: Union[Tuple[int], int] = 5, + num_cells: tuple[int] | int = 5, prob: float = 0.1, - distort_limit: Union[Tuple[float, float], float] = (-0.03, 0.03), - mode: Union[str, int] = GridSampleMode.BILINEAR, + distort_limit: tuple[float, float] | float = (-0.03, 0.03), + mode: str | int = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.BORDER, - device: Optional[torch.device] = None, + device: torch.device | None = None, ) -> None: """ Random grid distortion transform. Refer to: @@ -3067,7 +3062,7 @@ def randomize(self, spatial_shape: Sequence[int]) -> None: ) def __call__( - self, img: torch.Tensor, mode: Optional[str] = None, padding_mode: Optional[str] = None, randomize: bool = True + self, img: torch.Tensor, mode: str | None = None, padding_mode: str | None = None, randomize: bool = True ) -> torch.Tensor: """ Args: @@ -3113,7 +3108,7 @@ class GridSplit(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, grid: Tuple[int, int] = (2, 2), size: Optional[Union[int, Tuple[int, int]]] = None): + def __init__(self, grid: tuple[int, int] = (2, 2), size: int | tuple[int, int] | None = None): # Grid size self.grid = grid @@ -3121,15 +3116,15 @@ def __init__(self, grid: Tuple[int, int] = (2, 2), size: Optional[Union[int, Tup self.size = None if size is None else ensure_tuple_rep(size, len(self.grid)) def __call__( - self, image: NdarrayOrTensor, size: Optional[Union[int, Tuple[int, int], np.ndarray]] = None - ) -> List[NdarrayOrTensor]: + self, image: NdarrayOrTensor, size: int | tuple[int, int] | np.ndarray | None = None + ) -> list[NdarrayOrTensor]: input_size = self.size if size is None else ensure_tuple_rep(size, len(self.grid)) if self.grid == (1, 1) and input_size is None: return [image] split_size, steps = self._get_params(image.shape[1:], input_size) - patches: List[NdarrayOrTensor] + patches: list[NdarrayOrTensor] as_strided_func: Callable if isinstance(image, torch.Tensor): as_strided_func = torch.as_strided @@ -3157,9 +3152,7 @@ def __call__( return patches - def _get_params( - self, image_size: Union[Sequence[int], np.ndarray], size: Optional[Union[Sequence[int], np.ndarray]] = None - ): + def _get_params(self, image_size: Sequence[int] | np.ndarray, size: Sequence[int] | np.ndarray | None = None): """ Calculate the size and step required for splitting the image Args: @@ -3209,17 +3202,17 @@ class GridPatch(Transform): def __init__( self, patch_size: Sequence[int], - offset: Optional[Sequence[int]] = None, - num_patches: Optional[int] = None, - overlap: Union[Sequence[float], float] = 0.0, - sort_fn: Optional[str] = None, - threshold: Optional[float] = None, + offset: Sequence[int] | None = None, + num_patches: int | None = None, + overlap: Sequence[float] | float = 0.0, + sort_fn: str | None = None, + threshold: float | None = None, pad_mode: str = PytorchPadMode.CONSTANT, **pad_kwargs, ): self.patch_size = ensure_tuple(patch_size) self.offset = ensure_tuple(offset) if offset else (0,) * len(self.patch_size) - self.pad_mode: Optional[NumpyPadMode] = convert_pad_mode(dst=np.zeros(1), mode=pad_mode) if pad_mode else None + self.pad_mode: NumpyPadMode | None = convert_pad_mode(dst=np.zeros(1), mode=pad_mode) if pad_mode else None self.pad_kwargs = pad_kwargs self.overlap = overlap self.num_patches = num_patches @@ -3338,12 +3331,12 @@ class RandGridPatch(GridPatch, RandomizableTransform): def __init__( self, patch_size: Sequence[int], - min_offset: Optional[Union[Sequence[int], int]] = None, - max_offset: Optional[Union[Sequence[int], int]] = None, - num_patches: Optional[int] = None, - overlap: Union[Sequence[float], float] = 0.0, - sort_fn: Optional[str] = None, - threshold: Optional[float] = None, + min_offset: Sequence[int] | int | None = None, + max_offset: Sequence[int] | int | None = None, + num_patches: int | None = None, + overlap: Sequence[float] | float = 0.0, + sort_fn: str | None = None, + threshold: float | None = None, pad_mode: str = PytorchPadMode.CONSTANT, **pad_kwargs, ): diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index f30c97e452..7a50cacf12 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -15,7 +15,10 @@ Class names are ended with 'd' to denote dictionary-based transforms. """ -from typing import Any, Dict, Hashable, List, Mapping, Optional, Sequence, Tuple, Union, cast +from __future__ import annotations + +from collections.abc import Hashable, Mapping, Sequence +from typing import Any, cast import numpy as np import torch @@ -164,12 +167,12 @@ def __init__( keys: KeysCollection, mode: SequenceStr = GridSampleMode.BILINEAR, padding_mode: SequenceStr = GridSamplePadMode.BORDER, - align_corners: Union[Sequence[bool], bool] = False, - dtype: Union[Sequence[DtypeLike], DtypeLike] = np.float64, - meta_keys: Optional[KeysCollection] = None, + align_corners: Sequence[bool] | bool = False, + dtype: Sequence[DtypeLike] | DtypeLike = np.float64, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = "meta_dict", - meta_src_keys: Optional[KeysCollection] = "src_affine", - dst_keys: Optional[KeysCollection] = "dst_affine", + meta_src_keys: KeysCollection | None = "src_affine", + dst_keys: KeysCollection | None = "dst_affine", allow_missing_keys: bool = False, ) -> None: """ @@ -207,8 +210,8 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.dst_keys = ensure_tuple_rep(dst_keys, len(self.keys)) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + d: dict = dict(data) for (key, mode, padding_mode, align_corners, dtype, dst_key) in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype, self.dst_keys ): @@ -223,7 +226,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc ) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.sp_transform.inverse(d[key]) @@ -240,11 +243,11 @@ def __init__( self, keys: KeysCollection, key_dst: str, - template_key: Optional[str] = None, + template_key: str | None = None, mode: SequenceStr = GridSampleMode.BILINEAR, padding_mode: SequenceStr = GridSamplePadMode.BORDER, - align_corners: Union[Sequence[bool], bool] = False, - dtype: Union[Sequence[DtypeLike], DtypeLike] = np.float64, + align_corners: Sequence[bool] | bool = False, + dtype: Sequence[DtypeLike] | DtypeLike = np.float64, allow_missing_keys: bool = False, ): """ @@ -282,7 +285,7 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.resampler = ResampleToMatch() - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for (key, mode, padding_mode, align_corners, dtype) in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype @@ -297,7 +300,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc ) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.resampler.inverse(d[key]) @@ -325,18 +328,18 @@ class Spacingd(MapTransform, InvertibleTransform): def __init__( self, keys: KeysCollection, - pixdim: Union[Sequence[float], float], + pixdim: Sequence[float] | float, diagonal: bool = False, mode: SequenceStr = GridSampleMode.BILINEAR, padding_mode: SequenceStr = GridSamplePadMode.BORDER, - align_corners: Union[Sequence[bool], bool] = False, - dtype: Union[Sequence[DtypeLike], DtypeLike] = np.float64, + align_corners: Sequence[bool] | bool = False, + dtype: Sequence[DtypeLike] | DtypeLike = np.float64, scale_extent: bool = False, recompute_affine: bool = False, - meta_keys: Optional[KeysCollection] = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = "meta_dict", - min_pixdim: Union[Sequence[float], float, None] = None, - max_pixdim: Union[Sequence[float], float, None] = None, + min_pixdim: Sequence[float] | float | None = None, + max_pixdim: Sequence[float] | float | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -407,8 +410,8 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.scale_extent = ensure_tuple_rep(scale_extent, len(self.keys)) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + d: dict = dict(data) for key, mode, padding_mode, align_corners, dtype, scale_extent in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype, self.scale_extent ): @@ -423,7 +426,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc ) return d - def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.spacing_transform.inverse(cast(torch.Tensor, d[key])) @@ -446,10 +449,10 @@ class Orientationd(MapTransform, InvertibleTransform): def __init__( self, keys: KeysCollection, - axcodes: Optional[str] = None, + axcodes: str | None = None, as_closest_canonical: bool = False, - labels: Optional[Sequence[Tuple[str, str]]] = (("L", "R"), ("P", "A"), ("I", "S")), - meta_keys: Optional[KeysCollection] = None, + labels: Sequence[tuple[str, str]] | None = (("L", "R"), ("P", "A"), ("I", "S")), + meta_keys: KeysCollection | None = None, meta_key_postfix: str = "meta_dict", allow_missing_keys: bool = False, ) -> None: @@ -473,13 +476,13 @@ def __init__( super().__init__(keys, allow_missing_keys) self.ornt_transform = Orientation(axcodes=axcodes, as_closest_canonical=as_closest_canonical, labels=labels) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: - d: Dict = dict(data) + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + d: dict = dict(data) for key in self.key_iterator(d): d[key] = self.ornt_transform(d[key]) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.ornt_transform.inverse(d[key]) @@ -494,7 +497,7 @@ class Rotate90d(MapTransform, InvertibleTransform): backend = Rotate90.backend def __init__( - self, keys: KeysCollection, k: int = 1, spatial_axes: Tuple[int, int] = (0, 1), allow_missing_keys: bool = False + self, keys: KeysCollection, k: int = 1, spatial_axes: tuple[int, int] = (0, 1), allow_missing_keys: bool = False ) -> None: """ Args: @@ -506,13 +509,13 @@ def __init__( super().__init__(keys, allow_missing_keys) self.rotator = Rotate90(k, spatial_axes) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.rotator(d[key]) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.rotator.inverse(d[key]) @@ -533,7 +536,7 @@ def __init__( keys: KeysCollection, prob: float = 0.1, max_k: int = 3, - spatial_axes: Tuple[int, int] = (0, 1), + spatial_axes: tuple[int, int] = (0, 1), allow_missing_keys: bool = False, ) -> None: """ @@ -556,7 +559,7 @@ def __init__( self._rand_k = 0 - def randomize(self, data: Optional[Any] = None) -> None: + def randomize(self, data: Any | None = None) -> None: self._rand_k = self.R.randint(self.max_k) + 1 super().randomize(None) @@ -574,7 +577,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Mapping[Hashable, t self.push_transform(d[key], extra_info=xform) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): if not isinstance(d[key], MetaTensor): @@ -626,12 +629,12 @@ class Resized(MapTransform, InvertibleTransform): def __init__( self, keys: KeysCollection, - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, size_mode: str = "all", mode: SequenceStr = InterpolateMode.AREA, - align_corners: Union[Sequence[Optional[bool]], Optional[bool]] = None, - anti_aliasing: Union[Sequence[bool], bool] = False, - anti_aliasing_sigma: Union[Sequence[Union[Sequence[float], float, None]], Sequence[float], float, None] = None, + align_corners: Sequence[bool | None] | bool | None = None, + anti_aliasing: Sequence[bool] | bool = False, + anti_aliasing_sigma: Sequence[Sequence[float] | float | None] | Sequence[float] | float | None = None, allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) @@ -641,7 +644,7 @@ def __init__( self.anti_aliasing_sigma = ensure_tuple_rep(anti_aliasing_sigma, len(self.keys)) self.resizer = Resize(spatial_size=spatial_size, size_mode=size_mode) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key, mode, align_corners, anti_aliasing, anti_aliasing_sigma in self.key_iterator( d, self.mode, self.align_corners, self.anti_aliasing, self.anti_aliasing_sigma @@ -655,7 +658,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc ) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.resizer.inverse(d[key]) @@ -672,16 +675,16 @@ class Affined(MapTransform, InvertibleTransform): def __init__( self, keys: KeysCollection, - rotate_params: Optional[Union[Sequence[float], float]] = None, - shear_params: Optional[Union[Sequence[float], float]] = None, - translate_params: Optional[Union[Sequence[float], float]] = None, - scale_params: Optional[Union[Sequence[float], float]] = None, - affine: Optional[NdarrayOrTensor] = None, - spatial_size: Optional[Union[Sequence[int], int]] = None, + rotate_params: Sequence[float] | float | None = None, + shear_params: Sequence[float] | float | None = None, + translate_params: Sequence[float] | float | None = None, + scale_params: Sequence[float] | float | None = None, + affine: NdarrayOrTensor | None = None, + spatial_size: Sequence[int] | int | None = None, mode: SequenceStr = GridSampleMode.BILINEAR, padding_mode: SequenceStr = GridSamplePadMode.REFLECTION, - device: Optional[torch.device] = None, - dtype: Union[DtypeLike, torch.dtype] = np.float32, + device: torch.device | None = None, + dtype: DtypeLike | torch.dtype = np.float32, allow_missing_keys: bool = False, ) -> None: """ @@ -751,13 +754,13 @@ def __init__( self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key, mode, padding_mode in self.key_iterator(d, self.mode, self.padding_mode): d[key], _ = self.affine(d[key], mode=mode, padding_mode=padding_mode) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.affine.inverse(d[key]) @@ -774,16 +777,16 @@ class RandAffined(RandomizableTransform, MapTransform, InvertibleTransform): def __init__( self, keys: KeysCollection, - spatial_size: Optional[Union[Sequence[int], int]] = None, + spatial_size: Sequence[int] | int | None = None, prob: float = 0.1, - rotate_range: Optional[Union[Sequence[Union[Tuple[float, float], float]], float]] = None, - shear_range: Optional[Union[Sequence[Union[Tuple[float, float], float]], float]] = None, - translate_range: Optional[Union[Sequence[Union[Tuple[float, float], float]], float]] = None, - scale_range: Optional[Union[Sequence[Union[Tuple[float, float], float]], float]] = None, + rotate_range: Sequence[tuple[float, float] | float] | float | None = None, + shear_range: Sequence[tuple[float, float] | float] | float | None = None, + translate_range: Sequence[tuple[float, float] | float] | float | None = None, + scale_range: Sequence[tuple[float, float] | float] | float | None = None, mode: SequenceStr = GridSampleMode.BILINEAR, padding_mode: SequenceStr = GridSamplePadMode.REFLECTION, cache_grid: bool = False, - device: Optional[torch.device] = None, + device: torch.device | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -859,18 +862,16 @@ def __init__( self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandAffined": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAffined: self.rand_affine.set_random_state(seed, state) super().set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) first_key: Hashable = self.first_key(d) if first_key == (): - out: Dict[Hashable, NdarrayOrTensor] = convert_to_tensor(d, track_meta=get_track_meta()) + out: dict[Hashable, NdarrayOrTensor] = convert_to_tensor(d, track_meta=get_track_meta()) return out self.randomize(None) @@ -900,7 +901,7 @@ def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, N self.push_transform(d[key], extra_info={"do_resampling": do_resampling, "rand_affine_info": xform}) return d - def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): tr = self.pop_transform(d[key]) @@ -922,17 +923,17 @@ class Rand2DElasticd(RandomizableTransform, MapTransform): def __init__( self, keys: KeysCollection, - spacing: Union[Tuple[float, float], float], - magnitude_range: Tuple[float, float], - spatial_size: Optional[Union[Tuple[int, int], int]] = None, + spacing: tuple[float, float] | float, + magnitude_range: tuple[float, float], + spatial_size: tuple[int, int] | int | None = None, prob: float = 0.1, - rotate_range: Optional[Union[Sequence[Union[Tuple[float, float], float]], float]] = None, - shear_range: Optional[Union[Sequence[Union[Tuple[float, float], float]], float]] = None, - translate_range: Optional[Union[Sequence[Union[Tuple[float, float], float]], float]] = None, - scale_range: Optional[Union[Sequence[Union[Tuple[float, float], float]], float]] = None, + rotate_range: Sequence[tuple[float, float] | float] | float | None = None, + shear_range: Sequence[tuple[float, float] | float] | float | None = None, + translate_range: Sequence[tuple[float, float] | float] | float | None = None, + scale_range: Sequence[tuple[float, float] | float] | float | None = None, mode: SequenceStr = GridSampleMode.BILINEAR, padding_mode: SequenceStr = GridSamplePadMode.REFLECTION, - device: Optional[torch.device] = None, + device: torch.device | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -1008,19 +1009,17 @@ def __init__( self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "Rand2DElasticd": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Rand2DElasticd: self.rand_2d_elastic.set_random_state(seed, state) super().set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) first_key: Hashable = self.first_key(d) if first_key == (): - out: Dict[Hashable, NdarrayOrTensor] = convert_to_tensor(d, track_meta=get_track_meta()) + out: dict[Hashable, NdarrayOrTensor] = convert_to_tensor(d, track_meta=get_track_meta()) return out self.randomize(None) @@ -1062,17 +1061,17 @@ class Rand3DElasticd(RandomizableTransform, MapTransform): def __init__( self, keys: KeysCollection, - sigma_range: Tuple[float, float], - magnitude_range: Tuple[float, float], - spatial_size: Optional[Union[Tuple[int, int, int], int]] = None, + sigma_range: tuple[float, float], + magnitude_range: tuple[float, float], + spatial_size: tuple[int, int, int] | int | None = None, prob: float = 0.1, - rotate_range: Optional[Union[Sequence[Union[Tuple[float, float], float]], float]] = None, - shear_range: Optional[Union[Sequence[Union[Tuple[float, float], float]], float]] = None, - translate_range: Optional[Union[Sequence[Union[Tuple[float, float], float]], float]] = None, - scale_range: Optional[Union[Sequence[Union[Tuple[float, float], float]], float]] = None, + rotate_range: Sequence[tuple[float, float] | float] | float | None = None, + shear_range: Sequence[tuple[float, float] | float] | float | None = None, + translate_range: Sequence[tuple[float, float] | float] | float | None = None, + scale_range: Sequence[tuple[float, float] | float] | float | None = None, mode: SequenceStr = GridSampleMode.BILINEAR, padding_mode: SequenceStr = GridSamplePadMode.REFLECTION, - device: Optional[torch.device] = None, + device: torch.device | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -1150,19 +1149,17 @@ def __init__( self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "Rand3DElasticd": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Rand3DElasticd: self.rand_3d_elastic.set_random_state(seed, state) super().set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) first_key: Hashable = self.first_key(d) if first_key == (): - out: Dict[Hashable, torch.Tensor] = convert_to_tensor(d, track_meta=get_track_meta()) + out: dict[Hashable, torch.Tensor] = convert_to_tensor(d, track_meta=get_track_meta()) return out self.randomize(None) @@ -1204,21 +1201,18 @@ class Flipd(MapTransform, InvertibleTransform): backend = Flip.backend def __init__( - self, - keys: KeysCollection, - spatial_axis: Optional[Union[Sequence[int], int]] = None, - allow_missing_keys: bool = False, + self, keys: KeysCollection, spatial_axis: Sequence[int] | int | None = None, allow_missing_keys: bool = False ) -> None: super().__init__(keys, allow_missing_keys) self.flipper = Flip(spatial_axis=spatial_axis) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.flipper(d[key]) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.flipper.inverse(d[key]) @@ -1245,20 +1239,18 @@ def __init__( self, keys: KeysCollection, prob: float = 0.1, - spatial_axis: Optional[Union[Sequence[int], int]] = None, + spatial_axis: Sequence[int] | int | None = None, allow_missing_keys: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) RandomizableTransform.__init__(self, prob) self.flipper = Flip(spatial_axis=spatial_axis) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandFlipd": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandFlipd: super().set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) self.randomize(None) @@ -1272,7 +1264,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc self.push_transform(d[key], extra_info=xform_info) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): xform = self.pop_transform(d[key]) @@ -1304,14 +1296,12 @@ def __init__(self, keys: KeysCollection, prob: float = 0.1, allow_missing_keys: RandomizableTransform.__init__(self, prob) self.flipper = RandAxisFlip(prob=1.0) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandAxisFlipd": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandAxisFlipd: super().set_random_state(seed, state) self.flipper.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) first_key: Hashable = self.first_key(d) if first_key == (): @@ -1332,7 +1322,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc self.push_transform(d[key], extra_info=xform) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): xform = self.pop_transform(d[key]) @@ -1375,12 +1365,12 @@ class Rotated(MapTransform, InvertibleTransform): def __init__( self, keys: KeysCollection, - angle: Union[Sequence[float], float], + angle: Sequence[float] | float, keep_size: bool = True, mode: SequenceStr = GridSampleMode.BILINEAR, padding_mode: SequenceStr = GridSamplePadMode.BORDER, - align_corners: Union[Sequence[bool], bool] = False, - dtype: Union[Sequence[Union[DtypeLike, torch.dtype]], DtypeLike, torch.dtype] = np.float32, + align_corners: Sequence[bool] | bool = False, + dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) @@ -1391,7 +1381,7 @@ def __init__( self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key, mode, padding_mode, align_corners, dtype in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners, self.dtype @@ -1401,7 +1391,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc ) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.rotator.inverse(d[key]) @@ -1448,15 +1438,15 @@ class RandRotated(RandomizableTransform, MapTransform, InvertibleTransform): def __init__( self, keys: KeysCollection, - range_x: Union[Tuple[float, float], float] = 0.0, - range_y: Union[Tuple[float, float], float] = 0.0, - range_z: Union[Tuple[float, float], float] = 0.0, + range_x: tuple[float, float] | float = 0.0, + range_y: tuple[float, float] | float = 0.0, + range_z: tuple[float, float] | float = 0.0, prob: float = 0.1, keep_size: bool = True, mode: SequenceStr = GridSampleMode.BILINEAR, padding_mode: SequenceStr = GridSamplePadMode.BORDER, - align_corners: Union[Sequence[bool], bool] = False, - dtype: Union[Sequence[Union[DtypeLike, torch.dtype]], DtypeLike, torch.dtype] = np.float32, + align_corners: Sequence[bool] | bool = False, + dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, allow_missing_keys: bool = False, ) -> None: MapTransform.__init__(self, keys, allow_missing_keys) @@ -1467,14 +1457,12 @@ def __init__( self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.dtype = ensure_tuple_rep(dtype, len(self.keys)) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandRotated": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandRotated: super().set_random_state(seed, state) self.rand_rotate.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) self.randomize(None) @@ -1499,7 +1487,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc self.push_transform(d[key], extra_info=rot_info) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): xform = self.pop_transform(d[key]) @@ -1545,10 +1533,10 @@ class Zoomd(MapTransform, InvertibleTransform): def __init__( self, keys: KeysCollection, - zoom: Union[Sequence[float], float], + zoom: Sequence[float] | float, mode: SequenceStr = InterpolateMode.AREA, padding_mode: SequenceStr = NumpyPadMode.EDGE, - align_corners: Union[Sequence[Optional[bool]], Optional[bool]] = None, + align_corners: Sequence[bool | None] | bool | None = None, keep_size: bool = True, allow_missing_keys: bool = False, **kwargs, @@ -1559,7 +1547,7 @@ def __init__( self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) self.zoomer = Zoom(zoom=zoom, keep_size=keep_size, **kwargs) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key, mode, padding_mode, align_corners in self.key_iterator( d, self.mode, self.padding_mode, self.align_corners @@ -1567,7 +1555,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc d[key] = self.zoomer(d[key], mode=mode, padding_mode=padding_mode, align_corners=align_corners) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.zoomer.inverse(d[key]) @@ -1619,11 +1607,11 @@ def __init__( self, keys: KeysCollection, prob: float = 0.1, - min_zoom: Union[Sequence[float], float] = 0.9, - max_zoom: Union[Sequence[float], float] = 1.1, + min_zoom: Sequence[float] | float = 0.9, + max_zoom: Sequence[float] | float = 1.1, mode: SequenceStr = InterpolateMode.AREA, padding_mode: SequenceStr = NumpyPadMode.EDGE, - align_corners: Union[Sequence[Optional[bool]], Optional[bool]] = None, + align_corners: Sequence[bool | None] | bool | None = None, keep_size: bool = True, allow_missing_keys: bool = False, **kwargs, @@ -1635,18 +1623,16 @@ def __init__( self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) self.align_corners = ensure_tuple_rep(align_corners, len(self.keys)) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandZoomd": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandZoomd: super().set_random_state(seed, state) self.rand_zoom.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) first_key: Hashable = self.first_key(d) if first_key == (): - out: Dict[Hashable, torch.Tensor] = convert_to_tensor(d, track_meta=get_track_meta()) + out: dict[Hashable, torch.Tensor] = convert_to_tensor(d, track_meta=get_track_meta()) return out self.randomize(None) @@ -1668,7 +1654,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torc self.push_transform(d[key], extra_info=xform) return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): xform = self.pop_transform(d[key]) @@ -1688,11 +1674,11 @@ class GridDistortiond(MapTransform): def __init__( self, keys: KeysCollection, - num_cells: Union[Tuple[int], int], - distort_steps: List[Tuple], + num_cells: tuple[int] | int, + distort_steps: list[tuple], mode: str = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.BORDER, - device: Optional[torch.device] = None, + device: torch.device | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -1725,7 +1711,7 @@ def __init__( self.mode = ensure_tuple_rep(mode, len(self.keys)) self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key, mode, padding_mode in self.key_iterator(d, self.mode, self.padding_mode): d[key] = self.grid_distortion(d[key], mode=mode, padding_mode=padding_mode) @@ -1742,12 +1728,12 @@ class RandGridDistortiond(RandomizableTransform, MapTransform): def __init__( self, keys: KeysCollection, - num_cells: Union[Tuple[int], int] = 5, + num_cells: tuple[int] | int = 5, prob: float = 0.1, - distort_limit: Union[Tuple[float, float], float] = (-0.03, 0.03), + distort_limit: tuple[float, float] | float = (-0.03, 0.03), mode: str = GridSampleMode.BILINEAR, padding_mode: str = GridSamplePadMode.BORDER, - device: Optional[torch.device] = None, + device: torch.device | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -1785,17 +1771,17 @@ def __init__( self.padding_mode = ensure_tuple_rep(padding_mode, len(self.keys)) def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandGridDistortiond": + self, seed: int | None = None, state: np.random.RandomState | None = None + ) -> RandGridDistortiond: super().set_random_state(seed, state) self.rand_grid_distortion.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) self.randomize(None) if not self._do_transform: - out: Dict[Hashable, torch.Tensor] = convert_to_tensor(d, track_meta=get_track_meta()) + out: dict[Hashable, torch.Tensor] = convert_to_tensor(d, track_meta=get_track_meta()) return out first_key: Hashable = self.first_key(d) @@ -1831,8 +1817,8 @@ class GridSplitd(MapTransform): def __init__( self, keys: KeysCollection, - grid: Tuple[int, int] = (2, 2), - size: Optional[Union[int, Tuple[int, int], Dict[Hashable, Union[int, Tuple[int, int], None]]]] = None, + grid: tuple[int, int] = (2, 2), + size: int | tuple[int, int] | dict[Hashable, int | tuple[int, int] | None] | None = None, allow_missing_keys: bool = False, ): super().__init__(keys, allow_missing_keys) @@ -1840,10 +1826,10 @@ def __init__( self.size = size if isinstance(size, dict) else {key: size for key in self.keys} self.splitter = GridSplit(grid=grid) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> List[Dict[Hashable, NdarrayOrTensor]]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> list[dict[Hashable, NdarrayOrTensor]]: d = dict(data) n_outputs = np.prod(self.grid) - output: List[Dict[Hashable, NdarrayOrTensor]] = [dict(d) for _ in range(n_outputs)] + output: list[dict[Hashable, NdarrayOrTensor]] = [dict(d) for _ in range(n_outputs)] for key in self.key_iterator(d): result = self.splitter(d[key], self.size[key]) for i in range(n_outputs): @@ -1887,11 +1873,11 @@ def __init__( self, keys: KeysCollection, patch_size: Sequence[int], - offset: Optional[Sequence[int]] = None, - num_patches: Optional[int] = None, + offset: Sequence[int] | None = None, + num_patches: int | None = None, overlap: float = 0.0, - sort_fn: Optional[str] = None, - threshold: Optional[float] = None, + sort_fn: str | None = None, + threshold: float | None = None, pad_mode: str = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, **pad_kwargs, @@ -1908,7 +1894,7 @@ def __init__( **pad_kwargs, ) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.patcher(d[key]) @@ -1955,12 +1941,12 @@ def __init__( self, keys: KeysCollection, patch_size: Sequence[int], - min_offset: Optional[Union[Sequence[int], int]] = None, - max_offset: Optional[Union[Sequence[int], int]] = None, - num_patches: Optional[int] = None, + min_offset: Sequence[int] | int | None = None, + max_offset: Sequence[int] | int | None = None, + num_patches: int | None = None, overlap: float = 0.0, - sort_fn: Optional[str] = None, - threshold: Optional[float] = None, + sort_fn: str | None = None, + threshold: float | None = None, pad_mode: str = PytorchPadMode.CONSTANT, allow_missing_keys: bool = False, **pad_kwargs, @@ -1978,14 +1964,12 @@ def __init__( **pad_kwargs, ) - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "RandGridPatchd": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> RandGridPatchd: super().set_random_state(seed, state) self.patcher.set_random_state(seed, state) return self - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) # All the keys share the same random noise for key in self.key_iterator(d): diff --git a/monai/transforms/transform.py b/monai/transforms/transform.py index c5ab29133b..470f72566c 100644 --- a/monai/transforms/transform.py +++ b/monai/transforms/transform.py @@ -12,9 +12,12 @@ A collection of generic interfaces for MONAI transforms. """ +from __future__ import annotations + import logging from abc import ABC, abstractmethod -from typing import Any, Callable, Dict, Generator, Hashable, Iterable, List, Mapping, Optional, Tuple, TypeVar, Union +from collections.abc import Callable, Generator, Hashable, Iterable, Mapping +from typing import Any, TypeVar import numpy as np import torch @@ -72,7 +75,7 @@ def apply_transform( map_items: bool = True, unpack_items: bool = False, log_stats: bool = False, -) -> Union[List[ReturnType], ReturnType]: +) -> list[ReturnType] | ReturnType: """ Transform `data` with `transform`. @@ -113,7 +116,7 @@ def apply_transform( if isinstance(data, (list, tuple)): data = data[0] - def _log_stats(data, prefix: Optional[str] = "Data"): + def _log_stats(data, prefix: str | None = "Data"): if isinstance(data, (np.ndarray, torch.Tensor)): # log data type, shape, range for array datastats(img=data, data_shape=True, value_range=True, prefix=prefix) @@ -206,9 +209,7 @@ class Randomizable(ThreadUnsafe, RandomizableTrait): R: np.random.RandomState = np.random.RandomState() - def set_random_state( - self, seed: Optional[int] = None, state: Optional[np.random.RandomState] = None - ) -> "Randomizable": + def set_random_state(self, seed: int | None = None, state: np.random.RandomState | None = None) -> Randomizable: """ Set the random state locally, to control the randomness, the derived classes should use :py:attr:`self.R` instead of `np.random` to introduce random @@ -283,7 +284,7 @@ class Transform(ABC): # to other data types during the transformation. Note that not all `dtype` (such as float32, uint8) are supported # by all the data types, the `dtype` during the conversion is determined automatically by each transform, # please refer to the transform's docstring. - backend: List[TransformBackends] = [] + backend: list[TransformBackends] = [] @abstractmethod def __call__(self, data: Any): @@ -318,7 +319,7 @@ class LazyTransform(Transform, LazyTrait): dictionary transforms to simplify implementation of new lazy transforms. """ - def __init__(self, lazy_evaluation: Optional[bool] = True): + def __init__(self, lazy_evaluation: bool | None = True): self.lazy_evaluation = lazy_evaluation @property @@ -412,7 +413,7 @@ def __new__(cls, *args, **kwargs): return Transform.__new__(cls) def __init__(self, keys: KeysCollection, allow_missing_keys: bool = False) -> None: - self.keys: Tuple[Hashable, ...] = ensure_tuple(keys) + self.keys: tuple[Hashable, ...] = ensure_tuple(keys) self.allow_missing_keys = allow_missing_keys if not self.keys: raise ValueError("keys must be non empty.") @@ -469,7 +470,7 @@ def __call__(self, data): """ raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") - def key_iterator(self, data: Mapping[Hashable, Any], *extra_iterables: Optional[Iterable]) -> Generator: + def key_iterator(self, data: Mapping[Hashable, Any], *extra_iterables: Iterable | None) -> Generator: """ Iterate across keys and optionally extra iterables. If key is missing, exception is raised if `allow_missing_keys==False` (default). If `allow_missing_keys==True`, key is skipped. @@ -482,7 +483,7 @@ def key_iterator(self, data: Mapping[Hashable, Any], *extra_iterables: Optional[ ex_iters = extra_iterables or [[None] * len(self.keys)] # loop over keys and any extra iterables - _ex_iters: List[Any] + _ex_iters: list[Any] for key, *_ex_iters in zip(self.keys, *ex_iters): # all normal, yield (what we yield depends on whether extra iterables were given) if key in data: @@ -493,7 +494,7 @@ def key_iterator(self, data: Mapping[Hashable, Any], *extra_iterables: Optional[ " and allow_missing_keys==False." ) - def first_key(self, data: Dict[Hashable, Any]): + def first_key(self, data: dict[Hashable, Any]): """ Get the first available key of `self.keys` in the input `data` dictionary. If no available key, return an empty tuple `()`. diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index 5edb9536a4..253b1bf7f2 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -13,13 +13,16 @@ https://github.com/Project-MONAI/MONAI/wiki/MONAI_Design """ +from __future__ import annotations + import logging import sys import time import warnings +from collections.abc import Mapping, Sequence from copy import deepcopy from functools import partial -from typing import Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Union +from typing import Callable import numpy as np import torch @@ -230,11 +233,11 @@ class EnsureChannelFirst(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, strict_check: bool = True, channel_dim: Union[None, str, int] = None): + def __init__(self, strict_check: bool = True, channel_dim: None | str | int = None): self.strict_check = strict_check self.input_channel_dim = channel_dim - def __call__(self, img: torch.Tensor, meta_dict: Optional[Mapping] = None) -> torch.Tensor: + def __call__(self, img: torch.Tensor, meta_dict: Mapping | None = None) -> torch.Tensor: """ Apply the transform to `img`. """ @@ -350,7 +353,7 @@ def __init__(self, dim: int = -1, keepdim: bool = True, update_meta=True) -> Non self.keepdim = keepdim self.update_meta = update_meta - def __call__(self, img: torch.Tensor) -> List[torch.Tensor]: + def __call__(self, img: torch.Tensor) -> list[torch.Tensor]: """ Apply the transform to `img`. """ @@ -408,7 +411,7 @@ def __init__(self, dtype=np.float32) -> None: """ self.dtype = dtype - def __call__(self, img: NdarrayOrTensor, dtype: Union[DtypeLike, torch.dtype] = None) -> NdarrayOrTensor: + def __call__(self, img: NdarrayOrTensor, dtype: DtypeLike | torch.dtype = None) -> NdarrayOrTensor: """ Apply the transform to `img`, assuming `img` is a numpy array or PyTorch Tensor. @@ -443,10 +446,10 @@ class ToTensor(Transform): def __init__( self, - dtype: Optional[torch.dtype] = None, - device: Optional[torch.device] = None, + dtype: torch.dtype | None = None, + device: torch.device | None = None, wrap_sequence: bool = True, - track_meta: Optional[bool] = None, + track_meta: bool | None = None, ) -> None: super().__init__() self.dtype = dtype @@ -488,10 +491,10 @@ class EnsureType(Transform): def __init__( self, data_type: str = "tensor", - dtype: Optional[Union[DtypeLike, torch.dtype]] = None, - device: Optional[torch.device] = None, + dtype: DtypeLike | torch.dtype | None = None, + device: torch.device | None = None, wrap_sequence: bool = True, - track_meta: Optional[bool] = None, + track_meta: bool | None = None, ) -> None: self.data_type = look_up_option(data_type.lower(), {"tensor", "numpy"}) self.dtype = dtype @@ -563,7 +566,7 @@ class ToCupy(Transform): backend = [TransformBackends.CUPY] - def __init__(self, dtype: Optional[np.dtype] = None, wrap_sequence: bool = True) -> None: + def __init__(self, dtype: np.dtype | None = None, wrap_sequence: bool = True) -> None: super().__init__() self.dtype = dtype self.wrap_sequence = wrap_sequence @@ -600,7 +603,7 @@ class Transpose(Transform): backend = [TransformBackends.TORCH] - def __init__(self, indices: Optional[Sequence[int]]) -> None: + def __init__(self, indices: Sequence[int] | None) -> None: self.indices = None if indices is None else tuple(indices) def __call__(self, img: NdarrayOrTensor) -> NdarrayOrTensor: @@ -618,7 +621,7 @@ class SqueezeDim(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, dim: Optional[int] = 0, update_meta=True) -> None: + def __init__(self, dim: int | None = 0, update_meta=True) -> None: """ Args: dim: dimension to be squeezed. Default = 0 @@ -684,7 +687,7 @@ def __init__( data_shape: bool = True, value_range: bool = True, data_value: bool = False, - additional_info: Optional[Callable] = None, + additional_info: Callable | None = None, name: str = "DataStats", ) -> None: """ @@ -730,12 +733,12 @@ def __init__( def __call__( self, img: NdarrayOrTensor, - prefix: Optional[str] = None, - data_type: Optional[bool] = None, - data_shape: Optional[bool] = None, - value_range: Optional[bool] = None, - data_value: Optional[bool] = None, - additional_info: Optional[Callable] = None, + prefix: str | None = None, + data_type: bool | None = None, + data_shape: bool | None = None, + value_range: bool | None = None, + data_value: bool | None = None, + additional_info: Callable | None = None, ) -> NdarrayOrTensor: """ Apply the transform to `img`, optionally take arguments similar to the class constructor. @@ -787,7 +790,7 @@ def __init__(self, delay_time: float = 0.0) -> None: super().__init__() self.delay_time: float = delay_time - def __call__(self, img: NdarrayOrTensor, delay_time: Optional[float] = None) -> NdarrayOrTensor: + def __call__(self, img: NdarrayOrTensor, delay_time: float | None = None) -> NdarrayOrTensor: """ Args: img: data remain unchanged throughout this transform. @@ -823,13 +826,13 @@ class Lambda(InvertibleTransform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, func: Optional[Callable] = None, inv_func: Callable = no_collation) -> None: + def __init__(self, func: Callable | None = None, inv_func: Callable = no_collation) -> None: if func is not None and not callable(func): raise TypeError(f"func must be None or callable but is {type(func).__name__}.") self.func = func self.inv_func = inv_func - def __call__(self, img: NdarrayOrTensor, func: Optional[Callable] = None): + def __call__(self, img: NdarrayOrTensor, func: Callable | None = None): """ Apply `self.func` to `img`. @@ -872,11 +875,11 @@ class RandLambda(Lambda, RandomizableTransform): backend = Lambda.backend - def __init__(self, func: Optional[Callable] = None, prob: float = 1.0, inv_func: Callable = no_collation) -> None: + def __init__(self, func: Callable | None = None, prob: float = 1.0, inv_func: Callable = no_collation) -> None: Lambda.__init__(self=self, func=func, inv_func=inv_func) RandomizableTransform.__init__(self=self, prob=prob) - def __call__(self, img: NdarrayOrTensor, func: Optional[Callable] = None): + def __call__(self, img: NdarrayOrTensor, func: Callable | None = None): self.randomize(img) out = deepcopy(super().__call__(img, func) if self._do_transform else img) # convert to MetaTensor if necessary @@ -918,16 +921,13 @@ class LabelToMask(Transform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] def __init__( # pytype: disable=annotation-type-mismatch - self, select_labels: Union[Sequence[int], int], merge_channels: bool = False + self, select_labels: Sequence[int] | int, merge_channels: bool = False ) -> None: # pytype: disable=annotation-type-mismatch self.select_labels = ensure_tuple(select_labels) self.merge_channels = merge_channels def __call__( - self, - img: NdarrayOrTensor, - select_labels: Optional[Union[Sequence[int], int]] = None, - merge_channels: bool = False, + self, img: NdarrayOrTensor, select_labels: Sequence[int] | int | None = None, merge_channels: bool = False ) -> NdarrayOrTensor: """ Args: @@ -981,16 +981,13 @@ class FgBgToIndices(Transform): backend = [TransformBackends.NUMPY, TransformBackends.TORCH] - def __init__(self, image_threshold: float = 0.0, output_shape: Optional[Sequence[int]] = None) -> None: + def __init__(self, image_threshold: float = 0.0, output_shape: Sequence[int] | None = None) -> None: self.image_threshold = image_threshold self.output_shape = output_shape def __call__( - self, - label: NdarrayOrTensor, - image: Optional[NdarrayOrTensor] = None, - output_shape: Optional[Sequence[int]] = None, - ) -> Tuple[NdarrayOrTensor, NdarrayOrTensor]: + self, label: NdarrayOrTensor, image: NdarrayOrTensor | None = None, output_shape: Sequence[int] | None = None + ) -> tuple[NdarrayOrTensor, NdarrayOrTensor]: """ Args: label: input data to compute foreground and background indices. @@ -1013,10 +1010,7 @@ class ClassesToIndices(Transform): backend = [TransformBackends.NUMPY, TransformBackends.TORCH] def __init__( - self, - num_classes: Optional[int] = None, - image_threshold: float = 0.0, - output_shape: Optional[Sequence[int]] = None, + self, num_classes: int | None = None, image_threshold: float = 0.0, output_shape: Sequence[int] | None = None ) -> None: """ Compute indices of every class of the input label data, return a list of indices. @@ -1037,11 +1031,8 @@ def __init__( self.output_shape = output_shape def __call__( - self, - label: NdarrayOrTensor, - image: Optional[NdarrayOrTensor] = None, - output_shape: Optional[Sequence[int]] = None, - ) -> List[NdarrayOrTensor]: + self, label: NdarrayOrTensor, image: NdarrayOrTensor | None = None, output_shape: Sequence[int] | None = None + ) -> list[NdarrayOrTensor]: """ Args: label: input data to compute the indices of every class. @@ -1053,7 +1044,7 @@ def __call__( if output_shape is None: output_shape = self.output_shape - indices: List[NdarrayOrTensor] + indices: list[NdarrayOrTensor] indices = map_classes_to_indices(label, self.num_classes, image, self.image_threshold) if output_shape is not None: indices = [unravel_indices(cls_indices, output_shape) for cls_indices in indices] @@ -1109,7 +1100,7 @@ class AddExtremePointsChannel(Randomizable, Transform): def __init__(self, background: int = 0, pert: float = 0.0) -> None: self._background = background self._pert = pert - self._points: List[Tuple[int, ...]] = [] + self._points: list[tuple[int, ...]] = [] def randomize(self, label: NdarrayOrTensor) -> None: self._points = get_extreme_points(label, rand_state=self.R, background=self._background, pert=self._pert) @@ -1117,8 +1108,8 @@ def randomize(self, label: NdarrayOrTensor) -> None: def __call__( self, img: NdarrayOrTensor, - label: Optional[NdarrayOrTensor] = None, - sigma: Union[Sequence[float], float, Sequence[torch.Tensor], torch.Tensor] = 3.0, + label: NdarrayOrTensor | None = None, + sigma: Sequence[float] | float | Sequence[torch.Tensor] | torch.Tensor = 3.0, rescale_min: float = -1.0, rescale_max: float = 1.0, ) -> NdarrayOrTensor: @@ -1252,14 +1243,14 @@ class IntensityStats(Transform): backend = [TransformBackends.NUMPY] - def __init__(self, ops: Sequence[Union[str, Callable]], key_prefix: str, channel_wise: bool = False) -> None: + def __init__(self, ops: Sequence[str | Callable], key_prefix: str, channel_wise: bool = False) -> None: self.ops = ensure_tuple(ops) self.key_prefix = key_prefix self.channel_wise = channel_wise def __call__( - self, img: NdarrayOrTensor, meta_data: Optional[Dict] = None, mask: Optional[np.ndarray] = None - ) -> Tuple[NdarrayOrTensor, Dict]: + self, img: NdarrayOrTensor, meta_data: dict | None = None, mask: np.ndarray | None = None + ) -> tuple[NdarrayOrTensor, dict]: """ Compute statistics for the intensity of input image. @@ -1323,7 +1314,7 @@ class ToDevice(Transform): backend = [TransformBackends.TORCH] - def __init__(self, device: Union[torch.device, str], **kwargs) -> None: + def __init__(self, device: torch.device | str, **kwargs) -> None: """ Args: device: target device to move the Tensor, for example: "cuda:1". @@ -1573,9 +1564,7 @@ class ImageFilter(Transform): ["mean", "laplace", "elliptical", "sobel", "sharpen", "median", "gauss", "savitzky_golay"] ) - def __init__( - self, filter: Union[str, NdarrayOrTensor, nn.Module], filter_size: Optional[int] = None, **kwargs - ) -> None: + def __init__(self, filter: str | NdarrayOrTensor | nn.Module, filter_size: int | None = None, **kwargs) -> None: self._check_filter_format(filter, filter_size) self._check_kwargs_are_present(filter, **kwargs) @@ -1583,7 +1572,7 @@ def __init__( self.filter_size = filter_size self.additional_args_for_filter = kwargs - def __call__(self, img: NdarrayOrTensor, meta_dict: Optional[Dict] = None) -> NdarrayOrTensor: + def __call__(self, img: NdarrayOrTensor, meta_dict: dict | None = None) -> NdarrayOrTensor: """ Args: img: torch tensor data to apply filter to with shape: [channels, height, width[, depth]] @@ -1614,9 +1603,7 @@ def _check_all_values_uneven(self, x: tuple) -> None: if value % 2 == 0: raise ValueError(f"Only uneven filters are supported, but filter size is {x}") - def _check_filter_format( - self, filter: Union[str, NdarrayOrTensor, nn.Module], filter_size: Optional[int] = None - ) -> None: + def _check_filter_format(self, filter: str | NdarrayOrTensor | nn.Module, filter_size: int | None = None) -> None: if isinstance(filter, str): if not filter_size: raise ValueError("`filter_size` must be specified when specifying filters by string.") @@ -1643,7 +1630,7 @@ def _check_kwargs_are_present(self, filter, **kwargs): if filter == "savitzky_golay" and "order" not in kwargs.keys(): raise KeyError("`filter='savitzky_golay', requires the additonal keyword argument `order`") - def _get_filter_from_string(self, filter: str, size: int, ndim: int) -> Union[nn.Module, Callable]: + def _get_filter_from_string(self, filter: str, size: int, ndim: int) -> nn.Module | Callable: if filter == "mean": return MeanFilter(ndim, size) elif filter == "laplace": @@ -1700,12 +1687,12 @@ class RandImageFilter(RandomizableTransform): backend = ImageFilter.backend def __init__( - self, filter: Union[str, NdarrayOrTensor], filter_size: Optional[int] = None, prob: float = 0.1, **kwargs + self, filter: str | NdarrayOrTensor, filter_size: int | None = None, prob: float = 0.1, **kwargs ) -> None: super().__init__(prob) self.filter = ImageFilter(filter, filter_size, **kwargs) - def __call__(self, img: NdarrayOrTensor, meta_dict: Optional[Mapping] = None) -> NdarrayOrTensor: + def __call__(self, img: NdarrayOrTensor, meta_dict: Mapping | None = None) -> NdarrayOrTensor: """ Args: img: torch tensor data to apply filter to with shape: [channels, height, width[, depth]] diff --git a/monai/transforms/utility/dictionary.py b/monai/transforms/utility/dictionary.py index c16a456f74..0ec652bb03 100644 --- a/monai/transforms/utility/dictionary.py +++ b/monai/transforms/utility/dictionary.py @@ -15,9 +15,12 @@ Class names are ended with 'd' to denote dictionary-based transforms. """ +from __future__ import annotations + import re +from collections.abc import Callable, Hashable, Mapping from copy import deepcopy -from typing import Any, Callable, Dict, Hashable, List, Mapping, Optional, Sequence, Tuple, Union, cast +from typing import Any, Sequence, cast import numpy as np import torch @@ -210,7 +213,7 @@ def __init__(self, keys: KeysCollection, allow_missing_keys: bool = False) -> No super().__init__(keys, allow_missing_keys) self.identity = Identity() - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.identity(d[key]) @@ -235,7 +238,7 @@ def __init__(self, keys: KeysCollection, channel_dim: int = -1, allow_missing_ke super().__init__(keys, allow_missing_keys) self.converter = AsChannelFirst(channel_dim=channel_dim) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -260,7 +263,7 @@ def __init__(self, keys: KeysCollection, channel_dim: int = 0, allow_missing_key super().__init__(keys, allow_missing_keys) self.converter = AsChannelLast(channel_dim=channel_dim) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -284,7 +287,7 @@ def __init__(self, keys: KeysCollection, allow_missing_keys: bool = False) -> No super().__init__(keys, allow_missing_keys) self.adder = AddChannel() - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.adder(d[key]) @@ -303,7 +306,7 @@ class EnsureChannelFirstd(MapTransform): def __init__( self, keys: KeysCollection, - meta_keys: Optional[KeysCollection] = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, strict_check: bool = True, allow_missing_keys: bool = False, @@ -325,7 +328,7 @@ def __init__( self.meta_keys = ensure_tuple_rep(meta_keys, len(self.keys)) self.meta_key_postfix = ensure_tuple_rep(meta_key_postfix, len(self.keys)) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key, meta_key, meta_key_postfix in self.key_iterator(d, self.meta_keys, self.meta_key_postfix): d[key] = self.adjuster(d[key], d.get(meta_key or f"{key}_{meta_key_postfix}")) # type: ignore @@ -350,7 +353,7 @@ def __init__(self, keys: KeysCollection, repeats: int, allow_missing_keys: bool super().__init__(keys, allow_missing_keys) self.repeater = RepeatChannel(repeats) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.repeater(d[key]) @@ -375,7 +378,7 @@ def __init__(self, keys: KeysCollection, repeats: int, allow_missing_keys: bool super().__init__(keys, allow_missing_keys) self.repeater = RemoveRepeatedChannel(repeats) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.repeater(d[key]) @@ -389,7 +392,7 @@ class SplitDimd(MapTransform): def __init__( self, keys: KeysCollection, - output_postfixes: Optional[Sequence[str]] = None, + output_postfixes: Sequence[str] | None = None, dim: int = 0, keepdim: bool = True, update_meta: bool = True, @@ -421,7 +424,7 @@ def __init__( def __call__( self, data: Mapping[Hashable, torch.Tensor] - ) -> Union[Dict[Hashable, torch.Tensor], List[Dict[Hashable, torch.Tensor]]]: + ) -> dict[Hashable, torch.Tensor] | list[dict[Hashable, torch.Tensor]]: d = dict(data) all_keys = list(set(self.key_iterator(d))) @@ -459,7 +462,7 @@ class SplitChanneld(SplitDimd): def __init__( self, keys: KeysCollection, - output_postfixes: Optional[Sequence[str]] = None, + output_postfixes: Sequence[str] | None = None, channel_dim: int = 0, allow_missing_keys: bool = False, ) -> None: @@ -482,7 +485,7 @@ class CastToTyped(MapTransform): def __init__( self, keys: KeysCollection, - dtype: Union[Sequence[Union[DtypeLike, torch.dtype]], DtypeLike, torch.dtype] = np.float32, + dtype: Sequence[DtypeLike | torch.dtype] | DtypeLike | torch.dtype = np.float32, allow_missing_keys: bool = False, ) -> None: """ @@ -499,7 +502,7 @@ def __init__( self.dtype = ensure_tuple_rep(dtype, len(self.keys)) self.converter = CastToType() - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key, dtype in self.key_iterator(d, self.dtype): d[key] = self.converter(d[key], dtype=dtype) @@ -517,10 +520,10 @@ class ToTensord(MapTransform, InvertibleTransform): def __init__( self, keys: KeysCollection, - dtype: Optional[torch.dtype] = None, - device: Optional[torch.device] = None, + dtype: torch.dtype | None = None, + device: torch.device | None = None, wrap_sequence: bool = True, - track_meta: Optional[bool] = None, + track_meta: bool | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -539,14 +542,14 @@ def __init__( super().__init__(keys, allow_missing_keys) self.converter = ToTensor(dtype=dtype, device=device, wrap_sequence=wrap_sequence, track_meta=track_meta) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) self.push_transform(d, key) return d - def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def inverse(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): # Remove the applied transform @@ -577,10 +580,10 @@ def __init__( self, keys: KeysCollection, data_type: str = "tensor", - dtype: Union[DtypeLike, torch.dtype] = None, - device: Optional[torch.device] = None, + dtype: DtypeLike | torch.dtype = None, + device: torch.device | None = None, wrap_sequence: bool = True, - track_meta: Optional[bool] = None, + track_meta: bool | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -601,7 +604,7 @@ def __init__( data_type=data_type, dtype=dtype, device=device, wrap_sequence=wrap_sequence, track_meta=track_meta ) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -634,7 +637,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.converter = ToNumpy(dtype=dtype, wrap_sequence=wrap_sequence) - def __call__(self, data: Mapping[Hashable, Any]) -> Dict[Hashable, Any]: + def __call__(self, data: Mapping[Hashable, Any]) -> dict[Hashable, Any]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -661,14 +664,14 @@ class ToCupyd(MapTransform): def __init__( self, keys: KeysCollection, - dtype: Optional[np.dtype] = None, + dtype: np.dtype | None = None, wrap_sequence: bool = True, allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) self.converter = ToCupy(dtype=dtype, wrap_sequence=wrap_sequence) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -692,7 +695,7 @@ def __init__(self, keys: KeysCollection, allow_missing_keys: bool = False) -> No super().__init__(keys, allow_missing_keys) self.converter = ToPIL() - def __call__(self, data: Mapping[Hashable, Any]) -> Dict[Hashable, Any]: + def __call__(self, data: Mapping[Hashable, Any]) -> dict[Hashable, Any]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -706,13 +709,11 @@ class Transposed(MapTransform, InvertibleTransform): backend = Transpose.backend - def __init__( - self, keys: KeysCollection, indices: Optional[Sequence[int]], allow_missing_keys: bool = False - ) -> None: + def __init__(self, keys: KeysCollection, indices: Sequence[int] | None, allow_missing_keys: bool = False) -> None: super().__init__(keys, allow_missing_keys) self.transform = Transpose(indices) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.transform(d[key]) @@ -721,7 +722,7 @@ def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, N self.push_transform(d, key, extra_info={"indices": indices}) return d - def inverse(self, data: Mapping[Hashable, Any]) -> Dict[Hashable, Any]: + def inverse(self, data: Mapping[Hashable, Any]) -> dict[Hashable, Any]: d = dict(data) for key in self.key_iterator(d): transform = self.get_most_recent_transform(d, key) @@ -744,7 +745,7 @@ class DeleteItemsd(MapTransform): backend = [TransformBackends.TORCH, TransformBackends.NUMPY] - def __init__(self, keys: KeysCollection, sep: str = ".", use_re: Union[Sequence[bool], bool] = False) -> None: + def __init__(self, keys: KeysCollection, sep: str = ".", use_re: Sequence[bool] | bool = False) -> None: """ Args: keys: keys of the corresponding items to delete, can be "A{sep}B{sep}C" @@ -803,9 +804,9 @@ class FlattenSubKeysd(MapTransform): def __init__( self, keys: KeysCollection, - sub_keys: Optional[KeysCollection] = None, + sub_keys: KeysCollection | None = None, delete_keys: bool = True, - prefix: Optional[str] = None, + prefix: str | None = None, ) -> None: super().__init__(keys) self.sub_keys = sub_keys @@ -855,7 +856,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.converter = SqueezeDim(dim=dim, update_meta=update_meta) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -872,12 +873,12 @@ class DataStatsd(MapTransform): def __init__( self, keys: KeysCollection, - prefix: Union[Sequence[str], str] = "Data", - data_type: Union[Sequence[bool], bool] = True, - data_shape: Union[Sequence[bool], bool] = True, - value_range: Union[Sequence[bool], bool] = True, - data_value: Union[Sequence[bool], bool] = False, - additional_info: Optional[Union[Sequence[Callable], Callable]] = None, + prefix: Sequence[str] | str = "Data", + data_type: Sequence[bool] | bool = True, + data_shape: Sequence[bool] | bool = True, + value_range: Sequence[bool] | bool = True, + data_value: Sequence[bool] | bool = False, + additional_info: Sequence[Callable] | Callable | None = None, name: str = "DataStats", allow_missing_keys: bool = False, ) -> None: @@ -912,7 +913,7 @@ def __init__( self.additional_info = ensure_tuple_rep(additional_info, len(self.keys)) self.printer = DataStats(name=name) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key, prefix, data_type, data_shape, value_range, data_value, additional_info in self.key_iterator( d, self.prefix, self.data_type, self.data_shape, self.value_range, self.data_value, self.additional_info @@ -929,7 +930,7 @@ class SimulateDelayd(MapTransform): backend = SimulateDelay.backend def __init__( - self, keys: KeysCollection, delay_time: Union[Sequence[float], float] = 0.0, allow_missing_keys: bool = False + self, keys: KeysCollection, delay_time: Sequence[float] | float = 0.0, allow_missing_keys: bool = False ) -> None: """ Args: @@ -944,7 +945,7 @@ def __init__( self.delay_time = ensure_tuple_rep(delay_time, len(self.keys)) self.delayer = SimulateDelay() - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key, delay_time in self.key_iterator(d, self.delay_time): d[key] = self.delayer(d[key], delay_time=delay_time) @@ -963,7 +964,7 @@ def __init__( self, keys: KeysCollection, times: int = 1, - names: Optional[KeysCollection] = None, + names: KeysCollection | None = None, allow_missing_keys: bool = False, ) -> None: """ @@ -995,7 +996,7 @@ def __init__( ) self.names = names - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: """ Raises: KeyError: When a key in ``self.names`` already exists in ``data``. @@ -1034,7 +1035,7 @@ def __init__(self, keys: KeysCollection, name: str, dim: int = 0, allow_missing_ self.name = name self.dim = dim - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: """ Raises: TypeError: When items in ``data`` differ in type. @@ -1103,9 +1104,9 @@ class Lambdad(MapTransform, InvertibleTransform): def __init__( self, keys: KeysCollection, - func: Union[Sequence[Callable], Callable], - inv_func: Union[Sequence[Callable], Callable] = no_collation, - overwrite: Union[Sequence[bool], bool, Sequence[str], str] = True, + func: Sequence[Callable] | Callable, + inv_func: Sequence[Callable] | Callable = no_collation, + overwrite: Sequence[bool] | bool | Sequence[str] | str = True, allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) @@ -1114,7 +1115,7 @@ def __init__( self.overwrite = ensure_tuple_rep(overwrite, len(self.keys)) self._lambd = Lambda() - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key, func, overwrite in self.key_iterator(d, self.func, self.overwrite): ret = self._lambd(img=d[key], func=func) @@ -1162,9 +1163,9 @@ class RandLambdad(Lambdad, RandomizableTransform): def __init__( self, keys: KeysCollection, - func: Union[Sequence[Callable], Callable], - inv_func: Union[Sequence[Callable], Callable] = no_collation, - overwrite: Union[Sequence[bool], bool] = True, + func: Sequence[Callable] | Callable, + inv_func: Sequence[Callable] | Callable = no_collation, + overwrite: Sequence[bool] | bool = True, prob: float = 1.0, allow_missing_keys: bool = False, ) -> None: @@ -1194,7 +1195,7 @@ def __call__(self, data): d[key] = ret return d - def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def inverse(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key, overwrite in self.key_iterator(d, self.overwrite): if isinstance(d[key], MetaTensor): @@ -1228,14 +1229,14 @@ class LabelToMaskd(MapTransform): def __init__( # pytype: disable=annotation-type-mismatch self, keys: KeysCollection, - select_labels: Union[Sequence[int], int], + select_labels: Sequence[int] | int, merge_channels: bool = False, allow_missing_keys: bool = False, ) -> None: # pytype: disable=annotation-type-mismatch super().__init__(keys, allow_missing_keys) self.converter = LabelToMask(select_labels=select_labels, merge_channels=merge_channels) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -1270,9 +1271,9 @@ def __init__( keys: KeysCollection, fg_postfix: str = "_fg_indices", bg_postfix: str = "_bg_indices", - image_key: Optional[str] = None, + image_key: str | None = None, image_threshold: float = 0.0, - output_shape: Optional[Sequence[int]] = None, + output_shape: Sequence[int] | None = None, allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) @@ -1281,7 +1282,7 @@ def __init__( self.image_key = image_key self.converter = FgBgToIndices(image_threshold, output_shape) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) image = d[self.image_key] if self.image_key else None for key in self.key_iterator(d): @@ -1315,10 +1316,10 @@ def __init__( self, keys: KeysCollection, indices_postfix: str = "_cls_indices", - num_classes: Optional[int] = None, - image_key: Optional[str] = None, + num_classes: int | None = None, + image_key: str | None = None, image_threshold: float = 0.0, - output_shape: Optional[Sequence[int]] = None, + output_shape: Sequence[int] | None = None, allow_missing_keys: bool = False, ) -> None: super().__init__(keys, allow_missing_keys) @@ -1352,7 +1353,7 @@ def __init__(self, keys: KeysCollection, allow_missing_keys: bool = False): super().__init__(keys, allow_missing_keys) self.converter = ConvertToMultiChannelBasedOnBratsClasses() - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -1386,7 +1387,7 @@ def __init__( label_key: str, background: int = 0, pert: float = 0.0, - sigma: Union[Sequence[float], float, Sequence[torch.Tensor], torch.Tensor] = 3.0, + sigma: Sequence[float] | float | Sequence[torch.Tensor] | torch.Tensor = 3.0, rescale_min: float = -1.0, rescale_max: float = 1.0, allow_missing_keys: bool = False, @@ -1394,7 +1395,7 @@ def __init__( MapTransform.__init__(self, keys, allow_missing_keys) self.background = background self.pert = pert - self.points: List[Tuple[int, ...]] = [] + self.points: list[tuple[int, ...]] = [] self.label_key = label_key self.sigma = sigma self.rescale_min = rescale_min @@ -1403,7 +1404,7 @@ def __init__( def randomize(self, label: NdarrayOrTensor) -> None: self.points = get_extreme_points(label, rand_state=self.R, background=self.background, pert=self.pert) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) label = d[self.label_key] if label.shape[0] != 1: @@ -1453,7 +1454,7 @@ def __init__(self, keys: KeysCollection, name: str, allow_missing_keys: bool = F self.name = name self.trans = TorchVision(name, *args, **kwargs) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.trans(d[key]) @@ -1490,7 +1491,7 @@ def __init__(self, keys: KeysCollection, name: str, allow_missing_keys: bool = F self.name = name self.trans = TorchVision(name, *args, **kwargs) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.trans(d[key]) @@ -1525,7 +1526,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.mapper = MapLabelValue(orig_labels=orig_labels, target_labels=target_labels, dtype=dtype) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.mapper(d[key]) @@ -1572,11 +1573,11 @@ class IntensityStatsd(MapTransform): def __init__( self, keys: KeysCollection, - ops: Sequence[Union[str, Callable]], + ops: Sequence[str | Callable], key_prefix: str, - mask_keys: Optional[KeysCollection] = None, + mask_keys: KeysCollection | None = None, channel_wise: bool = False, - meta_keys: Optional[KeysCollection] = None, + meta_keys: KeysCollection | None = None, meta_key_postfix: str = DEFAULT_POST_FIX, allow_missing_keys: bool = False, ) -> None: @@ -1588,7 +1589,7 @@ def __init__( raise ValueError("meta_keys should have the same length as keys.") self.meta_key_postfix = ensure_tuple_rep(meta_key_postfix, len(self.keys)) - def __call__(self, data) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key, mask_key, meta_key, meta_key_postfix in self.key_iterator( d, self.mask_keys, self.meta_keys, self.meta_key_postfix @@ -1608,7 +1609,7 @@ class ToDeviced(MapTransform): backend = ToDevice.backend def __init__( - self, keys: KeysCollection, device: Union[torch.device, str], allow_missing_keys: bool = False, **kwargs + self, keys: KeysCollection, device: torch.device | str, allow_missing_keys: bool = False, **kwargs ) -> None: """ Args: @@ -1622,7 +1623,7 @@ def __init__( super().__init__(keys, allow_missing_keys) self.converter = ToDevice(device=device, **kwargs) - def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> Dict[Hashable, torch.Tensor]: + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.converter(d[key]) @@ -1734,7 +1735,7 @@ def __init__(self, keys: KeysCollection, spatial_dims: Sequence[int], allow_miss super().__init__(keys, allow_missing_keys) self.add_coordinate_channels = AddCoordinateChannels(spatial_dims=spatial_dims) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.add_coordinate_channels(d[key]) @@ -1764,15 +1765,15 @@ class ImageFilterd(MapTransform): def __init__( self, keys: KeysCollection, - kernel: Union[str, NdarrayOrTensor], - kernel_size: Optional[int] = None, + kernel: str | NdarrayOrTensor, + kernel_size: int | None = None, allow_missing_keys: bool = False, **kwargs, ) -> None: super().__init__(keys, allow_missing_keys) self.filter = ImageFilter(kernel, kernel_size, **kwargs) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) for key in self.key_iterator(d): d[key] = self.filter(d[key]) @@ -1804,8 +1805,8 @@ class RandImageFilterd(MapTransform, RandomizableTransform): def __init__( self, keys: KeysCollection, - kernel: Union[str, NdarrayOrTensor], - kernel_size: Optional[int] = None, + kernel: str | NdarrayOrTensor, + kernel_size: int | None = None, prob: float = 0.1, allow_missing_keys: bool = False, **kwargs, @@ -1814,7 +1815,7 @@ def __init__( RandomizableTransform.__init__(self, prob) self.filter = ImageFilter(kernel, kernel_size, **kwargs) - def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> Dict[Hashable, NdarrayOrTensor]: + def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: d = dict(data) self.randomize(None) if self._do_transform: diff --git a/monai/transforms/utils.py b/monai/transforms/utils.py index 8c7f475972..a39ff1f769 100644 --- a/monai/transforms/utils.py +++ b/monai/transforms/utils.py @@ -9,13 +9,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import itertools import random import warnings +from collections.abc import Callable, Hashable, Iterable, Mapping, Sequence from contextlib import contextmanager from functools import wraps from inspect import getmembers, isclass -from typing import Any, Callable, Hashable, Iterable, List, Mapping, Optional, Sequence, Set, Tuple, Union +from typing import Any import numpy as np import torch @@ -139,7 +142,7 @@ def in_bounds(x: float, y: float, margin: float, maxx: float, maxy: float) -> bo return bool(margin <= x < (maxx - margin) and margin <= y < (maxy - margin)) -def is_empty(img: Union[np.ndarray, torch.Tensor]) -> bool: +def is_empty(img: np.ndarray | torch.Tensor) -> bool: """ Returns True if `img` is empty, that is its maximum value is not greater than its minimum. """ @@ -165,9 +168,9 @@ def zero_margins(img: np.ndarray, margin: int) -> bool: def rescale_array( arr: NdarrayOrTensor, - minv: Optional[float] = 0.0, - maxv: Optional[float] = 1.0, - dtype: Union[DtypeLike, torch.dtype] = np.float32, + minv: float | None = 0.0, + maxv: float | None = 1.0, + dtype: DtypeLike | torch.dtype = np.float32, ) -> NdarrayOrTensor: """ Rescale the values of numpy array `arr` to be from `minv` to `maxv`. @@ -195,7 +198,7 @@ def rescale_array( def rescale_instance_array( - arr: np.ndarray, minv: Optional[float] = 0.0, maxv: Optional[float] = 1.0, dtype: DtypeLike = np.float32 + arr: np.ndarray, minv: float | None = 0.0, maxv: float | None = 1.0, dtype: DtypeLike = np.float32 ) -> np.ndarray: """ Rescale each array slice along the first dimension of `arr` independently. @@ -216,8 +219,8 @@ def rescale_array_int_max(arr: np.ndarray, dtype: DtypeLike = np.uint16) -> np.n def copypaste_arrays( - src_shape, dest_shape, srccenter: Sequence[int], destcenter: Sequence[int], dims: Sequence[Optional[int]] -) -> Tuple[Tuple[slice, ...], Tuple[slice, ...]]: + src_shape, dest_shape, srccenter: Sequence[int], destcenter: Sequence[int], dims: Sequence[int | None] +) -> tuple[tuple[slice, ...], tuple[slice, ...]]: """ Calculate the slices to copy a sliced area of array in `src_shape` into array in `dest_shape`. @@ -271,7 +274,7 @@ def copypaste_arrays( return tuple(srcslices), tuple(destslices) -def resize_center(img: np.ndarray, *resize_dims: Optional[int], fill_value: float = 0.0, inplace: bool = True): +def resize_center(img: np.ndarray, *resize_dims: int | None, fill_value: float = 0.0, inplace: bool = True): """ Resize `img` by cropping or expanding the image from the center. The `resize_dims` values are the output dimensions (or None to use original dimension of `img`). If a dimension is smaller than that of `img` then the result will be @@ -293,8 +296,8 @@ def resize_center(img: np.ndarray, *resize_dims: Optional[int], fill_value: floa def map_binary_to_indices( - label: NdarrayOrTensor, image: Optional[NdarrayOrTensor] = None, image_threshold: float = 0.0 -) -> Tuple[NdarrayOrTensor, NdarrayOrTensor]: + label: NdarrayOrTensor, image: NdarrayOrTensor | None = None, image_threshold: float = 0.0 +) -> tuple[NdarrayOrTensor, NdarrayOrTensor]: """ Compute the foreground and background of input label data, return the indices after fattening. For example: @@ -329,10 +332,10 @@ def map_binary_to_indices( def map_classes_to_indices( label: NdarrayOrTensor, - num_classes: Optional[int] = None, - image: Optional[NdarrayOrTensor] = None, + num_classes: int | None = None, + image: NdarrayOrTensor | None = None, image_threshold: float = 0.0, -) -> List[NdarrayOrTensor]: +) -> list[NdarrayOrTensor]: """ Filter out indices of every class of the input label data, return the indices after fattening. It can handle both One-Hot format label and Argmax format label, must provide `num_classes` for @@ -352,11 +355,11 @@ def map_classes_to_indices( determine the valid image content area and select class indices only in this area. """ - img_flat: Optional[NdarrayOrTensor] = None + img_flat: NdarrayOrTensor | None = None if image is not None: img_flat = ravel((image > image_threshold).any(0)) - indices: List[NdarrayOrTensor] = [] + indices: list[NdarrayOrTensor] = [] # assuming the first dimension is channel channels = len(label) @@ -377,11 +380,11 @@ def map_classes_to_indices( def weighted_patch_samples( - spatial_size: Union[int, Sequence[int]], + spatial_size: int | Sequence[int], w: NdarrayOrTensor, n_samples: int = 1, - r_state: Optional[np.random.RandomState] = None, -) -> List: + r_state: np.random.RandomState | None = None, +) -> list: """ Computes `n_samples` of random patch sampling locations, given the sampling weight map `w` and patch `spatial_size`. @@ -424,8 +427,8 @@ def weighted_patch_samples( def correct_crop_centers( - centers: List[int], - spatial_size: Union[Sequence[int], int], + centers: list[int], + spatial_size: Sequence[int] | int, label_spatial_shape: Sequence[int], allow_smaller: bool = False, ): @@ -465,15 +468,15 @@ def correct_crop_centers( def generate_pos_neg_label_crop_centers( - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, num_samples: int, pos_ratio: float, label_spatial_shape: Sequence[int], fg_indices: NdarrayOrTensor, bg_indices: NdarrayOrTensor, - rand_state: Optional[np.random.RandomState] = None, + rand_state: np.random.RandomState | None = None, allow_smaller: bool = False, -) -> List[List[int]]: +) -> list[list[int]]: """ Generate valid sample locations based on the label with option for specifying foreground ratio Valid: samples sitting entirely within image, expected input shape: [C, H, W, D] or [C, H, W] @@ -523,14 +526,14 @@ def generate_pos_neg_label_crop_centers( def generate_label_classes_crop_centers( - spatial_size: Union[Sequence[int], int], + spatial_size: Sequence[int] | int, num_samples: int, label_spatial_shape: Sequence[int], indices: Sequence[NdarrayOrTensor], - ratios: Optional[List[Union[float, int]]] = None, - rand_state: Optional[np.random.RandomState] = None, + ratios: list[float | int] | None = None, + rand_state: np.random.RandomState | None = None, allow_smaller: bool = False, -) -> List[List[int]]: +) -> list[list[int]]: """ Generate valid sample locations based on the specified ratios of label classes. Valid: samples sitting entirely within image, expected input shape: [C, H, W, D] or [C, H, W] @@ -553,7 +556,7 @@ def generate_label_classes_crop_centers( if num_samples < 1: raise ValueError("num_samples must be an int number and greater than 0.") - ratios_: List[Union[float, int]] = ([1] * len(indices)) if ratios is None else ratios + ratios_: list[float | int] = ([1] * len(indices)) if ratios is None else ratios if len(ratios_) != len(indices): raise ValueError("random crop ratios must match the number of indices of classes.") if any(i < 0 for i in ratios_): @@ -579,10 +582,10 @@ def generate_label_classes_crop_centers( def create_grid( spatial_size: Sequence[int], - spacing: Optional[Sequence[float]] = None, + spacing: Sequence[float] | None = None, homogeneous: bool = True, - dtype: Union[DtypeLike, torch.dtype] = float, - device: Optional[torch.device] = None, + dtype: DtypeLike | torch.dtype = float, + device: torch.device | None = None, backend=TransformBackends.NUMPY, ) -> NdarrayOrTensor: """ @@ -611,9 +614,9 @@ def create_grid( def _create_grid_numpy( spatial_size: Sequence[int], - spacing: Optional[Sequence[float]] = None, + spacing: Sequence[float] | None = None, homogeneous: bool = True, - dtype: Union[DtypeLike, torch.dtype] = float, + dtype: DtypeLike | torch.dtype = float, ): """ compute a `spatial_size` mesh with the numpy API. @@ -628,10 +631,10 @@ def _create_grid_numpy( def _create_grid_torch( spatial_size: Sequence[int], - spacing: Optional[Sequence[float]] = None, + spacing: Sequence[float] | None = None, homogeneous: bool = True, dtype=torch.float32, - device: Optional[torch.device] = None, + device: torch.device | None = None, ): """ compute a `spatial_size` mesh with the torch API. @@ -658,7 +661,7 @@ def create_control_grid( spacing: Sequence[float], homogeneous: bool = True, dtype: DtypeLike = float, - device: Optional[torch.device] = None, + device: torch.device | None = None, backend=TransformBackends.NUMPY, ): """ @@ -680,8 +683,8 @@ def create_control_grid( def create_rotate( spatial_dims: int, - radians: Union[Sequence[float], float], - device: Optional[torch.device] = None, + radians: Sequence[float] | float, + device: torch.device | None = None, backend: str = TransformBackends.NUMPY, ) -> NdarrayOrTensor: """ @@ -718,7 +721,7 @@ def create_rotate( def _create_rotate( spatial_dims: int, - radians: Union[Sequence[float], float], + radians: Sequence[float] | float, sin_func: Callable = np.sin, cos_func: Callable = np.cos, eye_func: Callable = np.eye, @@ -765,8 +768,8 @@ def _create_rotate( def create_shear( spatial_dims: int, - coefs: Union[Sequence[float], float], - device: Optional[torch.device] = None, + coefs: Sequence[float] | float, + device: torch.device | None = None, backend=TransformBackends.NUMPY, ) -> NdarrayOrTensor: """ @@ -801,7 +804,7 @@ def create_shear( raise ValueError(f"backend {backend} is not supported") -def _create_shear(spatial_dims: int, coefs: Union[Sequence[float], float], eye_func=np.eye) -> NdarrayOrTensor: +def _create_shear(spatial_dims: int, coefs: Sequence[float] | float, eye_func=np.eye) -> NdarrayOrTensor: if spatial_dims == 2: coefs = ensure_tuple_size(coefs, dim=2, pad_val=0.0) out = eye_func(3) @@ -819,8 +822,8 @@ def _create_shear(spatial_dims: int, coefs: Union[Sequence[float], float], eye_f def create_scale( spatial_dims: int, - scaling_factor: Union[Sequence[float], float], - device: Optional[torch.device] = None, + scaling_factor: Sequence[float] | float, + device: torch.device | None = None, backend=TransformBackends.NUMPY, ) -> NdarrayOrTensor: """ @@ -844,17 +847,15 @@ def create_scale( raise ValueError(f"backend {backend} is not supported") -def _create_scale( - spatial_dims: int, scaling_factor: Union[Sequence[float], float], array_func=np.diag -) -> NdarrayOrTensor: +def _create_scale(spatial_dims: int, scaling_factor: Sequence[float] | float, array_func=np.diag) -> NdarrayOrTensor: scaling_factor = ensure_tuple_size(scaling_factor, dim=spatial_dims, pad_val=1.0) return array_func(scaling_factor[:spatial_dims] + (1.0,)) # type: ignore def create_translate( spatial_dims: int, - shift: Union[Sequence[float], float], - device: Optional[torch.device] = None, + shift: Sequence[float] | float, + device: torch.device | None = None, backend=TransformBackends.NUMPY, ) -> NdarrayOrTensor: """ @@ -880,7 +881,7 @@ def create_translate( def _create_translate( - spatial_dims: int, shift: Union[Sequence[float], float], eye_func=np.eye, array_func=np.asarray + spatial_dims: int, shift: Sequence[float] | float, eye_func=np.eye, array_func=np.asarray ) -> NdarrayOrTensor: shift = ensure_tuple(shift) affine = eye_func(spatial_dims + 1) @@ -892,10 +893,10 @@ def _create_translate( def generate_spatial_bounding_box( img: NdarrayOrTensor, select_fn: Callable = is_positive, - channel_indices: Optional[IndexSelection] = None, - margin: Union[Sequence[int], int] = 0, + channel_indices: IndexSelection | None = None, + margin: Sequence[int] | int = 0, allow_smaller: bool = True, -) -> Tuple[List[int], List[int]]: +) -> tuple[list[int], list[int]]: """ Generate the spatial bounding box of foreground in the image with start-end positions (inclusive). Users can define arbitrary function to select expected foreground from the whole image or specified channels. @@ -952,7 +953,7 @@ def generate_spatial_bounding_box( def get_largest_connected_component_mask( - img: NdarrayTensor, connectivity: Optional[int] = None, num_components: int = 1 + img: NdarrayTensor, connectivity: int | None = None, num_components: int = 1 ) -> NdarrayTensor: """ Gets the largest connected component mask of an image. @@ -1046,9 +1047,7 @@ def remove_small_objects( return out -def get_unique_labels( - img: NdarrayOrTensor, is_onehot: bool, discard: Optional[Union[int, Iterable[int]]] = None -) -> Set[int]: +def get_unique_labels(img: NdarrayOrTensor, is_onehot: bool, discard: int | Iterable[int] | None = None) -> set[int]: """Get list of non-background labels in an image. Args: @@ -1060,7 +1059,7 @@ def get_unique_labels( Returns: Set of labels """ - applied_labels: Set[int] + applied_labels: set[int] n_channels = img.shape[0] if is_onehot: applied_labels = {i for i, s in enumerate(img) if s.sum() > 0} @@ -1075,7 +1074,7 @@ def get_unique_labels( def fill_holes( - img_arr: np.ndarray, applied_labels: Optional[Iterable[int]] = None, connectivity: Optional[int] = None + img_arr: np.ndarray, applied_labels: Iterable[int] | None = None, connectivity: int | None = None ) -> np.ndarray: """ Fill the holes in the provided image. @@ -1134,8 +1133,8 @@ def fill_holes( def get_extreme_points( - img: NdarrayOrTensor, rand_state: Optional[np.random.RandomState] = None, background: int = 0, pert: float = 0.0 -) -> List[Tuple[int, ...]]: + img: NdarrayOrTensor, rand_state: np.random.RandomState | None = None, background: int = 0, pert: float = 0.0 +) -> list[tuple[int, ...]]: """ Generate extreme points from an image. These are used to generate initial segmentation for annotation models. An optional perturbation can be passed to simulate user clicks. @@ -1191,9 +1190,9 @@ def _get_point(val, dim): def extreme_points_to_image( - points: List[Tuple[int, ...]], + points: list[tuple[int, ...]], label: NdarrayOrTensor, - sigma: Union[Sequence[float], float, Sequence[torch.Tensor], torch.Tensor] = 0.0, + sigma: Sequence[float] | float | Sequence[torch.Tensor] | torch.Tensor = 0.0, rescale_min: float = -1.0, rescale_max: float = 1.0, ) -> torch.Tensor: @@ -1237,8 +1236,8 @@ def extreme_points_to_image( def map_spatial_axes( - img_ndim: int, spatial_axes: Optional[Union[Sequence[int], int]] = None, channel_first: bool = True -) -> List[int]: + img_ndim: int, spatial_axes: Sequence[int] | int | None = None, channel_first: bool = True +) -> list[int]: """ Utility to map the spatial axes to real axes in channel first/last shape. For example: @@ -1272,7 +1271,7 @@ def map_spatial_axes( @contextmanager -def allow_missing_keys_mode(transform: Union[MapTransform, Compose, Tuple[MapTransform], Tuple[Compose]]): +def allow_missing_keys_mode(transform: MapTransform | Compose | tuple[MapTransform] | tuple[Compose]): """Temporarily set all MapTransforms to not throw an error if keys are missing. After, revert to original states. Args: @@ -1321,7 +1320,7 @@ def allow_missing_keys_mode(transform: Union[MapTransform, Compose, Tuple[MapTra _interp_modes = list(InterpolateMode) + list(GridSampleMode) -def convert_applied_interp_mode(trans_info, mode: str = "nearest", align_corners: Optional[bool] = None): +def convert_applied_interp_mode(trans_info, mode: str = "nearest", align_corners: bool | None = None): """ Recursively change the interpolation mode in the applied operation stacks, default to "nearest". @@ -1372,7 +1371,7 @@ def reset_ops_id(data): return {k: reset_ops_id(v) for k, v in data.items()} -def compute_divisible_spatial_size(spatial_shape: Sequence[int], k: Union[Sequence[int], int]): +def compute_divisible_spatial_size(spatial_shape: Sequence[int], k: Sequence[int] | int): """ Compute the target spatial size which should be divisible by `k`. @@ -1393,7 +1392,7 @@ def compute_divisible_spatial_size(spatial_shape: Sequence[int], k: Union[Sequen def equalize_hist( - img: np.ndarray, mask: Optional[np.ndarray] = None, num_bins: int = 256, min: int = 0, max: int = 255 + img: np.ndarray, mask: np.ndarray | None = None, num_bins: int = 256, min: int = 0, max: int = 255 ) -> np.ndarray: """ Utility to equalize input image based on the histogram. @@ -1459,7 +1458,7 @@ def shift_fourier(x: NdarrayOrTensor, spatial_dims: int) -> NdarrayOrTensor: return k @staticmethod - def inv_shift_fourier(k: NdarrayOrTensor, spatial_dims: int, n_dims: Optional[int] = None) -> NdarrayOrTensor: + def inv_shift_fourier(k: NdarrayOrTensor, spatial_dims: int, n_dims: int | None = None) -> NdarrayOrTensor: """ Applies inverse shift and fourier transform. Only the spatial dimensions are transformed. @@ -1484,7 +1483,7 @@ def inv_shift_fourier(k: NdarrayOrTensor, spatial_dims: int, n_dims: Optional[in return out -def get_number_image_type_conversions(transform: Compose, test_data: Any, key: Optional[Hashable] = None) -> int: +def get_number_image_type_conversions(transform: Compose, test_data: Any, key: Hashable | None = None) -> int: """ Get the number of times that the data need to be converted (e.g., numpy to torch). Conversions between different devices are also counted (e.g., CPU to GPU). @@ -1607,7 +1606,7 @@ def print_table_column(name, torch, numpy, color=Colors.none): print_color(f"Number of uncategorised: {n_uncategorized}", Colors.red) -def convert_pad_mode(dst: NdarrayOrTensor, mode: Optional[str]): +def convert_pad_mode(dst: NdarrayOrTensor, mode: str | None): """ Utility to convert padding mode between numpy array and PyTorch Tensor. @@ -1632,8 +1631,8 @@ def convert_pad_mode(dst: NdarrayOrTensor, mode: Optional[str]): def convert_to_contiguous( - data: Union[NdarrayOrTensor, str, bytes, Mapping, Sequence[Any]], **kwargs -) -> Union[NdarrayOrTensor, Mapping, Sequence[Any]]: + data: NdarrayOrTensor | str | bytes | Mapping | Sequence[Any], **kwargs +) -> NdarrayOrTensor | Mapping | Sequence[Any]: """ Check and ensure the numpy array or PyTorch Tensor in data to be contiguous in memory. diff --git a/monai/transforms/utils_create_transform_ims.py b/monai/transforms/utils_create_transform_ims.py index 12236e1bb1..f040ea18aa 100644 --- a/monai/transforms/utils_create_transform_ims.py +++ b/monai/transforms/utils_create_transform_ims.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import pathlib import tempfile diff --git a/monai/transforms/utils_pytorch_numpy_unification.py b/monai/transforms/utils_pytorch_numpy_unification.py index d3f7855649..42e97140b1 100644 --- a/monai/transforms/utils_pytorch_numpy_unification.py +++ b/monai/transforms/utils_pytorch_numpy_unification.py @@ -9,7 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Sequence, Tuple, TypeVar, Union +from __future__ import annotations + +from collections.abc import Sequence +from typing import TypeVar import numpy as np import torch @@ -58,7 +61,7 @@ def allclose(a: NdarrayTensor, b: NdarrayOrTensor, rtol=1e-5, atol=1e-8, equal_n return torch.allclose(a, b, rtol=rtol, atol=atol, equal_nan=equal_nan) # type: ignore -def moveaxis(x: NdarrayOrTensor, src: Union[int, Sequence[int]], dst: Union[int, Sequence[int]]) -> NdarrayOrTensor: +def moveaxis(x: NdarrayOrTensor, src: int | Sequence[int], dst: int | Sequence[int]) -> NdarrayOrTensor: """`moveaxis` for pytorch and numpy""" if isinstance(x, torch.Tensor): return torch.movedim(x, src, dst) # type: ignore @@ -83,8 +86,8 @@ def clip(a: NdarrayOrTensor, a_min, a_max) -> NdarrayOrTensor: def percentile( - x: NdarrayOrTensor, q, dim: Optional[int] = None, keepdim: bool = False, **kwargs -) -> Union[NdarrayOrTensor, float, int]: + x: NdarrayOrTensor, q, dim: int | None = None, keepdim: bool = False, **kwargs +) -> NdarrayOrTensor | float | int: """`np.percentile` with equivalent implementation for torch. Pytorch uses `quantile`. For more details please refer to: @@ -106,7 +109,7 @@ def percentile( q_np = convert_data_type(q, output_type=np.ndarray, wrap_sequence=True)[0] if ((q_np < 0) | (q_np > 100)).any(): raise ValueError(f"q values must be in [0, 100], got values: {q}.") - result: Union[NdarrayOrTensor, float, int] + result: NdarrayOrTensor | float | int if isinstance(x, np.ndarray) or (isinstance(x, torch.Tensor) and torch.numel(x) > 1_000_000): # pytorch#64947 _x = convert_data_type(x, output_type=np.ndarray)[0] result = np.percentile(_x, q_np, axis=dim, keepdims=keepdim, **kwargs) @@ -220,7 +223,7 @@ def ravel(x: NdarrayOrTensor) -> NdarrayOrTensor: return np.ravel(x) -def any_np_pt(x: NdarrayOrTensor, axis: Union[int, Sequence[int]]) -> NdarrayOrTensor: +def any_np_pt(x: NdarrayOrTensor, axis: int | Sequence[int]) -> NdarrayOrTensor: """`np.any` with equivalent implementation for torch. For pytorch, convert to boolean for compatibility with older versions. @@ -313,7 +316,7 @@ def searchsorted(a: NdarrayTensor, v: NdarrayOrTensor, right=False, sorter=None, return torch.searchsorted(a, v, right=right, **kwargs) # type: ignore -def repeat(a: NdarrayOrTensor, repeats: int, axis: Optional[int] = None, **kwargs) -> NdarrayOrTensor: +def repeat(a: NdarrayOrTensor, repeats: int, axis: int | None = None, **kwargs) -> NdarrayOrTensor: """ `np.repeat` with equivalent implementation for torch (`repeat_interleave`). @@ -345,7 +348,7 @@ def isnan(x: NdarrayOrTensor) -> NdarrayOrTensor: T = TypeVar("T") -def ascontiguousarray(x: Union[NdarrayTensor, T], **kwargs) -> Union[NdarrayOrTensor, T]: +def ascontiguousarray(x: NdarrayTensor | T, **kwargs) -> NdarrayOrTensor | T: """`np.ascontiguousarray` with equivalent implementation for torch (`contiguous`). Args: @@ -410,7 +413,7 @@ def linalg_inv(x: NdarrayTensor) -> NdarrayTensor: return torch.linalg.inv(x) if isinstance(x, torch.Tensor) else np.linalg.inv(x) # type: ignore -def max(x: NdarrayTensor, dim: Optional[Union[int, Tuple]] = None, **kwargs) -> NdarrayTensor: +def max(x: NdarrayTensor, dim: int | tuple | None = None, **kwargs) -> NdarrayTensor: """`torch.max` with equivalent implementation for numpy Args: @@ -433,7 +436,7 @@ def max(x: NdarrayTensor, dim: Optional[Union[int, Tuple]] = None, **kwargs) -> return ret -def mean(x: NdarrayTensor, dim: Optional[Union[int, Tuple]] = None, **kwargs) -> NdarrayTensor: +def mean(x: NdarrayTensor, dim: int | tuple | None = None, **kwargs) -> NdarrayTensor: """`torch.mean` with equivalent implementation for numpy Args: @@ -455,7 +458,7 @@ def mean(x: NdarrayTensor, dim: Optional[Union[int, Tuple]] = None, **kwargs) -> return ret -def median(x: NdarrayTensor, dim: Optional[Union[int, Tuple]] = None, **kwargs) -> NdarrayTensor: +def median(x: NdarrayTensor, dim: int | tuple | None = None, **kwargs) -> NdarrayTensor: """`torch.median` with equivalent implementation for numpy Args: @@ -477,7 +480,7 @@ def median(x: NdarrayTensor, dim: Optional[Union[int, Tuple]] = None, **kwargs) return ret -def min(x: NdarrayTensor, dim: Optional[Union[int, Tuple]] = None, **kwargs) -> NdarrayTensor: +def min(x: NdarrayTensor, dim: int | tuple | None = None, **kwargs) -> NdarrayTensor: """`torch.min` with equivalent implementation for numpy Args: @@ -499,7 +502,7 @@ def min(x: NdarrayTensor, dim: Optional[Union[int, Tuple]] = None, **kwargs) -> return ret -def std(x: NdarrayTensor, dim: Optional[Union[int, Tuple]] = None, unbiased: bool = False) -> NdarrayTensor: +def std(x: NdarrayTensor, dim: int | tuple | None = None, unbiased: bool = False) -> NdarrayTensor: """`torch.std` with equivalent implementation for numpy Args: @@ -521,7 +524,7 @@ def std(x: NdarrayTensor, dim: Optional[Union[int, Tuple]] = None, unbiased: boo return ret -def sum(x: NdarrayTensor, dim: Optional[Union[int, Tuple]] = None, **kwargs) -> NdarrayTensor: +def sum(x: NdarrayTensor, dim: int | tuple | None = None, **kwargs) -> NdarrayTensor: """`torch.sum` with equivalent implementation for numpy Args: diff --git a/monai/utils/__init__.py b/monai/utils/__init__.py index 1dd7e40930..45886df5e1 100644 --- a/monai/utils/__init__.py +++ b/monai/utils/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + # have to explicitly bring these in here to resolve circular import issues from .aliases import alias, resolve_name from .decorators import MethodReplacer, RestartGenerator diff --git a/monai/utils/aliases.py b/monai/utils/aliases.py index 967f1c5670..38a3fa6d31 100644 --- a/monai/utils/aliases.py +++ b/monai/utils/aliases.py @@ -12,6 +12,8 @@ This module is written for configurable workflow, not currently in use. """ +from __future__ import annotations + import importlib import inspect import sys diff --git a/monai/utils/decorators.py b/monai/utils/decorators.py index 0856c0fc1a..e3c295945f 100644 --- a/monai/utils/decorators.py +++ b/monai/utils/decorators.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from functools import wraps __all__ = ["RestartGenerator", "MethodReplacer"] diff --git a/monai/utils/deprecate_utils.py b/monai/utils/deprecate_utils.py index 793bcc2104..07d204064d 100644 --- a/monai/utils/deprecate_utils.py +++ b/monai/utils/deprecate_utils.py @@ -9,12 +9,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import inspect import sys import warnings +from collections.abc import Callable from functools import wraps from types import FunctionType -from typing import Any, Callable, Optional, TypeVar +from typing import Any, TypeVar from monai.utils.module import version_leq @@ -36,8 +39,8 @@ def warn_deprecated(obj, msg, warning_category=FutureWarning): def deprecated( - since: Optional[str] = None, - removed: Optional[str] = None, + since: str | None = None, + removed: str | None = None, msg_suffix: str = "", version_val: str = __version__, warning_category=FutureWarning, @@ -119,11 +122,11 @@ def _wrapper(*args, **kwargs): def deprecated_arg( name, - since: Optional[str] = None, - removed: Optional[str] = None, + since: str | None = None, + removed: str | None = None, msg_suffix: str = "", version_val: str = __version__, - new_name: Optional[str] = None, + new_name: str | None = None, warning_category=FutureWarning, ) -> Callable[[T], T]: """ @@ -228,8 +231,8 @@ def deprecated_arg_default( name: str, old_default: Any, new_default: Any, - since: Optional[str] = None, - replaced: Optional[str] = None, + since: str | None = None, + replaced: str | None = None, msg_suffix: str = "", version_val: str = __version__, warning_category=FutureWarning, diff --git a/monai/utils/dist.py b/monai/utils/dist.py index 37536bfe83..30c4ddefc1 100644 --- a/monai/utils/dist.py +++ b/monai/utils/dist.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List +from __future__ import annotations import torch import torch.distributed as dist @@ -62,7 +62,7 @@ def evenly_divisible_all_gather(data: torch.Tensor, concat: bool = True): ndims = data.ndimension() length: int = data.shape[0] if ndims > 0 else 1 - def _torch_all_gather(data: torch.Tensor) -> List[torch.Tensor]: + def _torch_all_gather(data: torch.Tensor) -> list[torch.Tensor]: """ Implementation based on native PyTorch distributed data parallel APIs. @@ -76,7 +76,7 @@ def _torch_all_gather(data: torch.Tensor) -> List[torch.Tensor]: length_tensor = torch.as_tensor([length], device=device) all_lens = [torch.zeros_like(length_tensor) for _ in range(dist.get_world_size())] dist.all_gather(all_lens, length_tensor) - all_lens_: List[int] = [int(i.item()) for i in all_lens] + all_lens_: list[int] = [int(i.item()) for i in all_lens] max_len: int = max(all_lens_) if length < max_len: @@ -88,14 +88,14 @@ def _torch_all_gather(data: torch.Tensor) -> List[torch.Tensor]: # remove the padding items, if all the input data doesn't have batch dim, squeeze the first dim return [(o.squeeze(0) if ndims == 0 else o[:l, ...]).to(orig_device) for o, l in zip(output, all_lens_)] - def _ignite_all_gather(data: torch.Tensor) -> List[torch.Tensor]: + def _ignite_all_gather(data: torch.Tensor) -> list[torch.Tensor]: """ Implementation based on PyTorch ignite package, it can support more kinds of backends. """ data = data.unsqueeze(0) if ndims == 0 else data # make sure the data is evenly-divisible on multi-GPUs - all_lens: List[int] = idist.all_gather(length) + all_lens: list[int] = idist.all_gather(length) max_len: int = max(all_lens) if length < max_len: size = [max_len - length] + list(data.shape[1:]) @@ -108,7 +108,7 @@ def _ignite_all_gather(data: torch.Tensor) -> List[torch.Tensor]: return list(torch.unbind(output, dim=0)) return [output[i * max_len : i * max_len + l, ...] for i, l in enumerate(all_lens)] - output: List[torch.Tensor] + output: list[torch.Tensor] if has_ignite: if idist.get_world_size() <= 1: return data @@ -123,7 +123,7 @@ def _ignite_all_gather(data: torch.Tensor) -> List[torch.Tensor]: return torch.cat(output, dim=0) if concat else output -def string_list_all_gather(strings: List[str], delimiter: str = "\t") -> List[str]: +def string_list_all_gather(strings: list[str], delimiter: str = "\t") -> list[str]: """ Utility function for distributed data parallel to all gather a list of strings. Refer to the idea of ignite `all_gather(string)`: diff --git a/monai/utils/enums.py b/monai/utils/enums.py index 4352d83473..4b1c3b2dce 100644 --- a/monai/utils/enums.py +++ b/monai/utils/enums.py @@ -9,9 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import random from enum import Enum -from typing import Optional from monai.utils.deprecate_utils import deprecated @@ -371,15 +372,15 @@ def _get_str(prefix, suffix): return suffix if prefix is None else f"{prefix}_{suffix}" @staticmethod - def meta(key: Optional[str] = None): + def meta(key: str | None = None): return PostFix._get_str(key, "meta_dict") @staticmethod - def orig_meta(key: Optional[str] = None): + def orig_meta(key: str | None = None): return PostFix._get_str(key, "orig_meta_dict") @staticmethod - def transforms(key: Optional[str] = None): + def transforms(key: str | None = None): return PostFix._get_str(key, TraceKeys.KEY_SUFFIX[1:]) diff --git a/monai/utils/jupyter_utils.py b/monai/utils/jupyter_utils.py index 38b8e0a7e7..0b6977f744 100644 --- a/monai/utils/jupyter_utils.py +++ b/monai/utils/jupyter_utils.py @@ -13,10 +13,13 @@ Matplotlib produce common plots for metrics and images. """ +from __future__ import annotations + import copy +from collections.abc import Callable from enum import Enum from threading import RLock, Thread -from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Any import numpy as np import torch @@ -43,9 +46,9 @@ def plot_metric_graph( ax, title: str, - graphmap: Dict[str, Union[List[float], Tuple[List[float], List[float]]]], + graphmap: dict[str, list[float] | tuple[list[float], list[float]]], yscale: str = "log", - avg_keys: Tuple[str] = (LOSS_NAME,), + avg_keys: tuple[str] = (LOSS_NAME,), window_fraction: int = 20, ): """ @@ -90,12 +93,12 @@ def plot_metric_graph( def plot_metric_images( fig, title: str, - graphmap: Dict[str, Union[List[float], Tuple[List[float], List[float]]]], - imagemap: Dict[str, np.ndarray], + graphmap: dict[str, list[float] | tuple[list[float], list[float]]], + imagemap: dict[str, np.ndarray], yscale: str = "log", - avg_keys: Tuple[str] = (LOSS_NAME,), + avg_keys: tuple[str] = (LOSS_NAME,), window_fraction: int = 20, -) -> List: +) -> list: """ Plot metric graph data with images below into figure `fig`. The intended use is for the graph data to be metrics from a training run and the images to be the batch and output from the last iteration. This uses @@ -157,12 +160,12 @@ def plot_engine_status( logger, title: str = "Training Log", yscale: str = "log", - avg_keys: Tuple[str] = (LOSS_NAME,), + avg_keys: tuple[str] = (LOSS_NAME,), window_fraction: int = 20, - image_fn: Optional[Callable] = tensor_to_images, + image_fn: Callable | None = tensor_to_images, fig=None, selected_inst: int = 0, -) -> Tuple: +) -> tuple: """ Plot the status of the given Engine with its logger. The plot will consist of a graph of loss values and metrics taken from the logger, and images taken from the `output` and `batch` members of `engine.state`. The images are @@ -191,7 +194,7 @@ def plot_engine_status( graphmap = {LOSS_NAME: logger.loss} graphmap.update(logger.metrics) - imagemap: Dict = {} + imagemap: dict = {} if image_fn is not None and engine.state is not None and engine.state.batch is not None: for src in (engine.state.batch, engine.state.output): label = "Batch" if src is engine.state.batch else "Output" @@ -230,7 +233,7 @@ def plot_engine_status( return fig, axes -def _get_loss_from_output(output: Union[Dict[str, torch.Tensor], torch.Tensor]): +def _get_loss_from_output(output: dict[str, torch.Tensor] | torch.Tensor): """Returns a single value from the network output, which is a dict or tensor.""" def _get_loss(data): @@ -280,7 +283,7 @@ def __init__( super().__init__() self.lock = RLock() self.engine = engine - self._status_dict: Dict[str, Any] = {} + self._status_dict: dict[str, Any] = {} self.loss_transform = loss_transform self.metric_transform = metric_transform self.fig = None @@ -301,7 +304,7 @@ def _update_status(self): """Called as an event, updates the internal status dict at the end of iterations.""" with self.lock: state = self.engine.state - stats: Dict[str, Any] = { + stats: dict[str, Any] = { StatusMembers.EPOCHS.value: 0, StatusMembers.ITERS.value: 0, StatusMembers.LOSS.value: float("nan"), @@ -331,7 +334,7 @@ def _update_status(self): self._status_dict.update(stats) @property - def status_dict(self) -> Dict[str, str]: + def status_dict(self) -> dict[str, str]: """A dictionary containing status information, current loss, and current metric values.""" with self.lock: stats = {StatusMembers.STATUS.value: "Running" if self.is_alive() else "Stopped"} diff --git a/monai/utils/misc.py b/monai/utils/misc.py index c751ad3b49..d76d43b3f4 100644 --- a/monai/utils/misc.py +++ b/monai/utils/misc.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import inspect import itertools import os @@ -18,10 +20,10 @@ import types import warnings from ast import literal_eval -from collections.abc import Iterable +from collections.abc import Callable, Iterable, Sequence from distutils.util import strtobool from pathlib import Path -from typing import Any, Callable, List, Optional, Sequence, Tuple, Union, cast +from typing import Any, cast import numpy as np import torch @@ -101,7 +103,7 @@ def issequenceiterable(obj: Any) -> bool: return isinstance(obj, Iterable) and not isinstance(obj, (str, bytes)) -def ensure_tuple(vals: Any, wrap_array: bool = False) -> Tuple[Any, ...]: +def ensure_tuple(vals: Any, wrap_array: bool = False) -> tuple[Any, ...]: """ Returns a tuple of `vals`. @@ -116,7 +118,7 @@ def ensure_tuple(vals: Any, wrap_array: bool = False) -> Tuple[Any, ...]: return tuple(vals) if issequenceiterable(vals) else (vals,) -def ensure_tuple_size(tup: Any, dim: int, pad_val: Any = 0) -> Tuple[Any, ...]: +def ensure_tuple_size(tup: Any, dim: int, pad_val: Any = 0) -> tuple[Any, ...]: """ Returns a copy of `tup` with `dim` values by either shortened or padded with `pad_val` as necessary. """ @@ -124,7 +126,7 @@ def ensure_tuple_size(tup: Any, dim: int, pad_val: Any = 0) -> Tuple[Any, ...]: return new_tup[:dim] -def ensure_tuple_rep(tup: Any, dim: int) -> Tuple[Any, ...]: +def ensure_tuple_rep(tup: Any, dim: int) -> tuple[Any, ...]: """ Returns a copy of `tup` with `dim` values by either shortened or duplicated input. @@ -160,8 +162,8 @@ def ensure_tuple_rep(tup: Any, dim: int) -> Tuple[Any, ...]: def fall_back_tuple( - user_provided: Any, default: Union[Sequence, NdarrayTensor], func: Callable = lambda x: x and x > 0 -) -> Tuple[Any, ...]: + user_provided: Any, default: Sequence | NdarrayTensor, func: Callable = lambda x: x and x > 0 +) -> tuple[Any, ...]: """ Refine `user_provided` according to the `default`, and returns as a validated tuple. @@ -215,7 +217,7 @@ def is_scalar(val: Any) -> bool: return bool(np.isscalar(val)) -def progress_bar(index: int, count: int, desc: Optional[str] = None, bar_len: int = 30, newline: bool = False) -> None: +def progress_bar(index: int, count: int, desc: str | None = None, bar_len: int = 30, newline: bool = False) -> None: """print a progress bar to track some time consuming task. Args: @@ -234,14 +236,14 @@ def progress_bar(index: int, count: int, desc: Optional[str] = None, bar_len: in print("") -def get_seed() -> Optional[int]: +def get_seed() -> int | None: return _seed def set_determinism( - seed: Optional[int] = NP_MAX, - use_deterministic_algorithms: Optional[bool] = None, - additional_settings: Optional[Union[Sequence[Callable[[int], Any]], Callable[[int], Any]]] = None, + seed: int | None = NP_MAX, + use_deterministic_algorithms: bool | None = None, + additional_settings: Sequence[Callable[[int], Any]] | Callable[[int], Any] | None = None, ) -> None: """ Set random seed for modules to enable or disable deterministic training. @@ -332,7 +334,7 @@ def _parse_var(s): def copy_to_device( - obj: Any, device: Optional[Union[str, torch.device]], non_blocking: bool = True, verbose: bool = False + obj: Any, device: str | torch.device | None, non_blocking: bool = True, verbose: bool = False ) -> Any: """ Copy object or tuple/list/dictionary of objects to ``device``. @@ -365,7 +367,7 @@ def copy_to_device( return obj -def str2bool(value: Union[str, bool], default: bool = False, raise_exc: bool = True) -> bool: +def str2bool(value: str | bool, default: bool = False, raise_exc: bool = True) -> bool: """ Convert a string to a boolean. Case insensitive. True: yes, true, t, y, 1. False: no, false, f, n, 0. @@ -400,7 +402,7 @@ def str2bool(value: Union[str, bool], default: bool = False, raise_exc: bool = T return default -def str2list(value: Optional[Union[str, list]], raise_exc: bool = True) -> Optional[list]: +def str2list(value: str | list | None, raise_exc: bool = True) -> list | None: """ Convert a string to a list. Useful with argparse commandline arguments: parser.add_argument("--blocks", default=[1,2,3], type=str2list) @@ -438,7 +440,7 @@ class MONAIEnvVars: """ @staticmethod - def data_dir() -> Optional[str]: + def data_dir() -> str | None: return os.environ.get("MONAI_DATA_DIRECTORY") @staticmethod @@ -447,7 +449,7 @@ def debug() -> bool: return val if isinstance(val, bool) else str2bool(val) @staticmethod - def doc_images() -> Optional[str]: + def doc_images() -> str | None: return os.environ.get("MONAI_DOC_IMAGES") @@ -461,7 +463,7 @@ class ImageMetaKey: SPATIAL_SHAPE = "spatial_shape" -def has_option(obj, keywords: Union[str, Sequence[str]]) -> bool: +def has_option(obj, keywords: str | Sequence[str]) -> bool: """ Return a boolean indicating whether the given callable `obj` has the `keywords` in its signature. """ @@ -521,9 +523,7 @@ def check_parent_dir(path: PathLike, create_dir: bool = True): raise ValueError(f"the directory of specified path does not exist: `{path_dir}`.") -def save_obj( - obj, path: PathLike, create_dir: bool = True, atomic: bool = True, func: Optional[Callable] = None, **kwargs -): +def save_obj(obj, path: PathLike, create_dir: bool = True, atomic: bool = True, func: Callable | None = None, **kwargs): """ Save an object to file with specified path. Support to serialize to a temporary file first, then move to final destination, @@ -564,7 +564,7 @@ def save_obj( pass -def label_union(x: List) -> List: +def label_union(x: list) -> list: """ Compute the union of class IDs in label and generate a list to include all class IDs Args: diff --git a/monai/utils/module.py b/monai/utils/module.py index 435b07fcac..2f2d5069dd 100644 --- a/monai/utils/module.py +++ b/monai/utils/module.py @@ -9,11 +9,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import enum import os import re import sys import warnings +from collections.abc import Callable, Collection, Hashable, Mapping from functools import partial, wraps from importlib import import_module from inspect import isclass, isfunction, ismethod @@ -21,7 +24,7 @@ from pydoc import locate from re import match from types import FunctionType -from typing import Any, Callable, Collection, Hashable, Iterable, List, Mapping, Tuple, Union, cast +from typing import Any import torch @@ -55,7 +58,7 @@ ] -def look_up_option(opt_str, supported: Union[Collection, enum.EnumMeta], default="no_default", print_all_options=True): +def look_up_option(opt_str, supported: Collection | enum.EnumMeta, default="no_default", print_all_options=True): """ Look up the option in the supported collection and return the matched item. Raise a value error possibly with a guess of the closest match. @@ -92,7 +95,7 @@ class Color(Enum): if isinstance(opt_str, str): opt_str = opt_str.strip() if isinstance(supported, enum.EnumMeta): - if isinstance(opt_str, str) and opt_str in {item.value for item in cast(Iterable[enum.Enum], supported)}: + if isinstance(opt_str, str) and opt_str in {item.value for item in supported}: # type: ignore # such as: "example" in MyEnum return supported(opt_str) if isinstance(opt_str, enum.Enum) and opt_str in supported: @@ -110,7 +113,7 @@ class Color(Enum): # find a close match set_to_check: set if isinstance(supported, enum.EnumMeta): - set_to_check = {item.value for item in cast(Iterable[enum.Enum], supported)} + set_to_check = {item.value for item in supported} # type: ignore else: set_to_check = set(supported) if supported is not None else set() if not set_to_check: @@ -190,7 +193,7 @@ def load_submodules(basemod, load_all: bool = True, exclude_pattern: str = "(.*[ `load_all` is True, excluding anything whose name matches `exclude_pattern`. """ submodules = [] - err_mod: List[str] = [] + err_mod: list[str] = [] for importer, name, is_pkg in walk_packages( basemod.__path__, prefix=basemod.__name__ + ".", onerror=err_mod.append ): @@ -310,7 +313,7 @@ def optional_import( version_args=None, allow_namespace_pkg: bool = False, as_type: str = "default", -) -> Tuple[Any, bool]: +) -> tuple[Any, bool]: """ Imports an optional module specified by `module` string. Any importing related exceptions will be stored, and exceptions raise lazily diff --git a/monai/utils/nvtx.py b/monai/utils/nvtx.py index 7b16d1e2b3..83ee154df5 100644 --- a/monai/utils/nvtx.py +++ b/monai/utils/nvtx.py @@ -12,9 +12,11 @@ Decorators and context managers for NVIDIA Tools Extension to profile MONAI components """ +from __future__ import annotations + from collections import defaultdict from functools import wraps -from typing import Any, Optional, Tuple, Union +from typing import Any from torch.autograd import Function from torch.nn import Module @@ -52,9 +54,9 @@ class Range: def __init__( self, - name: Optional[str] = None, - methods: Optional[Union[str, Tuple[str, ...]]] = None, - append_method_name: Optional[bool] = None, + name: str | None = None, + methods: str | tuple[str, ...] | None = None, + append_method_name: bool | None = None, recursive: bool = False, ) -> None: self.name = name diff --git a/monai/utils/profiling.py b/monai/utils/profiling.py index 291dfbbe16..ead0d88d60 100644 --- a/monai/utils/profiling.py +++ b/monai/utils/profiling.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import csv import datetime import multiprocessing @@ -21,7 +23,7 @@ from inspect import getframeinfo, stack from queue import Empty from time import perf_counter, perf_counter_ns -from typing import Any, Optional, cast +from typing import Any, cast import numpy as np import torch @@ -123,7 +125,7 @@ class PerfContext: def __init__(self): self.total_time: float = 0 - self.start_time: Optional[float] = None + self.start_time: float | None = None def __enter__(self): self.start_time = perf_counter() @@ -198,7 +200,7 @@ def foo(): pass def __init__(self, call_selector=select_transform_call): self.results = defaultdict(list) self.parent_pid = os.getpid() - self.read_thread: Optional[threading.Thread] = None + self.read_thread: threading.Thread | None = None self.lock = threading.RLock() self.queue: multiprocessing.SimpleQueue = multiprocessing.SimpleQueue() self.queue_timeout = 0.1 diff --git a/monai/utils/state_cacher.py b/monai/utils/state_cacher.py index 3e392ab979..df1b697939 100644 --- a/monai/utils/state_cacher.py +++ b/monai/utils/state_cacher.py @@ -9,11 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import copy import os import pickle import tempfile -from typing import Dict, Optional import torch from torch.serialization import DEFAULT_PROTOCOL @@ -41,7 +42,7 @@ class StateCacher: def __init__( self, in_memory: bool, - cache_dir: Optional[PathLike] = None, + cache_dir: PathLike | None = None, allow_overwrite: bool = True, pickle_module=pickle, pickle_protocol: int = DEFAULT_PROTOCOL, @@ -73,9 +74,9 @@ def __init__( self.allow_overwrite = allow_overwrite self.pickle_module = pickle_module self.pickle_protocol = pickle_protocol - self.cached: Dict = {} + self.cached: dict = {} - def store(self, key, data_obj, pickle_module=None, pickle_protocol: Optional[int] = None): + def store(self, key, data_obj, pickle_module=None, pickle_protocol: int | None = None): """ Store a given object with the given key name. diff --git a/monai/utils/type_conversion.py b/monai/utils/type_conversion.py index 2cb9bfd8c4..ca6be71a36 100644 --- a/monai/utils/type_conversion.py +++ b/monai/utils/type_conversion.py @@ -9,8 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import re -from typing import Any, Optional, Sequence, Tuple, Type, Union +from collections.abc import Sequence +from typing import Any import numpy as np import torch @@ -101,8 +104,8 @@ def get_dtype(data: Any): def convert_to_tensor( data, - dtype: Union[DtypeLike, torch.dtype] = None, - device: Union[None, str, torch.device] = None, + dtype: DtypeLike | torch.dtype = None, + device: None | str | torch.device = None, wrap_sequence: bool = False, track_meta: bool = False, safe: bool = False, @@ -215,7 +218,7 @@ def convert_to_numpy(data, dtype: DtypeLike = None, wrap_sequence: bool = False, return data -def convert_to_cupy(data, dtype: Optional[np.dtype] = None, wrap_sequence: bool = False, safe: bool = False): +def convert_to_cupy(data, dtype: np.dtype | None = None, wrap_sequence: bool = False, safe: bool = False): """ Utility to convert the input data to a cupy array. If passing a dictionary, list or tuple, recursively check every item and convert it to cupy array. @@ -255,12 +258,12 @@ def convert_to_cupy(data, dtype: Optional[np.dtype] = None, wrap_sequence: bool def convert_data_type( data: Any, - output_type: Optional[Type[NdarrayTensor]] = None, - device: Union[None, str, torch.device] = None, - dtype: Union[DtypeLike, torch.dtype] = None, + output_type: type[NdarrayTensor] | None = None, + device: None | str | torch.device = None, + dtype: DtypeLike | torch.dtype = None, wrap_sequence: bool = False, safe: bool = False, -) -> Tuple[NdarrayTensor, type, Optional[torch.device]]: +) -> tuple[NdarrayTensor, type, torch.device | None]: """ Convert to `MetaTensor`, `torch.Tensor` or `np.ndarray` from `MetaTensor`, `torch.Tensor`, `np.ndarray`, `float`, `int`, etc. @@ -325,11 +328,11 @@ def convert_data_type( def convert_to_dst_type( src: Any, dst: NdarrayTensor, - dtype: Union[DtypeLike, torch.dtype, None] = None, + dtype: DtypeLike | torch.dtype | None = None, wrap_sequence: bool = False, - device: Union[None, str, torch.device] = None, + device: None | str | torch.device = None, safe: bool = False, -) -> Tuple[NdarrayTensor, type, Optional[torch.device]]: +) -> tuple[NdarrayTensor, type, torch.device | None]: """ Convert source data to the same data type and device as the destination data. If `dst` is an instance of `torch.Tensor` or its subclass, convert `src` to `torch.Tensor` with the same data type as `dst`, @@ -375,7 +378,7 @@ def convert_to_dst_type( return output, _type, _device -def convert_to_list(data: Union[Sequence, torch.Tensor, np.ndarray]) -> list: +def convert_to_list(data: Sequence | torch.Tensor | np.ndarray) -> list: """ Convert to list from `torch.Tensor`/`np.ndarray`/`list`/`tuple` etc. Args: @@ -387,7 +390,7 @@ def convert_to_list(data: Union[Sequence, torch.Tensor, np.ndarray]) -> list: return data.tolist() if isinstance(data, (torch.Tensor, np.ndarray)) else list(data) -def get_dtype_bound_value(dtype: Union[DtypeLike, torch.dtype]): +def get_dtype_bound_value(dtype: DtypeLike | torch.dtype): """ Get dtype bound value Args: @@ -406,7 +409,7 @@ def get_dtype_bound_value(dtype: Union[DtypeLike, torch.dtype]): return (np.iinfo(dtype).min, np.iinfo(dtype).max) -def safe_dtype_range(data: Any, dtype: Union[DtypeLike, torch.dtype] = None): +def safe_dtype_range(data: Any, dtype: DtypeLike | torch.dtype = None): """ Utility to safely convert the input data to target dtype. diff --git a/monai/visualize/__init__.py b/monai/visualize/__init__.py index 49a628b66f..1f2bb7d024 100644 --- a/monai/visualize/__init__.py +++ b/monai/visualize/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from .class_activation_maps import CAM, GradCAM, GradCAMpp, ModelWithHooks, default_normalizer from .gradient_based import GuidedBackpropGrad, GuidedBackpropSmoothGrad, SmoothGrad, VanillaGrad from .img2tensorboard import add_animated_gif, make_animated_gif_summary, plot_2d_or_3d_image diff --git a/monai/visualize/class_activation_maps.py b/monai/visualize/class_activation_maps.py index 8654fbcc71..f32bc5ed45 100644 --- a/monai/visualize/class_activation_maps.py +++ b/monai/visualize/class_activation_maps.py @@ -9,8 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import warnings -from typing import Callable, Dict, Optional, Sequence, Union, cast +from collections.abc import Callable, Sequence +from typing import cast import numpy as np import torch @@ -52,7 +55,7 @@ class ModelWithHooks: def __init__( self, nn_module, - target_layer_names: Union[str, Sequence[str]], + target_layer_names: str | Sequence[str], register_forward: bool = False, register_backward: bool = False, ): @@ -67,10 +70,10 @@ def __init__( self.model = nn_module self.target_layers = ensure_tuple(target_layer_names) - self.gradients: Dict[str, torch.Tensor] = {} - self.activations: Dict[str, torch.Tensor] = {} - self.score: Optional[torch.Tensor] = None - self.class_idx: Optional[int] = None + self.gradients: dict[str, torch.Tensor] = {} + self.activations: dict[str, torch.Tensor] = {} + self.score: torch.Tensor | None = None + self.class_idx: int | None = None self.register_backward = register_backward self.register_forward = register_forward @@ -104,7 +107,7 @@ def _hook(_module, _input, output): return _hook - def get_layer(self, layer_id: Union[str, Callable]): + def get_layer(self, layer_id: str | Callable): """ Args: @@ -259,7 +262,7 @@ def __init__( self, nn_module: nn.Module, target_layers: str, - fc_layers: Union[str, Callable] = "fc", + fc_layers: str | Callable = "fc", upsampler: Callable = default_upsampler, postprocessing: Callable = default_normalizer, ) -> None: diff --git a/monai/visualize/img2tensorboard.py b/monai/visualize/img2tensorboard.py index 6dd7abcbf1..64ca9672b3 100644 --- a/monai/visualize/img2tensorboard.py +++ b/monai/visualize/img2tensorboard.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, List, Optional, Union +from __future__ import annotations + +from typing import TYPE_CHECKING import numpy as np import torch @@ -33,7 +35,7 @@ def _image3_animated_gif( - tag: str, image: Union[np.ndarray, torch.Tensor], writer, frame_dim: int = 0, scale_factor: float = 1.0 + tag: str, image: np.ndarray | torch.Tensor, writer, frame_dim: int = 0, scale_factor: float = 1.0 ): """Function to actually create the animated gif. @@ -68,7 +70,7 @@ def _image3_animated_gif( def make_animated_gif_summary( tag: str, - image: Union[np.ndarray, torch.Tensor], + image: np.ndarray | torch.Tensor, writer=None, max_out: int = 3, frame_dim: int = -3, @@ -93,7 +95,7 @@ def make_animated_gif_summary( summary_op = [] for it_i in range(min(max_out, list(image.shape)[0])): - one_channel_img: Union[torch.Tensor, np.ndarray] = ( + one_channel_img: torch.Tensor | np.ndarray = ( image[it_i, :, :, :].squeeze(dim=0) if isinstance(image, torch.Tensor) else image[it_i, :, :, :] ) summary_op.append( @@ -105,11 +107,11 @@ def make_animated_gif_summary( def add_animated_gif( writer, tag: str, - image_tensor: Union[np.ndarray, torch.Tensor], + image_tensor: np.ndarray | torch.Tensor, max_out: int = 3, frame_dim: int = -3, scale_factor: float = 1.0, - global_step: Optional[int] = None, + global_step: int | None = None, ) -> None: """Creates an animated gif out of an image tensor in 'CHWD' format and writes it with SummaryWriter. @@ -133,7 +135,7 @@ def add_animated_gif( def plot_2d_or_3d_image( - data: Union[NdarrayTensor, List[NdarrayTensor]], + data: NdarrayTensor | list[NdarrayTensor], step: int, writer, index: int = 0, diff --git a/monai/visualize/occlusion_sensitivity.py b/monai/visualize/occlusion_sensitivity.py index 61413c038c..91848a9829 100644 --- a/monai/visualize/occlusion_sensitivity.py +++ b/monai/visualize/occlusion_sensitivity.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from collections.abc import Sequence -from typing import Callable, Optional, Tuple, Union +from __future__ import annotations + +from collections.abc import Callable, Sequence import numpy as np import torch @@ -84,16 +85,16 @@ class OcclusionSensitivity: def __init__( self, nn_module: nn.Module, - pad_val: Optional[float] = None, - mask_size: Union[int, Sequence] = 16, + pad_val: float | None = None, + mask_size: int | Sequence = 16, n_batch: int = 16, - stride: Union[int, Sequence] = 1, + stride: int | Sequence = 1, per_channel: bool = True, - upsampler: Optional[Callable] = default_upsampler, + upsampler: Callable | None = default_upsampler, verbose: bool = True, - mode: Union[str, float, Callable] = "gaussian", + mode: str | float | Callable = "gaussian", overlap: float = 0.25, - activate: Union[bool, Callable] = True, + activate: bool | Callable = True, ) -> None: """ Occlusion sensitivity constructor. @@ -132,13 +133,13 @@ def __init__( self.mode = mode @staticmethod - def constant_occlusion(x: torch.Tensor, val: float, mask_size: Sequence) -> Tuple[float, torch.Tensor]: + def constant_occlusion(x: torch.Tensor, val: float, mask_size: Sequence) -> tuple[float, torch.Tensor]: """Occlude with a constant occlusion. Multiplicative is zero, additive is constant value.""" ones = torch.ones((*x.shape[:2], *mask_size), device=x.device, dtype=x.dtype) return 0, ones * val @staticmethod - def gaussian_occlusion(x: torch.Tensor, mask_size, sigma=0.25) -> Tuple[torch.Tensor, float]: + def gaussian_occlusion(x: torch.Tensor, mask_size, sigma=0.25) -> tuple[torch.Tensor, float]: """ For Gaussian occlusion, Multiplicative is 1-Gaussian, additive is zero. Default sigma of 0.25 empirically shown to give reasonable kernel, see here: @@ -165,11 +166,11 @@ def predictor( cropped_grid: torch.Tensor, nn_module: nn.Module, x: torch.Tensor, - mul: Union[torch.Tensor, float], - add: Union[torch.Tensor, float], + mul: torch.Tensor | float, + add: torch.Tensor | float, mask_size: Sequence, occ_mode: str, - activate: Union[bool, Callable], + activate: bool | Callable, module_kwargs, ) -> torch.Tensor: """ @@ -239,7 +240,7 @@ def predictor( @staticmethod def crop_meshgrid( grid: MetaTensor, b_box: Sequence, mask_size: Sequence - ) -> Tuple[MetaTensor, SpatialCrop, Sequence]: + ) -> tuple[MetaTensor, SpatialCrop, Sequence]: """Crop the meshgrid so we only perform occlusion sensitivity on a subsection of the image.""" # distance from center of mask to edge is -1 // 2. mask_edge = [(m - 1) // 2 for m in mask_size] @@ -263,9 +264,7 @@ def crop_meshgrid( mask_size[i] = min(s, mask_size[i]) return cropped, cropper, mask_size - def __call__( - self, x: torch.Tensor, b_box: Optional[Sequence] = None, **kwargs - ) -> Tuple[torch.Tensor, torch.Tensor]: + def __call__(self, x: torch.Tensor, b_box: Sequence | None = None, **kwargs) -> tuple[torch.Tensor, torch.Tensor]: """ Args: x: Image to use for inference. Should be a tensor consisting of 1 batch. @@ -311,8 +310,8 @@ def __call__( raise ValueError(f"Image (spatial shape) {grid.shape[2:]} should be bigger than mask {mask_size}.") # get additive and multiplicative factors if they are unchanged for all patches (i.e., not mean_patch) - add: Optional[Union[float, torch.Tensor]] - mul: Optional[Union[float, torch.Tensor]] + add: float | torch.Tensor | None + mul: float | torch.Tensor | None # multiply by 0, add value if isinstance(self.mode, float): mul, add = self.constant_occlusion(x, self.mode, mask_size) diff --git a/monai/visualize/utils.py b/monai/visualize/utils.py index e722a1f0c5..5137602072 100644 --- a/monai/visualize/utils.py +++ b/monai/visualize/utils.py @@ -9,7 +9,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Union +from __future__ import annotations import numpy as np import torch @@ -30,11 +30,11 @@ def matshow3d( volume, fig=None, - title: Optional[str] = None, + title: str | None = None, figsize=(10, 10), - frames_per_row: Optional[int] = None, + frames_per_row: int | None = None, frame_dim: int = -3, - channel_dim: Optional[int] = None, + channel_dim: int | None = None, vmin=None, vmax=None, every_n: int = 1, @@ -160,7 +160,7 @@ def matshow3d( def blend_images( image: NdarrayOrTensor, label: NdarrayOrTensor, - alpha: Union[float, NdarrayOrTensor] = 0.5, + alpha: float | NdarrayOrTensor = 0.5, cmap: str = "hsv", rescale_arrays: bool = True, transparent_background: bool = True, diff --git a/monai/visualize/visualizer.py b/monai/visualize/visualizer.py index 05ebb2e280..a6baa53ce1 100644 --- a/monai/visualize/visualizer.py +++ b/monai/visualize/visualizer.py @@ -9,7 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable +from __future__ import annotations + +from collections.abc import Callable import torch import torch.nn.functional as F diff --git a/setup.cfg b/setup.cfg index 095d77c91f..af63441288 100644 --- a/setup.cfg +++ b/setup.cfg @@ -160,6 +160,8 @@ profile = black line_length = 120 skip = .git, .eggs, venv, .venv, versioneer.py, _version.py, conf.py, monai/__init__.py skip_glob = *.pyi +add_imports = from __future__ import annotations +append_only = true [versioneer] VCS = git diff --git a/setup.py b/setup.py index faf63a9246..b90d9d0976 100644 --- a/setup.py +++ b/setup.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import glob import os import re diff --git a/tests/__init__.py b/tests/__init__.py index 0d6e28a679..58422f803e 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import unittest import warnings diff --git a/tests/clang_format_utils.py b/tests/clang_format_utils.py index 1b13ce0ac3..11483e957d 100644 --- a/tests/clang_format_utils.py +++ b/tests/clang_format_utils.py @@ -12,6 +12,8 @@ # this file is adapted from # github/pytorch/pytorch/blob/63d62d3e44a0a4ec09d94f30381d49b78cc5b095/tools/clang_format_utils.py +from __future__ import annotations + import os import platform import stat diff --git a/tests/croppers.py b/tests/croppers.py index 8f78249d90..2229dd2b1f 100644 --- a/tests/croppers.py +++ b/tests/croppers.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/hvd_evenly_divisible_all_gather.py b/tests/hvd_evenly_divisible_all_gather.py index a79038f4be..c7baac2bc9 100644 --- a/tests/hvd_evenly_divisible_all_gather.py +++ b/tests/hvd_evenly_divisible_all_gather.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import torch from monai.utils import evenly_divisible_all_gather diff --git a/tests/min_tests.py b/tests/min_tests.py index e5d5dac41d..9a705f0c28 100644 --- a/tests/min_tests.py +++ b/tests/min_tests.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import glob import os import sys diff --git a/tests/ngc_bundle_download.py b/tests/ngc_bundle_download.py index 2b376c3c2d..f380626d73 100644 --- a/tests/ngc_bundle_download.py +++ b/tests/ngc_bundle_download.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import sys import tempfile diff --git a/tests/padders.py b/tests/padders.py index 367d2059b9..4a4eb1874b 100644 --- a/tests/padders.py +++ b/tests/padders.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import List import numpy as np import torch @@ -22,7 +23,7 @@ MODES = [] # Test modes -NP_MODES: List = [ +NP_MODES: list = [ "constant", "edge", # `reflect` mode is not supported in some PyTorch versions, skip the test diff --git a/tests/profile_subclass/cprofile_profiling.py b/tests/profile_subclass/cprofile_profiling.py index 0befa0f450..a40b54def6 100644 --- a/tests/profile_subclass/cprofile_profiling.py +++ b/tests/profile_subclass/cprofile_profiling.py @@ -12,6 +12,8 @@ Profiling MetaTensor """ +from __future__ import annotations + import cProfile import torch diff --git a/tests/profile_subclass/min_classes.py b/tests/profile_subclass/min_classes.py index 702ba73e21..7104ffcd59 100644 --- a/tests/profile_subclass/min_classes.py +++ b/tests/profile_subclass/min_classes.py @@ -13,6 +13,8 @@ Adapted from https://github.com/pytorch/pytorch/tree/v1.11.0/benchmarks/overrides_benchmark """ +from __future__ import annotations + import torch __all__ = ["SubTensor", "SubWithTorchFunc"] diff --git a/tests/profile_subclass/profiling.py b/tests/profile_subclass/profiling.py index 46047b619c..ffa6a8b17d 100644 --- a/tests/profile_subclass/profiling.py +++ b/tests/profile_subclass/profiling.py @@ -12,6 +12,8 @@ Comparing torch.Tensor, SubTensor, SubWithTorchFunc, MetaTensor Adapted from https://github.com/pytorch/pytorch/tree/v1.11.0/benchmarks/overrides_benchmark """ +from __future__ import annotations + import argparse import torch diff --git a/tests/profile_subclass/pyspy_profiling.py b/tests/profile_subclass/pyspy_profiling.py index 1caeee69e7..c1b0963ba9 100644 --- a/tests/profile_subclass/pyspy_profiling.py +++ b/tests/profile_subclass/pyspy_profiling.py @@ -12,6 +12,8 @@ To be used with py-spy, comparing torch.Tensor, SubTensor, SubWithTorchFunc, MetaTensor Adapted from https://github.com/pytorch/pytorch/tree/v1.11.0/benchmarks/overrides_benchmark """ +from __future__ import annotations + import argparse import torch diff --git a/tests/runner.py b/tests/runner.py index 7356581365..96a1d4a5c4 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import argparse import glob import inspect diff --git a/tests/test_acn_block.py b/tests/test_acn_block.py index 4c12155fd8..2f3783cbb8 100644 --- a/tests/test_acn_block.py +++ b/tests/test_acn_block.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_activations.py b/tests/test_activations.py index 503ca0a350..476b44ab6f 100644 --- a/tests/test_activations.py +++ b/tests/test_activations.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_activationsd.py b/tests/test_activationsd.py index a8f8f600a4..22a275997c 100644 --- a/tests/test_activationsd.py +++ b/tests/test_activationsd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_adaptors.py b/tests/test_adaptors.py index f59bdaa15e..257c4346ad 100644 --- a/tests/test_adaptors.py +++ b/tests/test_adaptors.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import itertools import unittest diff --git a/tests/test_add_channeld.py b/tests/test_add_channeld.py index 9dc984aff3..0c0a1ffa49 100644 --- a/tests/test_add_channeld.py +++ b/tests/test_add_channeld.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_add_coordinate_channels.py b/tests/test_add_coordinate_channels.py index 5a483e25b9..cd33f98fd5 100644 --- a/tests/test_add_coordinate_channels.py +++ b/tests/test_add_coordinate_channels.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_add_coordinate_channelsd.py b/tests/test_add_coordinate_channelsd.py index c14ff0ba64..f5784928fd 100644 --- a/tests/test_add_coordinate_channelsd.py +++ b/tests/test_add_coordinate_channelsd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_add_extreme_points_channel.py b/tests/test_add_extreme_points_channel.py index 116f96126f..140caa34ba 100644 --- a/tests/test_add_extreme_points_channel.py +++ b/tests/test_add_extreme_points_channel.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_add_extreme_points_channeld.py b/tests/test_add_extreme_points_channeld.py index f9837e9ef4..5640e696fc 100644 --- a/tests/test_add_extreme_points_channeld.py +++ b/tests/test_add_extreme_points_channeld.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_adjust_contrast.py b/tests/test_adjust_contrast.py index 1c38d0edf3..c239f43346 100644 --- a/tests/test_adjust_contrast.py +++ b/tests/test_adjust_contrast.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_adjust_contrastd.py b/tests/test_adjust_contrastd.py index 2d674c6003..6de2658a5b 100644 --- a/tests/test_adjust_contrastd.py +++ b/tests/test_adjust_contrastd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_adn.py b/tests/test_adn.py index ffd28f8fc8..27e23a08d3 100644 --- a/tests/test_adn.py +++ b/tests/test_adn.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_affine.py b/tests/test_affine.py index 019a8f59a4..df38b885aa 100644 --- a/tests/test_affine.py +++ b/tests/test_affine.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_affine_grid.py b/tests/test_affine_grid.py index 23651c8b6b..f3febbe0f3 100644 --- a/tests/test_affine_grid.py +++ b/tests/test_affine_grid.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_affine_transform.py b/tests/test_affine_transform.py index 902437350a..7d16808bc1 100644 --- a/tests/test_affine_transform.py +++ b/tests/test_affine_transform.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_affined.py b/tests/test_affined.py index b922d80fb5..502026ac05 100644 --- a/tests/test_affined.py +++ b/tests/test_affined.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_ahnet.py b/tests/test_ahnet.py index dba8eaf72b..5707cf0452 100644 --- a/tests/test_ahnet.py +++ b/tests/test_ahnet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_alias.py b/tests/test_alias.py index 49f9fa56fe..e2dd8bcf26 100644 --- a/tests/test_alias.py +++ b/tests/test_alias.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import glob import inspect import os diff --git a/tests/test_anchor_box.py b/tests/test_anchor_box.py index a6abbc0200..3d53d79155 100644 --- a/tests/test_anchor_box.py +++ b/tests/test_anchor_box.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_apply.py b/tests/test_apply.py index f9e8a4a1eb..8974360381 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_apply_filter.py b/tests/test_apply_filter.py index 62372516a5..0de77bfb4d 100644 --- a/tests/test_apply_filter.py +++ b/tests/test_apply_filter.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_arraydataset.py b/tests/test_arraydataset.py index 380a6372b2..72d62008c4 100644 --- a/tests/test_arraydataset.py +++ b/tests/test_arraydataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import sys import tempfile diff --git a/tests/test_as_channel_first.py b/tests/test_as_channel_first.py index 732c559a1a..3bf4e877ab 100644 --- a/tests/test_as_channel_first.py +++ b/tests/test_as_channel_first.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_as_channel_firstd.py b/tests/test_as_channel_firstd.py index 91086f9299..bd50a96f5d 100644 --- a/tests/test_as_channel_firstd.py +++ b/tests/test_as_channel_firstd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_as_channel_last.py b/tests/test_as_channel_last.py index e6446ab7a6..8f88fb2928 100644 --- a/tests/test_as_channel_last.py +++ b/tests/test_as_channel_last.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_as_channel_lastd.py b/tests/test_as_channel_lastd.py index a6d94d216a..16086b769c 100644 --- a/tests/test_as_channel_lastd.py +++ b/tests/test_as_channel_lastd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_as_discrete.py b/tests/test_as_discrete.py index 014e439fe1..2802c7d9ff 100644 --- a/tests/test_as_discrete.py +++ b/tests/test_as_discrete.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_as_discreted.py b/tests/test_as_discreted.py index dc96a4218b..ec394fc3af 100644 --- a/tests/test_as_discreted.py +++ b/tests/test_as_discreted.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_atss_box_matcher.py b/tests/test_atss_box_matcher.py index 093641bb2f..a614497bc9 100644 --- a/tests/test_atss_box_matcher.py +++ b/tests/test_atss_box_matcher.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_attentionunet.py b/tests/test_attentionunet.py index 1ecd75c166..d5c67cee38 100644 --- a/tests/test_attentionunet.py +++ b/tests/test_attentionunet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_auto3dseg.py b/tests/test_auto3dseg.py index 74c45f6fec..383d55b8a3 100644 --- a/tests/test_auto3dseg.py +++ b/tests/test_auto3dseg.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_auto3dseg_ensemble.py b/tests/test_auto3dseg_ensemble.py index d90ea739e9..8752b0aed1 100644 --- a/tests/test_auto3dseg_ensemble.py +++ b/tests/test_auto3dseg_ensemble.py @@ -9,10 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest -from typing import Dict, List import nibabel as nib import numpy as np @@ -27,7 +28,7 @@ _, has_tb = optional_import("torch.utils.tensorboard", name="SummaryWriter") -fake_datalist: Dict[str, List[Dict]] = { +fake_datalist: dict[str, list[dict]] = { "testing": [{"image": "val_001.fake.nii.gz"}, {"image": "val_002.fake.nii.gz"}], "training": [ {"fold": 0, "image": "tr_image_001.fake.nii.gz", "label": "tr_label_001.fake.nii.gz"}, diff --git a/tests/test_auto3dseg_hpo.py b/tests/test_auto3dseg_hpo.py index 6ff906019b..0b65adbd70 100644 --- a/tests/test_auto3dseg_hpo.py +++ b/tests/test_auto3dseg_hpo.py @@ -9,11 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest from functools import partial -from typing import Dict, List import nibabel as nib import numpy as np @@ -49,7 +50,7 @@ def skip_if_no_optuna(obj): return unittest.skipUnless(has_optuna, "Skipping optuna tests")(obj) -fake_datalist: Dict[str, List[Dict]] = { +fake_datalist: dict[str, list[dict]] = { "testing": [{"image": "val_001.fake.nii.gz"}, {"image": "val_002.fake.nii.gz"}], "training": [ {"fold": 0, "image": "tr_image_001.fake.nii.gz", "label": "tr_label_001.fake.nii.gz"}, diff --git a/tests/test_autoencoder.py b/tests/test_autoencoder.py index bed5a198ff..485049c2d1 100644 --- a/tests/test_autoencoder.py +++ b/tests/test_autoencoder.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_basic_unet.py b/tests/test_basic_unet.py index a4f88367dd..23e19dd536 100644 --- a/tests/test_basic_unet.py +++ b/tests/test_basic_unet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_basic_unetplusplus.py b/tests/test_basic_unetplusplus.py index 3bca65676a..19ed5977fd 100644 --- a/tests/test_basic_unetplusplus.py +++ b/tests/test_basic_unetplusplus.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_bending_energy.py b/tests/test_bending_energy.py index 18f88ba759..f29d4f256b 100644 --- a/tests/test_bending_energy.py +++ b/tests/test_bending_energy.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_bilateral_approx_cpu.py b/tests/test_bilateral_approx_cpu.py index b3f4f5c3be..04dd39b227 100644 --- a/tests/test_bilateral_approx_cpu.py +++ b/tests/test_bilateral_approx_cpu.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_bilateral_approx_cuda.py b/tests/test_bilateral_approx_cuda.py index db34b0ff71..6ae3133719 100644 --- a/tests/test_bilateral_approx_cuda.py +++ b/tests/test_bilateral_approx_cuda.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_bilateral_precise.py b/tests/test_bilateral_precise.py index b19369d758..1d2a5918d8 100644 --- a/tests/test_bilateral_precise.py +++ b/tests/test_bilateral_precise.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_blend_images.py b/tests/test_blend_images.py index 341b79b949..9814a5a3f8 100644 --- a/tests/test_blend_images.py +++ b/tests/test_blend_images.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from unittest.case import skipUnless diff --git a/tests/test_border_pad.py b/tests/test_border_pad.py index 1194ae49a6..2c368fa8cc 100644 --- a/tests/test_border_pad.py +++ b/tests/test_border_pad.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_border_padd.py b/tests/test_border_padd.py index ca55c8b09d..b48276f0e3 100644 --- a/tests/test_border_padd.py +++ b/tests/test_border_padd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_bounding_rect.py b/tests/test_bounding_rect.py index a7c2648f1e..b9c232e2d2 100644 --- a/tests/test_bounding_rect.py +++ b/tests/test_bounding_rect.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_bounding_rectd.py b/tests/test_bounding_rectd.py index 47ed854263..248a0a8e47 100644 --- a/tests/test_bounding_rectd.py +++ b/tests/test_bounding_rectd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_box_coder.py b/tests/test_box_coder.py index 86ca7a98c2..5835341139 100644 --- a/tests/test_box_coder.py +++ b/tests/test_box_coder.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_box_transform.py b/tests/test_box_transform.py index d597ec76bb..94bd6ade52 100644 --- a/tests/test_box_transform.py +++ b/tests/test_box_transform.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_box_utils.py b/tests/test_box_utils.py index 8c56783c3b..c4fefb5a98 100644 --- a/tests/test_box_utils.py +++ b/tests/test_box_utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_bundle_ckpt_export.py b/tests/test_bundle_ckpt_export.py index e5847c57ab..a3130cefbd 100644 --- a/tests/test_bundle_ckpt_export.py +++ b/tests/test_bundle_ckpt_export.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import json import os import tempfile diff --git a/tests/test_bundle_download.py b/tests/test_bundle_download.py index 12f15d3b41..52aa515111 100644 --- a/tests/test_bundle_download.py +++ b/tests/test_bundle_download.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import json import os import tempfile diff --git a/tests/test_bundle_get_data.py b/tests/test_bundle_get_data.py index c36409f724..a560f3945f 100644 --- a/tests/test_bundle_get_data.py +++ b/tests/test_bundle_get_data.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_bundle_init_bundle.py b/tests/test_bundle_init_bundle.py index f702401481..08f921da01 100644 --- a/tests/test_bundle_init_bundle.py +++ b/tests/test_bundle_init_bundle.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_bundle_utils.py b/tests/test_bundle_utils.py index 0fbfae5094..9d28903f2f 100644 --- a/tests/test_bundle_utils.py +++ b/tests/test_bundle_utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import tempfile diff --git a/tests/test_bundle_verify_metadata.py b/tests/test_bundle_verify_metadata.py index 773cb40888..0701e905b9 100644 --- a/tests/test_bundle_verify_metadata.py +++ b/tests/test_bundle_verify_metadata.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import json import os import tempfile diff --git a/tests/test_bundle_verify_net.py b/tests/test_bundle_verify_net.py index a978f825b9..bd611323ac 100644 --- a/tests/test_bundle_verify_net.py +++ b/tests/test_bundle_verify_net.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_cachedataset.py b/tests/test_cachedataset.py index 6ddeb95ea5..dcae5fdce1 100644 --- a/tests/test_cachedataset.py +++ b/tests/test_cachedataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import sys import tempfile diff --git a/tests/test_cachedataset_parallel.py b/tests/test_cachedataset_parallel.py index 6279207f23..c3fc2cc362 100644 --- a/tests/test_cachedataset_parallel.py +++ b/tests/test_cachedataset_parallel.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_cachedataset_persistent_workers.py b/tests/test_cachedataset_persistent_workers.py index 7f241899eb..e60862238d 100644 --- a/tests/test_cachedataset_persistent_workers.py +++ b/tests/test_cachedataset_persistent_workers.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.data import CacheDataset, DataLoader, create_test_image_2d diff --git a/tests/test_cachentransdataset.py b/tests/test_cachentransdataset.py index bbfa0d39c1..d50fe4f8dd 100644 --- a/tests/test_cachentransdataset.py +++ b/tests/test_cachentransdataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_call_dist.py b/tests/test_call_dist.py index bed8289506..0621824b65 100644 --- a/tests/test_call_dist.py +++ b/tests/test_call_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from tests.utils import DistCall, DistTestCase diff --git a/tests/test_cast_to_type.py b/tests/test_cast_to_type.py index 82daabc4e7..070549edc8 100644 --- a/tests/test_cast_to_type.py +++ b/tests/test_cast_to_type.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_cast_to_typed.py b/tests/test_cast_to_typed.py index 1ac23314a5..687deeda1d 100644 --- a/tests/test_cast_to_typed.py +++ b/tests/test_cast_to_typed.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_center_scale_crop.py b/tests/test_center_scale_crop.py index 3fe7a453d3..3d1087ba60 100644 --- a/tests/test_center_scale_crop.py +++ b/tests/test_center_scale_crop.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_center_scale_cropd.py b/tests/test_center_scale_cropd.py index 088c1c70e7..62f7541862 100644 --- a/tests/test_center_scale_cropd.py +++ b/tests/test_center_scale_cropd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_center_spatial_crop.py b/tests/test_center_spatial_crop.py index 7b5b19107d..9215f57381 100644 --- a/tests/test_center_spatial_crop.py +++ b/tests/test_center_spatial_crop.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_center_spatial_cropd.py b/tests/test_center_spatial_cropd.py index fa7bc8c8fa..b9370acb34 100644 --- a/tests/test_center_spatial_cropd.py +++ b/tests/test_center_spatial_cropd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_channel_pad.py b/tests/test_channel_pad.py index bde0f18d83..2d8c57fd68 100644 --- a/tests/test_channel_pad.py +++ b/tests/test_channel_pad.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_check_hash.py b/tests/test_check_hash.py index 5297021540..bb3d0ff12e 100644 --- a/tests/test_check_hash.py +++ b/tests/test_check_hash.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_check_missing_files.py b/tests/test_check_missing_files.py index 9ddf4170ec..efbe5a95fb 100644 --- a/tests/test_check_missing_files.py +++ b/tests/test_check_missing_files.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_classes_to_indices.py b/tests/test_classes_to_indices.py index 1f39e0f480..e7dd7abfe5 100644 --- a/tests/test_classes_to_indices.py +++ b/tests/test_classes_to_indices.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_classes_to_indicesd.py b/tests/test_classes_to_indicesd.py index 398620d304..7da321c7d9 100644 --- a/tests/test_classes_to_indicesd.py +++ b/tests/test_classes_to_indicesd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_complex_utils.py b/tests/test_complex_utils.py index 8ba45c294c..77eaa924a2 100644 --- a/tests/test_complex_utils.py +++ b/tests/test_complex_utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_component_locator.py b/tests/test_component_locator.py index ebb2cca7b3..3b54a13706 100644 --- a/tests/test_component_locator.py +++ b/tests/test_component_locator.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from pydoc import locate diff --git a/tests/test_compose.py b/tests/test_compose.py index e322e216ad..ddb7ce25d8 100644 --- a/tests/test_compose.py +++ b/tests/test_compose.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import unittest diff --git a/tests/test_compose_get_number_conversions.py b/tests/test_compose_get_number_conversions.py index fca5bc727d..664558d9cd 100644 --- a/tests/test_compose_get_number_conversions.py +++ b/tests/test_compose_get_number_conversions.py @@ -9,9 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy -from typing import List, Tuple import numpy as np import torch @@ -69,7 +70,7 @@ def __call__(self, x): return _apply(x, lambda x: convert_to_tensor(x).cuda()) -TESTS: List[Tuple] = [] +TESTS: list[tuple] = [] for is_dict in (False, True): # same type depends on input TESTS.append(((N(), N()), is_dict, NP_ARR, 0)) diff --git a/tests/test_compute_confusion_matrix.py b/tests/test_compute_confusion_matrix.py index 56d619a289..060281a71e 100644 --- a/tests/test_compute_confusion_matrix.py +++ b/tests/test_compute_confusion_matrix.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Any, Dict, List +from typing import Any import torch from parameterized import parameterized @@ -25,7 +27,7 @@ _device = "cuda:0" if torch.cuda.is_available() else "cpu" # input data -data: Dict[Any, Any] = { +data: dict[Any, Any] = { "y_pred": torch.tensor( [ [[[0.0, 1.0], [0.0, 0.0]], [[0.0, 0.0], [1.0, 1.0]], [[1.0, 0.0], [0.0, 0.0]]], @@ -42,7 +44,7 @@ ), } -data_nan: Dict[Any, Any] = { +data_nan: dict[Any, Any] = { # confusion matrix:[[[0,1,2,1],[1,1,1,1],[0,1,2,1]], # [[0,0,0,4],[0,0,4,0],[0,4,0,0]], # [[0,0,2,2],[0,0,2,2],[0,4,0,0]]] @@ -62,7 +64,7 @@ ), } -data_clf: Dict[Any, Any] = { +data_clf: dict[Any, Any] = { "y_pred": torch.tensor([[1, 0, 0], [0, 0, 1]]), "y": torch.tensor([[1, 0, 0], [0, 1, 0]]), "compute_sample": False, @@ -146,7 +148,7 @@ result: Any = None for idx, item in enumerate(metric_names): for reduction in ["mean", "mean_batch"]: - TEST_CASE: List[Any] = [data.copy()] + TEST_CASE: list[Any] = [data.copy()] TEST_CASE[0]["compute_sample"] = True TEST_CASE[0]["include_background"] = True TEST_CASE[0]["metric_name"] = item @@ -161,7 +163,7 @@ # one input to compute multiple metrics for reduction in ["mean", "mean_batch"]: - TEST_CASE_MULTIPLE: List[Any] = [data.copy()] + TEST_CASE_MULTIPLE: list[Any] = [data.copy()] TEST_CASE_MULTIPLE[0]["compute_sample"] = True TEST_CASE_MULTIPLE[0]["include_background"] = True TEST_CASE_MULTIPLE[0]["metric_name"] = metric_names diff --git a/tests/test_compute_f_beta.py b/tests/test_compute_f_beta.py index 62641a52f5..e587f2b49d 100644 --- a/tests/test_compute_f_beta.py +++ b/tests/test_compute_f_beta.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy diff --git a/tests/test_compute_froc.py b/tests/test_compute_froc.py index 91c1ea1977..1724c469d5 100644 --- a/tests/test_compute_froc.py +++ b/tests/test_compute_froc.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_compute_generalized_dice.py b/tests/test_compute_generalized_dice.py index 38f6e57d32..2d38e5e0b1 100644 --- a/tests/test_compute_generalized_dice.py +++ b/tests/test_compute_generalized_dice.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_compute_ho_ver_maps.py b/tests/test_compute_ho_ver_maps.py index 5c4674dd04..50598cb57b 100644 --- a/tests/test_compute_ho_ver_maps.py +++ b/tests/test_compute_ho_ver_maps.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_compute_ho_ver_maps_d.py b/tests/test_compute_ho_ver_maps_d.py index 475e50bc70..27bb57988c 100644 --- a/tests/test_compute_ho_ver_maps_d.py +++ b/tests/test_compute_ho_ver_maps_d.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_compute_meandice.py b/tests/test_compute_meandice.py index 4dd5d77c4f..4b74e31847 100644 --- a/tests/test_compute_meandice.py +++ b/tests/test_compute_meandice.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_compute_meaniou.py b/tests/test_compute_meaniou.py index 52a0223a2d..68f87493a2 100644 --- a/tests/test_compute_meaniou.py +++ b/tests/test_compute_meaniou.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_compute_panoptic_quality.py b/tests/test_compute_panoptic_quality.py index cf5d0deb2a..5c32a7412a 100644 --- a/tests/test_compute_panoptic_quality.py +++ b/tests/test_compute_panoptic_quality.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from typing import List diff --git a/tests/test_compute_regression_metrics.py b/tests/test_compute_regression_metrics.py index cab1184812..5cddce7d62 100644 --- a/tests/test_compute_regression_metrics.py +++ b/tests/test_compute_regression_metrics.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from functools import partial diff --git a/tests/test_compute_roc_auc.py b/tests/test_compute_roc_auc.py index 0e57f1fe4a..2f080c76cb 100644 --- a/tests/test_compute_roc_auc.py +++ b/tests/test_compute_roc_auc.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_compute_variance.py b/tests/test_compute_variance.py index 2743fcdc79..6b6df19ac4 100644 --- a/tests/test_compute_variance.py +++ b/tests/test_compute_variance.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_concat_itemsd.py b/tests/test_concat_itemsd.py index c0a058a0dd..322a95d7df 100644 --- a/tests/test_concat_itemsd.py +++ b/tests/test_concat_itemsd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_config_item.py b/tests/test_config_item.py index 817175e1e3..24e1c11929 100644 --- a/tests/test_config_item.py +++ b/tests/test_config_item.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from functools import partial from typing import Callable diff --git a/tests/test_config_parser.py b/tests/test_config_parser.py index 305dae13e2..23e816861f 100644 --- a/tests/test_config_parser.py +++ b/tests/test_config_parser.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_contrastive_loss.py b/tests/test_contrastive_loss.py index d0eb7d86f2..4cafa0a905 100644 --- a/tests/test_contrastive_loss.py +++ b/tests/test_contrastive_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_convert_data_type.py b/tests/test_convert_data_type.py index 139d2e3f87..c3e4490ffe 100644 --- a/tests/test_convert_data_type.py +++ b/tests/test_convert_data_type.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import List, Tuple import numpy as np import torch @@ -20,14 +21,14 @@ from monai.utils.type_conversion import convert_data_type, convert_to_dst_type, get_equivalent_dtype from tests.utils import TEST_NDARRAYS_ALL, assert_allclose -TESTS: List[Tuple] = [] +TESTS: list[tuple] = [] for in_type in TEST_NDARRAYS_ALL + (int, float): for out_type in TEST_NDARRAYS_ALL: TESTS.append((in_type(np.array(1.0)), out_type(np.array(1.0)), None, False)) # type: ignore if in_type is not float: TESTS.append((in_type(np.array(256)), out_type(np.array(255)), np.uint8, True)) # type: ignore -TESTS_LIST: List[Tuple] = [] +TESTS_LIST: list[tuple] = [] for in_type in TEST_NDARRAYS_ALL + (int, float): for out_type in TEST_NDARRAYS_ALL: TESTS_LIST.append( diff --git a/tests/test_convert_to_multi_channel.py b/tests/test_convert_to_multi_channel.py index b606fee04f..78c3c90688 100644 --- a/tests/test_convert_to_multi_channel.py +++ b/tests/test_convert_to_multi_channel.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_convert_to_multi_channeld.py b/tests/test_convert_to_multi_channeld.py index 7525f8d7e2..351adddb13 100644 --- a/tests/test_convert_to_multi_channeld.py +++ b/tests/test_convert_to_multi_channeld.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_convert_to_torchscript.py b/tests/test_convert_to_torchscript.py index a1c1471463..0b8e9a8141 100644 --- a/tests/test_convert_to_torchscript.py +++ b/tests/test_convert_to_torchscript.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_convolutions.py b/tests/test_convolutions.py index dc018248c1..1311401f1d 100644 --- a/tests/test_convolutions.py +++ b/tests/test_convolutions.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.networks.blocks import Convolution, ResidualUnit diff --git a/tests/test_copy_itemsd.py b/tests/test_copy_itemsd.py index 8354f45bb5..ff4799a094 100644 --- a/tests/test_copy_itemsd.py +++ b/tests/test_copy_itemsd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_copy_model_state.py b/tests/test_copy_model_state.py index bc7b116e1f..2e7513b234 100644 --- a/tests/test_copy_model_state.py +++ b/tests/test_copy_model_state.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_correct_crop_centers.py b/tests/test_correct_crop_centers.py index 50478c7d5d..d2a95bf684 100644 --- a/tests/test_correct_crop_centers.py +++ b/tests/test_correct_crop_centers.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_create_cross_validation_datalist.py b/tests/test_create_cross_validation_datalist.py index 3a3e8481ea..d05a94f59e 100644 --- a/tests/test_create_cross_validation_datalist.py +++ b/tests/test_create_cross_validation_datalist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_create_grid_and_affine.py b/tests/test_create_grid_and_affine.py index d70db45468..2b5890a777 100644 --- a/tests/test_create_grid_and_affine.py +++ b/tests/test_create_grid_and_affine.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_crf_cpu.py b/tests/test_crf_cpu.py index 46da3298bc..5f749119e7 100644 --- a/tests/test_crf_cpu.py +++ b/tests/test_crf_cpu.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_crf_cuda.py b/tests/test_crf_cuda.py index ca25fe2de9..0c004b2825 100644 --- a/tests/test_crf_cuda.py +++ b/tests/test_crf_cuda.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_crop_foreground.py b/tests/test_crop_foreground.py index 2f46eba67c..dd967f6ac0 100644 --- a/tests/test_crop_foreground.py +++ b/tests/test_crop_foreground.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_crop_foregroundd.py b/tests/test_crop_foregroundd.py index fa36d2e065..13158ca6c2 100644 --- a/tests/test_crop_foregroundd.py +++ b/tests/test_crop_foregroundd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_cross_validation.py b/tests/test_cross_validation.py index 811dcea026..33d060560c 100644 --- a/tests/test_cross_validation.py +++ b/tests/test_cross_validation.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_csv_dataset.py b/tests/test_csv_dataset.py index f288ac4b95..82a0f7afbd 100644 --- a/tests/test_csv_dataset.py +++ b/tests/test_csv_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_csv_iterable_dataset.py b/tests/test_csv_iterable_dataset.py index d6b84074ba..65a0a420a5 100644 --- a/tests/test_csv_iterable_dataset.py +++ b/tests/test_csv_iterable_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import sys import tempfile diff --git a/tests/test_csv_saver.py b/tests/test_csv_saver.py index 01796da00c..833d1134cf 100644 --- a/tests/test_csv_saver.py +++ b/tests/test_csv_saver.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import csv import os import tempfile diff --git a/tests/test_cucim_dict_transform.py b/tests/test_cucim_dict_transform.py index 4a6d2f9d51..6ebfd8bac7 100644 --- a/tests/test_cucim_dict_transform.py +++ b/tests/test_cucim_dict_transform.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_cucim_transform.py b/tests/test_cucim_transform.py index dd73ad94c0..5884358a74 100644 --- a/tests/test_cucim_transform.py +++ b/tests/test_cucim_transform.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_cumulative.py b/tests/test_cumulative.py index 16f5c1d1f5..3377fa815c 100644 --- a/tests/test_cumulative.py +++ b/tests/test_cumulative.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_cumulative_average.py b/tests/test_cumulative_average.py index 543433a6d3..2373b0511c 100644 --- a/tests/test_cumulative_average.py +++ b/tests/test_cumulative_average.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_cumulative_average_dist.py b/tests/test_cumulative_average_dist.py index a5ee2fed15..9f45955544 100644 --- a/tests/test_cumulative_average_dist.py +++ b/tests/test_cumulative_average_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_cv2_dist.py b/tests/test_cv2_dist.py index ac37a4c784..edd2e1ec42 100644 --- a/tests/test_cv2_dist.py +++ b/tests/test_cv2_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_data_stats.py b/tests/test_data_stats.py index 2b652f8f62..6ef51bef92 100644 --- a/tests/test_data_stats.py +++ b/tests/test_data_stats.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import os import sys diff --git a/tests/test_data_statsd.py b/tests/test_data_statsd.py index 9c878addf5..374bc815ac 100644 --- a/tests/test_data_statsd.py +++ b/tests/test_data_statsd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import os import sys diff --git a/tests/test_dataloader.py b/tests/test_dataloader.py index b75c2a4ed8..2ee69687a6 100644 --- a/tests/test_dataloader.py +++ b/tests/test_dataloader.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import unittest diff --git a/tests/test_dataset.py b/tests/test_dataset.py index 449ba94c63..667595caa4 100644 --- a/tests/test_dataset.py +++ b/tests/test_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_dataset_func.py b/tests/test_dataset_func.py index b5871d7de1..afccd129fe 100644 --- a/tests/test_dataset_func.py +++ b/tests/test_dataset_func.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import json import os import tempfile diff --git a/tests/test_dataset_summary.py b/tests/test_dataset_summary.py index a5b5eee28f..746c3d79cf 100644 --- a/tests/test_dataset_summary.py +++ b/tests/test_dataset_summary.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import glob import os import tempfile diff --git a/tests/test_decathlondataset.py b/tests/test_decathlondataset.py index b3844484ce..2e3a3cf541 100644 --- a/tests/test_decathlondataset.py +++ b/tests/test_decathlondataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import unittest diff --git a/tests/test_decollate.py b/tests/test_decollate.py index 538eb38311..ba7d74eb6c 100644 --- a/tests/test_decollate.py +++ b/tests/test_decollate.py @@ -9,10 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import unittest from enum import Enum -from typing import List, Tuple import numpy as np import torch @@ -45,12 +46,12 @@ KEYS = ["image"] -TESTS_DICT: List[Tuple] = [] +TESTS_DICT: list[tuple] = [] TESTS_DICT.append((SpatialPadd(KEYS, 150), RandFlipd(KEYS, prob=1.0, spatial_axis=1))) TESTS_DICT.append((RandRotate90d(KEYS, prob=0.0, max_k=1),)) TESTS_DICT.append((RandAffined(KEYS, prob=0.0, translate_range=10),)) -TESTS_LIST: List[Tuple] = [] +TESTS_LIST: list[tuple] = [] TESTS_LIST.append((SpatialPad(150), RandFlip(prob=1.0, spatial_axis=1))) TESTS_LIST.append((RandRotate90(prob=0.0, max_k=1),)) TESTS_LIST.append((RandAffine(prob=0.0, translate_range=10),)) diff --git a/tests/test_deepedit_interaction.py b/tests/test_deepedit_interaction.py index 42fd87607d..5dcc6205f7 100644 --- a/tests/test_deepedit_interaction.py +++ b/tests/test_deepedit_interaction.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_deepedit_transforms.py b/tests/test_deepedit_transforms.py index f608a4342f..7f4d4eee1e 100644 --- a/tests/test_deepedit_transforms.py +++ b/tests/test_deepedit_transforms.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_deepgrow_dataset.py b/tests/test_deepgrow_dataset.py index 256aedcd6d..d8a412ade9 100644 --- a/tests/test_deepgrow_dataset.py +++ b/tests/test_deepgrow_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import tempfile diff --git a/tests/test_deepgrow_interaction.py b/tests/test_deepgrow_interaction.py index b040348b62..7cdbeed9f9 100644 --- a/tests/test_deepgrow_interaction.py +++ b/tests/test_deepgrow_interaction.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_deepgrow_transforms.py b/tests/test_deepgrow_transforms.py index bd20b45b6d..1328e13439 100644 --- a/tests/test_deepgrow_transforms.py +++ b/tests/test_deepgrow_transforms.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_delete_itemsd.py b/tests/test_delete_itemsd.py index 99d05fe787..1ec77f29fd 100644 --- a/tests/test_delete_itemsd.py +++ b/tests/test_delete_itemsd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import time import unittest diff --git a/tests/test_denseblock.py b/tests/test_denseblock.py index 3a3f61860e..c14ca2ae7a 100644 --- a/tests/test_denseblock.py +++ b/tests/test_denseblock.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch.nn as nn diff --git a/tests/test_densenet.py b/tests/test_densenet.py index 66f27cba51..8354237a25 100644 --- a/tests/test_densenet.py +++ b/tests/test_densenet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from typing import TYPE_CHECKING from unittest import skipUnless diff --git a/tests/test_deprecated.py b/tests/test_deprecated.py index 286ec4f8a5..1aac97e804 100644 --- a/tests/test_deprecated.py +++ b/tests/test_deprecated.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import warnings diff --git a/tests/test_detect_envelope.py b/tests/test_detect_envelope.py index 5eea0c8653..105d3a4ace 100644 --- a/tests/test_detect_envelope.py +++ b/tests/test_detect_envelope.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_detection_coco_metrics.py b/tests/test_detection_coco_metrics.py index b139377511..780031ee0c 100644 --- a/tests/test_detection_coco_metrics.py +++ b/tests/test_detection_coco_metrics.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import random import unittest diff --git a/tests/test_detector_boxselector.py b/tests/test_detector_boxselector.py index 6e22a7833a..8cc9b15911 100644 --- a/tests/test_detector_boxselector.py +++ b/tests/test_detector_boxselector.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_detector_utils.py b/tests/test_detector_utils.py index b8ae390016..41716934b5 100644 --- a/tests/test_detector_utils.py +++ b/tests/test_detector_utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import random import unittest diff --git a/tests/test_dev_collate.py b/tests/test_dev_collate.py index 83dbd71d28..97028f2597 100644 --- a/tests/test_dev_collate.py +++ b/tests/test_dev_collate.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import unittest diff --git a/tests/test_dice_ce_loss.py b/tests/test_dice_ce_loss.py index 1f43dd8c9a..13b4952ab3 100644 --- a/tests/test_dice_ce_loss.py +++ b/tests/test_dice_ce_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_dice_focal_loss.py b/tests/test_dice_focal_loss.py index af3e868654..ee5b49f456 100644 --- a/tests/test_dice_focal_loss.py +++ b/tests/test_dice_focal_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_dice_loss.py b/tests/test_dice_loss.py index 223b09e624..e7f64ccfb3 100644 --- a/tests/test_dice_loss.py +++ b/tests/test_dice_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_dints_cell.py b/tests/test_dints_cell.py index a5da39bae9..21cef39d68 100644 --- a/tests/test_dints_cell.py +++ b/tests/test_dints_cell.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_dints_mixop.py b/tests/test_dints_mixop.py index b686069173..09d2e7a423 100644 --- a/tests/test_dints_mixop.py +++ b/tests/test_dints_mixop.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_dints_network.py b/tests/test_dints_network.py index 08e75fab98..09059144be 100644 --- a/tests/test_dints_network.py +++ b/tests/test_dints_network.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_discriminator.py b/tests/test_discriminator.py index aa9b9720c4..62635e286e 100644 --- a/tests/test_discriminator.py +++ b/tests/test_discriminator.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_divisible_pad.py b/tests/test_divisible_pad.py index df610c4939..24a03b53a2 100644 --- a/tests/test_divisible_pad.py +++ b/tests/test_divisible_pad.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_divisible_padd.py b/tests/test_divisible_padd.py index 93e5a879f0..1b62d27f3e 100644 --- a/tests/test_divisible_padd.py +++ b/tests/test_divisible_padd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_download_and_extract.py b/tests/test_download_and_extract.py index e6045cada9..696bcfc78f 100644 --- a/tests/test_download_and_extract.py +++ b/tests/test_download_and_extract.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_downsample_block.py b/tests/test_downsample_block.py index ac2acb0845..cd40be4306 100644 --- a/tests/test_downsample_block.py +++ b/tests/test_downsample_block.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_drop_path.py b/tests/test_drop_path.py index f8ea454228..ab2150e548 100644 --- a/tests/test_drop_path.py +++ b/tests/test_drop_path.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_ds_loss.py b/tests/test_ds_loss.py index dc67b651a3..51200d9584 100644 --- a/tests/test_ds_loss.py +++ b/tests/test_ds_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_dvf2ddf.py b/tests/test_dvf2ddf.py index d061cca7ff..f18b5b7297 100644 --- a/tests/test_dvf2ddf.py +++ b/tests/test_dvf2ddf.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_dynunet.py b/tests/test_dynunet.py index e14d427640..c37158ded4 100644 --- a/tests/test_dynunet.py +++ b/tests/test_dynunet.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Any, Sequence, Union +from typing import Any, Sequence import torch from parameterized import parameterized @@ -21,7 +23,7 @@ device = "cuda" if torch.cuda.is_available() else "cpu" -strides: Sequence[Union[Sequence[int], int]] +strides: Sequence[Sequence[int] | int] kernel_size: Sequence[Any] expected_shape: Sequence[Any] diff --git a/tests/test_dynunet_block.py b/tests/test_dynunet_block.py index 1c83552766..b34ccb31ba 100644 --- a/tests/test_dynunet_block.py +++ b/tests/test_dynunet_block.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_efficientnet.py b/tests/test_efficientnet.py index d56f901af7..e67defa4a3 100644 --- a/tests/test_efficientnet.py +++ b/tests/test_efficientnet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from typing import TYPE_CHECKING diff --git a/tests/test_ensemble_evaluator.py b/tests/test_ensemble_evaluator.py index 15ea6a0952..40a4a72dd5 100644 --- a/tests/test_ensemble_evaluator.py +++ b/tests/test_ensemble_evaluator.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_ensure_channel_first.py b/tests/test_ensure_channel_first.py index d8dba562bb..027b18b7dd 100644 --- a/tests/test_ensure_channel_first.py +++ b/tests/test_ensure_channel_first.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_ensure_channel_firstd.py b/tests/test_ensure_channel_firstd.py index 44bb7e40f4..08e2709641 100644 --- a/tests/test_ensure_channel_firstd.py +++ b/tests/test_ensure_channel_firstd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_ensure_tuple.py b/tests/test_ensure_tuple.py index ea580871da..3d0ef40dd3 100644 --- a/tests/test_ensure_tuple.py +++ b/tests/test_ensure_tuple.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_ensure_type.py b/tests/test_ensure_type.py index 9325e0b601..7d6b7ca586 100644 --- a/tests/test_ensure_type.py +++ b/tests/test_ensure_type.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_ensure_typed.py b/tests/test_ensure_typed.py index 789afd1a46..98a41b5430 100644 --- a/tests/test_ensure_typed.py +++ b/tests/test_ensure_typed.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_enum_bound_interp.py b/tests/test_enum_bound_interp.py index 7607619e7a..5a63fc05af 100644 --- a/tests/test_enum_bound_interp.py +++ b/tests/test_enum_bound_interp.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.utils import optional_import diff --git a/tests/test_eval_mode.py b/tests/test_eval_mode.py index bc9c97d238..8458753e1f 100644 --- a/tests/test_eval_mode.py +++ b/tests/test_eval_mode.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_evenly_divisible_all_gather_dist.py b/tests/test_evenly_divisible_all_gather_dist.py index 5e4b0b3b5d..f338944daa 100644 --- a/tests/test_evenly_divisible_all_gather_dist.py +++ b/tests/test_evenly_divisible_all_gather_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_factorized_increase.py b/tests/test_factorized_increase.py index a86f5a2db9..f7642ff357 100644 --- a/tests/test_factorized_increase.py +++ b/tests/test_factorized_increase.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_factorized_reduce.py b/tests/test_factorized_reduce.py index d14418233e..224a0cb351 100644 --- a/tests/test_factorized_reduce.py +++ b/tests/test_factorized_reduce.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_fastmri_reader.py b/tests/test_fastmri_reader.py index 30393ffc56..b15bd4b6a2 100644 --- a/tests/test_fastmri_reader.py +++ b/tests/test_fastmri_reader.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_fft_utils.py b/tests/test_fft_utils.py index d5e3a22eaa..971df2b411 100644 --- a/tests/test_fft_utils.py +++ b/tests/test_fft_utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_fg_bg_to_indices.py b/tests/test_fg_bg_to_indices.py index 03eb770d6d..7d88bb7ee9 100644 --- a/tests/test_fg_bg_to_indices.py +++ b/tests/test_fg_bg_to_indices.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_fg_bg_to_indicesd.py b/tests/test_fg_bg_to_indicesd.py index 3be795919f..5d827b84d3 100644 --- a/tests/test_fg_bg_to_indicesd.py +++ b/tests/test_fg_bg_to_indicesd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_file_basename.py b/tests/test_file_basename.py index dc8b1316a2..93e2027575 100644 --- a/tests/test_file_basename.py +++ b/tests/test_file_basename.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_fill_holes.py b/tests/test_fill_holes.py index 688c65005e..65c59d49eb 100644 --- a/tests/test_fill_holes.py +++ b/tests/test_fill_holes.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_fill_holesd.py b/tests/test_fill_holesd.py index 7711df36b3..3f98dab1bf 100644 --- a/tests/test_fill_holesd.py +++ b/tests/test_fill_holesd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_fl_exchange_object.py b/tests/test_fl_exchange_object.py index bb2d0372db..293f9d518b 100644 --- a/tests/test_fl_exchange_object.py +++ b/tests/test_fl_exchange_object.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_fl_monai_algo.py b/tests/test_fl_monai_algo.py index cce01a169f..c4c5da00bb 100644 --- a/tests/test_fl_monai_algo.py +++ b/tests/test_fl_monai_algo.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import tempfile diff --git a/tests/test_fl_monai_algo_dist.py b/tests/test_fl_monai_algo_dist.py index 11f64ea318..36c2f419b3 100644 --- a/tests/test_fl_monai_algo_dist.py +++ b/tests/test_fl_monai_algo_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from os.path import join as pathjoin diff --git a/tests/test_fl_monai_algo_stats.py b/tests/test_fl_monai_algo_stats.py index fd2b73ea85..1955c35b36 100644 --- a/tests/test_fl_monai_algo_stats.py +++ b/tests/test_fl_monai_algo_stats.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_flatten_sub_keysd.py b/tests/test_flatten_sub_keysd.py index 336d0c296e..997f203870 100644 --- a/tests/test_flatten_sub_keysd.py +++ b/tests/test_flatten_sub_keysd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_flexible_unet.py b/tests/test_flexible_unet.py index 123d494e9a..4c66c903aa 100644 --- a/tests/test_flexible_unet.py +++ b/tests/test_flexible_unet.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Dict, List, Type, Union import torch from parameterized import parameterized @@ -88,14 +89,14 @@ def get_inplanes(): return [64, 128, 256, 512] @classmethod - def get_encoder_parameters(cls) -> List[Dict]: + def get_encoder_parameters(cls) -> list[dict]: """ Get parameter list to initialize encoder networks. Each parameter dict must have `spatial_dims`, `in_channels` and `pretrained` parameters. """ parameter_list = [] - res_type: Union[Type[ResNetBlock], Type[ResNetBottleneck]] + res_type: type[ResNetBlock] | type[ResNetBottleneck] for backbone in range(len(cls.backbone_names)): if backbone < 3: res_type = ResNetBlock diff --git a/tests/test_flip.py b/tests/test_flip.py index c5a281b127..bf29c76ed2 100644 --- a/tests/test_flip.py +++ b/tests/test_flip.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_flipd.py b/tests/test_flipd.py index b0e656b83f..3f7292fc5a 100644 --- a/tests/test_flipd.py +++ b/tests/test_flipd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_focal_loss.py b/tests/test_focal_loss.py index 6ac23fef36..5f30b7b07d 100644 --- a/tests/test_focal_loss.py +++ b/tests/test_focal_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_folder_layout.py b/tests/test_folder_layout.py index f7291933a3..d6d4bdf679 100644 --- a/tests/test_folder_layout.py +++ b/tests/test_folder_layout.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_foreground_mask.py b/tests/test_foreground_mask.py index 160db5bae3..eb59ae2db6 100644 --- a/tests/test_foreground_mask.py +++ b/tests/test_foreground_mask.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_foreground_maskd.py b/tests/test_foreground_maskd.py index 3c8aa08d7f..24cb233c30 100644 --- a/tests/test_foreground_maskd.py +++ b/tests/test_foreground_maskd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_fourier.py b/tests/test_fourier.py index b500f266d7..3613db989f 100644 --- a/tests/test_fourier.py +++ b/tests/test_fourier.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_fpn_block.py b/tests/test_fpn_block.py index a86cd22a19..c6121c5b98 100644 --- a/tests/test_fpn_block.py +++ b/tests/test_fpn_block.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from collections import OrderedDict diff --git a/tests/test_from_engine_hovernet.py b/tests/test_from_engine_hovernet.py index 201fc356cd..227fa66baa 100644 --- a/tests/test_from_engine_hovernet.py +++ b/tests/test_from_engine_hovernet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_fullyconnectednet.py b/tests/test_fullyconnectednet.py index 6378ec9718..94fc4caa6e 100644 --- a/tests/test_fullyconnectednet.py +++ b/tests/test_fullyconnectednet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_gaussian.py b/tests/test_gaussian.py index 461b11d076..b98507b793 100644 --- a/tests/test_gaussian.py +++ b/tests/test_gaussian.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_gaussian_filter.py b/tests/test_gaussian_filter.py index c4ffe56896..1beee579e8 100644 --- a/tests/test_gaussian_filter.py +++ b/tests/test_gaussian_filter.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_gaussian_sharpen.py b/tests/test_gaussian_sharpen.py index 248e3df4d5..2509a4fc26 100644 --- a/tests/test_gaussian_sharpen.py +++ b/tests/test_gaussian_sharpen.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_gaussian_sharpend.py b/tests/test_gaussian_sharpend.py index 0478007809..75ea915d96 100644 --- a/tests/test_gaussian_sharpend.py +++ b/tests/test_gaussian_sharpend.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_gaussian_smooth.py b/tests/test_gaussian_smooth.py index d5e0875f05..38b29bbd17 100644 --- a/tests/test_gaussian_smooth.py +++ b/tests/test_gaussian_smooth.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_gaussian_smoothd.py b/tests/test_gaussian_smoothd.py index 2e968461e8..8702c073c8 100644 --- a/tests/test_gaussian_smoothd.py +++ b/tests/test_gaussian_smoothd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generalized_dice_focal_loss.py b/tests/test_generalized_dice_focal_loss.py index ef8661c88d..8905da8106 100644 --- a/tests/test_generalized_dice_focal_loss.py +++ b/tests/test_generalized_dice_focal_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generalized_dice_loss.py b/tests/test_generalized_dice_loss.py index 619814037b..b8256a41a9 100644 --- a/tests/test_generalized_dice_loss.py +++ b/tests/test_generalized_dice_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generalized_wasserstein_dice_loss.py b/tests/test_generalized_wasserstein_dice_loss.py index 49a5aa0556..7b85fdc5b6 100644 --- a/tests/test_generalized_wasserstein_dice_loss.py +++ b/tests/test_generalized_wasserstein_dice_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_distance_map.py b/tests/test_generate_distance_map.py index ffd432af8c..724a335e1a 100644 --- a/tests/test_generate_distance_map.py +++ b/tests/test_generate_distance_map.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_distance_mapd.py b/tests/test_generate_distance_mapd.py index 98bd5007a9..17c5aa782b 100644 --- a/tests/test_generate_distance_mapd.py +++ b/tests/test_generate_distance_mapd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_instance_border.py b/tests/test_generate_instance_border.py index ceff4a915e..8634bb7d77 100644 --- a/tests/test_generate_instance_border.py +++ b/tests/test_generate_instance_border.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_instance_borderd.py b/tests/test_generate_instance_borderd.py index 800ed92ed2..fc81e8f87c 100644 --- a/tests/test_generate_instance_borderd.py +++ b/tests/test_generate_instance_borderd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_instance_centroid.py b/tests/test_generate_instance_centroid.py index b7293df0ee..f9fdc602a9 100644 --- a/tests/test_generate_instance_centroid.py +++ b/tests/test_generate_instance_centroid.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_instance_centroidd.py b/tests/test_generate_instance_centroidd.py index f989de5ff2..92e45cdf84 100644 --- a/tests/test_generate_instance_centroidd.py +++ b/tests/test_generate_instance_centroidd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_instance_contour.py b/tests/test_generate_instance_contour.py index 8c43bf5bc5..07a9f8525c 100644 --- a/tests/test_generate_instance_contour.py +++ b/tests/test_generate_instance_contour.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_instance_contourd.py b/tests/test_generate_instance_contourd.py index e92020c6bc..22e3669850 100644 --- a/tests/test_generate_instance_contourd.py +++ b/tests/test_generate_instance_contourd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_instance_type.py b/tests/test_generate_instance_type.py index 8a083d19b7..354f8640ae 100644 --- a/tests/test_generate_instance_type.py +++ b/tests/test_generate_instance_type.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_instance_typed.py b/tests/test_generate_instance_typed.py index 08d9f550a9..84a5344503 100644 --- a/tests/test_generate_instance_typed.py +++ b/tests/test_generate_instance_typed.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_label_classes_crop_centers.py b/tests/test_generate_label_classes_crop_centers.py index 4f64aadc26..5a4a7140a3 100644 --- a/tests/test_generate_label_classes_crop_centers.py +++ b/tests/test_generate_label_classes_crop_centers.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_generate_param_groups.py b/tests/test_generate_param_groups.py index 7ae42b8ec6..8301e40188 100644 --- a/tests/test_generate_param_groups.py +++ b/tests/test_generate_param_groups.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_generate_pos_neg_label_crop_centers.py b/tests/test_generate_pos_neg_label_crop_centers.py index d1a208770f..8c1729ef29 100644 --- a/tests/test_generate_pos_neg_label_crop_centers.py +++ b/tests/test_generate_pos_neg_label_crop_centers.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_generate_spatial_bounding_box.py b/tests/test_generate_spatial_bounding_box.py index d27d5a570f..a67e7d0175 100644 --- a/tests/test_generate_spatial_bounding_box.py +++ b/tests/test_generate_spatial_bounding_box.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_succinct_contour.py b/tests/test_generate_succinct_contour.py index 478c23b522..1c60e99546 100644 --- a/tests/test_generate_succinct_contour.py +++ b/tests/test_generate_succinct_contour.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_succinct_contourd.py b/tests/test_generate_succinct_contourd.py index b34142ec0d..e94a02fed5 100644 --- a/tests/test_generate_succinct_contourd.py +++ b/tests/test_generate_succinct_contourd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_watershed_markers.py b/tests/test_generate_watershed_markers.py index 92b48eeef1..a763361913 100644 --- a/tests/test_generate_watershed_markers.py +++ b/tests/test_generate_watershed_markers.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_watershed_markersd.py b/tests/test_generate_watershed_markersd.py index 22ce7fae0a..76d4ec1ae6 100644 --- a/tests/test_generate_watershed_markersd.py +++ b/tests/test_generate_watershed_markersd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_watershed_mask.py b/tests/test_generate_watershed_mask.py index 0fdb4fb428..1cc35dca5c 100644 --- a/tests/test_generate_watershed_mask.py +++ b/tests/test_generate_watershed_mask.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generate_watershed_maskd.py b/tests/test_generate_watershed_maskd.py index 65b9353fcc..aa6d5bf03a 100644 --- a/tests/test_generate_watershed_maskd.py +++ b/tests/test_generate_watershed_maskd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_generator.py b/tests/test_generator.py index 617655f86e..c336acf7ef 100644 --- a/tests/test_generator.py +++ b/tests/test_generator.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_get_equivalent_dtype.py b/tests/test_get_equivalent_dtype.py index a4df3ac2ac..01f8adca73 100644 --- a/tests/test_get_equivalent_dtype.py +++ b/tests/test_get_equivalent_dtype.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_get_extreme_points.py b/tests/test_get_extreme_points.py index 457351b98c..1338ba0e2c 100644 --- a/tests/test_get_extreme_points.py +++ b/tests/test_get_extreme_points.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_get_layers.py b/tests/test_get_layers.py index 6109052d1f..ad0be1a5c4 100644 --- a/tests/test_get_layers.py +++ b/tests/test_get_layers.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_get_package_version.py b/tests/test_get_package_version.py index c4e15c9d09..1881d79602 100644 --- a/tests/test_get_package_version.py +++ b/tests/test_get_package_version.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.utils.module import get_package_version diff --git a/tests/test_get_unique_labels.py b/tests/test_get_unique_labels.py index 67953a3205..e550882243 100644 --- a/tests/test_get_unique_labels.py +++ b/tests/test_get_unique_labels.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_gibbs_noise.py b/tests/test_gibbs_noise.py index e40eda38db..aad5d6fea6 100644 --- a/tests/test_gibbs_noise.py +++ b/tests/test_gibbs_noise.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_gibbs_noised.py b/tests/test_gibbs_noised.py index 6662e9e17c..3aa69b7280 100644 --- a/tests/test_gibbs_noised.py +++ b/tests/test_gibbs_noised.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_giou_loss.py b/tests/test_giou_loss.py index 25cc258054..e794ddab30 100644 --- a/tests/test_giou_loss.py +++ b/tests/test_giou_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_global_mutual_information_loss.py b/tests/test_global_mutual_information_loss.py index d53d6c9711..af66de46b2 100644 --- a/tests/test_global_mutual_information_loss.py +++ b/tests/test_global_mutual_information_loss.py @@ -8,6 +8,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_globalnet.py b/tests/test_globalnet.py index 4a3e9c124c..d4496858f2 100644 --- a/tests/test_globalnet.py +++ b/tests/test_globalnet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_gmm.py b/tests/test_gmm.py index ad5e383a6a..aede44a123 100644 --- a/tests/test_gmm.py +++ b/tests/test_gmm.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import tempfile diff --git a/tests/test_grid_dataset.py b/tests/test_grid_dataset.py index 9863b50df5..937dda344b 100644 --- a/tests/test_grid_dataset.py +++ b/tests/test_grid_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import unittest diff --git a/tests/test_grid_distortion.py b/tests/test_grid_distortion.py index d71642aae8..45210c9176 100644 --- a/tests/test_grid_distortion.py +++ b/tests/test_grid_distortion.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_grid_distortiond.py b/tests/test_grid_distortiond.py index 2cf8bc7ff9..62b72ebfcc 100644 --- a/tests/test_grid_distortiond.py +++ b/tests/test_grid_distortiond.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_grid_patch.py b/tests/test_grid_patch.py index 03b33147dd..05e773929e 100644 --- a/tests/test_grid_patch.py +++ b/tests/test_grid_patch.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_grid_patchd.py b/tests/test_grid_patchd.py index 0f1bea5f8a..a19e26a16d 100644 --- a/tests/test_grid_patchd.py +++ b/tests/test_grid_patchd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_grid_pull.py b/tests/test_grid_pull.py index 561b231498..8877b0c121 100644 --- a/tests/test_grid_pull.py +++ b/tests/test_grid_pull.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_grid_split.py b/tests/test_grid_split.py index 82734ffd93..3ccf6e75a8 100644 --- a/tests/test_grid_split.py +++ b/tests/test_grid_split.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_grid_splitd.py b/tests/test_grid_splitd.py index 086dd2691d..d8519b2121 100644 --- a/tests/test_grid_splitd.py +++ b/tests/test_grid_splitd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_handler_checkpoint_loader.py b/tests/test_handler_checkpoint_loader.py index bdd4499687..7dfb802bba 100644 --- a/tests/test_handler_checkpoint_loader.py +++ b/tests/test_handler_checkpoint_loader.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import tempfile import unittest diff --git a/tests/test_handler_checkpoint_saver.py b/tests/test_handler_checkpoint_saver.py index c87866490c..70810e018f 100644 --- a/tests/test_handler_checkpoint_saver.py +++ b/tests/test_handler_checkpoint_saver.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_handler_classification_saver.py b/tests/test_handler_classification_saver.py index 313842b443..a885cce7f7 100644 --- a/tests/test_handler_classification_saver.py +++ b/tests/test_handler_classification_saver.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import csv import os import tempfile diff --git a/tests/test_handler_classification_saver_dist.py b/tests/test_handler_classification_saver_dist.py index e92009d37f..ef06b69683 100644 --- a/tests/test_handler_classification_saver_dist.py +++ b/tests/test_handler_classification_saver_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import csv import os import tempfile diff --git a/tests/test_handler_confusion_matrix.py b/tests/test_handler_confusion_matrix.py index ee6f3cd681..5f3ee3ae97 100644 --- a/tests/test_handler_confusion_matrix.py +++ b/tests/test_handler_confusion_matrix.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Any, Dict +from typing import Any import torch from ignite.engine import Engine @@ -24,7 +26,7 @@ TEST_CASE_3 = [{"save_details": False, "metric_name": "f1", "reduction": "mean_batch"}, torch.tensor([0.6667, 0.8000])] TEST_CASE_SEG_1 = [{"include_background": True, "metric_name": "tpr"}, 0.7] -data_1: Dict[Any, Any] = { +data_1: dict[Any, Any] = { "y_pred": torch.tensor( [ [[[0.0, 1.0], [0.0, 0.0]], [[0.0, 0.0], [1.0, 1.0]], [[1.0, 0.0], [0.0, 0.0]]], @@ -39,7 +41,7 @@ ), } -data_2: Dict[Any, Any] = { +data_2: dict[Any, Any] = { "y_pred": torch.tensor([[[[0.0, 1.0], [1.0, 0.0]], [[1.0, 0.0], [1.0, 1.0]], [[0.0, 1.0], [0.0, 0.0]]]]), "y": torch.tensor([[[[1.0, 1.0], [1.0, 1.0]], [[1.0, 1.0], [1.0, 1.0]], [[1.0, 1.0], [1.0, 1.0]]]]), } diff --git a/tests/test_handler_confusion_matrix_dist.py b/tests/test_handler_confusion_matrix_dist.py index 511e84d22a..b74b7e57c4 100644 --- a/tests/test_handler_confusion_matrix_dist.py +++ b/tests/test_handler_confusion_matrix_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_handler_decollate_batch.py b/tests/test_handler_decollate_batch.py index 757708ea2b..5bc5584515 100644 --- a/tests/test_handler_decollate_batch.py +++ b/tests/test_handler_decollate_batch.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_handler_early_stop.py b/tests/test_handler_early_stop.py index 36604e5735..675a804472 100644 --- a/tests/test_handler_early_stop.py +++ b/tests/test_handler_early_stop.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from ignite.engine import Engine, Events diff --git a/tests/test_handler_garbage_collector.py b/tests/test_handler_garbage_collector.py index e3bc3411b9..f64039b6fb 100644 --- a/tests/test_handler_garbage_collector.py +++ b/tests/test_handler_garbage_collector.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import gc import unittest from unittest import skipUnless diff --git a/tests/test_handler_hausdorff_distance.py b/tests/test_handler_hausdorff_distance.py index 1fe9b2e4a3..906db86d62 100644 --- a/tests/test_handler_hausdorff_distance.py +++ b/tests/test_handler_hausdorff_distance.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Tuple import numpy as np import torch @@ -21,7 +22,7 @@ def create_spherical_seg_3d( - radius: float = 20.0, centre: Tuple[int, int, int] = (49, 49, 49), im_shape: Tuple[int, int, int] = (99, 99, 99) + radius: float = 20.0, centre: tuple[int, int, int] = (49, 49, 49), im_shape: tuple[int, int, int] = (99, 99, 99) ) -> np.ndarray: """ Return a 3D image with a sphere inside. Voxel values will be diff --git a/tests/test_handler_logfile.py b/tests/test_handler_logfile.py index b67bf63a5b..9e8006644d 100644 --- a/tests/test_handler_logfile.py +++ b/tests/test_handler_logfile.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_handler_lr_scheduler.py b/tests/test_handler_lr_scheduler.py index 15401fe1b2..f1d3f45f06 100644 --- a/tests/test_handler_lr_scheduler.py +++ b/tests/test_handler_lr_scheduler.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import os import re diff --git a/tests/test_handler_mean_dice.py b/tests/test_handler_mean_dice.py index 88eb4fbdcd..10cf981f02 100644 --- a/tests/test_handler_mean_dice.py +++ b/tests/test_handler_mean_dice.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_handler_mean_iou.py b/tests/test_handler_mean_iou.py index fdd4a5d04d..89dae3af58 100644 --- a/tests/test_handler_mean_iou.py +++ b/tests/test_handler_mean_iou.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_handler_metric_logger.py b/tests/test_handler_metric_logger.py index c3de866c5c..016af1e8b5 100644 --- a/tests/test_handler_metric_logger.py +++ b/tests/test_handler_metric_logger.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_handler_metrics_saver.py b/tests/test_handler_metrics_saver.py index 27f107605b..9888a73e5f 100644 --- a/tests/test_handler_metrics_saver.py +++ b/tests/test_handler_metrics_saver.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import csv import os import tempfile diff --git a/tests/test_handler_metrics_saver_dist.py b/tests/test_handler_metrics_saver_dist.py index 426d99c223..11d7db168b 100644 --- a/tests/test_handler_metrics_saver_dist.py +++ b/tests/test_handler_metrics_saver_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import csv import os import tempfile diff --git a/tests/test_handler_mlflow.py b/tests/test_handler_mlflow.py index f41957840f..c88caae0a9 100644 --- a/tests/test_handler_mlflow.py +++ b/tests/test_handler_mlflow.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import glob import os import shutil diff --git a/tests/test_handler_nvtx.py b/tests/test_handler_nvtx.py index 29a1b8e4fb..75cc5bc1f4 100644 --- a/tests/test_handler_nvtx.py +++ b/tests/test_handler_nvtx.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_handler_panoptic_quality.py b/tests/test_handler_panoptic_quality.py index 9e24c52d9e..1595b5ad2c 100644 --- a/tests/test_handler_panoptic_quality.py +++ b/tests/test_handler_panoptic_quality.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_handler_parameter_scheduler.py b/tests/test_handler_parameter_scheduler.py index 22bf046b83..1e7bbb7588 100644 --- a/tests/test_handler_parameter_scheduler.py +++ b/tests/test_handler_parameter_scheduler.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from ignite.engine import Engine, Events diff --git a/tests/test_handler_post_processing.py b/tests/test_handler_post_processing.py index 10c7ac4a8b..c449665c1e 100644 --- a/tests/test_handler_post_processing.py +++ b/tests/test_handler_post_processing.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_handler_prob_map_producer.py b/tests/test_handler_prob_map_producer.py index a968e7dea0..4fc44d612e 100644 --- a/tests/test_handler_prob_map_producer.py +++ b/tests/test_handler_prob_map_producer.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_handler_regression_metrics.py b/tests/test_handler_regression_metrics.py index c6af76a4db..101862ae66 100644 --- a/tests/test_handler_regression_metrics.py +++ b/tests/test_handler_regression_metrics.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from functools import partial diff --git a/tests/test_handler_regression_metrics_dist.py b/tests/test_handler_regression_metrics_dist.py index a8b644d550..a2e96b97d9 100644 --- a/tests/test_handler_regression_metrics_dist.py +++ b/tests/test_handler_regression_metrics_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_handler_rocauc.py b/tests/test_handler_rocauc.py index 6e2d6be27e..ce2351a9f5 100644 --- a/tests/test_handler_rocauc.py +++ b/tests/test_handler_rocauc.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_handler_rocauc_dist.py b/tests/test_handler_rocauc_dist.py index 994cbe139b..5b6ea045c7 100644 --- a/tests/test_handler_rocauc_dist.py +++ b/tests/test_handler_rocauc_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_handler_smartcache.py b/tests/test_handler_smartcache.py index 7bc9011c2d..c3b4d72cb4 100644 --- a/tests/test_handler_smartcache.py +++ b/tests/test_handler_smartcache.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import unittest diff --git a/tests/test_handler_stats.py b/tests/test_handler_stats.py index 7fe07d974b..cb93f93a29 100644 --- a/tests/test_handler_stats.py +++ b/tests/test_handler_stats.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import logging import os import re diff --git a/tests/test_handler_surface_distance.py b/tests/test_handler_surface_distance.py index 6d245693ac..736f7e251a 100644 --- a/tests/test_handler_surface_distance.py +++ b/tests/test_handler_surface_distance.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Tuple import numpy as np import torch @@ -21,7 +22,7 @@ def create_spherical_seg_3d( - radius: float = 20.0, centre: Tuple[int, int, int] = (49, 49, 49), im_shape: Tuple[int, int, int] = (99, 99, 99) + radius: float = 20.0, centre: tuple[int, int, int] = (49, 49, 49), im_shape: tuple[int, int, int] = (99, 99, 99) ) -> np.ndarray: """ Return a 3D image with a sphere inside. Voxel values will be diff --git a/tests/test_handler_tb_image.py b/tests/test_handler_tb_image.py index 749480e279..031dfe707f 100644 --- a/tests/test_handler_tb_image.py +++ b/tests/test_handler_tb_image.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import glob import tempfile import unittest diff --git a/tests/test_handler_tb_stats.py b/tests/test_handler_tb_stats.py index eef77e5e2b..4e7f599afa 100644 --- a/tests/test_handler_tb_stats.py +++ b/tests/test_handler_tb_stats.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import glob import tempfile import unittest diff --git a/tests/test_handler_validation.py b/tests/test_handler_validation.py index 42ffc8b9eb..deb762917d 100644 --- a/tests/test_handler_validation.py +++ b/tests/test_handler_validation.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_hardnegsampler.py b/tests/test_hardnegsampler.py index f4eff81810..b33cea1537 100644 --- a/tests/test_hardnegsampler.py +++ b/tests/test_hardnegsampler.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_hashing.py b/tests/test_hashing.py index 5a1265bd48..093de47cf9 100644 --- a/tests/test_hashing.py +++ b/tests/test_hashing.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_hausdorff_distance.py b/tests/test_hausdorff_distance.py index 44c011fe13..c9b33a8319 100644 --- a/tests/test_hausdorff_distance.py +++ b/tests/test_hausdorff_distance.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Tuple import numpy as np import torch @@ -20,7 +21,7 @@ def create_spherical_seg_3d( - radius: float = 20.0, centre: Tuple[int, int, int] = (49, 49, 49), im_shape: Tuple[int, int, int] = (99, 99, 99) + radius: float = 20.0, centre: tuple[int, int, int] = (49, 49, 49), im_shape: tuple[int, int, int] = (99, 99, 99) ) -> np.ndarray: """ Return a 3D image with a sphere inside. Voxel values will be diff --git a/tests/test_header_correct.py b/tests/test_header_correct.py index aa0a4dde08..71fed1e35d 100644 --- a/tests/test_header_correct.py +++ b/tests/test_header_correct.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import nibabel as nib diff --git a/tests/test_highresnet.py b/tests/test_highresnet.py index cb3a923f14..04520419b7 100644 --- a/tests/test_highresnet.py +++ b/tests/test_highresnet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_hilbert_transform.py b/tests/test_hilbert_transform.py index f7954d6b24..a34bdddc93 100644 --- a/tests/test_hilbert_transform.py +++ b/tests/test_hilbert_transform.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_histogram_normalize.py b/tests/test_histogram_normalize.py index 586f7a59d3..3a340db52a 100644 --- a/tests/test_histogram_normalize.py +++ b/tests/test_histogram_normalize.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_histogram_normalized.py b/tests/test_histogram_normalized.py index 64d5d514a6..24f27d225e 100644 --- a/tests/test_histogram_normalized.py +++ b/tests/test_histogram_normalized.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_hovernet.py b/tests/test_hovernet.py index 61bf85c50b..d768895bdc 100644 --- a/tests/test_hovernet.py +++ b/tests/test_hovernet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_hovernet_instance_map_post_processing.py b/tests/test_hovernet_instance_map_post_processing.py index db87332c13..990e2d9a10 100644 --- a/tests/test_hovernet_instance_map_post_processing.py +++ b/tests/test_hovernet_instance_map_post_processing.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_hovernet_instance_map_post_processingd.py b/tests/test_hovernet_instance_map_post_processingd.py index 2aa9f7ae64..69e42d3495 100644 --- a/tests/test_hovernet_instance_map_post_processingd.py +++ b/tests/test_hovernet_instance_map_post_processingd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_hovernet_loss.py b/tests/test_hovernet_loss.py index 653b8f8c88..10db4518fa 100644 --- a/tests/test_hovernet_loss.py +++ b/tests/test_hovernet_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import random import unittest diff --git a/tests/test_hovernet_nuclear_type_post_processing.py b/tests/test_hovernet_nuclear_type_post_processing.py index ae2b6dcdeb..f2b33c96ae 100644 --- a/tests/test_hovernet_nuclear_type_post_processing.py +++ b/tests/test_hovernet_nuclear_type_post_processing.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_hovernet_nuclear_type_post_processingd.py b/tests/test_hovernet_nuclear_type_post_processingd.py index 7f3462dddb..01478b7961 100644 --- a/tests/test_hovernet_nuclear_type_post_processingd.py +++ b/tests/test_hovernet_nuclear_type_post_processingd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_identity.py b/tests/test_identity.py index 60134c24a4..19116cbb8f 100644 --- a/tests/test_identity.py +++ b/tests/test_identity.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.transforms.utility.array import Identity diff --git a/tests/test_identityd.py b/tests/test_identityd.py index f1d27d61d4..98499def01 100644 --- a/tests/test_identityd.py +++ b/tests/test_identityd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.transforms.utility.dictionary import Identityd diff --git a/tests/test_image_dataset.py b/tests/test_image_dataset.py index a89759323d..ddafce9eac 100644 --- a/tests/test_image_dataset.py +++ b/tests/test_image_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_image_filter.py b/tests/test_image_filter.py index 20d8a7b58c..841a5d5cd5 100644 --- a/tests/test_image_filter.py +++ b/tests/test_image_filter.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_image_rw.py b/tests/test_image_rw.py index a120607224..42f9b0c4e7 100644 --- a/tests/test_image_rw.py +++ b/tests/test_image_rw.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import itertools import os import shutil diff --git a/tests/test_img2tensorboard.py b/tests/test_img2tensorboard.py index 58c4d3cfab..7825f9b4d7 100644 --- a/tests/test_img2tensorboard.py +++ b/tests/test_img2tensorboard.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_init_reader.py b/tests/test_init_reader.py index e9a8ec96f7..1350146220 100644 --- a/tests/test_init_reader.py +++ b/tests/test_init_reader.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.data import ITKReader, NibabelReader, NrrdReader, NumpyReader, PILReader, PydicomReader diff --git a/tests/test_integration_autorunner.py b/tests/test_integration_autorunner.py index 9ec467eca7..32c6f5cb1c 100644 --- a/tests/test_integration_autorunner.py +++ b/tests/test_integration_autorunner.py @@ -9,10 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest -from typing import Dict, List import nibabel as nib import numpy as np @@ -27,7 +28,7 @@ _, has_tb = optional_import("torch.utils.tensorboard", name="SummaryWriter") _, has_nni = optional_import("nni") -sim_datalist: Dict[str, List[Dict]] = { +sim_datalist: dict[str, list[dict]] = { "testing": [{"image": "val_001.fake.nii.gz"}, {"image": "val_002.fake.nii.gz"}], "training": [ {"fold": 0, "image": "tr_image_001.fake.nii.gz", "label": "tr_label_001.fake.nii.gz"}, diff --git a/tests/test_integration_bundle_run.py b/tests/test_integration_bundle_run.py index 1b3583a911..e2e313ca91 100644 --- a/tests/test_integration_bundle_run.py +++ b/tests/test_integration_bundle_run.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import json import os import shutil diff --git a/tests/test_integration_classification_2d.py b/tests/test_integration_classification_2d.py index 78f0bf9f36..16914073cb 100644 --- a/tests/test_integration_classification_2d.py +++ b/tests/test_integration_classification_2d.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest import warnings diff --git a/tests/test_integration_determinism.py b/tests/test_integration_determinism.py index 94d2325514..26d95dfb47 100644 --- a/tests/test_integration_determinism.py +++ b/tests/test_integration_determinism.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_integration_fast_train.py b/tests/test_integration_fast_train.py index bb50ddf7b6..497fe22dab 100644 --- a/tests/test_integration_fast_train.py +++ b/tests/test_integration_fast_train.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import math import os import shutil diff --git a/tests/test_integration_gpu_customization.py b/tests/test_integration_gpu_customization.py index c684675c88..0f03ee1fe0 100644 --- a/tests/test_integration_gpu_customization.py +++ b/tests/test_integration_gpu_customization.py @@ -9,10 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest -from typing import Dict, List import nibabel as nib import numpy as np @@ -27,7 +28,7 @@ _, has_tb = optional_import("torch.utils.tensorboard", name="SummaryWriter") -fake_datalist: Dict[str, List[Dict]] = { +fake_datalist: dict[str, list[dict]] = { "testing": [{"image": "val_001.fake.nii.gz"}, {"image": "val_002.fake.nii.gz"}], "training": [ {"fold": 0, "image": "tr_image_001.fake.nii.gz", "label": "tr_label_001.fake.nii.gz"}, diff --git a/tests/test_integration_segmentation_3d.py b/tests/test_integration_segmentation_3d.py index 27e42b6bf5..2b57ef84d6 100644 --- a/tests/test_integration_segmentation_3d.py +++ b/tests/test_integration_segmentation_3d.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import tempfile diff --git a/tests/test_integration_sliding_window.py b/tests/test_integration_sliding_window.py index 38cab0c2f0..4ae894956b 100644 --- a/tests/test_integration_sliding_window.py +++ b/tests/test_integration_sliding_window.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_integration_stn.py b/tests/test_integration_stn.py index 5b9b22668a..3103685de4 100644 --- a/tests/test_integration_stn.py +++ b/tests/test_integration_stn.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_integration_unet_2d.py b/tests/test_integration_unet_2d.py index e60c91968a..90c0098d36 100644 --- a/tests/test_integration_unet_2d.py +++ b/tests/test_integration_unet_2d.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_integration_workers.py b/tests/test_integration_workers.py index 654af5a89c..33c26cedf8 100644 --- a/tests/test_integration_workers.py +++ b/tests/test_integration_workers.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_integration_workflows.py b/tests/test_integration_workflows.py index 342e70cc8e..9393f0bb42 100644 --- a/tests/test_integration_workflows.py +++ b/tests/test_integration_workflows.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import tempfile diff --git a/tests/test_integration_workflows_gan.py b/tests/test_integration_workflows_gan.py index 7dd05848bb..db841fca7e 100644 --- a/tests/test_integration_workflows_gan.py +++ b/tests/test_integration_workflows_gan.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import tempfile diff --git a/tests/test_intensity_stats.py b/tests/test_intensity_stats.py index 3479306180..243fcd0dd4 100644 --- a/tests/test_intensity_stats.py +++ b/tests/test_intensity_stats.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_intensity_statsd.py b/tests/test_intensity_statsd.py index d6aeac61b0..3fe82b1df7 100644 --- a/tests/test_intensity_statsd.py +++ b/tests/test_intensity_statsd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import unittest diff --git a/tests/test_inverse.py b/tests/test_inverse.py index 192a8d345e..c081d38dfe 100644 --- a/tests/test_inverse.py +++ b/tests/test_inverse.py @@ -9,12 +9,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import random import sys import unittest from copy import deepcopy from functools import partial -from typing import TYPE_CHECKING, List, Tuple +from typing import TYPE_CHECKING from unittest.case import skipUnless import numpy as np @@ -77,7 +79,7 @@ KEYS = ["image", "label"] -TESTS: List[Tuple] = [] +TESTS: list[tuple] = [] # For pad, start with odd/even images and add odd/even amounts for name in ("1D even", "1D odd"): diff --git a/tests/test_inverse_array.py b/tests/test_inverse_array.py index c86bf92bef..ea021f2566 100644 --- a/tests/test_inverse_array.py +++ b/tests/test_inverse_array.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_inverse_collation.py b/tests/test_inverse_collation.py index 4614432808..aa8266710d 100644 --- a/tests/test_inverse_collation.py +++ b/tests/test_inverse_collation.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import unittest from typing import TYPE_CHECKING diff --git a/tests/test_invert.py b/tests/test_invert.py index 5d4926ecd2..c260193f75 100644 --- a/tests/test_invert.py +++ b/tests/test_invert.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import unittest from copy import deepcopy diff --git a/tests/test_invertd.py b/tests/test_invertd.py index f3b504b39d..5ad8735fdc 100644 --- a/tests/test_invertd.py +++ b/tests/test_invertd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import unittest diff --git a/tests/test_is_supported_format.py b/tests/test_is_supported_format.py index 6cb887c125..591772bb3a 100644 --- a/tests/test_is_supported_format.py +++ b/tests/test_is_supported_format.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_iterable_dataset.py b/tests/test_iterable_dataset.py index 851eb2f0d6..38be9ec30c 100644 --- a/tests/test_iterable_dataset.py +++ b/tests/test_iterable_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import sys import tempfile diff --git a/tests/test_itk_writer.py b/tests/test_itk_writer.py index 163fead76e..869ec7b947 100644 --- a/tests/test_itk_writer.py +++ b/tests/test_itk_writer.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_k_space_spike_noise.py b/tests/test_k_space_spike_noise.py index 537655b3e5..cc0a4932af 100644 --- a/tests/test_k_space_spike_noise.py +++ b/tests/test_k_space_spike_noise.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_k_space_spike_noised.py b/tests/test_k_space_spike_noised.py index 03c99d1533..8cd42d7c08 100644 --- a/tests/test_k_space_spike_noised.py +++ b/tests/test_k_space_spike_noised.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_keep_largest_connected_component.py b/tests/test_keep_largest_connected_component.py index a0e309f2d7..7da3c4b21f 100644 --- a/tests/test_keep_largest_connected_component.py +++ b/tests/test_keep_largest_connected_component.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_keep_largest_connected_componentd.py b/tests/test_keep_largest_connected_componentd.py index 544b7f1773..aac91a2de9 100644 --- a/tests/test_keep_largest_connected_componentd.py +++ b/tests/test_keep_largest_connected_componentd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_kspace_mask.py b/tests/test_kspace_mask.py index 42b90d1675..5d6d9c18ea 100644 --- a/tests/test_kspace_mask.py +++ b/tests/test_kspace_mask.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_label_filter.py b/tests/test_label_filter.py index 42aa419b1d..47a8706491 100644 --- a/tests/test_label_filter.py +++ b/tests/test_label_filter.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_label_filterd.py b/tests/test_label_filterd.py index eea18d0278..f27df08c2a 100644 --- a/tests/test_label_filterd.py +++ b/tests/test_label_filterd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_label_quality_score.py b/tests/test_label_quality_score.py index db31624a95..2f2d00da33 100644 --- a/tests/test_label_quality_score.py +++ b/tests/test_label_quality_score.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_label_to_contour.py b/tests/test_label_to_contour.py index cab116afbe..590fd5d4e4 100644 --- a/tests/test_label_to_contour.py +++ b/tests/test_label_to_contour.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_label_to_contourd.py b/tests/test_label_to_contourd.py index e11b59130a..6fcec72dd8 100644 --- a/tests/test_label_to_contourd.py +++ b/tests/test_label_to_contourd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_label_to_mask.py b/tests/test_label_to_mask.py index e374018836..2eba825cf3 100644 --- a/tests/test_label_to_mask.py +++ b/tests/test_label_to_mask.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_label_to_maskd.py b/tests/test_label_to_maskd.py index 3b6f527c9a..35f54ca5b9 100644 --- a/tests/test_label_to_maskd.py +++ b/tests/test_label_to_maskd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_lambda.py b/tests/test_lambda.py index 3fa080f794..91678c0b81 100644 --- a/tests/test_lambda.py +++ b/tests/test_lambda.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.data.meta_tensor import MetaTensor diff --git a/tests/test_lambdad.py b/tests/test_lambdad.py index 1e333da12b..55df819fa9 100644 --- a/tests/test_lambdad.py +++ b/tests/test_lambdad.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.data.meta_tensor import MetaTensor diff --git a/tests/test_lesion_froc.py b/tests/test_lesion_froc.py index 8c9f751b1e..10682c2bb7 100644 --- a/tests/test_lesion_froc.py +++ b/tests/test_lesion_froc.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from unittest import skipUnless diff --git a/tests/test_list_data_collate.py b/tests/test_list_data_collate.py index 7b6417820c..226d9e1a55 100644 --- a/tests/test_list_data_collate.py +++ b/tests/test_list_data_collate.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_list_to_dict.py b/tests/test_list_to_dict.py index ec81310c9f..4e6bb8cdf7 100644 --- a/tests/test_list_to_dict.py +++ b/tests/test_list_to_dict.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_lltm.py b/tests/test_lltm.py index 877d211767..6ee716e1ef 100644 --- a/tests/test_lltm.py +++ b/tests/test_lltm.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_lmdbdataset.py b/tests/test_lmdbdataset.py index 212898178a..080868b0dd 100644 --- a/tests/test_lmdbdataset.py +++ b/tests/test_lmdbdataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import tempfile diff --git a/tests/test_lmdbdataset_dist.py b/tests/test_lmdbdataset_dist.py index cad2949dde..0b4c7c35fa 100644 --- a/tests/test_lmdbdataset_dist.py +++ b/tests/test_lmdbdataset_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import shutil import tempfile import unittest diff --git a/tests/test_load_decathlon_datalist.py b/tests/test_load_decathlon_datalist.py index a58ba73ece..b0e390cd73 100644 --- a/tests/test_load_decathlon_datalist.py +++ b/tests/test_load_decathlon_datalist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import json import os import tempfile diff --git a/tests/test_load_image.py b/tests/test_load_image.py index 1db39a310b..ec748f9951 100644 --- a/tests/test_load_image.py +++ b/tests/test_load_image.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import tempfile diff --git a/tests/test_load_imaged.py b/tests/test_load_imaged.py index 8210d2f0d1..534cbb6618 100644 --- a/tests/test_load_imaged.py +++ b/tests/test_load_imaged.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import tempfile diff --git a/tests/test_load_spacing_orientation.py b/tests/test_load_spacing_orientation.py index 8257b9965f..e6ff5f8317 100644 --- a/tests/test_load_spacing_orientation.py +++ b/tests/test_load_spacing_orientation.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import time import unittest diff --git a/tests/test_loader_semaphore.py b/tests/test_loader_semaphore.py index 85cf5593f8..859ee1f8d5 100644 --- a/tests/test_loader_semaphore.py +++ b/tests/test_loader_semaphore.py @@ -10,6 +10,8 @@ # limitations under the License. """this test should not generate errors or UserWarning: semaphore_tracker: There appear to be 1 leaked semaphores""" +from __future__ import annotations + import multiprocessing as mp import unittest diff --git a/tests/test_local_normalized_cross_correlation_loss.py b/tests/test_local_normalized_cross_correlation_loss.py index e6052824a9..21fe8b973f 100644 --- a/tests/test_local_normalized_cross_correlation_loss.py +++ b/tests/test_local_normalized_cross_correlation_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_localnet.py b/tests/test_localnet.py index bdf8a44198..f557147960 100644 --- a/tests/test_localnet.py +++ b/tests/test_localnet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_localnet_block.py b/tests/test_localnet_block.py index 57932860ed..27ea4cd1a6 100644 --- a/tests/test_localnet_block.py +++ b/tests/test_localnet_block.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_look_up_option.py b/tests/test_look_up_option.py index 700f7b9691..5f81fb8d43 100644 --- a/tests/test_look_up_option.py +++ b/tests/test_look_up_option.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from enum import Enum diff --git a/tests/test_loss_metric.py b/tests/test_loss_metric.py index 74b6bd14a0..682221f5f5 100644 --- a/tests/test_loss_metric.py +++ b/tests/test_loss_metric.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_lr_finder.py b/tests/test_lr_finder.py index aed7976feb..4472a75716 100644 --- a/tests/test_lr_finder.py +++ b/tests/test_lr_finder.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import pickle import random diff --git a/tests/test_lr_scheduler.py b/tests/test_lr_scheduler.py index 44f4c50c0f..bcddf7627e 100644 --- a/tests/test_lr_scheduler.py +++ b/tests/test_lr_scheduler.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_make_nifti.py b/tests/test_make_nifti.py index 951f079764..4560507c6c 100644 --- a/tests/test_make_nifti.py +++ b/tests/test_make_nifti.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_map_binary_to_indices.py b/tests/test_map_binary_to_indices.py index bc96231160..1080c2a513 100644 --- a/tests/test_map_binary_to_indices.py +++ b/tests/test_map_binary_to_indices.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_map_classes_to_indices.py b/tests/test_map_classes_to_indices.py index 2f32382f6b..9576dd0f61 100644 --- a/tests/test_map_classes_to_indices.py +++ b/tests/test_map_classes_to_indices.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_map_label_value.py b/tests/test_map_label_value.py index ef08f7eae3..32f5fccdb6 100644 --- a/tests/test_map_label_value.py +++ b/tests/test_map_label_value.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_map_label_valued.py b/tests/test_map_label_valued.py index cf8ca6c8e2..8c91adaa49 100644 --- a/tests/test_map_label_valued.py +++ b/tests/test_map_label_valued.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_map_transform.py b/tests/test_map_transform.py index dd77ccb099..7430cf09c7 100644 --- a/tests/test_map_transform.py +++ b/tests/test_map_transform.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_mask_intensity.py b/tests/test_mask_intensity.py index da4b2a8192..2b831ba415 100644 --- a/tests/test_mask_intensity.py +++ b/tests/test_mask_intensity.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_mask_intensityd.py b/tests/test_mask_intensityd.py index 186ed6f2d8..6a39416de4 100644 --- a/tests/test_mask_intensityd.py +++ b/tests/test_mask_intensityd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_masked_dice_loss.py b/tests/test_masked_dice_loss.py index 317da3a316..b868f4d3a1 100644 --- a/tests/test_masked_dice_loss.py +++ b/tests/test_masked_dice_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_masked_inference_wsi_dataset.py b/tests/test_masked_inference_wsi_dataset.py index c424edd897..e9cd6724ab 100644 --- a/tests/test_masked_inference_wsi_dataset.py +++ b/tests/test_masked_inference_wsi_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_masked_loss.py b/tests/test_masked_loss.py index a00b3ae7e7..a5f507ff97 100644 --- a/tests/test_masked_loss.py +++ b/tests/test_masked_loss.py @@ -8,6 +8,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_masked_patch_wsi_dataset.py b/tests/test_masked_patch_wsi_dataset.py index 9783c2d7cf..730ce97bdb 100644 --- a/tests/test_masked_patch_wsi_dataset.py +++ b/tests/test_masked_patch_wsi_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from unittest import skipUnless diff --git a/tests/test_matshow3d.py b/tests/test_matshow3d.py index 18ed16dd2c..a6781fc72e 100644 --- a/tests/test_matshow3d.py +++ b/tests/test_matshow3d.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_mean_ensemble.py b/tests/test_mean_ensemble.py index 060170e3bf..09b7f94dc4 100644 --- a/tests/test_mean_ensemble.py +++ b/tests/test_mean_ensemble.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_mean_ensembled.py b/tests/test_mean_ensembled.py index f6d6286d35..01123b0729 100644 --- a/tests/test_mean_ensembled.py +++ b/tests/test_mean_ensembled.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_median_filter.py b/tests/test_median_filter.py index cf6286bdfe..9f27adff4c 100644 --- a/tests/test_median_filter.py +++ b/tests/test_median_filter.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_median_smooth.py b/tests/test_median_smooth.py index 314327835b..21cd45f28e 100644 --- a/tests/test_median_smooth.py +++ b/tests/test_median_smooth.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_median_smoothd.py b/tests/test_median_smoothd.py index 811e833a90..b8d3452c86 100644 --- a/tests/test_median_smoothd.py +++ b/tests/test_median_smoothd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_mednistdataset.py b/tests/test_mednistdataset.py index c46d846903..7011405e4a 100644 --- a/tests/test_mednistdataset.py +++ b/tests/test_mednistdataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import unittest diff --git a/tests/test_meta_affine.py b/tests/test_meta_affine.py index 269db33ef4..b95ea3f1ac 100644 --- a/tests/test_meta_affine.py +++ b/tests/test_meta_affine.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from copy import deepcopy diff --git a/tests/test_meta_tensor.py b/tests/test_meta_tensor.py index eb0bfdb12f..eb7c007c7a 100644 --- a/tests/test_meta_tensor.py +++ b/tests/test_meta_tensor.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import io import os import random @@ -18,7 +20,6 @@ import warnings from copy import deepcopy from multiprocessing.reduction import ForkingPickler -from typing import Optional, Union import numpy as np import torch @@ -86,7 +87,7 @@ def check( shape: bool = True, vals: bool = True, ids: bool = True, - device: Optional[Union[str, torch.device]] = None, + device: str | torch.device | None = None, meta: bool = True, check_ids: bool = True, **kwargs, diff --git a/tests/test_metatensor_integration.py b/tests/test_metatensor_integration.py index 6e8d5f40a3..6a4c67d160 100644 --- a/tests/test_metatensor_integration.py +++ b/tests/test_metatensor_integration.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_milmodel.py b/tests/test_milmodel.py index 2d58af4a2b..9178e0bccb 100644 --- a/tests/test_milmodel.py +++ b/tests/test_milmodel.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_mlp.py b/tests/test_mlp.py index 737762cfb1..a9010b2944 100644 --- a/tests/test_mlp.py +++ b/tests/test_mlp.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_mmar_download.py b/tests/test_mmar_download.py index a87d927ed2..66fca6bb7f 100644 --- a/tests/test_mmar_download.py +++ b/tests/test_mmar_download.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_module_list.py b/tests/test_module_list.py index acd574d463..293da95d5a 100644 --- a/tests/test_module_list.py +++ b/tests/test_module_list.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import glob import inspect import os diff --git a/tests/test_monai_env_vars.py b/tests/test_monai_env_vars.py index 663dcdd98d..b9285b58d8 100644 --- a/tests/test_monai_env_vars.py +++ b/tests/test_monai_env_vars.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_mri_utils.py b/tests/test_mri_utils.py index c1e2e788bf..2f67816e2e 100644 --- a/tests/test_mri_utils.py +++ b/tests/test_mri_utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_multi_scale.py b/tests/test_multi_scale.py index f348c09512..8b8acb2503 100644 --- a/tests/test_multi_scale.py +++ b/tests/test_multi_scale.py @@ -8,6 +8,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_net_adapter.py b/tests/test_net_adapter.py index c3abc8d142..74a2daab9d 100644 --- a/tests/test_net_adapter.py +++ b/tests/test_net_adapter.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_network_consistency.py b/tests/test_network_consistency.py index 327f0cfbf0..2ca8c5a4b0 100644 --- a/tests/test_network_consistency.py +++ b/tests/test_network_consistency.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import json import os import unittest diff --git a/tests/test_nifti_endianness.py b/tests/test_nifti_endianness.py index 7f179d3bde..0e00e6077d 100644 --- a/tests/test_nifti_endianness.py +++ b/tests/test_nifti_endianness.py @@ -9,11 +9,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest from pathlib import Path -from typing import TYPE_CHECKING, List, Tuple +from typing import TYPE_CHECKING from unittest.case import skipUnless import numpy as np @@ -36,7 +38,7 @@ nib, has_nib = optional_import("nibabel") PILImage, has_pil = optional_import("PIL.Image") -TESTS: List[Tuple] = [] +TESTS: list[tuple] = [] for endianness in ["<", ">"]: for use_array in [True, False]: for image_only in [True, False]: diff --git a/tests/test_nifti_header_revise.py b/tests/test_nifti_header_revise.py index 7f917cb0e9..3d000160e1 100644 --- a/tests/test_nifti_header_revise.py +++ b/tests/test_nifti_header_revise.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import nibabel as nib diff --git a/tests/test_nifti_rw.py b/tests/test_nifti_rw.py index 7da50617d9..cac18cf9e3 100644 --- a/tests/test_nifti_rw.py +++ b/tests/test_nifti_rw.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_nifti_saver.py b/tests/test_nifti_saver.py index 54489904df..d9702d9483 100644 --- a/tests/test_nifti_saver.py +++ b/tests/test_nifti_saver.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_normalize_intensity.py b/tests/test_normalize_intensity.py index 4d06a80c1d..193b5cc4b2 100644 --- a/tests/test_normalize_intensity.py +++ b/tests/test_normalize_intensity.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_normalize_intensityd.py b/tests/test_normalize_intensityd.py index a8167a1e93..451269b1c4 100644 --- a/tests/test_normalize_intensityd.py +++ b/tests/test_normalize_intensityd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_npzdictitemdataset.py b/tests/test_npzdictitemdataset.py index e24a2cfc1f..4ff4577b72 100644 --- a/tests/test_npzdictitemdataset.py +++ b/tests/test_npzdictitemdataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import tempfile import unittest from io import BytesIO diff --git a/tests/test_nrrd_reader.py b/tests/test_nrrd_reader.py index 03bfbfe156..01fabe65a8 100644 --- a/tests/test_nrrd_reader.py +++ b/tests/test_nrrd_reader.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_nuclick_transforms.py b/tests/test_nuclick_transforms.py index 4071dd2a07..fcdd362b01 100644 --- a/tests/test_nuclick_transforms.py +++ b/tests/test_nuclick_transforms.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_numpy_reader.py b/tests/test_numpy_reader.py index d220a67c92..393f613163 100644 --- a/tests/test_numpy_reader.py +++ b/tests/test_numpy_reader.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import sys import tempfile diff --git a/tests/test_nvtx_decorator.py b/tests/test_nvtx_decorator.py index 318c27f1e4..1e7bea17d4 100644 --- a/tests/test_nvtx_decorator.py +++ b/tests/test_nvtx_decorator.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_nvtx_transform.py b/tests/test_nvtx_transform.py index 5310270bc0..3a5314c35f 100644 --- a/tests/test_nvtx_transform.py +++ b/tests/test_nvtx_transform.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_occlusion_sensitivity.py b/tests/test_occlusion_sensitivity.py index 02e34704f1..c7ac5ef533 100644 --- a/tests/test_occlusion_sensitivity.py +++ b/tests/test_occlusion_sensitivity.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Any, List +from typing import Any import torch from parameterized import parameterized @@ -40,8 +42,8 @@ def __call__(self, x, adjoint_info): model_3d.eval() model_2d_adjoint.eval() -TESTS: List[Any] = [] -TESTS_FAIL: List[Any] = [] +TESTS: list[Any] = [] +TESTS_FAIL: list[Any] = [] # 2D w/ bounding box with all modes for mode in ("gaussian", "mean_patch", "mean_img"): diff --git a/tests/test_one_of.py b/tests/test_one_of.py index 2ea41c6e50..687ec71aad 100644 --- a/tests/test_one_of.py +++ b/tests/test_one_of.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_optim_novograd.py b/tests/test_optim_novograd.py index c1e63182e6..8808db432d 100644 --- a/tests/test_optim_novograd.py +++ b/tests/test_optim_novograd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_optional_import.py b/tests/test_optional_import.py index b87ebf8909..03db7b3fc6 100644 --- a/tests/test_optional_import.py +++ b/tests/test_optional_import.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.utils import OptionalImportError, exact_version, optional_import diff --git a/tests/test_ori_ras_lps.py b/tests/test_ori_ras_lps.py index d0a9b034e4..824793f927 100644 --- a/tests/test_ori_ras_lps.py +++ b/tests/test_ori_ras_lps.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_orientation.py b/tests/test_orientation.py index 48e85fd212..df6b39f595 100644 --- a/tests/test_orientation.py +++ b/tests/test_orientation.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from typing import cast diff --git a/tests/test_orientationd.py b/tests/test_orientationd.py index f0a599268c..441be7546d 100644 --- a/tests/test_orientationd.py +++ b/tests/test_orientationd.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Optional, cast +from typing import cast import nibabel as nib import numpy as np @@ -65,7 +67,7 @@ class TestOrientationdCase(unittest.TestCase): @parameterized.expand(TESTS) def test_orntd( - self, init_param, img: torch.Tensor, affine: Optional[torch.Tensor], expected_shape, expected_code, device + self, init_param, img: torch.Tensor, affine: torch.Tensor | None, expected_shape, expected_code, device ): ornt = Orientationd(**init_param) if affine is not None: diff --git a/tests/test_p3d_block.py b/tests/test_p3d_block.py index 62b7098dcd..db9e9c284d 100644 --- a/tests/test_p3d_block.py +++ b/tests/test_p3d_block.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_pad_collation.py b/tests/test_pad_collation.py index 89a8508967..0af9d49b39 100644 --- a/tests/test_pad_collation.py +++ b/tests/test_pad_collation.py @@ -9,10 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import random import unittest from functools import wraps -from typing import List, Tuple import numpy as np import torch @@ -42,7 +43,7 @@ def _testing_collate(x): return pad_list_data_collate(batch=x, method="end", mode="constant") -TESTS: List[Tuple] = [] +TESTS: list[tuple] = [] for pad_collate in [_testing_collate, PadListDataCollate(method="end", mode="constant")]: TESTS.append((dict, pad_collate, RandSpatialCropd("image", roi_size=[8, 7], random_size=True))) diff --git a/tests/test_pad_mode.py b/tests/test_pad_mode.py index ae2e94b60c..722d5b573f 100644 --- a/tests/test_pad_mode.py +++ b/tests/test_pad_mode.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_parallel_execution.py b/tests/test_parallel_execution.py index 6186e73f68..26ec873950 100644 --- a/tests/test_parallel_execution.py +++ b/tests/test_parallel_execution.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import warnings diff --git a/tests/test_parallel_execution_dist.py b/tests/test_parallel_execution_dist.py index f067b71d14..b6f6695be4 100644 --- a/tests/test_parallel_execution_dist.py +++ b/tests/test_parallel_execution_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_partition_dataset.py b/tests/test_partition_dataset.py index 687cf8df34..8640d8cc73 100644 --- a/tests/test_partition_dataset.py +++ b/tests/test_partition_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_partition_dataset_classes.py b/tests/test_partition_dataset_classes.py index 4ed283bdd7..c4fa5ed199 100644 --- a/tests/test_partition_dataset_classes.py +++ b/tests/test_partition_dataset_classes.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_patch_dataset.py b/tests/test_patch_dataset.py index 9574afccea..7d66bdccbb 100644 --- a/tests/test_patch_dataset.py +++ b/tests/test_patch_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import unittest diff --git a/tests/test_patch_wsi_dataset.py b/tests/test_patch_wsi_dataset.py index 0ba1a4a649..d2cc139ebc 100644 --- a/tests/test_patch_wsi_dataset.py +++ b/tests/test_patch_wsi_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from unittest import skipUnless diff --git a/tests/test_patchembedding.py b/tests/test_patchembedding.py index 6971eb0463..bfc1fdbbc9 100644 --- a/tests/test_patchembedding.py +++ b/tests/test_patchembedding.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from unittest import skipUnless diff --git a/tests/test_pathology_he_stain.py b/tests/test_pathology_he_stain.py index 48b3c88543..7ddad4ad6f 100644 --- a/tests/test_pathology_he_stain.py +++ b/tests/test_pathology_he_stain.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_pathology_he_stain_dict.py b/tests/test_pathology_he_stain_dict.py index 2115ce9a99..07db1c3e48 100644 --- a/tests/test_pathology_he_stain_dict.py +++ b/tests/test_pathology_he_stain_dict.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_pathology_prob_nms.py b/tests/test_pathology_prob_nms.py index 3399e33afa..0053500437 100644 --- a/tests/test_pathology_prob_nms.py +++ b/tests/test_pathology_prob_nms.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_persistentdataset.py b/tests/test_persistentdataset.py index bc52ed1102..1b8245e318 100644 --- a/tests/test_persistentdataset.py +++ b/tests/test_persistentdataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import pickle import tempfile diff --git a/tests/test_persistentdataset_dist.py b/tests/test_persistentdataset_dist.py index 20dcb2c264..e69c32b1eb 100644 --- a/tests/test_persistentdataset_dist.py +++ b/tests/test_persistentdataset_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import tempfile diff --git a/tests/test_phl_cpu.py b/tests/test_phl_cpu.py index d479f554b4..a558ba2827 100644 --- a/tests/test_phl_cpu.py +++ b/tests/test_phl_cpu.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_phl_cuda.py b/tests/test_phl_cuda.py index d49a60ecd9..97c3db6c70 100644 --- a/tests/test_phl_cuda.py +++ b/tests/test_phl_cuda.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_pil_reader.py b/tests/test_pil_reader.py index 4f0b891b72..ab4c5c40ca 100644 --- a/tests/test_pil_reader.py +++ b/tests/test_pil_reader.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_plot_2d_or_3d_image.py b/tests/test_plot_2d_or_3d_image.py index 5325c6a294..5add74c260 100644 --- a/tests/test_plot_2d_or_3d_image.py +++ b/tests/test_plot_2d_or_3d_image.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import glob import tempfile import unittest diff --git a/tests/test_png_rw.py b/tests/test_png_rw.py index 47b5571ac0..0b6e8184ea 100644 --- a/tests/test_png_rw.py +++ b/tests/test_png_rw.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_png_saver.py b/tests/test_png_saver.py index d832718643..4bbdceb7c0 100644 --- a/tests/test_png_saver.py +++ b/tests/test_png_saver.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_polyval.py b/tests/test_polyval.py index db3bcaca53..113c862cb3 100644 --- a/tests/test_polyval.py +++ b/tests/test_polyval.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_prepare_batch_default.py b/tests/test_prepare_batch_default.py index e3836ed86f..e440f5cfe3 100644 --- a/tests/test_prepare_batch_default.py +++ b/tests/test_prepare_batch_default.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_prepare_batch_default_dist.py b/tests/test_prepare_batch_default_dist.py index 3c7532e916..d015cf4b2f 100644 --- a/tests/test_prepare_batch_default_dist.py +++ b/tests/test_prepare_batch_default_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_prepare_batch_extra_input.py b/tests/test_prepare_batch_extra_input.py index 79c9a13679..1769a19e4a 100644 --- a/tests/test_prepare_batch_extra_input.py +++ b/tests/test_prepare_batch_extra_input.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_prepare_batch_hovernet.py b/tests/test_prepare_batch_hovernet.py index 9aed8e94c7..5a7080a225 100644 --- a/tests/test_prepare_batch_hovernet.py +++ b/tests/test_prepare_batch_hovernet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_preset_filters.py b/tests/test_preset_filters.py index ea34b2ebae..9bca24cef3 100644 --- a/tests/test_preset_filters.py +++ b/tests/test_preset_filters.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_print_info.py b/tests/test_print_info.py index 591316884c..bb748c3f7b 100644 --- a/tests/test_print_info.py +++ b/tests/test_print_info.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.config import print_debug_info diff --git a/tests/test_print_transform_backends.py b/tests/test_print_transform_backends.py index e714003769..4cd93c3fb2 100644 --- a/tests/test_print_transform_backends.py +++ b/tests/test_print_transform_backends.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.transforms.utils import get_transform_backends, print_transform_backends diff --git a/tests/test_probnms.py b/tests/test_probnms.py index aab312c1db..8da5396fac 100644 --- a/tests/test_probnms.py +++ b/tests/test_probnms.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_probnmsd.py b/tests/test_probnmsd.py index bb2315487b..1f0288811e 100644 --- a/tests/test_probnmsd.py +++ b/tests/test_probnmsd.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Any, List +from typing import Any import numpy as np import torch @@ -19,7 +21,7 @@ from monai.transforms.post.dictionary import ProbNMSD from tests.utils import TEST_NDARRAYS -TESTS: List[Any] = [] +TESTS: list[Any] = [] for p in TEST_NDARRAYS: probs_map_1 = p(np.random.rand(100, 100).clip(0, 0.5)) TESTS.append([{"spatial_dims": 2, "prob_threshold": 0.5, "box_size": 10}, {"prob_map": probs_map_1}, []]) diff --git a/tests/test_profiling.py b/tests/test_profiling.py index 40522b07c5..2b93fae196 100644 --- a/tests/test_profiling.py +++ b/tests/test_profiling.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import datetime import os import unittest diff --git a/tests/test_pytorch_version_after.py b/tests/test_pytorch_version_after.py index be43e49f82..4c8c032c80 100644 --- a/tests/test_pytorch_version_after.py +++ b/tests/test_pytorch_version_after.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_query_memory.py b/tests/test_query_memory.py index cdb44d3eb1..5e57913acb 100644 --- a/tests/test_query_memory.py +++ b/tests/test_query_memory.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from tests.utils import query_memory diff --git a/tests/test_rand_adjust_contrast.py b/tests/test_rand_adjust_contrast.py index 5dc800793e..bfeedc2fcf 100644 --- a/tests/test_rand_adjust_contrast.py +++ b/tests/test_rand_adjust_contrast.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_adjust_contrastd.py b/tests/test_rand_adjust_contrastd.py index b355ac3e4f..4037266da4 100644 --- a/tests/test_rand_adjust_contrastd.py +++ b/tests/test_rand_adjust_contrastd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_affine.py b/tests/test_rand_affine.py index b5bc67ffb1..529c2ff755 100644 --- a/tests/test_rand_affine.py +++ b/tests/test_rand_affine.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_affine_grid.py b/tests/test_rand_affine_grid.py index 6a40d39e4e..113987a85c 100644 --- a/tests/test_rand_affine_grid.py +++ b/tests/test_rand_affine_grid.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_affined.py b/tests/test_rand_affined.py index 566eed68ef..d962a45d2b 100644 --- a/tests/test_rand_affined.py +++ b/tests/test_rand_affined.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import itertools import unittest diff --git a/tests/test_rand_axis_flip.py b/tests/test_rand_axis_flip.py index 7458b9d6dd..c8e5f4b8d8 100644 --- a/tests/test_rand_axis_flip.py +++ b/tests/test_rand_axis_flip.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_axis_flipd.py b/tests/test_rand_axis_flipd.py index a62da88af3..6f54f82e28 100644 --- a/tests/test_rand_axis_flipd.py +++ b/tests/test_rand_axis_flipd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_bias_field.py b/tests/test_rand_bias_field.py index 690c4022eb..16f615146f 100644 --- a/tests/test_rand_bias_field.py +++ b/tests/test_rand_bias_field.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_bias_fieldd.py b/tests/test_rand_bias_fieldd.py index 05a5a1b636..2b8a60289d 100644 --- a/tests/test_rand_bias_fieldd.py +++ b/tests/test_rand_bias_fieldd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_coarse_dropout.py b/tests/test_rand_coarse_dropout.py index cc05edbf02..8c3876f10b 100644 --- a/tests/test_rand_coarse_dropout.py +++ b/tests/test_rand_coarse_dropout.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_coarse_dropoutd.py b/tests/test_rand_coarse_dropoutd.py index e54db130a5..7b16f992b7 100644 --- a/tests/test_rand_coarse_dropoutd.py +++ b/tests/test_rand_coarse_dropoutd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_coarse_shuffle.py b/tests/test_rand_coarse_shuffle.py index fb7311e5a3..adfb722b42 100644 --- a/tests/test_rand_coarse_shuffle.py +++ b/tests/test_rand_coarse_shuffle.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_coarse_shuffled.py b/tests/test_rand_coarse_shuffled.py index fa9c17286d..3b5a1434f4 100644 --- a/tests/test_rand_coarse_shuffled.py +++ b/tests/test_rand_coarse_shuffled.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_crop_by_label_classes.py b/tests/test_rand_crop_by_label_classes.py index b1165e8986..736366559a 100644 --- a/tests/test_rand_crop_by_label_classes.py +++ b/tests/test_rand_crop_by_label_classes.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_crop_by_label_classesd.py b/tests/test_rand_crop_by_label_classesd.py index 24600a41ef..aee7cfd665 100644 --- a/tests/test_rand_crop_by_label_classesd.py +++ b/tests/test_rand_crop_by_label_classesd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_crop_by_pos_neg_label.py b/tests/test_rand_crop_by_pos_neg_label.py index f6da393ab9..0b233bf29e 100644 --- a/tests/test_rand_crop_by_pos_neg_label.py +++ b/tests/test_rand_crop_by_pos_neg_label.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_rand_crop_by_pos_neg_labeld.py b/tests/test_rand_crop_by_pos_neg_labeld.py index 64673bf4bf..27412447f4 100644 --- a/tests/test_rand_crop_by_pos_neg_labeld.py +++ b/tests/test_rand_crop_by_pos_neg_labeld.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_rand_cucim_dict_transform.py b/tests/test_rand_cucim_dict_transform.py index 4a2fdfe77e..33e0667723 100644 --- a/tests/test_rand_cucim_dict_transform.py +++ b/tests/test_rand_cucim_dict_transform.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_cucim_transform.py b/tests/test_rand_cucim_transform.py index 8f81730c59..37d8e29f1d 100644 --- a/tests/test_rand_cucim_transform.py +++ b/tests/test_rand_cucim_transform.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_deform_grid.py b/tests/test_rand_deform_grid.py index 3e59e3207b..58b64ae596 100644 --- a/tests/test_rand_deform_grid.py +++ b/tests/test_rand_deform_grid.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_elastic_2d.py b/tests/test_rand_elastic_2d.py index 125da74528..c59052854f 100644 --- a/tests/test_rand_elastic_2d.py +++ b/tests/test_rand_elastic_2d.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_elastic_3d.py b/tests/test_rand_elastic_3d.py index 76c9e9024d..0ff3ef6129 100644 --- a/tests/test_rand_elastic_3d.py +++ b/tests/test_rand_elastic_3d.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_elasticd_2d.py b/tests/test_rand_elasticd_2d.py index d6f7a0cbba..d0fbd5aa88 100644 --- a/tests/test_rand_elasticd_2d.py +++ b/tests/test_rand_elasticd_2d.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_elasticd_3d.py b/tests/test_rand_elasticd_3d.py index 9db474861e..e058293584 100644 --- a/tests/test_rand_elasticd_3d.py +++ b/tests/test_rand_elasticd_3d.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_flip.py b/tests/test_rand_flip.py index cdd51dd77e..ed6e41d49e 100644 --- a/tests/test_rand_flip.py +++ b/tests/test_rand_flip.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_flipd.py b/tests/test_rand_flipd.py index 92b070fd0a..0b99674c65 100644 --- a/tests/test_rand_flipd.py +++ b/tests/test_rand_flipd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_gaussian_noise.py b/tests/test_rand_gaussian_noise.py index faa2b143f5..7d4d04ff3f 100644 --- a/tests/test_rand_gaussian_noise.py +++ b/tests/test_rand_gaussian_noise.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_gaussian_noised.py b/tests/test_rand_gaussian_noised.py index a927761186..24fc19f226 100644 --- a/tests/test_rand_gaussian_noised.py +++ b/tests/test_rand_gaussian_noised.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_gaussian_sharpen.py b/tests/test_rand_gaussian_sharpen.py index 01d1a1ecec..8dff69cd4c 100644 --- a/tests/test_rand_gaussian_sharpen.py +++ b/tests/test_rand_gaussian_sharpen.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_rand_gaussian_sharpend.py b/tests/test_rand_gaussian_sharpend.py index 02961fc7ec..4c32880053 100644 --- a/tests/test_rand_gaussian_sharpend.py +++ b/tests/test_rand_gaussian_sharpend.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_gaussian_smooth.py b/tests/test_rand_gaussian_smooth.py index f6d75305a5..9fb91a38a1 100644 --- a/tests/test_rand_gaussian_smooth.py +++ b/tests/test_rand_gaussian_smooth.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_gaussian_smoothd.py b/tests/test_rand_gaussian_smoothd.py index c9be44d3c9..d312494e46 100644 --- a/tests/test_rand_gaussian_smoothd.py +++ b/tests/test_rand_gaussian_smoothd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_gibbs_noise.py b/tests/test_rand_gibbs_noise.py index b87b839eb9..a0d18ae7f3 100644 --- a/tests/test_rand_gibbs_noise.py +++ b/tests/test_rand_gibbs_noise.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_rand_gibbs_noised.py b/tests/test_rand_gibbs_noised.py index 23a7dd5fdb..4120f967e2 100644 --- a/tests/test_rand_gibbs_noised.py +++ b/tests/test_rand_gibbs_noised.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_rand_grid_distortion.py b/tests/test_rand_grid_distortion.py index 88b4989cd5..51f11e0389 100644 --- a/tests/test_rand_grid_distortion.py +++ b/tests/test_rand_grid_distortion.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_grid_distortiond.py b/tests/test_rand_grid_distortiond.py index a7b64e5980..9f8ed3b9e6 100644 --- a/tests/test_rand_grid_distortiond.py +++ b/tests/test_rand_grid_distortiond.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_grid_patch.py b/tests/test_rand_grid_patch.py index 417915fbab..cb66276a8c 100644 --- a/tests/test_rand_grid_patch.py +++ b/tests/test_rand_grid_patch.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_grid_patchd.py b/tests/test_rand_grid_patchd.py index 4f3ec3bb6a..15f4d5447f 100644 --- a/tests/test_rand_grid_patchd.py +++ b/tests/test_rand_grid_patchd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_histogram_shift.py b/tests/test_rand_histogram_shift.py index 89198549cd..64dd6a926b 100644 --- a/tests/test_rand_histogram_shift.py +++ b/tests/test_rand_histogram_shift.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_histogram_shiftd.py b/tests/test_rand_histogram_shiftd.py index 7c94379e0e..45e81ab012 100644 --- a/tests/test_rand_histogram_shiftd.py +++ b/tests/test_rand_histogram_shiftd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_k_space_spike_noise.py b/tests/test_rand_k_space_spike_noise.py index 176699ddd1..4e7d59329b 100644 --- a/tests/test_rand_k_space_spike_noise.py +++ b/tests/test_rand_k_space_spike_noise.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_rand_k_space_spike_noised.py b/tests/test_rand_k_space_spike_noised.py index 7f493ef276..37e8ebcf81 100644 --- a/tests/test_rand_k_space_spike_noised.py +++ b/tests/test_rand_k_space_spike_noised.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_rand_lambda.py b/tests/test_rand_lambda.py index cb5c57e9e4..1f14499bc0 100644 --- a/tests/test_rand_lambda.py +++ b/tests/test_rand_lambda.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_rand_lambdad.py b/tests/test_rand_lambdad.py index 8bd7bbbfc8..6b60a3fe70 100644 --- a/tests/test_rand_lambdad.py +++ b/tests/test_rand_lambdad.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_rand_rician_noise.py b/tests/test_rand_rician_noise.py index 9ee1a6ce82..fe7135835e 100644 --- a/tests/test_rand_rician_noise.py +++ b/tests/test_rand_rician_noise.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_rician_noised.py b/tests/test_rand_rician_noised.py index 05707059bc..ae0acab4eb 100644 --- a/tests/test_rand_rician_noised.py +++ b/tests/test_rand_rician_noised.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_rotate.py b/tests/test_rand_rotate.py index 7cba4f99c9..b897064f0a 100644 --- a/tests/test_rand_rotate.py +++ b/tests/test_rand_rotate.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import List, Tuple import numpy as np import scipy.ndimage @@ -27,14 +28,14 @@ test_local_inversion, ) -TEST_CASES_2D: List[Tuple] = [] +TEST_CASES_2D: list[tuple] = [] for p in TEST_NDARRAYS_ALL: TEST_CASES_2D.append((p, np.pi / 2, True, "bilinear", "border", False)) TEST_CASES_2D.append((p, np.pi / 4, True, "nearest", "border", False)) TEST_CASES_2D.append((p, np.pi, False, "nearest", "zeros", True)) TEST_CASES_2D.append((p, (-np.pi / 4, 0), False, "nearest", "zeros", True)) -TEST_CASES_3D: List[Tuple] = [] +TEST_CASES_3D: list[tuple] = [] for p in TEST_NDARRAYS_ALL: TEST_CASES_3D.append( (p, np.pi / 2, -np.pi / 6, (0.0, np.pi), False, "bilinear", "border", False, (1, 81, 110, 112)) diff --git a/tests/test_rand_rotate90.py b/tests/test_rand_rotate90.py index 30ad906ac2..adddcabd3f 100644 --- a/tests/test_rand_rotate90.py +++ b/tests/test_rand_rotate90.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_rotate90d.py b/tests/test_rand_rotate90d.py index ec0e5ac92e..341dad09a6 100644 --- a/tests/test_rand_rotate90d.py +++ b/tests/test_rand_rotate90d.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_rotated.py b/tests/test_rand_rotated.py index fe57c641a5..6736591aa1 100644 --- a/tests/test_rand_rotated.py +++ b/tests/test_rand_rotated.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import List, Tuple import numpy as np import scipy.ndimage @@ -21,14 +22,14 @@ from monai.utils import GridSampleMode, GridSamplePadMode from tests.utils import TEST_NDARRAYS_ALL, NumpyImageTestCase2D, NumpyImageTestCase3D, test_local_inversion -TEST_CASES_2D: List[Tuple] = [] +TEST_CASES_2D: list[tuple] = [] for p in TEST_NDARRAYS_ALL: TEST_CASES_2D.append((p, np.pi / 2, True, "bilinear", "border", False)) TEST_CASES_2D.append((p, np.pi / 4, True, "nearest", "border", False)) TEST_CASES_2D.append((p, np.pi, False, "nearest", "zeros", True)) TEST_CASES_2D.append((p, (-np.pi / 4, 0), False, "nearest", "zeros", True)) -TEST_CASES_3D: List[Tuple] = [] +TEST_CASES_3D: list[tuple] = [] for p in TEST_NDARRAYS_ALL: TEST_CASES_3D.append( (p, np.pi / 2, -np.pi / 6, (0.0, np.pi), False, "bilinear", "border", False, (1, 81, 110, 112)) diff --git a/tests/test_rand_scale_crop.py b/tests/test_rand_scale_crop.py index a97a77a8e6..a4066de836 100644 --- a/tests/test_rand_scale_crop.py +++ b/tests/test_rand_scale_crop.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_scale_cropd.py b/tests/test_rand_scale_cropd.py index dd92783766..ae7305b2e8 100644 --- a/tests/test_rand_scale_cropd.py +++ b/tests/test_rand_scale_cropd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_scale_intensity.py b/tests/test_rand_scale_intensity.py index b0999a82a5..5f5ca076a8 100644 --- a/tests/test_rand_scale_intensity.py +++ b/tests/test_rand_scale_intensity.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_scale_intensityd.py b/tests/test_rand_scale_intensityd.py index d548ee34d6..6b5a04a8f3 100644 --- a/tests/test_rand_scale_intensityd.py +++ b/tests/test_rand_scale_intensityd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_shift_intensity.py b/tests/test_rand_shift_intensity.py index d5ad083d33..12b7ccf526 100644 --- a/tests/test_rand_shift_intensity.py +++ b/tests/test_rand_shift_intensity.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_shift_intensityd.py b/tests/test_rand_shift_intensityd.py index 1a8356c2c9..92bc39dd20 100644 --- a/tests/test_rand_shift_intensityd.py +++ b/tests/test_rand_shift_intensityd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_spatial_crop.py b/tests/test_rand_spatial_crop.py index 383ea8a1cb..b9eb940a1c 100644 --- a/tests/test_rand_spatial_crop.py +++ b/tests/test_rand_spatial_crop.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_spatial_crop_samples.py b/tests/test_rand_spatial_crop_samples.py index fd905a6dae..874a710f21 100644 --- a/tests/test_rand_spatial_crop_samples.py +++ b/tests/test_rand_spatial_crop_samples.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_spatial_crop_samplesd.py b/tests/test_rand_spatial_crop_samplesd.py index c860cbea4f..d3fb10cb35 100644 --- a/tests/test_rand_spatial_crop_samplesd.py +++ b/tests/test_rand_spatial_crop_samplesd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_spatial_cropd.py b/tests/test_rand_spatial_cropd.py index 1b256959c6..4fdd1f8385 100644 --- a/tests/test_rand_spatial_cropd.py +++ b/tests/test_rand_spatial_cropd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_std_shift_intensity.py b/tests/test_rand_std_shift_intensity.py index a2345dca1d..535fb7cb20 100644 --- a/tests/test_rand_std_shift_intensity.py +++ b/tests/test_rand_std_shift_intensity.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_std_shift_intensityd.py b/tests/test_rand_std_shift_intensityd.py index bbbed053ad..31209ee754 100644 --- a/tests/test_rand_std_shift_intensityd.py +++ b/tests/test_rand_std_shift_intensityd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_weighted_crop.py b/tests/test_rand_weighted_crop.py index 2e5554402d..aa7d2939e4 100644 --- a/tests/test_rand_weighted_crop.py +++ b/tests/test_rand_weighted_crop.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_weighted_cropd.py b/tests/test_rand_weighted_cropd.py index ee5fa5f083..421637e024 100644 --- a/tests/test_rand_weighted_cropd.py +++ b/tests/test_rand_weighted_cropd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_zoom.py b/tests/test_rand_zoom.py index b34d3b0419..b454a27d72 100644 --- a/tests/test_rand_zoom.py +++ b/tests/test_rand_zoom.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rand_zoomd.py b/tests/test_rand_zoomd.py index 3a067e8a90..6fccf456e1 100644 --- a/tests/test_rand_zoomd.py +++ b/tests/test_rand_zoomd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_random_order.py b/tests/test_random_order.py index 0ed46fbb0a..a60202dd78 100644 --- a/tests/test_random_order.py +++ b/tests/test_random_order.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_randomizable.py b/tests/test_randomizable.py index 7445287a12..96854a6db8 100644 --- a/tests/test_randomizable.py +++ b/tests/test_randomizable.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_randomizable_transform_type.py b/tests/test_randomizable_transform_type.py index 9f77d2cd5a..3a0995be68 100644 --- a/tests/test_randomizable_transform_type.py +++ b/tests/test_randomizable_transform_type.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.transforms.transform import RandomizableTrait, RandomizableTransform diff --git a/tests/test_randtorchvisiond.py b/tests/test_randtorchvisiond.py index d7d37a7abc..82f9adf473 100644 --- a/tests/test_randtorchvisiond.py +++ b/tests/test_randtorchvisiond.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_recon_net_utils.py b/tests/test_recon_net_utils.py index 6621bf735e..38adb9617b 100644 --- a/tests/test_recon_net_utils.py +++ b/tests/test_recon_net_utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_reference_based_normalize_intensity.py b/tests/test_reference_based_normalize_intensity.py index 01811e5907..8d2715f983 100644 --- a/tests/test_reference_based_normalize_intensity.py +++ b/tests/test_reference_based_normalize_intensity.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_reference_based_spatial_cropd.py b/tests/test_reference_based_spatial_cropd.py index ab5573044d..d5777482c0 100644 --- a/tests/test_reference_based_spatial_cropd.py +++ b/tests/test_reference_based_spatial_cropd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_reference_resolver.py b/tests/test_reference_resolver.py index e6b01c05f4..2ec5425258 100644 --- a/tests/test_reference_resolver.py +++ b/tests/test_reference_resolver.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_reg_loss_integration.py b/tests/test_reg_loss_integration.py index 822a056879..6cd973c32e 100644 --- a/tests/test_reg_loss_integration.py +++ b/tests/test_reg_loss_integration.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_regunet.py b/tests/test_regunet.py index 04f971d2eb..04ff60ef30 100644 --- a/tests/test_regunet.py +++ b/tests/test_regunet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_regunet_block.py b/tests/test_regunet_block.py index 81190fd038..eebe9d8694 100644 --- a/tests/test_regunet_block.py +++ b/tests/test_regunet_block.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_remove_repeated_channel.py b/tests/test_remove_repeated_channel.py index e4b707ce42..90b1b79b03 100644 --- a/tests/test_remove_repeated_channel.py +++ b/tests/test_remove_repeated_channel.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_remove_repeated_channeld.py b/tests/test_remove_repeated_channeld.py index 9db66a6aa0..6d36d32f6f 100644 --- a/tests/test_remove_repeated_channeld.py +++ b/tests/test_remove_repeated_channeld.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_remove_small_objects.py b/tests/test_remove_small_objects.py index 7130d60739..27dd648e24 100644 --- a/tests/test_remove_small_objects.py +++ b/tests/test_remove_small_objects.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import List, Tuple import numpy as np from parameterized import parameterized @@ -30,7 +31,7 @@ TEST_OUTPUT1 = np.array([[[0, 0, 2, 1, 0], [1, 1, 1, 2, 0], [1, 1, 1, 0, 0]]]) -TESTS: List[Tuple] = [] +TESTS: list[tuple] = [] for dtype in (int, float): for p in TEST_NDARRAYS: TESTS.append((dtype, p, TEST_ZEROS, None)) diff --git a/tests/test_repeat_channel.py b/tests/test_repeat_channel.py index 3d74b6479c..0ae5743836 100644 --- a/tests/test_repeat_channel.py +++ b/tests/test_repeat_channel.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_repeat_channeld.py b/tests/test_repeat_channeld.py index a348f3eea9..9f7872135d 100644 --- a/tests/test_repeat_channeld.py +++ b/tests/test_repeat_channeld.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_replace_module.py b/tests/test_replace_module.py index 4cb4443410..cac3fd39e5 100644 --- a/tests/test_replace_module.py +++ b/tests/test_replace_module.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Optional, Type import torch from parameterized import parameterized @@ -37,7 +38,7 @@ def setUp(self): self.total = self.get_num_modules() self.assertGreater(self.num_relus, 0) - def get_num_modules(self, mod: Optional[Type[torch.nn.Module]] = None) -> int: + def get_num_modules(self, mod: type[torch.nn.Module] | None = None) -> int: m = [m for _, m in self.net.named_modules()] if mod is not None: m = [_m for _m in m if isinstance(_m, mod)] diff --git a/tests/test_require_pkg.py b/tests/test_require_pkg.py index dbe63fff3f..b1a3d82a17 100644 --- a/tests/test_require_pkg.py +++ b/tests/test_require_pkg.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.utils import OptionalImportError, min_version, require_pkg diff --git a/tests/test_resample.py b/tests/test_resample.py index 0136552334..3ebdd23e02 100644 --- a/tests/test_resample.py +++ b/tests/test_resample.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_resample_backends.py b/tests/test_resample_backends.py index 6d231183a9..97ee0731e8 100644 --- a/tests/test_resample_backends.py +++ b/tests/test_resample_backends.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_resample_datalist.py b/tests/test_resample_datalist.py index fa120b261e..ae52492953 100644 --- a/tests/test_resample_datalist.py +++ b/tests/test_resample_datalist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_resample_to_match.py b/tests/test_resample_to_match.py index 30df565a26..0661981031 100644 --- a/tests/test_resample_to_match.py +++ b/tests/test_resample_to_match.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import itertools import os import random diff --git a/tests/test_resample_to_matchd.py b/tests/test_resample_to_matchd.py index 566ef4ada9..fb51d487c1 100644 --- a/tests/test_resample_to_matchd.py +++ b/tests/test_resample_to_matchd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import tempfile diff --git a/tests/test_resampler.py b/tests/test_resampler.py index 5c8ef24c0e..6f3996c7e3 100644 --- a/tests/test_resampler.py +++ b/tests/test_resampler.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_resize.py b/tests/test_resize.py index b755bb3faf..41e283f89e 100644 --- a/tests/test_resize.py +++ b/tests/test_resize.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_resize_with_pad_or_crop.py b/tests/test_resize_with_pad_or_crop.py index 4e097cd3d4..52eb7d2203 100644 --- a/tests/test_resize_with_pad_or_crop.py +++ b/tests/test_resize_with_pad_or_crop.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_resize_with_pad_or_cropd.py b/tests/test_resize_with_pad_or_cropd.py index eb4e5f09cc..dceb773206 100644 --- a/tests/test_resize_with_pad_or_cropd.py +++ b/tests/test_resize_with_pad_or_cropd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_resized.py b/tests/test_resized.py index a9da604b15..b588501434 100644 --- a/tests/test_resized.py +++ b/tests/test_resized.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_resnet.py b/tests/test_resnet.py index b09b97a450..cc24106373 100644 --- a/tests/test_resnet.py +++ b/tests/test_resnet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from typing import TYPE_CHECKING diff --git a/tests/test_retinanet.py b/tests/test_retinanet.py index f067e82962..3925cca2f4 100644 --- a/tests/test_retinanet.py +++ b/tests/test_retinanet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_retinanet_detector.py b/tests/test_retinanet_detector.py index 243828432d..a5a4001f5c 100644 --- a/tests/test_retinanet_detector.py +++ b/tests/test_retinanet_detector.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import random import unittest diff --git a/tests/test_retinanet_predict_utils.py b/tests/test_retinanet_predict_utils.py index 5157691696..d97806e91c 100644 --- a/tests/test_retinanet_predict_utils.py +++ b/tests/test_retinanet_predict_utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_rotate.py b/tests/test_rotate.py index d039738b21..253e53123a 100644 --- a/tests/test_rotate.py +++ b/tests/test_rotate.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import List, Tuple import numpy as np import scipy.ndimage @@ -21,7 +22,7 @@ from monai.transforms import Rotate from tests.utils import TEST_NDARRAYS_ALL, NumpyImageTestCase2D, NumpyImageTestCase3D, test_local_inversion -TEST_CASES_2D: List[Tuple] = [] +TEST_CASES_2D: list[tuple] = [] for p in TEST_NDARRAYS_ALL: TEST_CASES_2D.append((p, np.pi / 6, False, "bilinear", "border", False)) TEST_CASES_2D.append((p, np.pi / 4, True, "bilinear", "border", False)) @@ -29,7 +30,7 @@ TEST_CASES_2D.append((p, np.pi, False, "nearest", "zeros", False)) TEST_CASES_2D.append((p, -np.pi / 2, False, "bilinear", "zeros", True)) -TEST_CASES_3D: List[Tuple] = [] +TEST_CASES_3D: list[tuple] = [] for p in TEST_NDARRAYS_ALL: TEST_CASES_3D.append((p, -np.pi / 2, True, "nearest", "border", False)) TEST_CASES_3D.append((p, np.pi / 4, True, "bilinear", "border", False)) @@ -37,7 +38,7 @@ TEST_CASES_3D.append((p, np.pi, False, "nearest", "zeros", False)) TEST_CASES_3D.append((p, -np.pi / 2, False, "bilinear", "zeros", False)) -TEST_CASES_SHAPE_3D: List[Tuple] = [] +TEST_CASES_SHAPE_3D: list[tuple] = [] for p in TEST_NDARRAYS_ALL: TEST_CASES_SHAPE_3D.append((p, [-np.pi / 2, 1.0, 2.0], "nearest", "border", False)) TEST_CASES_SHAPE_3D.append((p, [np.pi / 4, 0, 0], "bilinear", "border", False)) diff --git a/tests/test_rotate90.py b/tests/test_rotate90.py index 69414430c2..fced4fa7be 100644 --- a/tests/test_rotate90.py +++ b/tests/test_rotate90.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rotate90d.py b/tests/test_rotate90d.py index f88e8937e8..79434ccf67 100644 --- a/tests/test_rotate90d.py +++ b/tests/test_rotate90d.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_rotated.py b/tests/test_rotated.py index 48b2e8a3c7..95a750e225 100644 --- a/tests/test_rotated.py +++ b/tests/test_rotated.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import List, Tuple import numpy as np import scipy.ndimage @@ -21,7 +22,7 @@ from monai.transforms import Rotated from tests.utils import TEST_NDARRAYS_ALL, NumpyImageTestCase2D, NumpyImageTestCase3D, test_local_inversion -TEST_CASES_2D: List[Tuple] = [] +TEST_CASES_2D: list[tuple] = [] for p in TEST_NDARRAYS_ALL: TEST_CASES_2D.append((p, -np.pi / 6, False, "bilinear", "border", False)) TEST_CASES_2D.append((p, -np.pi / 4, True, "bilinear", "border", False)) @@ -29,7 +30,7 @@ TEST_CASES_2D.append((p, -np.pi, False, "nearest", "zeros", False)) TEST_CASES_2D.append((p, np.pi / 2, False, "bilinear", "zeros", True)) -TEST_CASES_3D: List[Tuple] = [] +TEST_CASES_3D: list[tuple] = [] for p in TEST_NDARRAYS_ALL: TEST_CASES_3D.append((p, -np.pi / 6, False, "bilinear", "border", False)) TEST_CASES_3D.append((p, -np.pi / 4, True, "bilinear", "border", False)) diff --git a/tests/test_safe_dtype_range.py b/tests/test_safe_dtype_range.py index 2f0b1bcefc..73f9607d7d 100644 --- a/tests/test_safe_dtype_range.py +++ b/tests/test_safe_dtype_range.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import List, Tuple import numpy as np import torch @@ -22,7 +23,7 @@ cp, _ = optional_import("cupy") -TESTS: List[Tuple] = [] +TESTS: list[tuple] = [] for in_type in TEST_NDARRAYS_ALL + (int, float): TESTS.append((in_type(np.array(1.0)), in_type(np.array(1.0)), None)) # type: ignore if in_type is not float: @@ -31,7 +32,7 @@ for in_type in TEST_NDARRAYS_ALL: TESTS.append((in_type(np.array([[256, 255], [-12, 0]])), in_type(np.array([[255, 255], [0, 0]])), np.uint8)) -TESTS_LIST: List[Tuple] = [] +TESTS_LIST: list[tuple] = [] for in_type in TEST_NDARRAYS_ALL + (int, float): TESTS_LIST.append( ( diff --git a/tests/test_saliency_inferer.py b/tests/test_saliency_inferer.py index c97bcb7811..4efe30d7a6 100644 --- a/tests/test_saliency_inferer.py +++ b/tests/test_saliency_inferer.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_sample_slices.py b/tests/test_sample_slices.py index 117d39b486..02b7926392 100644 --- a/tests/test_sample_slices.py +++ b/tests/test_sample_slices.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_sampler_dist.py b/tests/test_sampler_dist.py index c40ede414a..b2f86c54cc 100644 --- a/tests/test_sampler_dist.py +++ b/tests/test_sampler_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_save_classificationd.py b/tests/test_save_classificationd.py index 10c65c2044..dd0b213bd6 100644 --- a/tests/test_save_classificationd.py +++ b/tests/test_save_classificationd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import csv import os import tempfile diff --git a/tests/test_save_image.py b/tests/test_save_image.py index 4f0c7b5a67..ba94ab5087 100644 --- a/tests/test_save_image.py +++ b/tests/test_save_image.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_save_imaged.py b/tests/test_save_imaged.py index 4b079b73fd..676eb74678 100644 --- a/tests/test_save_imaged.py +++ b/tests/test_save_imaged.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_save_state.py b/tests/test_save_state.py index c48b12ebdc..8ab7080700 100644 --- a/tests/test_save_state.py +++ b/tests/test_save_state.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_savitzky_golay_filter.py b/tests/test_savitzky_golay_filter.py index 0e54276533..b7f89cdfde 100644 --- a/tests/test_savitzky_golay_filter.py +++ b/tests/test_savitzky_golay_filter.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_savitzky_golay_smooth.py b/tests/test_savitzky_golay_smooth.py index b296372986..6da4f24c62 100644 --- a/tests/test_savitzky_golay_smooth.py +++ b/tests/test_savitzky_golay_smooth.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_savitzky_golay_smoothd.py b/tests/test_savitzky_golay_smoothd.py index 730fdeeef2..7e7176e2bb 100644 --- a/tests/test_savitzky_golay_smoothd.py +++ b/tests/test_savitzky_golay_smoothd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_scale_intensity.py b/tests/test_scale_intensity.py index 9941172b0e..57a7da1780 100644 --- a/tests/test_scale_intensity.py +++ b/tests/test_scale_intensity.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_scale_intensity_range.py b/tests/test_scale_intensity_range.py index 958881f790..898f4dfb45 100644 --- a/tests/test_scale_intensity_range.py +++ b/tests/test_scale_intensity_range.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_scale_intensity_range_percentiles.py b/tests/test_scale_intensity_range_percentiles.py index 181243b0fe..583dcec07e 100644 --- a/tests/test_scale_intensity_range_percentiles.py +++ b/tests/test_scale_intensity_range_percentiles.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_scale_intensity_range_percentilesd.py b/tests/test_scale_intensity_range_percentilesd.py index b35f937b95..8e2511d9e4 100644 --- a/tests/test_scale_intensity_range_percentilesd.py +++ b/tests/test_scale_intensity_range_percentilesd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_scale_intensity_ranged.py b/tests/test_scale_intensity_ranged.py index 4ac4910e37..724acf1c73 100644 --- a/tests/test_scale_intensity_ranged.py +++ b/tests/test_scale_intensity_ranged.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.transforms import ScaleIntensityRanged diff --git a/tests/test_scale_intensityd.py b/tests/test_scale_intensityd.py index d560523214..6705cfda9d 100644 --- a/tests/test_scale_intensityd.py +++ b/tests/test_scale_intensityd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_se_block.py b/tests/test_se_block.py index 88983a7746..de129f4d55 100644 --- a/tests/test_se_block.py +++ b/tests/test_se_block.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_se_blocks.py b/tests/test_se_blocks.py index 400ee85e7f..c97e459f50 100644 --- a/tests/test_se_blocks.py +++ b/tests/test_se_blocks.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_seg_loss_integration.py b/tests/test_seg_loss_integration.py index f4a0f25267..23bc63fbf6 100644 --- a/tests/test_seg_loss_integration.py +++ b/tests/test_seg_loss_integration.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_segresnet.py b/tests/test_segresnet.py index b7c37f87b9..cb34445efa 100644 --- a/tests/test_segresnet.py +++ b/tests/test_segresnet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_segresnet_block.py b/tests/test_segresnet_block.py index 9bb435ac1d..dd455aec13 100644 --- a/tests/test_segresnet_block.py +++ b/tests/test_segresnet_block.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_segresnet_ds.py b/tests/test_segresnet_ds.py index b9a5d873dc..e8382f7079 100644 --- a/tests/test_segresnet_ds.py +++ b/tests/test_segresnet_ds.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_select_cross_validation_folds.py b/tests/test_select_cross_validation_folds.py index 7693baca80..3ab6c0a9c5 100644 --- a/tests/test_select_cross_validation_folds.py +++ b/tests/test_select_cross_validation_folds.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_select_itemsd.py b/tests/test_select_itemsd.py index ba75a27cff..5eb4a1c51b 100644 --- a/tests/test_select_itemsd.py +++ b/tests/test_select_itemsd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import time import unittest diff --git a/tests/test_selfattention.py b/tests/test_selfattention.py index 407bee341c..08eac70e51 100644 --- a/tests/test_selfattention.py +++ b/tests/test_selfattention.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from unittest import skipUnless diff --git a/tests/test_senet.py b/tests/test_senet.py index b0d8ac0c0a..92b5f39ace 100644 --- a/tests/test_senet.py +++ b/tests/test_senet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from typing import TYPE_CHECKING diff --git a/tests/test_separable_filter.py b/tests/test_separable_filter.py index e6838e2f9b..1797a649e0 100644 --- a/tests/test_separable_filter.py +++ b/tests/test_separable_filter.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_set_determinism.py b/tests/test_set_determinism.py index 7d6c54909d..aab7af1079 100644 --- a/tests/test_set_determinism.py +++ b/tests/test_set_determinism.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_set_visible_devices.py b/tests/test_set_visible_devices.py index 75cbd6fb0d..53703e107a 100644 --- a/tests/test_set_visible_devices.py +++ b/tests/test_set_visible_devices.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_shift_intensity.py b/tests/test_shift_intensity.py index ecded268ab..f1bc36036e 100644 --- a/tests/test_shift_intensity.py +++ b/tests/test_shift_intensity.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_shift_intensityd.py b/tests/test_shift_intensityd.py index b5a2a3218d..e8d163b34a 100644 --- a/tests/test_shift_intensityd.py +++ b/tests/test_shift_intensityd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_shuffle_buffer.py b/tests/test_shuffle_buffer.py index 306bc636d2..9fcd3a23f6 100644 --- a/tests/test_shuffle_buffer.py +++ b/tests/test_shuffle_buffer.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import unittest diff --git a/tests/test_signal_continuouswavelet.py b/tests/test_signal_continuouswavelet.py index f8f028aec9..4886168a00 100644 --- a/tests/test_signal_continuouswavelet.py +++ b/tests/test_signal_continuouswavelet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from unittest import skipUnless diff --git a/tests/test_signal_fillempty.py b/tests/test_signal_fillempty.py index 388426bc95..f44e4ba29a 100644 --- a/tests/test_signal_fillempty.py +++ b/tests/test_signal_fillempty.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_signal_rand_add_gaussiannoise.py b/tests/test_signal_rand_add_gaussiannoise.py index dbaf716c4b..2090df876f 100644 --- a/tests/test_signal_rand_add_gaussiannoise.py +++ b/tests/test_signal_rand_add_gaussiannoise.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_signal_rand_add_sine.py b/tests/test_signal_rand_add_sine.py index 5cb63f1496..ae0684d608 100644 --- a/tests/test_signal_rand_add_sine.py +++ b/tests/test_signal_rand_add_sine.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_signal_rand_add_sine_partial.py b/tests/test_signal_rand_add_sine_partial.py index c04e6b138c..109fb006ea 100644 --- a/tests/test_signal_rand_add_sine_partial.py +++ b/tests/test_signal_rand_add_sine_partial.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_signal_rand_add_squarepulse.py b/tests/test_signal_rand_add_squarepulse.py index 6c96f69577..efbdc9af09 100644 --- a/tests/test_signal_rand_add_squarepulse.py +++ b/tests/test_signal_rand_add_squarepulse.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from unittest import skipUnless diff --git a/tests/test_signal_rand_add_squarepulse_partial.py b/tests/test_signal_rand_add_squarepulse_partial.py index dd7aeae793..eee3f5596d 100644 --- a/tests/test_signal_rand_add_squarepulse_partial.py +++ b/tests/test_signal_rand_add_squarepulse_partial.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from unittest import skipUnless diff --git a/tests/test_signal_rand_drop.py b/tests/test_signal_rand_drop.py index 4235ae6d87..5dcd466481 100644 --- a/tests/test_signal_rand_drop.py +++ b/tests/test_signal_rand_drop.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_signal_rand_scale.py b/tests/test_signal_rand_scale.py index 2ac708ef19..126d7cca65 100644 --- a/tests/test_signal_rand_scale.py +++ b/tests/test_signal_rand_scale.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_signal_rand_shift.py b/tests/test_signal_rand_shift.py index 402cd433f8..ed25cc8b1f 100644 --- a/tests/test_signal_rand_shift.py +++ b/tests/test_signal_rand_shift.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from unittest import skipUnless diff --git a/tests/test_signal_remove_frequency.py b/tests/test_signal_remove_frequency.py index fa70c4f795..b18de36c08 100644 --- a/tests/test_signal_remove_frequency.py +++ b/tests/test_signal_remove_frequency.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from unittest import skipUnless diff --git a/tests/test_simple_aspp.py b/tests/test_simple_aspp.py index 9c952bd791..f18b208e9c 100644 --- a/tests/test_simple_aspp.py +++ b/tests/test_simple_aspp.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_simulatedelay.py b/tests/test_simulatedelay.py index 3a0507dae7..5cf47b245e 100644 --- a/tests/test_simulatedelay.py +++ b/tests/test_simulatedelay.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import time import unittest diff --git a/tests/test_simulatedelayd.py b/tests/test_simulatedelayd.py index cbabb68e0f..827fe69510 100644 --- a/tests/test_simulatedelayd.py +++ b/tests/test_simulatedelayd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import time import unittest diff --git a/tests/test_skip_connection.py b/tests/test_skip_connection.py index f523891084..0ac8ef0d7a 100644 --- a/tests/test_skip_connection.py +++ b/tests/test_skip_connection.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_slice_inferer.py b/tests/test_slice_inferer.py index 0f33385f42..4d7dea026f 100644 --- a/tests/test_slice_inferer.py +++ b/tests/test_slice_inferer.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_sliding_patch_wsi_dataset.py b/tests/test_sliding_patch_wsi_dataset.py index 06395cf26c..e6d11de739 100644 --- a/tests/test_sliding_patch_wsi_dataset.py +++ b/tests/test_sliding_patch_wsi_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from unittest import skipUnless diff --git a/tests/test_sliding_window_hovernet_inference.py b/tests/test_sliding_window_hovernet_inference.py index 0106f1e5b4..0dc2216c22 100644 --- a/tests/test_sliding_window_hovernet_inference.py +++ b/tests/test_sliding_window_hovernet_inference.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_sliding_window_inference.py b/tests/test_sliding_window_inference.py index 519daee6f5..2696d4456c 100644 --- a/tests/test_sliding_window_inference.py +++ b/tests/test_sliding_window_inference.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import itertools import unittest diff --git a/tests/test_smartcache_patch_wsi_dataset.py b/tests/test_smartcache_patch_wsi_dataset.py index 5760264a7b..e51033bb4e 100644 --- a/tests/test_smartcache_patch_wsi_dataset.py +++ b/tests/test_smartcache_patch_wsi_dataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest from unittest import skipUnless diff --git a/tests/test_smartcachedataset.py b/tests/test_smartcachedataset.py index 84cba2477c..0e2a79fef3 100644 --- a/tests/test_smartcachedataset.py +++ b/tests/test_smartcachedataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import copy import os import sys diff --git a/tests/test_smooth_field.py b/tests/test_smooth_field.py index b731af36f4..c525311478 100644 --- a/tests/test_smooth_field.py +++ b/tests/test_smooth_field.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from itertools import product diff --git a/tests/test_sobel_gradient.py b/tests/test_sobel_gradient.py index c464062571..3d995a60c9 100644 --- a/tests/test_sobel_gradient.py +++ b/tests/test_sobel_gradient.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_sobel_gradientd.py b/tests/test_sobel_gradientd.py index 30df091ea0..7499a0410b 100644 --- a/tests/test_sobel_gradientd.py +++ b/tests/test_sobel_gradientd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_spacing.py b/tests/test_spacing.py index a1e289d19b..5e9b54ca16 100644 --- a/tests/test_spacing.py +++ b/tests/test_spacing.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Dict, List import numpy as np import torch @@ -23,7 +24,7 @@ from monai.utils import fall_back_tuple from tests.utils import TEST_DEVICES, TEST_NDARRAYS_ALL, assert_allclose, skip_if_quick -TESTS: List[List] = [] +TESTS: list[list] = [] for device in TEST_DEVICES: TESTS.append( [ @@ -267,10 +268,10 @@ class TestSpacingCase(unittest.TestCase): @parameterized.expand(TESTS) def test_spacing( self, - init_param: Dict, + init_param: dict, img: torch.Tensor, affine: torch.Tensor, - data_param: Dict, + data_param: dict, expected_output: torch.Tensor, device: torch.device, ): diff --git a/tests/test_spacingd.py b/tests/test_spacingd.py index 6505d2ceb8..a77c3636fa 100644 --- a/tests/test_spacingd.py +++ b/tests/test_spacingd.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import List, Tuple import numpy as np import torch @@ -22,7 +23,7 @@ from monai.transforms import Spacingd from tests.utils import TEST_DEVICES, assert_allclose -TESTS: List[Tuple] = [] +TESTS: list[tuple] = [] for device in TEST_DEVICES: TESTS.append( ( diff --git a/tests/test_spatial_crop.py b/tests/test_spatial_crop.py index 6fdfbd3f70..754a217a6d 100644 --- a/tests/test_spatial_crop.py +++ b/tests/test_spatial_crop.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_spatial_cropd.py b/tests/test_spatial_cropd.py index 11f6da0811..db6204be04 100644 --- a/tests/test_spatial_cropd.py +++ b/tests/test_spatial_cropd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_spatial_pad.py b/tests/test_spatial_pad.py index 5a70c10686..526ec28334 100644 --- a/tests/test_spatial_pad.py +++ b/tests/test_spatial_pad.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_spatial_padd.py b/tests/test_spatial_padd.py index 656a731de0..1b8f7a2775 100644 --- a/tests/test_spatial_padd.py +++ b/tests/test_spatial_padd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_spatial_resample.py b/tests/test_spatial_resample.py index 30bf33149b..446b164628 100644 --- a/tests/test_spatial_resample.py +++ b/tests/test_spatial_resample.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_spatial_resampled.py b/tests/test_spatial_resampled.py index 5ace0b3774..420d5b7798 100644 --- a/tests/test_spatial_resampled.py +++ b/tests/test_spatial_resampled.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_split_channel.py b/tests/test_split_channel.py index 4b41c334e8..12b336431d 100644 --- a/tests/test_split_channel.py +++ b/tests/test_split_channel.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_split_channeld.py b/tests/test_split_channeld.py index 7a34855676..63b4caf7a5 100644 --- a/tests/test_split_channeld.py +++ b/tests/test_split_channeld.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_split_on_grid.py b/tests/test_split_on_grid.py index b1d4cd93c5..1bf7dbf970 100644 --- a/tests/test_split_on_grid.py +++ b/tests/test_split_on_grid.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_split_on_grid_dict.py b/tests/test_split_on_grid_dict.py index 778a38da34..fdc16b3a0a 100644 --- a/tests/test_split_on_grid_dict.py +++ b/tests/test_split_on_grid_dict.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_splitdim.py b/tests/test_splitdim.py index d6ee4fc55e..38775dba5c 100644 --- a/tests/test_splitdim.py +++ b/tests/test_splitdim.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_splitdimd.py b/tests/test_splitdimd.py index 85184b494a..e512de6b03 100644 --- a/tests/test_splitdimd.py +++ b/tests/test_splitdimd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_squeezedim.py b/tests/test_squeezedim.py index a2b538d58c..9f08af540f 100644 --- a/tests/test_squeezedim.py +++ b/tests/test_squeezedim.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_squeezedimd.py b/tests/test_squeezedimd.py index 5908e7673f..9fa9d84030 100644 --- a/tests/test_squeezedimd.py +++ b/tests/test_squeezedimd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_ssim_loss.py b/tests/test_ssim_loss.py index 3e1c085069..a714d85da0 100644 --- a/tests/test_ssim_loss.py +++ b/tests/test_ssim_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_ssim_metric.py b/tests/test_ssim_metric.py index 01c48dd793..5505e5b750 100644 --- a/tests/test_ssim_metric.py +++ b/tests/test_ssim_metric.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_state_cacher.py b/tests/test_state_cacher.py index e4164be272..6cb404b976 100644 --- a/tests/test_state_cacher.py +++ b/tests/test_state_cacher.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import pickle import unittest from os.path import exists, join diff --git a/tests/test_std_shift_intensity.py b/tests/test_std_shift_intensity.py index b8306aa09c..af18c18aa2 100644 --- a/tests/test_std_shift_intensity.py +++ b/tests/test_std_shift_intensity.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_std_shift_intensityd.py b/tests/test_std_shift_intensityd.py index b86f6bd5e6..6cb7d416c7 100644 --- a/tests/test_std_shift_intensityd.py +++ b/tests/test_std_shift_intensityd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_str2bool.py b/tests/test_str2bool.py index e1d9ca1ee3..36f99b4064 100644 --- a/tests/test_str2bool.py +++ b/tests/test_str2bool.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.utils.misc import str2bool diff --git a/tests/test_str2list.py b/tests/test_str2list.py index 95a4dcaef0..b442925fb3 100644 --- a/tests/test_str2list.py +++ b/tests/test_str2list.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.utils.misc import str2list diff --git a/tests/test_subpixel_upsample.py b/tests/test_subpixel_upsample.py index bd46aecb97..a6de8dd846 100644 --- a/tests/test_subpixel_upsample.py +++ b/tests/test_subpixel_upsample.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_surface_dice.py b/tests/test_surface_dice.py index ccc6242e1e..585c1754ca 100644 --- a/tests/test_surface_dice.py +++ b/tests/test_surface_dice.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_surface_distance.py b/tests/test_surface_distance.py index 4cd70b43aa..6455a165ef 100644 --- a/tests/test_surface_distance.py +++ b/tests/test_surface_distance.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Tuple import numpy as np import torch @@ -20,7 +21,7 @@ def create_spherical_seg_3d( - radius: float = 20.0, centre: Tuple[int, int, int] = (49, 49, 49), im_shape: Tuple[int, int, int] = (99, 99, 99) + radius: float = 20.0, centre: tuple[int, int, int] = (49, 49, 49), im_shape: tuple[int, int, int] = (99, 99, 99) ) -> np.ndarray: """ Return a 3D image with a sphere inside. Voxel values will be diff --git a/tests/test_swin_unetr.py b/tests/test_swin_unetr.py index 6188d6225a..636fcc9e31 100644 --- a/tests/test_swin_unetr.py +++ b/tests/test_swin_unetr.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from unittest import skipUnless diff --git a/tests/test_synthetic.py b/tests/test_synthetic.py index 0da7054a10..116897e67d 100644 --- a/tests/test_synthetic.py +++ b/tests/test_synthetic.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_tciadataset.py b/tests/test_tciadataset.py index 301f2caac2..69f88927b9 100644 --- a/tests/test_tciadataset.py +++ b/tests/test_tciadataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import shutil import unittest diff --git a/tests/test_testtimeaugmentation.py b/tests/test_testtimeaugmentation.py index 93a569186d..20831ca294 100644 --- a/tests/test_testtimeaugmentation.py +++ b/tests/test_testtimeaugmentation.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from functools import partial from typing import TYPE_CHECKING diff --git a/tests/test_thread_buffer.py b/tests/test_thread_buffer.py index 87da22eab3..ab5dba77be 100644 --- a/tests/test_thread_buffer.py +++ b/tests/test_thread_buffer.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import sys import time import unittest diff --git a/tests/test_threadcontainer.py b/tests/test_threadcontainer.py index 2419b390fd..ca9fb244fc 100644 --- a/tests/test_threadcontainer.py +++ b/tests/test_threadcontainer.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import time diff --git a/tests/test_threshold_intensity.py b/tests/test_threshold_intensity.py index 3c0a2033ee..7fb28d413f 100644 --- a/tests/test_threshold_intensity.py +++ b/tests/test_threshold_intensity.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_threshold_intensityd.py b/tests/test_threshold_intensityd.py index 8aade12322..d5e7e5f517 100644 --- a/tests/test_threshold_intensityd.py +++ b/tests/test_threshold_intensityd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_tile_on_grid.py b/tests/test_tile_on_grid.py index 4aca252350..714b65f98c 100644 --- a/tests/test_tile_on_grid.py +++ b/tests/test_tile_on_grid.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Optional import numpy as np from parameterized import parameterized @@ -72,7 +73,7 @@ def make_image( tile_size: int, step: int = 0, random_offset: bool = False, - filter_mode: Optional[str] = None, + filter_mode: str | None = None, seed=123, **kwargs, ): diff --git a/tests/test_tile_on_grid_dict.py b/tests/test_tile_on_grid_dict.py index fa94dd0a70..cb824ee2e6 100644 --- a/tests/test_tile_on_grid_dict.py +++ b/tests/test_tile_on_grid_dict.py @@ -9,8 +9,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Optional import numpy as np import torch @@ -81,7 +82,7 @@ def make_image( tile_size: int, step: int = 0, random_offset: bool = False, - filter_mode: Optional[str] = None, + filter_mode: str | None = None, seed=123, **kwargs, ): diff --git a/tests/test_timedcall_dist.py b/tests/test_timedcall_dist.py index a2b3ae585a..af7cf8720f 100644 --- a/tests/test_timedcall_dist.py +++ b/tests/test_timedcall_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import multiprocessing import sys import time diff --git a/tests/test_to_contiguous.py b/tests/test_to_contiguous.py index a9c2a78278..3a57ae6d8b 100644 --- a/tests/test_to_contiguous.py +++ b/tests/test_to_contiguous.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_to_cupy.py b/tests/test_to_cupy.py index 36edf24f3f..12a377181d 100644 --- a/tests/test_to_cupy.py +++ b/tests/test_to_cupy.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from unittest import skipUnless diff --git a/tests/test_to_cupyd.py b/tests/test_to_cupyd.py index 3e778ae269..e9a3488489 100644 --- a/tests/test_to_cupyd.py +++ b/tests/test_to_cupyd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from unittest import skipUnless diff --git a/tests/test_to_device.py b/tests/test_to_device.py index 9f78119326..cad2b65316 100644 --- a/tests/test_to_device.py +++ b/tests/test_to_device.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_to_deviced.py b/tests/test_to_deviced.py index b3ee490566..093c3b0c4d 100644 --- a/tests/test_to_deviced.py +++ b/tests/test_to_deviced.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_to_from_meta_tensord.py b/tests/test_to_from_meta_tensord.py index 43a0e99081..470826313a 100644 --- a/tests/test_to_from_meta_tensord.py +++ b/tests/test_to_from_meta_tensord.py @@ -9,11 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import random import string import unittest from copy import deepcopy -from typing import Optional, Union import numpy as np import torch @@ -67,7 +68,7 @@ def check( shape: bool = True, vals: bool = True, ids: bool = True, - device: Optional[Union[str, torch.device]] = None, + device: str | torch.device | None = None, meta: bool = True, check_ids: bool = False, **kwargs, diff --git a/tests/test_to_numpy.py b/tests/test_to_numpy.py index e1f135a289..0c604fb9d4 100644 --- a/tests/test_to_numpy.py +++ b/tests/test_to_numpy.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from unittest import skipUnless diff --git a/tests/test_to_numpyd.py b/tests/test_to_numpyd.py index ba7cf798ef..d25bdf14a5 100644 --- a/tests/test_to_numpyd.py +++ b/tests/test_to_numpyd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from unittest import skipUnless diff --git a/tests/test_to_onehot.py b/tests/test_to_onehot.py index c08672bfb2..52307900af 100644 --- a/tests/test_to_onehot.py +++ b/tests/test_to_onehot.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_to_pil.py b/tests/test_to_pil.py index 0a1351028c..e4f74f6e1e 100644 --- a/tests/test_to_pil.py +++ b/tests/test_to_pil.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from typing import TYPE_CHECKING from unittest import skipUnless diff --git a/tests/test_to_pild.py b/tests/test_to_pild.py index d00ecf13d4..4eb5999b15 100644 --- a/tests/test_to_pild.py +++ b/tests/test_to_pild.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from typing import TYPE_CHECKING from unittest import skipUnless diff --git a/tests/test_to_tensor.py b/tests/test_to_tensor.py index cd1a814f21..cde845c246 100644 --- a/tests/test_to_tensor.py +++ b/tests/test_to_tensor.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_to_tensord.py b/tests/test_to_tensord.py index 4c1f2172ae..82456786fd 100644 --- a/tests/test_to_tensord.py +++ b/tests/test_to_tensord.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_torchscript_utils.py b/tests/test_torchscript_utils.py index cdf2f19eb3..ec24f388f1 100644 --- a/tests/test_torchscript_utils.py +++ b/tests/test_torchscript_utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import tempfile import unittest diff --git a/tests/test_torchvision.py b/tests/test_torchvision.py index 68b9413e65..9cd536aa6f 100644 --- a/tests/test_torchvision.py +++ b/tests/test_torchvision.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from parameterized import parameterized diff --git a/tests/test_torchvision_fc_model.py b/tests/test_torchvision_fc_model.py index d7341bc71e..5f92a1f8b4 100644 --- a/tests/test_torchvision_fc_model.py +++ b/tests/test_torchvision_fc_model.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from unittest import skipUnless diff --git a/tests/test_torchvisiond.py b/tests/test_torchvisiond.py index b72c6f86f1..b2a6bcafc5 100644 --- a/tests/test_torchvisiond.py +++ b/tests/test_torchvisiond.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_traceable_transform.py b/tests/test_traceable_transform.py index bc6aad3a62..cf3da7139a 100644 --- a/tests/test_traceable_transform.py +++ b/tests/test_traceable_transform.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from monai.transforms.inverse import TraceableTransform diff --git a/tests/test_train_mode.py b/tests/test_train_mode.py index 231e3854f0..6136e2f7db 100644 --- a/tests/test_train_mode.py +++ b/tests/test_train_mode.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_transchex.py b/tests/test_transchex.py index 713bc35f56..9ad847cdaa 100644 --- a/tests/test_transchex.py +++ b/tests/test_transchex.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_transform.py b/tests/test_transform.py index a6c5001147..57903ab88c 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_transformerblock.py b/tests/test_transformerblock.py index d6131d010c..2650367886 100644 --- a/tests/test_transformerblock.py +++ b/tests/test_transformerblock.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_transpose.py b/tests/test_transpose.py index 94a5b49c3a..0c9ae1c7e3 100644 --- a/tests/test_transpose.py +++ b/tests/test_transpose.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_transposed.py b/tests/test_transposed.py index 14e62eb9da..ab80520fc9 100644 --- a/tests/test_transposed.py +++ b/tests/test_transposed.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest from copy import deepcopy diff --git a/tests/test_tversky_loss.py b/tests/test_tversky_loss.py index 22f57cc8c6..d1175f40c5 100644 --- a/tests/test_tversky_loss.py +++ b/tests/test_tversky_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_unet.py b/tests/test_unet.py index a90e32230b..9cb4af3379 100644 --- a/tests/test_unet.py +++ b/tests/test_unet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_unetr.py b/tests/test_unetr.py index b233c72f24..406d30aa12 100644 --- a/tests/test_unetr.py +++ b/tests/test_unetr.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_unetr_block.py b/tests/test_unetr_block.py index 8a4ee3a163..60004be25e 100644 --- a/tests/test_unetr_block.py +++ b/tests/test_unetr_block.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_unified_focal_loss.py b/tests/test_unified_focal_loss.py index 1a7bb91059..0e7217e2b4 100644 --- a/tests/test_unified_focal_loss.py +++ b/tests/test_unified_focal_loss.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_upsample_block.py b/tests/test_upsample_block.py index 535ad80c11..a82a31b064 100644 --- a/tests/test_upsample_block.py +++ b/tests/test_upsample_block.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_utils_pytorch_numpy_unification.py b/tests/test_utils_pytorch_numpy_unification.py index 7041a09f52..619ae8aee3 100644 --- a/tests/test_utils_pytorch_numpy_unification.py +++ b/tests/test_utils_pytorch_numpy_unification.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_varautoencoder.py b/tests/test_varautoencoder.py index a6315ebc63..b050983d2c 100644 --- a/tests/test_varautoencoder.py +++ b/tests/test_varautoencoder.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_varnet.py b/tests/test_varnet.py index c715e7d37f..3ec6b0f087 100644 --- a/tests/test_varnet.py +++ b/tests/test_varnet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_version_leq.py b/tests/test_version_leq.py index 725c1ee128..ef9e70ad86 100644 --- a/tests/test_version_leq.py +++ b/tests/test_version_leq.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import itertools import unittest diff --git a/tests/test_video_datasets.py b/tests/test_video_datasets.py index 78e015e350..eedbe212eb 100644 --- a/tests/test_video_datasets.py +++ b/tests/test_video_datasets.py @@ -9,9 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest -from typing import Type, Union import torch @@ -31,8 +32,8 @@ class Base: class TestVideoDataset(unittest.TestCase): - video_source: Union[int, str] - ds: Type[VideoDataset] + video_source: int | str + ds: type[VideoDataset] def get_video_source(self): return self.video_source diff --git a/tests/test_vis_cam.py b/tests/test_vis_cam.py index 2137926424..bb3ff7237a 100644 --- a/tests/test_vis_cam.py +++ b/tests/test_vis_cam.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_vis_gradbased.py b/tests/test_vis_gradbased.py index 5af8769872..0fbe328c83 100644 --- a/tests/test_vis_gradbased.py +++ b/tests/test_vis_gradbased.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_vis_gradcam.py b/tests/test_vis_gradcam.py index 8ec5e2c913..f5ba188082 100644 --- a/tests/test_vis_gradcam.py +++ b/tests/test_vis_gradcam.py @@ -9,8 +9,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest -from typing import Any, List +from typing import Any import numpy as np import torch @@ -28,8 +30,8 @@ def __call__(self, x, adjoint_info): return super().__call__(x) -TESTS: List[Any] = [] -TESTS_ILL: List[Any] = [] +TESTS: list[Any] = [] +TESTS_ILL: list[Any] = [] for cam in (GradCAM, GradCAMpp): # 2D diff --git a/tests/test_vit.py b/tests/test_vit.py index 33a7902fad..62f8ad4f23 100644 --- a/tests/test_vit.py +++ b/tests/test_vit.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_vitautoenc.py b/tests/test_vitautoenc.py index f7685f946c..f9f489b9b9 100644 --- a/tests/test_vitautoenc.py +++ b/tests/test_vitautoenc.py @@ -8,6 +8,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_vnet.py b/tests/test_vnet.py index add0396bd8..633893ce51 100644 --- a/tests/test_vnet.py +++ b/tests/test_vnet.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_vote_ensemble.py b/tests/test_vote_ensemble.py index 79868d4706..32ff120c5d 100644 --- a/tests/test_vote_ensemble.py +++ b/tests/test_vote_ensemble.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_vote_ensembled.py b/tests/test_vote_ensembled.py index e42a57f3b7..17f9d54835 100644 --- a/tests/test_vote_ensembled.py +++ b/tests/test_vote_ensembled.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_warp.py b/tests/test_warp.py index 31f3540c9e..0f1ef3101f 100644 --- a/tests/test_warp.py +++ b/tests/test_warp.py @@ -8,6 +8,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest diff --git a/tests/test_watershed.py b/tests/test_watershed.py index 705ddce817..a5a232ba3c 100644 --- a/tests/test_watershed.py +++ b/tests/test_watershed.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_watershedd.py b/tests/test_watershedd.py index 6e04409e1d..c12f5ad140 100644 --- a/tests/test_watershedd.py +++ b/tests/test_watershedd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_weight_init.py b/tests/test_weight_init.py index c850ff4ce6..376faacc56 100644 --- a/tests/test_weight_init.py +++ b/tests/test_weight_init.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_weighted_random_sampler_dist.py b/tests/test_weighted_random_sampler_dist.py index d5322b6482..d38bab54f0 100644 --- a/tests/test_weighted_random_sampler_dist.py +++ b/tests/test_weighted_random_sampler_dist.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_with_allow_missing_keys.py b/tests/test_with_allow_missing_keys.py index 36d5c0c843..ec55654f07 100644 --- a/tests/test_with_allow_missing_keys.py +++ b/tests/test_with_allow_missing_keys.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_write_metrics_reports.py b/tests/test_write_metrics_reports.py index 769803682a..4f61e43fe1 100644 --- a/tests/test_write_metrics_reports.py +++ b/tests/test_write_metrics_reports.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import csv import os import tempfile diff --git a/tests/test_wsireader.py b/tests/test_wsireader.py index 45f11bb138..03af7a3f66 100644 --- a/tests/test_wsireader.py +++ b/tests/test_wsireader.py @@ -9,9 +9,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import os import unittest -from typing import Any, Tuple +from typing import Any from unittest import skipUnless import numpy as np @@ -255,7 +257,7 @@ def test_read_malformats(self, img_expected): @parameterized.expand([TEST_CASE_TRANSFORM_0]) def test_with_dataloader( - self, file_path: PathLike, level: int, expected_spatial_shape: Any, expected_shape: Tuple[int, ...] + self, file_path: PathLike, level: int, expected_spatial_shape: Any, expected_shape: tuple[int, ...] ): train_transform = Compose( [ @@ -362,7 +364,7 @@ def test_read_malformats(self, img_expected): @parameterized.expand([TEST_CASE_TRANSFORM_0]) def test_with_dataloader( - self, file_path: PathLike, level: int, expected_spatial_shape: Any, expected_shape: Tuple[int, ...] + self, file_path: PathLike, level: int, expected_spatial_shape: Any, expected_shape: tuple[int, ...] ): train_transform = Compose( [ @@ -379,7 +381,7 @@ def test_with_dataloader( @parameterized.expand([TEST_CASE_TRANSFORM_0]) def test_with_dataloader_batch( - self, file_path: PathLike, level: int, expected_spatial_shape: Any, expected_shape: Tuple[int, ...] + self, file_path: PathLike, level: int, expected_spatial_shape: Any, expected_shape: tuple[int, ...] ): train_transform = Compose( [ diff --git a/tests/test_zipdataset.py b/tests/test_zipdataset.py index f381e0a453..de8a8e80d6 100644 --- a/tests/test_zipdataset.py +++ b/tests/test_zipdataset.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import torch diff --git a/tests/test_zoom.py b/tests/test_zoom.py index 1d0447e319..9d1d77451f 100644 --- a/tests/test_zoom.py +++ b/tests/test_zoom.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/test_zoom_affine.py b/tests/test_zoom_affine.py index 3c4bcd302c..dc39a4f1c2 100644 --- a/tests/test_zoom_affine.py +++ b/tests/test_zoom_affine.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import nibabel as nib diff --git a/tests/test_zoomd.py b/tests/test_zoomd.py index b6ff86e474..b171a6b49c 100644 --- a/tests/test_zoomd.py +++ b/tests/test_zoomd.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import unittest import numpy as np diff --git a/tests/testing_data/cpp_resample_answers.py b/tests/testing_data/cpp_resample_answers.py index 93f596619e..266c45a508 100644 --- a/tests/testing_data/cpp_resample_answers.py +++ b/tests/testing_data/cpp_resample_answers.py @@ -9,14 +9,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import csv import os import warnings -from typing import List, Optional -def _read_testing_data_answers(fname: Optional[str] = None, delimiter=",") -> List: - answers: List = [] +def _read_testing_data_answers(fname: str | None = None, delimiter=",") -> list: + answers: list = [] if not fname: return answers # read answers from directory of the current file @@ -37,5 +38,5 @@ def _read_testing_data_answers(fname: Optional[str] = None, delimiter=",") -> Li return answers -Expected_1D_GP_fwd: List = _read_testing_data_answers(fname="1D_BP_fwd.txt") -Expected_1D_GP_bwd: List = _read_testing_data_answers(fname="1D_BP_bwd.txt") +Expected_1D_GP_fwd: list = _read_testing_data_answers(fname="1D_BP_fwd.txt") +Expected_1D_GP_bwd: list = _read_testing_data_answers(fname="1D_BP_bwd.txt") diff --git a/tests/testing_data/integration_answers.py b/tests/testing_data/integration_answers.py index 1907e3c4a5..8bf6b1e368 100644 --- a/tests/testing_data/integration_answers.py +++ b/tests/testing_data/integration_answers.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import numpy as np EXPECTED_ANSWERS = [ diff --git a/tests/utils.py b/tests/utils.py index 23d4cc578b..280b848806 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -9,6 +9,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import copy import datetime import functools @@ -28,7 +30,7 @@ from contextlib import contextmanager from functools import partial, reduce from subprocess import PIPE, Popen -from typing import Callable, Optional, Tuple, Union +from typing import Callable from urllib.error import ContentTooShortError, HTTPError import numpy as np @@ -80,7 +82,7 @@ def clone(data: NdarrayTensor) -> NdarrayTensor: def assert_allclose( actual: NdarrayOrTensor, desired: NdarrayOrTensor, - type_test: Union[bool, str] = True, + type_test: bool | str = True, device_test: bool = False, *args, **kwargs, @@ -341,7 +343,7 @@ def make_nifti_image( return fname -def make_rand_affine(ndim: int = 3, random_state: Optional[np.random.RandomState] = None): +def make_rand_affine(ndim: int = 3, random_state: np.random.RandomState | None = None): """Create random affine transformation (with values == -1, 0 or 1).""" rs = np.random.random.__self__ if random_state is None else random_state # type: ignore @@ -403,13 +405,13 @@ def __init__( nnodes: int = 1, nproc_per_node: int = 1, master_addr: str = "localhost", - master_port: Optional[int] = None, - node_rank: Optional[int] = None, + master_port: int | None = None, + node_rank: int | None = None, timeout=60, init_method=None, - backend: Optional[str] = None, - daemon: Optional[bool] = None, - method: Optional[str] = "spawn", + backend: str | None = None, + daemon: bool | None = None, + method: str | None = "spawn", verbose: bool = False, ): """ @@ -539,8 +541,8 @@ class TimedCall: def __init__( self, seconds: float = 60.0, - daemon: Optional[bool] = None, - method: Optional[str] = "spawn", + daemon: bool | None = None, + method: str | None = "spawn", force_quit: bool = True, skip_timing=False, ): @@ -782,7 +784,7 @@ def command_line_tests(cmd, copy_env=True): raise RuntimeError(f"subprocess call error {e.returncode}: {errors}, {output}") from e -TEST_TORCH_TENSORS: Tuple = (torch.as_tensor,) +TEST_TORCH_TENSORS: tuple = (torch.as_tensor,) if torch.cuda.is_available(): gpu_tensor: Callable = partial(torch.as_tensor, device="cuda") TEST_TORCH_TENSORS = TEST_TORCH_TENSORS + (gpu_tensor,) @@ -791,9 +793,9 @@ def command_line_tests(cmd, copy_env=True): [[2.0, 0.0, 0.0, 0.0], [0.0, 2.0, 0.0, 0.0], [0.0, 0.0, 2.0, 0.0], [0.0, 0.0, 0.0, 1.0]] ) _metatensor_creator = partial(MetaTensor, meta={"a": "b", "affine": DEFAULT_TEST_AFFINE}) -TEST_NDARRAYS_NO_META_TENSOR: Tuple[Callable] = (np.array,) + TEST_TORCH_TENSORS # type: ignore -TEST_NDARRAYS: Tuple[Callable] = TEST_NDARRAYS_NO_META_TENSOR + (_metatensor_creator,) # type: ignore -TEST_TORCH_AND_META_TENSORS: Tuple[Callable] = TEST_TORCH_TENSORS + (_metatensor_creator,) # type: ignore +TEST_NDARRAYS_NO_META_TENSOR: tuple[Callable] = (np.array,) + TEST_TORCH_TENSORS # type: ignore +TEST_NDARRAYS: tuple[Callable] = TEST_NDARRAYS_NO_META_TENSOR + (_metatensor_creator,) # type: ignore +TEST_TORCH_AND_META_TENSORS: tuple[Callable] = TEST_TORCH_TENSORS + (_metatensor_creator,) # type: ignore # alias for branch tests TEST_NDARRAYS_ALL = TEST_NDARRAYS