Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
70da5df
Rewrite __main__/cli so argparse uses sub-parsers
NickleDave Sep 12, 2025
71c1a2c
Add 'template' TOML configs in src/vak/config
NickleDave Sep 13, 2025
8114a68
Fix how we call cli.cli in __main__
NickleDave Sep 13, 2025
b999985
Add cli command 'configfile' in vak.cli.cli
NickleDave Sep 13, 2025
ff95d4a
Add src/vak/config/generate.py
NickleDave Sep 13, 2025
fa3f333
Revise module-level docstring in src/vak/config/load.py
NickleDave Sep 13, 2025
427b75d
Fix link in a comment in src/vak/cli/prep.py
NickleDave Sep 13, 2025
85aaeac
Move/add files -> src/vak/config/_toml_config_templates
NickleDave Sep 13, 2025
e734c66
Fixup vak.config.generate
NickleDave Sep 14, 2025
eb7ca31
Import generate function in vak.config.__init__.py
NickleDave Sep 14, 2025
203d7f3
Add tests/fixture/parser.py with get_parser fixture
NickleDave Sep 17, 2025
12b31f3
Add tests/test_cli/test_cli.py with unit tests moved from tests/test_…
NickleDave Sep 17, 2025
30d791e
Have __main__ call parser.print_help() if no args are passed to cli
NickleDave Sep 20, 2025
a117d0e
Set required=True when we add_subparsers in cli.get_parser, and add t…
NickleDave Sep 20, 2025
b6fef39
Add smoke test for vak.cli.cli.get_parser
NickleDave Sep 20, 2025
5fffe81
Rewrite __main__.main to take `args_list` for clarity
NickleDave Sep 21, 2025
d46931a
In vak/cli/cli.py, fix args attribute name config_file -> configfile,…
NickleDave Sep 21, 2025
cdd079c
Rewrite unit tests in test___main__.py: test main with arg_list, and …
NickleDave Sep 21, 2025
0e0ef5d
WIP: Rewrite unit tests in tests/test_cli/test_cli.py
NickleDave Sep 21, 2025
1482ad3
Finish re-writing unit tests in tests/test_cli/test_cli.py
NickleDave Sep 22, 2025
d77ae28
Import generate *module* not function in src/vak/config/__init__.py
NickleDave Sep 22, 2025
0b314ba
Remove print statements from vak.cli.cli.configfile helper function
NickleDave Sep 22, 2025
1b14a34
Fix how we get command names to validate in config.validators.are_tab…
NickleDave Sep 22, 2025
1ee8cd1
Fix how we set default for dst in vak.config.generate
NickleDave Sep 22, 2025
6963d69
Remove unit test in tests/test_cli/test_cli.py that was too tightly c…
NickleDave Sep 22, 2025
f2fbf8d
WIP: Add tests/test_config/test_generate.py
NickleDave Sep 22, 2025
ec78ccc
WIP: add test_configfile_command to tests/test__main__.py
NickleDave Sep 24, 2025
9658c0f
Remove prep section from a 'template' configfile that's not supposed …
NickleDave Sep 25, 2025
0836858
Finish writing unit tests in tests/test_config/test_generate.py
NickleDave Sep 25, 2025
11fc46e
Raise FileExistsError in vak.config.generate, not ValueError, if dst …
NickleDave Sep 25, 2025
524d965
Validate extension of `dst` in `vak.config.generate.generate` -- make…
NickleDave Sep 25, 2025
1d1eddb
Add unit test to test that vak.config.generate.generate raises ValueE…
NickleDave Sep 25, 2025
852116c
Add examples section to docstring of vak.config.generate.generate
NickleDave Sep 25, 2025
de1ebfd
Rename -> vak/config/_generate.py
NickleDave Sep 25, 2025
5248c36
Import generate function from _generate module in vak/config/__init__.py
NickleDave Sep 25, 2025
393ef21
Rewrite examples in vak.config._generate.generate docstring to use sh…
NickleDave Sep 25, 2025
af35c1a
Fix name -> vak.config.generate in test_config/test_generate.py
NickleDave Sep 25, 2025
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
46 changes: 13 additions & 33 deletions src/vak/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,31 @@
Invokes __main__ when the module is run as a script.
Example: python -m vak --help
"""

import argparse
from pathlib import Path
import sys

from .cli import cli


def get_parser():
"""returns ArgumentParser instance used by main()"""
parser = argparse.ArgumentParser(
prog="vak",
description="vak command-line interface",
formatter_class=argparse.RawTextHelpFormatter,
)
parser.add_argument(
"command",
type=str,
metavar="command",
choices=cli.CLI_COMMANDS,
help="Command to run, valid options are:\n"
f"{cli.CLI_COMMANDS}\n"
"$ vak train ./configs/config_2018-12-17.toml",
)
parser.add_argument(
"configfile",
type=Path,
help="name of config.toml file to use \n"
"$ vak train ./configs/config_2018-12-17.toml",
)
return parser


def main(args=None):
def main(args_list:list[str] | None = None):
"""Main function called when run as script or through command-line interface

called when package is run with `python -m vak` or
alternatively just calling `vak` at the command line (because this
function is installed under just `vak` as a console script)

``args`` is used for unit testing only
``args_list`` is used for unit testing only
"""
if args is None:
parser = get_parser()
parser = cli.get_parser()

if len(sys.argv) < 2:
parser.print_help()
sys.exit(1)

if args_list is None:
args = parser.parse_args()
cli.cli(command=args.command, config_file=args.configfile)
else:
args = parser.parse_args(args_list)
cli.cli(args)


if __name__ == "__main__":
Expand Down
216 changes: 190 additions & 26 deletions src/vak/cli/cli.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,220 @@
def eval(toml_path):
"""Implements the vak command-line interface"""
import argparse
import pathlib
from dataclasses import dataclass
from typing import Callable


def eval(args):
from .eval import eval

eval(toml_path=toml_path)
eval(toml_path=args.configfile)


def train(toml_path):
def train(args):
from .train import train

train(toml_path=toml_path)
train(toml_path=args.configfile)


def learncurve(toml_path):
def learncurve(args):
from .learncurve import learning_curve

learning_curve(toml_path=toml_path)
learning_curve(toml_path=args.configfile)


def predict(toml_path):
def predict(args):
from .predict import predict

predict(toml_path=toml_path)
predict(toml_path=args.configfile)


def prep(toml_path):
def prep(args):
from .prep import prep

prep(toml_path=toml_path)
prep(toml_path=args.configfile)


COMMAND_FUNCTION_MAP = {
"prep": prep,
"train": train,
"eval": eval,
"predict": predict,
"learncurve": learncurve,
}
def configfile(args):
from ..config.generate import generate
generate(
kind=args.kind,
add_prep=args.add_prep,
dst=args.dst,
)


CLI_COMMANDS = tuple(COMMAND_FUNCTION_MAP.keys())
@dataclass
class CLICommand:
"""Dataclass representing a cli command

Attributes
----------
name : str
Name of the command, that gets added to the CLI as a sub-parser
help : str
Help for the command, that gets added to the CLI as a sub-parser
func : Callable
Function to call for command
add_parser_args_func: Callable
Function to call to add arguments to sub-parser representing command
"""
name: str
help: str
func: Callable
add_parser_args_func : Callable


def add_single_arg_configfile_to_command(
cli_command,
cli_command_parser
):
"""Most of the CLICommands call this function
to add arguments to their sub-parser.
It adds a single positional argument, `configfile`.
Not to be confused with the *command* configfile,
that adds different arguments
"""
cli_command_parser.add_argument(
"configfile",
type=pathlib.Path,
help="name of TOML configuration file to use \n"
f"$ vak {cli_command.name} ./configs/config_rat01337.toml",
)


KINDS_OF_CONFIG_FILES = [
# FIXME: there's no way to have a stand-alone prep file right now
# we need to add a `purpose` key-value pair to the file format
# to make this possible
# "prep",
"train",
"eval",
"predict",
"learncurve",
]


def add_args_to_configfile_command(
cli_command,
cli_command_parser
):
"""This is the function that gets called
to add arguments to the sub-parser
for the configfile command
"""
cli_command_parser.add_argument(
"kind",
type=str,
choices=KINDS_OF_CONFIG_FILES,
help="kind: the kind of TOML configuration file to generate"
)
cli_command_parser.add_argument(
"--add-prep",
action=argparse.BooleanOptionalAction,
default=False,
help="Adding this option will add a 'prep' table to the TOML configuration file. Default is False."
)
cli_command_parser.add_argument(
"--dst",
type=pathlib.Path,
default=pathlib.Path.cwd(),
help="Destination, where TOML configuration file should be generated. Default is current working directory."
)
# TODO: add this option
# cli_command_parser.add_argument(
# "--from",
# type=pathlib.Path,
# help="Path to another configuration file that this file should be generated from\n"
# )


CLI_COMMANDS = [
CLICommand(
name='prep',
help='prepare a dataset',
func=prep,
add_parser_args_func=add_single_arg_configfile_to_command,
),
CLICommand(
name='train',
help='train a model',
func=train,
add_parser_args_func=add_single_arg_configfile_to_command,
),
CLICommand(
name='eval',
help='evaluate a trained model',
func=eval,
add_parser_args_func=add_single_arg_configfile_to_command,
),
CLICommand(
name='predict',
help='generate predictions from trained model',
func=predict,
add_parser_args_func=add_single_arg_configfile_to_command,
),
CLICommand(
name='learncurve',
help='run a learning curve',
func=learncurve,
add_parser_args_func=add_single_arg_configfile_to_command,
),
CLICommand(
name='configfile',
help='generate a TOML configuration file for vak',
func=configfile,
add_parser_args_func=add_args_to_configfile_command,
),
]


def get_parser():
"""returns ArgumentParser instance used by main()"""
parser = argparse.ArgumentParser(
prog="vak",
description="Vak command-line interface",
formatter_class=argparse.RawTextHelpFormatter,
)

# create sub-parser
sub_parsers = parser.add_subparsers(
title="Command",
description="Commands for the vak command-line interface",
dest="command",
required=True,
)

for cli_command in CLI_COMMANDS:
cli_command_parser = sub_parsers.add_parser(
cli_command.name,
help=cli_command.help
)
cli_command.add_parser_args_func(
cli_command,
cli_command_parser
)

return parser


CLI_COMMAND_FUNCTION_MAP = {
cli_command.name: cli_command.func
for cli_command in CLI_COMMANDS
}


def cli(command, config_file):
def cli(args: argparse.Namespace):
"""Execute the commands of the command-line interface.

Parameters
----------
command : string
One of {'prep', 'train', 'eval', 'predict', 'learncurve'}
config_file : str, Path
path to a config.toml file
args : argparse.Namespace
Result of calling :meth:`ArgumentParser.parse_args`
on the :class:`ArgumentParser` instance returned by
:func:`vak.cli.cli.get_parser`.
"""
if command in COMMAND_FUNCTION_MAP:
COMMAND_FUNCTION_MAP[command](toml_path=config_file)
if args.command in CLI_COMMAND_FUNCTION_MAP:
CLI_COMMAND_FUNCTION_MAP[args.command](args)
else:
raise ValueError(f"command not recognized: {command}")
raise ValueError(f"command not recognized: {args.command}")
2 changes: 1 addition & 1 deletion src/vak/cli/prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def purpose_from_toml(
# note NO LOGGING -- we configure logger inside `core.prep`
# so we can save log file inside dataset directory

# see https://github.com/NickleDave/vak/issues/334
# see https://github.com/vocalpy/vak/issues/334
TABLES_PREP_SHOULD_PARSE = "prep"


Expand Down
2 changes: 2 additions & 0 deletions src/vak/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .config import Config
from .dataset import DatasetConfig
from .eval import EvalConfig
from ._generate import generate
from .learncurve import LearncurveConfig
from .model import ModelConfig
from .predict import PredictConfig
Expand All @@ -29,6 +30,7 @@
"config",
"dataset",
"eval",
"generate",
"learncurve",
"model",
"load",
Expand Down
Loading
Loading