Skip to content

Commit e284527

Browse files
Merge pull request #2 from InformaticsMatters/issue-1
Experimental support for lists of inputs
2 parents 5574d15 + 6810c4d commit e284527

File tree

1 file changed

+91
-38
lines changed

1 file changed

+91
-38
lines changed

jote/jote.py

Lines changed: 91 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,8 @@ def _load(skip_lint: bool = False) -> Tuple[List[DefaultMunch], int]:
146146
return job_definitions, num_tests
147147

148148

149-
def _copy_inputs(test_inputs: DefaultMunch,
150-
project_path: str) -> bool:
149+
def _copy_inputs(test_inputs: List[str], project_path: str) -> bool:
151150
"""Copies all the test files into the test project directory.
152-
Files are expected to reside in the repo's 'data' directory
153-
and must begin 'data/'
154151
"""
155152

156153
# The files are assumed to reside in the repo's 'data' directory.
@@ -159,20 +156,19 @@ def _copy_inputs(test_inputs: DefaultMunch,
159156
expected_prefix: str = f'{_DATA_DIRECTORY}/'
160157
for test_input in test_inputs:
161158

162-
test_file: str = test_inputs[test_input]
163-
print(f'# + {test_file} ({test_input})')
159+
print(f'# + {test_input}')
164160

165-
if not test_file.startswith(expected_prefix):
161+
if not test_input.startswith(expected_prefix):
166162
print('! FAILURE')
167-
print(f'! Input file {test_file} must start with "{expected_prefix}"')
163+
print(f'! Input file {test_input} must start with "{expected_prefix}"')
168164
return False
169-
if not os.path.isfile(test_file):
165+
if not os.path.isfile(test_input):
170166
print('! FAILURE')
171-
print(f'! Missing input file {test_file} ({test_input})')
167+
print(f'! Missing input file {test_input} ({test_input})')
172168
return False
173169

174170
# Looks OK, copy it
175-
shutil.copy(test_file, project_path)
171+
shutil.copy(test_input, project_path)
176172

177173
print('# Copied')
178174

@@ -288,51 +284,110 @@ def _test(args: argparse.Namespace,
288284

289285
_print_test_banner(collection, job, job_test_name)
290286

287+
# The status changes to False if any
288+
# part of this block fails.
289+
test_status: bool = True
290+
291291
# Does the test have an 'ignore' declaration?
292292
if 'ignore' in job_definition.tests[job_test_name]:
293293
print('W Ignoring test (found "ignore")')
294294
tests_ignored += 1
295295
continue
296296

297297
# Render the command for this test.
298+
298299
# First extract the variables and values from 'options'
299-
# and then 'inputs'...
300+
# and then 'inputs'.
300301
job_variables: Dict[str, Any] = {}
301302
for variable in job_definition.tests[job_test_name].options:
302303
job_variables[variable] =\
303304
job_definition.tests[job_test_name].options[variable]
305+
306+
# If the option variable's declaration is 'multiple'
307+
# it must be handled as a list, e.g. it might be declared like this: -
308+
#
309+
# The double-comment is used
310+
# to avoid mypy getting upset by the 'type' line...
311+
#
312+
# # properties:
313+
# # fragments:
314+
# # title: Fragment molecules
315+
# # multiple: true
316+
# # mime-types:
317+
# # - chemical/x-mdl-molfile
318+
# # type: file
319+
#
304320
# We only pass the basename of the input to the command decoding
305321
# i.e. strip the source directory.
322+
323+
# A list of input files (relative to this directory)
324+
# We populate this with everything we find declared as an input
325+
input_files: List[str] = []
326+
327+
# Process every 'input'
306328
for variable in job_definition.tests[job_test_name].inputs:
307-
job_variables[variable] =\
308-
os.path.basename(job_definition.tests[job_test_name]
309-
.inputs[variable])
310-
311-
# Get the raw (encoded) command from the job definition...
312-
raw_command: str = job_definition.command
313-
# Decode it using our variables...
314-
decoded_command, test_status =\
315-
decoder.decode(raw_command,
316-
job_variables,
317-
'command',
318-
decoder.TextEncoding.JINJA2_3_0)
319-
if not test_status:
320-
print('! FAILURE')
321-
print('! Failed to render command')
322-
print(f'! error={decoded_command}')
323-
# Record but do no further processing
324-
tests_failed += 1
325-
test_status = False
329+
# Test variable must be known as an input or option.
330+
# Is the variable an option (otherwise it's an input)
331+
variable_is_option: bool = False
332+
variable_is_input: bool = False
333+
if variable in job_definition.variables.options.properties:
334+
variable_is_option = True
335+
elif variable in job_definition.variables.inputs.properties:
336+
variable_is_input = True
337+
if not variable_is_option and not variable_is_input:
338+
print('! FAILURE')
339+
print(f'! Test variable ({variable})' +
340+
' not declared as input or option')
341+
# Record but do no further processing
342+
tests_failed += 1
343+
test_status = False
344+
# Is it declared as a list?
345+
value_is_list: bool = False
346+
if variable_is_option:
347+
if job_definition.variables.options.properties[variable].multiple:
348+
value_is_list = True
349+
else:
350+
if job_definition.variables.inputs.properties[variable].multiple:
351+
value_is_list = True
352+
353+
# Add each value or just one value
354+
# (depending on whether it's a list)
355+
if value_is_list:
356+
job_variables[variable] = []
357+
for value in job_definition.tests[job_test_name].inputs[variable]:
358+
job_variables[variable].append(os.path.basename(value))
359+
input_files.append(value)
360+
else:
361+
value = job_definition.tests[job_test_name].inputs[variable]
362+
job_variables[variable] = os.path.basename(value)
363+
input_files.append(value)
326364

327-
# The command must not contain new-lines.
328-
# So split then join the command.
329-
job_command: str = ''.join(decoded_command.splitlines())
365+
if test_status:
366+
# Get the raw (encoded) command from the job definition...
367+
raw_command: str = job_definition.command
368+
# Decode it using our variables...
369+
decoded_command, test_status =\
370+
decoder.decode(raw_command,
371+
job_variables,
372+
'command',
373+
decoder.TextEncoding.JINJA2_3_0)
374+
if not test_status:
375+
print('! FAILURE')
376+
print('! Failed to render command')
377+
print(f'! error={decoded_command}')
378+
# Record but do no further processing
379+
tests_failed += 1
380+
test_status = False
330381

331382
# Create the test directories, docker-compose file
332383
# and copy inputs...
333384
t_compose: Optional[Compose] = None
334385
if test_status:
335386

387+
# The command must not contain new-lines.
388+
# So split then join the command.
389+
job_command: str = ''.join(decoded_command.splitlines())
390+
336391
print(f'> image={job_image}')
337392
print(f'> command="{job_command}"')
338393

@@ -351,10 +406,8 @@ def _test(args: argparse.Namespace,
351406

352407
# Copy the data into the test's project directory.
353408
# Data's expected to be found in the Job's 'inputs'.
354-
if job_definition.tests[job_test_name].inputs:
355-
test_status =\
356-
_copy_inputs(job_definition.tests[job_test_name].inputs,
357-
project_path)
409+
print(f'input_files={input_files}')
410+
test_status = _copy_inputs(input_files, project_path)
358411

359412
# Run the container
360413
if test_status and not args.dry_run:

0 commit comments

Comments
 (0)