From 56b1fc2187228d5e49c68ada6812081a986dc142 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 9 Jun 2020 14:17:56 +0200 Subject: [PATCH 1/6] Remove now-unused Makefile target --- Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Makefile b/Makefile index 805caf0..e862e36 100644 --- a/Makefile +++ b/Makefile @@ -106,9 +106,6 @@ docs: linkcheck: sphinx-build -W -b linkcheck ./docs/ ./docs/_build/linkcheck/ -release: - python setup.py sdist bdist_wheel upload - release-deb: sh scripts/release-deb.sh debian jessie sh scripts/release-deb.sh debian stretch From 9cb1f8d704ec8c0d14c485ac51cd97f6541681cc Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 9 Jun 2020 14:33:14 +0200 Subject: [PATCH 2/6] Drop support for Python 3.5 and 3.6 --- .travis.yml | 62 +++--------------------------- docs/conf.py | 2 +- docs/installation.rst | 2 +- scripts/make_travisconf.py | 11 ++---- setup.py | 2 - tests/storage/__init__.py | 4 +- tests/system/cli/test_repair.py | 2 +- tests/system/cli/test_sync.py | 2 +- tests/unit/cli/test_fetchparams.py | 2 +- tests/unit/sync/test_sync.py | 4 +- tests/unit/test_repair.py | 8 ++-- tests/unit/utils/test_vobject.py | 4 +- vdirsyncer/__init__.py | 4 +- vdirsyncer/cli/__init__.py | 4 +- vdirsyncer/cli/config.py | 4 +- vdirsyncer/cli/discover.py | 4 +- vdirsyncer/cli/fetchparams.py | 6 +-- vdirsyncer/cli/tasks.py | 6 +-- vdirsyncer/cli/utils.py | 11 +++--- vdirsyncer/exceptions.py | 4 +- vdirsyncer/http.py | 4 +- vdirsyncer/metasync.py | 12 +++--- vdirsyncer/repair.py | 2 +- vdirsyncer/storage/base.py | 2 +- vdirsyncer/storage/dav.py | 14 +++---- vdirsyncer/storage/etesync.py | 4 +- vdirsyncer/storage/google.py | 2 +- vdirsyncer/storage/memory.py | 2 +- vdirsyncer/utils.py | 6 +-- vdirsyncer/vobject.py | 16 ++++---- 30 files changed, 77 insertions(+), 135 deletions(-) diff --git a/.travis.yml b/.travis.yml index e12bc0c..f857063 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,75 +19,23 @@ "include": [ { "env": "BUILD=style", - "python": "3.6" + "python": "3.7" }, { "env": "BUILD=test REQUIREMENTS=release", - "python": "3.5" - }, - { - "dist": "trusty", - "env": "BUILD=test-storage DAV_SERVER=radicale REQUIREMENTS=release ", - "python": "3.5" - }, - { - "dist": "trusty", - "env": "BUILD=test-storage DAV_SERVER=xandikos REQUIREMENTS=release ", - "python": "3.5" - }, - { - "env": "BUILD=test REQUIREMENTS=minimal", - "python": "3.5" - }, - { - "dist": "trusty", - "env": "BUILD=test-storage DAV_SERVER=radicale REQUIREMENTS=minimal ", - "python": "3.5" - }, - { - "dist": "trusty", - "env": "BUILD=test-storage DAV_SERVER=xandikos REQUIREMENTS=minimal ", - "python": "3.5" - }, - { - "env": "BUILD=test REQUIREMENTS=release", - "python": "3.6" + "python": "3.7" }, { "env": "BUILD=test-storage DAV_SERVER=radicale REQUIREMENTS=release ", - "python": "3.6" + "python": "3.7" }, { "env": "BUILD=test-storage DAV_SERVER=xandikos REQUIREMENTS=release ", - "python": "3.6" + "python": "3.7" }, { "env": "BUILD=test-storage DAV_SERVER=fastmail REQUIREMENTS=release ", "if": "NOT (type IN (pull_request))", - "python": "3.6" - }, - { - "env": "BUILD=test REQUIREMENTS=minimal", - "python": "3.6" - }, - { - "env": "BUILD=test-storage DAV_SERVER=radicale REQUIREMENTS=minimal ", - "python": "3.6" - }, - { - "env": "BUILD=test-storage DAV_SERVER=xandikos REQUIREMENTS=minimal ", - "python": "3.6" - }, - { - "env": "BUILD=test REQUIREMENTS=release", - "python": "3.7" - }, - { - "env": "BUILD=test-storage DAV_SERVER=radicale REQUIREMENTS=release ", - "python": "3.7" - }, - { - "env": "BUILD=test-storage DAV_SERVER=xandikos REQUIREMENTS=release ", "python": "3.7" }, { @@ -128,7 +76,7 @@ }, { "env": "BUILD=test ETESYNC_TESTS=true REQUIREMENTS=latest", - "python": "3.6" + "python": "3.7" } ] }, diff --git a/docs/conf.py b/docs/conf.py index 385d2e8..5fe5e51 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -65,7 +65,7 @@ def github_issue_role(name, rawtext, text, lineno, inliner, if issue_num <= 0: raise ValueError() except ValueError: - msg = inliner.reporter.error('Invalid GitHub issue: {}'.format(text), + msg = inliner.reporter.error(f'Invalid GitHub issue: {text}', line=lineno) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] diff --git a/docs/installation.rst b/docs/installation.rst index 7690f2d..2d33c57 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -41,7 +41,7 @@ If your distribution doesn't provide a package for vdirsyncer, you still can use Python's package manager "pip". First, you'll have to check that the following things are installed: -- Python 3.5+ and pip. +- Python 3.7+ and pip. - ``libxml`` and ``libxslt`` - ``zlib`` - Linux or OS X. **Windows is not supported**, see :gh:`535`. diff --git a/scripts/make_travisconf.py b/scripts/make_travisconf.py index 07f4342..3e5882d 100755 --- a/scripts/make_travisconf.py +++ b/scripts/make_travisconf.py @@ -3,8 +3,7 @@ import itertools import json -python_versions = ("3.5", "3.6", "3.7", "3.8") -latest_python = "3.6" +python_versions = ["3.7", "3.8"] cfg = {} @@ -34,7 +33,7 @@ matrix = [] cfg['matrix'] = {'include': matrix, 'fast_finish': True} matrix.append({ - 'python': latest_python, + 'python': python_versions[0], 'env': 'BUILD=style' }) @@ -51,7 +50,7 @@ for python, requirements in itertools.product( 'env': f"BUILD=test REQUIREMENTS={requirements}", }) - if python == latest_python and requirements == "release": + if python == python_versions[0] and requirements == "release": dav_servers += ("fastmail",) for dav_server in dav_servers: @@ -61,8 +60,6 @@ for python, requirements in itertools.product( f"DAV_SERVER={dav_server} " f"REQUIREMENTS={requirements} ") } - if python == '3.5': - job['dist'] = 'trusty' build_prs = dav_server not in ("fastmail", "davical", "icloud") if not build_prs: @@ -71,7 +68,7 @@ for python, requirements in itertools.product( matrix.append(job) matrix.append({ - 'python': latest_python, + 'python': python_versions[0], 'env': ("BUILD=test " "ETESYNC_TESTS=true " "REQUIREMENTS=latest") diff --git a/setup.py b/setup.py index 653be04..9221367 100644 --- a/setup.py +++ b/setup.py @@ -87,8 +87,6 @@ setup( 'License :: OSI Approved :: BSD License', 'Operating System :: POSIX', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Topic :: Internet', diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py index dd0d0b3..b268972 100644 --- a/tests/storage/__init__.py +++ b/tests/storage/__init__.py @@ -19,7 +19,7 @@ from .. import EVENT_TEMPLATE, TASK_TEMPLATE, VCARD_TEMPLATE, \ def get_server_mixin(server_name): from . import __name__ as base - x = __import__('{}.servers.{}'.format(base, server_name), fromlist=['']) + x = __import__(f'{base}.servers.{server_name}', fromlist=['']) return x.ServerMixin @@ -183,7 +183,7 @@ class StorageTests: def test_discover(self, requires_collections, get_storage_args, get_item): collections = set() for i in range(1, 5): - collection = 'test{}'.format(i) + collection = f'test{i}' s = self.storage_class(**get_storage_args(collection=collection)) assert not list(s.list()) s.upload(get_item()) diff --git a/tests/system/cli/test_repair.py b/tests/system/cli/test_repair.py index cad41f6..d3c05cf 100644 --- a/tests/system/cli/test_repair.py +++ b/tests/system/cli/test_repair.py @@ -19,7 +19,7 @@ def storage(tmpdir, runner): def test_basic(storage, runner, collection): if collection is not None: storage = storage.mkdir(collection) - collection_arg = 'foo/{}'.format(collection) + collection_arg = f'foo/{collection}' else: collection_arg = 'foo' diff --git a/tests/system/cli/test_sync.py b/tests/system/cli/test_sync.py index 97e21e8..e84d798 100644 --- a/tests/system/cli/test_sync.py +++ b/tests/system/cli/test_sync.py @@ -461,7 +461,7 @@ def test_partial_sync(tmpdir, runner, partial_sync): fileext = ".txt" path = "{base}/bar" '''.format( - partial_sync=('partial_sync = "{}"\n'.format(partial_sync) + partial_sync=(f'partial_sync = "{partial_sync}"\n' if partial_sync else ''), base=str(tmpdir) ))) diff --git a/tests/unit/cli/test_fetchparams.py b/tests/unit/cli/test_fetchparams.py index 2d28507..9339066 100644 --- a/tests/unit/cli/test_fetchparams.py +++ b/tests/unit/cli/test_fetchparams.py @@ -47,7 +47,7 @@ def test_key_conflict(monkeypatch, mystrategy): @given(s=st.text(), t=st.text(min_size=1)) def test_fuzzing(s, t, mystrategy): config = expand_fetch_params({ - '{}.fetch'.format(s): ['mystrategy', t] + f'{s}.fetch': ['mystrategy', t] }) assert config[s] == t diff --git a/tests/unit/sync/test_sync.py b/tests/unit/sync/test_sync.py index 4e9651f..4d554c5 100644 --- a/tests/unit/sync/test_sync.py +++ b/tests/unit/sync/test_sync.py @@ -253,7 +253,7 @@ def test_conflict_resolution_both_etags_new(winning_storage): b.update(href_b, item_b, etag_b) with pytest.raises(SyncConflict): sync(a, b, status) - sync(a, b, status, conflict_resolution='{} wins'.format(winning_storage)) + sync(a, b, status, conflict_resolution=f'{winning_storage} wins') assert items(a) == items(b) == { item_a.raw if winning_storage == 'a' else item_b.raw } @@ -563,7 +563,7 @@ class SyncMachine(RuleBasedStateMachine): uid=uid_strategy, etag=st.text()) def upload(self, storage, uid, etag): - item = Item('UID:{}'.format(uid)) + item = Item(f'UID:{uid}') storage.items[uid] = (etag, item) @rule(storage=Storage, href=st.text()) diff --git a/tests/unit/test_repair.py b/tests/unit/test_repair.py index 5467c54..b444d0b 100644 --- a/tests/unit/test_repair.py +++ b/tests/unit/test_repair.py @@ -18,11 +18,11 @@ def test_repair_uids(uid): s.items = { 'one': ( 'asdf', - Item('BEGIN:VCARD\nFN:Hans\nUID:{}\nEND:VCARD'.format(uid)) + Item(f'BEGIN:VCARD\nFN:Hans\nUID:{uid}\nEND:VCARD') ), 'two': ( 'asdf', - Item('BEGIN:VCARD\nFN:Peppi\nUID:{}\nEND:VCARD'.format(uid)) + Item(f'BEGIN:VCARD\nFN:Peppi\nUID:{uid}\nEND:VCARD') ) } @@ -40,7 +40,7 @@ def test_repair_uids(uid): @settings(suppress_health_check=HealthCheck.all()) def test_repair_unsafe_uids(uid): s = MemoryStorage() - item = Item('BEGIN:VCARD\nUID:{}\nEND:VCARD'.format(uid)) + item = Item(f'BEGIN:VCARD\nUID:{uid}\nEND:VCARD') href, etag = s.upload(item) assert s.get(href)[0].uid == uid assert not href_safe(uid) @@ -58,7 +58,7 @@ def test_repair_unsafe_uids(uid): ('perfectly-fine', 'b@dh0mbr3') ]) def test_repair_unsafe_href(uid, href): - item = Item('BEGIN:VCARD\nUID:{}\nEND:VCARD'.format(uid)) + item = Item(f'BEGIN:VCARD\nUID:{uid}\nEND:VCARD') new_item = repair_item(href, item, set(), True) assert new_item.raw != item.raw assert new_item.uid != item.uid diff --git a/tests/unit/utils/test_vobject.py b/tests/unit/utils/test_vobject.py index 84d87a8..d029ed9 100644 --- a/tests/unit/utils/test_vobject.py +++ b/tests/unit/utils/test_vobject.py @@ -221,7 +221,7 @@ def test_replace_uid(template, uid): item = vobject.Item(template.format(r=123, uid=123)).with_uid(uid) assert item.uid == uid if uid: - assert item.raw.count('\nUID:{}'.format(uid)) == 1 + assert item.raw.count(f'\nUID:{uid}') == 1 else: assert '\nUID:' not in item.raw @@ -317,7 +317,7 @@ class VobjectMachine(RuleBasedStateMachine): params=st.lists(st.tuples(value_strategy, value_strategy))) def add_prop_raw(self, c, key, value, params): params_str = ','.join(k + '=' + v for k, v in params) - c.props.insert(0, '{};{}:{}'.format(key, params_str, value)) + c.props.insert(0, f'{key};{params_str}:{value}') assert c[key] == value assert key in c assert c.get(key) == value diff --git a/vdirsyncer/__init__.py b/vdirsyncer/__init__.py index 84dbb69..dbdd529 100644 --- a/vdirsyncer/__init__.py +++ b/vdirsyncer/__init__.py @@ -19,8 +19,8 @@ except ImportError: # pragma: no cover def _check_python_version(): # pragma: no cover import sys - if sys.version_info < (3, 4, 0): - print('vdirsyncer requires at least Python 3.5.') + if sys.version_info < (3, 7, 0): + print('vdirsyncer requires at least Python 3.7.') sys.exit(1) diff --git a/vdirsyncer/cli/__init__.py b/vdirsyncer/cli/__init__.py index 116759a..8f0e4e0 100644 --- a/vdirsyncer/cli/__init__.py +++ b/vdirsyncer/cli/__init__.py @@ -65,7 +65,7 @@ def max_workers_callback(ctx, param, value): if value == 0 and logging.getLogger('vdirsyncer').level == logging.DEBUG: value = 1 - cli_logger.debug('Using {} maximal workers.'.format(value)) + cli_logger.debug(f'Using {value} maximal workers.') return value @@ -75,7 +75,7 @@ def max_workers_option(default=0): help += 'The default is 0, which means "as many as necessary". ' \ 'With -vdebug enabled, the default is 1.' else: - help += 'The default is {}.'.format(default) + help += f'The default is {default}.' return click.option( '--max-workers', default=default, type=click.IntRange(min=0, max=None), diff --git a/vdirsyncer/cli/config.py b/vdirsyncer/cli/config.py index 643e506..3e74420 100644 --- a/vdirsyncer/cli/config.py +++ b/vdirsyncer/cli/config.py @@ -101,7 +101,7 @@ class _ConfigReader: def _parse_section(self, section_type, name, options): validate_section_name(name, section_type) if name in self._seen_names: - raise ValueError('Name "{}" already used.'.format(name)) + raise ValueError(f'Name "{name}" already used.') self._seen_names.add(name) if section_type == 'general': @@ -163,7 +163,7 @@ class Config: try: self.pairs[name] = PairConfig(self, name, options) except ValueError as e: - raise exceptions.UserError('Pair {}: {}'.format(name, e)) + raise exceptions.UserError(f'Pair {name}: {e}') @classmethod def from_fileobject(cls, f): diff --git a/vdirsyncer/cli/discover.py b/vdirsyncer/cli/discover.py index 30addc3..b3bff73 100644 --- a/vdirsyncer/cli/discover.py +++ b/vdirsyncer/cli/discover.py @@ -211,7 +211,7 @@ def _print_collections(instance_name, get_discovered): logger.warning('Failed to discover collections for {}, use `-vdebug` ' 'to see the full traceback.'.format(instance_name)) return - logger.info('{}:'.format(instance_name)) + logger.info(f'{instance_name}:') for args in discovered.values(): collection = args['collection'] if collection is None: @@ -226,7 +226,7 @@ def _print_collections(instance_name, get_discovered): logger.info(' - {}{}'.format( json.dumps(collection), - ' ("{}")'.format(displayname) + f' ("{displayname}")' if displayname and displayname != collection else '' )) diff --git a/vdirsyncer/cli/fetchparams.py b/vdirsyncer/cli/fetchparams.py index 346a14f..801d599 100644 --- a/vdirsyncer/cli/fetchparams.py +++ b/vdirsyncer/cli/fetchparams.py @@ -19,7 +19,7 @@ def expand_fetch_params(config): newkey = key[:-len(SUFFIX)] if newkey in config: - raise ValueError('Can\'t set {} and {}.'.format(key, newkey)) + raise ValueError(f'Can\'t set {key} and {newkey}.') config[newkey] = _fetch_value(config[key], key) del config[key] @@ -45,7 +45,7 @@ def _fetch_value(opts, key): cache_key = tuple(opts) if cache_key in password_cache: rv = password_cache[cache_key] - logger.debug('Found cached value for {!r}.'.format(opts)) + logger.debug(f'Found cached value for {opts!r}.') if isinstance(rv, BaseException): raise rv return rv @@ -54,7 +54,7 @@ def _fetch_value(opts, key): try: strategy_fn = STRATEGIES[strategy] except KeyError: - raise exceptions.UserError('Unknown strategy: {}'.format(strategy)) + raise exceptions.UserError(f'Unknown strategy: {strategy}') logger.debug('Fetching value for {} with {} strategy.' .format(key, strategy)) diff --git a/vdirsyncer/cli/tasks.py b/vdirsyncer/cli/tasks.py index da7adc5..7cadaf2 100644 --- a/vdirsyncer/cli/tasks.py +++ b/vdirsyncer/cli/tasks.py @@ -45,7 +45,7 @@ def sync_collection(wq, collection, general, force_delete): status_name = get_status_name(pair.name, collection.name) try: - cli_logger.info('Syncing {}'.format(status_name)) + cli_logger.info(f'Syncing {status_name}') a = storage_instance_from_config(collection.config_a) b = storage_instance_from_config(collection.config_b) @@ -110,7 +110,7 @@ def repair_collection(config, collection, repair_unsafe_uid): config['type'] = storage_type storage = storage_instance_from_config(config) - cli_logger.info('Repairing {}/{}'.format(storage_name, collection)) + cli_logger.info(f'Repairing {storage_name}/{collection}') cli_logger.warning('Make sure no other program is talking to the server.') repair_storage(storage, repair_unsafe_uid=repair_unsafe_uid) @@ -121,7 +121,7 @@ def metasync_collection(wq, collection, general): status_name = get_status_name(pair.name, collection.name) try: - cli_logger.info('Metasyncing {}'.format(status_name)) + cli_logger.info(f'Metasyncing {status_name}') status = load_status(general['status_path'], pair.name, collection.name, data_type='metadata') or {} diff --git a/vdirsyncer/cli/utils.py b/vdirsyncer/cli/utils.py index 765c411..46e37f2 100644 --- a/vdirsyncer/cli/utils.py +++ b/vdirsyncer/cli/utils.py @@ -144,11 +144,11 @@ def handle_cli_error(status_name=None, e=None): import traceback tb = traceback.format_tb(tb) if status_name: - msg = 'Unknown error occurred for {}'.format(status_name) + msg = f'Unknown error occurred for {status_name}' else: msg = 'Unknown error occurred' - msg += ': {}\nUse `-vdebug` to see the full traceback.'.format(e) + msg += f': {e}\nUse `-vdebug` to see the full traceback.' cli_logger.error(msg) cli_logger.debug(''.join(tb)) @@ -210,8 +210,7 @@ def manage_sync_status(base_path, pair_name, collection_name): with open(path, 'rb') as f: if f.read(1) == b'{': f.seek(0) - # json.load doesn't work on binary files for Python 3.5 - legacy_status = dict(json.loads(f.read().decode('utf-8'))) + legacy_status = dict(json.load(f)) except (OSError, ValueError): pass @@ -247,7 +246,7 @@ def storage_class_from_config(config): cls = storage_names[storage_name] except KeyError: raise exceptions.UserError( - 'Unknown storage type: {}'.format(storage_name)) + f'Unknown storage type: {storage_name}') return cls, config @@ -399,7 +398,7 @@ def handle_collection_not_found(config, collection, e=None): storage_name = config.get('instance_name', None) cli_logger.warning('{}No collection {} found for storage {}.' - .format('{}\n'.format(e) if e else '', + .format(f'{e}\n' if e else '', json.dumps(collection), storage_name)) if click.confirm('Should vdirsyncer attempt to create it?'): diff --git a/vdirsyncer/exceptions.py b/vdirsyncer/exceptions.py index 5644b54..b21cd27 100644 --- a/vdirsyncer/exceptions.py +++ b/vdirsyncer/exceptions.py @@ -10,7 +10,7 @@ class Error(Exception): def __init__(self, *args, **kwargs): for key, value in kwargs.items(): if getattr(self, key, object()) is not None: # pragma: no cover - raise TypeError('Invalid argument: {}'.format(key)) + raise TypeError(f'Invalid argument: {key}') setattr(self, key, value) super().__init__(*args) @@ -25,7 +25,7 @@ class UserError(Error, ValueError): def __str__(self): msg = Error.__str__(self) for problem in self.problems or (): - msg += '\n - {}'.format(problem) + msg += f'\n - {problem}' return msg diff --git a/vdirsyncer/http.py b/vdirsyncer/http.py index 5cb6204..8549216 100644 --- a/vdirsyncer/http.py +++ b/vdirsyncer/http.py @@ -7,7 +7,7 @@ from . import DOCS_HOME, exceptions, __version__ logger = logging.getLogger(__name__) -USERAGENT = 'vdirsyncer/{}'.format(__version__) +USERAGENT = f'vdirsyncer/{__version__}' def _detect_faulty_requests(): # pragma: no cover @@ -133,7 +133,7 @@ def request(method, url, session=None, latin1_fallback=True, func = session.request - logger.debug('{} {}'.format(method, url)) + logger.debug(f'{method} {url}') logger.debug(kwargs.get('headers', {})) logger.debug(kwargs.get('data', None)) logger.debug('Sending request...') diff --git a/vdirsyncer/metasync.py b/vdirsyncer/metasync.py index 875f255..82a4556 100644 --- a/vdirsyncer/metasync.py +++ b/vdirsyncer/metasync.py @@ -16,12 +16,12 @@ class MetaSyncConflict(MetaSyncError): def metasync(storage_a, storage_b, status, keys, conflict_resolution=None): def _a_to_b(): - logger.info('Copying {} to {}'.format(key, storage_b)) + logger.info(f'Copying {key} to {storage_b}') storage_b.set_meta(key, a) status[key] = a def _b_to_a(): - logger.info('Copying {} to {}'.format(key, storage_a)) + logger.info(f'Copying {key} to {storage_a}') storage_a.set_meta(key, b) status[key] = b @@ -45,10 +45,10 @@ def metasync(storage_a, storage_b, status, keys, conflict_resolution=None): a = storage_a.get_meta(key) b = storage_b.get_meta(key) s = normalize_meta_value(status.get(key)) - logger.debug('Key: {}'.format(key)) - logger.debug('A: {}'.format(a)) - logger.debug('B: {}'.format(b)) - logger.debug('S: {}'.format(s)) + logger.debug(f'Key: {key}') + logger.debug(f'A: {a}') + logger.debug(f'B: {b}') + logger.debug(f'S: {s}') if a != s and b != s: _resolve_conflict() diff --git a/vdirsyncer/repair.py b/vdirsyncer/repair.py index 6c8b257..89c2c3a 100644 --- a/vdirsyncer/repair.py +++ b/vdirsyncer/repair.py @@ -25,7 +25,7 @@ def repair_storage(storage, repair_unsafe_uid): 'The PRODID property may indicate which software ' 'created this item.' .format(href)) - logger.error('Item content: {!r}'.format(item.raw)) + logger.error(f'Item content: {item.raw!r}') continue seen_uids.add(new_item.uid) diff --git a/vdirsyncer/storage/base.py b/vdirsyncer/storage/base.py index 7dfcbdd..b78f828 100644 --- a/vdirsyncer/storage/base.py +++ b/vdirsyncer/storage/base.py @@ -71,7 +71,7 @@ class Storage(metaclass=StorageMeta): self.read_only = bool(read_only) if collection and instance_name: - instance_name = '{}/{}'.format(instance_name, collection) + instance_name = f'{instance_name}/{collection}' self.instance_name = instance_name self.collection = collection diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index a3becb7..4481e15 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -34,7 +34,7 @@ del _generate_path_reserved_chars def _contains_quoted_reserved_chars(x): for y in _path_reserved_chars: if y in x: - dav_logger.debug('Unsafe character: {!r}'.format(y)) + dav_logger.debug(f'Unsafe character: {y!r}') return True return False @@ -52,7 +52,7 @@ def _assert_multistatus_success(r): except (ValueError, IndexError): continue if st < 200 or st >= 400: - raise HTTPError('Server error: {}'.format(st)) + raise HTTPError(f'Server error: {st}') def _normalize_href(base, href): @@ -78,7 +78,7 @@ def _normalize_href(base, href): x = urlparse.quote(x, '/@%:') if orig_href == x: - dav_logger.debug('Already normalized: {!r}'.format(x)) + dav_logger.debug(f'Already normalized: {x!r}') else: dav_logger.debug('Normalized URL from {!r} to {!r}' .format(orig_href, x)) @@ -459,7 +459,7 @@ class DAVStorage(Storage): for href in hrefs: if href != self._normalize_href(href): raise exceptions.NotFoundError(href) - href_xml.append('{}'.format(href)) + href_xml.append(f'{href}') if not href_xml: return () @@ -591,7 +591,7 @@ class DAVStorage(Storage): props = _merge_xml(props) if props.find('{DAV:}resourcetype/{DAV:}collection') is not None: - dav_logger.debug('Skipping {!r}, is collection.'.format(href)) + dav_logger.debug(f'Skipping {href!r}, is collection.') continue etag = getattr(props.find('{DAV:}getetag'), 'text', '') @@ -641,7 +641,7 @@ class DAVStorage(Storage): except KeyError: raise exceptions.UnsupportedMetadataError() - xpath = '{{{}}}{}'.format(namespace, tagname) + xpath = f'{{{namespace}}}{tagname}' data = ''' @@ -674,7 +674,7 @@ class DAVStorage(Storage): except KeyError: raise exceptions.UnsupportedMetadataError() - lxml_selector = '{{{}}}{}'.format(namespace, tagname) + lxml_selector = f'{{{namespace}}}{tagname}' element = etree.Element(lxml_selector) element.text = normalize_meta_value(value) diff --git a/vdirsyncer/storage/etesync.py b/vdirsyncer/storage/etesync.py index 80339d1..e351d61 100644 --- a/vdirsyncer/storage/etesync.py +++ b/vdirsyncer/storage/etesync.py @@ -66,7 +66,7 @@ class _Session: key = self._get_key() if not key: password = click.prompt('Enter key password', hide_input=True) - click.echo('Deriving key for {}'.format(self.email)) + click.echo(f'Deriving key for {self.email}') self.etesync.derive_key(password) self._set_key(self.etesync.cipher_key) else: @@ -134,7 +134,7 @@ class EtesyncStorage(Storage): **kwargs ) else: - logger.debug('Skipping collection: {!r}'.format(entry)) + logger.debug(f'Skipping collection: {entry!r}') @classmethod def create_collection(cls, collection, email, secrets_dir, server_url=None, diff --git a/vdirsyncer/storage/google.py b/vdirsyncer/storage/google.py index f09ff48..1702f01 100644 --- a/vdirsyncer/storage/google.py +++ b/vdirsyncer/storage/google.py @@ -80,7 +80,7 @@ class GoogleSession(dav.DAVSession): # access_type and approval_prompt are Google specific # extra parameters. access_type='offline', approval_prompt='force') - click.echo('Opening {} ...'.format(authorization_url)) + click.echo(f'Opening {authorization_url} ...') try: open_graphical_browser(authorization_url) except Exception as e: diff --git a/vdirsyncer/storage/memory.py b/vdirsyncer/storage/memory.py index 74b58e8..99e533b 100644 --- a/vdirsyncer/storage/memory.py +++ b/vdirsyncer/storage/memory.py @@ -6,7 +6,7 @@ from .. import exceptions def _random_string(): - return '{:.9f}'.format(random.random()) + return f'{random.random():.9f}' class MemoryStorage(Storage): diff --git a/vdirsyncer/utils.py b/vdirsyncer/utils.py index 5559ec3..2737cbe 100644 --- a/vdirsyncer/utils.py +++ b/vdirsyncer/utils.py @@ -73,7 +73,7 @@ def get_etag_from_file(f): mtime = getattr(stat, 'st_mtime_ns', None) if mtime is None: mtime = stat.st_mtime - return '{:.9f};{}'.format(mtime, stat.st_ino) + return f'{mtime:.9f};{stat.st_ino}' def get_storage_init_specs(cls, stop_at=object): @@ -125,7 +125,7 @@ def checkdir(path, create=False, mode=0o750): if not os.path.isdir(path): if os.path.exists(path): - raise OSError('{} is not a directory.'.format(path)) + raise OSError(f'{path} is not a directory.') if create: os.makedirs(path, mode) else: @@ -143,7 +143,7 @@ def checkfile(path, create=False): checkdir(os.path.dirname(path), create=create) if not os.path.isfile(path): if os.path.exists(path): - raise OSError('{} is not a file.'.format(path)) + raise OSError(f'{path} is not a file.') if create: with open(path, 'wb'): pass diff --git a/vdirsyncer/vobject.py b/vdirsyncer/vobject.py index 521e97c..33c2d3d 100644 --- a/vdirsyncer/vobject.py +++ b/vdirsyncer/vobject.py @@ -205,14 +205,14 @@ def join_collection(items, wrappers=_default_join_wrappers): if wrapper_type is not None: lines = chain(*( - ['BEGIN:{}'.format(wrapper_type)], + [f'BEGIN:{wrapper_type}'], # XXX: wrapper_props is a list of lines (with line-wrapping), so # filtering out duplicate lines will almost certainly break # multiline-values. Since the only props we usually need to # support are PRODID and VERSION, I don't care. uniq(wrapper_props), lines, - ['END:{}'.format(wrapper_type)] + [f'END:{wrapper_type}'] )) return ''.join(line + '\r\n' for line in lines) @@ -299,14 +299,14 @@ class _Component: return rv[0] def dump_lines(self): - yield 'BEGIN:{}'.format(self.name) + yield f'BEGIN:{self.name}' yield from self.props for c in self.subcomponents: yield from c.dump_lines() - yield 'END:{}'.format(self.name) + yield f'END:{self.name}' def __delitem__(self, key): - prefix = ('{}:'.format(key), '{};'.format(key)) + prefix = (f'{key}:', f'{key};') new_lines = [] lineiter = iter(self.props) while True: @@ -329,7 +329,7 @@ class _Component: assert isinstance(val, str) assert '\n' not in val del self[key] - line = '{}:{}'.format(key, val) + line = f'{key}:{val}' self.props.append(line) def __contains__(self, obj): @@ -342,8 +342,8 @@ class _Component: raise ValueError(obj) def __getitem__(self, key): - prefix_without_params = '{}:'.format(key) - prefix_with_params = '{};'.format(key) + prefix_without_params = f'{key}:' + prefix_with_params = f'{key};' iterlines = iter(self.props) for line in iterlines: if line.startswith(prefix_without_params): From b1b4dd92feb63a0d6991a5993a47ac6a79300d53 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 9 Jun 2020 14:34:45 +0200 Subject: [PATCH 3/6] Sort imports I don't want to ever have to sort imports again. It's a poor use of developer time. Automate this with a pre-commit hook, and check this on CI. Developers: I suggest you configure your editor to use `reorder_python_imports`. It uses the standard sorting, and detects first/third party libs well. --- .pre-commit-config.yaml | 4 ++++ scripts/make_travisconf.py | 1 - setup.py | 6 +++--- tests/__init__.py | 5 +---- tests/conftest.py | 6 +++--- tests/storage/__init__.py | 18 +++++++++------- tests/storage/conftest.py | 4 ++-- tests/storage/dav/__init__.py | 10 +++------ tests/storage/dav/test_caldav.py | 13 ++++++------ tests/storage/dav/test_carddav.py | 3 +-- tests/storage/dav/test_main.py | 4 +++- .../etesync_server/etesync_server/settings.py | 1 - .../etesync_server/etesync_server/urls.py | 7 +++---- .../etesync_server/etesync_server/wsgi.py | 1 - tests/storage/etesync/test_main.py | 7 +++---- tests/storage/servers/davical/__init__.py | 3 ++- .../storage/servers/mysteryshack/__init__.py | 3 +-- tests/storage/test_filesystem.py | 3 +-- tests/storage/test_http.py | 5 ++--- tests/storage/test_http_with_singlefile.py | 4 +--- tests/storage/test_memory.py | 3 +-- tests/storage/test_singlefile.py | 3 +-- tests/system/cli/conftest.py | 3 +-- tests/system/cli/test_config.py | 3 ++- tests/system/cli/test_sync.py | 4 ++-- tests/system/cli/test_utils.py | 5 +++-- tests/system/utils/test_main.py | 5 +++-- tests/unit/cli/test_fetchparams.py | 6 +++--- tests/unit/sync/test_status.py | 6 +++--- tests/unit/sync/test_sync.py | 21 ++++++++++++------- tests/unit/test_metasync.py | 9 ++++---- tests/unit/test_repair.py | 10 +++++---- tests/unit/utils/test_vobject.py | 18 +++++++++------- vdirsyncer/cli/__init__.py | 4 ++-- vdirsyncer/cli/config.py | 6 ++++-- vdirsyncer/cli/discover.py | 10 +++++---- vdirsyncer/cli/fetchparams.py | 3 ++- vdirsyncer/cli/tasks.py | 18 ++++++++++------ vdirsyncer/cli/utils.py | 19 ++++++++++------- vdirsyncer/http.py | 4 +++- vdirsyncer/repair.py | 3 ++- vdirsyncer/storage/dav.py | 14 ++++++++----- vdirsyncer/storage/etesync.py | 2 +- vdirsyncer/storage/filesystem.py | 8 +++++-- vdirsyncer/storage/google.py | 11 +++++----- vdirsyncer/storage/http.py | 12 +++++++---- vdirsyncer/storage/memory.py | 4 ++-- vdirsyncer/storage/singlefile.py | 10 ++++++--- vdirsyncer/sync/__init__.py | 11 ++++++---- vdirsyncer/sync/status.py | 2 +- vdirsyncer/utils.py | 1 - vdirsyncer/vobject.py | 6 ++++-- 52 files changed, 196 insertions(+), 156 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 21a0985..8f0b7cd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,6 +14,10 @@ repos: hooks: - id: flake8 additional_dependencies: [flake8-import-order, flake8-bugbear] + - repo: https://github.com/asottile/reorder_python_imports + rev: v2.3.0 + hooks: + - id: reorder-python-imports - repo: local hooks: - id: update-travis diff --git a/scripts/make_travisconf.py b/scripts/make_travisconf.py index 3e5882d..99b9b25 100755 --- a/scripts/make_travisconf.py +++ b/scripts/make_travisconf.py @@ -1,5 +1,4 @@ #!/usr/bin/env python - import itertools import json diff --git a/setup.py b/setup.py index 9221367..59549f1 100644 --- a/setup.py +++ b/setup.py @@ -4,9 +4,9 @@ Vdirsyncer synchronizes calendars and contacts. Please refer to https://vdirsyncer.pimutils.org/en/stable/packaging.html for how to package vdirsyncer. ''' - - -from setuptools import Command, find_packages, setup +from setuptools import Command +from setuptools import find_packages +from setuptools import setup requirements = [ diff --git a/tests/__init__.py b/tests/__init__.py index 6948552..71f69b4 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,14 +1,11 @@ ''' Test suite for vdirsyncer. ''' - import hypothesis.strategies as st +import urllib3.exceptions from vdirsyncer.vobject import normalize_item -import urllib3 -import urllib3.exceptions - urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) diff --git a/tests/conftest.py b/tests/conftest.py index 5ed34f9..b05da73 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,10 +5,10 @@ import logging import os import click_log - -from hypothesis import HealthCheck, Verbosity, settings - import pytest +from hypothesis import HealthCheck +from hypothesis import settings +from hypothesis import Verbosity @pytest.fixture(autouse=True) diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py index b268972..a2e0022 100644 --- a/tests/storage/__init__.py +++ b/tests/storage/__init__.py @@ -1,21 +1,23 @@ import random -import uuid - import textwrap -from urllib.parse import quote as urlquote, unquote as urlunquote +import uuid +from urllib.parse import quote as urlquote +from urllib.parse import unquote as urlunquote import hypothesis.strategies as st +import pytest from hypothesis import given -import pytest - +from .. import assert_item_equals +from .. import EVENT_TEMPLATE +from .. import normalize_item +from .. import printable_characters_strategy +from .. import TASK_TEMPLATE +from .. import VCARD_TEMPLATE from vdirsyncer import exceptions from vdirsyncer.storage.base import normalize_meta_value from vdirsyncer.vobject import Item -from .. import EVENT_TEMPLATE, TASK_TEMPLATE, VCARD_TEMPLATE, \ - assert_item_equals, normalize_item, printable_characters_strategy - def get_server_mixin(server_name): from . import __name__ as base diff --git a/tests/storage/conftest.py b/tests/storage/conftest.py index dcb42f6..96865a6 100644 --- a/tests/storage/conftest.py +++ b/tests/storage/conftest.py @@ -1,7 +1,7 @@ -import pytest - import uuid +import pytest + @pytest.fixture def slow_create_collection(request): diff --git a/tests/storage/dav/__init__.py b/tests/storage/dav/__init__.py index 8886f73..3003df6 100644 --- a/tests/storage/dav/__init__.py +++ b/tests/storage/dav/__init__.py @@ -1,19 +1,15 @@ +import os import uuid -import os - import pytest - -import requests import requests.exceptions +from .. import get_server_mixin +from .. import StorageTests from tests import assert_item_equals - from vdirsyncer import exceptions from vdirsyncer.vobject import Item -from .. import StorageTests, get_server_mixin - dav_server = os.environ.get('DAV_SERVER', 'skip') ServerMixin = get_server_mixin(dav_server) diff --git a/tests/storage/dav/test_caldav.py b/tests/storage/dav/test_caldav.py index 1d3f2fa..37cd1f2 100644 --- a/tests/storage/dav/test_caldav.py +++ b/tests/storage/dav/test_caldav.py @@ -2,18 +2,17 @@ import datetime from textwrap import dedent import pytest - -import requests import requests.exceptions -from tests import EVENT_TEMPLATE, TASK_TEMPLATE, VCARD_TEMPLATE - +from . import dav_server +from . import DAVStorageTests +from .. import format_item +from tests import EVENT_TEMPLATE +from tests import TASK_TEMPLATE +from tests import VCARD_TEMPLATE from vdirsyncer import exceptions from vdirsyncer.storage.dav import CalDAVStorage -from . import DAVStorageTests, dav_server -from .. import format_item - class TestCalDAVStorage(DAVStorageTests): storage_class = CalDAVStorage diff --git a/tests/storage/dav/test_carddav.py b/tests/storage/dav/test_carddav.py index e15e047..a371423 100644 --- a/tests/storage/dav/test_carddav.py +++ b/tests/storage/dav/test_carddav.py @@ -1,8 +1,7 @@ import pytest -from vdirsyncer.storage.dav import CardDAVStorage - from . import DAVStorageTests +from vdirsyncer.storage.dav import CardDAVStorage class TestCardDAVStorage(DAVStorageTests): diff --git a/tests/storage/dav/test_main.py b/tests/storage/dav/test_main.py index 5766b55..ef39c46 100644 --- a/tests/storage/dav/test_main.py +++ b/tests/storage/dav/test_main.py @@ -1,6 +1,8 @@ import pytest -from vdirsyncer.storage.dav import _BAD_XML_CHARS, _merge_xml, _parse_xml +from vdirsyncer.storage.dav import _BAD_XML_CHARS +from vdirsyncer.storage.dav import _merge_xml +from vdirsyncer.storage.dav import _parse_xml def test_xml_utilities(): diff --git a/tests/storage/etesync/etesync_server/etesync_server/settings.py b/tests/storage/etesync/etesync_server/etesync_server/settings.py index 4283f1b..eccdae9 100644 --- a/tests/storage/etesync/etesync_server/etesync_server/settings.py +++ b/tests/storage/etesync/etesync_server/etesync_server/settings.py @@ -9,7 +9,6 @@ https://docs.djangoproject.com/en/1.10/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/1.10/ref/settings/ """ - import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) diff --git a/tests/storage/etesync/etesync_server/etesync_server/urls.py b/tests/storage/etesync/etesync_server/etesync_server/urls.py index 5b7fc61..921363d 100644 --- a/tests/storage/etesync/etesync_server/etesync_server/urls.py +++ b/tests/storage/etesync/etesync_server/etesync_server/urls.py @@ -13,11 +13,10 @@ Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ -from django.conf.urls import include, url - -from rest_framework_nested import routers - +from django.conf.urls import include +from django.conf.urls import url from journal import views +from rest_framework_nested import routers router = routers.DefaultRouter() router.register(r'journals', views.JournalViewSet) diff --git a/tests/storage/etesync/etesync_server/etesync_server/wsgi.py b/tests/storage/etesync/etesync_server/etesync_server/wsgi.py index eeb40bc..147e71e 100644 --- a/tests/storage/etesync/etesync_server/etesync_server/wsgi.py +++ b/tests/storage/etesync/etesync_server/etesync_server/wsgi.py @@ -6,7 +6,6 @@ It exposes the WSGI callable as a module-level variable named ``application``. For more information on this file, see https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ """ - import os from django.core.wsgi import get_wsgi_application diff --git a/tests/storage/etesync/test_main.py b/tests/storage/etesync/test_main.py index 2045646..b258daa 100644 --- a/tests/storage/etesync/test_main.py +++ b/tests/storage/etesync/test_main.py @@ -1,14 +1,13 @@ -import shutil import os +import shutil import sys - import pytest import requests -from vdirsyncer.storage.etesync import EtesyncContacts, EtesyncCalendars - from .. import StorageTests +from vdirsyncer.storage.etesync import EtesyncCalendars +from vdirsyncer.storage.etesync import EtesyncContacts pytestmark = pytest.mark.skipif(os.getenv('ETESYNC_TESTS', '') != 'true', diff --git a/tests/storage/servers/davical/__init__.py b/tests/storage/servers/davical/__init__.py index a402ba2..550f6e9 100644 --- a/tests/storage/servers/davical/__init__.py +++ b/tests/storage/servers/davical/__init__.py @@ -1,7 +1,8 @@ import os -import pytest import uuid +import pytest + try: caldav_args = { # Those credentials are configured through the Travis UI diff --git a/tests/storage/servers/mysteryshack/__init__.py b/tests/storage/servers/mysteryshack/__init__.py index 617c154..b238bcc 100644 --- a/tests/storage/servers/mysteryshack/__init__.py +++ b/tests/storage/servers/mysteryshack/__init__.py @@ -1,10 +1,9 @@ import os +import shutil import subprocess import time -import shutil import pytest - import requests testserver_repo = os.path.dirname(__file__) diff --git a/tests/storage/test_filesystem.py b/tests/storage/test_filesystem.py index e8a3d71..c98e86a 100644 --- a/tests/storage/test_filesystem.py +++ b/tests/storage/test_filesystem.py @@ -2,11 +2,10 @@ import subprocess import pytest +from . import StorageTests from vdirsyncer.storage.filesystem import FilesystemStorage from vdirsyncer.vobject import Item -from . import StorageTests - class TestFilesystemStorage(StorageTests): storage_class = FilesystemStorage diff --git a/tests/storage/test_http.py b/tests/storage/test_http.py index 5e63a8f..8c58c4e 100644 --- a/tests/storage/test_http.py +++ b/tests/storage/test_http.py @@ -1,11 +1,10 @@ import pytest - from requests import Response from tests import normalize_item - from vdirsyncer.exceptions import UserError -from vdirsyncer.storage.http import HttpStorage, prepare_auth +from vdirsyncer.storage.http import HttpStorage +from vdirsyncer.storage.http import prepare_auth def test_list(monkeypatch): diff --git a/tests/storage/test_http_with_singlefile.py b/tests/storage/test_http_with_singlefile.py index 6480a52..bb2b1bc 100644 --- a/tests/storage/test_http_with_singlefile.py +++ b/tests/storage/test_http_with_singlefile.py @@ -1,13 +1,11 @@ import pytest - from requests import Response import vdirsyncer.storage.http +from . import StorageTests from vdirsyncer.storage.base import Storage from vdirsyncer.storage.singlefile import SingleFileStorage -from . import StorageTests - class CombinedStorage(Storage): '''A subclass of HttpStorage to make testing easier. It supports writes via diff --git a/tests/storage/test_memory.py b/tests/storage/test_memory.py index 417db1b..82db79c 100644 --- a/tests/storage/test_memory.py +++ b/tests/storage/test_memory.py @@ -1,8 +1,7 @@ import pytest -from vdirsyncer.storage.memory import MemoryStorage - from . import StorageTests +from vdirsyncer.storage.memory import MemoryStorage class TestMemoryStorage(StorageTests): diff --git a/tests/storage/test_singlefile.py b/tests/storage/test_singlefile.py index 54ef020..b645629 100644 --- a/tests/storage/test_singlefile.py +++ b/tests/storage/test_singlefile.py @@ -1,8 +1,7 @@ import pytest -from vdirsyncer.storage.singlefile import SingleFileStorage - from . import StorageTests +from vdirsyncer.storage.singlefile import SingleFileStorage class TestSingleFileStorage(StorageTests): diff --git a/tests/system/cli/conftest.py b/tests/system/cli/conftest.py index 43e4b79..fc2225e 100644 --- a/tests/system/cli/conftest.py +++ b/tests/system/cli/conftest.py @@ -1,8 +1,7 @@ from textwrap import dedent -from click.testing import CliRunner - import pytest +from click.testing import CliRunner import vdirsyncer.cli as cli diff --git a/tests/system/cli/test_config.py b/tests/system/cli/test_config.py index 734e1ee..b88dcd5 100644 --- a/tests/system/cli/test_config.py +++ b/tests/system/cli/test_config.py @@ -3,7 +3,8 @@ from textwrap import dedent import pytest -from vdirsyncer import cli, exceptions +from vdirsyncer import cli +from vdirsyncer import exceptions from vdirsyncer.cli.config import Config diff --git a/tests/system/cli/test_sync.py b/tests/system/cli/test_sync.py index e84d798..f242376 100644 --- a/tests/system/cli/test_sync.py +++ b/tests/system/cli/test_sync.py @@ -3,9 +3,9 @@ import sys from textwrap import dedent import hypothesis.strategies as st -from hypothesis import example, given - import pytest +from hypothesis import example +from hypothesis import given def test_simple_run(tmpdir, runner): diff --git a/tests/system/cli/test_utils.py b/tests/system/cli/test_utils.py index bbf8997..23aa997 100644 --- a/tests/system/cli/test_utils.py +++ b/tests/system/cli/test_utils.py @@ -1,6 +1,7 @@ from vdirsyncer import exceptions -from vdirsyncer.cli.utils import handle_cli_error, \ - storage_instance_from_config, storage_names +from vdirsyncer.cli.utils import handle_cli_error +from vdirsyncer.cli.utils import storage_instance_from_config +from vdirsyncer.cli.utils import storage_names def test_handle_cli_error(capsys): diff --git a/tests/system/utils/test_main.py b/tests/system/utils/test_main.py index 10bfde9..9df4e86 100644 --- a/tests/system/utils/test_main.py +++ b/tests/system/utils/test_main.py @@ -1,11 +1,12 @@ -import sys import logging +import sys import click_log import pytest import requests -from vdirsyncer import http, utils +from vdirsyncer import http +from vdirsyncer import utils @pytest.fixture(autouse=True) diff --git a/tests/unit/cli/test_fetchparams.py b/tests/unit/cli/test_fetchparams.py index 9339066..664df5f 100644 --- a/tests/unit/cli/test_fetchparams.py +++ b/tests/unit/cli/test_fetchparams.py @@ -1,10 +1,10 @@ import hypothesis.strategies as st +import pytest from hypothesis import given -import pytest - from vdirsyncer import exceptions -from vdirsyncer.cli.fetchparams import STRATEGIES, expand_fetch_params +from vdirsyncer.cli.fetchparams import expand_fetch_params +from vdirsyncer.cli.fetchparams import STRATEGIES @pytest.fixture diff --git a/tests/unit/sync/test_status.py b/tests/unit/sync/test_status.py index b5fc912..6d7b549 100644 --- a/tests/unit/sync/test_status.py +++ b/tests/unit/sync/test_status.py @@ -1,7 +1,7 @@ -import pytest - -from hypothesis import assume, given import hypothesis.strategies as st +import pytest +from hypothesis import assume +from hypothesis import given from vdirsyncer.sync.status import SqliteStatus diff --git a/tests/unit/sync/test_sync.py b/tests/unit/sync/test_sync.py index 4d554c5..1a2ac71 100644 --- a/tests/unit/sync/test_sync.py +++ b/tests/unit/sync/test_sync.py @@ -1,17 +1,22 @@ from copy import deepcopy import hypothesis.strategies as st -from hypothesis import assume -from hypothesis.stateful import Bundle, RuleBasedStateMachine, rule - import pytest +from hypothesis import assume +from hypothesis.stateful import Bundle +from hypothesis.stateful import rule +from hypothesis.stateful import RuleBasedStateMachine -from tests import blow_up, uid_strategy - -from vdirsyncer.storage.memory import MemoryStorage, _random_string +from tests import blow_up +from tests import uid_strategy +from vdirsyncer.storage.memory import _random_string +from vdirsyncer.storage.memory import MemoryStorage from vdirsyncer.sync import sync as _sync -from vdirsyncer.sync.exceptions import BothReadOnly, IdentConflict, \ - PartialSync, StorageEmpty, SyncConflict +from vdirsyncer.sync.exceptions import BothReadOnly +from vdirsyncer.sync.exceptions import IdentConflict +from vdirsyncer.sync.exceptions import PartialSync +from vdirsyncer.sync.exceptions import StorageEmpty +from vdirsyncer.sync.exceptions import SyncConflict from vdirsyncer.sync.status import SqliteStatus from vdirsyncer.vobject import Item diff --git a/tests/unit/test_metasync.py b/tests/unit/test_metasync.py index ae6d94c..d342e70 100644 --- a/tests/unit/test_metasync.py +++ b/tests/unit/test_metasync.py @@ -1,12 +1,13 @@ import hypothesis.strategies as st -from hypothesis import example, given - import pytest +from hypothesis import example +from hypothesis import given from tests import blow_up - from vdirsyncer.exceptions import UserError -from vdirsyncer.metasync import MetaSyncConflict, logger, metasync +from vdirsyncer.metasync import logger +from vdirsyncer.metasync import metasync +from vdirsyncer.metasync import MetaSyncConflict from vdirsyncer.storage.base import normalize_meta_value from vdirsyncer.storage.memory import MemoryStorage diff --git a/tests/unit/test_repair.py b/tests/unit/test_repair.py index b444d0b..bac5ad5 100644 --- a/tests/unit/test_repair.py +++ b/tests/unit/test_repair.py @@ -1,10 +1,12 @@ -from hypothesis import HealthCheck, given, settings - import pytest +from hypothesis import given +from hypothesis import HealthCheck +from hypothesis import settings from tests import uid_strategy - -from vdirsyncer.repair import IrreparableItem, repair_item, repair_storage +from vdirsyncer.repair import IrreparableItem +from vdirsyncer.repair import repair_item +from vdirsyncer.repair import repair_storage from vdirsyncer.storage.memory import MemoryStorage from vdirsyncer.utils import href_safe from vdirsyncer.vobject import Item diff --git a/tests/unit/utils/test_vobject.py b/tests/unit/utils/test_vobject.py index d029ed9..44a5f8a 100644 --- a/tests/unit/utils/test_vobject.py +++ b/tests/unit/utils/test_vobject.py @@ -1,16 +1,20 @@ from textwrap import dedent import hypothesis.strategies as st -from hypothesis import assume, given -from hypothesis.stateful import Bundle, RuleBasedStateMachine, rule - import pytest - -from tests import BARE_EVENT_TEMPLATE, EVENT_TEMPLATE, \ - EVENT_WITH_TIMEZONE_TEMPLATE, VCARD_TEMPLATE, normalize_item, \ - uid_strategy +from hypothesis import assume +from hypothesis import given +from hypothesis.stateful import Bundle +from hypothesis.stateful import rule +from hypothesis.stateful import RuleBasedStateMachine import vdirsyncer.vobject as vobject +from tests import BARE_EVENT_TEMPLATE +from tests import EVENT_TEMPLATE +from tests import EVENT_WITH_TIMEZONE_TEMPLATE +from tests import normalize_item +from tests import uid_strategy +from tests import VCARD_TEMPLATE _simple_split = [ diff --git a/vdirsyncer/cli/__init__.py b/vdirsyncer/cli/__init__.py index 8f0e4e0..ff86502 100644 --- a/vdirsyncer/cli/__init__.py +++ b/vdirsyncer/cli/__init__.py @@ -3,10 +3,10 @@ import logging import sys import click - import click_log -from .. import BUGTRACKER_HOME, __version__ +from .. import __version__ +from .. import BUGTRACKER_HOME cli_logger = logging.getLogger(__name__) diff --git a/vdirsyncer/cli/config.py b/vdirsyncer/cli/config.py index 3e74420..339bde9 100644 --- a/vdirsyncer/cli/config.py +++ b/vdirsyncer/cli/config.py @@ -6,10 +6,12 @@ from itertools import chain from click_threading import get_ui_worker +from .. import exceptions +from .. import PROJECT_HOME +from ..utils import cached_property +from ..utils import expand_path from .fetchparams import expand_fetch_params from .utils import storage_class_from_config -from .. import PROJECT_HOME, exceptions -from ..utils import cached_property, expand_path GENERAL_ALL = frozenset(['status_path']) diff --git a/vdirsyncer/cli/discover.py b/vdirsyncer/cli/discover.py index b3bff73..fdc8f9d 100644 --- a/vdirsyncer/cli/discover.py +++ b/vdirsyncer/cli/discover.py @@ -3,12 +3,14 @@ import json import logging import sys -from .utils import handle_collection_not_found, handle_storage_init_error, \ - load_status, save_status, storage_class_from_config, \ - storage_instance_from_config - from .. import exceptions from ..utils import cached_property +from .utils import handle_collection_not_found +from .utils import handle_storage_init_error +from .utils import load_status +from .utils import save_status +from .utils import storage_class_from_config +from .utils import storage_instance_from_config # Increase whenever upgrade potentially breaks discovery cache and collections diff --git a/vdirsyncer/cli/fetchparams.py b/vdirsyncer/cli/fetchparams.py index 801d599..2f3e9b9 100644 --- a/vdirsyncer/cli/fetchparams.py +++ b/vdirsyncer/cli/fetchparams.py @@ -4,7 +4,8 @@ import click from . import AppContext from .. import exceptions -from ..utils import expand_path, synchronized +from ..utils import expand_path +from ..utils import synchronized SUFFIX = '.fetch' diff --git a/vdirsyncer/cli/tasks.py b/vdirsyncer/cli/tasks.py index 7cadaf2..58e14e2 100644 --- a/vdirsyncer/cli/tasks.py +++ b/vdirsyncer/cli/tasks.py @@ -1,13 +1,19 @@ import functools import json +from .. import exceptions +from .. import sync from .config import CollectionConfig -from .discover import collections_for_pair, storage_class_from_config, \ - storage_instance_from_config -from .utils import JobFailed, cli_logger, get_status_name, \ - handle_cli_error, load_status, manage_sync_status, save_status - -from .. import exceptions, sync +from .discover import collections_for_pair +from .discover import storage_class_from_config +from .discover import storage_instance_from_config +from .utils import cli_logger +from .utils import get_status_name +from .utils import handle_cli_error +from .utils import JobFailed +from .utils import load_status +from .utils import manage_sync_status +from .utils import save_status def prepare_pair(wq, pair_name, collections, config, callback, **kwargs): diff --git a/vdirsyncer/cli/utils.py b/vdirsyncer/cli/utils.py index 46e37f2..d8b420d 100644 --- a/vdirsyncer/cli/utils.py +++ b/vdirsyncer/cli/utils.py @@ -7,18 +7,21 @@ import os import queue import sys +import click +import click_threading from atomicwrites import atomic_write -import click - -import click_threading - from . import cli_logger -from .. import BUGTRACKER_HOME, DOCS_HOME, exceptions -from ..sync.exceptions import IdentConflict, PartialSync, StorageEmpty, \ - SyncConflict +from .. import BUGTRACKER_HOME +from .. import DOCS_HOME +from .. import exceptions +from ..sync.exceptions import IdentConflict +from ..sync.exceptions import PartialSync +from ..sync.exceptions import StorageEmpty +from ..sync.exceptions import SyncConflict from ..sync.status import SqliteStatus -from ..utils import expand_path, get_storage_init_args +from ..utils import expand_path +from ..utils import get_storage_init_args STATUS_PERMISSIONS = 0o600 diff --git a/vdirsyncer/http.py b/vdirsyncer/http.py index 8549216..63a5dbf 100644 --- a/vdirsyncer/http.py +++ b/vdirsyncer/http.py @@ -2,8 +2,10 @@ import logging import requests +from . import __version__ +from . import DOCS_HOME +from . import exceptions from .utils import expand_path -from . import DOCS_HOME, exceptions, __version__ logger = logging.getLogger(__name__) diff --git a/vdirsyncer/repair.py b/vdirsyncer/repair.py index 89c2c3a..3c94847 100644 --- a/vdirsyncer/repair.py +++ b/vdirsyncer/repair.py @@ -1,7 +1,8 @@ import logging from os.path import basename -from .utils import generate_href, href_safe +from .utils import generate_href +from .utils import href_safe logger = logging.getLogger(__name__) diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index 4481e15..07d164f 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -2,17 +2,21 @@ import datetime import logging import urllib.parse as urlparse import xml.etree.ElementTree as etree - from inspect import getfullargspec import requests from requests.exceptions import HTTPError -from .base import Storage, normalize_meta_value -from .. import exceptions, http, utils -from ..http import USERAGENT, prepare_auth, \ - prepare_client_cert, prepare_verify +from .. import exceptions +from .. import http +from .. import utils +from ..http import prepare_auth +from ..http import prepare_client_cert +from ..http import prepare_verify +from ..http import USERAGENT from ..vobject import Item +from .base import normalize_meta_value +from .base import Storage dav_logger = logging.getLogger(__name__) diff --git a/vdirsyncer/storage/etesync.py b/vdirsyncer/storage/etesync.py index e351d61..88bf07c 100644 --- a/vdirsyncer/storage/etesync.py +++ b/vdirsyncer/storage/etesync.py @@ -1,8 +1,8 @@ +import binascii import contextlib import functools import logging import os -import binascii import atomicwrites import click diff --git a/vdirsyncer/storage/filesystem.py b/vdirsyncer/storage/filesystem.py index 8859bbc..b398119 100644 --- a/vdirsyncer/storage/filesystem.py +++ b/vdirsyncer/storage/filesystem.py @@ -5,10 +5,14 @@ import subprocess from atomicwrites import atomic_write -from .base import Storage, normalize_meta_value from .. import exceptions -from ..utils import checkdir, expand_path, generate_href, get_etag_from_file +from ..utils import checkdir +from ..utils import expand_path +from ..utils import generate_href +from ..utils import get_etag_from_file from ..vobject import Item +from .base import normalize_meta_value +from .base import Storage logger = logging.getLogger(__name__) diff --git a/vdirsyncer/storage/google.py b/vdirsyncer/storage/google.py index 1702f01..675c273 100644 --- a/vdirsyncer/storage/google.py +++ b/vdirsyncer/storage/google.py @@ -3,15 +3,16 @@ import logging import os import urllib.parse as urlparse -from atomicwrites import atomic_write - import click - +from atomicwrites import atomic_write from click_threading import get_ui_worker -from . import base, dav +from . import base +from . import dav from .. import exceptions -from ..utils import checkdir, expand_path, open_graphical_browser +from ..utils import checkdir +from ..utils import expand_path +from ..utils import open_graphical_browser logger = logging.getLogger(__name__) diff --git a/vdirsyncer/storage/http.py b/vdirsyncer/storage/http.py index b9f12e9..e3ca984 100644 --- a/vdirsyncer/storage/http.py +++ b/vdirsyncer/storage/http.py @@ -1,10 +1,14 @@ import urllib.parse as urlparse -from .base import Storage from .. import exceptions -from ..http import USERAGENT, prepare_auth, \ - prepare_client_cert, prepare_verify, request -from ..vobject import Item, split_collection +from ..http import prepare_auth +from ..http import prepare_client_cert +from ..http import prepare_verify +from ..http import request +from ..http import USERAGENT +from ..vobject import Item +from ..vobject import split_collection +from .base import Storage class HttpStorage(Storage): diff --git a/vdirsyncer/storage/memory.py b/vdirsyncer/storage/memory.py index 99e533b..e953691 100644 --- a/vdirsyncer/storage/memory.py +++ b/vdirsyncer/storage/memory.py @@ -1,8 +1,8 @@ import random -from .base import Storage, normalize_meta_value - from .. import exceptions +from .base import normalize_meta_value +from .base import Storage def _random_string(): diff --git a/vdirsyncer/storage/singlefile.py b/vdirsyncer/storage/singlefile.py index 5a56aef..829204c 100644 --- a/vdirsyncer/storage/singlefile.py +++ b/vdirsyncer/storage/singlefile.py @@ -7,10 +7,14 @@ import os from atomicwrites import atomic_write -from .base import Storage from .. import exceptions -from ..utils import checkfile, expand_path, get_etag_from_file -from ..vobject import Item, join_collection, split_collection +from ..utils import checkfile +from ..utils import expand_path +from ..utils import get_etag_from_file +from ..vobject import Item +from ..vobject import join_collection +from ..vobject import split_collection +from .base import Storage logger = logging.getLogger(__name__) diff --git a/vdirsyncer/sync/__init__.py b/vdirsyncer/sync/__init__.py index bdb945d..2119063 100644 --- a/vdirsyncer/sync/__init__.py +++ b/vdirsyncer/sync/__init__.py @@ -15,10 +15,13 @@ import logging from ..exceptions import UserError from ..utils import uniq - -from .status import SubStatus, ItemMetadata -from .exceptions import BothReadOnly, IdentAlreadyExists, PartialSync, \ - StorageEmpty, SyncConflict +from .exceptions import BothReadOnly +from .exceptions import IdentAlreadyExists +from .exceptions import PartialSync +from .exceptions import StorageEmpty +from .exceptions import SyncConflict +from .status import ItemMetadata +from .status import SubStatus sync_logger = logging.getLogger(__name__) diff --git a/vdirsyncer/sync/status.py b/vdirsyncer/sync/status.py index 2544680..48b5634 100644 --- a/vdirsyncer/sync/status.py +++ b/vdirsyncer/sync/status.py @@ -1,7 +1,7 @@ import abc import contextlib -import sys import sqlite3 +import sys from .exceptions import IdentAlreadyExists diff --git a/vdirsyncer/utils.py b/vdirsyncer/utils.py index 2737cbe..8730519 100644 --- a/vdirsyncer/utils.py +++ b/vdirsyncer/utils.py @@ -2,7 +2,6 @@ import functools import os import sys import uuid - from inspect import getfullargspec from . import exceptions diff --git a/vdirsyncer/vobject.py b/vdirsyncer/vobject.py index 33c2d3d..92e46d2 100644 --- a/vdirsyncer/vobject.py +++ b/vdirsyncer/vobject.py @@ -1,7 +1,9 @@ import hashlib -from itertools import chain, tee +from itertools import chain +from itertools import tee -from .utils import cached_property, uniq +from .utils import cached_property +from .utils import uniq IGNORE_PROPS = ( From 3eb9ce5ae4320d52e6c876874511ff96a8a45f51 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 9 Jun 2020 14:45:02 +0200 Subject: [PATCH 4/6] Add compatibility with latest click --- setup.py | 2 +- tests/system/cli/test_sync.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 59549f1..d584b95 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from setuptools import setup requirements = [ # https://github.com/mitsuhiko/click/issues/200 - 'click>=5.0,<6.0', + 'click>=5.0', 'click-log>=0.3.0, <0.4.0', # https://github.com/pimutils/vdirsyncer/issues/478 diff --git a/tests/system/cli/test_sync.py b/tests/system/cli/test_sync.py index f242376..43d35dd 100644 --- a/tests/system/cli/test_sync.py +++ b/tests/system/cli/test_sync.py @@ -123,7 +123,10 @@ def test_verbosity(tmpdir, runner): runner.write_with_general('') result = runner.invoke(['--verbosity=HAHA', 'sync']) assert result.exception - assert 'invalid value for "--verbosity"' in result.output.lower() + assert ( + 'invalid value for "--verbosity"' in result.output.lower() + or "invalid value for '--verbosity'" in result.output.lower() + ) def test_collections_cache_invalidation(tmpdir, runner): From 47caebe843c9aa3a675f01ea53bbfac6bdebb034 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 9 Jun 2020 14:57:58 +0200 Subject: [PATCH 5/6] Simplify coverage handling - Always install coverage tools. - Show coverage if all tests pass. - Less conditional code. --- Makefile | 5 ----- setup.cfg | 7 ++++++- test-requirements.txt | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index e862e36..fbe59b8 100644 --- a/Makefile +++ b/Makefile @@ -30,11 +30,6 @@ PYTEST_ARGS = TEST_EXTRA_PACKAGES = -ifeq ($(COVERAGE), true) - TEST_EXTRA_PACKAGES += pytest-cov - PYTEST_ARGS += --cov-config .coveragerc --cov vdirsyncer -endif - ifeq ($(ETESYNC_TESTS), true) TEST_EXTRA_PACKAGES += git+https://github.com/etesync/journal-manager@v0.5.2 TEST_EXTRA_PACKAGES += django djangorestframework==3.8.2 wsgi_intercept drf-nested-routers diff --git a/setup.cfg b/setup.cfg index fb86724..e3d4d5c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,12 @@ universal = 1 [tool:pytest] norecursedirs = tests/storage/servers/* -addopts = --tb=short +addopts = + --tb=short + --cov-config .coveragerc + --cov=vdirsyncer + --cov-report=term-missing + --no-cov-on-fail [flake8] # E731: Use a def instead of lambda expr diff --git a/test-requirements.txt b/test-requirements.txt index feb39a6..b6e1181 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,4 +1,5 @@ hypothesis>=5.0.0 pytest +pytest-cov pytest-localserver pytest-subtesthack From 7577fa21177442aacc2d86640ef28cebf1c4aaef Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Tue, 9 Jun 2020 17:04:46 +0200 Subject: [PATCH 6/6] Update usage of deprecated method --- vdirsyncer/storage/dav.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index 07d164f..a7e2422 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -124,7 +124,7 @@ def _merge_xml(items): return None rv = items[0] for item in items[1:]: - rv.extend(item.getiterator()) + rv.extend(item.iter()) return rv