webis.py 6.6 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
122 123
    if is_verbose_logging():
        os.environ['WEBIS_DEBUG'] = '1'
Martin Potthast's avatar
Martin Potthast committed
124
    cmd = " ".join([subcommandpath] + params)
125 126
    return_value = os.system(cmd)
    if return_value == 0:
127
        lDbg("Done.")
128
    else:
Martin Potthast's avatar
Martin Potthast committed
129
        lError("An error occurred while executing " + cmd)
130
    return return_value
131

132

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

139 140

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

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

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

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

    if len(args) == 0:
        parser.print_help()
180 181
        return 0

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

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

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

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


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