diff --git a/examples/apps/ai_livertumor_seg_app/livertumor_seg_operator.py b/examples/apps/ai_livertumor_seg_app/livertumor_seg_operator.py index d7b18727..1471ee5b 100644 --- a/examples/apps/ai_livertumor_seg_app/livertumor_seg_operator.py +++ b/examples/apps/ai_livertumor_seg_app/livertumor_seg_operator.py @@ -52,14 +52,12 @@ class LiverTumorSegOperator(Operator): """ def __init__(self): - self.logger = logging.getLogger("{}.{}".format(__name__, type(self).__name__)) super().__init__() self._input_dataset_key = "image" self._pred_dataset_key = "pred" def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext): - input_image = op_input.get("image") if not input_image: raise ValueError("Input image is not found.") diff --git a/examples/apps/ai_unetr_seg_app/unetr_seg_operator.py b/examples/apps/ai_unetr_seg_app/unetr_seg_operator.py index 7b45d208..3f036503 100644 --- a/examples/apps/ai_unetr_seg_app/unetr_seg_operator.py +++ b/examples/apps/ai_unetr_seg_app/unetr_seg_operator.py @@ -46,14 +46,12 @@ class UnetrSegOperator(Operator): """ def __init__(self): - self.logger = logging.getLogger("{}.{}".format(__name__, type(self).__name__)) super().__init__() self._input_dataset_key = "image" self._pred_dataset_key = "pred" def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext): - input_image = op_input.get("image") if not input_image: raise ValueError("Input image is not found.") diff --git a/monai/deploy/core/application.py b/monai/deploy/core/application.py index 106ff8d5..c6261b4d 100644 --- a/monai/deploy/core/application.py +++ b/monai/deploy/core/application.py @@ -82,7 +82,7 @@ def __init__( self.description = get_docstring(self.__class__) if not self.version: try: - from _version import get_versions + from monai.deploy._version import get_versions self.version = get_versions()["version"] except ImportError: diff --git a/monai/deploy/core/executors/single_process_executor.py b/monai/deploy/core/executors/single_process_executor.py index a1086fc7..85c01665 100644 --- a/monai/deploy/core/executors/single_process_executor.py +++ b/monai/deploy/core/executors/single_process_executor.py @@ -141,7 +141,7 @@ def run(self): ) next_op_exec_context = ExecutionContext(exec_context, next_op) - for (out_label, in_labels) in io_map.items(): + for out_label, in_labels in io_map.items(): output = op_exec_context.output_context.get(out_label) for in_label in in_labels: next_op_exec_context.input_context.set(output, in_label) diff --git a/monai/deploy/core/graphs/nx_digraph.py b/monai/deploy/core/graphs/nx_digraph.py index 34397b12..a02ae250 100644 --- a/monai/deploy/core/graphs/nx_digraph.py +++ b/monai/deploy/core/graphs/nx_digraph.py @@ -52,5 +52,5 @@ def gen_worklist(self) -> Generator[Optional[Operator], None, None]: return worklist def gen_next_operators(self, op: Operator) -> Generator[Optional[Operator], None, None]: - for (_, v) in self._graph.out_edges(op): + for _, v in self._graph.out_edges(op): yield v diff --git a/monai/deploy/operators/dicom_data_loader_operator.py b/monai/deploy/operators/dicom_data_loader_operator.py index f1b57b1a..60937661 100644 --- a/monai/deploy/operators/dicom_data_loader_operator.py +++ b/monai/deploy/operators/dicom_data_loader_operator.py @@ -118,7 +118,6 @@ def _load_data(self, files: List[str]): logging.warning(f"Ignored {file}, reason being: {ex}") for sop_instance in sop_instances: - study_instance_uid = sop_instance[0x0020, 0x000D].value.name # name is the UID as str if study_instance_uid not in study_dict: diff --git a/monai/deploy/operators/dicom_encapsulated_pdf_writer_operator.py b/monai/deploy/operators/dicom_encapsulated_pdf_writer_operator.py index ddafac65..bd66915f 100644 --- a/monai/deploy/operators/dicom_encapsulated_pdf_writer_operator.py +++ b/monai/deploy/operators/dicom_encapsulated_pdf_writer_operator.py @@ -42,7 +42,6 @@ @md.output("dicom_instance", DataPath, IOType.DISK) @md.env(pip_packages=["pydicom >= 1.4.2", "PyPDF2 >= 2.11.1", "monai"]) class DICOMEncapsulatedPDFWriterOperator(Operator): - DCM_EXTENSION = ".dcm" def __init__( diff --git a/monai/deploy/operators/dicom_seg_writer_operator.py b/monai/deploy/operators/dicom_seg_writer_operator.py index efa33d9f..124415e5 100644 --- a/monai/deploy/operators/dicom_seg_writer_operator.py +++ b/monai/deploy/operators/dicom_seg_writer_operator.py @@ -11,6 +11,7 @@ import datetime import logging +import os from pathlib import Path from random import randint from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Union diff --git a/monai/deploy/operators/dicom_series_to_volume_operator.py b/monai/deploy/operators/dicom_series_to_volume_operator.py index e5837b8f..e89dd265 100644 --- a/monai/deploy/operators/dicom_series_to_volume_operator.py +++ b/monai/deploy/operators/dicom_series_to_volume_operator.py @@ -389,7 +389,6 @@ def _get_instance_properties(obj: object, not_none: bool = True) -> Dict: def test(): - from pathlib import Path from monai.deploy.operators.dicom_data_loader_operator import DICOMDataLoaderOperator diff --git a/monai/deploy/operators/dicom_text_sr_writer_operator.py b/monai/deploy/operators/dicom_text_sr_writer_operator.py index 422a2770..5b7937e3 100644 --- a/monai/deploy/operators/dicom_text_sr_writer_operator.py +++ b/monai/deploy/operators/dicom_text_sr_writer_operator.py @@ -39,7 +39,6 @@ @md.output("dicom_instance", DataPath, IOType.DISK) @md.env(pip_packages=["pydicom >= 1.4.2", "monai"]) class DICOMTextSRWriterOperator(Operator): - # File extension for the generated DICOM Part 10 file. DCM_EXTENSION = ".dcm" diff --git a/monai/deploy/operators/dicom_utils.py b/monai/deploy/operators/dicom_utils.py index 6d434953..6df12fe7 100644 --- a/monai/deploy/operators/dicom_utils.py +++ b/monai/deploy/operators/dicom_utils.py @@ -60,7 +60,6 @@ class ModelInfo(object): """ def __init__(self, creator: str = "", name: str = "", version: str = "", uid: str = ""): - self.creator = creator if isinstance(creator, str) else "" self.name = name if isinstance(name, str) else "" self.version = version if isinstance(version, str) else "" @@ -77,7 +76,6 @@ def __init__( series_number: str = "0000", software_version_number: str = "", ): - self.manufacturer = manufacturer if isinstance(manufacturer, str) else "" self.manufacturer_model = manufacturer_model if isinstance(manufacturer_model, str) else "" self.series_number = series_number if isinstance(series_number, str) else "" diff --git a/monai/deploy/operators/monai_bundle_inference_operator.py b/monai/deploy/operators/monai_bundle_inference_operator.py index 226e27af..156abe22 100644 --- a/monai/deploy/operators/monai_bundle_inference_operator.py +++ b/monai/deploy/operators/monai_bundle_inference_operator.py @@ -78,8 +78,8 @@ def _read_from_archive(archive, root_name: str, config_name: str, do_search=True # Try directly read with constructed and expected path into the archive for suffix in bundle_suffixes: + path = Path(root_name, config_folder, config_name).with_suffix(suffix) try: - path = Path(root_name, config_folder, config_name).with_suffix(suffix) logging.debug(f"Trying to read config '{config_name}' content from {path}.") content_text = archive.read(str(path)) break @@ -268,6 +268,7 @@ def _ensure_str_list(config_names): DEFAULT_BundleConfigNames = BundleConfigNames() + # The operator env decorator defines the required pip packages commonly used in the Bundles. # The MONAI Deploy App SDK packager currently relies on the App to consolidate all required packages in order to # install them in the MAP Docker image. @@ -768,7 +769,6 @@ def _convert_from_image(self, img: Image) -> Tuple[np.ndarray, Dict]: or ("spacing" in img_meta_dict and "original_affine" in img_meta_dict) or "row_pixel_spacing" not in img_meta_dict ): - return img.asnumpy(), img_meta_dict else: return self._convert_from_image_dicom_source(img) diff --git a/monai/deploy/utils/importutil.py b/monai/deploy/utils/importutil.py index 1cba334f..0fefb0d8 100644 --- a/monai/deploy/utils/importutil.py +++ b/monai/deploy/utils/importutil.py @@ -155,7 +155,7 @@ def exact_version(the_module, version_str: str = "") -> bool: Returns True if the module's __version__ matches version_str """ if not hasattr(the_module, "__version__"): - warnings.warn(f"{the_module} has no attribute __version__ in exact_version check.") + warnings.warn(f"{the_module} has no attribute __version__ in exact_version check.", stacklevel=2) return False return bool(the_module.__version__ == version_str) diff --git a/notebooks/tutorials/02_mednist_app-prebuilt.ipynb b/notebooks/tutorials/02_mednist_app-prebuilt.ipynb index 60d335f1..5c973452 100644 --- a/notebooks/tutorials/02_mednist_app-prebuilt.ipynb +++ b/notebooks/tutorials/02_mednist_app-prebuilt.ipynb @@ -84,7 +84,7 @@ "Collecting networkx>=2.4\n", " Downloading networkx-2.5.1-py3-none-any.whl (1.6 MB)\n", "\u001b[K |████████████████████████████████| 1.6 MB 12.8 MB/s eta 0:00:01\n", - "\u001b[?25hCollecting typeguard>=2.12.1\n", + "\u001b[?25hCollecting typeguard~=2.12.1\n", " Downloading typeguard-2.12.1-py3-none-any.whl (17 kB)\n", "Collecting numpy>=1.17\n", " Downloading numpy-1.19.5-cp36-cp36m-manylinux2010_x86_64.whl (14.8 MB)\n", diff --git a/notebooks/tutorials/03_segmentation_app.ipynb b/notebooks/tutorials/03_segmentation_app.ipynb index 29bc13e9..fc02e3a4 100644 --- a/notebooks/tutorials/03_segmentation_app.ipynb +++ b/notebooks/tutorials/03_segmentation_app.ipynb @@ -104,7 +104,7 @@ "!python -c \"import pydicom\" || pip install -q \"pydicom>=1.4.2\"\n", "!python -c \"import highdicom\" || pip install -q \"highdicom>=0.18.2\"\n", "!python -c \"import SimpleITK\" || pip install -q \"SimpleITK>=2.0.0\"\n", - "!python -c \"import typeguard\" || pip install -q \"typeguard>=2.12.1\"\n", + "!python -c \"import typeguard\" || pip install -q \"typeguard~=2.12.1\"\n", "\n", "# Install MONAI Deploy App SDK package\n", "!python -c \"import monai.deploy\" || pip install --upgrade -q \"monai-deploy-app-sdk\"" diff --git a/notebooks/tutorials/03_segmentation_viz_app.ipynb b/notebooks/tutorials/03_segmentation_viz_app.ipynb index d9ce34aa..5f7ce27b 100644 --- a/notebooks/tutorials/03_segmentation_viz_app.ipynb +++ b/notebooks/tutorials/03_segmentation_viz_app.ipynb @@ -113,7 +113,7 @@ "!python -c \"import pydicom\" || pip install -q \"pydicom>=1.4.2\"\n", "!python -c \"import highdicom\" || pip install -q \"highdicom>=0.18.2\"\n", "!python -c \"import SimpleITK\" || pip install -q \"SimpleITK>=2.0.0\"\n", - "!python -c \"import typeguard\" || pip install -q \"typeguard>=2.12.1\"\n", + "!python -c \"import typeguard\" || pip install -q \"typeguard~=2.12.1\"\n", "\n", "# Install MONAI Deploy App SDK package\n", "!python -c \"import monai.deploy\" || pip install --upgrade -q \"monai-deploy-app-sdk\"\n", diff --git a/notebooks/tutorials/06_monai_bundle_app.ipynb b/notebooks/tutorials/06_monai_bundle_app.ipynb index d1d8baf7..62090c80 100644 --- a/notebooks/tutorials/06_monai_bundle_app.ipynb +++ b/notebooks/tutorials/06_monai_bundle_app.ipynb @@ -106,7 +106,7 @@ "!python -c \"import pydicom\" || pip install -q \"pydicom>=1.4.2\"\n", "!python -c \"import highdicom\" || pip install -q \"highdicom>=0.18.2\"\n", "!python -c \"import SimpleITK\" || pip install -q \"SimpleITK>=2.0.0\"\n", - "!python -c \"import typeguard\" || pip install -q \"typeguard>=2.12.1\"\n", + "!python -c \"import typeguard\" || pip install -q \"typeguard~=2.12.1\"\n", "\n", "# Install MONAI Deploy App SDK package\n", "!python -c \"import monai.deploy\" || pip install --upgrade -q \"monai-deploy-app-sdk\"" diff --git a/notebooks/tutorials/07_multi_model_app.ipynb b/notebooks/tutorials/07_multi_model_app.ipynb index ac737859..a475863c 100644 --- a/notebooks/tutorials/07_multi_model_app.ipynb +++ b/notebooks/tutorials/07_multi_model_app.ipynb @@ -144,7 +144,7 @@ "!python -c \"import pydicom\" || pip install -q \"pydicom>=1.4.2\"\n", "!python -c \"import highdicom\" || pip install -q \"highdicom>=0.18.2\"\n", "!python -c \"import SimpleITK\" || pip install -q \"SimpleITK>=2.0.0\"\n", - "!python -c \"import typeguard\" || pip install -q \"typeguard>=2.12.1\"\n", + "!python -c \"import typeguard\" || pip install -q \"typeguard~=2.12.1\"\n", "\n", "# Install MONAI Deploy App SDK package\n", "!python -c \"import monai.deploy\" || pip install --upgrade -q \"monai-deploy-app-sdk\"" diff --git a/platforms/nuance_pin/app/inference.py b/platforms/nuance_pin/app/inference.py index b2d8e6e4..50c104cd 100644 --- a/platforms/nuance_pin/app/inference.py +++ b/platforms/nuance_pin/app/inference.py @@ -82,7 +82,6 @@ def detection_list(self): @md.env(pip_packages=["monai>=0.8.1", "torch>=1.5", "numpy>=1.21", "nibabel"]) class LungNoduleInferenceOperator(Operator): def __init__(self, model_path: str = "model/model.ts"): - self.logger = logging.getLogger("{}.{}".format(__name__, type(self).__name__)) super().__init__() self._input_dataset_key = "image" @@ -125,7 +124,6 @@ def __init__(self, model_path: str = "model/model.ts"): self.detector.eval() def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext): - input_image = op_input.get("image") if not input_image: raise ValueError("Input image not found.") diff --git a/platforms/nuance_pin/app/post_inference_ops.py b/platforms/nuance_pin/app/post_inference_ops.py index 2a0d78a2..200335bd 100644 --- a/platforms/nuance_pin/app/post_inference_ops.py +++ b/platforms/nuance_pin/app/post_inference_ops.py @@ -36,7 +36,6 @@ def __init__(self, pin_processor: Optional[AiJobProcessor], *args, **kwargs): self.pin_processor = pin_processor def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext): - selected_study = op_input.get("original_dicom")[0] # assuming a single study selected_series = selected_study.selected_series[0] # assuming a single series detection_result: DetectionResult = op_input.get("detection_predictions").detection_list[0] @@ -60,7 +59,6 @@ def compute(self, op_input: InputContext, op_output: OutputContext, context: Exe accession = all_ref_images[0].AccessionNumber for inst_num, (box_data, box_score) in enumerate(zip(detection_result.box_data, detection_result.score_data)): - tracking_id = f"{accession}_nodule_{inst_num}" # site-specific ID tracking_uid = hd.UID() @@ -160,7 +158,6 @@ def __init__(self, pin_processor: Optional[AiJobProcessor], *args, **kwargs): self.pin_processor = pin_processor def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext): - selected_study = op_input.get("original_dicom")[0] # assuming a single study selected_series = selected_study.selected_series[0] # assuming a single series detection_result: DetectionResult = op_input.get("detection_predictions").detection_list[0] @@ -190,7 +187,6 @@ def compute(self, op_input: InputContext, op_output: OutputContext, context: Exe slice_coords = list(range(len(selected_series.series.get_sop_instances()))) for box_data, box_score in zip(detection_result.box_data, detection_result.score_data): - affected_slice_idx = [ idx for idx, slice_coord in enumerate(slice_coords) diff --git a/platforms/nuance_pin/app_wrapper.py b/platforms/nuance_pin/app_wrapper.py index c9601812..99e14583 100644 --- a/platforms/nuance_pin/app_wrapper.py +++ b/platforms/nuance_pin/app_wrapper.py @@ -50,7 +50,6 @@ class MONAIAppWrapper(AiJobProcessor): - partner_name = os.environ["AI_PARTNER_NAME"] service_name = os.environ["AI_SVC_NAME"] service_version = os.environ["AI_SVC_VERSION"] diff --git a/pyproject.toml b/pyproject.toml index e820df8e..a7aefad4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,5 +36,78 @@ exclude = ''' ''' # https://github.com/microsoft/pyright/blob/main/docs/configuration.md +# NOTE: All relative paths are relative to the location of this file. [tool.pyright] -ignore = ["versioneer.py", "_version.py"] \ No newline at end of file +ignore = ["versioneer.py", "_version.py"] + +# https://google.github.io/pytype/ +[tool.pytype] +# Space-separated list of files or directories to exclude. +exclude = [ + 'versioneer.py', + '_version.py', + '**/_version.py', +] +# Space-separated list of files or directories to process. +inputs = [ + 'monai', +] +# Keep going past errors to analyze as many files as possible. +keep_going = true +# Run N jobs in parallel. When 'auto' is used, this will be equivalent to the +# number of CPUs on the host system. +jobs = 8 +# All pytype output goes here. +output = '.pytype' +# Platform (e.g., "linux", "win32") that the target code runs on. +platform = 'linux' +# Paths to source code directories, separated by ':'. +pythonpath = '.' + +# Always use function return type annotations. This flag is temporary and will +# be removed once this behavior is enabled by default. +always_use_return_annotations = false + +# Enable parameter count checks for overriding methods. This flag is temporary +# and will be removed once this behavior is enabled by default. +overriding_parameter_count_checks = false + +# Enable return type checks for overriding methods. This flag is temporary and +# will be removed once this behavior is enabled by default. +overriding_return_type_checks = true + +# Use the enum overlay for more precise enum checking. This flag is temporary +# and will be removed once this behavior is enabled by default. +use_enum_overlay = false + +# Opt-in: Do not allow Any as a return type. +no_return_any = false + +# Experimental: Support pyglib's @cached.property. +enable_cached_property = false + +# Experimental: Infer precise return types even for invalid function calls. +precise_return = false + +# Experimental: Solve unknown types to label with structural types. +protocols = false + +# Experimental: Only load submodules that are explicitly imported. +strict_import = false + +# Experimental: Enable exhaustive checking of function parameter types. +strict_parameter_checks = false + +# Experimental: Emit errors for comparisons between incompatible primitive +# types. +strict_primitive_comparisons = false + +# Space-separated list of error names to ignore. +disable = [ + 'pyi-error', + 'container-type-mismatch', + 'attribute-error', +] + +# Don't report errors. +report_errors = true diff --git a/requirements.txt b/requirements.txt index 92151b3f..ee919f55 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ numpy>=1.21.6 networkx>=2.4 colorama>=0.4.1 -typeguard>=2.12.1 +typeguard~=2.12.1 diff --git a/setup.cfg b/setup.cfg index 8fefc871..fa60f6c1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,7 +26,7 @@ install_requires = numpy>=1.21.6 networkx>=2.4 colorama>=0.4.1 - typeguard>=2.12.1 + typeguard~=2.12.1 [options.extras_require] all = @@ -43,8 +43,13 @@ ignore = E203,E305,E402,E501,E721,E741,F821,F841,F999,W503,W504,C408,E302,W291,E303, # N812 lowercase 'torch.nn.functional' imported as non lowercase 'F' N812, - B024, #abstract base class, but it has no abstract methods - B027 #method in base class with no implementation + #B024, abstract base class, but it has no abstract methods + B024, + # B027, #method in base class with no implementation + B027, + # B905 `zip()` without an explicit `strict=` parameter, but conflicting with pytype + B905 + per_file_ignores = __init__.py: F401 # Allow using camel case for variable/argument names for the sake of readability. @@ -105,38 +110,6 @@ ignore_errors = True # Ignores all non-fatal errors. ignore_errors = True -[pytype] -# Space-separated list of files or directories to exclude. -exclude = versioneer.py _version.py -# Space-separated list of files or directories to process. -inputs = monai -# Keep going past errors to analyze as many files as possible. -keep_going = True -# Run N jobs in parallel. -jobs = 8 -# All pytype output goes here. -output = .pytype -# Paths to source code directories, separated by ':'. -pythonpath = . -# Check attribute values against their annotations. -check_attribute_types = True -# Check container mutations against their annotations. -check_container_types = True -# Check parameter defaults and assignments against their annotations. -check_parameter_types = True -# Check variable values against their annotations. -check_variable_types = True -# Comma or space separated list of error names to ignore. -disable = pyi-error -# Report errors. -report_errors = True -# Experimental: Infer precise return types even for invalid function calls. -precise_return = True -# Experimental: solve unknown types to label with structural types. -protocols = True -# Experimental: Only load submodules that are explicitly imported. -strict_import = False - [tool:pytest] # If a pytest section is found in one of the possible config files # (pytest.ini, tox.ini or setup.cfg), then pytest will not look for any others,