webis.py 6.38 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:
Martin Potthast's avatar
Martin Potthast committed
125
        lInfo("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 145
    parser.register('action', 'parsers', AliasedSubParsersAction)

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

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

    if len(args) == 0:
        parser.print_help()
176 177
        return 0

Martin Potthast's avatar
Martin Potthast committed
178 179 180
    parseresults = parser.parse_known_args(args)
    argsdict = vars(parseresults[0])
    parameter = parseresults[1]
Steve Goering's avatar
Steve Goering committed
181

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

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

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


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