Skip to content

Commit

Permalink
update cli with common parser for all commands. leverage argparse for…
Browse files Browse the repository at this point in the history
… command parsing. help messages and usage needs to be fixed
  • Loading branch information
asampat3090 committed Apr 6, 2018
1 parent e2901e4 commit c73d16a
Show file tree
Hide file tree
Showing 11 changed files with 180 additions and 171 deletions.
61 changes: 61 additions & 0 deletions datmo/cli/command/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from datmo.cli.driver.parser import Parser
from datmo.util.exceptions import ClassMethodNotFound


class BaseCommand(object):
def __init__(self, home, cli_helper):
self.home = home
self.cli_helper = cli_helper
self.parser = Parser(prog="datmo", usage="""
datmo COMMAND [SUBCOMMANDS] ARGS
Datmo is a command line utility to enable tracking of data science projects.
It uses many of the tools you are already familiar with and combines them into a snapshot
which allows you to keep track of 5 components at once
1) Source Code
2) Dependency Environment
3) Large Files
4) Configurations
5) Metrics
command:
""")
# self.parser.add_argument("-v", "--verbose", action="store_true",
# help="run in verbose mode")
self.subparsers = self.parser.add_subparsers(title="commands", dest="command")

def parse(self, args):
self.args = self.parser.parse_args(args)

def execute(self):
"""
Calls the method if it exists on this object, otherwise
call a default method name (module name)
Raises
------
ClassMethodNotFound
If the Class method is not found
"""
command_args = vars(self.args).copy()
# use command name if it exists,
# otherwise use the module name

if "subcommand" in command_args:
method = getattr(self, getattr(self.args, "subcommand", self.args.command))
else:
method = getattr(self, getattr(self.args, "command", self.args.command))

# remove extraneous options that the method should need to care about
if "command" in command_args:
del command_args["command"]
if "subcommand" in command_args:
del command_args["subcommand"]

if method is None:
raise ClassMethodNotFound("Method %s.%s not found" % (self.args.command, method))

method(**command_args)
24 changes: 0 additions & 24 deletions datmo/cli/command/init.py

This file was deleted.

18 changes: 18 additions & 0 deletions datmo/cli/command/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from datmo.util.i18n import get as _
from datmo.cli.command.base import BaseCommand
from datmo.controller.project import ProjectController


class ProjectCommand(BaseCommand):
# NOTE: dal_driver is not passed into the project because it is created
# first by ProjectController and then passed down to all other Controllers
def __init__(self, home, cli_helper):
super(ProjectCommand, self).__init__(home, cli_helper)
init_parser = self.subparsers.add_parser("init", help="Initialize project")
init_parser.add_argument("--name")
init_parser.add_argument("--description", default=None)
self.project_controller = ProjectController(home=home)

def init(self, name, description):
self.cli_helper.echo(_('setup.init_project', {"name":name, "path": self.home}))
self.project_controller.init(name, description)
128 changes: 60 additions & 68 deletions datmo/cli/command/snapshot.py
Original file line number Diff line number Diff line change
@@ -1,99 +1,91 @@
from datmo.cli.driver.cli_base_command import CLIBaseCommand
from datmo.cli.driver.cli_argument_parser import CLIArgumentParser
from datmo.util.i18n import get as _
from datmo.cli.command.project import ProjectCommand
from datmo.controller.snapshot import SnapshotController

from datmo.util.exceptions import ProjectNotInitializedException

def get_parser():
snapshot_parser = CLIArgumentParser(prog="datmo", usage="%(prog)s snapshot")
snapshot_parser.add_argument("command", choices=["snapshot"])
# dest="subcommand" argument will populate a "subcommand" property with the subparsers name
# example "subcommand"="create" or "subcommand"="ls"
subcommand_parsers = snapshot_parser.add_subparsers(dest="subcommand")

create = subcommand_parsers.add_parser("create", help="Create snapshot")
create.add_argument("--message", "-m", dest="message", default="", help="Message to describe snapshot")
create.add_argument("--label", "-l", dest="label", default="", help="Label snapshots with a category (e.g. best)")
create.add_argument("--session-id", dest="session_id", default="", help="User given session id")
class SnapshotCommand(ProjectCommand):
def __init__(self, home, cli_helper):
super(SnapshotCommand, self).__init__(home, cli_helper)

create.add_argument("--task-id", dest="task_id", default=None,
help="Specify task id to pull information from")
snapshot_parser = self.subparsers.add_parser("snapshot", help="Snapshot module")
# dest="subcommand" argument will populate a "subcommand" property with the subparsers name
# example "subcommand"="create" or "subcommand"="ls"
subcommand_parsers = snapshot_parser.add_subparsers(title="subcommands", dest="subcommand")

create.add_argument("--code-id", dest="code_id", default="", help="User provided code id (e.g. git revision for git)")
create = subcommand_parsers.add_parser("create", help="Create snapshot")
create.add_argument("--message", "-m", dest="message", default="", help="Message to describe snapshot")
create.add_argument("--label", "-l", dest="label", default="",
help="Label snapshots with a category (e.g. best)")
create.add_argument("--session-id", dest="session_id", default="", help="User given session id")

create.add_argument("--environment-def-path", dest="environment_def_path", default="", help="Absolute filepath to environment definition file (e.g. /path/to/Dockerfile)")
create.add_argument("--task-id", dest="task_id", default=None,
help="Specify task id to pull information from")

create.add_argument("--config-filename", dest="config_filename", default=None, help="Filename to use to search for configuration JSON")
create.add_argument("--config-filepath", dest="config_filepath", default=None, help="Absolute filepath to use to search for configuration JSON")
create.add_argument("--code-id", dest="code_id", default="",
help="User provided code id (e.g. git revision for git)")

create.add_argument("--stats-filename", dest="stats_filename", default=None, help="Filename to use to search for metrics JSON")
create.add_argument("--stats-filepath", dest="stats_filepath", default=None, help="Absolute filepath to use to search for metrics JSON")
create.add_argument("--environment-def-path", dest="environment_def_path", default="",
help="Absolute filepath to environment definition file (e.g. /path/to/Dockerfile)")

create.add_argument("--filepaths", dest="filepaths", default=None, nargs="*",
help="Absolute paths to files or folders to include within the files of the snapshot")
create.add_argument("--config-filename", dest="config_filename", default=None,
help="Filename to use to search for configuration JSON")
create.add_argument("--config-filepath", dest="config_filepath", default=None,
help="Absolute filepath to use to search for configuration JSON")

delete = subcommand_parsers.add_parser("delete", help="Delete a snapshot by id")
delete.add_argument("--id", dest="snapshot_id", help="snapshot id to delete")
create.add_argument("--stats-filename", dest="stats_filename", default=None,
help="Filename to use to search for metrics JSON")
create.add_argument("--stats-filepath", dest="stats_filepath", default=None,
help="Absolute filepath to use to search for metrics JSON")

ls = subcommand_parsers.add_parser("ls", help="List snapshots")
ls.add_argument("--session-id", dest="session_id", default=None, help="Session ID to filter")
ls.add_argument("--session-name", dest="session_name", default=None, help="Session name to filter")
ls.add_argument("-a", dest="details", default=True, help="Show detailed Snapshot information")
create.add_argument("--filepaths", dest="filepaths", default=None, nargs="*",
help="Absolute paths to files or folders to include within the files of the snapshot")

checkout = subcommand_parsers.add_parser("checkout", help="Checkout a snapshot by id")
checkout.add_argument("--id", dest="snapshot_id", default=None, help="Snapshot ID")
delete = subcommand_parsers.add_parser("delete", help="Delete a snapshot by id")
delete.add_argument("--id", dest="snapshot_id", help="snapshot id to delete")

home = subcommand_parsers.add_parser("home", help="Checkout/Reset back to initial state")
ls = subcommand_parsers.add_parser("ls", help="List snapshots")
ls.add_argument("--session-id", dest="session_id", default=None, help="Session ID to filter")
ls.add_argument("--session-name", dest="session_name", default=None, help="Session name to filter")
ls.add_argument("-a", dest="details", default=True, help="Show detailed SnapshotCommand information")

update = subcommand_parsers.add_parser("update", help="Update Snapshot with meta information ")
update.add_argument("--id", dest="snapshot_id", default=None, help="Snapshot id to edit")
update.add_argument("--message", "-m", dest="message", default=None, help="Message to describe snapshot")
update.add_argument("--label", "-l", dest="label", default=None, help="Label snapshots with a category (e.g. best)")
checkout = subcommand_parsers.add_parser("checkout", help="Checkout a snapshot by id")
checkout.add_argument("--id", dest="snapshot_id", default=None, help="SnapshotCommand ID")

update.add_argument("--config-filename", dest="config_filename", default=None,
help="Filename to use to search for configuration JSON")
update.add_argument("--config-filepath", dest="config_filepath", default=None,
help="Absolute filepath to use to search for configuration JSON")
# home = subcommand_parsers.add_parser("home", help="Checkout/Reset back to initial state")

update.add_argument("--stats-filename", dest="stats", default=None,
help="Filename to use to search for metrics JSON")
update.add_argument("--stats-filepath", dest="stats", default=None,
help="Absolute filepath to use to search for metrics JSON")
update = subcommand_parsers.add_parser("update", help="Update SnapshotCommand with meta information ")
update.add_argument("--id", dest="snapshot_id", default=None, help="SnapshotCommand id to edit")
update.add_argument("--message", "-m", dest="message", default=None, help="Message to describe snapshot")
update.add_argument("--label", "-l", dest="label", default=None,
help="Label snapshots with a category (e.g. best)")

best = subcommand_parsers.add_parser("best", help="Sets the best snapshot for a model")
best.add_argument("--id", dest="snapshot_id", default=None, help="Snapshot ID")
update.add_argument("--config-filename", dest="config_filename", default=None,
help="Filename to use to search for configuration JSON")
update.add_argument("--config-filepath", dest="config_filepath", default=None,
help="Absolute filepath to use to search for configuration JSON")

# REMOTE datmo snapshot commands
# TODO: decide where these go
# push = command_parsers.add_parser("push", help="Push snapshot to remote server")
# push.add_argument("--id", dest="snapshot_id", default=None, help="Push Snapshot data from local to Datmo remote server (use with caution if your weights files are particularly large)")
#
# fetch = command_parsers.add_parser("fetch", help="Fetches a snapshot from a remote server")
# fetch.add_argument("--id", dest="snapshot_ids", nargs="+", default=None, help="Push Snapshot data from local to Datmo remote server (use with caution if your weights files are particularly large)")
update.add_argument("--stats-filename", dest="stats", default=None,
help="Filename to use to search for metrics JSON")
update.add_argument("--stats-filepath", dest="stats", default=None,
help="Absolute filepath to use to search for metrics JSON")

return snapshot_parser
best = subcommand_parsers.add_parser("best", help="Sets the best snapshot for a model")
best.add_argument("--id", dest="snapshot_id", default=None, help="SnapshotCommand ID")

class Snapshot(CLIBaseCommand):
def __init__(self, home, cli_helper, dal_driver=None):
self.cli_helper = cli_helper
self.controller = SnapshotController(home=home,
cli_helper=cli_helper,
dal_driver=dal_driver)
if not self.controller.is_initialized:
self.snapshot_controller = SnapshotController(home=home,
dal_driver=self.project_controller.dal_driver)
if not self.project_controller.is_initialized:
raise ProjectNotInitializedException("exception.cli.snapshot", {
"exception": "No project found in the current directory"
})

super(Snapshot, self).__init__(self.cli_helper, get_parser())

def create(self, **kwargs):
self.controller.create(**kwargs)

self.cli_helper.echo(_('snapshot.create'))
self.snapshot_controller.create(**kwargs)

def delete(self, snapshot_id):
print("delete:"+snapshot_id)
#project = DatmoProject(os.getcwd())
#project.delete_snapshot(snapshot_id)
self.snapshot_controller.delete(snapshot_id)

def ls(self, **kwargs):
print("ls", kwargs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
import os
import shutil
import tempfile
from datmo.cli.driver.cli_helper import CLIHelper
from datmo.cli.command.init import Init
from datmo.cli.driver.helper import Helper
from datmo.cli.command.project import ProjectCommand

class TestInit():
def setup_class(self):
self.temp_dir = tempfile.mkdtemp()
self.cli = CLIHelper()
self.init = Init(self.temp_dir, self.cli)
self.cli = Helper()
self.init = ProjectCommand(self.temp_dir, self.cli)

def teardown_class(self):
shutil.rmtree(self.temp_dir)
Expand Down
14 changes: 7 additions & 7 deletions datmo/cli/command/test/test_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,22 @@

import shutil
import tempfile
from datmo.cli.driver.cli_helper import CLIHelper
from datmo.cli.command.init import Init
from datmo.cli.command.snapshot import Snapshot
from datmo.cli.driver.helper import Helper
from datmo.cli.command.project import ProjectCommand
from datmo.cli.command.snapshot import SnapshotCommand

class TestSnapshot():
def setup_class(self):
self.temp_dir = tempfile.mkdtemp()
self.cli_helper = CLIHelper()
self.init = Init(self.temp_dir, self.cli_helper)
self.cli_helper = Helper()
self.init = ProjectCommand(self.temp_dir, self.cli_helper)
self.init.parse([
"init",
"--name", "foobar",
"--description", "test model"])
self.init.execute()
self.snapshot = Snapshot(self.temp_dir, self.cli_helper,
self.init.controller.dal.driver)
self.snapshot = SnapshotCommand(self.temp_dir, self.cli_helper,
self.init.controller.dal.driver)

def teardown_class(self):
shutil.rmtree(self.temp_dir)
Expand Down
35 changes: 0 additions & 35 deletions datmo/cli/driver/cli_base_command.py

This file was deleted.

8 changes: 6 additions & 2 deletions datmo/cli/driver/cli_helper.py → datmo/cli/driver/helper.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import sys
import importlib
import inspect
from datmo.util.i18n import get as _
from datmo.util.exceptions import ArgumentException

class CLIHelper():
class Helper():
def __init__(self):
pass

Expand Down Expand Up @@ -46,4 +47,7 @@ def get_command_class(self, command_name):
break

# command_class[1] == concrete class constructor
return command_class[1]
return command_class[1]

def get_command_choices(self):
return ["init", "snapshot", "task"]
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import sys
from datmo.util.exceptions import UnrecognizedCLIArgument

class CLIArgumentParser(argparse.ArgumentParser):
class Parser(argparse.ArgumentParser):
"""
Overwrite the original ArgumentParser
Expand Down Expand Up @@ -42,4 +42,4 @@ def error(self, message):
if exc:
exc.argument = self._get_action_from_name(exc.argument_name)
raise exc
super(CLIArgumentParser, self).error(message)
super(Parser, self).error(message)

0 comments on commit c73d16a

Please sign in to comment.