diff --git a/pyproject.toml b/pyproject.toml index 937984a..ad37e10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,35 @@ Homepage = "https://libraryofcongress.github.io/bagit-python/" [tool] [tool.ruff] -target-version = "py38" +target-version = "py39" + +lint.select = [ + "A", # flake8-builtins + "ASYNC", # flake8-async + "B", # flake8-bugbear + "BLE", # flake8-blind-except + "C4", # flake8-comprehensions + "ERA", # eradicate + "EXE", # flake8-executable + "F", # Pyflakes + "G", # flake8-logging-format + "ICN", # flake8-import-conventions + "INP", # flake8-no-pep420 + "INT", # flake8-gettext + "ISC", # flake8-implicit-str-concat + "N", # pep8-naming + "PIE", # flake8-pie + "PT", # flake8-pytest-style + "PYI", # flake8-pyi + "RET", # flake8-return + "RSE", # flake8-raise + "RUF", # Ruff-specific rules + "TCH", # flake8-type-checking + "TID", # flake8-tidy-imports + "UP", # pyupgrade + "W", # pycodestyle +] + [tool.isort] line_length = 110 diff --git a/src/bagit/__init__.py b/src/bagit/__init__.py index c17b1f2..6b7357e 100755 --- a/src/bagit/__init__.py +++ b/src/bagit/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import argparse import codecs @@ -208,74 +207,73 @@ def make_bag( raise BagError( _("Read permissions are required to calculate file fixities") ) - else: - LOGGER.info(_("Creating data directory")) - - # FIXME: if we calculate full paths we won't need to deal with changing directories - os.chdir(bag_dir) - cwd = os.getcwd() - temp_data = tempfile.mkdtemp(dir=cwd) - - for f in os.listdir("."): - if os.path.abspath(f) == temp_data: - continue - new_f = os.path.join(temp_data, f) - LOGGER.info( - _("Moving %(source)s to %(destination)s"), - {"source": f, "destination": new_f}, - ) - os.rename(f, new_f) + LOGGER.info(_("Creating data directory")) + + # FIXME: if we calculate full paths we won't need to deal with changing directories + os.chdir(bag_dir) + cwd = os.getcwd() + temp_data = tempfile.mkdtemp(dir=cwd) + for f in os.listdir("."): + if os.path.abspath(f) == temp_data: + continue + new_f = os.path.join(temp_data, f) LOGGER.info( _("Moving %(source)s to %(destination)s"), - {"source": temp_data, "destination": "data"}, + {"source": f, "destination": new_f}, ) - while True: - try: - os.rename(temp_data, "data") - break - except PermissionError as e: - if hasattr(e, "winerror") and e.winerror == 5: - LOGGER.warning( - _( - "PermissionError [WinError 5] when renaming temp folder. Retrying in 10 seconds..." - ) + os.rename(f, new_f) + + LOGGER.info( + _("Moving %(source)s to %(destination)s"), + {"source": temp_data, "destination": "data"}, + ) + while True: + try: + os.rename(temp_data, "data") + break + except PermissionError as e: + if hasattr(e, "winerror") and e.winerror == 5: + LOGGER.warning( + _( + "PermissionError [WinError 5] when renaming temp folder. Retrying in 10 seconds..." ) - time.sleep(10) - else: - raise + ) + time.sleep(10) + else: + raise - # permissions for the payload directory should match those of the - # original directory - os.chmod("data", os.stat(cwd).st_mode) + # permissions for the payload directory should match those of the + # original directory + os.chmod("data", os.stat(cwd).st_mode) - total_bytes, total_files = make_manifests( - "data", processes, algorithms=checksums, encoding=encoding - ) + total_bytes, total_files = make_manifests( + "data", processes, algorithms=checksums, encoding=encoding + ) - LOGGER.info(_("Creating bagit.txt")) - txt = """BagIt-Version: 1.0\nTag-File-Character-Encoding: UTF-8\n""" - with open_text_file("bagit.txt", "w") as bagit_file: - bagit_file.write(txt) - - LOGGER.info(_("Creating bag-info.txt")) - if bag_info is None: - bag_info = {} - - # allow 'Bagging-Date' and 'Bag-Software-Agent' to be overidden - if "Bagging-Date" not in bag_info: - bag_info["Bagging-Date"] = date.strftime(date.today(), "%Y-%m-%d") - if "Bag-Software-Agent" not in bag_info: - bag_info["Bag-Software-Agent"] = "bagit.py v%s <%s>" % ( - VERSION, - PROJECT_URL, - ) + LOGGER.info(_("Creating bagit.txt")) + txt = """BagIt-Version: 1.0\nTag-File-Character-Encoding: UTF-8\n""" + with open_text_file("bagit.txt", "w") as bagit_file: + bagit_file.write(txt) + + LOGGER.info(_("Creating bag-info.txt")) + if bag_info is None: + bag_info = {} + + # allow 'Bagging-Date' and 'Bag-Software-Agent' to be overidden + if "Bagging-Date" not in bag_info: + bag_info["Bagging-Date"] = date.strftime(date.today(), "%Y-%m-%d") + if "Bag-Software-Agent" not in bag_info: + bag_info["Bag-Software-Agent"] = "bagit.py v%s <%s>" % ( + VERSION, + PROJECT_URL, + ) - bag_info["Payload-Oxum"] = "%s.%s" % (total_bytes, total_files) - _make_tag_file("bag-info.txt", bag_info) + bag_info["Payload-Oxum"] = "%s.%s" % (total_bytes, total_files) + _make_tag_file("bag-info.txt", bag_info) - for c in checksums: - _make_tagmanifest_file(c, bag_dir, encoding="utf-8") + for c in checksums: + _make_tagmanifest_file(c, bag_dir, encoding="utf-8") except Exception: LOGGER.exception(_("An error occurred creating a bag in %s"), bag_dir) raise @@ -285,14 +283,14 @@ def make_bag( return Bag(bag_dir) -class Bag(object): +class Bag: """A representation of a bag.""" valid_files = ["bagit.txt", "fetch.txt"] valid_directories = ["data"] def __init__(self, path): - super(Bag, self).__init__() + super().__init__() self.tags = {} self.info = {} #: Dictionary of manifest entries and the checksum values for each @@ -723,8 +721,7 @@ def _load_manifests(self): ) if self.version_info >= (1,): raise BagError(msg % warning_ctx) - else: - LOGGER.warning(msg, warning_ctx) + LOGGER.warning(msg, warning_ctx) else: raise BagError( _( @@ -960,7 +957,7 @@ class BagError(Exception): class BagValidationError(BagError): def __init__(self, message, details=None): - super(BagValidationError, self).__init__() + super().__init__() if details is None: details = [] @@ -977,14 +974,14 @@ def __str__(self): class ManifestErrorDetail(BagError): def __init__(self, path): - super(ManifestErrorDetail, self).__init__() + super().__init__() self.path = path class ChecksumMismatch(ManifestErrorDetail): def __init__(self, path, algorithm=None, expected=None, found=None): - super(ChecksumMismatch, self).__init__(path) + super().__init__(path) self.path = path self.algorithm = algorithm @@ -1021,7 +1018,7 @@ class FileNormalizationConflict(BagError): """ def __init__(self, file_a, file_b): - super(FileNormalizationConflict, self).__init__() + super().__init__() self.file_a = file_a self.file_b = file_b @@ -1079,8 +1076,7 @@ def build_unicode_normalized_lookup_dict(filenames): normalized_filename = normalize_unicode(filename) if normalized_filename in output: raise FileNormalizationConflict(filename, output[normalized_filename]) - else: - output[normalized_filename] = filename + output[normalized_filename] = filename return output @@ -1153,7 +1149,7 @@ def _calculate_file_hashes(full_path, f_hashers): break for i in f_hashers.values(): i.update(block) - except (OSError, IOError) as e: + except OSError as e: raise BagValidationError( _("Could not read %(filename)s: %(error)s") % {"filename": full_path, "error": str(e)} diff --git a/test.py b/test.py index f79bca0..5b9f043 100644 --- a/test.py +++ b/test.py @@ -1,6 +1,4 @@ -# encoding: utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals import codecs import datetime @@ -38,7 +36,7 @@ class SelfCleaningTestCase(unittest.TestCase): """TestCase subclass which cleans up self.tmpdir after each test""" def setUp(self): - super(SelfCleaningTestCase, self).setUp() + super().setUp() self.starting_directory = ( os.getcwd() @@ -62,7 +60,7 @@ def tearDown(self): shutil.rmtree(self.tmpdir) - super(SelfCleaningTestCase, self).tearDown() + super().tearDown() @mock.patch( @@ -267,7 +265,7 @@ def test_validation_completeness_error_details(self): def test_bom_in_bagit_txt(self): bag = bagit.make_bag(self.tmpdir) BOM = codecs.BOM_UTF8.decode("utf-8") - with open(j(self.tmpdir, "bagit.txt"), "r") as bf: + with open(j(self.tmpdir, "bagit.txt")) as bf: bagfile = BOM + bf.read() with open(j(self.tmpdir, "bagit.txt"), "w") as bf: bf.write(bagfile) @@ -332,7 +330,7 @@ def test_mixed_case_checksums(self): hasher = hashlib.new("md5") contents = slurp_text_file(j(self.tmpdir, "manifest-md5.txt")).encode("utf-8") hasher.update(contents) - with open(j(self.tmpdir, "tagmanifest-md5.txt"), "r") as tagmanifest: + with open(j(self.tmpdir, "tagmanifest-md5.txt")) as tagmanifest: tagman_contents = tagmanifest.read() tagman_contents = tagman_contents.replace( bag.entries["manifest-md5.txt"]["md5"], hasher.hexdigest() @@ -424,7 +422,7 @@ def test_validate_optional_tagfile_in_directory(self): self.assertRaises(bagit.BagValidationError, self.validate, bag) hasher = hashlib.new("md5") - with open(j(tagdir, "tagfolder", "tagfile"), "r") as tf: + with open(j(tagdir, "tagfolder", "tagfile")) as tf: contents = tf.read().encode("utf-8") hasher.update(contents) with open(j(self.tmpdir, "tagmanifest-md5.txt"), "w") as tagman: @@ -454,7 +452,7 @@ def test_validate_unreadable_file(self): class TestMultiprocessValidation(TestSingleProcessValidation): def validate(self, bag, *args, **kwargs): - return super(TestMultiprocessValidation, self).validate( + return super().validate( bag, *args, processes=2, **kwargs ) @@ -1014,7 +1012,7 @@ def test_open_bag_with_unknown_encoding(self): class TestFetch(SelfCleaningTestCase): def setUp(self): - super(TestFetch, self).setUp() + super().setUp() # All of these tests will involve fetch.txt usage with an existing bag # so we'll simply create one: diff --git a/utils/bench.py b/utils/bench.py index 06b4796..274cd50 100755 --- a/utils/bench.py +++ b/utils/bench.py @@ -26,7 +26,7 @@ ftp.retrlines("NLST", files.append) for file in files: - print(("fetching %s" % file)) + print("fetching %s" % file) fh = open(os.path.join("bench-data", file), "wb") ftp.retrbinary("RETR %s" % file, fh.write) fh.close() @@ -49,7 +49,7 @@ for p in range(1, 9): t = timeit.Timer(statement % p) print( - ("create w/ %s processes: %.2f seconds " % (p, (10 * t.timeit(number=10) / 10))) + "create w/ %s processes: %.2f seconds " % (p, (10 * t.timeit(number=10) / 10)) ) @@ -71,10 +71,10 @@ for p in range(1, 9): t = timeit.Timer(statement % p) print( - ( + "validate w/ %s processes: %.2f seconds " % (p, (10 * t.timeit(number=10) / 10)) - ) + ) shutil.rmtree("bench-data-bag") diff --git a/utils/locales.py b/utils/locales.py index aaed193..3345221 100755 --- a/utils/locales.py +++ b/utils/locales.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# encoding: utf-8 import sys import subprocess