webis.py 6.54 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()


Martin Potthast's avatar
Martin Potthast committed
107 108 109 110 111 112 113
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
114

115

Martin Potthast's avatar
Martin Potthast committed
116 117 118
def run_subcommand(subcommand, subcommandpath, params=[]):
    """Runs subcommand passing params and returns its exit code."""
    lDbg("Running " + subcommand + " at " + subcommandpath)
119 120 121
    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
Martin Potthast's avatar
Martin Potthast committed
122
    cmd = " ".join([subcommandpath] + params)
123 124
    return_value = os.system(cmd)
    if return_value == 0:
125
        lDbg("Done.")
126
    else:
Martin Potthast's avatar
Martin Potthast committed
127
        lError("An error occurred while executing " + cmd)
128
    return return_value
129

130

131 132 133
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
134
        if alias in config["commands_aliases"][command]:
135 136
            return command

137 138

def main(args):
Martin Potthast's avatar
Martin Potthast committed
139
    config = load_config()
140 141
    check_commands_alias_definition(config)

Martin Potthast's avatar
Martin Potthast committed
142 143
    commands = get_commands(config)
    parser = argparse.ArgumentParser()
144
    parser.register('action', 'parsers', AliasedSubParsersAction)
145 146
    parser.add_argument('-v', '--verbose', help='Print debug messages.',
                        action='store_true')
147

Martin Potthast's avatar
Martin Potthast committed
148 149 150 151 152
    subparsers = {}
    allcommands = {}
    commandparsers = parser.add_subparsers(
        metavar='<command>', dest='<command>', help='Available commands:')
    for command in commands:
153 154 155 156 157 158
        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
159
        commandparser = commandparsers.add_parser(
160 161 162
            command, aliases=config["commands_aliases"].get(command, []),
            help=help_msg)

Martin Potthast's avatar
Martin Potthast committed
163 164 165 166 167 168 169 170 171
        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
172
                    get_subcommand_help(subcommands[subcommand])),
Martin Potthast's avatar
Martin Potthast committed
173 174 175 176 177
                add_help=False)
            subparsers[subcommand] = subcommandparser

    if len(args) == 0:
        parser.print_help()
178 179
        return 0

Martin Potthast's avatar
Martin Potthast committed
180 181
    parseresults = parser.parse_known_args(args)
    argsdict = vars(parseresults[0])
182
    set_verbose_logging(argsdict['verbose'])
Martin Potthast's avatar
Martin Potthast committed
183
    parameter = parseresults[1]
Steve Goering's avatar
Steve Goering committed
184

Steve Goering's avatar
Steve Goering committed
185
    if argsdict['<command>'] not in subparsers:
Steve Goering's avatar
Steve Goering committed
186 187
        argsdict['<command>'] = translate_alias_to_command(
            config, argsdict['<command>'])
188

Steve Goering's avatar
Steve Goering committed
189
    if len(argsdict) == 2 and argsdict['<subcommand>'] is None:
Steve Goering's avatar
Steve Goering committed
190
        subparsers[argsdict['<command>']].print_help()
Martin Potthast's avatar
Martin Potthast committed
191
        return 0
192

Steve Goering's avatar
Steve Goering committed
193 194 195 196
    return run_subcommand(
        argsdict['<subcommand>'],
        allcommands[argsdict['<command>']][argsdict['<subcommand>']],
        parameter)
197 198 199


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