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

16 17 18 19 20 21 22 23 24 25 26 27 28 29
# 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
30

Martin Potthast's avatar
Martin Potthast committed
31
Copyright webis.de 2015-today
32
Authors: Janek Bevendorff, Steve Göring, Martin Potthast, Michael Völske
Steve Goering's avatar
Steve Goering committed
33
"""
Martin Potthast's avatar
Martin Potthast committed
34

35
import argparse
Martin Potthast's avatar
Martin Potthast committed
36
import collections
37
import json
Martin Potthast's avatar
Martin Potthast committed
38 39
import os
import sys
40

Steve Goering's avatar
Steve Goering committed
41 42
import loader

43
from lib import *
Martin Potthast's avatar
Martin Potthast committed
44
from log import *
45 46 47
from system import *


48 49 50 51 52 53 54
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
55
    if len([x for x in aliashisto if aliashisto[x] > 1]):
56 57 58 59
        lError("Commands alias definition has collisions, please fix it.")
        sys.exit(1)


Martin Potthast's avatar
Martin Potthast committed
60 61 62
def load_config():
    """Returns the configuration, based on the JSON configuration file."""
    try:
63
        config = json.loads(read_file(os.path.dirname(
Martin Potthast's avatar
Martin Potthast committed
64
            os.path.realpath(__file__)) + "/config.json"))
65 66 67 68 69 70 71 72 73 74 75 76 77

        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
78 79 80
    except Exception as e:
        lError("The file config.json is invalid.")
        sys.exit(1)
81

Steve Goering's avatar
Steve Goering committed
82

Martin Potthast's avatar
Martin Potthast committed
83 84 85
def get_commands(config):
    """Returns commands mapped to directories containing their subcommands."""
    commands = collections.OrderedDict()
86 87
    for directory in os.listdir(config["commands_directory"]):
        if (os.path.isdir(config["commands_directory"] + directory) and
Steve Goering's avatar
Steve Goering committed
88
                directory not in config["ignored_dirs"]):
89
            commands[directory] = config["commands_directory"] + directory
Martin Potthast's avatar
Martin Potthast committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
    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)
112 113 114 115 116 117 118
    lines = f.readlines()
    f.close()
    if len(lines) < 1 or lines[1][0] != "#":
        return ""
    return lines[1][1:].replace("\n", "").strip()


119 120 121
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
122
    for command in sorted(commands):
123
        command_help = str(config["commands_help"].get(command))
124
        print("{:20s} {}".format(command, command_help))
125
        subcommands = get_subcommands(config, commands[command])
Johannes Kiesel's avatar
Johannes Kiesel committed
126
        for subcommand in sorted(subcommands):
127
            subcommand_help = str(get_subcommand_help(subcommands[subcommand]))
128 129
            print("    {:20s} {}".format(subcommand, subcommand_help))
        print("")
130 131


Martin Potthast's avatar
Martin Potthast committed
132 133 134 135 136 137 138
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
139

140

Martin Potthast's avatar
Martin Potthast committed
141 142 143
def run_subcommand(subcommand, subcommandpath, params=[]):
    """Runs subcommand passing params and returns its exit code."""
    lDbg("Running " + subcommand + " at " + subcommandpath)
144 145 146
    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
147 148
    if is_verbose_logging():
        os.environ['WEBIS_DEBUG'] = '1'
Martin Potthast's avatar
Martin Potthast committed
149
    cmd = " ".join([subcommandpath] + params)
150 151
    return_value = os.system(cmd)
    if return_value == 0:
152
        lDbg("Done.")
153
    else:
Martin Potthast's avatar
Martin Potthast committed
154
        lError("An error occurred while executing " + cmd)
155
    return return_value
156

157

158 159 160
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
161
        if alias in config["commands_aliases"][command]:
162 163
            return command

164 165

def main(args):
Martin Potthast's avatar
Martin Potthast committed
166
    config = load_config()
167 168
    check_commands_alias_definition(config)

Martin Potthast's avatar
Martin Potthast committed
169 170
    commands = get_commands(config)
    parser = argparse.ArgumentParser()
171
    parser.register('action', 'parsers', AliasedSubParsersAction)
172 173 174
    parser.add_argument('-H', '--helptable',
                        help='Print help of commands and subcommands',
                        action='store_true')
175 176
    parser.add_argument('-v', '--verbose', help='Print debug messages.',
                        action='store_true')
177

Martin Potthast's avatar
Martin Potthast committed
178 179 180 181 182
    subparsers = {}
    allcommands = {}
    commandparsers = parser.add_subparsers(
        metavar='<command>', dest='<command>', help='Available commands:')
    for command in commands:
183 184 185 186 187 188
        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
189
        commandparser = commandparsers.add_parser(
190 191 192
            command, aliases=config["commands_aliases"].get(command, []),
            help=help_msg)

Martin Potthast's avatar
Martin Potthast committed
193 194 195 196 197 198 199 200 201
        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
202
                    get_subcommand_help(subcommands[subcommand])),
Martin Potthast's avatar
Martin Potthast committed
203 204 205 206 207
                add_help=False)
            subparsers[subcommand] = subcommandparser

    if len(args) == 0:
        parser.print_help()
208 209
        return 0

Martin Potthast's avatar
Martin Potthast committed
210 211
    parseresults = parser.parse_known_args(args)
    argsdict = vars(parseresults[0])
212
    set_verbose_logging(argsdict['verbose'])
Martin Potthast's avatar
Martin Potthast committed
213
    parameter = parseresults[1]
Steve Goering's avatar
Steve Goering committed
214

215 216 217 218
    if argsdict['helptable']:
        print_help_table(config)
        return 0

Steve Goering's avatar
Steve Goering committed
219
    if argsdict['<command>'] not in subparsers:
Steve Goering's avatar
Steve Goering committed
220 221
        argsdict['<command>'] = translate_alias_to_command(
            config, argsdict['<command>'])
222

223
    if argsdict['<subcommand>'] not in allcommands[argsdict['<command>']]:
Steve Goering's avatar
Steve Goering committed
224
        subparsers[argsdict['<command>']].print_help()
Martin Potthast's avatar
Martin Potthast committed
225
        return 0
226

Steve Goering's avatar
Steve Goering committed
227 228 229 230
    return run_subcommand(
        argsdict['<subcommand>'],
        allcommands[argsdict['<command>']][argsdict['<subcommand>']],
        parameter)
231 232 233


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