def call_as(self, items, meta): return D.Call( sp(self.filename, meta), items[0], items[1].value, items[2] if len(items) > 2 else dict(), )
def document(self, items, meta): imports = [] structs = {} tasks = [] workflow = None for item in items: if isinstance(item, D.Task): tasks.append(item) elif isinstance(item, D.Workflow): if workflow is not None: raise Err.MultipleDefinitions( sp(self.filename, meta), "Document has multiple workflows") workflow = item elif isinstance(item, D.StructTypeDef): if item.name in structs: raise Err.MultipleDefinitions( sp(self.filename, meta), "multiple structs named " + item.name) structs[item.name] = item elif isinstance(item, lark.Tree) and item.data == "version": pass elif isinstance(item, D.DocImport): imports.append(item) else: assert False return D.Document(sp(self.filename, meta), imports, structs, tasks, workflow)
def task(self, items, meta): d = {} for item in items: if isinstance(item, dict): for k, v in item.items(): if k in d: raise Err.MultipleDefinitions( sp(self.filename, meta), "redundant sections in task" ) d[k] = v else: assert isinstance(item, str) assert "name" not in d d["name"] = item.value return D.Task( sp(self.filename, meta), d["name"], d.get("inputs", []), d.get("decls", []), d["command"], d.get("outputs", []), d.get("parameter_meta", {}), d.get("runtime", {}), d.get("meta", {}), )
def decl(self, items, meta): return D.Decl( sp(self.filename, meta), items[0], items[1].value, (items[2] if len(items) > 2 else None), )
def parse_wdl(wdl_path: Path, import_dirs: Optional[Sequence[Path]] = (), check_quant: bool = False, **_) -> Document: return Tree.load(str(wdl_path), path=[str(path) for path in import_dirs], check_quant=check_quant)
def parse_document( txt: str, version: Optional[str] = None, uri: str = "", imported: bool = False ) -> D.Document: if version is None: # for now assume the version is 1.0 if the first line is "version <number>" # otherwise draft-2 version = "draft-2" for line in txt.split("\n"): line = line.strip() if line and line[0] != "#": if line.startswith("version ") and line[8].isdigit(): version = "1.0" break if not txt.strip(): return D.Document( SourcePosition(filename=uri, line=0, column=0, end_line=0, end_column=0), [], [], None, imported, ) try: return _DocTransformer(uri, imported).transform(parse(txt, "document", version)) except lark.exceptions.UnexpectedCharacters as exn: raise Err.ParserError(uri if uri != "" else "(in buffer)") from exn except lark.exceptions.UnexpectedToken as exn: raise Err.ParserError(uri if uri != "" else "(in buffer)") from exn
def task(self, items, meta): d = {"noninput_decls": []} for item in items: if isinstance(item, dict): for k, v in item.items(): if k == "noninput_decl": d["noninput_decls"].append(v) elif k in d: raise Err.MultipleDefinitions( sp(self.filename, meta), "redundant sections in task") else: d[k] = v else: assert isinstance(item, str) assert "name" not in d d["name"] = item.value _check_keyword(sp(self.filename, meta), d["name"]) return D.Task( sp(self.filename, meta), d["name"], d.get("inputs", None), d["noninput_decls"], d["command"], d.get("outputs", []), d.get("parameter_meta", {}), d.get("runtime", {}), d.get("meta", {}), )
def load(uri: str, path: List[str] = [], check_quant: bool = True) -> Document: """ Parse a WDL document given filename/URI, recursively descend into imported documents, then typecheck the tasks and workflow. :param path: local filesystem directories to search for imports, in addition to the current working directory :param check_quant: set to ``False`` to relax static typechecking of the optional (?) and nonempty (+) type quantifiers. This is discouraged, but may be useful for older WDL workflows which assume less-rigorous static validation of these annotations. """ return Tree.load(uri, path, check_quant)
def _get_workflow_name(self, wdl_path: Path, kwargs: dict): if "workflow_name" in kwargs: return kwargs["workflow_name"] elif Tree: if "check_quant" not in kwargs: kwargs["check_quant"] = False doc = Tree.load(str(wdl_path), path=[str(path) for path in self.import_dirs], **kwargs) return doc.workflow.name else: # TODO: test this return safe_string(wdl_path.stem)
def struct(self, items, meta): assert len(items) >= 1 name = items[0] _check_keyword(sp(self.filename, meta), name) members = {} for d in items[1:]: assert not d.expr if d.name in members: raise Err.MultipleDefinitions(sp(self.filename, meta), "duplicate members in struct") members[d.name] = d.type return D.StructTypeDef(sp(self.filename, meta), name, members)
def workflow(self, items, meta): elements = [] inputs = None outputs = None output_idents = None output_idents_pos = None parameter_meta = None meta_section = None for item in items[1:]: if isinstance(item, dict): if "inputs" in item: assert inputs is None inputs = item["inputs"] elif "outputs" in item: if outputs is not None: raise Err.MultipleDefinitions( sp(self.filename, meta), "redundant sections in workflow") outputs = item["outputs"] if "output_idents" in item: assert output_idents is None output_idents = item["output_idents"] output_idents_pos = item["pos"] elif "meta" in item: if meta_section is not None: raise Err.MultipleDefinitions( sp(self.filename, meta), "redundant sections in workflow") meta_section = item["meta"] elif "parameter_meta" in item: if parameter_meta is not None: raise Err.MultipleDefinitions( sp(self.filename, meta), "redundant sections in workflow") parameter_meta = item["parameter_meta"] else: assert False elif isinstance(item, (D.Call, D.Conditional, D.Decl, D.Scatter)): elements.append(item) else: assert False _check_keyword(sp(self.filename, meta), items[0].value) return D.Workflow( sp(self.filename, meta), items[0].value, inputs, elements, outputs, parameter_meta or dict(), meta_section or dict(), output_idents, output_idents_pos, )
def import_doc(self, items, meta): uri = items[0] if len(items) > 1 and isinstance(items[1], str): namespace = items[1].value else: namespace = uri try: namespace = namespace[namespace.rindex("/") + 1:] except ValueError: pass if namespace.endswith(".wdl"): namespace = namespace[:-4] _check_keyword(sp(self.filename, meta), namespace) aliases = [p for p in items[1:] if isinstance(p, tuple)] return D.DocImport(pos=sp(self.filename, meta), uri=uri, namespace=namespace, aliases=aliases, doc=None)
def document(self, items, meta): imports = [] tasks = [] workflow = None for item in items: if isinstance(item, D.Task): tasks.append(item) elif isinstance(item, D.Workflow): if workflow is not None: raise Err.MultipleDefinitions( sp(self.filename, meta), "Document has multiple workflows" ) workflow = item elif isinstance(item, lark.Tree) and item.data == "version": pass elif isinstance(item, dict) and "import" in item: imports.append(item["import"]) else: assert False return D.Document(sp(self.filename, meta), imports, tasks, workflow, self.imported)
def parse_document(txt: str, version: Optional[str] = None, uri: str = "") -> D.Document: if version is None: # for now assume the version is 1.0 if the first line is "version <number>" # otherwise draft-2 version = "draft-2" for line in txt.split("\n"): line = line.strip() if line and line[0] != "#": if line.startswith("version ") and line[8].isdigit(): version = "1.0" break if not txt.strip(): return D.Document( SourcePosition(filename=uri, line=0, column=0, end_line=0, end_column=0), [], {}, [], None, ) try: return _DocTransformer(uri).transform(parse(txt, "document", version)) except lark.exceptions.UnexpectedInput as exn: pos = SourcePosition( filename=(uri if uri != "" else "(buffer)"), line=getattr(exn, "line", "?"), column=getattr(exn, "column", "?"), end_line=getattr(exn, "line", "?"), end_column=getattr(exn, "column", "?"), ) raise Err.SyntaxError(pos, str(exn)) from None except lark.exceptions.VisitError as exn: raise exn.__context__
def scatter(self, items, meta): return D.Scatter(sp(self.filename, meta), items[0].value, items[1], items[2:])
def conditional(self, items, meta): return D.Conditional(sp(self.filename, meta), items[0], items[1:])
def scatter(self, items, meta): _check_keyword(sp(self.filename, meta), items[0].value) return D.Scatter(sp(self.filename, meta), items[0].value, items[1], items[2:])
def call(self, items, meta): return D.Call(sp(self.filename, meta), items[0], None, items[1] if len(items) > 1 else dict())