webis.py 6 KB
Newer Older
Janek Bevendorff's avatar
Janek Bevendorff committed
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
Martin Potthast's avatar
Martin Potthast committed
3
"""The main webis command.
Steve Goering's avatar
Steve Goering committed
4

Martin Potthast's avatar
Martin Potthast committed
5 6
Copyright webis.de 2015-today
Authors: Steve Göring, Martin Potthast
Steve Goering's avatar
Steve Goering committed
7
"""
Martin Potthast's avatar
Martin Potthast committed
8

9
import argparse
Martin Potthast's avatar
Martin Potthast committed
10
import collections
11
import json
Martin Potthast's avatar
Martin Potthast committed
12 13
import os
import sys
14

Steve Goering's avatar
Steve Goering committed
15 16
import loader

17
from lib import *
Martin Potthast's avatar
Martin Potthast committed
18
from log import *
19 20 21
from system import *


22 23 24 25 26 27 28
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
29
    if len([x for x in aliashisto if aliashisto[x] > 1]):
30 31 32 33
        lError("Commands alias definition has collisions, please fix it.")
        sys.exit(1)


Martin Potthast's avatar
Martin Potthast committed
34 35 36
def load_config():
    """Returns the configuration, based on the JSON configuration file."""
    try:
37
        config = json.loads(read_file(os.path.dirname(
Martin Potthast's avatar
Martin Potthast committed
38
            os.path.realpath(__file__)) + "/config.json"))
39 40 41 42 43 44 45 46 47 48 49 50 51

        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
52 53 54
    except Exception as e:
        lError("The file config.json is invalid.")
        sys.exit(1)
55

Steve Goering's avatar
Steve Goering committed
56

Martin Potthast's avatar
Martin Potthast committed
57 58 59
def get_commands(config):
    """Returns commands mapped to directories containing their subcommands."""
    commands = collections.OrderedDict()
60 61
    for directory in os.listdir(config["commands_directory"]):
        if (os.path.isdir(config["commands_directory"] + directory) and
Steve Goering's avatar
Steve Goering committed
62
                directory not in config["ignored_dirs"]):
63
            commands[directory] = config["commands_directory"] + directory
Martin Potthast's avatar
Martin Potthast committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
    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)
86 87 88 89 90 91 92
    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
93 94 95 96 97 98 99
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
100

101

Martin Potthast's avatar
Martin Potthast committed
102 103 104
def run_subcommand(subcommand, subcommandpath, params=[]):
    """Runs subcommand passing params and returns its exit code."""
    lDbg("Running " + subcommand + " at " + subcommandpath)
105 106 107
    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
108
    cmd = " ".join([subcommandpath] + params)
109 110
    return_value = os.system(cmd)
    if return_value == 0:
Martin Potthast's avatar
Martin Potthast committed
111
        lInfo("Done.")
112
    else:
Martin Potthast's avatar
Martin Potthast committed
113
        lError("An error occurred while executing " + cmd)
114
    return return_value
115

116

117 118 119
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
120
        if alias in config["commands_aliases"][command]:
121 122
            return command

123 124

def main(args):
Martin Potthast's avatar
Martin Potthast committed
125
    config = load_config()
126 127
    check_commands_alias_definition(config)

Martin Potthast's avatar
Martin Potthast committed
128 129
    commands = get_commands(config)
    parser = argparse.ArgumentParser()
130 131
    parser.register('action', 'parsers', AliasedSubParsersAction)

Martin Potthast's avatar
Martin Potthast committed
132 133 134 135 136
    subparsers = {}
    allcommands = {}
    commandparsers = parser.add_subparsers(
        metavar='<command>', dest='<command>', help='Available commands:')
    for command in commands:
137 138 139 140 141 142
        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
143
        commandparser = commandparsers.add_parser(
144 145 146
            command, aliases=config["commands_aliases"].get(command, []),
            help=help_msg)

Martin Potthast's avatar
Martin Potthast committed
147 148 149 150 151 152 153 154 155
        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
156
                    get_subcommand_help(subcommands[subcommand])),
Martin Potthast's avatar
Martin Potthast committed
157 158 159 160 161
                add_help=False)
            subparsers[subcommand] = subcommandparser

    if len(args) == 0:
        parser.print_help()
162 163
        return 0

Martin Potthast's avatar
Martin Potthast committed
164 165 166
    parseresults = parser.parse_known_args(args)
    argsdict = vars(parseresults[0])
    parameter = parseresults[1]
Steve Goering's avatar
Steve Goering committed
167

Steve Goering's avatar
Steve Goering committed
168
    if argsdict['<command>'] not in subparsers:
Steve Goering's avatar
Steve Goering committed
169 170
        argsdict['<command>'] = translate_alias_to_command(
            config, argsdict['<command>'])
171

Steve Goering's avatar
Steve Goering committed
172
    if len(argsdict) == 2 and argsdict['<subcommand>'] is None:
Steve Goering's avatar
Steve Goering committed
173
        subparsers[argsdict['<command>']].print_help()
Martin Potthast's avatar
Martin Potthast committed
174
        return 0
175

Steve Goering's avatar
Steve Goering committed
176 177 178 179
    return run_subcommand(
        argsdict['<subcommand>'],
        allcommands[argsdict['<command>']][argsdict['<subcommand>']],
        parameter)
180 181 182


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