|
20 | 20 | Iterable,
|
21 | 21 | Iterator,
|
22 | 22 | List,
|
| 23 | + Mapping, |
23 | 24 | MutableMapping,
|
24 | 25 | MutableSequence,
|
25 | 26 | Optional,
|
|
55 | 56 | from .loghandler import _logger
|
56 | 57 | from .mpi import MPIRequirementName
|
57 | 58 | from .pathmapper import MapperEnt, PathMapper
|
| 59 | +from .sandboxjs import execjs |
58 | 60 | from .secrets import SecretStore
|
59 | 61 | from .stdfsaccess import StdFsAccess
|
60 | 62 | from .update import INTERNAL_VERSION, ORIGINAL_CWLVERSION
|
@@ -523,10 +525,11 @@ def var_spool_cwl_detector(
|
523 | 525 | r = False
|
524 | 526 | if isinstance(obj, str):
|
525 | 527 | if "var/spool/cwl" in obj and obj_key != "dockerOutputDirectory":
|
| 528 | + debug = _logger.isEnabledFor(logging.DEBUG) |
526 | 529 | _logger.warning(
|
527 |
| - SourceLine(item=item, key=obj_key, raise_type=str).makeError( |
528 |
| - _VAR_SPOOL_ERROR.format(obj) |
529 |
| - ) |
| 530 | + SourceLine( |
| 531 | + item=item, key=obj_key, raise_type=str, include_traceback=debug |
| 532 | + ).makeError(_VAR_SPOOL_ERROR.format(obj)) |
530 | 533 | )
|
531 | 534 | r = True
|
532 | 535 | elif isinstance(obj, MutableMapping):
|
@@ -743,7 +746,9 @@ def __init__(
|
743 | 746 | and not is_req
|
744 | 747 | ):
|
745 | 748 | _logger.warning(
|
746 |
| - SourceLine(item=dockerReq, raise_type=str).makeError( |
| 749 | + SourceLine( |
| 750 | + item=dockerReq, raise_type=str, include_traceback=debug |
| 751 | + ).makeError( |
747 | 752 | "When 'dockerOutputDirectory' is declared, DockerRequirement "
|
748 | 753 | "should go in the 'requirements' section, not 'hints'."
|
749 | 754 | ""
|
@@ -805,6 +810,98 @@ def _init_job(
|
805 | 810 | vocab=INPUT_OBJ_VOCAB,
|
806 | 811 | )
|
807 | 812 |
|
| 813 | + restriction_req, mandatory_restrictions = self.get_requirement( |
| 814 | + "ParameterRestrictions" |
| 815 | + ) |
| 816 | + |
| 817 | + if restriction_req: |
| 818 | + restrictions = restriction_req["restrictions"] |
| 819 | + for entry in cast(List[CWLObjectType], restrictions): |
| 820 | + name = shortname(cast(str, entry["input"])) |
| 821 | + if name in job: |
| 822 | + value = job[name] |
| 823 | + matched = False |
| 824 | + for constraint in cast( |
| 825 | + List[CWLOutputType], entry["constraints"] |
| 826 | + ): |
| 827 | + if isinstance(constraint, Mapping): |
| 828 | + if constraint["class"] == "intInterval": |
| 829 | + if not isinstance(value, int): |
| 830 | + raise SourceLine( |
| 831 | + constraint, |
| 832 | + None, |
| 833 | + WorkflowException, |
| 834 | + runtime_context.debug, |
| 835 | + ).makeError( |
| 836 | + "intInterval parameter restriction is only valid for inputs of type 'int'; " |
| 837 | + f"instead got {type(value)}: {value}." |
| 838 | + ) |
| 839 | + low = cast( |
| 840 | + Union[int, float], |
| 841 | + constraint.get("low", -math.inf), |
| 842 | + ) |
| 843 | + high = cast( |
| 844 | + Union[int, float], |
| 845 | + constraint.get("high", math.inf), |
| 846 | + ) |
| 847 | + matched = value >= low and value <= high |
| 848 | + elif constraint["class"] == "realInterval": |
| 849 | + if not isinstance(value, (int, float)): |
| 850 | + raise SourceLine( |
| 851 | + constraint, |
| 852 | + None, |
| 853 | + WorkflowException, |
| 854 | + runtime_context.debug, |
| 855 | + ).makeError( |
| 856 | + "realInterval parameter restriction is only valid for inputs of type 'int', 'float', and 'double'; " |
| 857 | + f"instead got {type(value)}: {value}." |
| 858 | + ) |
| 859 | + low = cast( |
| 860 | + Union[int, float], |
| 861 | + constraint.get("low", -math.inf), |
| 862 | + ) |
| 863 | + high = cast( |
| 864 | + Union[int, float], |
| 865 | + constraint.get("high", math.inf), |
| 866 | + ) |
| 867 | + low_inclusive = constraint.get( |
| 868 | + "low_inclusive", True |
| 869 | + ) |
| 870 | + high_inclusive = constraint.get( |
| 871 | + "high_inclusive", True |
| 872 | + ) |
| 873 | + check_low = ( |
| 874 | + value >= low if low_inclusive else value > low |
| 875 | + ) |
| 876 | + check_high = ( |
| 877 | + value <= high if low_inclusive else value < high |
| 878 | + ) |
| 879 | + matched = check_low and check_high |
| 880 | + elif constraint["class"] == "regex": |
| 881 | + rpattern = constraint["rpattern"] |
| 882 | + quoted_value = json.dumps(value) |
| 883 | + matched = cast( |
| 884 | + bool, |
| 885 | + execjs( |
| 886 | + f"/{rpattern}/.test({quoted_value})", |
| 887 | + "", |
| 888 | + runtime_context.eval_timeout, |
| 889 | + runtime_context.force_docker_pull, |
| 890 | + ), |
| 891 | + ) |
| 892 | + elif constraint == value: |
| 893 | + matched = True |
| 894 | + if matched: |
| 895 | + break |
| 896 | + if not matched: |
| 897 | + raise SourceLine( |
| 898 | + job, name, WorkflowException, runtime_context.debug |
| 899 | + ).makeError( |
| 900 | + f"The field '{name}' is not valid because its " |
| 901 | + f"value '{value}' failed to match any of the " |
| 902 | + f"constraints '{json.dumps(entry['constraints'])}'." |
| 903 | + ) |
| 904 | + |
808 | 905 | if load_listing and load_listing != "no_listing":
|
809 | 906 | get_listing(fs_access, job, recursive=(load_listing == "deep_listing"))
|
810 | 907 |
|
|
0 commit comments