From 8933da7db4fb7adf2e90b35e2950c39f08b9eef1 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 27 Dec 2014 16:43:49 +0100 Subject: [PATCH] Add example configuration to storage docs --- docs/conf.py | 25 +++++++++++++++++++++++++ docs/config.rst | 10 +++++----- vdirsyncer/cli/utils.py | 14 ++++++++++++++ vdirsyncer/storage/dav.py | 20 +++++++++----------- vdirsyncer/storage/filesystem.py | 7 ++++--- vdirsyncer/storage/http.py | 3 +-- vdirsyncer/storage/singlefile.py | 3 +-- vdirsyncer/utils/__init__.py | 29 ++++++++++++++++------------- 8 files changed, 75 insertions(+), 36 deletions(-) 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):