mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-03-25 08:55:50 +00:00
New config format
See #141 Basically this tries to parse config values with JSON, if that fails, the value is interpreted as string. I'd greatly appreciate feedback on this and #141
This commit is contained in:
parent
566a988f32
commit
08c07c4be4
7 changed files with 76 additions and 39 deletions
|
|
@ -2,6 +2,18 @@
|
|||
Configuration
|
||||
=============
|
||||
|
||||
Vdirsyncer uses an ini-like format for storing its configuration. All values
|
||||
are JSON, invalid JSON will get interpreted as string::
|
||||
|
||||
"foo"
|
||||
foo # Same as "foo"
|
||||
42
|
||||
["a", "b", "c"]
|
||||
[a, b, c] # This doesn't work though!
|
||||
true
|
||||
false
|
||||
null # Also known as None
|
||||
|
||||
|
||||
.. _general_config:
|
||||
|
||||
|
|
@ -46,11 +58,11 @@ Pair Section
|
|||
already directly pointing to one collection each. Specifying a collection
|
||||
multiple times won't make vdirsyncer sync that collection more than once.
|
||||
|
||||
Furthermore, there are the special values ``from a`` and ``from b``, which
|
||||
tell vdirsyncer to try autodiscovery on a specific storage::
|
||||
Furthermore, there are the special values ``"from a"`` and ``"from b"``,
|
||||
which tell vdirsyncer to try autodiscovery on a specific storage::
|
||||
|
||||
collections = from b,foo,bar # all in storage b + "foo" + "bar"
|
||||
collections = from b,from a # all in storage a + all in storage b
|
||||
collections = ["from b", "foo", "bar"] # all in storage b + "foo" + "bar"
|
||||
collections = ["from b", from a"] # all in storage a + all in storage b
|
||||
|
||||
- ``conflict_resolution``: Optional, define how conflicts should be handled. A
|
||||
conflict occurs when one item (event, task) changed on both sides since the
|
||||
|
|
@ -58,9 +70,9 @@ Pair Section
|
|||
|
||||
Valid values are:
|
||||
|
||||
- ``a wins`` and ``b wins``, where the whole item is taken from one side.
|
||||
- ``"a wins"`` and ``"b wins"``, where the whole item is taken from one side.
|
||||
Vdirsyncer will not attempt to merge the two items.
|
||||
- ``None``, the default, where an error is shown and no changes are done.
|
||||
- ``null``, the default, where an error is shown and no changes are done.
|
||||
|
||||
.. _storage_config:
|
||||
|
||||
|
|
@ -75,8 +87,8 @@ Storage Section
|
|||
- ``type`` defines which kind of storage is defined. See :ref:`storages`.
|
||||
|
||||
- ``read_only`` defines whether the storage should be regarded as a read-only
|
||||
storage. The value ``True`` means synchronization will discard any changes
|
||||
made to the other side. The value ``False`` implies normal 2-way
|
||||
storage. The value ``true`` means synchronization will discard any changes
|
||||
made to the other side. The value ``false`` implies normal 2-way
|
||||
synchronization.
|
||||
|
||||
- Any further parameters are passed on to the storage class.
|
||||
|
|
@ -92,7 +104,7 @@ Read-write storages
|
|||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These storages generally support reading and changing of their items. Their
|
||||
default value for ``read_only`` is ``False``, but can be set to ``True`` if
|
||||
default value for ``read_only`` is ``false``, but can be set to ``true`` if
|
||||
wished.
|
||||
|
||||
.. autoclass:: CaldavStorage
|
||||
|
|
@ -107,7 +119,7 @@ Read-only storages
|
|||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These storages don't support writing of their items, consequently ``read_only``
|
||||
is set to ``True`` by default. Changing ``read_only`` to ``False`` on them
|
||||
is set to ``true`` by default. Changing ``read_only`` to ``false`` on them
|
||||
leads to an error.
|
||||
|
||||
.. autoclass:: HttpStorage
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ is very tedious to do. Instead we will use a shortcut:
|
|||
|
||||
[pair my_contacts]
|
||||
...
|
||||
collections = default,work
|
||||
collections = ["default", "work"]
|
||||
|
||||
This will synchronize
|
||||
``https://owncloud.example.com/remote.php/carddav/addressbooks/bob/default/``
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ b = bob_contacts_remote
|
|||
# Omitting this parameter implies that the given path and URL in the
|
||||
# corresponding `[storage <name>]` blocks are already pointing to a
|
||||
# collection each.
|
||||
collections = default,work
|
||||
collections = ["default", "work"]
|
||||
|
||||
# To resolve a conflict the following values are possible:
|
||||
# `None` - abort when collisions occur (default)
|
||||
# `a wins` - assume a's items to be more up-to-date
|
||||
# `b wins` - assume b's items to be more up-to-date
|
||||
#conflict_resolution = None
|
||||
#conflict_resolution = null
|
||||
|
||||
[storage bob_contacts_local]
|
||||
# A storage references actual data on a remote server or on the local disk.
|
||||
|
|
@ -63,7 +63,7 @@ url = https://owncloud.example.com/remote.php/carddav/addressbooks/bob/
|
|||
[pair bob_calendar]
|
||||
a = bob_calendar_local
|
||||
b = bob_calendar_remote
|
||||
collections = private,work
|
||||
collections = ["private", "work"]
|
||||
|
||||
[storage bob_calendar_local]
|
||||
type = filesystem
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ def test_wrong_general_section(tmpdir):
|
|||
config_file = tmpdir.join('config')
|
||||
config_file.write(dedent('''
|
||||
[general]
|
||||
wrong = yes
|
||||
wrong = true
|
||||
'''))
|
||||
|
||||
runner = CliRunner()
|
||||
|
|
|
|||
|
|
@ -213,8 +213,16 @@ def parse_pairs_args(pairs_args, all_pairs):
|
|||
.format(pair, list(all_pairs)))
|
||||
|
||||
if collection is None:
|
||||
collections = [x.strip() or None for x in
|
||||
pair_options.get('collections', '').split(',')]
|
||||
collections = pair_options.get('collections', [None])
|
||||
if isinstance(collections, str):
|
||||
# XXX: Deprecation
|
||||
orig_collections = collections
|
||||
collections = [x.strip() or None
|
||||
for x in collections.split(',')]
|
||||
cli_logger.warning(
|
||||
'{!r} is deprecated, please use:\ncollections = {}\n'
|
||||
'The old form will be removed in 0.4.0.'
|
||||
.format(orig_collections, json.dumps(collections)))
|
||||
else:
|
||||
collections = [collection]
|
||||
|
||||
|
|
|
|||
|
|
@ -484,11 +484,19 @@ class CaldavStorage(DavStorage):
|
|||
get_multi_data_query = '{urn:ietf:params:xml:ns:caldav}calendar-data'
|
||||
|
||||
def __init__(self, start_date=None, end_date=None,
|
||||
item_types='VTODO, VEVENT', **kwargs):
|
||||
item_types=('VTODO', 'VEVENT'), **kwargs):
|
||||
super(CaldavStorage, self).__init__(**kwargs)
|
||||
if isinstance(item_types, str):
|
||||
item_types = filter(bool,
|
||||
(x.strip() for x in item_types.split(',')))
|
||||
orig_item_types = item_types
|
||||
item_types = [x.strip() for x in item_types.split(',')]
|
||||
|
||||
# XXX: Deprecation
|
||||
import json
|
||||
dav_logger.warning(
|
||||
'{!r} is deprecated, please use:\nitem_types = {}'
|
||||
'The old form will be removed in 0.4.0.'
|
||||
.format(orig_item_types, json.dumps(item_types)))
|
||||
|
||||
self.item_types = tuple(item_types)
|
||||
if (start_date is None) != (end_date is None):
|
||||
raise ValueError('If start_date is given, '
|
||||
|
|
|
|||
|
|
@ -7,13 +7,14 @@
|
|||
:license: MIT, see LICENSE for more details.
|
||||
'''
|
||||
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
|
||||
import requests
|
||||
from requests.packages.urllib3.poolmanager import PoolManager
|
||||
|
||||
from .compat import iteritems, urlparse
|
||||
from .compat import iteritems, text_type, urlparse
|
||||
from .. import exceptions, log
|
||||
from ..doubleclick import click, ctx
|
||||
|
||||
|
|
@ -57,28 +58,36 @@ def split_sequence(s, f):
|
|||
|
||||
|
||||
def parse_config_value(value):
|
||||
if len(value.splitlines()) > 1:
|
||||
# The reason we use comma-separated values instead of
|
||||
# multiline-values for lists is simple: 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:
|
||||
if value in ('on', '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 in ('off', '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 == '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
|
||||
|
||||
try:
|
||||
rv = json.loads(value)
|
||||
except ValueError:
|
||||
rv = value
|
||||
|
||||
if isinstance(rv, (bytes, text_type)) and 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.')
|
||||
raise ValueError('No multiline-values allowed:\n{!r}'.format(value))
|
||||
|
||||
if value.lower() in ('yes', 'true', 'on'):
|
||||
return True
|
||||
elif value.lower() in ('no', 'false', 'off'):
|
||||
return False
|
||||
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return value
|
||||
return rv
|
||||
|
||||
|
||||
def parse_options(items, section=None):
|
||||
|
|
|
|||
Loading…
Reference in a new issue