Skip to content

Commit 6e38291

Browse files
committed
Merge remote-tracking branch 'origin/dev-define-engines-abc' into dev-redefine-patchpredictor
# Conflicts: # tests/test_utils.py # tiatoolbox/utils/misc.py
2 parents 122e114 + 7dd2909 commit 6e38291

File tree

8 files changed

+78
-14
lines changed

8 files changed

+78
-14
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# This workflow will perform code type checking using mypy
2+
3+
name: mypy type checking
4+
5+
on:
6+
push:
7+
branches: [ develop, pre-release, master, main ]
8+
pull_request:
9+
branches: [ develop, pre-release, master, main ]
10+
11+
jobs:
12+
13+
build:
14+
15+
runs-on: ubuntu-22.04
16+
17+
strategy:
18+
matrix:
19+
python-version: ["3.8", "3.9", "3.10", "3.11"]
20+
21+
steps:
22+
23+
- name: Set up Python ${{ matrix.python-version }}
24+
uses: actions/setup-python@v3
25+
with:
26+
python-version: ${{ matrix.python-version }}
27+
28+
- name: Checkout repository
29+
uses: actions/checkout@v3
30+
31+
- name: Setup mypy
32+
run: |
33+
pip install mypy
34+
35+
- name: Perform type checking
36+
run: |
37+
mypy --install-types --non-interactive --follow-imports=skip \
38+
tiatoolbox/__init__.py \
39+
tiatoolbox/__main__.py \
40+
tiatoolbox/utils/exceptions.py

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ repos:
5959
- id: rst-directive-colons # Detect mistake of rst directive not ending with double colon.
6060
- id: rst-inline-touching-normal # Detect mistake of inline code touching normal text in rst.
6161
- repo: https://github.com/psf/black
62-
rev: 23.9.1 # Replace with any tag/version: https://github.com/psf/black/tags
62+
rev: 23.10.0 # Replace with any tag/version: https://github.com/psf/black/tags
6363
hooks:
6464
- id: black
6565
language_version: python3 # Should be a command that runs python3.+
@@ -68,7 +68,7 @@ repos:
6868
language: python
6969
- repo: https://github.com/astral-sh/ruff-pre-commit
7070
# Ruff version.
71-
rev: v0.0.292
71+
rev: v0.1.1
7272
hooks:
7373
- id: ruff
7474
args: [--fix, --exit-non-zero-on-fix]

requirements/requirements_dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ black>=23.3.0
44
coverage>=7.0.0
55
docutils>=0.18.1
66
jinja2>=3.0.3, <3.1.0
7+
mypy>=1.6.1
78
pip>=22.3
89
poetry-bumpversion>=0.3.1
910
pre-commit>=2.20.0

tests/test_init.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,14 @@ def test_lazy_import() -> None:
139139
)
140140

141141
assert "exceptions" in sys.modules
142+
143+
144+
def test_lazy_import_module_not_found() -> None:
145+
"""'Test lazy import for ModuleNotFoundError."""
146+
from tiatoolbox import _lazy_import
147+
148+
with pytest.raises(ModuleNotFoundError):
149+
_lazy_import(
150+
"nonexistent_module",
151+
Path(__file__).parent.parent / "tiatoolbox",
152+
)

tests/test_utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1638,7 +1638,7 @@ def test_patch_pred_store() -> None:
16381638
"other": "other",
16391639
}
16401640

1641-
store = misc.patch_pred_store(patch_output, (1.0, 1.0))
1641+
store = misc.dict_to_store(patch_output, (1.0, 1.0))
16421642

16431643
# Check that its an SQLiteStore containing the expected annotations
16441644
assert isinstance(store, SQLiteStore)
@@ -1651,7 +1651,7 @@ def test_patch_pred_store() -> None:
16511651
patch_output.pop("coordinates")
16521652
# check correct error is raised if coordinates are missing
16531653
with pytest.raises(ValueError, match="coordinates"):
1654-
misc.patch_pred_store(patch_output, (1.0, 1.0))
1654+
misc.dict_to_store(patch_output, (1.0, 1.0))
16551655

16561656

16571657
def test_patch_pred_store_cdict() -> None:
@@ -1665,7 +1665,7 @@ def test_patch_pred_store_cdict() -> None:
16651665
"other": "other",
16661666
}
16671667
class_dict = {0: "class0", 1: "class1"}
1668-
store = misc.patch_pred_store(patch_output, (1.0, 1.0), class_dict=class_dict)
1668+
store = misc.dict_to_store(patch_output, (1.0, 1.0), class_dict=class_dict)
16691669

16701670
# Check that its an SQLiteStore containing the expected annotations
16711671
assert isinstance(store, SQLiteStore)
@@ -1686,7 +1686,7 @@ def test_patch_pred_store_sf() -> None:
16861686
"probabilities": [[0.1, 0.9], [0.9, 0.1], [0.4, 0.6]],
16871687
"labels": [1, 0, 1],
16881688
}
1689-
store = misc.patch_pred_store(patch_output, (2.0, 2.0))
1689+
store = misc.dict_to_store(patch_output, (2.0, 2.0))
16901690

16911691
# Check that its an SQLiteStore containing the expected annotations
16921692
assert isinstance(store, SQLiteStore)

tiatoolbox/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
if sys.version_info >= (3, 9): # pragma: no cover
1010
import importlib.resources as importlib_resources
1111
else: # pragma: no cover
12-
import importlib_resources # To support Python 3.8
12+
# To support Python 3.8
13+
import importlib_resources # type: ignore[import-not-found]
1314

1415
import yaml
1516

1617
if TYPE_CHECKING: # pragma: no cover
1718
from logging import LogRecord
19+
from types import ModuleType
1820

1921
__author__ = """TIA Centre"""
2022
__email__ = "[email protected]"
@@ -71,6 +73,7 @@ def filter(self: DuplicateFilter, record: LogRecord) -> bool:
7173

7274

7375
# runtime context parameters
76+
rcParam: dict[str, str | Path | dict] # noqa: N816
7477
rcParam = { # noqa: N816
7578
"TIATOOLBOX_HOME": Path.home() / ".tiatoolbox",
7679
}
@@ -88,6 +91,7 @@ def read_registry_files(path_to_registry: str | Path) -> dict | str:
8891
8992
9093
"""
94+
path_to_registry = str(path_to_registry) # To pass tests with Python 3.8
9195
pretrained_files_registry_path = importlib_resources.as_file(
9296
importlib_resources.files("tiatoolbox") / path_to_registry,
9397
)
@@ -101,8 +105,10 @@ def read_registry_files(path_to_registry: str | Path) -> dict | str:
101105
rcParam["pretrained_model_info"] = read_registry_files("data/pretrained_model.yaml")
102106

103107

104-
def _lazy_import(name: str, module_location: Path) -> sys.modules:
108+
def _lazy_import(name: str, module_location: Path) -> ModuleType:
105109
spec = importlib.util.spec_from_file_location(name, module_location)
110+
if spec is None or spec.loader is None:
111+
raise ModuleNotFoundError(name=name, path=str(module_location))
106112
loader = importlib.util.LazyLoader(spec.loader)
107113
spec.loader = loader
108114
module = importlib.util.module_from_spec(spec)

tiatoolbox/utils/exceptions.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Custom Errors and Exceptions for TIAToolbox."""
2+
from __future__ import annotations
23

34

45
class FileNotSupportedError(Exception):
@@ -10,7 +11,7 @@ class FileNotSupportedError(Exception):
1011
"""
1112

1213
def __init__(
13-
self: Exception,
14+
self: FileNotSupportedError,
1415
message: str = "File format is not supported",
1516
) -> None:
1617
"""Initialize :class:`FileNotSupportedError`."""
@@ -25,6 +26,9 @@ class MethodNotSupportedError(Exception):
2526
2627
"""
2728

28-
def __init__(self: Exception, message: str = "Method is not supported") -> None:
29+
def __init__(
30+
self: MethodNotSupportedError,
31+
message: str = "Method is not supported",
32+
) -> None:
2933
"""Initialize :class:`MethodNotSupportedError`."""
3034
super().__init__(message)

tiatoolbox/utils/misc.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,7 +1180,7 @@ def add_from_dat(
11801180
store.append_many(anns)
11811181

11821182

1183-
def patch_pred_store(
1183+
def dict_to_store(
11841184
patch_output: dict,
11851185
scale_factor: tuple[int, int],
11861186
class_dict: dict | None = None,
@@ -1190,9 +1190,11 @@ def patch_pred_store(
11901190
"""Create an SQLiteStore containing Annotations for each patch.
11911191
11921192
Args:
1193-
patch_output (dict): A dictionary of patch prediction information. Important
1193+
patch_output (dict):
1194+
A dictionary in the TIAToolbox Engines output format. Important
11941195
keys are "probabilities", "predictions", "coordinates", and "labels".
1195-
scale_factor (tuple[int, int]): The scale factor to use when loading the
1196+
scale_factor (tuple[int, int]):
1197+
The scale factor to use when loading the
11961198
annotations. All coordinates will be multiplied by this factor to allow
11971199
conversion of annotations saved at non-baseline resolution to baseline.
11981200
Should be model_mpp/slide_mpp.
@@ -1229,7 +1231,7 @@ def patch_pred_store(
12291231
if class_dict is None:
12301232
# if no class dict create a default one
12311233
class_dict = {i: i for i in np.unique(preds + labels).tolist()}
1232-
annotations = []
1234+
12331235
# find what keys we need to save
12341236
keys = ["predictions"]
12351237
keys = keys + [key for key in ["probabilities", "labels"] if key in patch_output]

0 commit comments

Comments
 (0)