-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] making compute CLI command usable inside Python #245
Conversation
sourmash_lib/__main__.py
Outdated
commands[cmd].add_args(subp) | ||
subp.set_defaults(cmd=commands[cmd]) | ||
|
||
args = parser.parse_args() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
subparsers
for all commands must be added before calling this. That's why I'm going for some sort of command.add_args
function for each command.
sourmash_lib/__main__.py
Outdated
cmd = args.cmd | ||
args_dict = vars(args) | ||
args_dict.pop('cmd') | ||
cmd(**args_dict) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dirty trick with vars
to convert the args
(a Namespace
object?) to a dictionary that can be expanded for the function call.
sourmash_lib/commands.py
Outdated
def compute(filenames=None, check_sequence=False, ksizes=(21,), dna=True, singleton=False, | ||
email='', scaled=10000, force=False, output=None, num_hashes=500, protein=False, | ||
name_from_first=False, seed=42, input_is_protein=False, merge=None, | ||
track_abundance=False): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possible issue: keeping defaults here and in the argparse code the same. Probably leave the add_args
function before the command definition, so at least they are close to each other...
click uses decorators for this:
import click
@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
help='The person to greet.')
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for x in range(count):
click.echo('Hello %s!' % name)
if __name__ == '__main__':
hello()
sourmash_lib/commands.py
Outdated
notify('WARNING: input is protein, turning off DNA hashing') | ||
args.dna = False | ||
args.protein = True | ||
dna = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of changes in this diff is just removing args.
from the arguments...
sourmash_lib/commands.py
Outdated
help='set resolution of signature size') | ||
parser.add_argument('--seed', type=int, help='hash seed', | ||
default=sourmash_lib.DEFAULT_SEED) | ||
compute.add_args = compute_add_args |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dirty, horrible hack. Sorry. Will think about something better than this later 😄
I talked with @standage yesterday and he has another approach for kevlar, where you build a list of CLI-like arguments and then parse with argparse, passing the resulting |
I talked with @betatim today, and he has a similar setup for |
What's old is new! https://github.com/ctb/twill/blob/master/twill/commands.py |
68374f5
to
c7a79ef
Compare
1420a8a
to
579de0a
Compare
6693797
to
dae9ea9
Compare
d8cbe5e
to
c265796
Compare
I was talking with @taylorreiter and it seems like this is a good PR to start going more into sourmash code, so I updated it to the current master. After this PR was created there were code changes (for |
sourmash/__main__.py
Outdated
else: | ||
cmd = commands.get(args.command) | ||
return cmd(sys.argv[2:]) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once all commands are converted we can remove this if
block and keep only the first part (the else
is the current implementation for subcommands)
sourmash/__main__.py
Outdated
from sourmash_utils.__main__ import main as utils_main | ||
UTILS_AVAILABLE = True | ||
except ImportError: | ||
UTILS_AVAILABLE = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is here because the original PR was against #236 and I changed it to be against master to have Travis run the tests...
I updated in this PR in #722, which resolves the conflicts. Still have some kind of bug in there but it's safe to merge. |
553f734
to
d5cda3d
Compare
d5cda3d
to
b768a31
Compare
Codecov Report
@@ Coverage Diff @@
## master #245 +/- ##
=========================================
Coverage ? 89.47%
=========================================
Files ? 29
Lines ? 4570
Branches ? 46
=========================================
Hits ? 4089
Misses ? 478
Partials ? 3
Continue to review full report at Codecov.
|
b768a31
to
da231dd
Compare
@ctb can you take a look at this commit: 193748c This is adding a |
I'm also starting to feel like this should move from |
5449e14
to
1a5b068
Compare
prepare compute test command fix default ksize parsing failing test: if output is a string or path, should we open it as a file?
1a5b068
to
8766e95
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like everything about this, except for the final command "path" of sourmash.command_compute.compute
, which seems ...long. No immediate solution (and happy to merge this!) but let's think about how to simplify that!
yeah, the What I've been doing in the Rust crate is creating submodules over time. Example: at the moment the
On the Python side, it would translate to Currently:
Proposed (like what
This way we can start with a massive implementation file (like Before merging this, I think it is good to have some guidelines on how to proceed for all commands. From the top of my head:
|
@luizirber has thus declared: "#853 should fix the commands-taking-output problem mentioned above." yay w00t! |
Still think this is useful, especially considering pyodide #2433 and using it in a browser repl, but lots to update... And so I'm closing it. |
I'm opening this PR for requesting comments about how to properly support calling CLI commands from the Python API, without being too verbose.
This is similar to the design I'm going for in sourmash-utils.(Old PR comments, now this is adapted to #785 ideas)I'll add inline comments with details, but the rationale is:
We focus on exposing the CLI, but if I want to calculate a signature using the Python API there is quite a bit of boilerplate involved:
what I want to do:
(or something similar).
The idea of this PoC is to make arguments explicit in the function signature. The CLI exposes commands and options from subparsers, and function arguments are 'fake-positional': only use kwargs, but leave the required ones (like
filenames
in thecompute(filenames)
example) in the beginning to avoid having to define explicitly all the keywords.Checklist
make test
Did it pass the tests?make coverage
Is the new code covered?without a major version increment. Changing file formats also requires a
major version number increment.
changes were made?