diff --git a/tests/storage/dav/__init__.py b/tests/storage/dav/__init__.py
index b3e8c45..4fdb23f 100644
--- a/tests/storage/dav/__init__.py
+++ b/tests/storage/dav/__init__.py
@@ -1,4 +1,3 @@
-
# -*- coding: utf-8 -*-
'''
vdirsyncer.tests.storage.dav
@@ -7,43 +6,3 @@
:copyright: (c) 2014 Markus Unterwaditzer
:license: MIT, see LICENSE for more details.
'''
-
-import os
-import pytest
-
-from .. import StorageTests
-import vdirsyncer.exceptions as exceptions
-from vdirsyncer.storage.base import Item
-import requests.exceptions
-
-
-dav_server = os.environ.get('DAV_SERVER', '').strip() or 'radicale_filesystem'
-if dav_server.startswith('radicale_'):
- from ._radicale import ServerMixin
-elif dav_server == 'owncloud':
- from ._owncloud import ServerMixin
-else:
- raise RuntimeError('{} is not a known DAV server.'.format(dav_server))
-
-try:
- import radicale
- radicale_version = radicale.VERSION
- del radicale
-except ImportError:
- radicale_version = None
-
-
-pytestmark = pytest.mark.xfail(
- dav_server == 'radicale_database' and radicale_version == '0.8',
- reason='Database storage of Radicale 0.8 is broken.')
-
-
-class DavStorageTests(ServerMixin, StorageTests):
- def test_dav_broken_item(self):
- item = Item(u'UID:1')
- s = self._get_storage()
- try:
- s.upload(item)
- except (exceptions.Error, requests.exceptions.HTTPError):
- pass
- assert not list(s.list())
diff --git a/tests/storage/dav/test_carddav.py b/tests/storage/dav/test_carddav.py
deleted file mode 100644
index 85bf63d..0000000
--- a/tests/storage/dav/test_carddav.py
+++ /dev/null
@@ -1,34 +0,0 @@
-
-# -*- coding: utf-8 -*-
-'''
- vdirsyncer.tests.storage.test_carddav
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- :copyright: (c) 2014 Markus Unterwaditzer
- :license: MIT, see LICENSE for more details.
-'''
-
-from vdirsyncer.storage.dav.carddav import CarddavStorage
-from . import DavStorageTests, pytestmark
-
-
-VCARD_TEMPLATE = u'''BEGIN:VCARD
-VERSION:3.0
-FN:Cyrus Daboo
-N:Daboo;Cyrus
-ADR;TYPE=POSTAL:;2822 Email HQ;Suite 2821;RFCVille;PA;15213;USA
-EMAIL;TYPE=INTERNET;TYPE=PREF:cyrus@example.com
-NICKNAME:me
-NOTE:Example VCard.
-ORG:Self Employed
-TEL;TYPE=WORK;TYPE=VOICE:412 605 0499
-TEL;TYPE=FAX:412 605 0705
-URL:http://www.example.com
-UID:{uid}
-X-SOMETHING:{r}
-END:VCARD'''
-
-
-class TestCarddavStorage(DavStorageTests):
- storage_class = CarddavStorage
- item_template = VCARD_TEMPLATE
diff --git a/tests/storage/dav/test_caldav.py b/tests/storage/dav/test_main.py
similarity index 59%
rename from tests/storage/dav/test_caldav.py
rename to tests/storage/dav/test_main.py
index 9c2dd25..0aa7594 100644
--- a/tests/storage/dav/test_caldav.py
+++ b/tests/storage/dav/test_main.py
@@ -1,19 +1,59 @@
# -*- coding: utf-8 -*-
'''
- vdirsyncer.tests.storage.test_caldav
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ vdirsyncer.tests.storage.dav.test_main
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: (c) 2014 Markus Unterwaditzer
:license: MIT, see LICENSE for more details.
'''
-
+import os
import pytest
-import requests.exceptions
-from vdirsyncer.storage.dav.caldav import CaldavStorage
+
+from .. import StorageTests
import vdirsyncer.exceptions as exceptions
-from . import DavStorageTests, pytestmark
+from vdirsyncer.storage.base import Item
+from vdirsyncer.storage.dav import CaldavStorage, CarddavStorage
+import requests.exceptions
+
+
+dav_server = os.environ.get('DAV_SERVER', '').strip() or 'radicale_filesystem'
+if dav_server.startswith('radicale_'):
+ from ._radicale import ServerMixin
+elif dav_server == 'owncloud':
+ from ._owncloud import ServerMixin
+else:
+ raise RuntimeError('{} is not a known DAV server.'.format(dav_server))
+
+try:
+ import radicale
+ radicale_version = radicale.VERSION
+ del radicale
+except ImportError:
+ radicale_version = None
+
+
+pytestmark = pytest.mark.xfail(
+ dav_server == 'radicale_database' and radicale_version == '0.8',
+ reason='Database storage of Radicale 0.8 is broken.')
+
+
+VCARD_TEMPLATE = u'''BEGIN:VCARD
+VERSION:3.0
+FN:Cyrus Daboo
+N:Daboo;Cyrus
+ADR;TYPE=POSTAL:;2822 Email HQ;Suite 2821;RFCVille;PA;15213;USA
+EMAIL;TYPE=INTERNET;TYPE=PREF:cyrus@example.com
+NICKNAME:me
+NOTE:Example VCard.
+ORG:Self Employed
+TEL;TYPE=WORK;TYPE=VOICE:412 605 0499
+TEL;TYPE=FAX:412 605 0705
+URL:http://www.example.com
+UID:{uid}
+X-SOMETHING:{r}
+END:VCARD'''
TASK_TEMPLATE = u'''BEGIN:VCALENDAR
@@ -43,13 +83,24 @@ UID:{uid}
END:VEVENT
END:VCALENDAR'''
-
templates = {
+ 'VCARD': VCARD_TEMPLATE,
'VEVENT': EVENT_TEMPLATE,
'VTODO': TASK_TEMPLATE
}
+class DavStorageTests(ServerMixin, StorageTests):
+ def test_dav_broken_item(self):
+ item = Item(u'UID:1')
+ s = self._get_storage()
+ try:
+ s.upload(item)
+ except (exceptions.Error, requests.exceptions.HTTPError):
+ pass
+ assert not list(s.list())
+
+
class TestCaldavStorage(DavStorageTests):
storage_class = CaldavStorage
@@ -90,3 +141,8 @@ class TestCaldavStorage(DavStorageTests):
a = self.storage_class(item_types='VTODO,VEVENT', **kw)
b = self.storage_class(item_types=('VTODO', 'VEVENT'), **kw)
assert a.item_types == b.item_types == ('VTODO', 'VEVENT')
+
+
+class TestCarddavStorage(DavStorageTests):
+ storage_class = CarddavStorage
+ item_template = VCARD_TEMPLATE
diff --git a/vdirsyncer/storage/__init__.py b/vdirsyncer/storage/__init__.py
index 9f6c3a8..e915f5b 100644
--- a/vdirsyncer/storage/__init__.py
+++ b/vdirsyncer/storage/__init__.py
@@ -12,8 +12,8 @@
:license: MIT, see LICENSE for more details.
'''
-from .dav.caldav import CaldavStorage
-from .dav.carddav import CarddavStorage
+from .dav import CaldavStorage
+from .dav import CarddavStorage
from .filesystem import FilesystemStorage
from .http import HttpStorage
diff --git a/vdirsyncer/storage/dav/base.py b/vdirsyncer/storage/dav.py
similarity index 62%
rename from vdirsyncer/storage/dav/base.py
rename to vdirsyncer/storage/dav.py
index eeb17be..574c513 100644
--- a/vdirsyncer/storage/dav/base.py
+++ b/vdirsyncer/storage/dav.py
@@ -1,14 +1,14 @@
# -*- coding: utf-8 -*-
'''
- vdirsyncer.storage.dav.base
+ vdirsyncer.storage.dav
~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: (c) 2014 Markus Unterwaditzer, Christian Geier and contributors
:license: MIT, see LICENSE for more details.
'''
-from ..base import Item
-from ..http import HttpStorageBase
+from .base import Item
+from .http import HttpStorageBase
import vdirsyncer.exceptions as exceptions
import vdirsyncer.log as log
import requests
@@ -18,6 +18,9 @@ from lxml import etree
dav_logger = log.get('storage.dav')
+CALDAV_DT_FORMAT = '%Y%m%dT%H%M%SZ'
+CONFIG_DT_FORMAT = '%Y-%m-%d'
+
class DavStorage(HttpStorageBase):
@@ -228,3 +231,122 @@ class DavStorage(HttpStorageBase):
'{DAV:}prop').find('{DAV:}getetag').text
href = self._normalize_href(element.find('{DAV:}href').text)
yield href, etag
+
+
+
+
+class CaldavStorage(DavStorage):
+
+ fileext = '.ics'
+ item_mimetype = 'text/calendar'
+ dav_header = 'calendar-access'
+ leif_class = 'CalDiscover'
+
+ start_date = None
+ end_date = None
+
+ get_multi_template = '''
+
+
+
+
+
+ {hrefs}
+ '''
+
+ 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):
+ '''
+ :param start_date: Start date of timerange to show, default -inf.
+ :param end_date: End date of timerange to show, default +inf.
+ :param item_types: The item types to show from the server. Dependent on
+ server functionality, no clientside validation of results. This
+ currently only affects the `list` method, but this shouldn't cause
+ problems in the normal usecase.
+ '''
+ super(CaldavStorage, self).__init__(**kwargs)
+ if isinstance(item_types, str):
+ item_types = [x.strip() for x in item_types.split(',')]
+ self.item_types = tuple(item_types)
+ if (start_date is None) != (end_date is None):
+ raise ValueError('If start_date is given, '
+ 'end_date has to be given too.')
+ elif start_date is not None and end_date is not None:
+ namespace = dict(datetime.__dict__)
+ namespace['start_date'] = self.start_date = \
+ (eval(start_date, namespace) if isinstance(start_date, str)
+ else start_date)
+ self.end_date = \
+ (eval(end_date, namespace) if isinstance(end_date, str)
+ else end_date)
+
+ self._list_template = self._get_list_template()
+
+ def _get_list_template(self):
+ data = '''
+
+
+
+
+
+
+
+ {caldavfilter}
+
+
+
+ '''
+ start = self.start_date
+ end = self.end_date
+ caldavfilter = ''
+ if start is not None and end is not None:
+ start = start.strftime(CALDAV_DT_FORMAT)
+ end = end.strftime(CALDAV_DT_FORMAT)
+ caldavfilter = (''
+ .format(start=start, end=end))
+ return data.format(caldavfilter=caldavfilter, component='{item_type}')
+
+ def list(self):
+ hrefs = set()
+ for t in self.item_types:
+ xml = self._list_template.format(item_type=t)
+ for href, etag in self._list(xml):
+ assert href not in hrefs
+ hrefs.add(href)
+ yield href, etag
+
+
+class CarddavStorage(DavStorage):
+
+ fileext = '.vcf'
+ item_mimetype = 'text/vcard'
+ dav_header = 'addressbook'
+ leif_class = 'CardDiscover'
+
+ get_multi_template = '''
+
+
+
+
+
+ {hrefs}
+ '''
+
+ get_multi_data_query = '{urn:ietf:params:xml:ns:carddav}address-data'
+
+ def list(self):
+ return self._list('''
+
+
+
+
+
+
+
+ ''')
diff --git a/vdirsyncer/storage/dav/__init__.py b/vdirsyncer/storage/dav/__init__.py
deleted file mode 100644
index dd12acd..0000000
--- a/vdirsyncer/storage/dav/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-'''
- vdirsyncer.storage.dav
- ~~~~~~~~~~~~~~~~~~~~~~
-
- :copyright: (c) 2014 Markus Unterwaditzer
- :license: MIT, see LICENSE for more details.
-'''
diff --git a/vdirsyncer/storage/dav/caldav.py b/vdirsyncer/storage/dav/caldav.py
deleted file mode 100644
index 90a6306..0000000
--- a/vdirsyncer/storage/dav/caldav.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# -*- coding: utf-8 -*-
-'''
- vdirsyncer.storage.dav.caldav
- ~~~~~~~~~~~~~~~~~~~~~~~~~
-
- Original version from khal: https://github.com/geier/khal
-
- :copyright: (c) 2014 Markus Unterwaditzer, Christian Geier and contributors
- :license: MIT, see LICENSE for more details.
-'''
-
-from .base import DavStorage
-import datetime
-
-CALDAV_DT_FORMAT = '%Y%m%dT%H%M%SZ'
-CONFIG_DT_FORMAT = '%Y-%m-%d'
-
-
-class CaldavStorage(DavStorage):
-
- fileext = '.ics'
- item_mimetype = 'text/calendar'
- dav_header = 'calendar-access'
- leif_class = 'CalDiscover'
-
- start_date = None
- end_date = None
-
- get_multi_template = '''
-
-
-
-
-
- {hrefs}
- '''
-
- 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):
- '''
- :param start_date: Start date of timerange to show, default -inf.
- :param end_date: End date of timerange to show, default +inf.
- :param item_types: The item types to show from the server. Dependent on
- server functionality, no clientside validation of results. This
- currently only affects the `list` method, but this shouldn't cause
- problems in the normal usecase.
- '''
- super(CaldavStorage, self).__init__(**kwargs)
- if isinstance(item_types, str):
- item_types = [x.strip() for x in item_types.split(',')]
- self.item_types = tuple(item_types)
- if (start_date is None) != (end_date is None):
- raise ValueError('If start_date is given, '
- 'end_date has to be given too.')
- elif start_date is not None and end_date is not None:
- namespace = dict(datetime.__dict__)
- namespace['start_date'] = self.start_date = \
- (eval(start_date, namespace) if isinstance(start_date, str)
- else start_date)
- self.end_date = \
- (eval(end_date, namespace) if isinstance(end_date, str)
- else end_date)
-
- self._list_template = self._get_list_template()
-
- def _get_list_template(self):
- data = '''
-
-
-
-
-
-
-
- {caldavfilter}
-
-
-
- '''
- start = self.start_date
- end = self.end_date
- caldavfilter = ''
- if start is not None and end is not None:
- start = start.strftime(CALDAV_DT_FORMAT)
- end = end.strftime(CALDAV_DT_FORMAT)
- caldavfilter = (''
- .format(start=start, end=end))
- return data.format(caldavfilter=caldavfilter, component='{item_type}')
-
- def list(self):
- hrefs = set()
- for t in self.item_types:
- xml = self._list_template.format(item_type=t)
- for href, etag in self._list(xml):
- assert href not in hrefs
- hrefs.add(href)
- yield href, etag
diff --git a/vdirsyncer/storage/dav/carddav.py b/vdirsyncer/storage/dav/carddav.py
deleted file mode 100644
index 2194b6b..0000000
--- a/vdirsyncer/storage/dav/carddav.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-'''
- vdirsyncer.storage.dav.carddav
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- Original version from pycarddav: https://github.com/geier/pycarddav
-
- :copyright: (c) 2014 Markus Unterwaditzer, Christian Geier and contributors
- :license: MIT, see LICENSE for more details.
-'''
-
-from .base import DavStorage
-
-
-class CarddavStorage(DavStorage):
-
- fileext = '.vcf'
- item_mimetype = 'text/vcard'
- dav_header = 'addressbook'
- leif_class = 'CardDiscover'
-
- get_multi_template = '''
-
-
-
-
-
- {hrefs}
- '''
-
- get_multi_data_query = '{urn:ietf:params:xml:ns:carddav}address-data'
-
- def list(self):
- return self._list('''
-
-
-
-
-
-
-
- ''')