Skip to content

Commit 6f950b0

Browse files
committed
Checkpoint work so far.
1 parent 49124e8 commit 6f950b0

File tree

3 files changed

+73
-10
lines changed

3 files changed

+73
-10
lines changed

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pydantic = "^1.10.2"
2424
psutil = "^5.9.3"
2525
rich = "^13.7.0"
2626
dill = "^0.3.6"
27+
typeguard = "^4.2.1"
2728

2829
[tool.poetry.scripts]
2930
stimela = "stimela.main:cli"

scabha/basetypes.py

+68-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from dataclasses import field, dataclass
22
from collections import OrderedDict
3-
from typing import List
3+
from typing import List, Union, get_args, get_origin
44
import os.path
55
import re
66
from .exceptions import UnsetError
7+
from itertools import zip_longest
8+
from typeguard import check_type, TypeCheckError
9+
710

811
def EmptyDictDefault():
912
return field(default_factory=lambda:OrderedDict())
@@ -55,7 +58,7 @@ def __init__(self, value):
5558
def parse(value: str, expand_user=True):
5659
"""
5760
Parses URI. If URI does not start with "protocol://", assumes "file://"
58-
61+
5962
Returns tuple of (protocol, path, is_remote)
6063
6164
If expand_user is True, ~ in (file-protocol) paths will be expanded.
@@ -75,7 +78,7 @@ class File(URI):
7578
@property
7679
def NAME(self):
7780
return File(os.path.basename(self))
78-
81+
7982
@property
8083
def PATH(self):
8184
return File(os.path.abspath(self))
@@ -95,7 +98,7 @@ def BASENAME(self):
9598
@property
9699
def EXT(self):
97100
return os.path.splitext(self)[1]
98-
101+
99102
@property
100103
def EXISTS(self):
101104
return os.path.exists(self)
@@ -114,3 +117,64 @@ def is_file_type(dtype):
114117
def is_file_list_type(dtype):
115118
return any(dtype == List[t] for t in FILE_TYPES)
116119

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

stimela/backends/utils.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from stimela.kitchen.cab import Cab, Parameter
55
from scabha.exceptions import SchemaError
66
from stimela.exceptions import BackendError
7-
from scabha.basetypes import File, Directory, MS, URI
7+
from scabha.basetypes import File, Directory, MS, URI, get_filelikes
88

99
## commenting out for now -- will need to fix when we reactive the kube backend (and have tests for it)
1010

@@ -34,11 +34,9 @@ def add_target(param_name, path, must_exist, readwrite):
3434
if schema is None:
3535
raise SchemaError(f"parameter {name} not in defined inputs or outputs for this cab. This should have been caught by validation earlier!")
3636

37-
if schema.is_file_type:
38-
files = [value]
39-
elif schema.is_file_list_type:
40-
files = value
41-
else:
37+
files = get_filelikes(schema._dtype, value)
38+
39+
if not files:
4240
continue
4341

4442
must_exist = schema.must_exist and name in inputs

0 commit comments

Comments
 (0)