Skip to content

Commit 4e31d34

Browse files
committed
inherit parents requirements correclty
1 parent a233e28 commit 4e31d34

9 files changed

Lines changed: 232 additions & 153 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
## 🐛 Bugfixes
88
- fixed a bug with Dockerfile path resolution
99
- handle NetworkAccess Requirment in runner
10-
- ramping up runner conformance from 160/378 to 179/378
10+
- inherit parents requirements correclty
11+
- ramping up runner conformance from 160/378 to 183/378
1112

1213
# v0.5.2
1314
## 🐛 Bugfixes

crates/cwl-execution/src/environment.rs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
use crate::{get_available_disk_space, get_available_ram, get_processor_count, util::evaluate_input, validate::set_placeholder_values_in_string};
1+
use crate::{
2+
get_available_disk_space, get_available_ram, get_processor_count, util::evaluate_input, validate::set_placeholder_values_in_string, InputObject,
3+
};
24
use cwl::{
35
inputs::CommandInputParameter,
4-
requirements::{check_timelimit, EnvVarRequirement, NetworkAccess, ResourceRequirement},
6+
requirements::{EnvVarRequirement, NetworkAccess, ResourceRequirement, ToolTimeLimit},
57
types::{DefaultValue, EnviromentDefs},
68
CWLDocument, StringOrNumber,
79
};
@@ -42,7 +44,7 @@ impl RuntimeEnvironment {
4244

4345
pub fn initialize(
4446
tool: &CWLDocument,
45-
input_values: HashMap<String, DefaultValue>,
47+
input_values: &InputObject,
4648
outdir: impl AsRef<Path>,
4749
tooldir: impl AsRef<Path>,
4850
tmpdir: impl AsRef<Path>,
@@ -68,23 +70,23 @@ impl RuntimeEnvironment {
6870

6971
runtime.insert(
7072
"network".to_string(),
71-
if tool.get_requirement::<NetworkAccess>().is_some() {
73+
if input_values.get_requirement::<NetworkAccess>().is_some() {
7274
StringOrNumber::Integer(1)
7375
} else {
7476
StringOrNumber::Integer(0)
7577
},
7678
);
7779

78-
let inputs = collect_inputs(tool, input_values, tooldir)?;
80+
let inputs = collect_inputs(tool, &input_values.inputs, tooldir)?;
7981

8082
let mut environment = RuntimeEnvironment {
8183
runtime,
82-
time_limit: check_timelimit(tool).unwrap_or(0),
84+
time_limit: input_values.get_requirement::<ToolTimeLimit>().map(|tt| tt.timelimit).unwrap_or(0),
8385
inputs,
8486
..Default::default()
8587
};
8688

87-
if let Some(rr) = tool.get_requirement::<ResourceRequirement>() {
89+
if let Some(rr) = input_values.get_requirement::<ResourceRequirement>() {
8890
if let Some(cores) = &rr.cores_min {
8991
environment
9092
.runtime
@@ -121,8 +123,8 @@ fn evaluate(val: &StringOrNumber, runtime: &RuntimeEnvironment, inputs: &[Comman
121123
}
122124
}
123125

124-
pub(crate) fn collect_environment(tool: &CWLDocument) -> HashMap<String, String> {
125-
if let Some(env) = tool.get_requirement::<EnvVarRequirement>() {
126+
pub(crate) fn collect_environment(input_values: &InputObject) -> HashMap<String, String> {
127+
if let Some(env) = input_values.get_requirement::<EnvVarRequirement>() {
126128
match &env.env_def {
127129
EnviromentDefs::Vec(vec) => vec.iter().map(|i| (i.env_name.clone(), i.env_value.clone())).collect::<HashMap<_, _>>(),
128130
EnviromentDefs::Map(map) => map.clone(),
@@ -134,12 +136,12 @@ pub(crate) fn collect_environment(tool: &CWLDocument) -> HashMap<String, String>
134136

135137
pub(crate) fn collect_inputs(
136138
tool: &CWLDocument,
137-
input_values: HashMap<String, DefaultValue>,
139+
input_values: &HashMap<String, DefaultValue>,
138140
tool_dir: impl AsRef<Path>,
139141
) -> Result<HashMap<String, DefaultValue>, Box<dyn Error>> {
140142
let mut inputs = HashMap::new();
141143
for input in &tool.inputs {
142-
let mut result_input = evaluate_input(input, &input_values)?;
144+
let mut result_input = evaluate_input(input, input_values)?;
143145
if let DefaultValue::File(f) = &mut result_input {
144146
if input.load_contents {
145147
f.contents = Some(fs::read_to_string(f.location.as_ref().expect("Could not read file"))?);
@@ -157,7 +159,7 @@ pub(crate) fn collect_inputs(
157159
#[cfg(test)]
158160
mod tests {
159161
use super::*;
160-
use cwl::{clt::CommandLineTool, requirements::{EnvVarRequirement, Requirement}};
162+
use cwl::requirements::{EnvVarRequirement, Requirement};
161163

162164
#[test]
163165
fn test_requirements_overwrite_hints() {
@@ -167,12 +169,13 @@ mod tests {
167169
let requirement = EnvVarRequirement {
168170
env_def: EnviromentDefs::Map(HashMap::from([("MY_ENV".to_string(), "REQUIREMENT".to_string())])),
169171
};
172+
let input_values = InputObject {
173+
requirements: vec![Requirement::EnvVarRequirement(requirement.clone())],
174+
hints: vec![Requirement::EnvVarRequirement(hint.clone())],
175+
..Default::default()
176+
};
170177

171-
let tool = CommandLineTool::default()
172-
.with_requirements(vec![Requirement::EnvVarRequirement(requirement)])
173-
.with_hints(vec![Requirement::EnvVarRequirement(hint)]);
174-
175-
let environment = collect_environment(&CWLDocument::CommandLineTool(tool));
178+
let environment = collect_environment(&input_values);
176179

177180
assert_eq!(environment["MY_ENV"], "REQUIREMENT".to_string())
178181
}

crates/cwl-execution/src/expression.rs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{environment::RuntimeEnvironment, split_ranges};
1+
use crate::{environment::RuntimeEnvironment, split_ranges, InputObject};
22
use cwl::{
33
clt::{Argument, Command},
44
et::{Expression, ExpressionType},
@@ -182,22 +182,20 @@ pub(crate) fn parse_expressions(input: &str) -> Vec<Expression> {
182182
expressions
183183
}
184184

185-
pub(crate) fn process_tool_expressions(tool: &mut CWLDocument) -> Result<(), Box<dyn Error>> {
186-
if let Some(requirements) = &mut tool.requirements {
187-
for requirement in requirements {
188-
if let Requirement::InitialWorkDirRequirement(wd_req) = requirement {
189-
for listing in &mut wd_req.listing {
190-
if let WorkDirItem::Dirent(dirent) = listing {
191-
dirent.entryname = replace_expressions(&dirent.entryname)?;
192-
dirent.entry = match &mut dirent.entry {
193-
Entry::Source(src) => {
194-
*src = replace_expressions(src)?;
195-
Entry::Source(src.clone())
196-
}
197-
Entry::Include(include) => {
198-
include.include = replace_expressions(&include.include)?;
199-
Entry::Include(include.clone())
200-
}
185+
pub(crate) fn process_expressions(tool: &mut CWLDocument, input_values: &mut InputObject) -> Result<(), Box<dyn Error>> {
186+
for requirement in &mut input_values.requirements {
187+
if let Requirement::InitialWorkDirRequirement(wd_req) = requirement {
188+
for listing in &mut wd_req.listing {
189+
if let WorkDirItem::Dirent(dirent) = listing {
190+
dirent.entryname = replace_expressions(&dirent.entryname)?;
191+
dirent.entry = match &mut dirent.entry {
192+
Entry::Source(src) => {
193+
*src = replace_expressions(src)?;
194+
Entry::Source(src.clone())
195+
}
196+
Entry::Include(include) => {
197+
include.include = replace_expressions(&include.include)?;
198+
Entry::Include(include.clone())
201199
}
202200
}
203201
}
@@ -352,7 +350,8 @@ stderr: $(inputs.dirname)/stderr
352350
};
353351
prepare_expression_engine(&runtime).unwrap();
354352
let mut tool: CWLDocument = serde_yaml::from_str(tool).unwrap();
355-
process_tool_expressions(&mut tool).unwrap();
353+
let mut input_values = InputObject::default();
354+
process_expressions(&mut tool, &mut input_values).unwrap();
356355
reset_expression_engine().unwrap();
357356

358357
assert!(matches!(tool, CWLDocument::CommandLineTool(_)));

crates/cwl-execution/src/lib.rs

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,43 @@ pub mod staging;
66
pub mod util;
77
pub mod validate;
88

9+
use cwl::requirements::{FromRequirement, Requirement};
910
use cwl::types::{guess_type, CWLType, DefaultValue, Directory, File, PathItem};
1011
use cwl::CWLDocument;
1112
use io::preprocess_path_join;
1213
use runner::{run_tool, run_workflow};
14+
use serde::{Deserialize, Serialize};
1315
use serde_yaml::Value;
1416
use std::{cell::RefCell, collections::HashMap, error::Error, fmt::Display, fs, num::NonZero, path::Path, process::Command, thread};
1517
use sysinfo::{Disks, MemoryRefreshKind, System};
1618
use util::preprocess_cwl;
1719

1820
pub fn execute_cwlfile(cwlfile: impl AsRef<Path>, raw_inputs: &[String], outdir: Option<impl AsRef<Path>>) -> Result<(), Box<dyn Error>> {
1921
//gather inputs
20-
let mut inputs = if raw_inputs.len() == 1 && !raw_inputs[0].starts_with('-') {
22+
let mut input_values = if raw_inputs.len() == 1 && !raw_inputs[0].starts_with('-') {
2123
let yaml = fs::read_to_string(&raw_inputs[0])?;
2224
serde_yaml::from_str(&yaml).map_err(|e| format!("Could not read job file: {e}"))?
2325
} else {
24-
raw_inputs
25-
.chunks_exact(2)
26-
.filter_map(|pair| {
27-
if let Some(key) = pair[0].strip_prefix("--") {
28-
let raw_value = &pair[1];
29-
let value = match guess_type(raw_value) {
30-
CWLType::File => DefaultValue::File(File::from_location(raw_value)),
31-
CWLType::Directory => DefaultValue::Directory(Directory::from_location(raw_value)),
32-
CWLType::String => DefaultValue::Any(Value::String(raw_value.to_string())),
33-
_ => DefaultValue::Any(serde_yaml::from_str(raw_value).expect("Could not read input")),
34-
};
35-
Some((key.to_string(), value))
36-
} else {
37-
None
38-
}
39-
})
40-
.collect::<HashMap<_, _>>()
26+
InputObject {
27+
inputs: raw_inputs
28+
.chunks_exact(2)
29+
.filter_map(|pair| {
30+
if let Some(key) = pair[0].strip_prefix("--") {
31+
let raw_value = &pair[1];
32+
let value = match guess_type(raw_value) {
33+
CWLType::File => DefaultValue::File(File::from_location(raw_value)),
34+
CWLType::Directory => DefaultValue::Directory(Directory::from_location(raw_value)),
35+
CWLType::String => DefaultValue::Any(Value::String(raw_value.to_string())),
36+
_ => DefaultValue::Any(serde_yaml::from_str(raw_value).expect("Could not read input")),
37+
};
38+
Some((key.to_string(), value))
39+
} else {
40+
None
41+
}
42+
})
43+
.collect::<HashMap<_, _>>(),
44+
..Default::default()
45+
}
4146
};
4247

4348
fn correct_path<T: PathItem>(item: &mut T, path_prefix: &Path) {
@@ -69,15 +74,15 @@ pub fn execute_cwlfile(cwlfile: impl AsRef<Path>, raw_inputs: &[String], outdir:
6974
} else {
7075
Path::new(".")
7176
};
72-
for value in inputs.values_mut() {
77+
for value in input_values.inputs.values_mut() {
7378
match value {
7479
DefaultValue::File(file) => correct_path(file, path_prefix),
7580
DefaultValue::Directory(directory) => correct_path(directory, path_prefix),
7681
_ => (),
7782
}
7883
}
7984

80-
let output_values = execute(cwlfile, inputs, outdir, None)?;
85+
let output_values = execute(cwlfile, input_values, outdir, None)?;
8186
let json = serde_json::to_string_pretty(&output_values)?;
8287
println!("{json}");
8388

@@ -86,7 +91,7 @@ pub fn execute_cwlfile(cwlfile: impl AsRef<Path>, raw_inputs: &[String], outdir:
8691

8792
pub fn execute(
8893
cwlfile: impl AsRef<Path>,
89-
inputs: HashMap<String, DefaultValue>,
94+
input_values: InputObject,
9095
outdir: Option<impl AsRef<Path>>,
9196
cwl_doc: Option<&CWLDocument>,
9297
) -> Result<HashMap<String, DefaultValue>, Box<dyn Error>> {
@@ -102,19 +107,67 @@ pub fn execute(
102107
match doc {
103108
CWLDocument::CommandLineTool(_) | CWLDocument::ExpressionTool(_) => run_tool(
104109
&mut doc,
105-
inputs,
110+
input_values,
106111
Some(&cwlfile.as_ref().to_path_buf()),
107112
outdir.map(|d| d.as_ref().to_string_lossy().into_owned()),
108113
),
109114
CWLDocument::Workflow(mut workflow) => run_workflow(
110115
&mut workflow,
111-
inputs,
116+
input_values,
112117
Some(&cwlfile.as_ref().to_path_buf()),
113118
outdir.map(|d| d.as_ref().to_string_lossy().into_owned()),
114119
),
115120
}
116121
}
117122

123+
#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Clone)]
124+
pub struct InputObject {
125+
#[serde(flatten)]
126+
pub inputs: HashMap<String, DefaultValue>,
127+
#[serde(rename = "cwl:requirements")]
128+
#[serde(default)]
129+
pub requirements: Vec<Requirement>,
130+
#[serde(rename = "cwl:hints")]
131+
#[serde(default)]
132+
pub hints: Vec<Requirement>,
133+
}
134+
135+
impl InputObject {
136+
pub fn get_requirement<T>(&self) -> Option<&T>
137+
where
138+
Requirement: FromRequirement<T>,
139+
{
140+
self.requirements.iter().chain(self.hints.iter()).find_map(|req| Requirement::get(req))
141+
}
142+
143+
pub fn add_requirement(&mut self, requirement: &Requirement) {
144+
if self
145+
.requirements
146+
.iter()
147+
.any(|r| std::mem::discriminant(r) == std::mem::discriminant(requirement))
148+
{
149+
return;
150+
}
151+
self.requirements.push(requirement.clone());
152+
}
153+
154+
pub fn add_hint(&mut self, hint: &Requirement) {
155+
if self.hints.iter().any(|r| std::mem::discriminant(r) == std::mem::discriminant(hint)) {
156+
return;
157+
}
158+
self.hints.push(hint.clone());
159+
}
160+
}
161+
162+
impl From<HashMap<String, DefaultValue>> for InputObject {
163+
fn from(inputs: HashMap<String, DefaultValue>) -> Self {
164+
Self {
165+
inputs,
166+
..Default::default()
167+
}
168+
}
169+
}
170+
118171
pub trait ExitCode {
119172
fn exit_code(&self) -> i32;
120173
}

0 commit comments

Comments
 (0)