diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b37ba86..03b1d44 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,17 +20,14 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip setuptools wheel - pip install coverage - pip install --editable . - - name: Run test - run: python -m unittest discover + - name: Install uv + uses: astral-sh/setup-uv@v5 + - name: Run tests + run: uv run pytest diff --git a/.gitignore b/.gitignore index 1f4b930..333e320 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ dist MANIFEST bagit.egg-info .idea +uv.lock +*.log diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index db7a199..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,7 +0,0 @@ -prune test-data -exclude .* -exclude Dockerfile -exclude MANIFEST.in -exclude test.py -exclude bench.py -recursive-include locale *.po *.mo diff --git a/Makefile b/Makefile index 9907ac7..17b0319 100644 --- a/Makefile +++ b/Makefile @@ -3,13 +3,13 @@ COMPILED_MESSAGES=$(patsubst %.po,%.mo, $(wildcard locale/*/LC_MESSAGES/bagit-py all: messages compile clean: - rm -f locale/*/LC_MESSAGES/*.mo + rm -f src/bagit/locale/*/LC_MESSAGES/*.mo messages: - xgettext --language=python -d bagit-python --no-location -o locale/bagit-python.pot bagit.py + xgettext --language=python -d bagit-python --no-location -o src/bagit/locale/bagit-python.pot src/bagit/__init__.py # Until http://savannah.gnu.org/bugs/?20923 is fixed: - sed -i '' -e 's/CHARSET/UTF-8/g' locale/bagit-python.pot - msgmerge --no-fuzzy-matching --lang=en --output-file=locale/en/LC_MESSAGES/bagit-python.po locale/en/LC_MESSAGES/bagit-python.po locale/bagit-python.pot + sed -i '' -e 's/CHARSET/UTF-8/g' src/bagit/locale/bagit-python.pot + msgmerge --no-fuzzy-matching --lang=en --output-file=src/bagit/locale/en/LC_MESSAGES/bagit-python.po src/bagit/locale/en/LC_MESSAGES/bagit-python.po src/bagit/locale/bagit-python.pot %.mo: %.po msgfmt -o $@ $< diff --git a/README.rst b/README.rst index d7804e4..f4fa486 100644 --- a/README.rst +++ b/README.rst @@ -219,23 +219,23 @@ Contributing to bagit-python development % git clone git://github.com/LibraryOfCongress/bagit-python.git % cd bagit-python # MAKE CHANGES - % python test.py + % uv run pytest Running the tests ~~~~~~~~~~~~~~~~~ -You can quickly run the tests using the built-in unittest framework: +You can quickly run the tests using `uv `_. :: - python -m unittest discover + uv run pytest If you have Docker installed, you can run the tests under Linux inside a container: :: - % docker build -t bagit:latest . && docker run -it bagit:latest + docker build -t bagit:latest . && docker run -it bagit:latest Benchmarks ---------- @@ -246,7 +246,7 @@ bench utility: :: - % ./bench.py + ./utils/bench.py License ------- diff --git a/pyproject.toml b/pyproject.toml index 7cd2d39..937984a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] -requires = ["setuptools>=64", "setuptools-scm>=8"] -build-backend = "setuptools.build_meta" +requires = ["hatchling", "hatch-vcs"] +build-backend = "hatchling.build" [project] name = "bagit" @@ -8,7 +8,8 @@ dynamic = ["version"] description = "Create and validate BagIt packages" readme = {file = "README.rst", content-type = "text/x-rst"} authors = [ - { name = "Ed Summers", email = "ehs@pobox.com" }, + { name = "Chris Adams", email = "chris@improbable.org" }, + { name = "Ed Summers", email = "ehs@pobox.com" } ] classifiers = [ "Intended Audience :: Developers", @@ -18,6 +19,10 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: System :: Filesystems", ] +requires-python = ">= 3.9" + +[project.scripts] +"bagit" = "bagit:main" [project.urls] Homepage = "https://libraryofcongress.github.io/bagit-python/" @@ -27,14 +32,34 @@ Homepage = "https://libraryofcongress.github.io/bagit-python/" [tool.ruff] target-version = "py38" - -[tool.setuptools_scm] - [tool.isort] line_length = 110 default_section = "THIRDPARTY" known_first_party = "bagit" +[tool.pytest.ini_options] +testpaths = ["test.py"] +addopts = "-v" + [tool.coverage.run] branch = true -include = "bagit.py" +include = "src" + +[tool.hatch.version] +source = "vcs" + +[tool.hatch.build.targets.sdist] +packages = ["src/bagit"] +include = [ + "src/bagit/*.py", + "src/bagit/locale/*.po", + "src/bagit/locale/*.mo", +] + + +[dependency-groups] +dev = [ + "coverage>=7.8.2", + "pytest>=8.4.0", + "ruff>=0.11.12", +] diff --git a/setup.py b/setup.py deleted file mode 100644 index 15c91b4..0000000 --- a/setup.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -from __future__ import absolute_import, print_function - -import glob -import os -import subprocess -import sys - -from setuptools import setup - -description = "Create and validate BagIt packages" - - -def get_message_catalogs(): - message_catalogs = [] - - for po_file in glob.glob("locale/*/LC_MESSAGES/bagit-python.po"): - mo_file = po_file.replace(".po", ".mo") - - if not os.path.exists(mo_file) or os.path.getmtime(mo_file) < os.path.getmtime( - po_file - ): - try: - subprocess.check_call(["msgfmt", "-o", mo_file, po_file]) - except (OSError, subprocess.CalledProcessError) as exc: - print( - "Translation catalog %s could not be compiled (is gettext installed?) " - " — translations will not be available for this language: %s" - % (po_file, exc), - file=sys.stderr, - ) - continue - - message_catalogs.append((os.path.dirname(mo_file), (mo_file,))) - - return message_catalogs - - -setup( - name="bagit", - use_scm_version=True, - url="https://libraryofcongress.github.io/bagit-python/", - author="Ed Summers", - author_email="ehs@pobox.com", - py_modules=["bagit"], - scripts=["bagit.py"], - data_files=get_message_catalogs(), - description=description, - platforms=["POSIX"], - setup_requires=["setuptools_scm"], - classifiers=[ - "License :: Public Domain", - "Intended Audience :: Developers", - "Topic :: Communications :: File Sharing", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: System :: Filesystems", - "Programming Language :: Python :: 3", - ], -) diff --git a/bagit.py b/src/bagit/__init__.py similarity index 99% rename from bagit.py rename to src/bagit/__init__.py index c9c1737..ce24059 100755 --- a/bagit.py +++ b/src/bagit/__init__.py @@ -240,7 +240,7 @@ def make_bag( ) LOGGER.info(_("Creating bagit.txt")) - txt = """BagIt-Version: 0.97\nTag-File-Character-Encoding: UTF-8\n""" + 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) @@ -1479,10 +1479,7 @@ def _make_parser(): checksum_args = parser.add_argument_group( _("Checksum Algorithms"), - _( - "Select the manifest algorithms to be used when creating bags" - " (default=%s)" - ) + _("Select the manifest algorithms to be used when creating bags (default=%s)") % ", ".join(DEFAULT_CHECKSUMS), ) diff --git a/locale/bagit-python.pot b/src/bagit/locale/bagit-python.pot similarity index 96% rename from locale/bagit-python.pot rename to src/bagit/locale/bagit-python.pot index 3543c31..85bf167 100644 --- a/locale/bagit-python.pot +++ b/src/bagit/locale/bagit-python.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-06-26 10:28-0400\n" +"POT-Creation-Date: 2025-06-05 12:14-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -226,20 +226,23 @@ msgid "" "%(found_byte_count)d bytes" msgstr "" -msgid "Bag validation failed" +msgid "Bag is incomplete" msgstr "" #, python-format msgid "Unable to calculate file hashes for %s" msgstr "" +msgid "Bag validation failed" +msgstr "" + msgid "bagit.txt must not contain a byte-order mark" msgstr "" #, python-format msgid "" -"%(path)s %(algorithm)s validation failed: expected=\"%(expected)s\" found=" -"\"%(found)s\"" +"%(path)s %(algorithm)s validation failed: expected=\"%(expected)s\" " +"found=\"%(found)s\"" msgstr "" #, python-format @@ -345,16 +348,23 @@ msgstr "" msgid "bagit-python version %s" msgstr "" -msgid "The number of processes must be 0 or greater" +msgid "The number of processes must be greater than 0" msgstr "" msgid "--fast is only allowed as an option for --validate!" msgstr "" +msgid "--completeness-only is only allowed as an option for --validate!" +msgstr "" + #, python-format msgid "%s valid according to Payload-Oxum" msgstr "" +#, python-format +msgid "%s is complete and valid according to Payload-Oxum" +msgstr "" + #, python-format msgid "%s is valid" msgstr "" diff --git a/locale/bagit.pot b/src/bagit/locale/bagit.pot similarity index 100% rename from locale/bagit.pot rename to src/bagit/locale/bagit.pot diff --git a/locale/en/LC_MESSAGES/bagit-python.po b/src/bagit/locale/en/LC_MESSAGES/bagit-python.po similarity index 96% rename from locale/en/LC_MESSAGES/bagit-python.po rename to src/bagit/locale/en/LC_MESSAGES/bagit-python.po index ddf0f3f..8767e56 100644 --- a/locale/en/LC_MESSAGES/bagit-python.po +++ b/src/bagit/locale/en/LC_MESSAGES/bagit-python.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-06-26 10:28-0400\n" +"POT-Creation-Date: 2025-06-05 12:14-0400\n" "PO-Revision-Date: 2017-04-27 15:02-0400\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" @@ -233,20 +233,23 @@ msgid "" "%(found_byte_count)d bytes" msgstr "" -msgid "Bag validation failed" +msgid "Bag is incomplete" msgstr "" #, python-format msgid "Unable to calculate file hashes for %s" msgstr "Unable to calculate file hashes for %s" +msgid "Bag validation failed" +msgstr "" + msgid "bagit.txt must not contain a byte-order mark" msgstr "" #, python-format msgid "" -"%(path)s %(algorithm)s validation failed: expected=\"%(expected)s\" found=" -"\"%(found)s\"" +"%(path)s %(algorithm)s validation failed: expected=\"%(expected)s\" " +"found=\"%(found)s\"" msgstr "" #, python-format @@ -352,16 +355,23 @@ msgstr "" msgid "bagit-python version %s" msgstr "" -msgid "The number of processes must be 0 or greater" +msgid "The number of processes must be greater than 0" msgstr "" msgid "--fast is only allowed as an option for --validate!" msgstr "" +msgid "--completeness-only is only allowed as an option for --validate!" +msgstr "" + #, python-format msgid "%s valid according to Payload-Oxum" msgstr "%s valid according to Payload-Oxum" +#, python-format +msgid "%s is complete and valid according to Payload-Oxum" +msgstr "" + #, python-format msgid "%s is valid" msgstr "%s is valid" diff --git a/test.py b/test.py index 735bd73..c18784d 100644 --- a/test.py +++ b/test.py @@ -523,7 +523,7 @@ def test_make_bag(self): tagmanifest_txt = slurp_text_file( j(self.tmpdir, "tagmanifest-md5.txt") ).splitlines() - self.assertIn("9e5ad981e0d29adc278f6a294b8c2aca bagit.txt", tagmanifest_txt) + self.assertIn("eaa2c609ff6371712f623f5531945b44 bagit.txt", tagmanifest_txt) self.assertIn( "a0ce6631a2a6d1a88e6d38453ccc72a5 manifest-md5.txt", tagmanifest_txt ) diff --git a/bench.py b/utils/bench.py similarity index 100% rename from bench.py rename to utils/bench.py diff --git a/utils/locales.py b/utils/locales.py new file mode 100755 index 0000000..aaed193 --- /dev/null +++ b/utils/locales.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# encoding: utf-8 + +import sys +import subprocess + +from pathlib import Path + +src_dir = Path(__file__).parent.parent / "src" + +for po_file in src_dir.glob("bagit/locale/*/LC_MESSAGES/bagit-python.po"): + mo_file = po_file.with_suffix(".mo") + + if not mo_file.is_file() or mo_file.stat().st_mtime < po_file.stat().st_mtime: + try: + print(f"compiling {po_file} to {mo_file}") + subprocess.check_call(["msgfmt", "-o", mo_file, po_file]) + except (OSError, subprocess.CalledProcessError) as exc: + print( + "Translation catalog %s could not be compiled (is gettext installed?) " + " — translations will not be available for this language: %s" + % (po_file, exc), + file=sys.stderr, + ) + continue