diff --git a/docs/conf.py b/docs/conf.py
index 3119ddc..0d043f2 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -89,6 +89,31 @@ def github_issue_role(name, rawtext, text, lineno, inliner, options={},
return [node], []
+from sphinx.ext import autodoc
+
+
+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'
+ directivetype = 'attribute'
+
+ def format_signature(self):
+ return ''
+
+ def get_doc(self, encoding=None, ignore=1):
+ from vdirsyncer.cli.utils import format_storage_config
+ 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):
app.add_role('gh', github_issue_role)
app.add_role('ghpr', github_issue_role)
+ app.add_autodocumenter(StorageDocumenter)
diff --git a/docs/config.rst b/docs/config.rst
index 0d935cc..ea7b1ef 100644
--- a/docs/config.rst
+++ b/docs/config.rst
@@ -126,13 +126,13 @@ 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
wished.
-.. autoclass:: CaldavStorage
+.. autostorage:: CaldavStorage
-.. autoclass:: CarddavStorage
+.. autostorage:: CarddavStorage
-.. autoclass:: FilesystemStorage
+.. autostorage:: FilesystemStorage
-.. autoclass:: SingleFileStorage
+.. autostorage:: SingleFileStorage
Read-only storages
~~~~~~~~~~~~~~~~~~
@@ -141,4 +141,4 @@ 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
leads to an error.
-.. autoclass:: HttpStorage
+.. autostorage:: HttpStorage
diff --git a/vdirsyncer/cli/utils.py b/vdirsyncer/cli/utils.py
index 39839ee..c267728 100644
--- a/vdirsyncer/cli/utils.py
+++ b/vdirsyncer/cli/utils.py
@@ -517,3 +517,17 @@ def parse_options(items, section=None):
except ValueError as e:
raise ValueError('Section {!r}, option {!r}: {}'
.format(section, key, e))
+
+
+def format_storage_config(cls, header=True):
+ if header is True:
+ yield '[storage {}]'.format(cls.storage_name)
+
+ from ..storage.base import Storage
+ from ..utils import get_class_init_specs
+ for spec in get_class_init_specs(cls, stop_at=Storage):
+ defaults = dict(zip(spec.args[-len(spec.defaults):], spec.defaults))
+ for key in spec.args[1:]:
+ comment = '' if key not in defaults else '#'
+ value = defaults.get(key, '...')
+ yield '{}{} = {}'.format(comment, key, json.dumps(value))
diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py
index b9af786..2f935b8 100644
--- a/vdirsyncer/storage/dav.py
+++ b/vdirsyncer/storage/dav.py
@@ -253,12 +253,6 @@ class DavSession(object):
class DavStorage(Storage):
'''
- .. note::
-
- Please also see :doc:`supported` for very important information, as
- changing some of the default options might be very dangerous with some
- servers.
-
:param url: Base URL or an URL to a collection.
:param username: Username for authentication.
:param password: Password for authentication.
@@ -272,6 +266,12 @@ class DavStorage(Storage):
:param useragent: Default ``vdirsyncer``.
:param unsafe_href_chars: Replace the given characters when generating
hrefs. Defaults to ``'@'``.
+
+ .. note::
+
+ Please also see :doc:`supported` for very important information, as
+ changing some of the default options might be very dangerous with some
+ servers.
'''
# the file extension of items. Useful for testing against radicale.
@@ -478,7 +478,7 @@ class DavStorage(Storage):
class CaldavStorage(DavStorage):
__doc__ = '''
- CalDAV. Usable as ``caldav`` in the config file.
+ CalDAV.
You can set a timerange to synchronize with the parameters ``start_date``
and ``end_date``. Inside those parameters, you can use any Python
@@ -492,15 +492,13 @@ class CaldavStorage(DavStorage):
Either both or none have to be specified. The default is to synchronize
everything.
- ''' + DavStorage.__doc__ + '''
: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: Comma-separated collection types to show from the
server. Dependent on server functionality, no clientside validation of
results. The empty value ``''`` is the same as ``'VTODO, VEVENT,
VJOURNAL'``.
-
- '''
+ ''' + DavStorage.__doc__
storage_name = 'caldav'
fileext = '.ics'
@@ -625,7 +623,7 @@ class CaldavStorage(DavStorage):
class CarddavStorage(DavStorage):
__doc__ = '''
- CardDAV. Usable as ``carddav`` in the config file.
+ CardDAV.
''' + DavStorage.__doc__
storage_name = 'carddav'
diff --git a/vdirsyncer/storage/filesystem.py b/vdirsyncer/storage/filesystem.py
index f656b7f..43a5f9b 100644
--- a/vdirsyncer/storage/filesystem.py
+++ b/vdirsyncer/storage/filesystem.py
@@ -20,9 +20,10 @@ logger = log.get(__name__)
class FilesystemStorage(Storage):
'''
- Saves each item in its own file, given a directory. Can be used with `khal
- `_. See :doc:`vdir` for a more formal
- description of the format. Usable as ``filesystem`` in the config file.
+ Saves each item in its own file, given a directory.
+
+ Can be used with `khal `_. See :doc:`vdir` for
+ a more formal description of the format.
:param path: Absolute path to a vdir or collection, depending on the
collection parameter (see :py:class:`vdirsyncer.storage.base.Storage`).
diff --git a/vdirsyncer/storage/http.py b/vdirsyncer/storage/http.py
index d9a776b..190c1e8 100644
--- a/vdirsyncer/storage/http.py
+++ b/vdirsyncer/storage/http.py
@@ -46,8 +46,7 @@ def prepare_verify(verify):
class HttpStorage(Storage):
'''
- Use a simple ``.ics`` file (or similar) from the web. Usable as ``http`` in
- the config file.
+ Use a simple ``.ics`` file (or similar) from the web.
:param url: URL to the ``.ics`` file.
:param username: Username for authentication.
diff --git a/vdirsyncer/storage/singlefile.py b/vdirsyncer/storage/singlefile.py
index 165db86..76dffc4 100644
--- a/vdirsyncer/storage/singlefile.py
+++ b/vdirsyncer/storage/singlefile.py
@@ -20,8 +20,7 @@ logger = log.get(__name__)
class SingleFileStorage(Storage):
- '''Save data in single ``.vcf`` or ``.ics`` file. Usable as ``singlefile``
- in the config file.
+ '''Save data in single local ``.vcf`` or ``.ics`` file.
The storage basically guesses how items should be joined in the file.
diff --git a/vdirsyncer/utils/__init__.py b/vdirsyncer/utils/__init__.py
index 6f9e1a1..f917717 100644
--- a/vdirsyncer/utils/__init__.py
+++ b/vdirsyncer/utils/__init__.py
@@ -282,7 +282,17 @@ def get_etag_from_file(fpath):
return '{:.9f}'.format(os.path.getmtime(fpath))
-def get_class_init_args(cls):
+def get_class_init_specs(cls, stop_at=object):
+ if cls is stop_at:
+ return ()
+ import inspect
+ spec = inspect.getargspec(cls.__init__)
+ supercls = next(getattr(x.__init__, '__objclass__', x)
+ for x in cls.__mro__[1:])
+ return (spec,) + get_class_init_specs(supercls, stop_at=stop_at)
+
+
+def get_class_init_args(cls, stop_at=object):
'''
Get args which are taken during class initialization. Assumes that all
classes' __init__ calls super().__init__ with the rest of the arguments.
@@ -292,19 +302,12 @@ def get_class_init_args(cls):
class can take, and ``required`` is the subset of arguments the class
requires.
'''
- import inspect
+ all, required = set(), set()
+ for spec in get_class_init_specs(cls, stop_at=stop_at):
+ all.update(spec.args[1:])
+ required.update(spec.args[1:-len(spec.defaults or ())])
- if cls is object:
- return set(), set()
-
- spec = inspect.getargspec(cls.__init__)
- all = set(spec.args[1:])
- required = set(spec.args[1:-len(spec.defaults or ())])
- supercls = next(getattr(x.__init__, '__objclass__', x)
- for x in cls.__mro__[1:])
- s_all, s_required = get_class_init_args(supercls)
-
- return all | s_all, required | s_required
+ return all, required
def checkdir(path, create=False, mode=0o750):