From c60d3470b9653af3dbce2bd7c3083169e42ee36f Mon Sep 17 00:00:00 2001 From: Chris Adams Date: Fri, 27 Feb 2026 15:17:04 -0500 Subject: [PATCH 1/5] Update pre-commit hooks --- .pre-commit-config.yaml | 6 +++--- bagit.py | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9f1dc32..31dc8e5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,19 +2,19 @@ exclude: ".*test-data.*" repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.9 + rev: v0.15.2 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - id: ruff-format - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-added-large-files args: ["--maxkb=128"] - id: check-ast - - id: check-byte-order-marker + - id: fix-byte-order-marker - id: check-case-conflict - id: check-docstring-first - id: check-executables-have-shebangs diff --git a/bagit.py b/bagit.py index eab85d3..7eee184 100755 --- a/bagit.py +++ b/bagit.py @@ -1489,10 +1489,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), ) From 487b63bbecfe12734c2d05af9f16a09fea35a657 Mon Sep 17 00:00:00 2001 From: Chris Adams Date: Fri, 27 Feb 2026 15:17:09 -0500 Subject: [PATCH 2/5] Replace os.path.commonprefix with os.path.commonpath See https://sethmlarson.dev/deprecate-confusing-apis-like-os-path-commonprefix --- bagit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bagit.py b/bagit.py index 7eee184..ea8fcb4 100755 --- a/bagit.py +++ b/bagit.py @@ -946,7 +946,7 @@ def _path_is_dangerous(self, path): real_path = os.path.normpath(real_path) bag_path = os.path.realpath(self.path) bag_path = os.path.normpath(bag_path) - common = os.path.commonprefix((bag_path, real_path)) + common = os.path.commonpath((bag_path, real_path)) return not (common == bag_path) From 226f8b0c4b9fd0e1b29bf2ce114f1ff7d6f48495 Mon Sep 17 00:00:00 2001 From: Ed Summers Date: Thu, 5 Jun 2025 12:19:21 -0400 Subject: [PATCH 3/5] Use pyproject and remove setup.py This commit includes changes to remove the use of setup.py and setuptools in favor of using the existing pyrproject.toml and hatch. A source layout was chosen, which required several changes such as: - pushing bagit.py into src/bagit/ - pushing locale into src/bagit/locale The gettext related code in setup.py was relocated to utils/locales.py It's possible the way that the gettext functionality is setup needs to adjustment, as I'm new to using it. Also we now rely on running tests with uv and pytest, which happens in the test github action. We could chose to update the PyPI publishing step to use uv build/publish too? --- .github/workflows/test.yml | 13 ++-- .gitignore | 2 + MANIFEST.in | 7 --- Makefile | 8 +-- README.rst | 10 +-- pyproject.toml | 39 +++++++++--- setup.py | 61 ------------------- bagit.py => src/bagit/__init__.py | 2 +- {locale => src/bagit/locale}/bagit-python.pot | 20 ++++-- {locale => src/bagit/locale}/bagit.pot | 0 .../locale}/en/LC_MESSAGES/bagit-python.po | 20 ++++-- test.py | 2 +- bench.py => utils/bench.py | 0 utils/locales.py | 25 ++++++++ 14 files changed, 105 insertions(+), 104 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 setup.py rename bagit.py => src/bagit/__init__.py (99%) rename {locale => src/bagit/locale}/bagit-python.pot (96%) rename {locale => src/bagit/locale}/bagit.pot (100%) rename {locale => src/bagit/locale}/en/LC_MESSAGES/bagit-python.po (96%) rename bench.py => utils/bench.py (100%) create mode 100755 utils/locales.py 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 ed33262..1ad7710 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 ea8fcb4..b38352e 100755 --- a/bagit.py +++ b/src/bagit/__init__.py @@ -250,7 +250,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) 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 From 908ee4867729e8a0a0365f0b2c482bae412587e2 Mon Sep 17 00:00:00 2001 From: Chris Adams Date: Fri, 27 Feb 2026 15:32:07 -0500 Subject: [PATCH 4/5] Add Python 3.14 to test matrix --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 03b1d44..ed2dfcc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} From ed5855c33666b47b0ae89e1067a9a12c6867eedb Mon Sep 17 00:00:00 2001 From: Chris Adams Date: Fri, 27 Feb 2026 15:36:07 -0500 Subject: [PATCH 5/5] Ruff simplifications --- src/bagit/__init__.py | 10 +++++--- test.py | 56 +++++++++++++++++++------------------------ 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/src/bagit/__init__.py b/src/bagit/__init__.py index b38352e..c17b1f2 100755 --- a/src/bagit/__init__.py +++ b/src/bagit/__init__.py @@ -236,7 +236,11 @@ def make_bag( 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...")) + LOGGER.warning( + _( + "PermissionError [WinError 5] when renaming temp folder. Retrying in 10 seconds..." + ) + ) time.sleep(10) else: raise @@ -1124,12 +1128,12 @@ def _calc_hashes(args): full_path = os.path.join(base_path, rel_path) # Create a clone of the default empty hash objects: - f_hashers = dict((alg, hashlib.new(alg)) for alg in hashes if alg in algorithms) + f_hashers = {alg: hashlib.new(alg) for alg in hashes if alg in algorithms} try: f_hashes = _calculate_file_hashes(full_path, f_hashers) except BagValidationError as e: - f_hashes = dict((alg, str(e)) for alg in f_hashers.keys()) + f_hashes = {alg: str(e) for alg in f_hashers} return rel_path, f_hashes, hashes diff --git a/test.py b/test.py index c18784d..f79bca0 100644 --- a/test.py +++ b/test.py @@ -1157,10 +1157,9 @@ def test_invalid_fast_validate(self): os.remove(j(self.tmpdir, "data", "loc", "2478433644_2839c5e8b8_o_d.jpg")) testargs = ["bagit.py", "--validate", "--completeness-only", self.tmpdir] - with self.assertLogs() as captured: - with self.assertRaises(SystemExit) as cm: - with mock.patch.object(sys, "argv", testargs): - bagit.main() + with self.assertLogs() as captured, self.assertRaises(SystemExit) as cm: + with mock.patch.object(sys, "argv", testargs): + bagit.main() self.assertEqual(cm.exception.code, 1) self.assertIn( @@ -1172,10 +1171,9 @@ def test_valid_fast_validate(self): bagit.make_bag(self.tmpdir) testargs = ["bagit.py", "--validate", "--fast", self.tmpdir] - with self.assertLogs() as captured: - with self.assertRaises(SystemExit) as cm: - with mock.patch.object(sys, "argv", testargs): - bagit.main() + with self.assertLogs() as captured, self.assertRaises(SystemExit) as cm: + with mock.patch.object(sys, "argv", testargs): + bagit.main() self.assertEqual(cm.exception.code, 0) self.assertEqual( @@ -1206,10 +1204,9 @@ def test_invalid_completeness_validate(self): testargs = ["bagit.py", "--validate", "--completeness-only", self.tmpdir] - with self.assertLogs() as captured: - with self.assertRaises(SystemExit) as cm: - with mock.patch.object(sys, "argv", testargs): - bagit.main() + with self.assertLogs() as captured, self.assertRaises(SystemExit) as cm: + with mock.patch.object(sys, "argv", testargs): + bagit.main() self.assertEqual(cm.exception.code, 1) self.assertIn( @@ -1221,10 +1218,9 @@ def test_valid_completeness_validate(self): bagit.make_bag(self.tmpdir) testargs = ["bagit.py", "--validate", "--completeness-only", self.tmpdir] - with self.assertLogs() as captured: - with self.assertRaises(SystemExit) as cm: - with mock.patch.object(sys, "argv", testargs): - bagit.main() + with self.assertLogs() as captured, self.assertRaises(SystemExit) as cm: + with mock.patch.object(sys, "argv", testargs): + bagit.main() self.assertEqual(cm.exception.code, 0) self.assertEqual( @@ -1242,10 +1238,9 @@ def test_invalid_full_validate(self): testargs = ["bagit.py", "--validate", self.tmpdir] - with self.assertLogs() as captured: - with self.assertRaises(SystemExit) as cm: - with mock.patch.object(sys, "argv", testargs): - bagit.main() + with self.assertLogs() as captured, self.assertRaises(SystemExit) as cm: + with mock.patch.object(sys, "argv", testargs): + bagit.main() self.assertEqual(cm.exception.code, 1) self.assertIn("Bag validation failed", captured.records[-1].getMessage()) @@ -1254,10 +1249,9 @@ def test_valid_full_validate(self): bagit.make_bag(self.tmpdir) testargs = ["bagit.py", "--validate", self.tmpdir] - with self.assertLogs() as captured: - with self.assertRaises(SystemExit) as cm: - with mock.patch.object(sys, "argv", testargs): - bagit.main() + with self.assertLogs() as captured, self.assertRaises(SystemExit) as cm: + with mock.patch.object(sys, "argv", testargs): + bagit.main() self.assertEqual(cm.exception.code, 0) self.assertEqual("%s is valid" % self.tmpdir, captured.records[-1].getMessage()) @@ -1267,10 +1261,9 @@ def test_failed_create_bag(self): testargs = ["bagit.py", self.tmpdir] - with self.assertLogs() as captured: - with self.assertRaises(SystemExit) as cm: - with mock.patch.object(sys, "argv", testargs): - bagit.main() + with self.assertLogs() as captured, self.assertRaises(SystemExit) as cm: + with mock.patch.object(sys, "argv", testargs): + bagit.main() self.assertEqual(cm.exception.code, 1) self.assertIn( @@ -1281,10 +1274,9 @@ def test_failed_create_bag(self): def test_create_bag(self): testargs = ["bagit.py", self.tmpdir] - with self.assertLogs() as captured: - with self.assertRaises(SystemExit) as cm: - with mock.patch.object(sys, "argv", testargs): - bagit.main() + with self.assertLogs() as captured, self.assertRaises(SystemExit) as cm: + with mock.patch.object(sys, "argv", testargs): + bagit.main() for rec in captured.records: print(rec.getMessage())