summaryrefslogtreecommitdiffstats
path: root/deluge/ui/console/cmdline/commands/config.py
blob: bd0a1e15f93d961fd3f0fcd826879b6639d6ccfe (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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
# Copyright (C) 2009 Andrew Resch <andrewresch@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.
#

from __future__ import unicode_literals

import logging
import tokenize
from io import StringIO

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

from . import BaseCommand

log = logging.getLogger(__name__)


def atom(src, token):
    """taken with slight modifications from http://effbot.org/zone/simple-iterator-parser.htm"""
    if token[1] == '(':
        out = []
        token = next(src)
        while token[1] != ')':
            out.append(atom(src, token))
            token = next(src)
            if token[1] == ',':
                token = next(src)
        return tuple(out)
    elif token[0] is tokenize.NUMBER or token[1] == '-':
        try:
            if token[1] == '-':
                return int(token[-1], 0)
            else:
                if token[1].startswith('0x'):
                    # Hex number so return unconverted as string.
                    return token[1].decode('string-escape')
                else:
                    return int(token[1], 0)
        except ValueError:
            try:
                return float(token[-1])
            except ValueError:
                return str(token[-1])
    elif token[1].lower() == 'true':
        return True
    elif token[1].lower() == 'false':
        return False
    elif token[0] is tokenize.STRING or token[1] == '/':
        return token[-1].decode('string-escape')
    elif token[1].isalpha():
        # Parse Windows paths e.g. 'C:\\xyz' or 'C:/xyz'.
        if next()[1] == ':' and next()[1] in '\\/':
            return token[-1].decode('string-escape')

    raise SyntaxError('malformed expression (%s)' % token[1])


def simple_eval(source):
    """ evaluates the 'source' string into a combination of primitive python objects
    taken from http://effbot.org/zone/simple-iterator-parser.htm"""
    src = StringIO(source).readline
    src = tokenize.generate_tokens(src)
    src = (token for token in src if token[0] is not tokenize.NL)
    res = atom(src, next(src))
    return res


class Command(BaseCommand):
    """Show and set configuration values"""

    usage = _('Usage: config [--set <key> <value>] [<key> [<key>...] ]')

    def add_arguments(self, parser):
        set_group = parser.add_argument_group('setting a value')
        set_group.add_argument(
            '-s',
            '--set',
            action='store',
            metavar='<key>',
            help=_('set value for this key'),
        )
        set_group.add_argument(
            'values', metavar='<value>', nargs='+', help=_('Value to set')
        )
        get_group = parser.add_argument_group('getting values')
        get_group.add_argument(
            'keys',
            metavar='<keys>',
            nargs='*',
            help=_('one or more keys separated by space'),
        )

    def handle(self, options):
        self.console = component.get('ConsoleUI')
        if options.set:
            return self._set_config(options)
        else:
            return self._get_config(options)

    def _get_config(self, options):
        def _on_get_config(config):
            string = ''
            for key in sorted(config):
                if key not in options.values:
                    continue

                color = '{!white,black,bold!}'
                value = config[key]
                try:
                    color = colors.type_color[type(value)]
                except KeyError:
                    pass

                # We need to format dicts for printing
                if isinstance(value, dict):
                    import pprint

                    value = pprint.pformat(value, 2, 80)
                    new_value = []
                    for line in value.splitlines():
                        new_value.append('%s%s' % (color, line))
                    value = '\n'.join(new_value)

                string += '%s: %s%s\n' % (key, color, value)
            self.console.write(string.strip())

        return client.core.get_config().addCallback(_on_get_config)

    def _set_config(self, options):
        config = component.get('CoreConfig')
        key = options.set
        val = ' '.join(options.values)

        try:
            val = simple_eval(val)
        except SyntaxError as ex:
            self.console.write('{!error!}%s' % ex)
            return

        if key not in config:
            self.console.write('{!error!}Invalid key: %s' % key)
            return

        if not isinstance(config[key], type(val)):
            try:
                val = type(config[key])(val)
            except TypeError:
                self.config.write(
                    '{!error!}Configuration value provided has incorrect type.'
                )
                return

        def on_set_config(result):
            self.console.write('{!success!}Configuration value successfully updated.')

        self.console.write('Setting "%s" to: %s' % (key, val))
        return client.core.set_config({key: val}).addCallback(on_set_config)

    def complete(self, text):
        return [k for k in component.get('CoreConfig') if k.startswith(text)]