Move config parsing into cli module

This commit is contained in:
Markus Unterwaditzer 2014-12-26 00:58:41 +01:00
parent 6ef330aac5
commit cdb25d61ec
4 changed files with 111 additions and 106 deletions

View file

@ -435,3 +435,47 @@ def test_invalid_collections_arg(tmpdir, runner):
'Section `pair foobar`: `collections` parameter must be a list of '
'collection names (strings!) or `null`.'
)
def test_parse_config_value():
x = cli.utils.parse_config_value
with pytest.raises(ValueError):
x('123 # comment!')
assert x('"123 # comment!"') == '123 # comment!'
assert x('True') is True
assert x('False') is False
assert x('Yes') is True
assert x('3.14') == 3.14
assert x('') == ''
assert x('""') == ''
def test_parse_options():
o = {
'foo': 'yes',
'hah': 'true',
'bar': '',
'baz': 'whatever',
'bam': '123',
'asd': 'off'
}
a = dict(cli.utils.parse_options(o.items()))
expected = {
'foo': True,
'hah': True,
'bar': '',
'baz': 'whatever',
'bam': 123,
'asd': False
}
assert a == expected
for key in a:
# Yes, we want a very strong typecheck here, because we actually have
# to differentiate between bool and int, and in Python 2, bool is a
# subclass of int.
assert type(a[key]) is type(expected[key]) # noqa

View file

@ -7,28 +7,30 @@
:license: MIT, see LICENSE for more details.
'''
import click
import pytest
from click.testing import CliRunner
import os
import stat
import click
from click.testing import CliRunner
import pytest
import requests
import vdirsyncer.utils as utils
import vdirsyncer.doubleclick as doubleclick
from vdirsyncer.utils.vobject import split_collection
import vdirsyncer.utils as utils
from .. import blow_up, normalize_item, SIMPLE_TEMPLATE, BARE_EVENT_TEMPLATE
from .. import blow_up
class EmptyNetrc(object):
def __init__(self, file=None):
self._file = file
def authenticators(self, hostname):
return None
class EmptyKeyring(object):
def get_password(self, *a, **kw):
return None
@ -40,49 +42,6 @@ def empty_password_storages(monkeypatch):
monkeypatch.setattr(utils, 'keyring', EmptyKeyring())
def test_parse_options():
o = {
'foo': 'yes',
'hah': 'true',
'bar': '',
'baz': 'whatever',
'bam': '123',
'asd': 'off'
}
a = dict(utils.parse_options(o.items()))
expected = {
'foo': True,
'hah': True,
'bar': '',
'baz': 'whatever',
'bam': 123,
'asd': False
}
assert a == expected
for key in a:
# Yes, we want a very strong typecheck here, because we actually have
# to differentiate between bool and int, and in Python 2, bool is a
# subclass of int.
assert type(a[key]) is type(expected[key]) # flake8: noqa
def test_parse_config_value():
with pytest.raises(ValueError):
utils.parse_config_value('123 # comment!')
assert utils.parse_config_value('"123 # comment!"') == '123 # comment!'
assert utils.parse_config_value('True') is True
assert utils.parse_config_value('False') is False
assert utils.parse_config_value('Yes') is True
assert utils.parse_config_value('3.14') == 3.14
assert utils.parse_config_value('') == ''
assert utils.parse_config_value('""') == ''
def test_get_password_from_netrc(monkeypatch):
username = 'foouser'
password = 'foopass'
@ -133,9 +92,9 @@ def test_get_password_from_command(tmpdir):
filepath = str(tmpdir) + '/' + filename
f = open(filepath, 'w')
f.write('#!/bin/sh\n'
'[ "$1" != "my_username" ] && exit 1\n'
'[ "$2" != "example.com" ] && exit 1\n'
'echo "{}"'.format(password))
'[ "$1" != "my_username" ] && exit 1\n'
'[ "$2" != "example.com" ] && exit 1\n'
'echo "{}"'.format(password))
f.close()
st = os.stat(filepath)
@ -144,7 +103,7 @@ def test_get_password_from_command(tmpdir):
@doubleclick.click.command()
@doubleclick.click.pass_context
def fake_app(ctx):
ctx.obj = {'config' : ({'password_command' : filepath},{},{})}
ctx.obj = {'config': ({'password_command': filepath}, {}, {})}
_password = utils.get_password(username, resource)
assert _password == password
@ -154,8 +113,6 @@ def test_get_password_from_command(tmpdir):
def test_get_password_from_prompt():
getpass_calls = []
user = 'my_user'
resource = 'http://example.com'

View file

@ -20,8 +20,7 @@ from .. import DOCS_HOME, PROJECT_HOME, log
from ..doubleclick import click
from ..storage import storage_names
from ..sync import StorageEmpty, SyncConflict
from ..utils import expand_path, get_class_init_args, parse_options, \
safe_write
from ..utils import expand_path, get_class_init_args, safe_write
from ..utils.compat import text_type
@ -465,3 +464,56 @@ class WorkerQueue(object):
def put(self, f):
return self._queue.put(f)
def parse_config_value(value):
try:
return json.loads(value)
except ValueError:
rv = value
if value.lower() in ('on', 'true', 'yes'):
cli_logger.warning(
'{} is deprecated for the config, please use true.\n'
'The old form will be removed in 0.4.0.'
.format(value)
)
return True
if value.lower() in ('off', 'false', 'no'):
cli_logger.warning(
'{} is deprecated for the config, please use false.\n'
'The old form will be removed in 0.4.0.'
.format(value)
)
return False
if value.lower() == 'none':
cli_logger.warning(
'None is deprecated for the config, please use null.\n'
'The old form will be removed in 0.4.0.'
)
return None
if '#' in value:
raise ValueError('Invalid value:{}\n'
'Use double quotes (") if you want to use hashes in '
'your value.')
if len(value.splitlines()) > 1:
# ConfigParser's barrier for mistaking an arbitrary line for the
# continuation of a value is awfully low. The following example will
# also contain the second line in the value:
#
# foo = bar
# # my comment
raise ValueError('No multiline-values allowed:\n{!r}'.format(value))
return rv
def parse_options(items, section=None):
for key, value in items:
try:
yield key, parse_config_value(value)
except ValueError as e:
raise ValueError('Section {!r}, option {!r}: {}'
.format(section, key, e))

View file

@ -7,7 +7,6 @@
:license: MIT, see LICENSE for more details.
'''
import json
import os
import threading
@ -68,53 +67,6 @@ def uniq(s):
yield x
def parse_config_value(value):
try:
return json.loads(value)
except ValueError:
rv = value
if value.lower() in ('on', 'true', 'yes'):
logger.warning('{} is deprecated for the config, please use true.\n'
'The old form will be removed in 0.4.0.'
.format(value))
return True
if value.lower() in ('off', 'false', 'no'):
logger.warning('{} is deprecated for the config, please use false.\n'
'The old form will be removed in 0.4.0.'
.format(value))
return False
if value.lower() == 'none':
logger.warning('None is deprecated for the config, please use null.\n'
'The old form will be removed in 0.4.0.')
return None
if '#' in value:
raise ValueError('Invalid value:{}\n'
'Use double quotes (") if you want to use hashes in '
'your value.')
if len(value.splitlines()) > 1:
# ConfigParser's barrier for mistaking an arbitrary line for the
# continuation of a value is awfully low. The following example will
# also contain the second line in the value:
#
# foo = bar
# # my comment
raise ValueError('No multiline-values allowed:\n{!r}'.format(value))
return rv
def parse_options(items, section=None):
for key, value in items:
try:
yield key, parse_config_value(value)
except ValueError as e:
raise ValueError('Section {!r}, option {!r}: {}'
.format(section, key, e))
def get_password(username, resource, _lock=threading.Lock()):
"""tries to access saved password or asks user for it