Skip to content

Commit

Permalink
Rebase...
Browse files Browse the repository at this point in the history
  • Loading branch information
jmchilton committed Sep 22, 2024
1 parent 9c34969 commit 0df8e95
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 69 deletions.
4 changes: 2 additions & 2 deletions lib/galaxy/tool_util/parameters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
dereference,
encode,
encode_test,
fill_defaults,
fill_static_defaults,
)
from .factory import (
from_input_source,
Expand Down Expand Up @@ -138,7 +138,7 @@
"decode",
"encode",
"encode_test",
"fill_defaults",
"fill_static_defaults",
"dereference",
"WorkflowStepToolState",
"WorkflowStepLinkedToolState",
Expand Down
7 changes: 4 additions & 3 deletions lib/galaxy/tool_util/parameters/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,8 @@ def encode_callback(parameter: ToolParameterT, value: Any):
return request_state


def fill_defaults(
tool_state: Dict[str, Any], input_models: ToolParameterBundle, partial: bool = True
def fill_static_defaults(
tool_state: Dict[str, Any], input_models: ToolParameterBundle, profile: str, partial: bool = True
) -> Dict[str, Any]:
"""If additional defaults might stem from Galaxy runtime, partial should be true.
Expand Down Expand Up @@ -269,7 +269,8 @@ def _fill_default_for(tool_state: Dict[str, Any], parameter: ToolParameterT) ->
# even optional parameters default to false if not in the body of the request :_(
# see test_tools.py -> expression_null_handling_boolean or test cases for gx_boolean_optional.xml
tool_state[parameter_name] = boolean.value or False
if parameter_type in ["gx_integer", "gx_float", "gx_hidden", "gx_data_column"]:

if parameter_type in ["gx_integer", "gx_float", "gx_hidden"]:
has_value_parameter = cast(
Union[
IntegerParameterModel,
Expand Down
31 changes: 17 additions & 14 deletions lib/galaxy/tool_util/parameters/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
PagesSource,
ToolSource,
)
from galaxy.tool_util.parser.util import parse_profile_version
from galaxy.util import string_as_bool
from .models import (
BaseUrlParameterModel,
Expand Down Expand Up @@ -64,7 +65,7 @@ def get_color_value(input_source: InputSource) -> str:
return input_source.get("value", "#000000")


def _from_input_source_galaxy(input_source: InputSource) -> ToolParameterT:
def _from_input_source_galaxy(input_source: InputSource, profile: float) -> ToolParameterT:
input_type = input_source.parse_input_type()
if input_type == "param":
param_type = input_source.get("type")
Expand Down Expand Up @@ -180,8 +181,8 @@ def _from_input_source_galaxy(input_source: InputSource) -> ToolParameterT:
multiple = input_source.get_bool("multiple", False)
optional = input_source.parse_optional()
value = input_source.parse_data_column_value()
accept_default = (
input_source.get("accept_default", False) or not multiple
accept_default = input_source.get("accept_default", False) or (
not multiple and profile < 20.01
) # wild but that is what the tests say I think
if value is None and accept_default:
if multiple:
Expand Down Expand Up @@ -224,7 +225,8 @@ def _from_input_source_galaxy(input_source: InputSource) -> ToolParameterT:
elif input_type == "conditional":
test_param_input_source = input_source.parse_test_input_source()
test_parameter = cast(
Union[BooleanParameterModel, SelectParameterModel], _from_input_source_galaxy(test_param_input_source)
Union[BooleanParameterModel, SelectParameterModel],
_from_input_source_galaxy(test_param_input_source, profile),
)
whens = []
default_test_value = cond_test_parameter_default_value(test_parameter)
Expand All @@ -235,7 +237,7 @@ def _from_input_source_galaxy(input_source: InputSource) -> ToolParameterT:
else:
typed_value = value

tool_parameter_models = input_models_for_page(case_inputs_sources)
tool_parameter_models = input_models_for_page(case_inputs_sources, profile)
is_default_when = False
if typed_value == default_test_value:
is_default_when = True
Expand All @@ -254,7 +256,7 @@ def _from_input_source_galaxy(input_source: InputSource) -> ToolParameterT:
# title = input_source.get("title")
# help = input_source.get("help", None)
instance_sources = input_source.parse_nested_inputs_source()
instance_tool_parameter_models = input_models_for_page(instance_sources)
instance_tool_parameter_models = input_models_for_page(instance_sources, profile)
min_raw = input_source.get("min", None)
max_raw = input_source.get("max", None)
min = int(min_raw) if min_raw is not None else None
Expand All @@ -268,7 +270,7 @@ def _from_input_source_galaxy(input_source: InputSource) -> ToolParameterT:
elif input_type == "section":
name = input_source.get("name")
instance_sources = input_source.parse_nested_inputs_source()
instance_tool_parameter_models = input_models_for_page(instance_sources)
instance_tool_parameter_models = input_models_for_page(instance_sources, profile)
return SectionParameterModel(
name=name,
parameters=instance_tool_parameter_models,
Expand Down Expand Up @@ -341,34 +343,35 @@ def tool_parameter_bundle_from_json(json: Dict[str, Any]) -> ToolParameterBundle

def input_models_for_tool_source(tool_source: ToolSource) -> ToolParameterBundleModel:
pages = tool_source.parse_input_pages()
return ToolParameterBundleModel(parameters=input_models_for_pages(pages))
profile = parse_profile_version(tool_source)
return ToolParameterBundleModel(parameters=input_models_for_pages(pages, profile))


def input_models_for_pages(pages: PagesSource) -> List[ToolParameterT]:
def input_models_for_pages(pages: PagesSource, profile: float) -> List[ToolParameterT]:
input_models = []
if pages.inputs_defined:
for page_source in pages.page_sources:
input_models.extend(input_models_for_page(page_source))
input_models.extend(input_models_for_page(page_source, profile))

return input_models


def input_models_for_page(page_source: PageSource) -> List[ToolParameterT]:
def input_models_for_page(page_source: PageSource, profile: float) -> List[ToolParameterT]:
input_models = []
for input_source in page_source.parse_input_sources():
input_type = input_source.parse_input_type()
if input_type == "display":
# not a real input... just skip this. Should this be handled in the parser layer better?
continue
tool_parameter_model = from_input_source(input_source)
tool_parameter_model = from_input_source(input_source, profile)
input_models.append(tool_parameter_model)
return input_models


def from_input_source(input_source: InputSource) -> ToolParameterT:
def from_input_source(input_source: InputSource, profile: float) -> ToolParameterT:
tool_parameter: ToolParameterT
if isinstance(input_source, CwlInputSource):
tool_parameter = _from_input_source_cwl(input_source)
else:
tool_parameter = _from_input_source_galaxy(input_source)
tool_parameter = _from_input_source_galaxy(input_source, profile)
return tool_parameter
22 changes: 16 additions & 6 deletions lib/galaxy/tool_util/parameters/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1033,15 +1033,25 @@ def model_x_discriminator(v: Any) -> Optional[str]:
else:
return str(test_param_val)

cond_type = union_type(when_types)
if len(when_types) > 1:
cond_type = union_type(when_types)

class ConditionalType(RootModel):
root: cond_type = Field(..., discriminator=Discriminator(model_x_discriminator)) # type: ignore[valid-type]
class ConditionalType(RootModel):
root: cond_type = Field(..., discriminator=Discriminator(model_x_discriminator)) # type: ignore[valid-type]

if default_type is not None:
initialize_cond = None
else:
initialize_cond = ...

if default_type is not None:
initialize_cond = None
else:
initialize_cond = ...
ConditionalType = when_types[0]
# a better check here would be if any of the parameters below this have a required value,
# in the case of job_internal though this is correct
if state_representation == "job_internal":
initialize_cond = ...
else:
initialize_cond = None

py_type = ConditionalType

Expand Down
4 changes: 3 additions & 1 deletion lib/galaxy/tool_util/parser/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,9 @@ def to_element(xml_element_dict: "TestCollectionDefElementInternal") -> "JsonTes
identifier=identifier, **element_object._test_format_to_dict()
)
else:
input_as_dict: Optional[JsonTestDatasetDefDict] = xml_data_input_to_json(cast(ToolSourceTestInput, element_object))
input_as_dict: Optional[JsonTestDatasetDefDict] = xml_data_input_to_json(
cast(ToolSourceTestInput, element_object)
)
if input_as_dict is not None:
as_dict = JsonTestCollectionDefDatasetElementDict(
identifier=identifier,
Expand Down
8 changes: 6 additions & 2 deletions lib/galaxy/tool_util/verify/interactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,10 @@ def run_tool(
parameters = request_schema["parameters"]

def adapt_datasets(test_input: JsonTestDatasetDefDict) -> DataRequestHda:
return DataRequestHda(**self.uploads[test_input["path"]])
# if path is not set it might be a composite file with a path,
# e.g. composite_shapefile
test_input_path = test_input.get("path", "")
return DataRequestHda(**self.uploads[test_input_path])

def adapt_collections(test_input: JsonTestCollectionDefDict) -> DataCollectionRequest:
test_collection_def = TestCollectionDef.from_dict(test_input)
Expand Down Expand Up @@ -709,7 +712,8 @@ def adapt_collections(test_input: JsonTestCollectionDefDict) -> DataCollectionRe
if not successful:
request = self.get_tool_request(tool_request_id) or {}
raise RunToolException(
f"Tool request failure - state {request.get('state')}, message: {request.get('state_message')}", inputs_tree
f"Tool request failure - state {request.get('state')}, message: {request.get('state_message')}",
inputs_tree,
)
jobs = self.jobs_for_tool_request(tool_request_id)
outputs = OutputsDict()
Expand Down
11 changes: 6 additions & 5 deletions lib/galaxy/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
)
from galaxy.tool_util.output_checker import DETECTED_JOB_STATE
from galaxy.tool_util.parameters import (
fill_defaults,
fill_static_defaults,
input_models_for_pages,
JobInternalToolState,
RequestInternalDereferencedToolState,
Expand Down Expand Up @@ -127,6 +127,7 @@
from galaxy.tools.imp_exp import JobImportHistoryArchiveWrapper
from galaxy.tools.parameters import (
check_param,
fill_dynamic_defaults,
params_from_strings,
params_to_incoming,
params_to_json,
Expand Down Expand Up @@ -1439,7 +1440,7 @@ def parse_inputs(self, tool_source: ToolSource):
pages = tool_source.parse_input_pages()
enctypes: Set[str] = set()
try:
parameters = input_models_for_pages(pages)
parameters = input_models_for_pages(pages, tool_source.parse_profile())
self.parameters = parameters
except Exception:
pass
Expand Down Expand Up @@ -1874,10 +1875,10 @@ def expand_incoming_async(
all_params: List[ToolStateJobInstancePopulatedT] = []
internal_states: List[JobInternalToolState] = []
for expanded_incoming in job_tool_states:
expanded_incoming = fill_defaults(expanded_incoming, self)
expanded_incoming = fill_static_defaults(expanded_incoming, self, self.profile)
fill_dynamic_defaults(request_context, self.inputs, expanded_incoming)
internal_tool_state = JobInternalToolState(expanded_incoming)
# internal_tool_state.validate(self)
# some defaults might not be available until populate runs...
internal_tool_state.validate(self)
internal_states.append(internal_tool_state)
params, errors = self._populate(request_context, expanded_incoming, "21.01")

Expand Down
55 changes: 55 additions & 0 deletions lib/galaxy/tools/parameters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,61 @@ def _populate_state_legacy(
state[input.name] = value


def fill_dynamic_defaults(
request_context,
inputs: ToolInputsT,
incoming: ToolStateJobInstanceT,
context=None,
):
"""
Expands incoming parameters with default values.
"""
if context is None:
context = flat_to_nested_state(incoming)
for input in inputs.values():
if input.type == "repeat":
repeat_input = cast(Repeat, input)
for rep in incoming[repeat_input.name]:
fill_dynamic_defaults(
request_context,
repeat_input.inputs,
rep,
context=context,
)

elif input.type == "conditional":
conditional_input = cast(Conditional, input)
test_param = cast(ToolParameter, conditional_input.test_param)
test_param_value = incoming.get(conditional_input.name, {}).get(test_param.name)
try:
current_case = conditional_input.get_current_case(test_param_value)
fill_dynamic_defaults(
request_context,
conditional_input.cases[current_case].inputs,
cast(ToolStateJobInstanceT, incoming.get(conditional_input.name)),
context=context,
)
except Exception:
raise Exception("The selected case is unavailable/invalid.")

elif input.type == "section":
section_input = cast(Section, input)
fill_dynamic_defaults(
request_context,
section_input.inputs,
cast(ToolStateJobInstanceT, incoming.get(section_input.name)),
context=context,
)

elif input.type == "upload_dataset":
raise NotImplementedError

else:
if input.name not in incoming:
param_value = input.get_initial_value(request_context, context)
incoming[input.name] = param_value


def _get_incoming_value(incoming, key, default):
"""
Fetch value from incoming dict directly or check special nginx upload
Expand Down
1 change: 1 addition & 0 deletions test/functional/tools/select_optional.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<tool id="select_optional" name="Optional select" version="1.0.0" profile="20.01">
<!-- this tool has a profile of 20.01, but has an identical behavior without it -->
<command><![CDATA[
echo select_opt $select_opt >> '$output1' &&
echo select_mult_opt $select_mult_opt >> '$output1' &&
Expand Down
34 changes: 34 additions & 0 deletions test/unit/tool_util/framework_tool_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,37 @@ empty_select:
- {}
request_invalid:
- select_optional: anyoption

select_from_dataset_in_conditional:
request_valid:
- single: {src: hda, id: abcde133543d}
request_invalid:
- single: 7
request_internal_valid:
- single: {src: hda, id: 7}
request_internal_invalid:
- single: 7
job_internal_valid:
- single: {src: hda, id: 7}
job_internal_valid:
- single: {src: hda, id: 7}
cond:
cond: single
select_single: chr10
inner_cond:
inner_cond: single
select_single: chr10
job_internal_invalid:
- single: {src: hda, id: 7}
cond:
cond: single
select_single: chr10
inner_cond:
inner_cond: single
select_single: chr10
badoption: true
- single: {src: hda, id: 7}
cond:
cond: single
select_single: chr10

Loading

0 comments on commit 0df8e95

Please sign in to comment.