diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3d3a88e..9943738 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,15 +9,18 @@ Package maintainers and users who have to manually update their installation may want to subscribe to `GitHub's tag feed `_. +Version 0.16.6 +============== + +- **Packagers:** Documentation building no longer needs a working installation + of vdirsyncer. + Version 0.16.5 ============== -*released on 13 June 2018* - - **Packagers:** click-log 0.3 is required. - All output will now happen on stderr (because of the upgrade of ``click-log``). - Version 0.16.4 ============== diff --git a/docs-requirements.txt b/docs-requirements.txt index ae6b606..08275e4 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -1,2 +1,3 @@ sphinx != 1.4.7 sphinx_rtd_theme +setuptools_scm diff --git a/docs/conf.py b/docs/conf.py index c261c4c..e4277e3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -6,7 +6,7 @@ import os from sphinx.ext import autodoc -import vdirsyncer +import setuptools_scm extensions = ['sphinx.ext.autodoc'] @@ -19,7 +19,7 @@ project = u'vdirsyncer' copyright = (u'2014-{}, Markus Unterwaditzer & contributors' .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. 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) return [prb], [msg] - import vdirsyncer 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', issue_num) linktext = ('issue #{}' if name == 'gh' @@ -87,64 +88,9 @@ def github_issue_role(name, rawtext, text, lineno, inliner, 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), - '') - - 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): from sphinx.domains.python import PyObject app.add_object_type('storage', 'storage', 'pair: %s; storage', doc_field_types=PyObject.doc_field_types) 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 d6a47a9..89a9c8f 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -150,9 +150,107 @@ Supported Storages 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 ++++++ @@ -206,9 +304,42 @@ or write anything to it. a rather hidden `settings page `_. -.. 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 +++++++ @@ -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 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 +++++ -.. 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 `_. 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 ++++++++++++++++++ @@ -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 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``. diff --git a/vdirsyncer/http.py b/vdirsyncer/http.py index 915d7aa..cb13ae3 100644 --- a/vdirsyncer/http.py +++ b/vdirsyncer/http.py @@ -93,26 +93,6 @@ def prepare_client_cert(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): prefix = 'https://' try: diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index 2c84b20..47ccf8b 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -12,7 +12,7 @@ from requests.exceptions import HTTPError from .base import Storage, normalize_meta_value 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 from ..vobject import Item @@ -397,17 +397,6 @@ class DAVSession(object): 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. fileext = None # mimetype of items @@ -714,36 +703,6 @@ class DAVStorage(Storage): 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' fileext = '.ics' item_mimetype = 'text/calendar' @@ -868,11 +827,6 @@ class CalDAVStorage(DAVStorage): class CardDAVStorage(DAVStorage): - - __doc__ = ''' - CardDAV. - ''' + DAVStorage.__doc__ - storage_name = 'carddav' fileext = '.vcf' item_mimetype = 'text/vcard' diff --git a/vdirsyncer/storage/etesync.py b/vdirsyncer/storage/etesync.py index ebdfea4..a390cbd 100644 --- a/vdirsyncer/storage/etesync.py +++ b/vdirsyncer/storage/etesync.py @@ -100,14 +100,6 @@ class _Session: 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 _item_type = None _at_once = False @@ -225,20 +217,12 @@ class EtesyncStorage(Storage): class EtesyncContacts(EtesyncStorage): - __doc__ = ''' - Contacts for EteSync. - ''' + EtesyncStorage.__doc__ - _collection_type = AddressBook _item_type = Contact storage_name = 'etesync_contacts' class EtesyncCalendars(EtesyncStorage): - __doc__ = ''' - Calendars for EteSync. - ''' + EtesyncStorage.__doc__ - _collection_type = Calendar _item_type = Event storage_name = 'etesync_calendars' diff --git a/vdirsyncer/storage/filesystem.py b/vdirsyncer/storage/filesystem.py index 271c9a3..67892e9 100644 --- a/vdirsyncer/storage/filesystem.py +++ b/vdirsyncer/storage/filesystem.py @@ -17,28 +17,6 @@ logger = logging.getLogger(__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. - - 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' _repr_attributes = ('path',) diff --git a/vdirsyncer/storage/google.py b/vdirsyncer/storage/google.py index c2464e0..8558f3b 100644 --- a/vdirsyncer/storage/google.py +++ b/vdirsyncer/storage/google.py @@ -101,20 +101,7 @@ class GoogleSession(dav.DAVSession): _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): - __doc__ = '''Google calendar. - - Please refer to :storage:`caldav` regarding - the ``item_types`` and timerange parameters. - ''' + GOOGLE_PARAMS_DOCS - class session_class(GoogleSession): url = 'https://apidata.googleusercontent.com/caldav/v2/' scope = ['https://www.googleapis.com/auth/calendar'] @@ -150,10 +137,6 @@ class GoogleCalendarStorage(dav.CalDAVStorage): class GoogleContactsStorage(dav.CardDAVStorage): - __doc__ = '''Google contacts. - - ''' + GOOGLE_PARAMS_DOCS - class session_class(GoogleSession): # Google CardDAV is completely bonkers. Collection discovery doesn't # work properly, well-known URI takes us directly to single collection diff --git a/vdirsyncer/storage/http.py b/vdirsyncer/storage/http.py index c71c48c..e2ea90f 100644 --- a/vdirsyncer/storage/http.py +++ b/vdirsyncer/storage/http.py @@ -4,45 +4,12 @@ import urllib.parse as urlparse from .base import Storage from .. import exceptions -from ..http import HTTP_STORAGE_PARAMETERS, USERAGENT, prepare_auth, \ +from ..http import USERAGENT, prepare_auth, \ prepare_client_cert, prepare_verify, request from ..vobject import Item, split_collection 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' read_only = True _repr_attributes = ('username', 'url') diff --git a/vdirsyncer/storage/singlefile.py b/vdirsyncer/storage/singlefile.py index 17f224b..dfad262 100644 --- a/vdirsyncer/storage/singlefile.py +++ b/vdirsyncer/storage/singlefile.py @@ -30,56 +30,6 @@ def _writing_op(f): 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' _repr_attributes = ('path',)