Skip to content

Commit 886a6ac

Browse files
authored
Appveyor upgrade & MyPy 0.610 (#747)
* TravisCI: cache mypy * be more consistent about testing for strings * correct C.UTF-8 capitalization * imports cleanup, used `make isort` and manual fixes * makeTool→make_tool rename * type tweak * rescue critical line from merge error
1 parent 2037975 commit 886a6ac

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1742
-1304
lines changed

.travis.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ before_install:
1313
- sudo apt-get install -y singularity-container
1414

1515
language: python
16-
cache: pip
16+
cache:
17+
pip: true
18+
directories:
19+
- .mypy_cache
1720
python:
1821
- 2.7
1922
- 3.4

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ Technical outline of how cwltool works internally, for maintainers.
509509

510510
#. ``CommandLineTool`` job() objects yield a single runnable object.
511511

512-
#. The CommandLineTool ``job()`` method calls ``makeJobRunner()`` to create a
512+
#. The CommandLineTool ``job()`` method calls ``make_job_runner()`` to create a
513513
``CommandLineJob`` object
514514
#. The job method configures the CommandLineJob object by setting public
515515
attributes

cwltool/__main__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from __future__ import absolute_import
2+
23
import sys
34

45
from . import main

cwltool/argparser.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
from __future__ import absolute_import
2-
from __future__ import print_function
1+
from __future__ import absolute_import, print_function
32

43
import argparse
5-
import logging
64
import os
5+
from typing import (Any, AnyStr, Dict, List, # pylint: disable=unused-import
6+
Optional, Sequence, Text, Union, cast)
77

8-
from typing import (Any, AnyStr, Dict, List, Sequence, Text, Union, cast)
9-
10-
from . import loghandler
118
from schema_salad.ref_resolver import file_uri
12-
from .process import (Process, shortname)
13-
from .resolver import ga4gh_tool_registries
14-
from .software_requirements import (SOFTWARE_REQUIREMENTS_ENABLED)
159

16-
_logger = logging.getLogger("cwltool")
17-
18-
DEFAULT_TMP_PREFIX = "tmp"
10+
from .loghandler import _logger
11+
from .process import Process, shortname # pylint: disable=unused-import
12+
from .resolver import ga4gh_tool_registries
13+
from .software_requirements import SOFTWARE_REQUIREMENTS_ENABLED
14+
from .utils import DEFAULT_TMP_PREFIX
1915

2016

2117
def arg_parser(): # type: () -> argparse.ArgumentParser
@@ -351,7 +347,7 @@ def add_argument(toolparser, name, inptype, records, description="",
351347
return None
352348

353349
ahelp = description.replace("%", "%%")
354-
action = None # type: Union[argparse.Action, Text]
350+
action = None # type: Optional[Union[argparse.Action, Text]]
355351
atype = None # type: Any
356352

357353
if inptype == "File":

cwltool/builder.py

Lines changed: 114 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,38 @@
11
from __future__ import absolute_import
2+
23
import copy
3-
import os
44
import logging
5-
from typing import Any, Callable, Dict, List, Text, Type, Union, Set
6-
7-
import six
8-
from six import iteritems, string_types
5+
from typing import (Any, Callable, Dict, List, # pylint: disable=unused-import
6+
Optional, Set, Text, Type, Union)
97

8+
from rdflib import Graph, URIRef # pylint: disable=unused-import
9+
from rdflib.namespace import OWL, RDFS
10+
import schema_salad.schema # pylint: disable=unused-import
1011
import schema_salad.validate as validate
11-
import schema_salad.schema as schema
12-
from schema_salad.sourceline import SourceLine
1312
from schema_salad.schema import AvroSchemaFromJSONData
14-
15-
from rdflib import Graph, URIRef
16-
from rdflib.namespace import OWL, RDFS
13+
from schema_salad.sourceline import SourceLine
14+
from six import iteritems, string_types
1715

1816
from . import expression
1917
from .errors import WorkflowException
20-
from .mutation import MutationManager
21-
from .pathmapper import (PathMapper, get_listing, normalizeFilesDirs,
22-
visit_class)
23-
from .stdfsaccess import StdFsAccess
24-
from .utils import (aslist, get_feature, docker_windows_path_adjust,
18+
from .loghandler import _logger
19+
from .mutation import MutationManager # pylint: disable=unused-import
20+
from .pathmapper import (PathMapper, # pylint: disable=unused-import
21+
get_listing, normalizeFilesDirs, visit_class)
22+
from .stdfsaccess import StdFsAccess # pylint: disable=unused-import
23+
from .utils import (aslist, docker_windows_path_adjust, get_feature,
2524
json_dumps, onWindows)
2625

27-
_logger = logging.getLogger("cwltool")
28-
2926
CONTENT_LIMIT = 64 * 1024
3027

3128

3229
def substitute(value, replace): # type: (Text, Text) -> Text
3330
if replace[0] == "^":
3431
return substitute(value[0:value.rindex('.')], replace[1:])
35-
else:
36-
return value + replace
32+
return value + replace
3733

3834
def formatSubclassOf(fmt, cls, ontology, visited):
39-
# type: (Text, Text, Graph, Set[Text]) -> bool
35+
# type: (Text, Text, Optional[Graph], Set[Text]) -> bool
4036
"""Determine if `fmt` is a subclass of `cls`."""
4137

4238
if URIRef(fmt) == URIRef(cls):
@@ -69,49 +65,76 @@ def formatSubclassOf(fmt, cls, ontology, visited):
6965

7066
return False
7167

72-
def checkFormat(actualFile, inputFormats, ontology):
73-
# type: (Union[Dict[Text, Any], List, Text], Union[List[Text], Text], Graph) -> None
74-
for af in aslist(actualFile):
75-
if not af:
68+
def check_format(actual_file, # type: Union[Dict[Text, Any], List, Text]
69+
input_formats, # type: Union[List[Text], Text]
70+
ontology # type: Optional[Graph]
71+
): # type: (...) -> None
72+
""" Confirms that the format present is valid for the allowed formats."""
73+
for afile in aslist(actual_file):
74+
if not afile:
7675
continue
77-
if "format" not in af:
76+
if "format" not in afile:
7877
raise validate.ValidationException(
79-
u"File has no 'format' defined: %s" % json_dumps(af, indent=4))
80-
for inpf in aslist(inputFormats):
81-
if af["format"] == inpf or formatSubclassOf(af["format"], inpf, ontology, set()):
78+
u"File has no 'format' defined: {}".format(
79+
json_dumps(afile, indent=4)))
80+
for inpf in aslist(input_formats):
81+
if afile["format"] == inpf or \
82+
formatSubclassOf(afile["format"], inpf, ontology, set()):
8283
return
8384
raise validate.ValidationException(
84-
u"File has an incompatible format: %s" % json_dumps(af, indent=4))
85+
u"File has an incompatible format: {}".format(
86+
json_dumps(afile, indent=4)))
8587

8688
class Builder(object):
87-
def __init__(self): # type: () -> None
88-
self.names = None # type: schema.Names
89-
self.schemaDefs = None # type: Dict[Text, Dict[Text, Any]]
90-
self.files = None # type: List[Dict[Text, Text]]
91-
self.fs_access = None # type: StdFsAccess
92-
self.job = None # type: Dict[Text, Union[Dict[Text, Any], List, Text]]
93-
self.requirements = None # type: List[Dict[Text, Any]]
94-
self.hints = None # type: List[Dict[Text, Any]]
95-
self.outdir = None # type: Text
96-
self.tmpdir = None # type: Text
97-
self.resources = None # type: Dict[Text, Union[int, Text]]
98-
self.bindings = [] # type: List[Dict[Text, Any]]
99-
self.timeout = None # type: int
100-
self.pathmapper = None # type: PathMapper
101-
self.stagedir = None # type: Text
102-
self.make_fs_access = None # type: Type[StdFsAccess]
103-
self.debug = False # type: bool
104-
self.js_console = False # type: bool
105-
self.mutation_manager = None # type: MutationManager
106-
self.force_docker_pull = False # type: bool
107-
self.formatgraph = None # type: Graph
89+
def __init__(self,
90+
job, # type: Dict[Text, Union[Dict[Text, Any], List, Text]]
91+
files, # type: List[Dict[Text, Text]]
92+
bindings, # type: List[Dict[Text, Any]]
93+
schemaDefs, # type: Dict[Text, Dict[Text, Any]]
94+
names, # type: schema_salad.schema.Names
95+
requirements, # type: List[Dict[Text, Any]]
96+
hints, # type: List[Dict[Text, Any]]
97+
timeout, # type: float
98+
debug, # type: bool
99+
resources, # type: Dict[Text, Union[int, Text, None]]
100+
js_console, # type: bool
101+
mutation_manager, # type: MutationManager
102+
formatgraph, # type: Optional[Graph]
103+
make_fs_access, # type: Type[StdFsAccess]
104+
fs_access, # type: StdFsAccess
105+
force_docker_pull, # type: bool
106+
loadListing, # type: Text
107+
outdir, # type: Text
108+
tmpdir, # type: Text
109+
stagedir, # type: Text
110+
job_script_provider, # type: Optional[Any]
111+
): # type: (...) -> None
112+
self.names = names
113+
self.schemaDefs = schemaDefs
114+
self.files = files
115+
self.fs_access = fs_access
116+
self.job = job
117+
self.requirements = requirements
118+
self.hints = hints
119+
self.outdir = outdir
120+
self.tmpdir = tmpdir
121+
self.resources = resources
122+
self.bindings = bindings
123+
self.timeout = timeout
124+
self.pathmapper = None # type: Optional[PathMapper]
125+
self.stagedir = stagedir
126+
self.make_fs_access = make_fs_access
127+
self.debug = debug
128+
self.js_console = js_console
129+
self.mutation_manager = mutation_manager
130+
self.force_docker_pull = force_docker_pull
131+
self.formatgraph = formatgraph
108132

109133
# One of "no_listing", "shallow_listing", "deep_listing"
110-
# Will be default "no_listing" for CWL v1.1
111-
self.loadListing = "deep_listing" # type: Union[None, str]
134+
self.loadListing = loadListing
112135

113-
self.find_default_container = None # type: Callable[[], Text]
114-
self.job_script_provider = None # type: Any
136+
self.find_default_container = None # type: Optional[Callable[[], Text]]
137+
self.job_script_provider = job_script_provider
115138

116139
def build_job_script(self, commands):
117140
# type: (List[Text]) -> Text
@@ -121,14 +144,19 @@ def build_job_script(self, commands):
121144
else:
122145
return None
123146

124-
def bind_input(self, schema, datum, lead_pos=None, tail_pos=None, discover_secondaryFiles=False):
125-
# type: (Dict[Text, Any], Any, Union[int, List[int]], List[int], bool) -> List[Dict[Text, Any]]
147+
def bind_input(self,
148+
schema, # type: Dict[Text, Any]
149+
datum, # type: Any
150+
discover_secondaryFiles, # type: bool
151+
lead_pos=None, # type: Optional[Union[int, List[int]]]
152+
tail_pos=None, # type: Optional[List[int]]
153+
): # type: (...) -> List[Dict[Text, Any]]
126154
if tail_pos is None:
127155
tail_pos = []
128156
if lead_pos is None:
129157
lead_pos = []
130158
bindings = [] # type: List[Dict[Text,Text]]
131-
binding = None # type: Dict[Text,Any]
159+
binding = None # type: Optional[Dict[Text,Any]]
132160
value_from_expression = False
133161
if "inputBinding" in schema and isinstance(schema["inputBinding"], dict):
134162
binding = copy.copy(schema["inputBinding"])
@@ -146,7 +174,7 @@ def bind_input(self, schema, datum, lead_pos=None, tail_pos=None, discover_secon
146174
if isinstance(schema["type"], list):
147175
bound_input = False
148176
for t in schema["type"]:
149-
if isinstance(t, (str, Text)) and self.names.has_name(t, ""):
177+
if isinstance(t, string_types) and self.names.has_name(t, ""):
150178
avsc = self.names.get_name(t, "")
151179
elif isinstance(t, dict) and "name" in t and self.names.has_name(t["name"], ""):
152180
avsc = self.names.get_name(t["name"], "")
@@ -238,9 +266,12 @@ def bind_input(self, schema, datum, lead_pos=None, tail_pos=None, discover_secon
238266

239267
if "format" in schema:
240268
try:
241-
checkFormat(datum, self.do_eval(schema["format"]), self.formatgraph)
269+
check_format(datum, self.do_eval(schema["format"]),
270+
self.formatgraph)
242271
except validate.ValidationException as ve:
243-
raise WorkflowException("Expected value of '%s' to have format %s but\n %s" % (schema["name"], schema["format"], ve))
272+
raise WorkflowException(
273+
"Expected value of '%s' to have format %s but\n "
274+
" %s" % (schema["name"], schema["format"], ve))
244275

245276
def _capture_files(f):
246277
self.files.append(f)
@@ -269,28 +300,32 @@ def tostr(self, value): # type: (Any) -> Text
269300

270301
# Path adjust for windows file path when passing to docker, docker accepts unix like path only
271302
(docker_req, docker_is_req) = get_feature(self, "DockerRequirement")
272-
if onWindows() and docker_req is not None: # docker_req is none only when there is no dockerRequirement mentioned in hints and Requirement
273-
return docker_windows_path_adjust(value["path"])
303+
if onWindows() and docker_req is not None:
304+
# docker_req is none only when there is no dockerRequirement
305+
# mentioned in hints and Requirement
306+
path = docker_windows_path_adjust(value["path"])
307+
assert path is not None
308+
return path
274309
return value["path"]
275310
else:
276311
return Text(value)
277312

278-
def generate_arg(self, binding): # type: (Dict[Text,Any]) -> List[Text]
313+
def generate_arg(self, binding): # type: (Dict[Text, Any]) -> List[Text]
279314
value = binding.get("datum")
280315
if "valueFrom" in binding:
281316
with SourceLine(binding, "valueFrom", WorkflowException, _logger.isEnabledFor(logging.DEBUG)):
282317
value = self.do_eval(binding["valueFrom"], context=value)
283318

284-
prefix = binding.get("prefix")
319+
prefix = binding.get("prefix") # type: Optional[Text]
285320
sep = binding.get("separate", True)
286321
if prefix is None and not sep:
287322
with SourceLine(binding, "separate", WorkflowException, _logger.isEnabledFor(logging.DEBUG)):
288323
raise WorkflowException("'separate' option can not be specified without prefix")
289324

290-
l = [] # type: List[Dict[Text,Text]]
325+
argl = [] # type: List[Dict[Text,Text]]
291326
if isinstance(value, list):
292327
if binding.get("itemSeparator") and value:
293-
l = [binding["itemSeparator"].join([self.tostr(v) for v in value])]
328+
argl = [binding["itemSeparator"].join([self.tostr(v) for v in value])]
294329
elif binding.get("valueFrom"):
295330
value = [self.tostr(v) for v in value]
296331
return ([prefix] if prefix else []) + value
@@ -299,36 +334,41 @@ def generate_arg(self, binding): # type: (Dict[Text,Any]) -> List[Text]
299334
else:
300335
return []
301336
elif isinstance(value, dict) and value.get("class") in ("File", "Directory"):
302-
l = [value]
337+
argl = [value]
303338
elif isinstance(value, dict):
304339
return [prefix] if prefix else []
305340
elif value is True and prefix:
306341
return [prefix]
307342
elif value is False or value is None or (value is True and not prefix):
308343
return []
309344
else:
310-
l = [value]
345+
argl = [value]
311346

312347
args = []
313-
for j in l:
348+
for j in argl:
314349
if sep:
315350
args.extend([prefix, self.tostr(j)])
316351
else:
352+
assert prefix is not None
317353
args.append(prefix + self.tostr(j))
318354

319355
return [a for a in args if a is not None]
320356

321-
def do_eval(self, ex, context=None, pull_image=True, recursive=False, strip_whitespace=True):
322-
# type: (Union[Dict[Text, Text], Text], Any, bool, bool, bool) -> Any
357+
def do_eval(self, ex, context=None, recursive=False, strip_whitespace=True):
358+
# type: (Union[Dict[Text, Text], Text], Any, bool, bool) -> Any
323359
if recursive:
324360
if isinstance(ex, dict):
325-
return {k: self.do_eval(v, context, pull_image, recursive) for k, v in iteritems(ex)}
361+
return {k: self.do_eval(v, context, recursive)
362+
for k, v in iteritems(ex)}
326363
if isinstance(ex, list):
327-
return [self.do_eval(v, context, pull_image, recursive) for v in ex]
364+
return [self.do_eval(v, context, recursive)
365+
for v in ex]
366+
if context is None and isinstance(ex, string_types) and "self" in ex:
367+
return None
328368
return expression.do_eval(ex, self.job, self.requirements,
329369
self.outdir, self.tmpdir,
330370
self.resources,
331-
context=context, pull_image=pull_image,
371+
context=context,
332372
timeout=self.timeout,
333373
debug=self.debug,
334374
js_console=self.js_console,

0 commit comments

Comments
 (0)