diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py
index cbd6218..51a66a0 100644
--- a/tests/storage/__init__.py
+++ b/tests/storage/__init__.py
@@ -383,7 +383,7 @@ class StorageTests:
uid = str(uuid.uuid4())
item = Item(
textwrap.dedent(
- """
+ f"""
BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
@@ -417,9 +417,7 @@ class StorageTests:
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
- """.format(
- uid=uid
- )
+ """
).strip()
)
diff --git a/tests/storage/dav/test_main.py b/tests/storage/dav/test_main.py
index 196094b..08d7126 100644
--- a/tests/storage/dav/test_main.py
+++ b/tests/storage/dav/test_main.py
@@ -39,8 +39,8 @@ def test_xml_utilities():
def test_xml_specialchars(char):
x = _parse_xml(
''
- "ye{}s\r\n"
- "hello".format(chr(char)).encode("ascii")
+ f"ye{chr(char)}s\r\n"
+ "hello".encode("ascii")
)
if char in _BAD_XML_CHARS:
diff --git a/tests/system/cli/test_discover.py b/tests/system/cli/test_discover.py
index fb9aeac..b2f3235 100644
--- a/tests/system/cli/test_discover.py
+++ b/tests/system/cli/test_discover.py
@@ -152,7 +152,7 @@ def test_discover_direct_path(tmpdir, runner):
def test_null_collection_with_named_collection(tmpdir, runner):
runner.write_with_general(
dedent(
- """
+ f"""
[pair foobar]
a = "foo"
b = "bar"
@@ -160,15 +160,13 @@ def test_null_collection_with_named_collection(tmpdir, runner):
[storage foo]
type = "filesystem"
- path = "{base}/foo/"
+ path = "{str(tmpdir)}/foo/"
fileext = ".txt"
[storage bar]
type = "singlefile"
- path = "{base}/bar.txt"
- """.format(
- base=str(tmpdir)
- )
+ path = "{str(tmpdir)}/bar.txt"
+ """
)
)
@@ -221,7 +219,7 @@ def test_collection_required(a_requires, b_requires, tmpdir, runner, monkeypatch
runner.write_with_general(
dedent(
- """
+ f"""
[pair foobar]
a = "foo"
b = "bar"
@@ -229,14 +227,12 @@ def test_collection_required(a_requires, b_requires, tmpdir, runner, monkeypatch
[storage foo]
type = "test"
- require_collection = {a}
+ require_collection = {json.dumps(a_requires)}
[storage bar]
type = "test"
- require_collection = {b}
- """.format(
- a=json.dumps(a_requires), b=json.dumps(b_requires)
- )
+ require_collection = {json.dumps(b_requires)}
+ """
)
)
diff --git a/tests/system/cli/test_fetchparams.py b/tests/system/cli/test_fetchparams.py
index f458760..fdc8d91 100644
--- a/tests/system/cli/test_fetchparams.py
+++ b/tests/system/cli/test_fetchparams.py
@@ -4,7 +4,7 @@ from textwrap import dedent
def test_get_password_from_command(tmpdir, runner):
runner.write_with_general(
dedent(
- """
+ f"""
[pair foobar]
a = "foo"
b = "bar"
@@ -12,16 +12,14 @@ def test_get_password_from_command(tmpdir, runner):
[storage foo]
type.fetch = ["shell", "echo filesystem"]
- path = "{base}/foo/"
+ path = "{str(tmpdir)}/foo/"
fileext.fetch = ["command", "echo", ".txt"]
[storage bar]
type = "filesystem"
- path = "{base}/bar/"
+ path = "{str(tmpdir)}/bar/"
fileext.fetch = ["prompt", "Fileext for bar"]
- """.format(
- base=str(tmpdir)
- )
+ """
)
)
diff --git a/tests/system/cli/test_sync.py b/tests/system/cli/test_sync.py
index 9deee66..4fd1cfd 100644
--- a/tests/system/cli/test_sync.py
+++ b/tests/system/cli/test_sync.py
@@ -280,24 +280,22 @@ def test_multiple_pairs(tmpdir, runner):
def test_create_collections(collections, tmpdir, runner):
runner.write_with_general(
dedent(
- """
+ f"""
[pair foobar]
a = "foo"
b = "bar"
- collections = {colls}
+ collections = {json.dumps(list(collections))}
[storage foo]
type = "filesystem"
- path = "{base}/foo/"
+ path = "{str(tmpdir)}/foo/"
fileext = ".txt"
[storage bar]
type = "filesystem"
- path = "{base}/bar/"
+ path = "{str(tmpdir)}/bar/"
fileext = ".txt"
- """.format(
- base=str(tmpdir), colls=json.dumps(list(collections))
- )
+ """
)
)
@@ -315,7 +313,7 @@ def test_create_collections(collections, tmpdir, runner):
def test_ident_conflict(tmpdir, runner):
runner.write_with_general(
dedent(
- """
+ f"""
[pair foobar]
a = "foo"
b = "bar"
@@ -323,16 +321,14 @@ def test_ident_conflict(tmpdir, runner):
[storage foo]
type = "filesystem"
- path = "{base}/foo/"
+ path = "{str(tmpdir)}/foo/"
fileext = ".txt"
[storage bar]
type = "filesystem"
- path = "{base}/bar/"
+ path = "{str(tmpdir)}/bar/"
fileext = ".txt"
- """.format(
- base=str(tmpdir)
- )
+ """
)
)
@@ -371,7 +367,7 @@ def test_ident_conflict(tmpdir, runner):
def test_unknown_storage(tmpdir, runner, existing, missing):
runner.write_with_general(
dedent(
- """
+ f"""
[pair foobar]
a = "foo"
b = "bar"
@@ -379,11 +375,9 @@ def test_unknown_storage(tmpdir, runner, existing, missing):
[storage {existing}]
type = "filesystem"
- path = "{base}/{existing}/"
+ path = "{str(tmpdir)}/{existing}/"
fileext = ".txt"
- """.format(
- base=str(tmpdir), existing=existing
- )
+ """
)
)
@@ -393,10 +387,8 @@ def test_unknown_storage(tmpdir, runner, existing, missing):
assert result.exception
assert (
- "Storage '{missing}' not found. "
- "These are the configured storages: ['{existing}']".format(
- missing=missing, existing=existing
- )
+ f"Storage '{missing}' not found. "
+ f"These are the configured storages: ['{existing}']"
) in result.output
@@ -416,25 +408,23 @@ def test_no_configured_pairs(tmpdir, runner, cmd):
def test_conflict_resolution(tmpdir, runner, resolution, expect_foo, expect_bar):
runner.write_with_general(
dedent(
- """
+ f"""
[pair foobar]
a = "foo"
b = "bar"
collections = null
- conflict_resolution = {val}
+ conflict_resolution = {json.dumps(resolution)}
[storage foo]
type = "filesystem"
fileext = ".txt"
- path = "{base}/foo"
+ path = "{str(tmpdir)}/foo"
[storage bar]
type = "filesystem"
fileext = ".txt"
- path = "{base}/bar"
- """.format(
- base=str(tmpdir), val=json.dumps(resolution)
- )
+ path = "{str(tmpdir)}/bar"
+ """
)
)
@@ -526,13 +516,11 @@ def test_fetch_only_necessary_params(tmpdir, runner):
fetch_script = tmpdir.join("fetch_script")
fetch_script.write(
dedent(
- """
+ f"""
set -e
- touch "{}"
+ touch "{str(fetched_file)}"
echo ".txt"
- """.format(
- str(fetched_file)
- )
+ """
)
)
diff --git a/vdirsyncer/__init__.py b/vdirsyncer/__init__.py
index 76ba468..f982dbc 100644
--- a/vdirsyncer/__init__.py
+++ b/vdirsyncer/__init__.py
@@ -17,10 +17,10 @@ except ImportError: # pragma: no cover
)
-def _check_python_version(): # pragma: no cover
+def _check_python_version():
import sys
- if sys.version_info < (3, 7, 0):
+ if sys.version_info < (3, 7, 0): # noqa: UP036
print("vdirsyncer requires at least Python 3.7.")
sys.exit(1)
diff --git a/vdirsyncer/cli/__init__.py b/vdirsyncer/cli/__init__.py
index acda1b0..ff42cdc 100644
--- a/vdirsyncer/cli/__init__.py
+++ b/vdirsyncer/cli/__init__.py
@@ -54,7 +54,7 @@ def app(ctx, config: str):
cli_logger.warning(
"Vdirsyncer currently does not support Windows. "
"You will likely encounter bugs. "
- "See {}/535 for more information.".format(BUGTRACKER_HOME)
+ f"See {BUGTRACKER_HOME}/535 for more information."
)
if not ctx.config:
diff --git a/vdirsyncer/cli/config.py b/vdirsyncer/cli/config.py
index 344ce80..8aed824 100644
--- a/vdirsyncer/cli/config.py
+++ b/vdirsyncer/cli/config.py
@@ -27,9 +27,9 @@ def validate_section_name(name, section_type):
if invalid:
chars_display = "".join(sorted(SECTION_NAME_CHARS))
raise exceptions.UserError(
- 'The {}-section "{}" contains invalid characters. Only '
+ f'The {section_type}-section "{name}" contains invalid characters. Only '
"the following characters are allowed for storage and "
- "pair names:\n{}".format(section_type, name, chars_display)
+ f"pair names:\n{chars_display}"
)
@@ -51,7 +51,7 @@ def _validate_general_section(general_config: dict[str, str]):
if problems:
raise exceptions.UserError(
"Invalid general section. Copy the example "
- "config from the repository and edit it: {}".format(PROJECT_HOME),
+ f"config from the repository and edit it: {PROJECT_HOME}",
problems=problems,
)
@@ -210,10 +210,8 @@ class Config:
args = self.storages[storage_name]
except KeyError:
raise exceptions.UserError(
- "Storage {!r} not found. "
- "These are the configured storages: {}".format(
- storage_name, list(self.storages)
- )
+ f"Storage {storage_name!r} not found. "
+ f"These are the configured storages: {list(self.storages)}"
)
else:
return expand_fetch_params(args)
diff --git a/vdirsyncer/cli/discover.py b/vdirsyncer/cli/discover.py
index 9de6177..c3870ea 100644
--- a/vdirsyncer/cli/discover.py
+++ b/vdirsyncer/cli/discover.py
@@ -66,12 +66,12 @@ async def collections_for_pair(
elif rv:
raise exceptions.UserError(
"Detected change in config file, "
- "please run `vdirsyncer discover {}`.".format(pair.name)
+ f"please run `vdirsyncer discover {pair.name}`."
)
else:
raise exceptions.UserError(
- "Please run `vdirsyncer discover {}` "
- " before synchronization.".format(pair.name)
+ f"Please run `vdirsyncer discover {pair.name}` "
+ " before synchronization."
)
logger.info(f"Discovering collections for pair {pair.name}")
@@ -271,8 +271,8 @@ async def _print_collections(
logger.debug("".join(traceback.format_tb(sys.exc_info()[2])))
logger.warning(
- "Failed to discover collections for {}, use `-vdebug` "
- "to see the full traceback.".format(instance_name)
+ f"Failed to discover collections for {instance_name}, use `-vdebug` "
+ "to see the full traceback."
)
return
logger.info(f"{instance_name}:")
diff --git a/vdirsyncer/cli/fetchparams.py b/vdirsyncer/cli/fetchparams.py
index 2cdcea9..f4d56fc 100644
--- a/vdirsyncer/cli/fetchparams.py
+++ b/vdirsyncer/cli/fetchparams.py
@@ -65,8 +65,7 @@ def _fetch_value(opts, key):
else:
if not rv:
raise exceptions.UserError(
- "Empty value for {}, this most likely "
- "indicates an error.".format(key)
+ f"Empty value for {key}, this most likely indicates an error."
)
password_cache[cache_key] = rv
return rv
diff --git a/vdirsyncer/cli/utils.py b/vdirsyncer/cli/utils.py
index 247a748..428a9c9 100644
--- a/vdirsyncer/cli/utils.py
+++ b/vdirsyncer/cli/utils.py
@@ -88,23 +88,19 @@ def handle_cli_error(status_name=None, e=None):
)
except PartialSync as e:
cli_logger.error(
- "{status_name}: Attempted change on {storage}, which is read-only"
+ f"{status_name}: Attempted change on {e.storage}, which is read-only"
". Set `partial_sync` in your pair section to `ignore` to ignore "
- "those changes, or `revert` to revert them on the other side.".format(
- status_name=status_name, storage=e.storage
- )
+ "those changes, or `revert` to revert them on the other side."
)
except SyncConflict as e:
cli_logger.error(
- "{status_name}: One item changed on both sides. Resolve this "
+ f"{status_name}: One item changed on both sides. Resolve this "
"conflict manually, or by setting the `conflict_resolution` "
"parameter in your config file.\n"
- "See also {docs}/config.html#pair-section\n"
- "Item ID: {e.ident}\n"
- "Item href on side A: {e.href_a}\n"
- "Item href on side B: {e.href_b}\n".format(
- status_name=status_name, e=e, docs=DOCS_HOME
- )
+ f"See also {DOCS_HOME}/config.html#pair-section\n"
+ f"Item ID: {e.ident}\n"
+ f"Item href on side A: {e.href_a}\n"
+ f"Item href on side B: {e.href_b}\n"
)
except IdentConflict as e:
cli_logger.error(
@@ -125,17 +121,17 @@ def handle_cli_error(status_name=None, e=None):
pass
except exceptions.PairNotFound as e:
cli_logger.error(
- "Pair {pair_name} does not exist. Please check your "
+ f"Pair {e.pair_name} does not exist. Please check your "
"configuration file and make sure you've typed the pair name "
- "correctly".format(pair_name=e.pair_name)
+ "correctly"
)
except exceptions.InvalidResponse as e:
cli_logger.error(
"The server returned something vdirsyncer doesn't understand. "
- "Error message: {!r}\n"
+ f"Error message: {e!r}\n"
"While this is most likely a serverside problem, the vdirsyncer "
"devs are generally interested in such bugs. Please report it in "
- "the issue tracker at {}".format(e, BUGTRACKER_HOME)
+ f"the issue tracker at {BUGTRACKER_HOME}"
)
except exceptions.CollectionRequired:
cli_logger.error(
@@ -367,7 +363,7 @@ async def handle_collection_not_found(config, collection, e=None):
cli_logger.error(e)
raise exceptions.UserError(
- 'Unable to find or create collection "{collection}" for '
- 'storage "{storage}". Please create the collection '
- "yourself.".format(collection=collection, storage=storage_name)
+ f'Unable to find or create collection "{collection}" for '
+ f'storage "{storage_name}". Please create the collection '
+ "yourself."
)
diff --git a/vdirsyncer/http.py b/vdirsyncer/http.py
index 30cfe90..f56afa9 100644
--- a/vdirsyncer/http.py
+++ b/vdirsyncer/http.py
@@ -57,8 +57,7 @@ def prepare_auth(auth, username, password):
raise exceptions.UserError(f"Unknown authentication method: {auth}")
elif auth:
raise exceptions.UserError(
- "You need to specify username and password "
- "for {} authentication.".format(auth)
+ f"You need to specify username and password for {auth} authentication."
)
return None
diff --git a/vdirsyncer/repair.py b/vdirsyncer/repair.py
index e72e480..cc9609e 100644
--- a/vdirsyncer/repair.py
+++ b/vdirsyncer/repair.py
@@ -24,9 +24,9 @@ async def repair_storage(storage, repair_unsafe_uid):
new_item = repair_item(href, item, seen_uids, repair_unsafe_uid)
except IrreparableItem:
logger.error(
- "Item {!r} is malformed beyond repair. "
+ f"Item {href!r} is malformed beyond repair. "
"The PRODID property may indicate which software "
- "created this item.".format(href)
+ "created this item."
)
logger.error(f"Item content: {item.raw!r}")
continue
diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py
index e21e0aa..eece4ee 100644
--- a/vdirsyncer/storage/dav.py
+++ b/vdirsyncer/storage/dav.py
@@ -92,8 +92,7 @@ def _parse_xml(content):
return etree.XML(_clean_body(content))
except etree.ParseError as e:
raise InvalidXMLResponse(
- "Invalid XML encountered: {}\n"
- "Double-check the URLs in your config.".format(e)
+ f"Invalid XML encountered: {e}\nDouble-check the URLs in your config."
)
diff --git a/vdirsyncer/storage/google.py b/vdirsyncer/storage/google.py
index bc80cee..4933deb 100644
--- a/vdirsyncer/storage/google.py
+++ b/vdirsyncer/storage/google.py
@@ -106,8 +106,8 @@ class GoogleSession(dav.DAVSession):
pass
except ValueError as e:
raise exceptions.UserError(
- "Failed to load token file {}, try deleting it. "
- "Original error: {}".format(self._token_file, e)
+ f"Failed to load token file {self._token_file}, try deleting it. "
+ f"Original error: {e}"
)
if not self._token:
diff --git a/vdirsyncer/storage/singlefile.py b/vdirsyncer/storage/singlefile.py
index 12f48bb..b443f5a 100644
--- a/vdirsyncer/storage/singlefile.py
+++ b/vdirsyncer/storage/singlefile.py
@@ -177,11 +177,9 @@ class SingleFileStorage(Storage):
self.path
):
raise exceptions.PreconditionFailed(
- (
- "Some other program modified the file {!r}. Re-run the "
- "synchronization and make sure absolutely no other program is "
- "writing into the same file."
- ).format(self.path)
+ f"Some other program modified the file {self.path!r}. Re-run the "
+ "synchronization and make sure absolutely no other program is "
+ "writing into the same file."
)
text = join_collection(item.raw for item, etag in self._items.values())
try:
diff --git a/vdirsyncer/sync/status.py b/vdirsyncer/sync/status.py
index d0d2c10..dc3ba84 100644
--- a/vdirsyncer/sync/status.py
+++ b/vdirsyncer/sync/status.py
@@ -247,10 +247,10 @@ class SqliteStatus(_StatusBase):
def _get_impl(self, ident, side, table):
res = self._c.execute(
- "SELECT href_{side} AS href,"
- " hash_{side} AS hash,"
- " etag_{side} AS etag "
- "FROM {table} WHERE ident=?".format(side=side, table=table),
+ f"SELECT href_{side} AS href,"
+ f" hash_{side} AS hash,"
+ f" etag_{side} AS etag "
+ f"FROM {table} WHERE ident=?",
(ident,),
).fetchone()
if res is None:
@@ -304,8 +304,8 @@ class SqliteStatus(_StatusBase):
def _get_by_href_impl(self, href, default=(None, None), side=None):
res = self._c.execute(
- "SELECT ident, hash_{side} AS hash, etag_{side} AS etag "
- "FROM status WHERE href_{side}=?".format(side=side),
+ f"SELECT ident, hash_{side} AS hash, etag_{side} AS etag "
+ f"FROM status WHERE href_{side}=?",
(href,),
).fetchone()
if not res: