Less files

This commit is contained in:
Markus Unterwaditzer 2014-03-26 18:11:26 +01:00
parent 47d3b1917d
commit 475671f437
8 changed files with 190 additions and 240 deletions

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
vdirsyncer.tests.storage.dav vdirsyncer.tests.storage.dav
@ -7,43 +6,3 @@
:copyright: (c) 2014 Markus Unterwaditzer :copyright: (c) 2014 Markus Unterwaditzer
:license: MIT, see LICENSE for more details. :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())

View file

@ -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

View file

@ -1,19 +1,59 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
vdirsyncer.tests.storage.test_caldav vdirsyncer.tests.storage.dav.test_main
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: (c) 2014 Markus Unterwaditzer :copyright: (c) 2014 Markus Unterwaditzer
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
''' '''
import os
import pytest import pytest
import requests.exceptions
from vdirsyncer.storage.dav.caldav import CaldavStorage from .. import StorageTests
import vdirsyncer.exceptions as exceptions 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 TASK_TEMPLATE = u'''BEGIN:VCALENDAR
@ -43,13 +83,24 @@ UID:{uid}
END:VEVENT END:VEVENT
END:VCALENDAR''' END:VCALENDAR'''
templates = { templates = {
'VCARD': VCARD_TEMPLATE,
'VEVENT': EVENT_TEMPLATE, 'VEVENT': EVENT_TEMPLATE,
'VTODO': TASK_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): class TestCaldavStorage(DavStorageTests):
storage_class = CaldavStorage storage_class = CaldavStorage
@ -90,3 +141,8 @@ class TestCaldavStorage(DavStorageTests):
a = self.storage_class(item_types='VTODO,VEVENT', **kw) a = self.storage_class(item_types='VTODO,VEVENT', **kw)
b = 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') assert a.item_types == b.item_types == ('VTODO', 'VEVENT')
class TestCarddavStorage(DavStorageTests):
storage_class = CarddavStorage
item_template = VCARD_TEMPLATE

View file

@ -12,8 +12,8 @@
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
''' '''
from .dav.caldav import CaldavStorage from .dav import CaldavStorage
from .dav.carddav import CarddavStorage from .dav import CarddavStorage
from .filesystem import FilesystemStorage from .filesystem import FilesystemStorage
from .http import HttpStorage from .http import HttpStorage

View file

@ -1,14 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
vdirsyncer.storage.dav.base vdirsyncer.storage.dav
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: (c) 2014 Markus Unterwaditzer, Christian Geier and contributors :copyright: (c) 2014 Markus Unterwaditzer, Christian Geier and contributors
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
''' '''
from ..base import Item from .base import Item
from ..http import HttpStorageBase from .http import HttpStorageBase
import vdirsyncer.exceptions as exceptions import vdirsyncer.exceptions as exceptions
import vdirsyncer.log as log import vdirsyncer.log as log
import requests import requests
@ -18,6 +18,9 @@ from lxml import etree
dav_logger = log.get('storage.dav') dav_logger = log.get('storage.dav')
CALDAV_DT_FORMAT = '%Y%m%dT%H%M%SZ'
CONFIG_DT_FORMAT = '%Y-%m-%d'
class DavStorage(HttpStorageBase): class DavStorage(HttpStorageBase):
@ -228,3 +231,122 @@ class DavStorage(HttpStorageBase):
'{DAV:}prop').find('{DAV:}getetag').text '{DAV:}prop').find('{DAV:}getetag').text
href = self._normalize_href(element.find('{DAV:}href').text) href = self._normalize_href(element.find('{DAV:}href').text)
yield href, etag 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 = '''<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-multiget xmlns:D="DAV:"
xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop>
<D:getetag/>
<C:calendar-data/>
</D:prop>
{hrefs}
</C:calendar-multiget>'''
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 = '''<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:"
xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop>
<D:getetag/>
</D:prop>
<C:filter>
<C:comp-filter name="VCALENDAR">
<C:comp-filter name="{component}">
{caldavfilter}
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>'''
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 = ('<C:time-range start="{start}" end="{end}"/>'
.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 = '''<?xml version="1.0" encoding="utf-8" ?>
<C:addressbook-multiget xmlns:D="DAV:"
xmlns:C="urn:ietf:params:xml:ns:carddav">
<D:prop>
<D:getetag/>
<C:address-data/>
</D:prop>
{hrefs}
</C:addressbook-multiget>'''
get_multi_data_query = '{urn:ietf:params:xml:ns:carddav}address-data'
def list(self):
return self._list('''<?xml version="1.0" encoding="utf-8" ?>
<C:addressbook-query xmlns:D="DAV:"
xmlns:C="urn:ietf:params:xml:ns:carddav">
<D:prop>
<D:getetag/>
</D:prop>
<C:filter>
<C:comp-filter name="VCARD"/>
</C:filter>
</C:addressbook-query>''')

View file

@ -1,8 +0,0 @@
# -*- coding: utf-8 -*-
'''
vdirsyncer.storage.dav
~~~~~~~~~~~~~~~~~~~~~~
:copyright: (c) 2014 Markus Unterwaditzer
:license: MIT, see LICENSE for more details.
'''

View file

@ -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 = '''<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-multiget xmlns:D="DAV:"
xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop>
<D:getetag/>
<C:calendar-data/>
</D:prop>
{hrefs}
</C:calendar-multiget>'''
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 = '''<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:"
xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop>
<D:getetag/>
</D:prop>
<C:filter>
<C:comp-filter name="VCALENDAR">
<C:comp-filter name="{component}">
{caldavfilter}
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>'''
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 = ('<C:time-range start="{start}" end="{end}"/>'
.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

View file

@ -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 = '''<?xml version="1.0" encoding="utf-8" ?>
<C:addressbook-multiget xmlns:D="DAV:"
xmlns:C="urn:ietf:params:xml:ns:carddav">
<D:prop>
<D:getetag/>
<C:address-data/>
</D:prop>
{hrefs}
</C:addressbook-multiget>'''
get_multi_data_query = '{urn:ietf:params:xml:ns:carddav}address-data'
def list(self):
return self._list('''<?xml version="1.0" encoding="utf-8" ?>
<C:addressbook-query xmlns:D="DAV:"
xmlns:C="urn:ietf:params:xml:ns:carddav">
<D:prop>
<D:getetag/>
</D:prop>
<C:filter>
<C:comp-filter name="VCARD"/>
</C:filter>
</C:addressbook-query>''')