Skip to content

Commit e769118

Browse files
committed
Review command-line handling framework toward consistency and extension.
- Implement a wrapper around ``click.option`` that handles global config consistently without having to deal with callbacks directly in ``planemo.options``. - Implement tracking of how options are set (command-line, global config, or default). - Replace command decorator ``pass_context`` with a new decorator that performs the context pass but provides an entry point for more sophisticated command-line hanlding in the future (will use for Galaxy profiles). - Improvements to docstrings. - Allow new options to be globally configured with defaults in ~/.planemo.yml including afew conda options. - Add aenum as a project dependency for tracking option sources.
1 parent fa7b743 commit e769118

39 files changed

+289
-184
lines changed

planemo/cli.py

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"""The module describes a CLI framework extending ``click``."""
12
import os
23
import sys
34
import traceback
@@ -21,36 +22,62 @@
2122

2223

2324
class Context(object):
25+
"""Describe context of Planemo computation.
26+
27+
Handles cross cutting concerns for Planemo such as verbose log
28+
tracking, the definition of the Planemo workspace (``~/.planemo``),
29+
and the global configuraton defined in ``~/.planemo.yml``.
30+
"""
2431

2532
def __init__(self):
33+
"""Construct a Context object using execution environment."""
2634
self.home = os.getcwd()
2735
self._global_config = None
2836
# Will be set by planemo CLI driver
2937
self.verbose = False
3038
self.planemo_config = None
3139
self.planemo_directory = None
40+
self.option_source = {}
41+
42+
def set_option_source(self, param_name, option_source):
43+
"""Specify how an option was set."""
44+
assert param_name not in self.option_source
45+
self.option_source[param_name] = option_source
46+
47+
def get_option_source(self, param_name):
48+
"""Return OptionSource value indicating how the option was set."""
49+
assert param_name not in self.option_source
50+
return self.option_source[param_name]
3251

3352
@property
3453
def global_config(self):
54+
"""Read Planemo's global configuration.
55+
56+
As defined most simply by ~/.planemo.yml.
57+
"""
3558
if self._global_config is None:
3659
self._global_config = read_global_config(self.planemo_config)
3760
return self._global_config
3861

3962
def log(self, msg, *args):
40-
"""Logs a message to stderr."""
63+
"""Log a message to stderr."""
4164
if args:
4265
msg %= args
4366
click.echo(msg, file=sys.stderr)
4467

4568
def vlog(self, msg, *args, **kwds):
46-
"""Logs a message to stderr only if verbose is enabled."""
69+
"""Log a message to stderr only if verbose is enabled."""
4770
if self.verbose:
4871
self.log(msg, *args)
4972
if kwds.get("exception", False):
5073
traceback.print_exc(file=sys.stderr)
5174

5275
@property
5376
def workspace(self):
77+
"""Create and return Planemo's workspace.
78+
79+
By default this will be ``~/.planemo``.
80+
"""
5481
if not self.planemo_directory:
5582
raise Exception("No planemo workspace defined.")
5683
workspace = self.planemo_directory
@@ -69,6 +96,7 @@ def workspace(self):
6996

7097

7198
def list_cmds():
99+
"""List planemo commands from commands folder."""
72100
rv = []
73101
for filename in os.listdir(cmd_folder):
74102
if filename.endswith('.py') and \
@@ -81,7 +109,7 @@ def list_cmds():
81109
return rv
82110

83111

84-
def name_to_command(name):
112+
def _name_to_command(name):
85113
try:
86114
if sys.version_info[0] == 2:
87115
name = name.encode('ascii', 'replace')
@@ -101,7 +129,16 @@ def list_commands(self, ctx):
101129
def get_command(self, ctx, name):
102130
if name in COMMAND_ALIASES:
103131
name = COMMAND_ALIASES[name]
104-
return name_to_command(name)
132+
return _name_to_command(name)
133+
134+
135+
def command_function(f):
136+
"""Extension point for processing kwds after click callbacks."""
137+
def outer(*args, **kwargs):
138+
# arg_spec = inspect.getargspec(f)
139+
return f(*args, **kwargs)
140+
outer.__doc__ = f.__doc__
141+
return pass_context(outer)
105142

106143

107144
@click.command(cls=PlanemoCLI, context_settings=CONTEXT_SETTINGS)
@@ -117,7 +154,7 @@ def get_command(self, ctx, name):
117154
envvar="PLANEMO_GLOBAL_WORKSPACE",
118155
help="Workspace for planemo.")
119156
@pass_context
120-
def planemo(ctx, config, directory, verbose): # noqa
157+
def planemo(ctx, config, directory, verbose):
121158
"""A command-line toolkit for building tools and workflows for Galaxy.
122159
123160
Check out the full documentation for Planemo online
@@ -129,5 +166,8 @@ def planemo(ctx, config, directory, verbose): # noqa
129166

130167

131168
__all__ = [
169+
"Context",
170+
"list_cmds",
171+
"pass_context",
132172
"planemo",
133173
]

planemo/commands/cmd_brew.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Module describing the planemo ``brew`` command."""
22
import click
33

4-
from planemo.cli import pass_context
4+
from planemo.cli import command_function
55
from planemo import options
66

77
from galaxy.tools.loader_directory import load_tool_elements_from_path
@@ -15,7 +15,7 @@
1515
@click.command('brew')
1616
@options.optional_tools_arg()
1717
@options.brew_option()
18-
@pass_context
18+
@command_function
1919
def cli(ctx, path, brew=None):
2020
"""Install tool requirements using brew. (**Experimental**)
2121

planemo/commands/cmd_brew_env.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import click
44
import os
55

6-
from planemo.cli import pass_context
6+
from planemo.cli import command_function
77
from planemo import options
88
from planemo.io import ps1_for_path
99

@@ -22,7 +22,7 @@
2222
"--shell",
2323
is_flag=True
2424
)
25-
@pass_context
25+
@command_function
2626
def cli(ctx, path, brew=None, skip_install=False, shell=None):
2727
"""Display commands used to modify environment to inject tool's brew
2828
dependencies.::

planemo/commands/cmd_brew_init.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
import urllib
55
from tempfile import mkstemp
66

7-
from planemo.cli import pass_context
7+
from planemo.cli import command_function
88
from planemo.io import shell
99

1010

1111
INSTALL_SCRIPT = "https://raw.github.com/Homebrew/linuxbrew/go/install"
1212

1313

1414
@click.command('brew_init')
15-
@pass_context
15+
@command_function
1616
def cli(ctx):
1717
"""Download linuxbrew install and run it with ruby. Linuxbrew is a fork
1818
of Homebrew (http://brew.sh/linuxbrew/).

planemo/commands/cmd_conda_env.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from __future__ import print_function
33
import click
44

5-
from planemo.cli import pass_context
5+
from planemo.cli import command_function
66
from planemo import options
77

88
from planemo.io import ps1_for_path
@@ -34,7 +34,7 @@
3434
@options.optional_tools_arg()
3535
@options.conda_target_options()
3636
# @options.skip_install_option() # TODO
37-
@pass_context
37+
@command_function
3838
def cli(ctx, path, **kwds):
3939
"""Source output to activate a conda environment for this tool.
4040

planemo/commands/cmd_conda_init.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Module describing the planemo ``conda_init`` command."""
22
import click
33

4-
from planemo.cli import pass_context
4+
from planemo.cli import command_function
55
from planemo import options
66
from planemo.conda import build_conda_context
77

@@ -10,7 +10,7 @@
1010

1111
@click.command('conda_init')
1212
@options.conda_target_options()
13-
@pass_context
13+
@command_function
1414
def cli(ctx, **kwds):
1515
"""Download and install conda.
1616

planemo/commands/cmd_conda_install.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Module describing the planemo ``conda_install`` command."""
22
import click
33

4-
from planemo.cli import pass_context
4+
from planemo.cli import command_function
55
from planemo.io import coalesce_return_codes
66
from planemo import options
77

@@ -13,10 +13,9 @@
1313
@click.command('conda_install')
1414
@options.optional_tools_arg()
1515
@options.conda_target_options()
16-
@pass_context
16+
@command_function
1717
def cli(ctx, path, **kwds):
18-
"""Install conda packages for tool requirements.
19-
"""
18+
"""Install conda packages for tool requirements."""
2019
conda_context = build_conda_context(**kwds)
2120
return_codes = []
2221
for conda_target in collect_conda_targets(path):

planemo/commands/cmd_create_gist.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Module describing the planemo ``create_gist`` command."""
22
import click
33

4-
from planemo.cli import pass_context
4+
from planemo.cli import command_function
55
from planemo.io import info
66
from planemo import github_util
77

@@ -24,7 +24,7 @@
2424
default="raw",
2525
help=("Link type to generate for the file.")
2626
)
27-
@pass_context
27+
@command_function
2828
def cli(ctx, path, **kwds):
2929
"""Download a tool repository as a tarball from the tool shed and extract
3030
to the specified directory.

planemo/commands/cmd_cwl_run.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Module describing the planemo ``cwl_run`` command."""
22
import click
3-
from planemo.cli import pass_context
3+
from planemo.cli import command_function
44
from planemo import options
55
from planemo import cwl
66

@@ -19,7 +19,7 @@
1919
"but the CWL reference implementation cwltool and be selected "
2020
"also.")
2121
)
22-
@pass_context
22+
@command_function
2323
def cli(ctx, path, job_path, **kwds):
2424
"""Planemo command for running CWL tools and jobs.
2525

planemo/commands/cmd_dependency_script.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from xml.etree import ElementTree as ET
88

99
from planemo.io import info, error
10-
from planemo.cli import pass_context
10+
from planemo.cli import command_function
1111
from planemo import options
1212

1313
from planemo.shed2tap.base import BasePackage, Dependency
@@ -161,7 +161,7 @@ def process_tool_dependencies_xml(tool_dep, install_handle, env_sh_handle):
161161
@click.command('dependency_script')
162162
@options.shed_realization_options()
163163
@options.dependencies_script_options()
164-
@pass_context
164+
@command_function
165165
def cli(ctx, paths, recursive=False, fail_fast=True, download_cache=None):
166166
"""Prepare a bash shell script to install tool requirements (**Experimental**)
167167

planemo/commands/cmd_docker_build.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Module describing the planemo ``docker_build`` command."""
22
import click
3-
from planemo.cli import pass_context
3+
from planemo.cli import command_function
44
from planemo import options
55
from planemo.io import error
66

@@ -15,9 +15,9 @@
1515
@options.docker_sudo_option()
1616
@options.docker_sudo_cmd_option()
1717
@options.docker_host_option()
18-
@pass_context
18+
@command_function
1919
def cli(ctx, path=".", dockerfile=None, **kwds):
20-
"""Builds (and optionally caches Docker images) for tool Dockerfiles.
20+
"""Build (and optionally cache Docker images) for tool Dockerfiles.
2121
2222
Loads the tool or tools referenced by ``TOOL_PATH`` (by default all tools
2323
in current directory), and ensures they all reference the same Docker image

planemo/commands/cmd_docker_shell.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from __future__ import print_function
1414
import click
1515
import os
16-
from planemo.cli import pass_context
16+
from planemo.cli import command_function
1717
from planemo import options
1818

1919
from galaxy.tools.loader import load_tool
@@ -40,7 +40,7 @@
4040
@options.docker_sudo_option()
4141
@options.docker_sudo_cmd_option()
4242
@options.docker_host_option()
43-
@pass_context
43+
@command_function
4444
def cli(ctx, path, **kwds):
4545
"""Launch a shell in the Docker container referenced by the specified
4646
tool. Prints a command to do this the way Galaxy would in job files it

planemo/commands/cmd_docs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
"""Module describing the planemo ``docs`` command."""
22
import click
33

4-
from planemo.cli import pass_context
4+
from planemo.cli import command_function
55

66
SYNTAX_URL = "http://planemo.readthedocs.org/en/latest/"
77

88

99
@click.command("syntax")
10-
@pass_context
10+
@command_function
1111
def cli(ctx, **kwds):
1212
"""Open the Planemo documentation in a web browser."""
1313
click.launch(SYNTAX_URL)

planemo/commands/cmd_lint.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import click
55

6-
from planemo.cli import pass_context
6+
from planemo.cli import command_function
77
from planemo import options
88

99
from planemo.tool_lint import build_lint_args
@@ -30,7 +30,7 @@
3030
# help="If an sha256sum is available, download the entire file AND validate it.",
3131
# default=False,
3232
# )
33-
@pass_context
33+
@command_function
3434
def cli(ctx, paths, **kwds):
3535
"""Check specified tool(s) for common errors and adherence to best
3636
practices.

planemo/commands/cmd_normalize.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import click
55

6-
from planemo.cli import pass_context
6+
from planemo.cli import command_function
77
from planemo import options
88

99
from galaxy.tools.loader import (
@@ -35,7 +35,7 @@
3535
"best practices as part of this command, this flag will disable "
3636
"that behavior.")
3737
)
38-
@pass_context
38+
@command_function
3939
def cli(ctx, path, expand_macros=False, **kwds):
4040
"""Generate normalized tool XML from input (breaks formatting).
4141

planemo/commands/cmd_project_init.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import click
77

8-
from planemo.cli import pass_context
8+
from planemo.cli import command_function
99
from planemo import options
1010
from planemo.io import (
1111
warn,
@@ -25,7 +25,7 @@
2525
'--template',
2626
default=None
2727
)
28-
@pass_context
28+
@command_function
2929
def cli(ctx, path, template=None, **kwds):
3030
"""Initialize a new tool project (demo only right now).
3131
"""

0 commit comments

Comments
 (0)