summaryrefslogtreecommitdiffstats
path: root/deluge/ui/console/parser.py
blob: c0686b1565b7905e50cfb6c5138e18665f2adebb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#
# Copyright (C) 2016 bendikro <bro.devel+deluge@gmail.com>
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#

import argparse
import shlex

import deluge.component as component
from deluge.ui.console.utils.colors import ConsoleColorFormatter


class OptionParserError(Exception):
    pass


class ConsoleBaseParser(argparse.ArgumentParser):
    def format_help(self):
        """Differs from ArgumentParser.format_help by adding the raw epilog
        as formatted in the string. Default behavior mangles the formatting.

        """
        # Handle epilog manually to keep the text formatting
        epilog = self.epilog
        self.epilog = ''
        help_str = super().format_help()
        if epilog is not None:
            help_str += epilog
        self.epilog = epilog
        return help_str


class ConsoleCommandParser(ConsoleBaseParser):
    def _split_args(self, args):
        command_options = []
        for a in args:
            if not a:
                continue
            if ';' in a:
                cmd_lines = [arg.strip() for arg in a.split(';')]
            elif ' ' in a:
                cmd_lines = [a]
            else:
                continue

            for cmd_line in cmd_lines:
                cmds = shlex.split(cmd_line)
                cmd_options = super().parse_args(args=cmds)
                cmd_options.command = cmds[0]
                command_options.append(cmd_options)

        return command_options

    def parse_args(self, args=None):
        """Parse known UI args and handle common and process group options.

        Notes:
            If started by deluge entry script this has already been done.

        Args:
            args (list, optional): The arguments to parse.

        Returns:
            argparse.Namespace: The parsed arguments.
        """
        from deluge.ui.ui_entry import AMBIGUOUS_CMD_ARGS

        self.base_parser.parse_known_ui_args(args, withhold=AMBIGUOUS_CMD_ARGS)

        multi_command = self._split_args(args)
        # If multiple commands were passed to console
        if multi_command:
            # With multiple commands, normal parsing will fail, so only parse
            # known arguments using the base parser, and then set
            # options.parsed_cmds to the already parsed commands
            options, remaining = self.base_parser.parse_known_args(args=args)
            options.parsed_cmds = multi_command
        else:
            subcommand = False
            if hasattr(self.base_parser, 'subcommand'):
                subcommand = getattr(self.base_parser, 'subcommand')
            if not subcommand:
                # We must use parse_known_args to handle case when no subcommand
                # is provided, because argparse does not support parsing without
                # a subcommand
                options, remaining = self.base_parser.parse_known_args(args=args)
                # If any options remain it means they do not exist. Reparse with
                # parse_args to trigger help message
                if remaining:
                    options = self.base_parser.parse_args(args=args)
                options.parsed_cmds = []
            else:
                options = super().parse_args(args=args)
                options.parsed_cmds = [options]

        if not hasattr(options, 'remaining'):
            options.remaining = []

        return options


class OptionParser(ConsoleBaseParser):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.formatter = ConsoleColorFormatter()

    def exit(self, status=0, msg=None):
        self._exit = True
        if msg:
            print(msg)

    def error(self, msg):
        """error(msg : string)

        Print a usage message incorporating 'msg' to stderr and exit.
        If you override this in a subclass, it should not return -- it
        should either exit or raise an exception.
        """
        raise OptionParserError(msg)

    def print_usage(self, _file=None):
        console = component.get('ConsoleUI')
        if self.usage:
            for line in self.format_usage().splitlines():
                console.write(line)

    def print_help(self, _file=None):
        console = component.get('ConsoleUI')
        console.set_batch_write(True)
        for line in self.format_help().splitlines():
            console.write(line)
        console.set_batch_write(False)

    def format_help(self):
        """Return help formatted with colors."""
        help_str = super().format_help()
        return self.formatter.format_colors(help_str)