1
1
from dataclasses import field , dataclass
2
2
from collections import OrderedDict
3
- from typing import List
3
+ from typing import List , Union , get_args , get_origin
4
4
import os .path
5
5
import re
6
6
from .exceptions import UnsetError
7
+ from itertools import zip_longest
8
+ from typeguard import check_type , TypeCheckError
9
+
7
10
8
11
def EmptyDictDefault ():
9
12
return field (default_factory = lambda :OrderedDict ())
@@ -55,7 +58,7 @@ def __init__(self, value):
55
58
def parse (value : str , expand_user = True ):
56
59
"""
57
60
Parses URI. If URI does not start with "protocol://", assumes "file://"
58
-
61
+
59
62
Returns tuple of (protocol, path, is_remote)
60
63
61
64
If expand_user is True, ~ in (file-protocol) paths will be expanded.
@@ -75,7 +78,7 @@ class File(URI):
75
78
@property
76
79
def NAME (self ):
77
80
return File (os .path .basename (self ))
78
-
81
+
79
82
@property
80
83
def PATH (self ):
81
84
return File (os .path .abspath (self ))
@@ -95,7 +98,7 @@ def BASENAME(self):
95
98
@property
96
99
def EXT (self ):
97
100
return os .path .splitext (self )[1 ]
98
-
101
+
99
102
@property
100
103
def EXISTS (self ):
101
104
return os .path .exists (self )
@@ -114,3 +117,64 @@ def is_file_type(dtype):
114
117
def is_file_list_type (dtype ):
115
118
return any (dtype == List [t ] for t in FILE_TYPES )
116
119
120
+
121
+ class Skip (object ):
122
+ def iterate_samples (self , collection ):
123
+ return ()
124
+
125
+
126
+ def get_filelikes (dtype , value , filelikes = None ):
127
+ """Recursively recover all filelike elements from a composite dtype."""
128
+
129
+ filelikes = set () if filelikes is None else filelikes
130
+
131
+ origin = get_origin (dtype )
132
+ args = get_args (dtype )
133
+
134
+ if origin : # Implies composition.
135
+
136
+ if origin is dict :
137
+
138
+ # No further work required for empty collections.
139
+ if len (value ) == 0 :
140
+ return filelikes
141
+
142
+ k_dtype , v_dtype = args
143
+
144
+ for k , v in value .items ():
145
+ filelikes = get_filelikes (k_dtype , k , filelikes )
146
+ filelikes = get_filelikes (v_dtype , v , filelikes )
147
+
148
+ elif origin in (tuple , list , set ):
149
+
150
+ # No further work required for empty collections.
151
+ if len (value ) == 0 :
152
+ return filelikes
153
+
154
+ # 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 != ...])
158
+
159
+ for dt , v in zip_longest (args , value , fillvalue = args [0 ]):
160
+ filelikes = get_filelikes (dt , v , filelikes )
161
+
162
+ elif origin is Union :
163
+
164
+ for dt in args :
165
+
166
+ try :
167
+ # Do not check collection member types.
168
+ check_type (value , dt , collection_check_strategy = Skip ())
169
+ except TypeCheckError :
170
+ continue
171
+ filelikes = get_filelikes (dt , value , filelikes )
172
+
173
+ else :
174
+ raise ValueError (f"Failed to traverse { dtype } dtype when looking for files." )
175
+
176
+ else :
177
+ if is_file_type (dtype ):
178
+ filelikes .add (value )
179
+
180
+ return filelikes
0 commit comments