You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
4.1 KiB
164 lines
4.1 KiB
# coding: utf-8
|
|
from __future__ import unicode_literals, division, absolute_import, print_function
|
|
|
|
import ast
|
|
import _ast
|
|
import os
|
|
import sys
|
|
|
|
from . import package_root, task_keyword_args
|
|
from ._import import _import_from
|
|
|
|
|
|
if sys.version_info < (3,):
|
|
byte_cls = str
|
|
else:
|
|
byte_cls = bytes
|
|
|
|
|
|
def _list_tasks():
|
|
"""
|
|
Fetches a list of all valid tasks that may be run, and the args they
|
|
accept. Does not actually import the task module to prevent errors if a
|
|
user does not have the dependencies installed for every task.
|
|
|
|
:return:
|
|
A list of 2-element tuples:
|
|
0: a unicode string of the task name
|
|
1: a list of dicts containing the parameter definitions
|
|
"""
|
|
|
|
out = []
|
|
dev_path = os.path.join(package_root, 'dev')
|
|
for fname in sorted(os.listdir(dev_path)):
|
|
if fname.startswith('.') or fname.startswith('_'):
|
|
continue
|
|
if not fname.endswith('.py'):
|
|
continue
|
|
name = fname[:-3]
|
|
args = ()
|
|
|
|
full_path = os.path.join(package_root, 'dev', fname)
|
|
with open(full_path, 'rb') as f:
|
|
full_code = f.read()
|
|
if sys.version_info >= (3,):
|
|
full_code = full_code.decode('utf-8')
|
|
|
|
task_node = ast.parse(full_code, filename=full_path)
|
|
for node in ast.iter_child_nodes(task_node):
|
|
if isinstance(node, _ast.Assign):
|
|
if len(node.targets) == 1 \
|
|
and isinstance(node.targets[0], _ast.Name) \
|
|
and node.targets[0].id == 'run_args':
|
|
args = ast.literal_eval(node.value)
|
|
break
|
|
|
|
out.append((name, args))
|
|
return out
|
|
|
|
|
|
def show_usage():
|
|
"""
|
|
Prints to stderr the valid options for invoking tasks
|
|
"""
|
|
|
|
valid_tasks = []
|
|
for task in _list_tasks():
|
|
usage = task[0]
|
|
for run_arg in task[1]:
|
|
usage += ' '
|
|
name = run_arg.get('name', '')
|
|
if run_arg.get('required', False):
|
|
usage += '{%s}' % name
|
|
else:
|
|
usage += '[%s]' % name
|
|
valid_tasks.append(usage)
|
|
|
|
out = 'Usage: run.py'
|
|
for karg in task_keyword_args:
|
|
out += ' [%s=%s]' % (karg['name'], karg['placeholder'])
|
|
out += ' (%s)' % ' | '.join(valid_tasks)
|
|
|
|
print(out, file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
def _get_arg(num):
|
|
"""
|
|
:return:
|
|
A unicode string of the requested command line arg
|
|
"""
|
|
|
|
if len(sys.argv) < num + 1:
|
|
return None
|
|
arg = sys.argv[num]
|
|
if isinstance(arg, byte_cls):
|
|
arg = arg.decode('utf-8')
|
|
return arg
|
|
|
|
|
|
def run_task():
|
|
"""
|
|
Parses the command line args, invoking the requested task
|
|
"""
|
|
|
|
arg_num = 1
|
|
task = None
|
|
args = []
|
|
kwargs = {}
|
|
|
|
# We look for the task name, processing any global task keyword args
|
|
# by setting the appropriate env var
|
|
while True:
|
|
val = _get_arg(arg_num)
|
|
if val is None:
|
|
break
|
|
|
|
next_arg = False
|
|
for karg in task_keyword_args:
|
|
if val.startswith(karg['name'] + '='):
|
|
os.environ[karg['env_var']] = val[len(karg['name']) + 1:]
|
|
next_arg = True
|
|
break
|
|
|
|
if next_arg:
|
|
arg_num += 1
|
|
continue
|
|
|
|
task = val
|
|
break
|
|
|
|
if task is None:
|
|
show_usage()
|
|
|
|
task_mod = _import_from('dev.%s' % task, package_root, allow_error=True)
|
|
if task_mod is None:
|
|
show_usage()
|
|
|
|
run_args = task_mod.__dict__.get('run_args', [])
|
|
max_args = arg_num + 1 + len(run_args)
|
|
|
|
if len(sys.argv) > max_args:
|
|
show_usage()
|
|
|
|
for i, run_arg in enumerate(run_args):
|
|
val = _get_arg(arg_num + 1 + i)
|
|
if val is None:
|
|
if run_arg.get('required', False):
|
|
show_usage()
|
|
break
|
|
|
|
if run_arg.get('cast') == 'int' and val.isdigit():
|
|
val = int(val)
|
|
|
|
kwarg = run_arg.get('kwarg')
|
|
if kwarg:
|
|
kwargs[kwarg] = val
|
|
else:
|
|
args.append(val)
|
|
|
|
run = task_mod.__dict__.get('run')
|
|
|
|
result = run(*args, **kwargs)
|
|
sys.exit(int(not result))
|