Skip to content

Commit 2c4a9b8

Browse files
committed
Add tests and refine approach.
1 parent 6f950b0 commit 2c4a9b8

File tree

2 files changed

+66
-12
lines changed

2 files changed

+66
-12
lines changed

scabha/basetypes.py

+24-12
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
from __future__ import annotations
12
from dataclasses import field, dataclass
23
from collections import OrderedDict
3-
from typing import List, Union, get_args, get_origin
4+
from typing import List, Union, get_args, get_origin, Any
45
import os.path
56
import re
67
from .exceptions import UnsetError
78
from itertools import zip_longest
8-
from typeguard import check_type, TypeCheckError
9+
from typeguard import (
10+
check_type, TypeCheckError, TypeCheckerCallable, TypeCheckMemo, checker_lookup_functions
11+
)
12+
from inspect import isclass
913

1014

1115
def EmptyDictDefault():
@@ -118,11 +122,21 @@ def is_file_list_type(dtype):
118122
return any(dtype == List[t] for t in FILE_TYPES)
119123

120124

121-
class Skip(object):
122-
def iterate_samples(self, collection):
123-
return ()
125+
def check_filelike(value: Any, origin_type: Any, args: tuple[Any, ...], memo: TypeCheckMemo) -> None:
126+
"""Custom checker for filelike objects. Currently checks for strings."""
127+
if not isinstance(value, str):
128+
raise TypeCheckError(f'{value} is not compatible with URI or its subclasses.')
124129

125130

131+
def filelike_lookup(origin_type: Any, args: tuple[Any, ...], extras: tuple[Any, ...]) -> TypeCheckerCallable | None:
132+
"""Lookup the custom checker for filelike objects."""
133+
if isclass(origin_type) and issubclass(origin_type, URI):
134+
return check_filelike
135+
136+
return None
137+
138+
checker_lookup_functions.append(filelike_lookup) # Register custom type checker.
139+
126140
def get_filelikes(dtype, value, filelikes=None):
127141
"""Recursively recover all filelike elements from a composite dtype."""
128142

@@ -152,21 +166,19 @@ def get_filelikes(dtype, value, filelikes=None):
152166
return filelikes
153167

154168
# This is a special case for tuples of arbitrary
155-
# length i.e. list-like behaviour.
156-
if ... in args:
157-
args = tuple([a for a in args if a != ...])
169+
# length i.e. list-like behaviour. We can simply
170+
# strip out the Ellipsis.
171+
args = tuple([arg for arg in args if arg != ...])
158172

159173
for dt, v in zip_longest(args, value, fillvalue=args[0]):
160174
filelikes = get_filelikes(dt, v, filelikes)
161175

162176
elif origin is Union:
163177

164178
for dt in args:
165-
166179
try:
167-
# Do not check collection member types.
168-
check_type(value, dt, collection_check_strategy=Skip())
169-
except TypeCheckError:
180+
check_type(value, dt)
181+
except TypeCheckError: # Value doesn't match dtype - incorrect branch.
170182
continue
171183
filelikes = get_filelikes(dt, value, filelikes)
172184

tests/scabha_tests/test_filelikes.py

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from scabha.basetypes import get_filelikes, File, URI, Directory, MS
2+
from typing import Dict, List, Set, Tuple, Union, Optional
3+
import pytest
4+
5+
6+
@pytest.fixture(scope="module", params=[File, URI, Directory, MS])
7+
def templates(request):
8+
9+
ft = request.param
10+
11+
TEMPLATES = (
12+
(Tuple, (), set()),
13+
(Tuple[int, ...], [1, 2], set()),
14+
(Tuple[ft, ...], ("foo", "bar"), {"foo", "bar"}),
15+
(Tuple[ft, str], ("foo", "bar"), {"foo"}),
16+
(Dict[str, int], {"a": 1, "b": 2}, set()),
17+
(Dict[str, ft], {"a": "foo", "b": "bar"}, {"foo", "bar"}),
18+
(Dict[ft, str], {"foo": "a", "bar": "b"}, {"foo", "bar"}),
19+
(List[ft], [], set()),
20+
(List[int], [1, 2], set()),
21+
(List[ft], ["foo", "bar"], {"foo", "bar"}),
22+
(Set[ft], set(), set()),
23+
(Set[int], {1, 2}, set()),
24+
(Set[ft], {"foo", "bar"}, {"foo", "bar"}),
25+
(Union[str, List[ft]], "foo", set()),
26+
(Union[str, List[ft]], ["foo"], {"foo"}),
27+
(Union[str, Tuple[ft]], "foo", set()),
28+
(Union[str, Tuple[ft]], ("foo",), {"foo"}),
29+
(Optional[ft], None, set()),
30+
(Optional[ft], "foo", {"foo"}),
31+
(Optional[Union[ft, int]], 1, set()),
32+
(Optional[Union[ft, int]], "foo", {"foo"}),
33+
(Dict[str, Tuple[ft, str]], {"a": ("foo", "bar")}, {"foo"})
34+
)
35+
36+
return TEMPLATES
37+
38+
39+
def test_get_filelikes(templates):
40+
41+
for dt, v, res in templates:
42+
assert get_filelikes(dt, v) == res, f"Failed for dtype {dt} and value {v}."

0 commit comments

Comments
 (0)