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/.travis.yml b/.travis.yml
index 59a2f35..c104147 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,74 +19,22 @@
"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 ",
- "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"
},
{
@@ -127,7 +75,7 @@
},
{
"env": "BUILD=test ETESYNC_TESTS=true REQUIREMENTS=latest",
- "python": "3.6"
+ "python": "3.7"
}
]
},
diff --git a/Makefile b/Makefile
index 805caf0..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
@@ -106,9 +101,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
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 8a2d630..7c790e5 100755
--- a/scripts/make_travisconf.py
+++ b/scripts/make_travisconf.py
@@ -1,10 +1,8 @@
#!/usr/bin/env python
-
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 +32,7 @@ matrix = []
cfg['matrix'] = {'include': matrix, 'fast_finish': True}
matrix.append({
- 'python': latest_python,
+ 'python': python_versions[0],
'env': 'BUILD=style'
})
@@ -51,7 +49,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 +59,6 @@ for python, requirements in itertools.product(
f"DAV_SERVER={dav_server} "
f"REQUIREMENTS={requirements} ")
}
- if python == '3.5':
- job['dist'] = 'trusty'
if dav_server in ("davical", "icloud"):
job['if'] = 'NOT (type IN (pull_request))'
@@ -70,7 +66,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.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/setup.py b/setup.py
index 653be04..d584b95 100644
--- a/setup.py
+++ b/setup.py
@@ -4,14 +4,14 @@ 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 = [
# 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
@@ -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/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
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 dd0d0b3..a2e0022 100644
--- a/tests/storage/__init__.py
+++ b/tests/storage/__init__.py
@@ -1,25 +1,27 @@
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
- x = __import__('{}.servers.{}'.format(base, server_name), fromlist=[''])
+ x = __import__(f'{base}.servers.{server_name}', fromlist=[''])
return x.ServerMixin
@@ -183,7 +185,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/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 b999e25..e1f996e 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_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..43d35dd 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):
@@ -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):
@@ -461,7 +464,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/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 2d28507..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
@@ -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_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 4e9651f..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
@@ -253,7 +258,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 +568,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_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 5467c54..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
@@ -18,11 +20,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 +42,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 +60,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..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 = [
@@ -221,7 +225,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 +321,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..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__)
@@ -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..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'])
@@ -101,7 +103,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 +165,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..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
@@ -211,7 +213,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 +228,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..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'
@@ -19,7 +20,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 +46,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 +55,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..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):
@@ -45,7 +51,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 +116,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 +127,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..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
@@ -144,11 +147,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 +213,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 +249,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 +401,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..63a5dbf 100644
--- a/vdirsyncer/http.py
+++ b/vdirsyncer/http.py
@@ -2,12 +2,14 @@ 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__)
-USERAGENT = 'vdirsyncer/{}'.format(__version__)
+USERAGENT = f'vdirsyncer/{__version__}'
def _detect_faulty_requests(): # pragma: no cover
@@ -133,7 +135,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..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__)
@@ -25,7 +26,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..a7e2422 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__)
@@ -34,7 +38,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 +56,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 +82,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))
@@ -120,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
@@ -459,7 +463,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 +595,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 +645,7 @@ class DAVStorage(Storage):
except KeyError:
raise exceptions.UnsupportedMetadataError()
- xpath = '{{{}}}{}'.format(namespace, tagname)
+ xpath = f'{{{namespace}}}{tagname}'
data = '''
@@ -674,7 +678,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..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
@@ -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/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 f09ff48..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__)
@@ -80,7 +81,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/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 74b58e8..e953691 100644
--- a/vdirsyncer/storage/memory.py
+++ b/vdirsyncer/storage/memory.py
@@ -1,12 +1,12 @@
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():
- return '{:.9f}'.format(random.random())
+ return f'{random.random():.9f}'
class MemoryStorage(Storage):
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 5559ec3..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
@@ -73,7 +72,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 +124,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 +142,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..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 = (
@@ -205,14 +207,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 +301,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 +331,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 +344,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):