webis.py 5.45 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 36 37 38 39 40
def load_config():
    """Returns the configuration, based on the JSON configuration file."""
    try:
        return json.loads(read_file(os.path.dirname(
            os.path.realpath(__file__)) + "/config.json"))
    except Exception as e:
        lError("The file config.json is invalid.")
        sys.exit(1)
41

Steve Goering's avatar
Steve Goering committed
42

Martin Potthast's avatar
Martin Potthast committed
43 44 45
def get_commands(config):
    """Returns commands mapped to directories containing their subcommands."""
    commands = collections.OrderedDict()
46 47
    for directory in os.listdir(config["commands_directory"]):
        if (os.path.isdir(config["commands_directory"] + directory) and
Steve Goering's avatar
Steve Goering committed
48
                directory not in config["ignored_dirs"]):
49
            commands[directory] = config["commands_directory"] + directory
Martin Potthast's avatar
Martin Potthast committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
    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)
72 73 74 75 76 77 78
    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
79 80 81 82 83 84 85
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
86

87

Martin Potthast's avatar
Martin Potthast committed
88 89 90 91 92 93
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)
94 95
    return_value = os.system(cmd)
    if return_value == 0:
Martin Potthast's avatar
Martin Potthast committed
96
        lInfo("Done.")
97
    else:
Martin Potthast's avatar
Martin Potthast committed
98
        lError("An error occurred while executing " + cmd)
99
    return return_value
100

101 102 103 104 105 106
def translate_alias_to_command(config, alias):
    """Transforms a given alias to the unique command."""
    for command in config["commands_aliases"]:
        if alias in config["commands_aliases"][cmd]:
            return command

107 108

def main(args):
Martin Potthast's avatar
Martin Potthast committed
109
    config = load_config()
110 111
    check_commands_alias_definition(config)

Martin Potthast's avatar
Martin Potthast committed
112 113
    commands = get_commands(config)
    parser = argparse.ArgumentParser()
114 115
    parser.register('action', 'parsers', AliasedSubParsersAction)

Martin Potthast's avatar
Martin Potthast committed
116 117 118 119 120
    subparsers = {}
    allcommands = {}
    commandparsers = parser.add_subparsers(
        metavar='<command>', dest='<command>', help='Available commands:')
    for command in commands:
121 122 123 124 125 126
        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
127
        commandparser = commandparsers.add_parser(
128 129 130
            command, aliases=config["commands_aliases"].get(command, []),
            help=help_msg)

Martin Potthast's avatar
Martin Potthast committed
131 132 133 134 135 136 137 138 139
        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
140
                    get_subcommand_help(subcommands[subcommand])),
Martin Potthast's avatar
Martin Potthast committed
141 142 143 144 145
                add_help=False)
            subparsers[subcommand] = subcommandparser

    if len(args) == 0:
        parser.print_help()
146 147
        return 0

Martin Potthast's avatar
Martin Potthast committed
148 149 150
    parseresults = parser.parse_known_args(args)
    argsdict = vars(parseresults[0])
    parameter = parseresults[1]
Steve Goering's avatar
Steve Goering committed
151 152

    if len(argsdict) == 2 and argsdict['<subcommand>'] is None:
153 154 155 156 157
        command = argsdict['<command>']
        if command not in subparsers:
            command = translate_alias_to_command(command)

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

Steve Goering's avatar
Steve Goering committed
160 161 162 163
    return run_subcommand(
        argsdict['<subcommand>'],
        allcommands[argsdict['<command>']][argsdict['<subcommand>']],
        parameter)
164 165 166 167


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