From 0a44163d82f9f6f71001822fdcc2252cf96d6bd9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 8 Apr 2017 11:32:44 +0530 Subject: [PATCH] Read and write CS settings from a file --- .../devices/smart_device_app/driver.py | 5 +- src/calibre/gui2/wizard/__init__.py | 6 +- src/calibre/srv/opts.py | 74 ++++++++++++++++++- 3 files changed, 77 insertions(+), 8 deletions(-) diff --git a/src/calibre/devices/smart_device_app/driver.py b/src/calibre/devices/smart_device_app/driver.py index 3ebb09a4b3..b137728ff1 100644 --- a/src/calibre/devices/smart_device_app/driver.py +++ b/src/calibre/devices/smart_device_app/driver.py @@ -114,9 +114,8 @@ class ConnectionListener(Thread): remote = packet[1] content_server_port = b'' try: - from calibre.library.server import server_config as content_server_config - content_server_port = \ - str(content_server_config().parse().port) + from calibre.srv.opts import server_config + content_server_port = str(server_config().port) except Exception: pass message = str(self.driver.ZEROCONF_CLIENT_STRING + b' (on ' + diff --git a/src/calibre/gui2/wizard/__init__.py b/src/calibre/gui2/wizard/__init__.py index 57775ffb94..95a07a156a 100644 --- a/src/calibre/gui2/wizard/__init__.py +++ b/src/calibre/gui2/wizard/__init__.py @@ -559,9 +559,8 @@ class StanzaPage(QWizardPage, StanzaUI): def commit(self): p = self.set_port() if p is not None: - from calibre.library.server import server_config - c = server_config() - c.set('port', p) + from calibre.srv.opts import change_settings + change_settings(port=p) def set_port(self, *args): if not self.content_server.isChecked(): @@ -895,6 +894,7 @@ def wizard(parent=None): w = Wizard(parent) return w + if __name__ == '__main__': from calibre.gui2 import Application app = Application([]) diff --git a/src/calibre/srv/opts.py b/src/calibre/srv/opts.py index cdb003ea1c..8233607546 100644 --- a/src/calibre/srv/opts.py +++ b/src/calibre/srv/opts.py @@ -6,11 +6,15 @@ from __future__ import (unicode_literals, division, absolute_import, __license__ = 'GPL v3' __copyright__ = '2015, Kovid Goyal ' +import errno, os from itertools import izip_longest from collections import namedtuple, OrderedDict from operator import attrgetter from functools import partial +from calibre.constants import config_dir +from calibre.utils.lock import ExclusiveFile + Option = namedtuple('Option', 'name default longdoc shortdoc choices') @@ -21,6 +25,7 @@ class Choices(frozenset): self.default = args[0] return self + raw_options = ( _('Path to the SSL certificate file'), @@ -133,8 +138,7 @@ raw_options = ( _('Path to a file in which to store the user and password information. By default a' ' file in the calibre configuration directory is used.'), - _('Choose the type of authentication used'), - 'auth_mode', Choices('auto', 'basic', 'digest'), + _('Choose the type of authentication used'), 'auth_mode', Choices('auto', 'basic', 'digest'), _('Set the HTTP authentication mode used by the server. Set to "basic" if you are' ' putting this server behind an SSL proxy. Otherwise, leave it as "auto", which' ' will use "basic" if SSL is configured otherwise it will use "digest".'), @@ -162,6 +166,7 @@ def grouper(n, iterable, fillvalue=None): args = [iter(iterable)] * n return izip_longest(*args, fillvalue=fillvalue) + for shortdoc, name, default, doc in grouper(4, raw_options): choices = None if isinstance(default, Choices): @@ -215,3 +220,68 @@ def opts_to_parser(usage): add_option(name, type=otype) return parser + + +DEFAULT_CONFIG = os.path.join(config_dir, 'server-config.txt') + + +def parse_config_file(path=DEFAULT_CONFIG): + try: + with ExclusiveFile(path) as f: + raw = f.read().decode('utf-8') + except EnvironmentError as err: + if err.errno != errno.ENOENT: + raise + raw = '' + ans = {} + for line in raw.splitlines(): + line = line.strip() + if line.startswith('#'): + continue + key, rest = line.partition(' ')[::2] + opt = options.get(key) + if opt is None: + continue + val = rest + if isinstance(opt.default, (int, long, float)): + try: + val = type(opt.default)(rest) + except Exception: + raise ValueError('The value for %s: %s is not a valid number' % (key, rest)) + elif opt.choices: + if rest not in opt.choices: + raise ValueError('The value for %s: %s is not valid' % (key, rest)) + ans[key] = val + return Options(**ans) + + +def write_config_file(opts, path=DEFAULT_CONFIG): + changed = {name:getattr(opts, name) for name in options if getattr(opts, name) != options[name].default} + lines = [] + for name in sorted(changed): + o = options[name] + lines.append('# ' + o.shortdoc) + if o.longdoc: + lines.append('# ' + o.longdoc) + lines.append('%s %s' % (name, changed[name])) + raw = '\n'.join(lines).encode('utf-8') + with ExclusiveFile(path) as f: + f.write(raw) + + +def server_config(refresh=False): + if refresh or not hasattr(server_config, 'ans'): + server_config.ans = parse_config_file() + return server_config.ans + + +def change_settings(**kwds): + new_opts = {} + opts = server_config() + for name in options: + if name in kwds: + new_opts[name] = kwds[name] + else: + new_opts[name] = getattr(opts, name) + new_opts = server_config.ans = Options(**new_opts) + write_config_file(new_opts)