aboutsummaryrefslogtreecommitdiffstats
path: root/python/refcat/cli.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/refcat/cli.py')
-rw-r--r--python/refcat/cli.py251
1 files changed, 251 insertions, 0 deletions
diff --git a/python/refcat/cli.py b/python/refcat/cli.py
new file mode 100644
index 0000000..ca1cab7
--- /dev/null
+++ b/python/refcat/cli.py
@@ -0,0 +1,251 @@
+"""
+ ____ __
+ ________ / __/________ _/ /_
+ / ___/ _ \/ /_/ ___/ __ `/ __/
+ / / / __/ __/ /__/ /_/ / /_
+/_/ \___/_/ \___/\__,_/\__/
+
+Command line entry point for running various data tasks.
+
+ $ refcat.pyz [COMMAND | TASK] [OPTIONS]
+
+Commands: ls, ll, deps, tasks, files, config, cat, completion
+
+To install completion run:
+
+ $ source <(refcat.pyz completion)
+"""
+
+import logging
+import subprocess
+import sys
+import tempfile
+
+import gluish
+import luigi
+from luigi.parameter import MissingParameterException
+from luigi.task import Register
+from luigi.task_register import TaskClassNotFoundException
+
+from refcat import __version__
+from refcat.settings import LOGGING_CONF_FILE, settings
+from refcat.deps import dump_deps
+from refcat.tasks import *
+from refcat.utils import columnize
+
+# XXX: get rid of gluish dep, include them in refcat
+suppress_task_names = [
+ "Available",
+ "BaseTask",
+ "Config",
+ "Executable",
+ "ExternalTask",
+ "FillSolrIndex",
+ "GitCloneRepository",
+ "GitUpdateRepository",
+ "MockTask",
+ "RangeBase",
+ "RangeByMinutes",
+ "RangeByMinutesBase",
+ "RangeDaily",
+ "RangeDailyBase",
+ "RangeHourly",
+ "RangeHourlyBase",
+ "RangeMonthly",
+ "Task",
+ "TestNotificationsTask",
+ "WrapperTask",
+]
+
+
+def tasks():
+ """
+ Print task name.
+ """
+ names = [name for name in sorted(Register.task_names()) if name not in suppress_task_names and not name.islower()]
+ for name in names:
+ print(name)
+
+
+def files():
+ """
+ Print task name and file.
+ """
+ names = [name for name in sorted(Register.task_names()) if name not in suppress_task_names and not name.islower()]
+ for name in names:
+ klass = Register.get_task_cls(name)
+ try:
+ print("{:30s}{}".format(name, klass().output().path))
+ except AttributeError:
+ print("{:30s}".format(name))
+
+
+def cat(*args):
+ """
+ Inspect file.
+ """
+ if len(args) == 0:
+ raise ValueError("ls failed: task name required")
+ task = find_task(args[0])
+ try:
+ filename = task().output().path
+ if filename.endswith(".zst"):
+ subprocess.run(["zstdcat", "-T0", filename])
+ elif filename.endswith(".gz"):
+ subprocess.run(["zcat", filename])
+ else:
+ subprocess.run(["cat", filename])
+ except FileNotFoundError:
+ print("file not found: {}".format(filename), file=sys.stderr)
+ except AttributeError:
+ print("{} is most likely not a task object".format(name), file=sys.stderr)
+
+
+def ls(*args):
+ """
+ Print output filename for task.
+ """
+ if len(args) == 0:
+ raise ValueError("ls failed: task name required")
+ task = find_task(args[0])
+ print(task().output().path)
+
+
+def ll(*args):
+ """
+ Long output.
+ """
+ if len(args) == 0:
+ raise ValueError("ls failed: task name required")
+ task = find_task(args[0])
+ try:
+ filename = task().output().path
+ subprocess.run(["ls", "-lah", filename])
+ except FileNotFoundError:
+ print("file not found: {}".format(filename), file=sys.stderr)
+ except AttributeError:
+ print("{} is most likely not a task object".format(name), file=sys.stderr)
+
+
+def deps(*args):
+ """
+ Render task dependencies.
+ """
+ if len(args) == 0:
+ raise ValueError("deps failed: task name required")
+ task = find_task(args[0])
+ dump_deps(task())
+
+
+def config():
+ """
+ Dump config to stdout.
+ """
+ with open(settings.settings_file) as f:
+ print(f.read())
+
+
+def completion():
+ snippet = """
+_refcat_completion()
+{
+ local cur prev
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $(compgen -W "cat completion config deps files ll ls tasks" -- $cur) )
+ elif [ $COMP_CWORD -eq 2 ]; then
+ case "$prev" in
+ "cat")
+ COMPREPLY=( $(compgen -W "$(refcat.pyz tasks)" -- $cur) )
+ ;;
+ "ll")
+ COMPREPLY=( $(compgen -W "$(refcat.pyz tasks)" -- $cur) )
+ ;;
+ "ls")
+ COMPREPLY=( $(compgen -W "$(refcat.pyz tasks)" -- $cur) )
+ ;;
+ *)
+ ;;
+ esac
+ fi
+ return 0
+}
+
+complete -F _refcat_completion "refcat.pyz"
+complete -F _refcat_completion "refcat"
+ """
+ print(snippet)
+
+
+def find_task(name):
+ """
+ Note: return task class, not instance.
+ """
+ task = globals().get(name)
+ if task is None:
+ raise RuntimeError("no such task: {}".format(name))
+ if not isinstance(task(), luigi.Task):
+ raise RuntimeError("not a task: {}".format(name))
+ return task
+
+
+def main():
+ """
+ The main command line entry point.
+ """
+ if os.path.exists(LOGGING_CONF_FILE):
+ logging.config.fileConfig(LOGGING_CONF_FILE)
+
+ if settings.TMPDIR:
+ # This might not be passed on to subprocesses.
+ tempfile.tempdir = settings.TMPDIR
+
+ # Explicitly name valid subcommands.
+ sub_commands = [
+ "cat",
+ "completion",
+ "config",
+ "deps",
+ "files",
+ "ll",
+ "ls",
+ "tasks",
+ ]
+
+ if len(sys.argv) >= 2 and sys.argv[1] in sub_commands:
+ try:
+ func = globals()[sys.argv[1]]
+ if callable(func):
+ func(*sys.argv[2:])
+ else:
+ print("not implemented: {}".format(sys.argv[1]))
+ except KeyError:
+ print("sub-command not implemented: {}".format(sys.argv[1]), file=sys.stderr)
+ except (AttributeError, ValueError, RuntimeError) as exc:
+ print(exc, file=sys.stderr)
+ sys.exit(1)
+ else:
+ sys.exit(0)
+ elif len(sys.argv) == 1:
+ print(__doc__)
+ print("VERSION {}".format(__version__))
+ print("SETTINGS {}".format(settings.settings_file))
+ print("BASE {}".format(settings.BASE))
+ print("TMPDIR {}".format(settings.TMPDIR))
+ print()
+ names = [name for name in sorted(Register.task_names()) if name not in suppress_task_names and not name.islower()]
+ print(columnize(names))
+ sys.exit(0)
+
+ # If we found not subcommand, assume task name.
+ try:
+ luigi.run(local_scheduler=True) # XXX: add logging_conf_file
+ except MissingParameterException as err:
+ print('missing parameter: %s' % err, file=sys.stderr)
+ sys.exit(1)
+ except TaskClassNotFoundException as err:
+ print(err, file=sys.stderr)
+ sys.exit(1)