Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name:

on: [push]
on: [push, pull_request]

jobs:
test_conda:
Expand Down
1 change: 1 addition & 0 deletions acclimatise/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from acclimatise.converter import WrapperGenerator
from acclimatise.converter.cwl import CwlGenerator
from acclimatise.converter.galaxy import GalaxyGenerator
from acclimatise.converter.wdl import WdlGenerator
from acclimatise.converter.yml import YmlGenerator
from acclimatise.execution import execute_cmd
Expand Down
6 changes: 3 additions & 3 deletions acclimatise/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ def main():
"--format",
"-f",
"formats",
type=click.Choice(["wdl", "cwl", "yml"]),
type=click.Choice(["wdl", "cwl", "yml", "galaxy"]),
multiple=True,
default=("yml", "wdl", "cwl"),
default=("yml", "wdl", "cwl", "galaxy"),
help="The language in which to output the CLI wrapper",
)
@click.option(
Expand Down Expand Up @@ -123,7 +123,7 @@ def explore(
@click.option(
"--format",
"-f",
type=click.Choice(["wdl", "cwl", "yml"]),
type=click.Choice(["wdl", "cwl", "yml", "galaxy"]),
default="cwl",
help="The language in which to output the CLI wrapper",
)
Expand Down
2 changes: 1 addition & 1 deletion acclimatise/converter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def choose_converter(cls, typ) -> Type["WrapperGenerator"]:
if subclass.format() == typ:
return subclass

raise Exception("Unknown format type")
raise Exception("Unknown format type %s" % typ)

@classmethod
@abstractmethod
Expand Down
112 changes: 106 additions & 6 deletions acclimatise/converter/galaxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@

from dataclasses import dataclass

import galaxyxml.tool as gxt
import galaxyxml.tool.parameters as gxtp
from acclimatise import cli_types
from acclimatise.converter import NamedArgument, WrapperGenerator
from acclimatise.model import CliArgument, Command, Flag, Positional
from acclimatise.yaml import yaml
from galaxy.tool_util.lint import LEVEL_ALL, LEVEL_ERROR, LEVEL_WARN, lint_tool_source
from galaxy.tool_util.parser import get_tool_source


@dataclass
Expand All @@ -25,14 +29,110 @@ def format(cls) -> str:
def suffix(self) -> str:
return ".xml"

@staticmethod
def to_gxy_class(typ: cli_types.CliType):
if isinstance(typ, cli_types.CliFile):
return gxtp.DataParam
elif isinstance(typ, cli_types.CliDir):
return gxtp.DataParam # can make composite datatype
elif isinstance(typ, cli_types.CliString):
return gxtp.TextParam
elif isinstance(typ, cli_types.CliFloat):
return gxtp.FloatParam
elif isinstance(typ, cli_types.CliInteger):
return gxtp.IntegerParam
elif isinstance(typ, cli_types.CliBoolean):
return gxtp.BooleanParam
# elif isinstance(typ, cli_types.CliEnum):
# return gxtp.BooleanParam
# elif isinstance(typ, cli_types.CliList):
# return CwlGenerator.to_cwl_type(typ.value) + "[]"
# elif isinstance(typ, cli_types.CliTuple):
# return [CwlGenerator.to_cwl_type(subtype) for subtype in set(typ.values)]
else:
raise Exception(f"Invalid type {typ}!")

def save_to_string(self, cmd: Command) -> str:
# Todo
pass
# Some current limits?:
# No package name information
# No version information
# No outputs

inputs: List[CliArgument] = [*cmd.named] + (
[] if self.ignore_positionals else [*cmd.positional]
)
names = self.choose_variable_names(inputs)

tool_name = cmd.as_filename
tool_id = cmd.as_filename
tool_version = "0.0.1"
tool_description = ""
tool_executable = " ".join(cmd.command)
version_command = "%s %s" % (tool_executable, cmd.version_flag.full_name())
tool = gxt.Tool(
tool_name,
tool_id,
tool_version,
tool_description,
tool_executable,
hidden=False,
tool_type=None,
URL_method=None,
workflow_compatible=True,
interpreter=None,
version_command=version_command,
)

tool.inputs = gxtp.Inputs()
tool.outputs = gxtp.Outputs()
tool.help = self._format_help(cmd.help_text)

def save_to_file(self, cmd: Command, path: Path) -> None:
# Todo
pass
tool.tests = gxtp.Tests() # ToDo: add tests
tool.citations = gxtp.Citations() # ToDo: add citations

# Add requirements
requirements = gxtp.Requirements()
requirements.append(gxtp.Requirement("package", tool_executable, version=None))
tool.requirements = requirements

for arg in names:
assert arg.name != "", arg
param_cls = self.to_gxy_class(arg.arg.get_type())
# not yet handled:
# default values?
# ints & floats: min, max
param = param_cls(
arg.name,
label=arg.arg.description,
positional=isinstance(arg.arg, Positional),
help=arg.arg.description,
value=None,
num_dashes=len(arg.arg.longest_synonym)
- len(arg.arg.longest_synonym.lstrip("-")),
optional=arg.arg.optional,
)
# output or input?
tool.inputs.append(param)
return tool.export()

@classmethod
def validate(cls, wrapper: str, cmd: Command = None, explore=True):
pass
# ToDo: Tests? What level to validate?
# Is wrapper assumed to be generated here, or should we also compare to result of output of save_to_string (as if wrapper was being generated externally)
# Raise value error if validation fails
with tempfile.NamedTemporaryFile(mode="w+", suffix=".xml") as fh:
fh.write(wrapper)
fh.flush()
tool_source = get_tool_source(config_file=fh.name)
if not lint_tool_source(
tool_source, level=LEVEL_ALL, fail_level=LEVEL_WARN
):
raise ValueError("Linting Failed")
return True

def _format_help(self, help_text):
# Just cheat and make it a huge block quote
rval = "::\n"
for line in help_text.split("\n"):
rval = "%s\n %s" % (rval, line.rstrip())
return "%s\n\n" % (rval)
7 changes: 7 additions & 0 deletions acclimatise/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ def as_filename(self) -> str:
"""
return "_".join(self.command).replace("-", "_")

@property
def empty(self) -> bool:
"""
True if we think this command failed in parsing, ie it has no arguments
"""
return (len(self.positional) + len(self.named) + len(self.subcommands)) == 0

@property
def depth(self) -> int:
"""
Expand Down
2 changes: 1 addition & 1 deletion acclimatise/usage_parser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def normalise_cline(tokens):
return [Path(el.lower()).stem for el in tokens]


def parse_usage(cmd, text, debug=False):
def parse_usage(cmd, text, debug=False) -> Command:
toks = usage.setDebug(debug).searchString(text)
if not toks:
# If we had no results, return an empty command
Expand Down
6 changes: 4 additions & 2 deletions acclimatise/usage_parser/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,10 @@ def visit_usage(s, loc, toks):
return toks[0][0]


usage = Regex("usage:", flags=re.IGNORECASE).suppress() + OneOrMore(
usage_element, stopOn=LineEnd()
usage = (
LineStart()
+ Regex("usage:", flags=re.IGNORECASE).suppress()
+ OneOrMore(usage_element, stopOn=LineEnd())
) # .setParseAction(visit_usage).setDebug()


Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"pyparsing",
"jinja2",
"spacy",
"cwlgen",
"miniwdl",
"wordsegment",
"inflection",
Expand All @@ -21,6 +20,8 @@
"word2number",
"psutil",
"dataclasses",
"galaxyxml",
"galaxy-tool-util",
],
python_requires=">=3.6",
entry_points={"console_scripts": ["acclimatise = acclimatise.cli:main"]},
Expand Down
8 changes: 8 additions & 0 deletions test/usage/test_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,11 @@ def test_samtools_dict():
"""
command = parse_usage(["samtools", "dict"], text, debug=True)
assert len(command.positional) == 1


def test_mid_line_usage():
text = """
Can't open --usage: No such file or directory at /usr/bin/samtools.pl line 50.
"""
command = parse_usage(["samtools.pl", "showALEN"], text, debug=True)
assert command.empty