Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/cwltool/validate_js.py @ 0:d30785e31577 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:18:57 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:d30785e31577 |
|---|---|
| 1 import copy | |
| 2 import itertools | |
| 3 import json | |
| 4 import logging | |
| 5 from collections import namedtuple | |
| 6 from typing import (cast, Any, Dict, List, MutableMapping, MutableSequence, | |
| 7 Optional, Tuple, Union) | |
| 8 | |
| 9 from pkg_resources import resource_stream | |
| 10 from ruamel.yaml.comments import CommentedMap # pylint: disable=unused-import | |
| 11 from six import string_types | |
| 12 from typing_extensions import Text # pylint: disable=unused-import | |
| 13 from schema_salad import avro | |
| 14 from schema_salad.sourceline import SourceLine | |
| 15 from schema_salad.validate import Schema # pylint: disable=unused-import | |
| 16 from schema_salad.validate import validate_ex | |
| 17 | |
| 18 from .errors import WorkflowException | |
| 19 from .expression import scanner as scan_expression | |
| 20 from .expression import SubstitutionError | |
| 21 from .loghandler import _logger | |
| 22 from .sandboxjs import code_fragment_to_js, exec_js_process | |
| 23 from .utils import json_dumps | |
| 24 | |
| 25 | |
| 26 def is_expression(tool, schema): | |
| 27 # type: (Any, Optional[Schema]) -> bool | |
| 28 return isinstance(schema, avro.schema.EnumSchema) \ | |
| 29 and schema.name == "Expression" and isinstance(tool, string_types) | |
| 30 | |
| 31 class SuppressLog(logging.Filter): | |
| 32 def __init__(self, name): # type: (Text) -> None | |
| 33 """Initialize this log suppressor.""" | |
| 34 name = str(name) | |
| 35 super(SuppressLog, self).__init__(name) | |
| 36 | |
| 37 def filter(self, record): # type: (logging.LogRecord) -> bool | |
| 38 return False | |
| 39 | |
| 40 | |
| 41 _logger_validation_warnings = logging.getLogger("cwltool.validation_warnings") | |
| 42 _logger_validation_warnings.addFilter(SuppressLog("cwltool.validation_warnings")) | |
| 43 | |
| 44 def get_expressions(tool, # type: Union[CommentedMap, Text] | |
| 45 schema, # type: Optional[avro.schema.Schema] | |
| 46 source_line=None # type: Optional[SourceLine] | |
| 47 ): # type: (...) -> List[Tuple[Text, Optional[SourceLine]]] | |
| 48 if is_expression(tool, schema): | |
| 49 return [(cast(Text, tool), source_line)] | |
| 50 elif isinstance(schema, avro.schema.UnionSchema): | |
| 51 valid_schema = None | |
| 52 | |
| 53 for possible_schema in schema.schemas: | |
| 54 if is_expression(tool, possible_schema): | |
| 55 return [(cast(Text, tool), source_line)] | |
| 56 elif validate_ex(possible_schema, tool, raise_ex=False, | |
| 57 logger=_logger_validation_warnings): | |
| 58 valid_schema = possible_schema | |
| 59 | |
| 60 return get_expressions(tool, valid_schema, source_line) | |
| 61 elif isinstance(schema, avro.schema.ArraySchema): | |
| 62 if not isinstance(tool, MutableSequence): | |
| 63 return [] | |
| 64 | |
| 65 return list(itertools.chain( | |
| 66 *map(lambda x: get_expressions(x[1], schema.items, SourceLine(tool, x[0])), enumerate(tool)) | |
| 67 )) | |
| 68 | |
| 69 elif isinstance(schema, avro.schema.RecordSchema): | |
| 70 if not isinstance(tool, MutableMapping): | |
| 71 return [] | |
| 72 | |
| 73 expression_nodes = [] | |
| 74 | |
| 75 for schema_field in schema.fields: | |
| 76 if schema_field.name in tool: | |
| 77 expression_nodes.extend(get_expressions( | |
| 78 tool[schema_field.name], | |
| 79 schema_field.type, | |
| 80 SourceLine(tool, schema_field.name) | |
| 81 )) | |
| 82 | |
| 83 return expression_nodes | |
| 84 else: | |
| 85 return [] | |
| 86 | |
| 87 | |
| 88 JSHintJSReturn = namedtuple("jshint_return", ["errors", "globals"]) | |
| 89 | |
| 90 def jshint_js(js_text, # type: Text | |
| 91 globals=None, # type: Optional[List[Text]] | |
| 92 options=None # type: Optional[Dict[Text, Union[List[Text], Text, int]]] | |
| 93 ): # type: (...) -> Tuple[List[Text], List[Text]] | |
| 94 if globals is None: | |
| 95 globals = [] | |
| 96 if options is None: | |
| 97 options = { | |
| 98 "includewarnings": [ | |
| 99 "W117", # <VARIABLE> not defined | |
| 100 "W104", "W119" # using ES6 features | |
| 101 ], | |
| 102 "strict": "implied", | |
| 103 "esversion": 5 | |
| 104 } | |
| 105 | |
| 106 with resource_stream(__name__, "jshint/jshint.js") as file: | |
| 107 # NOTE: we need a global variable for lodash (which jshint depends on) | |
| 108 jshint_functions_text = "var global = this;" + file.read().decode('utf-8') | |
| 109 | |
| 110 with resource_stream(__name__, "jshint/jshint_wrapper.js") as file: | |
| 111 # NOTE: we need to assign to ob, as the expression {validateJS: validateJS} as an expression | |
| 112 # is interpreted as a block with a label `validateJS` | |
| 113 jshint_functions_text += "\n" + file.read().decode('utf-8') + "\nvar ob = {validateJS: validateJS}; ob" | |
| 114 | |
| 115 returncode, stdout, stderr = exec_js_process( | |
| 116 "validateJS(%s)" % json_dumps({ | |
| 117 "code": js_text, | |
| 118 "options": options, | |
| 119 "globals": globals | |
| 120 }), | |
| 121 timeout=30, | |
| 122 context=jshint_functions_text | |
| 123 ) | |
| 124 | |
| 125 def dump_jshint_error(): | |
| 126 # type: () -> None | |
| 127 raise RuntimeError("jshint failed to run succesfully\nreturncode: %d\nstdout: \"%s\"\nstderr: \"%s\"" % ( | |
| 128 returncode, | |
| 129 stdout, | |
| 130 stderr | |
| 131 )) | |
| 132 | |
| 133 if returncode == -1: | |
| 134 _logger.warning("jshint process timed out") | |
| 135 | |
| 136 if returncode != 0: | |
| 137 dump_jshint_error() | |
| 138 | |
| 139 try: | |
| 140 jshint_json = json.loads(stdout) | |
| 141 except ValueError: | |
| 142 dump_jshint_error() | |
| 143 | |
| 144 jshint_errors = [] # type: List[Text] | |
| 145 | |
| 146 js_text_lines = js_text.split("\n") | |
| 147 | |
| 148 for jshint_error_obj in jshint_json.get("errors", []): | |
| 149 text = u"JSHINT: " + js_text_lines[jshint_error_obj["line"] - 1] + "\n" | |
| 150 text += u"JSHINT: " + " " * (jshint_error_obj["character"] - 1) + "^\n" | |
| 151 text += u"JSHINT: %s: %s" % (jshint_error_obj["code"], jshint_error_obj["reason"]) | |
| 152 jshint_errors.append(text) | |
| 153 | |
| 154 return JSHintJSReturn(jshint_errors, jshint_json.get("globals", [])) | |
| 155 | |
| 156 | |
| 157 def print_js_hint_messages(js_hint_messages, source_line): | |
| 158 # type: (List[Text], Optional[SourceLine]) -> None | |
| 159 if source_line is not None: | |
| 160 for js_hint_message in js_hint_messages: | |
| 161 _logger.warning(source_line.makeError(js_hint_message)) | |
| 162 | |
| 163 def validate_js_expressions(tool, # type: CommentedMap | |
| 164 schema, # type: Schema | |
| 165 jshint_options=None # type: Optional[Dict[Text, Union[List[Text], Text, int]]] | |
| 166 ): # type: (...) -> None | |
| 167 | |
| 168 if tool.get("requirements") is None: | |
| 169 return | |
| 170 | |
| 171 requirements = tool["requirements"] | |
| 172 | |
| 173 default_globals = [u"self", u"inputs", u"runtime", u"console"] | |
| 174 | |
| 175 for prop in reversed(requirements): | |
| 176 if prop["class"] == "InlineJavascriptRequirement": | |
| 177 expression_lib = prop.get("expressionLib", []) | |
| 178 break | |
| 179 else: | |
| 180 return | |
| 181 | |
| 182 js_globals = copy.deepcopy(default_globals) | |
| 183 | |
| 184 for i, expression_lib_line in enumerate(expression_lib): | |
| 185 expression_lib_line_errors, expression_lib_line_globals = jshint_js(expression_lib_line, js_globals, jshint_options) | |
| 186 js_globals.extend(expression_lib_line_globals) | |
| 187 print_js_hint_messages(expression_lib_line_errors, SourceLine(expression_lib, i)) | |
| 188 | |
| 189 expressions = get_expressions(tool, schema) | |
| 190 | |
| 191 for expression, source_line in expressions: | |
| 192 unscanned_str = expression.strip() | |
| 193 try: | |
| 194 scan_slice = scan_expression(unscanned_str) | |
| 195 except SubstitutionError as se: | |
| 196 if source_line: | |
| 197 source_line.raise_type = WorkflowException | |
| 198 raise source_line.makeError(str(se)) | |
| 199 else: | |
| 200 raise se | |
| 201 | |
| 202 while scan_slice: | |
| 203 if unscanned_str[scan_slice[0]] == '$': | |
| 204 code_fragment = unscanned_str[scan_slice[0] + 1:scan_slice[1]] | |
| 205 code_fragment_js = code_fragment_to_js(code_fragment, "") | |
| 206 expression_errors, _ = jshint_js(code_fragment_js, js_globals, jshint_options) | |
| 207 print_js_hint_messages(expression_errors, source_line) | |
| 208 | |
| 209 unscanned_str = unscanned_str[scan_slice[1]:] | |
| 210 scan_slice = scan_expression(unscanned_str) |
