(backport) Make docs build independent of app

This commit is contained in:
Markus Unterwaditzer 2018-03-16 18:03:45 +01:00
parent 0094acddd0
commit 68f2cf3195
11 changed files with 306 additions and 277 deletions

View file

@ -9,15 +9,18 @@ Package maintainers and users who have to manually update their installation
may want to subscribe to `GitHub's tag feed may want to subscribe to `GitHub's tag feed
<https://github.com/pimutils/vdirsyncer/tags.atom>`_. <https://github.com/pimutils/vdirsyncer/tags.atom>`_.
Version 0.16.6
==============
- **Packagers:** Documentation building no longer needs a working installation
of vdirsyncer.
Version 0.16.5 Version 0.16.5
============== ==============
*released on 13 June 2018*
- **Packagers:** click-log 0.3 is required. - **Packagers:** click-log 0.3 is required.
- All output will now happen on stderr (because of the upgrade of ``click-log``). - All output will now happen on stderr (because of the upgrade of ``click-log``).
Version 0.16.4 Version 0.16.4
============== ==============

View file

@ -1,2 +1,3 @@
sphinx != 1.4.7 sphinx != 1.4.7
sphinx_rtd_theme sphinx_rtd_theme
setuptools_scm

View file

@ -6,7 +6,7 @@ import os
from sphinx.ext import autodoc from sphinx.ext import autodoc
import vdirsyncer import setuptools_scm
extensions = ['sphinx.ext.autodoc'] extensions = ['sphinx.ext.autodoc']
@ -19,7 +19,7 @@ project = u'vdirsyncer'
copyright = (u'2014-{}, Markus Unterwaditzer & contributors' copyright = (u'2014-{}, Markus Unterwaditzer & contributors'
.format(datetime.date.today().strftime('%Y'))) .format(datetime.date.today().strftime('%Y')))
release = vdirsyncer.__version__ release = setuptools_scm.get_version(root='..', relative_to=__file__)
version = '.'.join(release.split('.')[:2]) # The short X.Y version. version = '.'.join(release.split('.')[:2]) # The short X.Y version.
rst_epilog = '.. |vdirsyncer_version| replace:: %s' % release rst_epilog = '.. |vdirsyncer_version| replace:: %s' % release
@ -75,9 +75,10 @@ def github_issue_role(name, rawtext, text, lineno, inliner,
prb = inliner.problematic(rawtext, rawtext, msg) prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg] return [prb], [msg]
import vdirsyncer
from docutils import nodes from docutils import nodes
link = '{}/{}/{}'.format(vdirsyncer.PROJECT_HOME,
PROJECT_HOME = 'https://github.com/pimutils/vdirsyncer'
link = '{}/{}/{}'.format(PROJECT_HOME,
'issues' if name == 'gh' else 'pull', 'issues' if name == 'gh' else 'pull',
issue_num) issue_num)
linktext = ('issue #{}' if name == 'gh' linktext = ('issue #{}' if name == 'gh'
@ -87,64 +88,9 @@ def github_issue_role(name, rawtext, text, lineno, inliner,
return [node], [] return [node], []
def format_storage_config(cls, header=True):
if header is True:
yield '[storage example_for_{}]'.format(cls.storage_name)
yield 'type = "{}"'.format(cls.storage_name)
from vdirsyncer.storage.base import Storage
from vdirsyncer.utils import get_storage_init_specs
handled = set()
for spec in get_storage_init_specs(cls, stop_at=Storage):
defaults = spec.defaults or ()
defaults = dict(zip(spec.args[-len(defaults):], defaults))
for key in spec.args[1:]:
if key in handled:
continue
handled.add(key)
comment = '' if key not in defaults else '#'
value = defaults.get(key, '...')
yield '{}{} = {}'.format(comment, key, json.dumps(value))
class StorageDocumenter(autodoc.ClassDocumenter):
'''Custom formatter for auto-documenting storage classes. It assumes that
the first line of the class' docstring is its own paragraph.
After that first paragraph, an example configuration will be inserted and
Sphinx' __init__ signature removed.'''
objtype = 'storage'
domain = None
directivetype = 'storage'
option_spec = {}
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
from vdirsyncer.storage.base import Storage
return isinstance(member, Storage)
def format_signature(self):
return ''
def add_directive_header(self, sig):
directive = getattr(self, 'directivetype', self.objtype)
name = self.object.storage_name
self.add_line(u'.. %s:: %s%s' % (directive, name, sig),
'<autodoc>')
def get_doc(self, encoding=None, ignore=1):
rv = autodoc.ClassDocumenter.get_doc(self, encoding, ignore)
config = [u' ' + x for x in format_storage_config(self.object)]
rv[0] = rv[0][:1] + [u'::', u''] + config + [u''] + rv[0][1:]
return rv
def setup(app): def setup(app):
from sphinx.domains.python import PyObject from sphinx.domains.python import PyObject
app.add_object_type('storage', 'storage', 'pair: %s; storage', app.add_object_type('storage', 'storage', 'pair: %s; storage',
doc_field_types=PyObject.doc_field_types) doc_field_types=PyObject.doc_field_types)
app.add_role('gh', github_issue_role) app.add_role('gh', github_issue_role)
app.add_role('ghpr', github_issue_role) app.add_role('ghpr', github_issue_role)
app.add_autodocumenter(StorageDocumenter)

View file

@ -150,9 +150,107 @@ Supported Storages
CalDAV and CardDAV CalDAV and CardDAV
++++++++++++++++++ ++++++++++++++++++
.. autostorage:: vdirsyncer.storage.dav.CalDAVStorage .. note::
.. autostorage:: vdirsyncer.storage.dav.CardDAVStorage Please also see :ref:`supported-servers`, as some servers may not work
well.
.. storage:: caldav
CalDAV.
::
[storage example_for_caldav]
type = "caldav"
#start_date = null
#end_date = null
#item_types = []
url = "..."
#username = ""
#password = ""
#verify = true
#auth = null
#useragent = "vdirsyncer/0.16.4"
#verify_fingerprint = null
#auth_cert = null
You can set a timerange to synchronize with the parameters ``start_date``
and ``end_date``. Inside those parameters, you can use any Python
expression to return a valid :py:class:`datetime.datetime` object. For
example, the following would synchronize the timerange from one year in the
past to one year in the future::
start_date = "datetime.now() - timedelta(days=365)"
end_date = "datetime.now() + timedelta(days=365)"
Either both or none have to be specified. The default is to synchronize
everything.
You can set ``item_types`` to restrict the *kind of items* you want to
synchronize. For example, if you want to only synchronize events (but don't
download any tasks from the server), set ``item_types = ["VEVENT"]``. If
you want to synchronize events and tasks, but have some ``VJOURNAL`` items
on the server you don't want to synchronize, use ``item_types = ["VEVENT",
"VTODO"]``.
: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: Kind of items to show. The default, the empty list, is
to show all. This depends on particular features on the server, the
results are not validated.
:param url: Base URL or an URL to a calendar.
:param username: Username for authentication.
:param password: Password for authentication.
:param verify: Verify SSL certificate, default True. This can also be a
local path to a self-signed SSL certificate. See :ref:`ssl-tutorial`
for more information.
:param verify_fingerprint: Optional. SHA1 or MD5 fingerprint of the
expected server certificate. See :ref:`ssl-tutorial` for more
information.
:param auth: Optional. Either ``basic``, ``digest`` or ``guess``. The
default is preemptive Basic auth, sending credentials even if server
didn't request them. This saves from an additional roundtrip per
request. Consider setting ``guess`` if this causes issues with your
server.
:param auth_cert: Optional. Either a path to a certificate with a client
certificate and the key or a list of paths to the files with them.
:param useragent: Default ``vdirsyncer``.
.. storage:: carddav
CardDAV.
::
[storage example_for_carddav]
type = "carddav"
url = "..."
#username = ""
#password = ""
#verify = true
#auth = null
#useragent = "vdirsyncer/0.16.4"
#verify_fingerprint = null
#auth_cert = null
:param url: Base URL or an URL to an addressbook.
:param username: Username for authentication.
:param password: Password for authentication.
:param verify: Verify SSL certificate, default True. This can also be a
local path to a self-signed SSL certificate. See :ref:`ssl-tutorial`
for more information.
:param verify_fingerprint: Optional. SHA1 or MD5 fingerprint of the
expected server certificate. See :ref:`ssl-tutorial` for more
information.
:param auth: Optional. Either ``basic``, ``digest`` or ``guess``. The
default is preemptive Basic auth, sending credentials even if server
didn't request them. This saves from an additional roundtrip per
request. Consider setting ``guess`` if this causes issues with your
server.
:param auth_cert: Optional. Either a path to a certificate with a client
certificate and the key or a list of paths to the files with them.
:param useragent: Default ``vdirsyncer``.
Google Google
++++++ ++++++
@ -206,9 +304,42 @@ or write anything to it.
a rather hidden `settings page a rather hidden `settings page
<https://calendar.google.com/calendar/syncselect>`_. <https://calendar.google.com/calendar/syncselect>`_.
.. autostorage:: vdirsyncer.storage.google.GoogleCalendarStorage .. storage:: google_calendar
.. autostorage:: vdirsyncer.storage.google.GoogleContactsStorage Google calendar.
::
[storage example_for_google_calendar]
type = "google_calendar"
token_file = "..."
client_id = "..."
client_secret = "..."
#start_date = null
#end_date = null
#item_types = []
Please refer to :storage:`caldav` regarding the ``item_types`` and timerange parameters.
:param token_file: A filepath where access tokens are stored.
:param client_id/client_secret: OAuth credentials, obtained from the Google
API Manager.
.. storage:: google_contacts
Google contacts.
::
[storage example_for_google_contacts]
type = "google_contacts"
token_file = "..."
client_id = "..."
client_secret = "..."
:param token_file: A filepath where access tokens are stored.
:param client_id/client_secret: OAuth credentials, obtained from the Google
API Manager.
EteSync EteSync
+++++++ +++++++
@ -224,17 +355,123 @@ To use it, you need to install some optional dependencies::
On first usage you will be prompted for the service password and the encryption On first usage you will be prompted for the service password and the encryption
password. Neither are stored. password. Neither are stored.
.. autostorage:: vdirsyncer.storage.etesync.EtesyncContacts .. storage:: etesync_contacts
.. autostorage:: vdirsyncer.storage.etesync.EtesyncCalendars Contacts for etesync.
::
[storage example_for_etesync_contacts]
email = ...
secrets_dir = ...
#server_path = ...
#db_path = ...
:param email: The email address of your account.
:param secrets_dir: A directory where vdirsyncer can store the encryption
key and authentication token.
:param server_url: Optional. URL to the root of your custom server.
:param db_path: Optional. Use a different path for the database.
.. storage:: etesync_calendars
Calendars for etesync.
::
[storage example_for_etesync_calendars]
email = ...
secrets_dir = ...
#server_path = ...
#db_path = ...
:param email: The email address of your account.
:param secrets_dir: A directory where vdirsyncer can store the encryption
key and authentication token.
:param server_url: Optional. URL to the root of your custom server.
:param db_path: Optional. Use a different path for the database.
Local Local
+++++ +++++
.. autostorage:: vdirsyncer.storage.filesystem.FilesystemStorage .. storage:: filesystem
.. autostorage:: vdirsyncer.storage.singlefile.SingleFileStorage Saves each item in its own file, given a directory.
::
[storage example_for_filesystem]
type = "filesystem"
path = "..."
fileext = "..."
#encoding = "utf-8"
#post_hook = null
Can be used with `khal <http://lostpackets.de/khal/>`_. See :doc:`vdir` for
a more formal description of the format.
Directories with a leading dot are ignored to make usage of e.g. version
control easier.
:param path: Absolute path to a vdir/collection. If this is used in
combination with the ``collections`` parameter in a pair-section, this
should point to a directory of vdirs instead.
:param fileext: The file extension to use (e.g. ``.txt``). Contained in the
href, so if you change the file extension after a sync, this will
trigger a re-download of everything (but *should* not cause data-loss
of any kind).
:param encoding: File encoding for items, both content and filename.
:param post_hook: A command to call for each item creation and
modification. The command will be called with the path of the
new/updated file.
.. storage:: singlefile
Save data in single local ``.vcf`` or ``.ics`` file.
The storage basically guesses how items should be joined in the file.
.. versionadded:: 0.1.6
.. note::
This storage is very slow, and that is unlikely to change. You should
consider using :storage:`filesystem` if it fits your usecase.
:param path: The filepath to the file to be written to. If collections are
used, this should contain ``%s`` as a placeholder for the collection
name.
:param encoding: Which encoding the file should use. Defaults to UTF-8.
Example for syncing with :storage:`caldav`::
[pair my_calendar]
a = my_calendar_local
b = my_calendar_remote
collections = ["from a", "from b"]
[storage my_calendar_local]
type = "singlefile"
path = ~/.calendars/%s.ics
[storage my_calendar_remote]
type = "caldav"
url = https://caldav.example.org/
#username =
#password =
Example for syncing with :storage:`caldav` using a ``null`` collection::
[pair my_calendar]
a = my_calendar_local
b = my_calendar_remote
[storage my_calendar_local]
type = "singlefile"
path = ~/my_calendar.ics
[storage my_calendar_remote]
type = "caldav"
url = https://caldav.example.org/username/my_calendar/
#username =
#password =
Read-only storages Read-only storages
++++++++++++++++++ ++++++++++++++++++
@ -243,4 +480,50 @@ 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. leads to an error.
.. autostorage:: vdirsyncer.storage.http.HttpStorage .. storage:: http
Use a simple ``.ics`` file (or similar) from the web.
``webcal://``-calendars are supposed to be used with this, but you have to
replace ``webcal://`` with ``http://``, or better, ``https://``.
::
[pair holidays]
a = holidays_local
b = holidays_remote
collections = null
[storage holidays_local]
type = "filesystem"
path = ~/.config/vdir/calendars/holidays/
fileext = .ics
[storage holidays_remote]
type = "http"
url = https://example.com/holidays_from_hicksville.ics
Too many WebCAL providers generate UIDs of all ``VEVENT``-components
on-the-fly, i.e. all UIDs change every time the calendar is downloaded.
This leads many synchronization programs to believe that all events have
been deleted and new ones created, and accordingly causes a lot of
unnecessary uploads and deletions on the other side. Vdirsyncer completely
ignores UIDs coming from :storage:`http` and will replace them with a hash
of the normalized item content.
:param url: URL to the ``.ics`` file.
:param username: Username for authentication.
:param password: Password for authentication.
:param verify: Verify SSL certificate, default True. This can also be a
local path to a self-signed SSL certificate. See :ref:`ssl-tutorial`
for more information.
:param verify_fingerprint: Optional. SHA1 or MD5 fingerprint of the
expected server certificate. See :ref:`ssl-tutorial` for more
information.
:param auth: Optional. Either ``basic``, ``digest`` or ``guess``. The
default is preemptive Basic auth, sending credentials even if server
didn't request them. This saves from an additional roundtrip per
request. Consider setting ``guess`` if this causes issues with your
server.
:param auth_cert: Optional. Either a path to a certificate with a client
certificate and the key or a list of paths to the files with them.
:param useragent: Default ``vdirsyncer``.

View file

@ -93,26 +93,6 @@ def prepare_client_cert(cert):
return cert return cert
HTTP_STORAGE_PARAMETERS = '''
:param username: Username for authentication.
:param password: Password for authentication.
:param verify: Verify SSL certificate, default True. This can also be a
local path to a self-signed SSL certificate. See :ref:`ssl-tutorial`
for more information.
:param verify_fingerprint: Optional. SHA1 or MD5 fingerprint of the
expected server certificate. See :ref:`ssl-tutorial` for more
information.
:param auth: Optional. Either ``basic``, ``digest`` or ``guess``. The
default is preemptive Basic auth, sending credentials even if server
didn't request them. This saves from an additional roundtrip per
request. Consider setting ``guess`` if this causes issues with your
server.
:param auth_cert: Optional. Either a path to a certificate with a client
certificate and the key or a list of paths to the files with them.
:param useragent: Default ``vdirsyncer``.
'''
def _install_fingerprint_adapter(session, fingerprint): def _install_fingerprint_adapter(session, fingerprint):
prefix = 'https://' prefix = 'https://'
try: try:

View file

@ -12,7 +12,7 @@ from requests.exceptions import HTTPError
from .base import Storage, normalize_meta_value from .base import Storage, normalize_meta_value
from .. import exceptions, http, utils from .. import exceptions, http, utils
from ..http import HTTP_STORAGE_PARAMETERS, USERAGENT, prepare_auth, \ from ..http import USERAGENT, prepare_auth, \
prepare_client_cert, prepare_verify prepare_client_cert, prepare_verify
from ..vobject import Item from ..vobject import Item
@ -397,17 +397,6 @@ class DAVSession(object):
class DAVStorage(Storage): class DAVStorage(Storage):
__doc__ = '''
:param url: Base URL or an URL to a collection.
''' + HTTP_STORAGE_PARAMETERS + '''
.. note::
Please also see :ref:`supported-servers`, as some servers may not work
well.
'''
# the file extension of items. Useful for testing against radicale. # the file extension of items. Useful for testing against radicale.
fileext = None fileext = None
# mimetype of items # mimetype of items
@ -714,36 +703,6 @@ class DAVStorage(Storage):
class CalDAVStorage(DAVStorage): class CalDAVStorage(DAVStorage):
__doc__ = '''
CalDAV.
You can set a timerange to synchronize with the parameters ``start_date``
and ``end_date``. Inside those parameters, you can use any Python
expression to return a valid :py:class:`datetime.datetime` object. For
example, the following would synchronize the timerange from one year in the
past to one year in the future::
start_date = datetime.now() - timedelta(days=365)
end_date = datetime.now() + timedelta(days=365)
Either both or none have to be specified. The default is to synchronize
everything.
You can set ``item_types`` to restrict the *kind of items* you want to
synchronize. For example, if you want to only synchronize events (but don't
download any tasks from the server), set ``item_types = ["VEVENT"]``. If
you want to synchronize events and tasks, but have some ``VJOURNAL`` items
on the server you don't want to synchronize, use ``item_types = ["VEVENT",
"VTODO"]``.
: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: Kind of items to show. The default, the empty list, is
to show all. This depends on particular features on the server, the
results are not validated.
''' + DAVStorage.__doc__
storage_name = 'caldav' storage_name = 'caldav'
fileext = '.ics' fileext = '.ics'
item_mimetype = 'text/calendar' item_mimetype = 'text/calendar'
@ -868,11 +827,6 @@ class CalDAVStorage(DAVStorage):
class CardDAVStorage(DAVStorage): class CardDAVStorage(DAVStorage):
__doc__ = '''
CardDAV.
''' + DAVStorage.__doc__
storage_name = 'carddav' storage_name = 'carddav'
fileext = '.vcf' fileext = '.vcf'
item_mimetype = 'text/vcard' item_mimetype = 'text/vcard'

View file

@ -100,14 +100,6 @@ class _Session:
class EtesyncStorage(Storage): class EtesyncStorage(Storage):
'''
:param email: The email address of your account.
:param secrets_dir: A directory where vdirsyncer can store the encryption
key and authentication token.
:param server_url: Optional. URL to the root of your custom server.
:param db_path: Optional. Use a different path for the database.
'''
_collection_type = None _collection_type = None
_item_type = None _item_type = None
_at_once = False _at_once = False
@ -225,20 +217,12 @@ class EtesyncStorage(Storage):
class EtesyncContacts(EtesyncStorage): class EtesyncContacts(EtesyncStorage):
__doc__ = '''
Contacts for EteSync.
''' + EtesyncStorage.__doc__
_collection_type = AddressBook _collection_type = AddressBook
_item_type = Contact _item_type = Contact
storage_name = 'etesync_contacts' storage_name = 'etesync_contacts'
class EtesyncCalendars(EtesyncStorage): class EtesyncCalendars(EtesyncStorage):
__doc__ = '''
Calendars for EteSync.
''' + EtesyncStorage.__doc__
_collection_type = Calendar _collection_type = Calendar
_item_type = Event _item_type = Event
storage_name = 'etesync_calendars' storage_name = 'etesync_calendars'

View file

@ -17,28 +17,6 @@ logger = logging.getLogger(__name__)
class FilesystemStorage(Storage): class FilesystemStorage(Storage):
'''
Saves each item in its own file, given a directory.
Can be used with `khal <http://lostpackets.de/khal/>`_. See :doc:`vdir` for
a more formal description of the format.
Directories with a leading dot are ignored to make usage of e.g. version
control easier.
:param path: Absolute path to a vdir/collection. If this is used in
combination with the ``collections`` parameter in a pair-section, this
should point to a directory of vdirs instead.
:param fileext: The file extension to use (e.g. ``.txt``). Contained in the
href, so if you change the file extension after a sync, this will
trigger a re-download of everything (but *should* not cause data-loss
of any kind).
:param encoding: File encoding for items, both content and filename.
:param post_hook: A command to call for each item creation and
modification. The command will be called with the path of the
new/updated file.
'''
storage_name = 'filesystem' storage_name = 'filesystem'
_repr_attributes = ('path',) _repr_attributes = ('path',)

View file

@ -101,20 +101,7 @@ class GoogleSession(dav.DAVSession):
_save_token(token) _save_token(token)
GOOGLE_PARAMS_DOCS = '''
:param token_file: A filepath where access tokens are stored.
:param client_id/client_secret: OAuth credentials, obtained from the Google
API Manager.
'''
class GoogleCalendarStorage(dav.CalDAVStorage): class GoogleCalendarStorage(dav.CalDAVStorage):
__doc__ = '''Google calendar.
Please refer to :storage:`caldav` regarding
the ``item_types`` and timerange parameters.
''' + GOOGLE_PARAMS_DOCS
class session_class(GoogleSession): class session_class(GoogleSession):
url = 'https://apidata.googleusercontent.com/caldav/v2/' url = 'https://apidata.googleusercontent.com/caldav/v2/'
scope = ['https://www.googleapis.com/auth/calendar'] scope = ['https://www.googleapis.com/auth/calendar']
@ -150,10 +137,6 @@ class GoogleCalendarStorage(dav.CalDAVStorage):
class GoogleContactsStorage(dav.CardDAVStorage): class GoogleContactsStorage(dav.CardDAVStorage):
__doc__ = '''Google contacts.
''' + GOOGLE_PARAMS_DOCS
class session_class(GoogleSession): class session_class(GoogleSession):
# Google CardDAV is completely bonkers. Collection discovery doesn't # Google CardDAV is completely bonkers. Collection discovery doesn't
# work properly, well-known URI takes us directly to single collection # work properly, well-known URI takes us directly to single collection

View file

@ -4,45 +4,12 @@ import urllib.parse as urlparse
from .base import Storage from .base import Storage
from .. import exceptions from .. import exceptions
from ..http import HTTP_STORAGE_PARAMETERS, USERAGENT, prepare_auth, \ from ..http import USERAGENT, prepare_auth, \
prepare_client_cert, prepare_verify, request prepare_client_cert, prepare_verify, request
from ..vobject import Item, split_collection from ..vobject import Item, split_collection
class HttpStorage(Storage): class HttpStorage(Storage):
__doc__ = '''
Use a simple ``.ics`` file (or similar) from the web.
``webcal://``-calendars are supposed to be used with this, but you have to
replace ``webcal://`` with ``http://``, or better, ``https://``.
Too many WebCAL providers generate UIDs of all ``VEVENT``-components
on-the-fly, i.e. all UIDs change every time the calendar is downloaded.
This leads many synchronization programs to believe that all events have
been deleted and new ones created, and accordingly causes a lot of
unnecessary uploads and deletions on the other side. Vdirsyncer completely
ignores UIDs coming from :storage:`http` and will replace them with a hash
of the normalized item content.
:param url: URL to the ``.ics`` file.
''' + HTTP_STORAGE_PARAMETERS + '''
A simple example::
[pair holidays]
a = holidays_local
b = holidays_remote
collections = null
[storage holidays_local]
type = "filesystem"
path = ~/.config/vdir/calendars/holidays/
fileext = .ics
[storage holidays_remote]
type = "http"
url = https://example.com/holidays_from_hicksville.ics
'''
storage_name = 'http' storage_name = 'http'
read_only = True read_only = True
_repr_attributes = ('username', 'url') _repr_attributes = ('username', 'url')

View file

@ -30,56 +30,6 @@ def _writing_op(f):
class SingleFileStorage(Storage): class SingleFileStorage(Storage):
'''Save data in single local ``.vcf`` or ``.ics`` file.
The storage basically guesses how items should be joined in the file.
.. versionadded:: 0.1.6
.. note::
This storage is very slow, and that is unlikely to change. You should
consider using :storage:`filesystem` if it fits your usecase.
:param path: The filepath to the file to be written to. If collections are
used, this should contain ``%s`` as a placeholder for the collection
name.
:param encoding: Which encoding the file should use. Defaults to UTF-8.
Example for syncing with :storage:`caldav`::
[pair my_calendar]
a = my_calendar_local
b = my_calendar_remote
collections = ["from a", "from b"]
[storage my_calendar_local]
type = "singlefile"
path = ~/.calendars/%s.ics
[storage my_calendar_remote]
type = "caldav"
url = https://caldav.example.org/
#username =
#password =
Example for syncing with :storage:`caldav` using a ``null`` collection::
[pair my_calendar]
a = my_calendar_local
b = my_calendar_remote
[storage my_calendar_local]
type = "singlefile"
path = ~/my_calendar.ics
[storage my_calendar_remote]
type = "caldav"
url = https://caldav.example.org/username/my_calendar/
#username =
#password =
'''
storage_name = 'singlefile' storage_name = 'singlefile'
_repr_attributes = ('path',) _repr_attributes = ('path',)