webis.py 7.41 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#!/bin/sh
# -*- coding: utf-8 ; mode: python -*-
"true" '''\'
# 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
18

Martin Potthast's avatar
Martin Potthast committed
19
Copyright webis.de 2015-today
20
Authors: Janek Bevendorff, Steve Göring, Martin Potthast, Michael Völske
Steve Goering's avatar
Steve Goering committed
21
"""
Martin Potthast's avatar
Martin Potthast committed
22

23
import argparse
Martin Potthast's avatar
Martin Potthast committed
24
import collections
25
import json
Martin Potthast's avatar
Martin Potthast committed
26 27
import os
import sys
28

Steve Goering's avatar
Steve Goering committed
29 30
import loader

31
from lib import *
Martin Potthast's avatar
Martin Potthast committed
32
from log import *
33 34 35
from system import *


36 37 38 39 40 41 42
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
43
    if len([x for x in aliashisto if aliashisto[x] > 1]):
44 45 46 47
        lError("Commands alias definition has collisions, please fix it.")
        sys.exit(1)


Martin Potthast's avatar
Martin Potthast committed
48 49 50
def load_config():
    """Returns the configuration, based on the JSON configuration file."""
    try:
51
        config = json.loads(read_file(os.path.dirname(
Martin Potthast's avatar
Martin Potthast committed
52
            os.path.realpath(__file__)) + "/config.json"))
53 54 55 56 57 58 59 60 61 62 63 64 65

        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
66 67 68
    except Exception as e:
        lError("The file config.json is invalid.")
        sys.exit(1)
69

Steve Goering's avatar
Steve Goering committed
70

Martin Potthast's avatar
Martin Potthast committed
71 72 73
def get_commands(config):
    """Returns commands mapped to directories containing their subcommands."""
    commands = collections.OrderedDict()
74 75
    for directory in os.listdir(config["commands_directory"]):
        if (os.path.isdir(config["commands_directory"] + directory) and
Steve Goering's avatar
Steve Goering committed
76
                directory not in config["ignored_dirs"]):
77
            commands[directory] = config["commands_directory"] + directory
Martin Potthast's avatar
Martin Potthast committed
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
    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)
100 101 102 103 104 105 106
    lines = f.readlines()
    f.close()
    if len(lines) < 1 or lines[1][0] != "#":
        return ""
    return lines[1][1:].replace("\n", "").strip()


107 108 109
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
110
    for command in sorted(commands):
111
        command_help = str(config["commands_help"].get(command))
112
        print("{:20s} {}".format(command, command_help))
113
        subcommands = get_subcommands(config, commands[command])
Johannes Kiesel's avatar
Johannes Kiesel committed
114
        for subcommand in sorted(subcommands):
115
            subcommand_help = str(get_subcommand_help(subcommands[subcommand]))
116 117
            print("    {:20s} {}".format(subcommand, subcommand_help))
        print("")
118 119


Martin Potthast's avatar
Martin Potthast committed
120 121 122 123 124 125 126
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
127

128

Martin Potthast's avatar
Martin Potthast committed
129 130 131
def run_subcommand(subcommand, subcommandpath, params=[]):
    """Runs subcommand passing params and returns its exit code."""
    lDbg("Running " + subcommand + " at " + subcommandpath)
132 133 134
    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
135 136
    if is_verbose_logging():
        os.environ['WEBIS_DEBUG'] = '1'
Martin Potthast's avatar
Martin Potthast committed
137
    cmd = " ".join([subcommandpath] + params)
138 139
    return_value = os.system(cmd)
    if return_value == 0:
140
        lDbg("Done.")
141
    else:
Martin Potthast's avatar
Martin Potthast committed
142
        lError("An error occurred while executing " + cmd)
143
    return return_value
144

145

146 147 148
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
149
        if alias in config["commands_aliases"][command]:
150 151
            return command

152 153

def main(args):
Martin Potthast's avatar
Martin Potthast committed
154
    config = load_config()
155 156
    check_commands_alias_definition(config)

Martin Potthast's avatar
Martin Potthast committed
157 158
    commands = get_commands(config)
    parser = argparse.ArgumentParser()
159
    parser.register('action', 'parsers', AliasedSubParsersAction)
160 161 162
    parser.add_argument('-H', '--helptable',
                        help='Print help of commands and subcommands',
                        action='store_true')
163 164
    parser.add_argument('-v', '--verbose', help='Print debug messages.',
                        action='store_true')
165

Martin Potthast's avatar
Martin Potthast committed
166 167 168 169 170
    subparsers = {}
    allcommands = {}
    commandparsers = parser.add_subparsers(
        metavar='<command>', dest='<command>', help='Available commands:')
    for command in commands:
171 172 173 174 175 176
        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
177
        commandparser = commandparsers.add_parser(
178 179 180
            command, aliases=config["commands_aliases"].get(command, []),
            help=help_msg)

Martin Potthast's avatar
Martin Potthast committed
181 182 183 184 185 186 187 188 189
        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
190
                    get_subcommand_help(subcommands[subcommand])),
Martin Potthast's avatar
Martin Potthast committed
191 192 193 194 195
                add_help=False)
            subparsers[subcommand] = subcommandparser

    if len(args) == 0:
        parser.print_help()
196 197
        return 0

Martin Potthast's avatar
Martin Potthast committed
198 199
    parseresults = parser.parse_known_args(args)
    argsdict = vars(parseresults[0])
200
    set_verbose_logging(argsdict['verbose'])
Martin Potthast's avatar
Martin Potthast committed
201
    parameter = parseresults[1]
Steve Goering's avatar
Steve Goering committed
202

203 204 205 206
    if argsdict['helptable']:
        print_help_table(config)
        return 0

Steve Goering's avatar
Steve Goering committed
207
    if argsdict['<command>'] not in subparsers:
Steve Goering's avatar
Steve Goering committed
208 209
        argsdict['<command>'] = translate_alias_to_command(
            config, argsdict['<command>'])
210

Steve Goering's avatar
Steve Goering committed
211
    if len(argsdict) == 2 and argsdict['<subcommand>'] is None:
Steve Goering's avatar
Steve Goering committed
212
        subparsers[argsdict['<command>']].print_help()
Martin Potthast's avatar
Martin Potthast committed
213
        return 0
214

Steve Goering's avatar
Steve Goering committed
215 216 217 218
    return run_subcommand(
        argsdict['<subcommand>'],
        allcommands[argsdict['<command>']][argsdict['<subcommand>']],
        parameter)
219 220 221


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