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

113

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

120 121

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

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

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

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

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

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

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

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

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


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