diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index df3754aee..712db5b5e 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -30,7 +30,7 @@ jobs: sudo apt update sudo apt-get install -y libopenjp2-7 libopenjp2-tools python -m pip install --upgrade pip - python -m pip install ruff==0.11.13 pytest pytest-cov pytest-runner + python -m pip install ruff==0.12.2 pytest pytest-cov pytest-runner pip install -r requirements/requirements.txt - name: Cache tiatoolbox static assets uses: actions/cache@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e6676a75d..1dbaf1606 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -60,7 +60,7 @@ repos: - id: rst-inline-touching-normal # Detect mistake of inline code touching normal text in rst. - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.11.13 + rev: v0.12.2 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/benchmarks/annotation_store_alloc.py b/benchmarks/annotation_store_alloc.py index f7b9b8525..b0c77e71e 100644 --- a/benchmarks/annotation_store_alloc.py +++ b/benchmarks/annotation_store_alloc.py @@ -141,6 +141,7 @@ def __exit__(self: memray, *args: object) -> None: import numpy as np import psutil +from shapely import affinity from shapely.geometry import Polygon from tqdm import tqdm @@ -188,8 +189,6 @@ def cell_polygon( round_coords (bool): Round coordinates to integers. Defaults to False. """ - from shapely import affinity - rand_state = np.random.default_rng().__getstate__() rng = np.random.default_rng(seed) if repeat_first: diff --git a/pre-commit/notebook_urls.py b/pre-commit/notebook_urls.py index c8b0f04fb..51190dcc6 100644 --- a/pre-commit/notebook_urls.py +++ b/pre-commit/notebook_urls.py @@ -14,7 +14,7 @@ def git_branch_name() -> str: """Get the current branch name.""" return ( - subprocess.check_output( # noqa: S603 + subprocess.check_output( ["/usr/bin/git", "rev-parse", "--abbrev-ref", "HEAD"], ) .decode() @@ -45,7 +45,7 @@ def git_previous_commit_modified_paths() -> set[Path]: """Get a set of file paths modified in the previous commit.""" return { Path(p) - for p in subprocess.check_output( # noqa: S603 + for p in subprocess.check_output( ["/usr/bin/git", "diff", "--name-only", "HEAD~"], ) .decode() diff --git a/pre-commit/requirements_consistency.py b/pre-commit/requirements_consistency.py index 7daecd0e8..4f8d4f442 100644 --- a/pre-commit/requirements_consistency.py +++ b/pre-commit/requirements_consistency.py @@ -115,7 +115,7 @@ def parse_setup_py(file_path: Path) -> dict[str, Requirement]: pkg_resources.Requirement. """ mock_setup = {} - import setuptools + import setuptools # noqa: PLC0415 setuptools.setup = lambda **kw: mock_setup.update(kw) spec = importlib.util.spec_from_file_location("setup", str(file_path)) diff --git a/requirements/requirements_dev.txt b/requirements/requirements_dev.txt index cb62bc7ae..6d481c964 100644 --- a/requirements/requirements_dev.txt +++ b/requirements/requirements_dev.txt @@ -10,7 +10,7 @@ pytest>=7.2.0 pytest-cov>=4.0.0 pytest-runner>=6.0 pytest-xdist[psutil] -ruff==0.11.13 # This will be updated by pre-commit bot to latest version +ruff==0.12.2 # This will be updated by pre-commit bot to latest version toml>=0.10.2 twine>=4.0.1 wheel>=0.37.1 diff --git a/tests/conftest.py b/tests/conftest.py index fa205c205..2b7de0fd6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -589,9 +589,9 @@ def chdir() -> Callable: """ try: - from contextlib import chdir + from contextlib import chdir # noqa: PLC0415 except ImportError: - from contextlib import AbstractContextManager + from contextlib import AbstractContextManager # noqa: PLC0415 class chdir(AbstractContextManager): # noqa: N801 """Non thread-safe context manager to change the current working directory. diff --git a/tests/test_init.py b/tests/test_init.py index 6d8ed8238..71cb8bb5e 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -16,7 +16,7 @@ def test_set_root_dir(tmp_path: Path) -> None: """Test for setting new root dir.""" # skipcq importlib.reload(tiatoolbox) - from tiatoolbox import rcParam + from tiatoolbox import rcParam # noqa: PLC0415 old_root_dir = rcParam["TIATOOLBOX_HOME"] test_dir_path = tmp_path / "tmp_check" @@ -27,7 +27,7 @@ def test_set_root_dir(tmp_path: Path) -> None: # reimport to see if it overwrites # silence Deep Source because this is an intentional check # skipcq - from tiatoolbox import rcParam + from tiatoolbox import rcParam # noqa: PLC0415 rcParam["TIATOOLBOX_HOME"].mkdir(parents=True) if not Path.exists(test_dir_path): @@ -135,9 +135,9 @@ def test_duplicate_filter(caplog: pytest.LogCaptureFixture) -> None: def test_lazy_import() -> None: """Test lazy import for tiatoolbox.""" - import sys + import sys # noqa: PLC0415 - from tiatoolbox import _lazy_import + from tiatoolbox import _lazy_import # noqa: PLC0415 assert "exceptions" not in sys.modules @@ -151,7 +151,7 @@ def test_lazy_import() -> None: def test_lazy_import_module_not_found() -> None: """'Test lazy import for ModuleNotFoundError.""" - from tiatoolbox import _lazy_import + from tiatoolbox import _lazy_import # noqa: PLC0415 with pytest.raises(ModuleNotFoundError): _lazy_import( diff --git a/tests/test_utils.py b/tests/test_utils.py index 3cfeefbb2..f8908f434 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,6 +3,7 @@ from __future__ import annotations import hashlib +import json import shutil from pathlib import Path from typing import TYPE_CHECKING, NoReturn @@ -27,7 +28,10 @@ from tiatoolbox.annotation.storage import DictionaryStore, SQLiteStore from tiatoolbox.enums import GeometryType from tiatoolbox.models.architecture import fetch_pretrained_weights -from tiatoolbox.models.architecture.utils import compile_model +from tiatoolbox.models.architecture.utils import ( + compile_model, + is_torch_compile_compatible, +) from tiatoolbox.utils import misc from tiatoolbox.utils.exceptions import FileNotSupportedError from tiatoolbox.utils.transforms import locsize2bounds @@ -1348,8 +1352,6 @@ def test_select_device() -> None: def test_save_as_json(tmp_path: Path) -> None: """Test save data to json.""" # This should be broken up into separate tests! - import json - # dict with nested dict, list, and np.array key_dict = { "a1": {"name": "John", "age": 23, "sex": "male"}, @@ -1860,8 +1862,6 @@ def test_torch_compile_disable() -> None: def test_torch_compile_compatibility(caplog: pytest.LogCaptureFixture) -> None: """Test if torch-compile compatibility is checked correctly.""" - from tiatoolbox.models.architecture.utils import is_torch_compile_compatible - is_torch_compile_compatible() assert "torch.compile" in caplog.text diff --git a/tiatoolbox/annotation/dsl.py b/tiatoolbox/annotation/dsl.py index 9e3820630..cbf9bf7d5 100644 --- a/tiatoolbox/annotation/dsl.py +++ b/tiatoolbox/annotation/dsl.py @@ -217,7 +217,7 @@ class SQLTriplet(SQLExpression): def __init__( self: SQLExpression, - lhs: SQLTriplet | str | SQLExpression | Number | bool | object, + lhs: SQLTriplet | str | SQLExpression | Number | bool | object, # noqa: FBT001 op: Callable | str | None = None, rhs: SQLTriplet | str | SQLExpression | Number | SQLNone | object | None = None, ) -> None: @@ -373,7 +373,7 @@ def json_contains(json_str: str, x: object) -> bool: return x in json.loads(json_str) -def sql_is_none(x: SQLExpression | Number | str | bool) -> SQLTriplet: +def sql_is_none(x: SQLExpression | Number | str | bool) -> SQLTriplet: # noqa: FBT001 """Check if x is None. Returns: @@ -384,7 +384,7 @@ def sql_is_none(x: SQLExpression | Number | str | bool) -> SQLTriplet: return SQLTriplet(x, "is_none") -def sql_is_not_none(x: SQLExpression | Number | str | bool) -> SQLTriplet: +def sql_is_not_none(x: SQLExpression | Number | str | bool) -> SQLTriplet: # noqa: FBT001 """Check if x is not None. Returns: diff --git a/tiatoolbox/annotation/storage.py b/tiatoolbox/annotation/storage.py index eed3ee05a..de386f3a1 100644 --- a/tiatoolbox/annotation/storage.py +++ b/tiatoolbox/annotation/storage.py @@ -4047,7 +4047,6 @@ def __len__(self: DictionaryStore) -> int: """Return the length of the instance attributes.""" return len(self._rows) - # flake8: noqa: A003 @classmethod def open(cls: type[AnnotationStore], fp: Path | str | IO) -> AnnotationStore: """Opens :class:`DictionaryStore` from file pointer or path.""" diff --git a/tiatoolbox/cli/common.py b/tiatoolbox/cli/common.py index 26f85625e..c663170c9 100644 --- a/tiatoolbox/cli/common.py +++ b/tiatoolbox/cli/common.py @@ -13,6 +13,7 @@ def add_default_to_usage_help( usage_help: str, + *, default: str | float | bool | None, ) -> str: """Adds default value to usage help string. @@ -36,6 +37,7 @@ def add_default_to_usage_help( def cli_img_input( usage_help: str = "Path to WSI or directory containing WSIs.", + *, multiple: bool | None = None, ) -> Callable: """Enables --img-input option for cli.""" @@ -49,6 +51,7 @@ def cli_img_input( def cli_name( usage_help: str = "User defined name to be used as an identifier.", + *, multiple: bool | None = None, ) -> Callable: """Enable --name option for cli.""" @@ -67,7 +70,7 @@ def cli_output_path( """Enables --output-path option for cli.""" return click.option( "--output-path", - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), type=str, default=default, ) @@ -80,7 +83,7 @@ def cli_file_type( """Enables --file-types option for cli.""" return click.option( "--file-types", - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), default=default, type=str, ) @@ -96,7 +99,7 @@ def cli_mode( input_type = click.Choice(["show", "save"], case_sensitive=False) return click.option( "--mode", - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), default=default, type=input_type, ) @@ -130,7 +133,7 @@ def cli_units( "--units", default=default, type=input_type, - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), ) @@ -143,7 +146,7 @@ def cli_resolution( "--resolution", type=float, default=default, - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), ) @@ -156,7 +159,7 @@ def cli_tile_objective( "--tile-objective-value", type=int, default=default, - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), ) @@ -197,7 +200,7 @@ def cli_method( "--method", type=input_type, default=default, - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), ) @@ -216,7 +219,7 @@ def cli_pretrained_model( """Enables --pretrained-model option for cli.""" return click.option( "--pretrained-model", - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), default=default, ) @@ -229,7 +232,7 @@ def cli_pretrained_weights( """Enables --pretrained-weights option for cli.""" return click.option( "--pretrained-weights", - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), default=default, ) @@ -241,7 +244,7 @@ def cli_device( """Enables --pretrained-weights option for cli.""" return click.option( "--device", - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), default=default, ) @@ -255,7 +258,7 @@ def cli_return_probabilities( return click.option( "--return-probabilities", type=bool, - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), default=default, ) @@ -270,7 +273,7 @@ def cli_merge_predictions( "--merge-predictions", type=bool, default=default, - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), ) @@ -283,7 +286,7 @@ def cli_return_labels( return click.option( "--return-labels", type=bool, - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), default=default, ) @@ -295,7 +298,7 @@ def cli_batch_size( """Enables --batch-size option for cli.""" return click.option( "--batch-size", - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), default=default, ) @@ -312,7 +315,7 @@ def cli_masks( """Enables --masks option for cli.""" return click.option( "--masks", - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), default=default, ) @@ -325,7 +328,7 @@ def cli_auto_generate_mask( """Enables --auto-generate-mask option for cli.""" return click.option( "--auto-generate-mask", - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), type=bool, default=default, ) @@ -340,7 +343,7 @@ def cli_yaml_config_path( """Enables --yaml-config-path option for cli.""" return click.option( "--yaml-config-path", - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), default=default, ) @@ -353,7 +356,7 @@ def cli_num_loader_workers( """Enables --num-loader-workers option for cli.""" return click.option( "--num-loader-workers", - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), type=int, default=default, ) @@ -366,7 +369,7 @@ def cli_num_postproc_workers( """Enables --num-postproc-workers option for cli.""" return click.option( "--num-postproc-workers", - help=add_default_to_usage_help(usage_help, default), + help=add_default_to_usage_help(usage_help, default=default), type=int, default=default, ) @@ -381,7 +384,7 @@ def cli_verbose( return click.option( "--verbose", type=bool, - help=add_default_to_usage_help(usage_help, str(default)), + help=add_default_to_usage_help(usage_help, default=str(default)), default=default, ) @@ -451,7 +454,10 @@ def prepare_file_dir_cli( pathlib.Path: updated output path. """ - from tiatoolbox.utils.misc import grab_files_from_dir, string_to_tuple + from tiatoolbox.utils.misc import ( # noqa: PLC0415 + grab_files_from_dir, + string_to_tuple, + ) img_input = no_input_message(input_file=img_input) file_types_tuple = string_to_tuple(in_str=file_types) @@ -512,7 +518,10 @@ def prepare_model_cli( Output path. """ - from tiatoolbox.utils.misc import grab_files_from_dir, string_to_tuple + from tiatoolbox.utils.misc import ( # noqa: PLC0415 + grab_files_from_dir, + string_to_tuple, + ) img_input = no_input_message(input_file=img_input) output_path = Path(output_path) @@ -558,7 +567,7 @@ def prepare_ioconfig_seg( yaml_config_path: str | Path, ) -> IOConfigABC | None: """Prepare ioconfig for segmentation.""" - import yaml + import yaml # noqa: PLC0415 if pretrained_weights is not None: with Path(yaml_config_path).open() as registry_handle: diff --git a/tiatoolbox/cli/nucleus_instance_segment.py b/tiatoolbox/cli/nucleus_instance_segment.py index fdb4b95ca..ab5ad8548 100644 --- a/tiatoolbox/cli/nucleus_instance_segment.py +++ b/tiatoolbox/cli/nucleus_instance_segment.py @@ -67,8 +67,11 @@ def nucleus_instance_segment( verbose: bool, ) -> None: """Process an image/directory of input images with a patch classification CNN.""" - from tiatoolbox.models import IOSegmentorConfig, NucleusInstanceSegmentor - from tiatoolbox.utils import save_as_json + from tiatoolbox.models import ( # noqa: PLC0415 + IOSegmentorConfig, + NucleusInstanceSegmentor, + ) + from tiatoolbox.utils import save_as_json # noqa: PLC0415 files_all, masks_all, output_path = prepare_model_cli( img_input=img_input, diff --git a/tiatoolbox/cli/patch_predictor.py b/tiatoolbox/cli/patch_predictor.py index 069b6c367..fdb85e3ea 100644 --- a/tiatoolbox/cli/patch_predictor.py +++ b/tiatoolbox/cli/patch_predictor.py @@ -72,8 +72,8 @@ def patch_predictor( verbose: bool, ) -> None: """Process an image/directory of input images with a patch classification CNN.""" - from tiatoolbox.models import PatchPredictor - from tiatoolbox.utils import save_as_json + from tiatoolbox.models import PatchPredictor # noqa: PLC0415 + from tiatoolbox.utils import save_as_json # noqa: PLC0415 files_all, masks_all, output_path = prepare_model_cli( img_input=img_input, diff --git a/tiatoolbox/cli/read_bounds.py b/tiatoolbox/cli/read_bounds.py index a0460cc01..98bd62d4f 100644 --- a/tiatoolbox/cli/read_bounds.py +++ b/tiatoolbox/cli/read_bounds.py @@ -36,10 +36,10 @@ def read_bounds( mode: str, ) -> None: """Read a region in a whole slide image as specified.""" - from PIL import Image + from PIL import Image # noqa: PLC0415 - from tiatoolbox.utils import imwrite - from tiatoolbox.wsicore.wsireader import WSIReader + from tiatoolbox.utils import imwrite # noqa: PLC0415 + from tiatoolbox.wsicore.wsireader import WSIReader # noqa: PLC0415 no_input_message(input_file=img_input) diff --git a/tiatoolbox/cli/save_tiles.py b/tiatoolbox/cli/save_tiles.py index f3211b5fe..7a5b4f4e8 100644 --- a/tiatoolbox/cli/save_tiles.py +++ b/tiatoolbox/cli/save_tiles.py @@ -38,7 +38,7 @@ def save_tiles( verbose: bool, ) -> None: """Display or save WSI metadata.""" - from tiatoolbox.wsicore.wsireader import WSIReader + from tiatoolbox.wsicore.wsireader import WSIReader # noqa: PLC0415 files_all, output_path = prepare_file_dir_cli( img_input, diff --git a/tiatoolbox/cli/semantic_segment.py b/tiatoolbox/cli/semantic_segment.py index cbfe18e58..ec59ca311 100644 --- a/tiatoolbox/cli/semantic_segment.py +++ b/tiatoolbox/cli/semantic_segment.py @@ -61,8 +61,8 @@ def semantic_segment( verbose: bool, ) -> None: """Process an image/directory of input images with a patch classification CNN.""" - from tiatoolbox.models import IOSegmentorConfig, SemanticSegmentor - from tiatoolbox.utils import save_as_json + from tiatoolbox.models import IOSegmentorConfig, SemanticSegmentor # noqa: PLC0415 + from tiatoolbox.utils import save_as_json # noqa: PLC0415 files_all, masks_all, output_path = prepare_model_cli( img_input=img_input, diff --git a/tiatoolbox/cli/show_wsi.py b/tiatoolbox/cli/show_wsi.py index 6a95dec13..6ac5856af 100644 --- a/tiatoolbox/cli/show_wsi.py +++ b/tiatoolbox/cli/show_wsi.py @@ -31,8 +31,8 @@ def show_wsi( colour_map: str, ) -> None: # pragma: no cover """Show a slide together with any overlays.""" - from tiatoolbox.utils.visualization import AnnotationRenderer - from tiatoolbox.visualization.tileserver import TileServer + from tiatoolbox.utils.visualization import AnnotationRenderer # noqa: PLC0415 + from tiatoolbox.visualization.tileserver import TileServer # noqa: PLC0415 renderer = AnnotationRenderer() if colour_by is not None: diff --git a/tiatoolbox/cli/slide_info.py b/tiatoolbox/cli/slide_info.py index 8733c309e..22bcd120c 100644 --- a/tiatoolbox/cli/slide_info.py +++ b/tiatoolbox/cli/slide_info.py @@ -33,7 +33,7 @@ def slide_info( verbose: bool, ) -> None: """Displays or saves WSI metadata depending on the mode argument.""" - from tiatoolbox import utils, wsicore + from tiatoolbox import utils, wsicore # noqa: PLC0415 all_files, output_path = prepare_file_dir_cli( img_input, diff --git a/tiatoolbox/cli/slide_thumbnail.py b/tiatoolbox/cli/slide_thumbnail.py index a2029b8dc..854ac35a6 100644 --- a/tiatoolbox/cli/slide_thumbnail.py +++ b/tiatoolbox/cli/slide_thumbnail.py @@ -34,10 +34,10 @@ def slide_thumbnail( file-types="*.ndpi, *.svs, *.mrxs, *.jp2". """ - from PIL import Image + from PIL import Image # noqa: PLC0415 - from tiatoolbox.utils import imwrite - from tiatoolbox.wsicore.wsireader import WSIReader + from tiatoolbox.utils import imwrite # noqa: PLC0415 + from tiatoolbox.wsicore.wsireader import WSIReader # noqa: PLC0415 files_all, output_path = prepare_file_dir_cli( img_input, diff --git a/tiatoolbox/cli/stain_norm.py b/tiatoolbox/cli/stain_norm.py index e2fdcbac1..068c342dd 100644 --- a/tiatoolbox/cli/stain_norm.py +++ b/tiatoolbox/cli/stain_norm.py @@ -48,8 +48,8 @@ def stain_norm( file_types: str, ) -> None: """Stain normalize an input image/directory of input images.""" - from tiatoolbox.tools import stainnorm as sn - from tiatoolbox.utils import imread, imwrite + from tiatoolbox.tools import stainnorm as sn # noqa: PLC0415 + from tiatoolbox.utils import imread, imwrite # noqa: PLC0415 files_all, output_path = prepare_file_dir_cli( img_input, diff --git a/tiatoolbox/cli/tissue_mask.py b/tiatoolbox/cli/tissue_mask.py index a73dd2f30..bb5bfe3af 100644 --- a/tiatoolbox/cli/tissue_mask.py +++ b/tiatoolbox/cli/tissue_mask.py @@ -30,7 +30,7 @@ def get_masker( resolution: float, ) -> TissueMasker: """Get Tissue Masker.""" - from tiatoolbox.tools import tissuemask + from tiatoolbox.tools import tissuemask # noqa: PLC0415 if method == "Otsu": return tissuemask.OtsuTissueMasker() @@ -73,11 +73,11 @@ def tissue_mask( file_types: str, ) -> None: """Generate tissue mask for a WSI.""" - import numpy as np - from PIL import Image + import numpy as np # noqa: PLC0415 + from PIL import Image # noqa: PLC0415 - from tiatoolbox.utils import imwrite - from tiatoolbox.wsicore.wsireader import WSIReader + from tiatoolbox.utils import imwrite # noqa: PLC0415 + from tiatoolbox.wsicore.wsireader import WSIReader # noqa: PLC0415 files_all, output_path = prepare_file_dir_cli( img_input, diff --git a/tiatoolbox/cli/visualize.py b/tiatoolbox/cli/visualize.py index f1edbb318..30627dcf2 100644 --- a/tiatoolbox/cli/visualize.py +++ b/tiatoolbox/cli/visualize.py @@ -19,7 +19,7 @@ def run_tileserver() -> None: def run_app() -> None: """Run the tileserver app.""" - from tiatoolbox.visualization.tileserver import TileServer + from tiatoolbox.visualization.tileserver import TileServer # noqa: PLC0415 app = TileServer( title="Tiatoolbox TileServer", diff --git a/tiatoolbox/data/__init__.py b/tiatoolbox/data/__init__.py index ba922df4c..9379f2740 100644 --- a/tiatoolbox/data/__init__.py +++ b/tiatoolbox/data/__init__.py @@ -12,6 +12,7 @@ from urllib.parse import urlparse from tiatoolbox import logger, read_registry_files +from tiatoolbox.utils import imread if TYPE_CHECKING: # pragma: no cover import numpy as np @@ -87,8 +88,6 @@ def _local_sample_path(path: str | Path) -> Path: def stain_norm_target() -> np.ndarray: """Target image for stain normalization.""" - from tiatoolbox.utils import imread - return imread(_local_sample_path("target_image.png")) diff --git a/tiatoolbox/models/engine/semantic_segmentor.py b/tiatoolbox/models/engine/semantic_segmentor.py index 80f6e57c9..f4b85e5c1 100644 --- a/tiatoolbox/models/engine/semantic_segmentor.py +++ b/tiatoolbox/models/engine/semantic_segmentor.py @@ -20,7 +20,10 @@ from tiatoolbox import logger, rcParam from tiatoolbox.models.architecture import get_pretrained_model -from tiatoolbox.models.architecture.utils import compile_model +from tiatoolbox.models.architecture.utils import ( + compile_model, + is_torch_compile_compatible, +) from tiatoolbox.models.models_abc import IOConfigABC, model_to from tiatoolbox.tools.patchextraction import PatchExtractor from tiatoolbox.utils import imread @@ -1422,7 +1425,6 @@ def predict( # noqa: PLR0913 logger.warning("Unable to remove %s", self._cache_dir) self._memory_cleanup() - from tiatoolbox.models.architecture.utils import is_torch_compile_compatible if ( device == "cuda" diff --git a/tiatoolbox/tools/graph.py b/tiatoolbox/tools/graph.py index 1f812333d..0cc036b51 100644 --- a/tiatoolbox/tools/graph.py +++ b/tiatoolbox/tools/graph.py @@ -9,6 +9,7 @@ import numpy as np import torch import umap +from matplotlib import collections as mc from matplotlib import pyplot as plt from scipy.cluster import hierarchy from scipy.spatial import Delaunay, cKDTree @@ -479,8 +480,6 @@ def visualise( >>> plt.show() """ - from matplotlib import collections as mc - # Check that the graph is valid if "x" not in graph: msg = "Graph must contain key `x`." diff --git a/tiatoolbox/utils/env_detection.py b/tiatoolbox/utils/env_detection.py index 8605af745..bc6f72ca4 100644 --- a/tiatoolbox/utils/env_detection.py +++ b/tiatoolbox/utils/env_detection.py @@ -97,7 +97,7 @@ def is_notebook() -> bool: """ try: - from IPython import get_ipython + from IPython import get_ipython # noqa: PLC0415 shell = get_ipython().__class__.__name__ if shell == "ZMQInteractiveShell": @@ -250,11 +250,11 @@ def check_pixman_using_anaconda(versions: list) -> tuple[list, str]: """Using anaconda to check for pixman.""" using = "conda" try: - conda_list = subprocess.Popen( # noqa: S603 + conda_list = subprocess.Popen( ("conda", "list"), stdout=subprocess.PIPE, ) - conda_pixman = subprocess.check_output( # noqa: S603 + conda_pixman = subprocess.check_output( ("grep", "pixman"), stdin=conda_list.stdout, ) @@ -276,7 +276,7 @@ def check_pixman_using_dpkg(versions: list) -> tuple[list, str]: """Using dpkg to check for pixman.""" using = "dpkg" try: - dkpg_output = subprocess.check_output( # noqa: S603 + dkpg_output = subprocess.check_output( ["/usr/bin/dpkg", "-s", "libpixman-1-0"], ) except subprocess.SubprocessError: @@ -296,11 +296,11 @@ def check_pixman_using_brew(versions: list) -> tuple[list, str]: """Using homebrew to check for pixman.""" using = "brew" try: - brew_list = subprocess.Popen( # noqa: S603 + brew_list = subprocess.Popen( ("brew", "list", "--versions"), stdout=subprocess.PIPE, ) - brew_pixman = subprocess.check_output( # noqa: S603 + brew_pixman = subprocess.check_output( ("grep", "pixman"), stdin=brew_list.stdout, ) @@ -326,11 +326,11 @@ def check_pixman_using_macports(versions: list) -> tuple[list, str]: """ using = "port" - port_list = subprocess.Popen( # noqa: S603 + port_list = subprocess.Popen( ("port", "installed"), stdout=subprocess.PIPE, ) - port_pixman = subprocess.check_output( # noqa: S603 + port_pixman = subprocess.check_output( ("grep", "pixman"), stdin=port_list.stdout, ) diff --git a/tiatoolbox/utils/misc.py b/tiatoolbox/utils/misc.py index 060efcaa4..6c755d04e 100644 --- a/tiatoolbox/utils/misc.py +++ b/tiatoolbox/utils/misc.py @@ -162,7 +162,7 @@ def imwrite(image_path: PathLike, img: np.ndarray) -> None: raise OSError(msg) -def imread(image_path: PathLike, as_uint8: bool | None = None) -> np.ndarray: +def imread(image_path: PathLike, *, as_uint8: bool | None = None) -> np.ndarray: """Read an image as a NumPy array. Args: diff --git a/tiatoolbox/wsicore/wsireader.py b/tiatoolbox/wsicore/wsireader.py index 73481060c..46f10cc36 100644 --- a/tiatoolbox/wsicore/wsireader.py +++ b/tiatoolbox/wsicore/wsireader.py @@ -1552,7 +1552,7 @@ def tissue_mask( Extra kwargs passed to the masker class. """ - from tiatoolbox.tools import tissuemask + from tiatoolbox.tools import tissuemask # noqa: PLC0415 thumbnail = self.slide_thumbnail(resolution, units) if method not in ["otsu", "morphological"]: @@ -2285,7 +2285,7 @@ def __init__( ) -> None: """Initialize :class:`OmnyxJP2WSIReader`.""" super().__init__(input_img=input_img, mpp=mpp, power=power) - import glymur + import glymur # noqa: PLC0415 glymur.set_option("lib.num_threads", os.cpu_count() or 1) self.glymur_jp2 = glymur.Jp2k(filename=str(self.input_path)) @@ -2780,7 +2780,7 @@ def _info(self: JP2WSIReader) -> WSIMeta: Metadata information. """ - import glymur + import glymur # noqa: PLC0415 jp2 = self.glymur_jp2 boxes = self._get_jp2_boxes(jp2) @@ -4561,7 +4561,7 @@ def __init__( power: Number | None = None, ) -> None: """Initialize :class:`DICOMWSIReader`.""" - from wsidicom import WsiDicom + from wsidicom import WsiDicom # noqa: PLC0415 super().__init__(input_img, mpp, power) self.wsi = WsiDicom.open(input_img) @@ -5075,9 +5075,9 @@ class NGFFWSIReader(WSIReader): def __init__(self: NGFFWSIReader, path: str | Path, **kwargs: dict) -> None: """Initialize :class:`NGFFWSIReader`.""" super().__init__(path, **kwargs) - from imagecodecs import numcodecs + from imagecodecs import numcodecs # noqa: PLC0415 - from tiatoolbox.wsicore.metadata import ngff + from tiatoolbox.wsicore.metadata import ngff # noqa: PLC0415 numcodecs.register_codecs() store = zarr.SQLiteStore(path) if is_sqlite3(path) else path