webis.py 7.65 KB
Newer Older
1
#!/bin/bash
2 3
# -*- coding: utf-8 ; mode: python -*-
"true" '''\'
4 5 6 7 8 9
rootpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. ${rootpath}/libs/bashhelper.sh
if $(cd "$webiscmdrootpath" ; git_repo_has_updates ); then
    logWarn "Your webis-cmd is outdated. Please run 'webis core update'"
fi

10 11 12 13 14 15 16 17 18 19 20 21 22 23
# try running as python3, if that fails fall back to (any) python
command -v python3 > /dev/null
if [ $? -eq 0 ]; then
    exec env python3 "$0" "$@"
fi
command -v python > /dev/null
if [ $? -eq 0 ]; then
    exec env python "$0" "$@"
else
    echo -e "\033[91m[ERROR] Install Python and try again!" 1>&2
    exit 1
fi
'''
__doc__ = """The main webis command.
Steve Goering's avatar
Steve Goering committed
24

Martin Potthast's avatar
Martin Potthast committed
25
Copyright webis.de 2015-today
26
Authors: Janek Bevendorff, Steve Göring, Martin Potthast, Michael Völske
Steve Goering's avatar
Steve Goering committed
27
"""
Martin Potthast's avatar
Martin Potthast committed
28

29
import argparse
Martin Potthast's avatar
Martin Potthast committed
30
import collections
31
import json
Martin Potthast's avatar
Martin Potthast committed
32 33
import os
import sys
34

Steve Goering's avatar
Steve Goering committed
35 36
import loader

37
from lib import *
Martin Potthast's avatar
Martin Potthast committed
38
from log import *
39 40 41
from system import *


42 43 44 45 46 47 48
def check_commands_alias_definition(config):
    """Checks if the command alias definition has no collisions."""
    aliashisto = {}
    for command in config["commands_aliases"]:
        for alias in config["commands_aliases"][command]:
            aliashisto[alias] = aliashisto.get(alias, 0) + 1

Steve Goering's avatar
Steve Goering committed
49
    if len([x for x in aliashisto if aliashisto[x] > 1]):
50 51 52 53
        lError("Commands alias definition has collisions, please fix it.")
        sys.exit(1)


Martin Potthast's avatar
Martin Potthast committed
54 55 56
def load_config():
    """Returns the configuration, based on the JSON configuration file."""
    try:
57
        config = json.loads(read_file(os.path.dirname(
Martin Potthast's avatar
Martin Potthast committed
58
            os.path.realpath(__file__)) + "/config.json"))
59 60 61 62 63 64 65 66 67 68 69 70 71

        if not os.path.isdir(config["commands_directory"]):
            config["commands_directory"] = os.path.dirname(
                os.path.realpath(__file__)) + "/" + config["commands_directory"]

        if not os.path.isdir(config["commands_directory"]):
            lError(
                "moduls_directory {} is not a valid directory, "
                "check config.json".format(config["commands_directory"]))
            sys.exit(1)

        return config

Martin Potthast's avatar
Martin Potthast committed
72 73 74
    except Exception as e:
        lError("The file config.json is invalid.")
        sys.exit(1)
75

Steve Goering's avatar
Steve Goering committed
76

Martin Potthast's avatar
Martin Potthast committed
77 78 79
def get_commands(config):
    """Returns commands mapped to directories containing their subcommands."""
    commands = collections.OrderedDict()
80 81
    for directory in os.listdir(config["commands_directory"]):
        if (os.path.isdir(config["commands_directory"] + directory) and
Steve Goering's avatar
Steve Goering committed
82
                directory not in config["ignored_dirs"]):
83
            commands[directory] = config["commands_directory"] + directory
Martin Potthast's avatar
Martin Potthast committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
    return commands


def get_subcommands(config, commandpath):
    """Returns subcommands of a given command mapped to their script files."""
    subcommands = collections.OrderedDict()
    for script in os.listdir(commandpath):
        subcommandpath = commandpath + "/" + script
        if os.path.isfile(subcommandpath):
            allowed_scripts = config["allowed_scripts"]
            if check_subcommand_shebang(subcommandpath, allowed_scripts):
                subcommand = script.rsplit(".", 1)[0]
                subcommands[subcommand] = subcommandpath
    return subcommands


def get_subcommand_help(subcommandpath):
    """Returns the help message expected at the beginning of a command."""
    if not os.path.isfile(subcommandpath):
        return os.path.basename(subcommandpath)

    f = open(subcommandpath)
106 107 108 109 110 111 112
    lines = f.readlines()
    f.close()
    if len(lines) < 1 or lines[1][0] != "#":
        return ""
    return lines[1][1:].replace("\n", "").strip()


113 114 115
def print_help_table(config):
    """Prints a table with all commands, subcommands, and their descriptions."""
    commands = get_commands(config)
Johannes Kiesel's avatar
Johannes Kiesel committed
116
    for command in sorted(commands):
117
        command_help = str(config["commands_help"].get(command))
118
        print("{:20s} {}".format(command, command_help))
119
        subcommands = get_subcommands(config, commands[command])
Johannes Kiesel's avatar
Johannes Kiesel committed
120
        for subcommand in sorted(subcommands):
121
            subcommand_help = str(get_subcommand_help(subcommands[subcommand]))
122 123
            print("    {:20s} {}".format(subcommand, subcommand_help))
        print("")
124 125


Martin Potthast's avatar
Martin Potthast committed
126 127 128 129 130 131 132
def check_subcommand_shebang(subcommandpath, allowed_scripts):
    """Checks if the shebang of a given script is among the allowed shebangs."""
    shebang = get_shebang(subcommandpath)
    for interpreter in allowed_scripts:
        if interpreter in shebang:
            return True
    return False
Steve Goering's avatar
Steve Goering committed
133

134

Martin Potthast's avatar
Martin Potthast committed
135 136 137
def run_subcommand(subcommand, subcommandpath, params=[]):
    """Runs subcommand passing params and returns its exit code."""
    lDbg("Running " + subcommand + " at " + subcommandpath)
138 139 140
    pp = os.environ.get('PYTHONPATH')
    webis_dir = os.path.dirname(os.path.realpath(__file__))
    os.environ['PYTHONPATH'] = webis_dir if pp is None else pp + ':' + webis_dir
141 142
    if is_verbose_logging():
        os.environ['WEBIS_DEBUG'] = '1'
Martin Potthast's avatar
Martin Potthast committed
143
    cmd = " ".join([subcommandpath] + params)
144 145
    return_value = os.system(cmd)
    if return_value == 0:
146
        lDbg("Done.")
147
    else:
Martin Potthast's avatar
Martin Potthast committed
148
        lError("An error occurred while executing " + cmd)
149
    return return_value
150

151

152 153 154
def translate_alias_to_command(config, alias):
    """Transforms a given alias to the unique command."""
    for command in config["commands_aliases"]:
Steve Goering's avatar
Steve Goering committed
155
        if alias in config["commands_aliases"][command]:
156 157
            return command

158 159

def main(args):
Martin Potthast's avatar
Martin Potthast committed
160
    config = load_config()
161 162
    check_commands_alias_definition(config)

Martin Potthast's avatar
Martin Potthast committed
163 164
    commands = get_commands(config)
    parser = argparse.ArgumentParser()
165
    parser.register('action', 'parsers', AliasedSubParsersAction)
166 167 168
    parser.add_argument('-H', '--helptable',
                        help='Print help of commands and subcommands',
                        action='store_true')
169 170
    parser.add_argument('-v', '--verbose', help='Print debug messages.',
                        action='store_true')
171

Martin Potthast's avatar
Martin Potthast committed
172 173 174 175 176
    subparsers = {}
    allcommands = {}
    commandparsers = parser.add_subparsers(
        metavar='<command>', dest='<command>', help='Available commands:')
    for command in commands:
177 178 179 180 181 182
        help_msg = config["commands_help"].get(
            command,
            colorred(
                "Please add a short description of {} "
                "in config.json[commands_help].".format(command)))

Steve Goering's avatar
Steve Goering committed
183
        commandparser = commandparsers.add_parser(
184 185 186
            command, aliases=config["commands_aliases"].get(command, []),
            help=help_msg)

Martin Potthast's avatar
Martin Potthast committed
187 188 189 190 191 192 193 194 195
        subparsers[command] = commandparser
        subcommandparsers = commandparser.add_subparsers(
            metavar='<subcommand>', dest='<subcommand>',
            help='Available subcommands:')
        subcommands = get_subcommands(config, commands[command])
        allcommands[command] = subcommands
        for subcommand in subcommands:
            subcommandparser = subcommandparsers.add_parser(
                subcommand, help=str(
Steve Goering's avatar
Steve Goering committed
196
                    get_subcommand_help(subcommands[subcommand])),
Martin Potthast's avatar
Martin Potthast committed
197 198 199 200 201
                add_help=False)
            subparsers[subcommand] = subcommandparser

    if len(args) == 0:
        parser.print_help()
202 203
        return 0

Martin Potthast's avatar
Martin Potthast committed
204 205
    parseresults = parser.parse_known_args(args)
    argsdict = vars(parseresults[0])
206
    set_verbose_logging(argsdict['verbose'])
Martin Potthast's avatar
Martin Potthast committed
207
    parameter = parseresults[1]
Steve Goering's avatar
Steve Goering committed
208

209 210 211 212
    if argsdict['helptable']:
        print_help_table(config)
        return 0

Steve Goering's avatar
Steve Goering committed
213
    if argsdict['<command>'] not in subparsers:
Steve Goering's avatar
Steve Goering committed
214 215
        argsdict['<command>'] = translate_alias_to_command(
            config, argsdict['<command>'])
216

217
    if argsdict['<subcommand>'] not in allcommands[argsdict['<command>']]:
Steve Goering's avatar
Steve Goering committed
218
        subparsers[argsdict['<command>']].print_help()
Martin Potthast's avatar
Martin Potthast committed
219
        return 0
220

Steve Goering's avatar
Steve Goering committed
221 222 223 224
    return run_subcommand(
        argsdict['<subcommand>'],
        allcommands[argsdict['<command>']][argsdict['<subcommand>']],
        parameter)
225 226 227


if __name__ == "__main__":
Janek Bevendorff's avatar
Janek Bevendorff committed
228
    sys.exit(main(sys.argv[1:]) >> 8)