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