Skip to content

Commit 6ef915d

Browse files
Jiaqi-Lvshaneahmed
andauthored
☑️ Add mypy Checks in tiatoolbox/cli (#846)
- ☑️ Add `mypy` Checks in `tiatoolbox/cli` --------- Co-authored-by: Shan E Ahmed Raza <[email protected]>
1 parent 6df8565 commit 6ef915d

File tree

2 files changed

+52
-49
lines changed

2 files changed

+52
-49
lines changed

.github/workflows/mypy-type-check.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,5 @@ jobs:
4545
tiatoolbox/utils \
4646
tiatoolbox/tools \
4747
tiatoolbox/data \
48-
tiatoolbox/annotation
48+
tiatoolbox/annotation \
49+
tiatoolbox/cli/common.py

tiatoolbox/cli/common.py

Lines changed: 50 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from __future__ import annotations
44

55
from pathlib import Path
6-
from typing import TYPE_CHECKING, Any
6+
from typing import TYPE_CHECKING, Any, Callable
77

88
import click
99

@@ -13,7 +13,7 @@
1313

1414
def add_default_to_usage_help(
1515
usage_help: str,
16-
default: str or int or float or bool,
16+
default: str | float | bool | None,
1717
) -> str:
1818
"""Adds default value to usage help string.
1919
@@ -37,7 +37,7 @@ def add_default_to_usage_help(
3737
def cli_img_input(
3838
usage_help: str = "Path to WSI or directory containing WSIs.",
3939
multiple: bool | None = None,
40-
) -> callable:
40+
) -> Callable:
4141
"""Enables --img-input option for cli."""
4242
if multiple is None:
4343
multiple = False
@@ -50,7 +50,7 @@ def cli_img_input(
5050
def cli_name(
5151
usage_help: str = "User defined name to be used as an identifier.",
5252
multiple: bool | None = None,
53-
) -> callable:
53+
) -> Callable:
5454
"""Enable --name option for cli."""
5555
if multiple is None:
5656
multiple = False
@@ -63,7 +63,7 @@ def cli_name(
6363
def cli_output_path(
6464
usage_help: str = "Path to output directory to save the output.",
6565
default: str | None = None,
66-
) -> callable:
66+
) -> Callable:
6767
"""Enables --output-path option for cli."""
6868
return click.option(
6969
"--output-path",
@@ -76,7 +76,7 @@ def cli_output_path(
7676
def cli_file_type(
7777
usage_help: str = "File types to capture from directory.",
7878
default: str = "*.ndpi, *.svs, *.mrxs, *.jp2",
79-
) -> callable:
79+
) -> Callable:
8080
"""Enables --file-types option for cli."""
8181
return click.option(
8282
"--file-types",
@@ -90,7 +90,7 @@ def cli_mode(
9090
usage_help: str = "Selected mode to show or save the required information.",
9191
default: str = "save",
9292
input_type: click.Choice | None = None,
93-
) -> callable:
93+
) -> Callable:
9494
"""Enables --mode option for cli."""
9595
if input_type is None:
9696
input_type = click.Choice(["show", "save"], case_sensitive=False)
@@ -105,7 +105,7 @@ def cli_mode(
105105
def cli_region(
106106
usage_help: str = "Image region in the whole slide image to read from. "
107107
"default=0 0 2000 2000",
108-
) -> callable:
108+
) -> Callable:
109109
"""Enables --region option for cli."""
110110
return click.option(
111111
"--region",
@@ -119,7 +119,7 @@ def cli_units(
119119
usage_help: str = "Image resolution units to read the image.",
120120
default: str = "level",
121121
input_type: click.Choice | None = None,
122-
) -> callable:
122+
) -> Callable:
123123
"""Enables --units option for cli."""
124124
if input_type is None:
125125
input_type = click.Choice(
@@ -137,7 +137,7 @@ def cli_units(
137137
def cli_resolution(
138138
usage_help: str = "Image resolution to read the image.",
139139
default: float = 0,
140-
) -> callable:
140+
) -> Callable:
141141
"""Enables --resolution option for cli."""
142142
return click.option(
143143
"--resolution",
@@ -150,7 +150,7 @@ def cli_resolution(
150150
def cli_tile_objective(
151151
usage_help: str = "Objective value for the saved tiles.",
152152
default: int = 20,
153-
) -> callable:
153+
) -> Callable:
154154
"""Enables --tile-objective-value option for cli."""
155155
return click.option(
156156
"--tile-objective-value",
@@ -162,7 +162,7 @@ def cli_tile_objective(
162162

163163
def cli_tile_read_size(
164164
usage_help: str = "Width and Height of saved tiles. default=5000 5000",
165-
) -> callable:
165+
) -> Callable:
166166
"""Enables --tile-read-size option for cli."""
167167
return click.option(
168168
"--tile-read-size",
@@ -175,7 +175,7 @@ def cli_tile_read_size(
175175

176176
def cli_tile_format(
177177
usage_help: str = "File format to save image tiles, defaults = '.jpg'",
178-
) -> callable:
178+
) -> Callable:
179179
"""Enables --tile-format option for cli."""
180180
return click.option(
181181
"--tile-format",
@@ -189,7 +189,7 @@ def cli_method(
189189
usage_help: str = "Select method of for tissue masking.",
190190
default: str = "Otsu",
191191
input_type: click.Choice | None = None,
192-
) -> callable:
192+
) -> Callable:
193193
"""Enables --method option for cli."""
194194
if input_type is None:
195195
input_type = click.Choice(["Otsu", "Morphological"], case_sensitive=True)
@@ -212,7 +212,7 @@ def cli_pretrained_model(
212212
"downloaded. However, you can override with your own set of weights"
213213
"via the `pretrained_weights` argument. Argument is case insensitive.",
214214
default: str = "resnet18-kather100k",
215-
) -> callable:
215+
) -> Callable:
216216
"""Enables --pretrained-model option for cli."""
217217
return click.option(
218218
"--pretrained-model",
@@ -225,7 +225,7 @@ def cli_pretrained_weights(
225225
usage_help: str = "Path to the model weight file. If not supplied, the default "
226226
"pretrained weight will be used.",
227227
default: str | None = None,
228-
) -> callable:
228+
) -> Callable:
229229
"""Enables --pretrained-weights option for cli."""
230230
return click.option(
231231
"--pretrained-weights",
@@ -238,7 +238,7 @@ def cli_return_probabilities(
238238
usage_help: str = "Whether to return raw model probabilities.",
239239
*,
240240
default: bool = False,
241-
) -> callable:
241+
) -> Callable:
242242
"""Enables --return-probabilities option for cli."""
243243
return click.option(
244244
"--return-probabilities",
@@ -252,7 +252,7 @@ def cli_merge_predictions(
252252
usage_help: str = "Whether to merge the predictions to form a 2-dimensional map.",
253253
*,
254254
default: bool = True,
255-
) -> callable:
255+
) -> Callable:
256256
"""Enables --merge-predictions option for cli."""
257257
return click.option(
258258
"--merge-predictions",
@@ -266,7 +266,7 @@ def cli_return_labels(
266266
usage_help: str = "Whether to return raw model output as labels.",
267267
*,
268268
default: bool = True,
269-
) -> callable:
269+
) -> Callable:
270270
"""Enables --return-labels option for cli."""
271271
return click.option(
272272
"--return-labels",
@@ -279,7 +279,7 @@ def cli_return_labels(
279279
def cli_batch_size(
280280
usage_help: str = "Number of image patches to feed into the model each time.",
281281
default: int = 1,
282-
) -> callable:
282+
) -> Callable:
283283
"""Enables --batch-size option for cli."""
284284
return click.option(
285285
"--batch-size",
@@ -296,7 +296,7 @@ def cli_masks(
296296
"automatically generated for whole-slide images or the entire image is "
297297
"processed for image tiles. Supported file types are jpg, png and npy.",
298298
default: str | None = None,
299-
) -> callable:
299+
) -> Callable:
300300
"""Enables --masks option for cli."""
301301
return click.option(
302302
"--masks",
@@ -309,7 +309,7 @@ def cli_auto_generate_mask(
309309
usage_help: str = "Automatically generate tile/WSI tissue mask.",
310310
*,
311311
default: bool = False,
312-
) -> callable:
312+
) -> Callable:
313313
"""Enables --auto-generate-mask option for cli."""
314314
return click.option(
315315
"--auto-generate-mask",
@@ -324,7 +324,7 @@ def cli_yaml_config_path(
324324
"tiatoolbox.data.pretrained_model.yaml. "
325325
"if pretrained_model is used the ioconfig is automatically set.",
326326
default: str | None = None,
327-
) -> callable:
327+
) -> Callable:
328328
"""Enables --yaml-config-path option for cli."""
329329
return click.option(
330330
"--yaml-config-path",
@@ -337,7 +337,7 @@ def cli_on_gpu(
337337
usage_help: str = "Run the model on GPU.",
338338
*,
339339
default: bool = False,
340-
) -> callable:
340+
) -> Callable:
341341
"""Enables --on-gpu option for cli."""
342342
return click.option(
343343
"--on-gpu",
@@ -351,7 +351,7 @@ def cli_num_loader_workers(
351351
usage_help: str = "Number of workers to load the data. Please note that they will "
352352
"also perform preprocessing.",
353353
default: int = 0,
354-
) -> callable:
354+
) -> Callable:
355355
"""Enables --num-loader-workers option for cli."""
356356
return click.option(
357357
"--num-loader-workers",
@@ -364,7 +364,7 @@ def cli_num_loader_workers(
364364
def cli_num_postproc_workers(
365365
usage_help: str = "Number of workers to post-process the network output.",
366366
default: int = 0,
367-
) -> callable:
367+
) -> Callable:
368368
"""Enables --num-postproc-workers option for cli."""
369369
return click.option(
370370
"--num-postproc-workers",
@@ -378,7 +378,7 @@ def cli_verbose(
378378
usage_help: str = "Prints the console output.",
379379
*,
380380
default: bool = True,
381-
) -> callable:
381+
) -> Callable:
382382
"""Enables --verbose option for cli."""
383383
return click.option(
384384
"--verbose",
@@ -397,13 +397,13 @@ def __init__(
397397
**kwargs: dict[str, Any],
398398
) -> None:
399399
"""Initialize TIAToolboxCLI."""
400-
super().__init__(*args, **kwargs)
400+
super().__init__(*args, **kwargs) # type: ignore[arg-type]
401401
self.help = "Computational pathology toolbox by TIA Centre."
402-
self.add_help_option = {"help_option_names": ["-h", "--help"]}
402+
self.help_option_names = ["-h", "--help"]
403403

404404

405405
def no_input_message(
406-
input_file: (str or Path) | None = None,
406+
input_file: str | Path | None = None,
407407
message: str = "No image input provided.\n",
408408
) -> Path:
409409
"""This function is called if no input is provided.
@@ -419,18 +419,17 @@ def no_input_message(
419419
"""
420420
if input_file is None:
421421
ctx = click.get_current_context()
422-
ctx.fail(message=message)
423-
422+
return ctx.fail(message=message)
424423
return Path(input_file)
425424

426425

427426
def prepare_file_dir_cli(
428-
img_input: str or Path,
429-
output_path: str or Path,
427+
img_input: str | Path,
428+
output_path: str | Path,
430429
file_types: str,
431430
mode: str,
432431
sub_dirname: str,
433-
) -> [list, Path]:
432+
) -> tuple[list, Path]:
434433
"""Prepares CLI for running code on multiple files or a directory.
435434
436435
Checks for existing directories to run tests.
@@ -457,7 +456,7 @@ def prepare_file_dir_cli(
457456
from tiatoolbox.utils.misc import grab_files_from_dir, string_to_tuple
458457

459458
img_input = no_input_message(input_file=img_input)
460-
file_types = string_to_tuple(in_str=file_types)
459+
file_types_tuple = string_to_tuple(in_str=file_types)
461460

462461
if isinstance(output_path, str):
463462
output_path = Path(output_path)
@@ -470,7 +469,9 @@ def prepare_file_dir_cli(
470469
]
471470

472471
if Path.is_dir(img_input):
473-
files_all = grab_files_from_dir(input_path=img_input, file_types=file_types)
472+
files_all = grab_files_from_dir(
473+
input_path=img_input, file_types=file_types_tuple
474+
)
474475

475476
if output_path is None and mode == "save":
476477
input_dir = Path(img_input).parent
@@ -479,15 +480,15 @@ def prepare_file_dir_cli(
479480
if mode == "save":
480481
output_path.mkdir(parents=True, exist_ok=True)
481482

482-
return [files_all, output_path]
483+
return (files_all, output_path)
483484

484485

485486
def prepare_model_cli(
486-
img_input: str or Path,
487-
output_path: str or Path,
488-
masks: str or Path,
487+
img_input: str | Path,
488+
output_path: str | Path,
489+
masks: str | Path,
489490
file_types: str,
490-
) -> [list, list, Path]:
491+
) -> tuple[list, list | None, Path]:
491492
"""Prepares cli for running models.
492493
493494
Checks for existing directories to run tests.
@@ -517,7 +518,7 @@ def prepare_model_cli(
517518

518519
img_input = no_input_message(input_file=img_input)
519520
output_path = Path(output_path)
520-
file_types = string_to_tuple(in_str=file_types)
521+
file_types_tuple = string_to_tuple(in_str=file_types)
521522

522523
if output_path.exists():
523524
msg = "Path already exists."
@@ -543,17 +544,19 @@ def prepare_model_cli(
543544
)
544545

545546
if Path.is_dir(img_input):
546-
files_all = grab_files_from_dir(input_path=img_input, file_types=file_types)
547+
files_all = grab_files_from_dir(
548+
input_path=img_input, file_types=file_types_tuple
549+
)
547550

548-
return [files_all, masks_all, output_path]
551+
return (files_all, masks_all, output_path)
549552

550553

551554
tiatoolbox_cli = TIAToolboxCLI()
552555

553556

554557
def prepare_ioconfig_seg(
555-
segment_config_class: IOConfigABC,
556-
pretrained_weights: str | Path,
558+
segment_config_class: type[IOConfigABC],
559+
pretrained_weights: str | Path | None,
557560
yaml_config_path: str | Path,
558561
) -> IOConfigABC | None:
559562
"""Prepare ioconfig for segmentation."""
@@ -562,7 +565,6 @@ def prepare_ioconfig_seg(
562565
if pretrained_weights is not None:
563566
with Path(yaml_config_path).open() as registry_handle:
564567
ioconfig = yaml.safe_load(registry_handle)
565-
566568
return segment_config_class(**ioconfig)
567569

568570
return None

0 commit comments

Comments
 (0)