webis.py 5.92 KB
Newer Older
1
#!/usr/bin/env python3
Martin Potthast's avatar
Martin Potthast committed
2
"""The main webis command.
Steve Goering's avatar
Steve Goering committed
3

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

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

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

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


21 22 23 24 25 26 27 28 29 30 31 32
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

    if len(config["commands_aliases"].keys()) != len(aliashisto.keys()):
        lError("Commands alias definition has collisions, please fix it.")
        sys.exit(1)


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

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

Steve Goering's avatar
Steve Goering committed
55

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

100

Martin Potthast's avatar
Martin Potthast committed
101 102 103 104 105 106
def run_subcommand(subcommand, subcommandpath, params=[]):
    """Runs subcommand passing params and returns its exit code."""
    lDbg("Running " + subcommand + " at " + subcommandpath)
    # FIXME: When the script is installed at /usr/lib, this won't work, anymore:
    os.system("chmod +x {}".format(subcommandpath))
    cmd = " ".join([subcommandpath] + params)
107 108
    return_value = os.system(cmd)
    if return_value == 0:
Martin Potthast's avatar
Martin Potthast committed
109
        lInfo("Done.")
110
    else:
Martin Potthast's avatar
Martin Potthast committed
111
        lError("An error occurred while executing " + cmd)
112
    return return_value
113

114

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

121 122

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

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

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

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

    if len(args) == 0:
        parser.print_help()
160 161
        return 0

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

    if len(argsdict) == 2 and argsdict['<subcommand>'] is None:
167 168
        command = argsdict['<command>']
        if command not in subparsers:
Steve Goering's avatar
Steve Goering committed
169
            command = translate_alias_to_command(config, command)
170 171

        subparsers[command].print_help()
Martin Potthast's avatar
Martin Potthast committed
172
        return 0
173

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


if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))