Apply pyupgrade changes

This commit is contained in:
Andrew Senetar 2022-04-27 20:53:12 -05:00
parent e0061d7bc1
commit 63dd4d4561
Signed by: arsenetar
GPG Key ID: C63300DCE48AB2F1
36 changed files with 89 additions and 92 deletions

View File

@ -61,7 +61,7 @@ def parse_args():
def build_one_help(language):
print("Generating Help in {}".format(language))
print(f"Generating Help in {language}")
current_path = Path(".").absolute()
changelog_path = current_path.joinpath("help", "changelog")
tixurl = "https://github.com/arsenetar/dupeguru/issues/{}"
@ -139,7 +139,7 @@ def build_normal():
print("Building localizations")
build_localizations()
print("Building Qt stuff")
print_and_do("pyrcc5 {0} > {1}".format(Path("qt", "dg.qrc"), Path("qt", "dg_rc.py")))
print_and_do("pyrcc5 {} > {}".format(Path("qt", "dg.qrc"), Path("qt", "dg_rc.py")))
fix_qt_resource_file(Path("qt", "dg_rc.py"))
build_help()

View File

@ -264,7 +264,7 @@ class DupeGuru(Broadcaster):
try:
f._read_all_info(attrnames=self.METADATA_TO_READ)
return f
except EnvironmentError:
except OSError:
return None
def _get_export_data(self):

View File

@ -120,7 +120,7 @@ class Directories:
file.is_ref = state == DirectoryState.REFERENCE
count += 1
yield file
except (EnvironmentError, OSError, fs.InvalidPath):
except (OSError, fs.InvalidPath):
pass
logging.debug(
"Collected %d files in folder %s",
@ -134,14 +134,13 @@ class Directories:
j.check_if_cancelled()
try:
for subfolder in from_folder.subfolders:
for folder in self._get_folders(subfolder, j):
yield folder
yield from self._get_folders(subfolder, j)
state = self.get_state(from_folder.path)
if state != DirectoryState.EXCLUDED:
from_folder.is_ref = state == DirectoryState.REFERENCE
logging.debug("Yielding Folder %r state: %d", from_folder, state)
yield from_folder
except (EnvironmentError, fs.InvalidPath):
except (OSError, fs.InvalidPath):
pass
# ---Public
@ -173,7 +172,7 @@ class Directories:
subpaths = [p for p in path.glob("*") if p.is_dir()]
subpaths.sort(key=lambda x: x.name.lower())
return subpaths
except EnvironmentError:
except OSError:
return []
def get_files(self, fileclasses=None, j=job.nulljob):

View File

@ -166,7 +166,7 @@ def reduce_common_words(word_dict, threshold):
The exception to this removal are the objects where all the words of the object are common.
Because if we remove them, we will miss some duplicates!
"""
uncommon_words = set(word for word, objects in word_dict.items() if len(objects) < threshold)
uncommon_words = {word for word, objects in word_dict.items() if len(objects) < threshold}
for word, objects in list(word_dict.items()):
if len(objects) < threshold:
continue
@ -409,7 +409,7 @@ class Group:
You can call this after the duplicate scanning process to free a bit of memory.
"""
discarded = set(m for m in self.matches if not all(obj in self.unordered for obj in [m.first, m.second]))
discarded = {m for m in self.matches if not all(obj in self.unordered for obj in [m.first, m.second])}
self.matches -= discarded
self.candidates = defaultdict(set)
return discarded
@ -456,7 +456,7 @@ class Group:
self._matches_for_ref = None
if (len(self) > 1) and any(not getattr(item, "is_ref", False) for item in self):
if discard_matches:
self.matches = set(m for m in self.matches if item not in m)
self.matches = {m for m in self.matches if item not in m}
else:
self._clear()
except ValueError:
@ -529,7 +529,7 @@ def get_groups(matches):
del dupe2group
del matches
# should free enough memory to continue
logging.warning("Memory Overflow. Groups: {0}".format(len(groups)))
logging.warning(f"Memory Overflow. Groups: {len(groups)}")
# Now that we have a group, we have to discard groups' matches and see if there're any "orphan"
# matches, that is, matches that were candidate in a group but that none of their 2 files were
# accepted in the group. With these orphan groups, it's safe to build additional groups

View File

@ -197,7 +197,7 @@ class File:
self.path = path
def __repr__(self):
return "<{} {}>".format(self.__class__.__name__, str(self.path))
return f"<{self.__class__.__name__} {str(self.path)}>"
def __getattribute__(self, attrname):
result = object.__getattribute__(self, attrname)
@ -317,7 +317,7 @@ class File:
raise AlreadyExistsError(newname, self.path.parent)
try:
self.path.rename(destpath)
except EnvironmentError:
except OSError:
raise OperationError(self)
if not destpath.exists():
raise OperationError(self)
@ -421,5 +421,5 @@ def get_files(path, fileclasses=[File]):
if file is not None:
result.append(file)
return result
except EnvironmentError:
except OSError:
raise InvalidPath(path)

View File

@ -13,7 +13,7 @@ def colors_to_string(colors):
[(0,100,255)] --> 0064ff
[(1,2,3),(4,5,6)] --> 010203040506
"""
return "".join("%02x%02x%02x" % (r, g, b) for r, g, b in colors)
return "".join("{:02x}{:02x}{:02x}".format(r, g, b) for r, g, b in colors)
# This function is an important bottleneck of dupeGuru PE. It has been converted to C.

View File

@ -14,7 +14,7 @@ from .cache import string_to_colors, colors_to_string
def wrap_path(path):
return "path:{}".format(path)
return f"path:{path}"
def unwrap_path(key):
@ -22,7 +22,7 @@ def unwrap_path(key):
def wrap_id(path):
return "id:{}".format(path)
return f"id:{path}"
def unwrap_id(key):

View File

@ -87,7 +87,7 @@ def prepare_pictures(pictures, cache_path, with_dimensions, j=job.nulljob):
blocks = picture.get_blocks(BLOCK_COUNT_PER_SIDE)
cache[picture.unicode_path] = blocks
prepared.append(picture)
except (IOError, ValueError) as e:
except (OSError, ValueError) as e:
logging.warning(str(e))
except MemoryError:
logging.warning(

View File

@ -43,7 +43,7 @@ class Criterion:
@property
def display(self):
return "{} ({})".format(self.category.NAME, self.display_value)
return f"{self.category.NAME} ({self.display_value})"
class ValueListCategory(CriterionCategory):

View File

@ -191,7 +191,7 @@ class Results(Markable):
self.__filters.append(filter_str)
if self.__filtered_dupes is None:
self.__filtered_dupes = flatten(g[:] for g in self.groups)
self.__filtered_dupes = set(dupe for dupe in self.__filtered_dupes if filter_re.search(str(dupe.path)))
self.__filtered_dupes = {dupe for dupe in self.__filtered_dupes if filter_re.search(str(dupe.path))}
filtered_groups = set()
for dupe in self.__filtered_dupes:
filtered_groups.add(self.get_group_of_duplicate(dupe))
@ -301,7 +301,7 @@ class Results(Markable):
try:
func(dupe)
to_remove.append(dupe)
except (EnvironmentError, UnicodeEncodeError) as e:
except (OSError, UnicodeEncodeError) as e:
self.problems.append((dupe, str(e)))
if remove_from_results:
self.remove_duplicates(to_remove)
@ -374,8 +374,8 @@ class Results(Markable):
try:
do_write(outfile)
except IOError as e:
# If our IOError is because dest is already a directory, we want to handle that. 21 is
except OSError as e:
# If our OSError is because dest is already a directory, we want to handle that. 21 is
# the code we get on OS X and Linux, 13 is what we get on Windows.
if e.errno in {21, 13}:
p = str(outfile)

View File

@ -95,7 +95,7 @@ class TestCaseDupeGuru:
# At some point, any() was used in a wrong way that made Scan() wrongly return 1
app = TestApp().app
f1, f2 = [FakeFile("foo") for _ in range(2)]
f1, f2 = (FakeFile("foo") for _ in range(2))
f1.is_ref, f2.is_ref = (False, False)
assert not (bool(f1) and bool(f2))
add_fake_files_to_directories(app.directories, [f1, f2])

View File

@ -271,9 +271,9 @@ class TestCaseBuildWordDict:
class TestCaseMergeSimilarWords:
def test_some_similar_words(self):
d = {
"foobar": set([1]),
"foobar1": set([2]),
"foobar2": set([3]),
"foobar": {1},
"foobar1": {2},
"foobar2": {3},
}
merge_similar_words(d)
eq_(1, len(d))
@ -283,8 +283,8 @@ class TestCaseMergeSimilarWords:
class TestCaseReduceCommonWords:
def test_typical(self):
d = {
"foo": set([NamedObject("foo bar", True) for _ in range(50)]),
"bar": set([NamedObject("foo bar", True) for _ in range(49)]),
"foo": {NamedObject("foo bar", True) for _ in range(50)},
"bar": {NamedObject("foo bar", True) for _ in range(49)},
}
reduce_common_words(d, 50)
assert "foo" not in d
@ -293,7 +293,7 @@ class TestCaseReduceCommonWords:
def test_dont_remove_objects_with_only_common_words(self):
d = {
"common": set([NamedObject("common uncommon", True) for _ in range(50)] + [NamedObject("common", True)]),
"uncommon": set([NamedObject("common uncommon", True)]),
"uncommon": {NamedObject("common uncommon", True)},
}
reduce_common_words(d, 50)
eq_(1, len(d["common"]))
@ -302,7 +302,7 @@ class TestCaseReduceCommonWords:
def test_values_still_are_set_instances(self):
d = {
"common": set([NamedObject("common uncommon", True) for _ in range(50)] + [NamedObject("common", True)]),
"uncommon": set([NamedObject("common uncommon", True)]),
"uncommon": {NamedObject("common uncommon", True)},
}
reduce_common_words(d, 50)
assert isinstance(d["common"], set)
@ -312,9 +312,9 @@ class TestCaseReduceCommonWords:
# If a word has been removed by the reduce, an object in a subsequent common word that
# contains the word that has been removed would cause a KeyError.
d = {
"foo": set([NamedObject("foo bar baz", True) for _ in range(50)]),
"bar": set([NamedObject("foo bar baz", True) for _ in range(50)]),
"baz": set([NamedObject("foo bar baz", True) for _ in range(49)]),
"foo": {NamedObject("foo bar baz", True) for _ in range(50)},
"bar": {NamedObject("foo bar baz", True) for _ in range(50)},
"baz": {NamedObject("foo bar baz", True) for _ in range(49)},
}
try:
reduce_common_words(d, 50)
@ -328,7 +328,7 @@ class TestCaseReduceCommonWords:
o.words = [["foo", "bar"], ["baz"]]
return o
d = {"foo": set([create_it() for _ in range(50)])}
d = {"foo": {create_it() for _ in range(50)}}
try:
reduce_common_words(d, 50)
except TypeError:
@ -343,7 +343,7 @@ class TestCaseReduceCommonWords:
d = {
"foo": set([NamedObject("foo bar baz", True) for _ in range(49)] + [only_common]),
"bar": set([NamedObject("foo bar baz", True) for _ in range(49)] + [only_common]),
"baz": set([NamedObject("foo bar baz", True) for _ in range(49)]),
"baz": {NamedObject("foo bar baz", True) for _ in range(49)},
}
reduce_common_words(d, 50)
eq_(1, len(d["foo"]))
@ -884,7 +884,7 @@ class TestCaseGetGroups:
# If, with a (A, B, C, D) set, all match with A, but C and D don't match with B and that the
# (A, B) match is the highest (thus resulting in an (A, B) group), still match C and D
# in a separate group instead of discarding them.
A, B, C, D = [NamedObject() for _ in range(4)]
A, B, C, D = (NamedObject() for _ in range(4))
m1 = Match(A, B, 90) # This is the strongest "A" match
m2 = Match(A, C, 80) # Because C doesn't match with B, it won't be in the group
m3 = Match(A, D, 80) # Same thing for D

View File

@ -289,8 +289,8 @@ class TestCaseListEmptyUnion(TestCaseListEmpty):
compiled = [x for x in self.exclude_list.compiled]
assert regex not in compiled
# Need to escape both to get the same strings after compilation
compiled_escaped = set([x.encode("unicode-escape").decode() for x in compiled[0].pattern.split("|")])
default_escaped = set([x.encode("unicode-escape").decode() for x in default_regexes])
compiled_escaped = {x.encode("unicode-escape").decode() for x in compiled[0].pattern.split("|")}
default_escaped = {x.encode("unicode-escape").decode() for x in default_regexes}
assert compiled_escaped == default_escaped
eq_(len(default_regexes), len(compiled[0].pattern.split("|")))
@ -366,8 +366,8 @@ class TestCaseDictEmptyUnion(TestCaseDictEmpty):
compiled = [x for x in self.exclude_list.compiled]
assert regex not in compiled
# Need to escape both to get the same strings after compilation
compiled_escaped = set([x.encode("unicode-escape").decode() for x in compiled[0].pattern.split("|")])
default_escaped = set([x.encode("unicode-escape").decode() for x in default_regexes])
compiled_escaped = {x.encode("unicode-escape").decode() for x in compiled[0].pattern.split("|")}
default_escaped = {x.encode("unicode-escape").decode() for x in default_regexes}
assert compiled_escaped == default_escaped
eq_(len(default_regexes), len(compiled[0].pattern.split("|")))

View File

@ -337,7 +337,7 @@ class TestCaseResultsMarkings:
def log_object(o):
log.append(o)
if o is self.objects[1]:
raise EnvironmentError("foobar")
raise OSError("foobar")
log = []
self.results.mark_all()
@ -464,7 +464,7 @@ class TestCaseResultsXML:
eq_(6, len(g1))
eq_(3, len([c for c in g1 if c.tag == "file"]))
eq_(3, len([c for c in g1 if c.tag == "match"]))
d1, d2, d3 = [c for c in g1 if c.tag == "file"]
d1, d2, d3 = (c for c in g1 if c.tag == "file")
eq_(op.join("basepath", "foo bar"), d1.get("path"))
eq_(op.join("basepath", "bar bleh"), d2.get("path"))
eq_(op.join("basepath", "foo bleh"), d3.get("path"))
@ -477,7 +477,7 @@ class TestCaseResultsXML:
eq_(3, len(g2))
eq_(2, len([c for c in g2 if c.tag == "file"]))
eq_(1, len([c for c in g2 if c.tag == "match"]))
d1, d2 = [c for c in g2 if c.tag == "file"]
d1, d2 = (c for c in g2 if c.tag == "file")
eq_(op.join("basepath", "ibabtu"), d1.get("path"))
eq_(op.join("basepath", "ibabtu"), d2.get("path"))
eq_("n", d1.get("is_ref"))

View File

@ -29,7 +29,7 @@ class NamedObject:
self.words = getwords(name)
def __repr__(self):
return "<NamedObject %r %r>" % (self.name, self.path)
return "<NamedObject {!r} {!r}>".format(self.name, self.path)
no = NamedObject
@ -336,7 +336,7 @@ def test_tag_scan(fake_fileexists):
def test_tag_with_album_scan(fake_fileexists):
s = Scanner()
s.scan_type = ScanType.TAG
s.scanned_tags = set(["artist", "album", "title"])
s.scanned_tags = {"artist", "album", "title"}
o1 = no("foo")
o2 = no("bar")
o3 = no("bleh")
@ -356,7 +356,7 @@ def test_tag_with_album_scan(fake_fileexists):
def test_that_dash_in_tags_dont_create_new_fields(fake_fileexists):
s = Scanner()
s.scan_type = ScanType.TAG
s.scanned_tags = set(["artist", "album", "title"])
s.scanned_tags = {"artist", "album", "title"}
s.min_match_percentage = 50
o1 = no("foo")
o2 = no("bar")
@ -373,7 +373,7 @@ def test_that_dash_in_tags_dont_create_new_fields(fake_fileexists):
def test_tag_scan_with_different_scanned(fake_fileexists):
s = Scanner()
s.scan_type = ScanType.TAG
s.scanned_tags = set(["track", "year"])
s.scanned_tags = {"track", "year"}
o1 = no("foo")
o2 = no("bar")
o1.artist = "The White Stripes"
@ -391,7 +391,7 @@ def test_tag_scan_with_different_scanned(fake_fileexists):
def test_tag_scan_only_scans_existing_tags(fake_fileexists):
s = Scanner()
s.scan_type = ScanType.TAG
s.scanned_tags = set(["artist", "foo"])
s.scanned_tags = {"artist", "foo"}
o1 = no("foo")
o2 = no("bar")
o1.artist = "The White Stripes"
@ -405,7 +405,7 @@ def test_tag_scan_only_scans_existing_tags(fake_fileexists):
def test_tag_scan_converts_to_str(fake_fileexists):
s = Scanner()
s.scan_type = ScanType.TAG
s.scanned_tags = set(["track"])
s.scanned_tags = {"track"}
o1 = no("foo")
o2 = no("bar")
o1.track = 42
@ -420,7 +420,7 @@ def test_tag_scan_converts_to_str(fake_fileexists):
def test_tag_scan_non_ascii(fake_fileexists):
s = Scanner()
s.scan_type = ScanType.TAG
s.scanned_tags = set(["title"])
s.scanned_tags = {"title"}
o1 = no("foo")
o2 = no("bar")
o1.title = "foobar\u00e9"

View File

@ -40,7 +40,7 @@ def _perform(src, dst, action, actionname):
shutil.rmtree(dst)
else:
os.remove(dst)
print("%s %s --> %s" % (actionname, src, dst))
print("{} {} --> {}".format(actionname, src, dst))
action(src, dst)
@ -95,12 +95,12 @@ def filereplace(filename, outfilename=None, **kwargs):
"""Reads `filename`, replaces all {variables} in kwargs, and writes the result to `outfilename`."""
if outfilename is None:
outfilename = filename
fp = open(filename, "rt", encoding="utf-8")
fp = open(filename, encoding="utf-8")
contents = fp.read()
fp.close()
# We can't use str.format() because in some files, there might be {} characters that mess with it.
for key, item in kwargs.items():
contents = contents.replace("{{{}}}".format(key), item)
contents = contents.replace(f"{{{key}}}", item)
fp = open(outfilename, "wt", encoding="utf-8")
fp.write(contents)
fp.close()
@ -143,8 +143,8 @@ def package_cocoa_app_in_dmg(app_path, destfolder, args):
# phase because running the app before packaging can modify it and we want to be sure to have
# a valid signature.
if args.sign_identity:
sign_identity = "Developer ID Application: {}".format(args.sign_identity)
result = print_and_do('codesign --force --deep --sign "{}" "{}"'.format(sign_identity, app_path))
sign_identity = f"Developer ID Application: {args.sign_identity}"
result = print_and_do(f'codesign --force --deep --sign "{sign_identity}" "{app_path}"')
if result != 0:
print("ERROR: Signing failed. Aborting packaging.")
return
@ -164,15 +164,15 @@ def build_dmg(app_path, destfolder):
workpath = tempfile.mkdtemp()
dmgpath = op.join(workpath, plist["CFBundleName"])
os.mkdir(dmgpath)
print_and_do('cp -R "%s" "%s"' % (app_path, dmgpath))
print_and_do('cp -R "{}" "{}"'.format(app_path, dmgpath))
print_and_do('ln -s /Applications "%s"' % op.join(dmgpath, "Applications"))
dmgname = "%s_osx_%s.dmg" % (
dmgname = "{}_osx_{}.dmg".format(
plist["CFBundleName"].lower().replace(" ", "_"),
plist["CFBundleVersion"].replace(".", "_"),
)
print("Building %s" % dmgname)
# UDBZ = bzip compression. UDZO (zip compression) was used before, but it compresses much less.
print_and_do('hdiutil create "%s" -format UDBZ -nocrossdev -srcdir "%s"' % (op.join(destfolder, dmgname), dmgpath))
print_and_do('hdiutil create "{}" -format UDBZ -nocrossdev -srcdir "{}"'.format(op.join(destfolder, dmgname), dmgpath))
print("Build Complete")
@ -216,7 +216,7 @@ def copy_packages(packages_names, dest, create_links=False, extra_ignores=None):
os.unlink(dest_path)
else:
shutil.rmtree(dest_path)
print("Copying package at {0} to {1}".format(source_path, dest_path))
print(f"Copying package at {source_path} to {dest_path}")
if create_links:
os.symlink(op.abspath(source_path), dest_path)
else:
@ -297,7 +297,7 @@ def read_changelog_file(filename):
return
yield version, date, description
with open(filename, "rt", encoding="utf-8") as fp:
with open(filename, encoding="utf-8") as fp:
contents = fp.read()
splitted = re_changelog_header.split(contents)[1:] # the first item is empty
result = []

View File

@ -18,7 +18,7 @@ def get_parser():
def main():
args = get_parser().parse_args()
print("Building {}...".format(args.name[0]))
print(f"Building {args.name[0]}...")
ext = Extension(args.name[0], args.source_files)
setup(
script_args=["build_ext", "--inplace"],

View File

@ -73,7 +73,7 @@ def smart_copy(source_path, dest_path):
"""Copies ``source_path`` to ``dest_path``, recursively and with conflict resolution."""
try:
_smart_move_or_copy(shutil.copy, source_path, dest_path)
except IOError as e:
except OSError as e:
if e.errno in {
21,
13,

View File

@ -216,7 +216,7 @@ class Columns(GUIObject):
self.view.restore_columns()
return
for col in self.column_list:
pref_name = "{}.Columns.{}".format(self.savename, col.name)
pref_name = f"{self.savename}.Columns.{col.name}"
coldata = self.prefaccess.get_default(pref_name, fallback_value={})
if "index" in coldata:
col.ordered_index = coldata["index"]
@ -231,7 +231,7 @@ class Columns(GUIObject):
if not (self.prefaccess and self.savename and self.coldata):
return
for col in self.column_list:
pref_name = "{}.Columns.{}".format(self.savename, col.name)
pref_name = f"{self.savename}.Columns.{col.name}"
coldata = {"index": col.ordered_index, "width": col.width}
if col.optional:
coldata["visible"] = col.visible

View File

@ -451,7 +451,7 @@ class Row:
"""
def __init__(self, table):
super(Row, self).__init__()
super().__init__()
self.table = table
def _edit(self):

View File

@ -77,8 +77,7 @@ class Node(MutableSequence):
if include_self and predicate(self):
yield self
for child in self:
for found in child.findall(predicate, include_self=True):
yield found
yield from child.findall(predicate, include_self=True)
def get_node(self, index_path):
"""Returns the node at ``index_path``.

View File

@ -47,7 +47,7 @@ def log_io_error(func):
def wrapper(path, *args, **kwargs):
try:
return func(path, *args, **kwargs)
except (IOError, OSError) as e:
except OSError as e:
msg = 'Error "{0}" during operation "{1}" on "{2}": "{3}"'
classname = e.__class__.__name__
funcname = func.__name__

View File

@ -374,7 +374,7 @@ def main(source_files, outpath, keywords=None):
fp = open(options.excludefilename, encoding="utf-8")
options.toexclude = fp.readlines()
fp.close()
except IOError:
except OSError:
print(
"Can't read --exclude-file: %s" % options.excludefilename,
file=sys.stderr,

View File

@ -24,7 +24,7 @@ def tixgen(tixurl):
"""
urlpattern = tixurl.format("\\1") # will be replaced buy the content of the first group in re
R = re.compile(r"#(\d+)")
repl = "`#\\1 <{}>`__".format(urlpattern)
repl = f"`#\\1 <{urlpattern}>`__"
return lambda text: R.sub(repl, text)

View File

@ -113,7 +113,7 @@ def test_repeater_with_repeated_notifications():
# If REPEATED_NOTIFICATIONS is not empty, only notifs in this set are repeated (but they're
# still dispatched locally).
class MyRepeater(HelloRepeater):
REPEATED_NOTIFICATIONS = set(["hello"])
REPEATED_NOTIFICATIONS = {"hello"}
def __init__(self, broadcaster):
HelloRepeater.__init__(self, broadcaster)

View File

@ -98,7 +98,7 @@ def test_selection_override():
def test_findall():
t = tree_with_some_nodes()
r = t.findall(lambda n: n.name.startswith("sub"))
eq_(set(r), set([t[0][0], t[0][1]]))
eq_(set(r), {t[0][0], t[0][1]})
def test_findall_dont_include_self():
@ -106,7 +106,7 @@ def test_findall_dont_include_self():
t = tree_with_some_nodes()
del t._name # so that if the predicate is called on `t`, we crash
r = t.findall(lambda n: not n.name.startswith("sub"), include_self=False) # no crash
eq_(set(r), set([t[0], t[1], t[2]]))
eq_(set(r), {t[0], t[1], t[2]})
def test_find_dont_include_self():

View File

@ -14,7 +14,7 @@ import py.path
def eq_(a, b, msg=None):
__tracebackhide__ = True
assert a == b, msg or "%r != %r" % (a, b)
assert a == b, msg or "{!r} != {!r}".format(a, b)
def eq_sorted(a, b, msg=None):
@ -97,17 +97,17 @@ class CallLogger:
__tracebackhide__ = True
if expected is not None:
not_called = set(expected) - set(self.calls)
assert not not_called, "These calls haven't been made: {0}".format(not_called)
assert not not_called, f"These calls haven't been made: {not_called}"
if verify_order:
max_index = 0
for call in expected:
index = self.calls.index(call)
if index < max_index:
raise AssertionError("The call {0} hasn't been made in the correct order".format(call))
raise AssertionError(f"The call {call} hasn't been made in the correct order")
max_index = index
if not_expected is not None:
called = set(not_expected) & set(self.calls)
assert not called, "These calls shouldn't have been made: {0}".format(called)
assert not called, f"These calls shouldn't have been made: {called}"
self.clear_calls()
@ -133,7 +133,7 @@ class TestApp:
parent = self.default_parent
if holder is None:
holder = self
setattr(holder, "{0}_gui".format(name), view)
setattr(holder, f"{name}_gui", view)
gui = class_(parent)
gui.view = view
setattr(holder, name, gui)

View File

@ -112,7 +112,7 @@ def install_gettext_trans(base_folder, lang):
return lambda s: s
try:
return gettext.translation(domain, localedir=base_folder, languages=[lang]).gettext
except IOError:
except OSError:
return lambda s: s
default_gettext = gettext_trget("core")

View File

@ -270,7 +270,7 @@ def format_size(size, decimal=0, forcepower=-1, showdesc=True):
_valid_xml_range = "\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD"
if sys.maxunicode > 0x10000:
_valid_xml_range += "%s-%s" % (chr(0x10000), chr(min(sys.maxunicode, 0x10FFFF)))
_valid_xml_range += "{}-{}".format(chr(0x10000), chr(min(sys.maxunicode, 0x10FFFF)))
RE_INVALID_XML_SUB = re.compile("[^%s]" % _valid_xml_range, re.U).sub
@ -328,11 +328,11 @@ def modified_after(first_path: Path, second_path: Path):
"""
try:
first_mtime = first_path.stat().st_mtime
except (EnvironmentError, AttributeError):
except (OSError, AttributeError):
return False
try:
second_mtime = second_path.stat().st_mtime
except (EnvironmentError, AttributeError):
except (OSError, AttributeError):
return True
return first_mtime > second_mtime

View File

@ -442,6 +442,6 @@ class DupeGuru(QObject):
def select_dest_file(self, prompt, extension):
files = tr("{} file (*.{})").format(extension.upper(), extension)
destination, chosen_filter = QFileDialog.getSaveFileName(self.resultWindow, prompt, "", files)
if not destination.endswith(".{}".format(extension)):
destination = "{}.{}".format(destination, extension)
if not destination.endswith(f".{extension}"):
destination = f"{destination}.{extension}"
return destination

View File

@ -347,7 +347,7 @@ class DirectoriesDialog(QMainWindow):
destination, chosen_filter = QFileDialog.getSaveFileName(self, title, "", files)
if destination:
if not destination.endswith(".dupegurudirs"):
destination = "{}.dupegurudirs".format(destination)
destination = f"{destination}.dupegurudirs"
self.app.model.save_directories_as(destination)
def scanButtonClicked(self):

View File

@ -22,7 +22,7 @@ class File(PhotoBase):
return (size.width(), size.height())
else:
return (0, 0)
except EnvironmentError:
except OSError:
logging.warning("Could not read image '%s'", str(self.path))
return (0, 0)

View File

@ -470,7 +470,7 @@ class ResultWindow(QMainWindow):
destination, chosen_filter = QFileDialog.getSaveFileName(self, title, "", files)
if destination:
if not destination.endswith(".dupeguru"):
destination = "{}.dupeguru".format(destination)
destination = f"{destination}.dupeguru"
self.app.model.save_as(destination)
self.app.recentResults.insertItem(destination)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# Created On: 2009-10-16
# Copyright 2015 Hardcoded Software (http://www.hardcoded.net)

View File

@ -38,7 +38,7 @@ class ClearableEdit(QLineEdit):
self._clearButton = LineEditButton(self)
frame_width = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth)
padding_right = self._clearButton.sizeHint().width() + frame_width + 1
stylesheet = "QLineEdit {{ padding-right:{0}px; }}".format(padding_right)
stylesheet = f"QLineEdit {{ padding-right:{padding_right}px; }}"
self.setStyleSheet(stylesheet)
self._updateClearButton()

2
run.py
View File

@ -71,7 +71,7 @@ def main():
# has been installed
from qt.app import DupeGuru
app.setWindowIcon(QIcon(QPixmap(":/{0}".format(DupeGuru.LOGO_NAME))))
app.setWindowIcon(QIcon(QPixmap(f":/{DupeGuru.LOGO_NAME}")))
global dgapp
dgapp = DupeGuru()
install_excepthook("https://github.com/hsoft/dupeguru/issues")