From 63dd4d4561af28a079ae92046868b1062876336b Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Wed, 27 Apr 2022 20:53:12 -0500 Subject: [PATCH] Apply pyupgrade changes --- build.py | 4 ++-- core/app.py | 2 +- core/directories.py | 9 ++++----- core/engine.py | 8 ++++---- core/fs.py | 6 +++--- core/pe/cache.py | 2 +- core/pe/cache_shelve.py | 4 ++-- core/pe/matchblock.py | 2 +- core/prioritize.py | 2 +- core/results.py | 8 ++++---- core/tests/app_test.py | 2 +- core/tests/engine_test.py | 26 +++++++++++++------------- core/tests/exclude_test.py | 8 ++++---- core/tests/results_test.py | 6 +++--- core/tests/scanner_test.py | 14 +++++++------- hscommon/build.py | 20 ++++++++++---------- hscommon/build_ext.py | 2 +- hscommon/conflict.py | 2 +- hscommon/gui/column.py | 4 ++-- hscommon/gui/table.py | 2 +- hscommon/gui/tree.py | 3 +-- hscommon/path.py | 2 +- hscommon/pygettext.py | 2 +- hscommon/sphinxgen.py | 2 +- hscommon/tests/notify_test.py | 2 +- hscommon/tests/tree_test.py | 4 ++-- hscommon/testutil.py | 10 +++++----- hscommon/trans.py | 2 +- hscommon/util.py | 6 +++--- qt/app.py | 4 ++-- qt/directories_dialog.py | 2 +- qt/pe/photo.py | 2 +- qt/result_window.py | 2 +- qtlib/app.py | 1 - qtlib/search_edit.py | 2 +- run.py | 2 +- 36 files changed, 89 insertions(+), 92 deletions(-) diff --git a/build.py b/build.py index 01165deb..2a615b79 100644 --- a/build.py +++ b/build.py @@ -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() diff --git a/core/app.py b/core/app.py index 786e57f3..3ca97823 100644 --- a/core/app.py +++ b/core/app.py @@ -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): diff --git a/core/directories.py b/core/directories.py index edac6b93..5cfcf239 100644 --- a/core/directories.py +++ b/core/directories.py @@ -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): diff --git a/core/engine.py b/core/engine.py index 958c1f7b..f9e9a519 100644 --- a/core/engine.py +++ b/core/engine.py @@ -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 diff --git a/core/fs.py b/core/fs.py index 40dea749..d006611f 100644 --- a/core/fs.py +++ b/core/fs.py @@ -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) diff --git a/core/pe/cache.py b/core/pe/cache.py index 7c1b7a1d..9928f098 100644 --- a/core/pe/cache.py +++ b/core/pe/cache.py @@ -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. diff --git a/core/pe/cache_shelve.py b/core/pe/cache_shelve.py index 2aaeed91..d1264a8c 100644 --- a/core/pe/cache_shelve.py +++ b/core/pe/cache_shelve.py @@ -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): diff --git a/core/pe/matchblock.py b/core/pe/matchblock.py index 063ec725..61ebacd7 100644 --- a/core/pe/matchblock.py +++ b/core/pe/matchblock.py @@ -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( diff --git a/core/prioritize.py b/core/prioritize.py index 52c11d51..fc5de01d 100644 --- a/core/prioritize.py +++ b/core/prioritize.py @@ -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): diff --git a/core/results.py b/core/results.py index 377a7313..6767bae2 100644 --- a/core/results.py +++ b/core/results.py @@ -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) diff --git a/core/tests/app_test.py b/core/tests/app_test.py index bc0104ba..3c64d30e 100644 --- a/core/tests/app_test.py +++ b/core/tests/app_test.py @@ -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]) diff --git a/core/tests/engine_test.py b/core/tests/engine_test.py index b35076f3..e55a6e7e 100644 --- a/core/tests/engine_test.py +++ b/core/tests/engine_test.py @@ -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 diff --git a/core/tests/exclude_test.py b/core/tests/exclude_test.py index af812211..60a2d2a0 100644 --- a/core/tests/exclude_test.py +++ b/core/tests/exclude_test.py @@ -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("|"))) diff --git a/core/tests/results_test.py b/core/tests/results_test.py index 363f3cdc..2a64a054 100644 --- a/core/tests/results_test.py +++ b/core/tests/results_test.py @@ -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")) diff --git a/core/tests/scanner_test.py b/core/tests/scanner_test.py index bfdbdc57..0354ae3f 100644 --- a/core/tests/scanner_test.py +++ b/core/tests/scanner_test.py @@ -29,7 +29,7 @@ class NamedObject: self.words = getwords(name) def __repr__(self): - return "" % (self.name, self.path) + return "".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" diff --git a/hscommon/build.py b/hscommon/build.py index fdb34fea..fa442bd7 100644 --- a/hscommon/build.py +++ b/hscommon/build.py @@ -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 = [] diff --git a/hscommon/build_ext.py b/hscommon/build_ext.py index ce8b5c71..db10f745 100644 --- a/hscommon/build_ext.py +++ b/hscommon/build_ext.py @@ -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"], diff --git a/hscommon/conflict.py b/hscommon/conflict.py index 42acf46d..970ad1df 100644 --- a/hscommon/conflict.py +++ b/hscommon/conflict.py @@ -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, diff --git a/hscommon/gui/column.py b/hscommon/gui/column.py index 7b6c7787..5d25c7b6 100644 --- a/hscommon/gui/column.py +++ b/hscommon/gui/column.py @@ -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 diff --git a/hscommon/gui/table.py b/hscommon/gui/table.py index 975daf19..cd25e0c1 100644 --- a/hscommon/gui/table.py +++ b/hscommon/gui/table.py @@ -451,7 +451,7 @@ class Row: """ def __init__(self, table): - super(Row, self).__init__() + super().__init__() self.table = table def _edit(self): diff --git a/hscommon/gui/tree.py b/hscommon/gui/tree.py index ea6d6562..ff8499b3 100644 --- a/hscommon/gui/tree.py +++ b/hscommon/gui/tree.py @@ -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``. diff --git a/hscommon/path.py b/hscommon/path.py index 08acce80..65617f46 100644 --- a/hscommon/path.py +++ b/hscommon/path.py @@ -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__ diff --git a/hscommon/pygettext.py b/hscommon/pygettext.py index fbe803be..026219bf 100644 --- a/hscommon/pygettext.py +++ b/hscommon/pygettext.py @@ -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, diff --git a/hscommon/sphinxgen.py b/hscommon/sphinxgen.py index cda3baa1..d49ae0c6 100644 --- a/hscommon/sphinxgen.py +++ b/hscommon/sphinxgen.py @@ -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) diff --git a/hscommon/tests/notify_test.py b/hscommon/tests/notify_test.py index 7450ea5e..14dbb9fc 100644 --- a/hscommon/tests/notify_test.py +++ b/hscommon/tests/notify_test.py @@ -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) diff --git a/hscommon/tests/tree_test.py b/hscommon/tests/tree_test.py index addd059c..f9d20136 100644 --- a/hscommon/tests/tree_test.py +++ b/hscommon/tests/tree_test.py @@ -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(): diff --git a/hscommon/testutil.py b/hscommon/testutil.py index b87415b0..87338fd5 100644 --- a/hscommon/testutil.py +++ b/hscommon/testutil.py @@ -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) diff --git a/hscommon/trans.py b/hscommon/trans.py index 7767f7be..564739c5 100644 --- a/hscommon/trans.py +++ b/hscommon/trans.py @@ -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") diff --git a/hscommon/util.py b/hscommon/util.py index 22b023c3..febd3c6a 100644 --- a/hscommon/util.py +++ b/hscommon/util.py @@ -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 diff --git a/qt/app.py b/qt/app.py index 225544fd..e5aaaf99 100644 --- a/qt/app.py +++ b/qt/app.py @@ -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 diff --git a/qt/directories_dialog.py b/qt/directories_dialog.py index 617ac868..82b3f1f2 100644 --- a/qt/directories_dialog.py +++ b/qt/directories_dialog.py @@ -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): diff --git a/qt/pe/photo.py b/qt/pe/photo.py index 4ba1de76..167ef260 100644 --- a/qt/pe/photo.py +++ b/qt/pe/photo.py @@ -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) diff --git a/qt/result_window.py b/qt/result_window.py index c326fdb9..0fc47eb3 100644 --- a/qt/result_window.py +++ b/qt/result_window.py @@ -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) diff --git a/qtlib/app.py b/qtlib/app.py index 7842e258..043a201d 100644 --- a/qtlib/app.py +++ b/qtlib/app.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Created By: Virgil Dupras # Created On: 2009-10-16 # Copyright 2015 Hardcoded Software (http://www.hardcoded.net) diff --git a/qtlib/search_edit.py b/qtlib/search_edit.py index f1273076..e7ecb6c2 100644 --- a/qtlib/search_edit.py +++ b/qtlib/search_edit.py @@ -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() diff --git a/run.py b/run.py index 8b49584b..8cfa7d45 100644 --- a/run.py +++ b/run.py @@ -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")