Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package patool for openSUSE:Factory checked in at 2026-09-12 21:21:57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/patool (Old) and /work/SRC/openSUSE:Factory/.patool.new.1265 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "patool" Sat Sep 12 21:21:57 2026 rev:4 rq:1377553 version:4.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/patool/patool.changes 2026-06-17 16:20:56.513646249 +0200 +++ /work/SRC/openSUSE:Factory/.patool.new.1265/patool.changes 2026-09-12 21:25:51.804809602 +0200 @@ -1,0 +2,18 @@ +Sat Sep 12 14:12:45 UTC 2026 - Martin Pluskal <[email protected]> + +- update to 4.1.0: + 4.1.0 (released 04.09.2026) + * Add more invalid characters for Windows filenames. + * Require Python >= 3.12 for native tarfile extraction filters. + 4.0.7 (released 03.09.2026) + * Fix quoting of shell arguments on Windows systems. + 4.0.6 (released 03.09.2026) + * Prefer zpaqfranz for ZPAQ (.zpaq) archives. + * Document dependencies, added SECURITY.md. + * Fix quoting of shell arguments on Windows systems. +- require Python >= 3.12 to match upstream. +- run upstream testsuite in %check (one upstream-broken password + test deselected). +- spec cleanup: drop Group tag, split %license, tighten %files. + +------------------------------------------------------------------- Old: ---- patool-4.0.5.tar.gz New: ---- patool-4.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ patool.spec ++++++ --- /var/tmp/diff_new_pack.4qVuQx/_old 2026-09-12 21:25:52.367833233 +0200 +++ /var/tmp/diff_new_pack.4qVuQx/_new 2026-09-12 21:25:52.368833275 +0200 @@ -19,23 +19,24 @@ Name: patool -Version: 4.0.5 +Version: 4.1.0 Release: 0 Summary: Portable Command Line Archive File Manager License: GPL-3.0-or-later -Group: Productivity/Archiving/Compression URL: https://github.com/wummel/patool Source: https://github.com/wummel/patool/releases/download/%{version}/patool-%{version}.tar.gz#/%{name}-%{version}.tar.gz BuildRequires: fdupes BuildRequires: python-rpm-macros -BuildRequires: python3-base >= 3.11 +BuildRequires: python3-base >= 3.12 BuildRequires: python3-devel +BuildRequires: python3-pytest BuildRequires: python3-setuptools -BuildArch: noarch - +BuildRequires: unzip +BuildRequires: zip # upstream no longer provides a bash completion file Obsoletes: %{name}-bash-completion Provides: %{name}-bash-completion +BuildArch: noarch %description patool is a portable command line archive file manager. Various archive types @@ -62,8 +63,17 @@ %python3_install %fdupes %{buildroot} +%check +# single-flavor spec (python3_* macros): run the suite with the default +# interpreter; the bare pytest macro would expand over all pythons flavors. +# TestPyzipPasswordfile is broken upstream (create_zip takes no password, +# fails with and without zip installed), so deselect it. +python3 -m pytest tests/ --deselect tests/archives/test_pyzipfile.py::TestPyzipPasswordfile::test_py_zipfile + %files -%doc COPYING doc/*.txt +%license COPYING +%doc doc/*.txt %{_bindir}/patool -%{python3_sitelib}/* +%{python3_sitelib}/patoolib +%{python3_sitelib}/patool-%{version}*-info ++++++ patool-4.0.5.tar.gz -> patool-4.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/Makefile new/patool-4.1.0/Makefile --- old/patool-4.0.5/Makefile 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/Makefile 2026-09-04 12:45:16.000000000 +0200 @@ -20,8 +20,6 @@ AUTHOR:=$(shell grep "MyName:" patoolib/configuration.py | cut -d '"' -f2) APPNAME:=$(shell grep "AppName:" patoolib/configuration.py | cut -d '"' -f2) -# exclude packages that are newer than this -EXCLUDE_NEWER:="7 days" # Pytest options: # -s: do not capture stdout/stderr (some tests fail otherwise) # --full-trace: print full stacktrace on keyboard interrupts @@ -34,6 +32,8 @@ TESTOPTS= # python files and directories PY_FILES_DIRS:=setup.py patoolib tests doc/web/source +# python files and directories +SH_FILES_DIRS:=scripts/*.sh # Release configuration ARCHIVE_SOURCE:=$(APPNAME)-$(VERSION).tar.gz @@ -115,30 +115,28 @@ $(MAKE) dist release-gh release-pypi release-homepage github-issues .PHONY: releasecheck -releasecheck: update-webmeta checkgit checkchangelog lint test typecheck checkgitreleasetag ## check that repo is ready for release +releasecheck: update-webmeta checkchangelog lint test typecheck checkgit ## check that repo is ready for release .PHONY: checkgit checkgit: ## check that git changes are all committed on the main branch -# check that branch is the main branch + # check that branch is the main branch @if [ "$(shell git rev-parse --abbrev-ref HEAD)" != "$(GIT_MAIN_BRANCH)" ]; then \ echo "ERROR: current git branch is not '$(GIT_MAIN_BRANCH)'"; \ git rev-parse --abbrev-ref HEAD; \ false; \ fi -# check for uncommitted versions + # check for uncommitted versions @if [ -n "$(shell git status --porcelain --untracked-files=all)" ]; then \ echo "ERROR: uncommitted git changes"; \ git status --porcelain --untracked-files=all; \ false; \ fi - -.PHONY: checkgitreleasetag -checkgitreleasetag: ## check release tag for git exists + # check release tag for git exists @if [ -z "$(shell git tag -l -- $(GITRELEASETAG))" ]; then \ echo "ERROR: git tag \"$(GITRELEASETAG)\" does not exist, execute 'git tag -a $(GITRELEASETAG) -m \"$(GITRELEASETAG)\"'"; \ false; \ fi -# check that release tags is pushed to remote + # check that release tags is pushed to remote @if ! git ls-remote --exit-code --tags origin $(GITRELEASETAG); then \ echo "ERROR: git tag \"$(GITRELEASETAG)\" does not exist on remote repo, execute 'git push --tags'"; \ fi @@ -182,9 +180,20 @@ ############ Linting and syntax checks ############ .PHONY: lint -lint: ## lint python code +lint: lint-py lint-md lint-sh ## run various linters + +.PHONY: lint-py +lint-py: ## lint python files ruff check $(PY_FILES_DIRS) +.PHONY: lint-md +lint-md: ## lint markdown files + rumdl check *.md doc/*.md + +.PHONY: lint-sh +lint-sh: ## lint shell scripts + shellcheck -x $(SH_FILES_DIRS) + .PHONY: audit audit: ## run audit checks zizmor --pedantic .github/workflows/python-package.yml @@ -194,29 +203,38 @@ reformat: ## format the python code ruff check --fix $(PY_FILES_DIRS) ruff format $(PY_FILES_DIRS) + shfmt -w scripts/ + tombi format pyproject.toml .PHONY: checkoutdated checkoutdated-py checkoutdated-gh -checkoutdated: checkoutdated-py checkoutdated-gh +checkoutdated: checkoutdated-gh checkoutdated-py checkoutdated-py: ## Check for outdated package requirements puc \ - --exclude-newer $(EXCLUDE_NEWER) \ - --exclude-newer-package "python-update-checker=1 minute" \ + --constraint constraints.txt \ check pyproject.toml uv.lock -checkoutdated-gh: ## check for outdated github projects +checkoutdated-gh: checkratelimit-gh ## check for outdated github projects # github-check-outdated is a local tool which compares a given version with the latest available github release version # see https://gist.github.com/wummel/ef14989766009effa4e262b01096fc8c for an example implementation @echo "Check for outdated Github tools" github-check-outdated astral-sh uv "$(shell uv --version | cut -f2 -d" ")" github-check-outdated python cpython v$(shell python --version | cut -f2 -d" ") '^v3\.14\.[0-9]+$$' +.PHONY: checkratelimit-gh +checkratelimit-gh: ## check for rate limiting + @( + set +e + github-check-outdated --check astral-sh uv 0 >/dev/null + if [ "$$?" == 2 ]; then exit 1; fi + ) + .PHONY: upgradeoutdated upgradeoutdated: upgradeoutdated-gh upgradeoutdated-py .PHONY: upgradeoutdated-gh -upgradeoutdated-gh: +upgradeoutdated-gh: checkratelimit-gh ## upgrade github project versions sed -i -e 's/uv_version_dev = ".*"/uv_version_dev = "$(shell github-check-outdated astral-sh uv 0 | cut -f4 -d" ")"/' pyproject.toml sed -i -e 's/ version: ".*"/ version: "$(shell github-check-outdated astral-sh uv 0 | cut -f4 -d" ")"/' .github/workflows/python-package.yml sed -i -e 's/python_version_dev = ".*"/python_version_dev = "$(shell github-check-outdated python cpython 0 '^v3\.14\.[0-9]+$$' | cut -f4 -d" " | cut -b2-)"/' pyproject.toml @@ -224,17 +242,13 @@ .PHONY: upgradeoutdated-py upgradeoutdated-py: ## upgrade dependencies in pyproject.toml and uv.lock - puc \ - --exclude-newer $(EXCLUDE_NEWER) \ - --exclude-newer-package "python-update-checker=1 minute" \ - update pyproject.toml - uv lock \ - --exclude-newer $(EXCLUDE_NEWER) \ - --exclude-newer-package "python-update-checker=1 minute" \ - --upgrade + # upgrade pyproject.toml dependencies + puc update pyproject.toml + # upgrade depencencies in uv lock file + uv lock --upgrade + # install upgraded package versions in virtual environment $(MAKE) init - ############ Testing ############ .PHONY: test diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/PKG-INFO new/patool-4.1.0/PKG-INFO --- old/patool-4.0.5/PKG-INFO 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/PKG-INFO 2026-09-04 12:45:16.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: patool -Version: 4.0.5 +Version: 4.1.0 Summary: portable archive file manager Home-page: https://github.com/wummel/patool Author: Bastian Kleineidam @@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Operating System :: OS Independent -Requires-Python: >=3.11 +Requires-Python: >=3.12 Description-Content-Type: text/markdown License-File: COPYING Provides-Extra: argcompletion @@ -35,8 +35,7 @@ Dynamic: requires-python Dynamic: summary -Patool -======= +# Patool Patool is a portable archive file manager for the command line. @@ -56,16 +55,16 @@ RZIP (.rz), SHN (.shn), TAR (.tar, .cbt), UDF (.udf), XZ (.xz), ZIP (.zip, .jar, .cbz), ZOO (.zoo) and ZSTANDARD (.zst) archive formats. -It relies on helper applications to handle those archive formats -(for example `xz` for XZ (.xz) archives). - -The archive formats TAR, ZIP, BZIP2, LZMA and GZIP are supported natively -and do not require helper applications to be installed. +The archive formats TAR, ZIP, BZIP2, LZMA and GZIP are supported natively. When using Python >= 3.14 the ZSTANDARD archive format is also supported natively. -Examples ---------- -``` +Other formats require archive programs as helper applications to handle those +formats (for example `xz` for XZ (.xz) archives). +The installation instructions (see below) list suggested archive programs. + +## Examples + +```bash # Extract several archives with different formats patool extract archive.zip otherarchive.rar @@ -94,13 +93,17 @@ patool repack linux-2.6.33.tar.gz linux-2.6.33.tar.bz2 ``` -Website and installation -------------------------- -See https://wummel.github.io/patool/ for more info and downloads. -See [doc/install.md](https://github.com/wummel/patool/blob/main/doc/install.md) for detailed install instructions. +## Website + +See [https://wummel.github.io/patool/] for more info and downloads. + +## Installation and archive programs + +See [doc/install.md](https://github.com/wummel/patool/blob/main/doc/install.md) +for detailed install instructions and a list of suggested archive programs. + +## API -API ----- You can use patool functions from other Python applications. Log output uses a Python logging handler named "patool" and is [configured](https://github.com/wummel/patool/blob/main/patoolib/log.py) @@ -109,7 +112,7 @@ Note that extra options or customization for specific archive programs are not supported. -``` +```python import patoolib patoolib.extract_archive("archive.zip", outdir="/tmp") patoolib.test_archive("dist.tar.gz", verbosity=1) @@ -121,16 +124,15 @@ patoolib.is_archive("package.deb") ``` -See https://wummel.github.io/patool/ for detailed API documentation. +See [https://wummel.github.io/patool/] for detailed API documentation. + +## Test suite -Test suite ------------ -Patool has [extensive unit tests](https://github.com/wummel/patool/tree/master/tests) to ensure the code quality. -The tests are run on each code push by a github runner. +Patool has [extensive unit tests](https://github.com/wummel/patool/tree/master/tests) to ensure code quality. +The tests are run on each code push on different platforms and python versions. +## Bash completion -Bash completion ----------------- Install the argcomplete python package eg. on Debian/Ubuntu with `apt-get install python3-argcomplete`, then run @@ -138,7 +140,6 @@ After that typing `patool`, a `<SPACE>` and then `<TAB>` lists available options and commands. +## Development -Development ------------- See [doc/development.md](https://github.com/wummel/patool/blob/main/doc/development.md). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/README.md new/patool-4.1.0/README.md --- old/patool-4.0.5/README.md 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/README.md 2026-09-04 12:45:16.000000000 +0200 @@ -1,5 +1,4 @@ -Patool -======= +# Patool Patool is a portable archive file manager for the command line. @@ -19,16 +18,16 @@ RZIP (.rz), SHN (.shn), TAR (.tar, .cbt), UDF (.udf), XZ (.xz), ZIP (.zip, .jar, .cbz), ZOO (.zoo) and ZSTANDARD (.zst) archive formats. -It relies on helper applications to handle those archive formats -(for example `xz` for XZ (.xz) archives). - -The archive formats TAR, ZIP, BZIP2, LZMA and GZIP are supported natively -and do not require helper applications to be installed. +The archive formats TAR, ZIP, BZIP2, LZMA and GZIP are supported natively. When using Python >= 3.14 the ZSTANDARD archive format is also supported natively. -Examples ---------- -``` +Other formats require archive programs as helper applications to handle those +formats (for example `xz` for XZ (.xz) archives). +The installation instructions (see below) list suggested archive programs. + +## Examples + +```bash # Extract several archives with different formats patool extract archive.zip otherarchive.rar @@ -57,13 +56,17 @@ patool repack linux-2.6.33.tar.gz linux-2.6.33.tar.bz2 ``` -Website and installation -------------------------- -See https://wummel.github.io/patool/ for more info and downloads. -See [doc/install.md](https://github.com/wummel/patool/blob/main/doc/install.md) for detailed install instructions. +## Website + +See [https://wummel.github.io/patool/] for more info and downloads. + +## Installation and archive programs + +See [doc/install.md](https://github.com/wummel/patool/blob/main/doc/install.md) +for detailed install instructions and a list of suggested archive programs. + +## API -API ----- You can use patool functions from other Python applications. Log output uses a Python logging handler named "patool" and is [configured](https://github.com/wummel/patool/blob/main/patoolib/log.py) @@ -72,7 +75,7 @@ Note that extra options or customization for specific archive programs are not supported. -``` +```python import patoolib patoolib.extract_archive("archive.zip", outdir="/tmp") patoolib.test_archive("dist.tar.gz", verbosity=1) @@ -84,16 +87,15 @@ patoolib.is_archive("package.deb") ``` -See https://wummel.github.io/patool/ for detailed API documentation. +See [https://wummel.github.io/patool/] for detailed API documentation. + +## Test suite -Test suite ------------ -Patool has [extensive unit tests](https://github.com/wummel/patool/tree/master/tests) to ensure the code quality. -The tests are run on each code push by a github runner. +Patool has [extensive unit tests](https://github.com/wummel/patool/tree/master/tests) to ensure code quality. +The tests are run on each code push on different platforms and python versions. +## Bash completion -Bash completion ----------------- Install the argcomplete python package eg. on Debian/Ubuntu with `apt-get install python3-argcomplete`, then run @@ -101,7 +103,6 @@ After that typing `patool`, a `<SPACE>` and then `<TAB>` lists available options and commands. +## Development -Development ------------- See [doc/development.md](https://github.com/wummel/patool/blob/main/doc/development.md). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/doc/api.md new/patool-4.1.0/doc/api.md --- old/patool-4.0.5/doc/api.md 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/doc/api.md 2026-09-04 12:45:16.000000000 +0200 @@ -1,22 +1,23 @@ -Development with the patoolib library -====================================== +# Development with the patoolib library The patool functionality can also be used in other Python programs. To do this, install the patool program, import the library and use one or more of the convenience functions. +```python import patoolib try: patoolib.extract("myarchive.zip", verbose=True) print("Success.") except patoolib.util.PatoolError as msg: print("Error:", msg) +``` General rules for all convenience functions: * All convenience functions raise PatoolError on errors. -* If your application is not interactive, you should set ``interactive=False``. +* If your application is not interactive, you should set `interactive=False`. Otherwise the function call could block while it waits for user input. * Error messages are printed on stderr, informative messages @@ -35,23 +36,25 @@ The convenience functions are: -* ``def extract_archive(archive, verbosity=0, outdir=None, program=None, interactive=True, password=None)`` +* `def extract_archive(archive, verbosity=0, outdir=None,` + `program=None, interactive=True, password=None)` Extracts the given archive filename to the current working directory or if specified to the given directory name in outdir. Checks that the archive exists and is readable before extracting it. -* ``def list_archive(archive, verbosity=1, program=None, interactive=True, password=None)`` +* `def list_archive(archive, verbosity=1, program=None, interactive=True, password=None)` Lists the contents of the given archive filename on stdout. Checks that the archive exists and is readable before listing it. -* ``def test_archive(archive, verbosity=0, program=None, interactive=True, password=None)`` +* `def test_archive(archive, verbosity=0, program=None, interactive=True, password=None)` Tests the given archive filename. Checks that the archive exists and is readable before testing it. -* ``def create_archive(archive, filenames, verbosity=0, program=None, interactive=True, password=None)`` +* `def create_archive(archive, filenames, verbosity=0, program=None,` + `interactive=True, password=None)` Creates a new archive. The type of archive is determined by the archive filename extension. @@ -59,20 +62,20 @@ Also checks that the filename list is not empty and that all files exist and are readable. -* ``diff_archives(archive1, archive2, verbosity=0, interactive=True)`` +* `diff_archives(archive1, archive2, verbosity=0, interactive=True)` This function lists differences in the content of the two archives. Both archives are extracted and the contents are compared recursively with the diff(1) program. Checks that both archives exist and are readable. -* ``def search_archive(pattern, archive, verbosity=0, interactive=True, password=None)`` +* `def search_archive(pattern, archive, verbosity=0, interactive=True, password=None)` This function searches the given pattern in the archive file contents with grep(1). Checks that archive exists and is readable. -* ``repack_archive (archive, archive_new, verbosity=0, interactive=True, password=None)`` +* `repack_archive (archive, archive_new, verbosity=0, interactive=True, password=None)` This function extracts the contents of the archive and packs them into archive_new. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/doc/changelog.txt new/patool-4.1.0/doc/changelog.txt --- old/patool-4.0.5/doc/changelog.txt 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/doc/changelog.txt 2026-09-04 12:45:16.000000000 +0200 @@ -1,3 +1,23 @@ +4.1.0 (released 04.09.2026) + * [Bugfix] Add more invalid characters for Windows filenames. + * [Security] Require Python >= 3.12 in order to use Pythons + native extraction filters for tarfiles. + +4.0.7 (released 03.09.2026) + * [Bugfix] Fix quoting of shell arguments on Windows systems. + +4.0.6 (released 03.09.2026) + * [Feature] Added zpaqfranz as preferred archive program to handle + ZPAQ (.zpaq) archives. + Closes: GH bug #195 + * [Docs] Document dependencies in doc/install.md. + Closes: GH bug #192 + * [Security] Added SECURITY.md documenting the security implications + of running patool and how to report vulnerabilities. + Closes: GH bug #191 + Closes: GH bug #194 + * [Security] Fix quoting of shell arguments on Windows systems + 4.0.5 (released 18.05.2026) * [Security] Fix generation of files and directories outside the given extraction directory when extracting TAR (.tar) archives diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/doc/development.md new/patool-4.1.0/doc/development.md --- old/patool-4.0.5/doc/development.md 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/doc/development.md 2026-09-04 12:45:16.000000000 +0200 @@ -1,5 +1,4 @@ -Development -============ +# Development Development for patool is done with the following environment: @@ -7,7 +6,8 @@ 2. A user with sudo permissions to install required system packages. -MacOS or Windows should also be ok to develop patool, but the scripts below have been tested only on Debian Linux. +MacOS or Windows should also be ok to develop patool, but the scripts +below have been tested only on Debian Linux. To start developing, run the following commands: @@ -19,76 +19,82 @@ `scripts/install_dev.sh` - This script installs required system packages and a lot of archive programs for testing - with `sudo apt-get install <package>`. - It also downloads the [uv tool](https://docs.astral.sh/uv/) to `./bin/uv` and configures the PATH variable in `.envrc` + This script installs required system packages and a lot of archive + programs for testing with `sudo apt-get install <package>`. + It also downloads the [uv tool](https://docs.astral.sh/uv/) to + `./bin/uv` and configures the PATH variable in `.envrc` for `direnv` to find it. 3. Create the Python virtual environment `make init` - This command uses `uv` to create a virtual Python environment in the directory `.venv/` + This command uses `uv` to create a virtual Python environment + in the directory `.venv/` and installs required Python modules for development. Rerun this when changing dependencies in pyproject.toml -3. Run the tests +4. Run the tests `make test` - This runs all tests in parallel. Tests detect necessary features and programs to run, + This runs all tests in parallel. Tests detect necessary features + and programs to run, so they do not fail but are skipped when archive programs are missing. -4. Now change the code, eg. to implement a new features. +5. Now change the code, eg. to implement a new features. After changing the code you should run the following commands: `make lint` - This catches simple coding and syntax errors. Run this first after changing the code. + This catches simple coding and syntax errors. Run this first after + changing the code. `make test` - Run the tests again with the changed code. It is a good idea to add new tests when - adding new features. + Run the tests again with the changed code. It is a good idea to + add new tests when adding new features. `make reformat` Formats the code according to the configured rules in .ruff.toml. Run this before git commit. -5. Other make targets are documented in the Makefile itself. +6. Other make targets are documented in the Makefile itself. Use this command to display all make targets with a short explanation: `make help` - - -After committing and pushing new code to a github repository, the Github workflow defined +After committing and pushing new code to a github repository, +the Github workflow defined in `.github/workflows/python-package.yml` runs. The python-package.yml workflow tests different architectures and Python versions. -Tests are run on Windows, MacOS and Linux, tested Python versions are Python 3.12, 3.13 and 3.14. +Tests are run on Windows, MacOS and Linux, tested Python versions are +Python 3.12, 3.13 and 3.14. -A useful tool to test your github workflow on a local system is https://github.com/nektos/act, -though it only supports Linux systems. +A useful tool to test your github workflow on a local system is +[https://github.com/nektos/act], though it only supports Linux systems. +## Dependencies -Dependencies --------------- Dependencies are specified in `pyproject.toml`. -All dependencies are pinned to a specific version by using the `==x.y.z` version specifier. +All dependencies are pinned to a specific version by using +the `==x.y.z` version specifier. 1. Optional dependencies are specified under `[project.optional-dependencies]`. -2. Development dependencies are specified under `[dependency-groups]` in the group `dev`. +2. Development dependencies are specified under `[dependency-groups]` + in the group `dev`. 3. Python and uv versions are specified unter `[tool.patool]`. -The command `make checkoutdated` checks for new version of python, uv and optional and development dependencies. +The command `make checkoutdated` checks for new version of python, +uv and optional and development dependencies. -The command `make upgradedoutdated` updates all dependencies in pyproject.toml and installs them. +The command `make upgradedoutdated` updates all dependencies in +`pyproject.toml` and installs them. +## Webpage -Webpage --------- The webpage of patool is written with [Sphinx](https://www.sphinx-doc.org/en/master/). -The index page is in `doc/web/source/index.md`. +The index page is in `doc/web/source/index.txt`. When releasing a new version, the webpage is automatically updated. See the `release-homepage` target in `Makefile`. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/doc/install.md new/patool-4.1.0/doc/install.md --- old/patool-4.0.5/doc/install.md 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/doc/install.md 2026-09-04 12:45:16.000000000 +0200 @@ -1,36 +1,30 @@ -Installation -============ +# Installation First, install the required software. -1. Python >= 3.12 from https://www.python.org/ - +1. Python >= 3.12 from [https://www.python.org/] Now install the application. 1. Installation with pip - If you have pip installed: ``pip install patool`` - -2. Installation from source - - a) Installation as root + If you have pip installed, run `pip install patool`. + To install pip, run `python -m ensurepip`. - Run ``sudo python setup.py install`` to install patool. +2. Installation on Linux - b) Installation as a normal user + Several Linux distributions have packaged patool, for example + [Debian](https://packages.debian.org/patool), + [Arch](https://aur.archlinux.org/packages/patool) or + [Fedora](https://packages.fedoraproject.org/pkgs/patool/patool/). - Run ``python setup.py install --home $HOME``. Note that you have - to adjust your PATH and PYTHONPATH environment variables, e.g. by - adding the commands ``export PYTHONPATH=$HOME/lib/python`` and - ``export PATH=$PATH:$HOME/bin`` to your shell configuration - file. + Look at the installation instructions for the patool package of + your distribution. + But you can also use the pip installation above. - For more information look at the - [Modifying Python's search path](http://docs.python.org/inst/search-path.html#SECTION000410000000000000000) - documentation. +3. Installation on Windows -3. Optional: install cygwin file, grep and diff packages on Windows + See installation with pip above. On Windows systems, the archive type is only detectable through file extensions. To be able to detect archives with missing or non-standard file extensions, @@ -43,9 +37,29 @@ b) Run `setup-x86_64.exe -q -p file,grep,diff` - c) Add the `c:\cygwin64\bin` directory to your PATH + c) Add `c:\cygwin64\bin` directory to your PATH + +4. Installation in MacOS + + See installation with pip above. + There is not yet a homebrew formula for patool that I know of. + +## Suggested archive programs + +Installing the following programs lets patool support a wide variety +of archive formats. + +[7-zip](https://www.7-zip.org/) supports a lot of formats, installing +it is often sufficient for most users. + +[Peazip](https://peazip.github.io/) also supports a lot of +archive formats. +To handle RAR files install either peazip or the +[7zip-rar](https://packages.debian.org/trixie/7zip-rar) package. -After installation ------------------- -Patool is now installed. Have fun! +For more unusual formats (eg. bzip3) see the ArchivePrograms configuration +at +[patoolib/__init__.py](https://github.com/wummel/patool/blob/3255e723f2bb88f8d4ee43d384b4a05131664991/patoolib/__init__.py#L164). +For each archive format the programs that support extracting, +listing or creating archive files are listed. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/patool.egg-info/PKG-INFO new/patool-4.1.0/patool.egg-info/PKG-INFO --- old/patool-4.0.5/patool.egg-info/PKG-INFO 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/patool.egg-info/PKG-INFO 2026-09-04 12:45:16.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: patool -Version: 4.0.5 +Version: 4.1.0 Summary: portable archive file manager Home-page: https://github.com/wummel/patool Author: Bastian Kleineidam @@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Operating System :: OS Independent -Requires-Python: >=3.11 +Requires-Python: >=3.12 Description-Content-Type: text/markdown License-File: COPYING Provides-Extra: argcompletion @@ -35,8 +35,7 @@ Dynamic: requires-python Dynamic: summary -Patool -======= +# Patool Patool is a portable archive file manager for the command line. @@ -56,16 +55,16 @@ RZIP (.rz), SHN (.shn), TAR (.tar, .cbt), UDF (.udf), XZ (.xz), ZIP (.zip, .jar, .cbz), ZOO (.zoo) and ZSTANDARD (.zst) archive formats. -It relies on helper applications to handle those archive formats -(for example `xz` for XZ (.xz) archives). - -The archive formats TAR, ZIP, BZIP2, LZMA and GZIP are supported natively -and do not require helper applications to be installed. +The archive formats TAR, ZIP, BZIP2, LZMA and GZIP are supported natively. When using Python >= 3.14 the ZSTANDARD archive format is also supported natively. -Examples ---------- -``` +Other formats require archive programs as helper applications to handle those +formats (for example `xz` for XZ (.xz) archives). +The installation instructions (see below) list suggested archive programs. + +## Examples + +```bash # Extract several archives with different formats patool extract archive.zip otherarchive.rar @@ -94,13 +93,17 @@ patool repack linux-2.6.33.tar.gz linux-2.6.33.tar.bz2 ``` -Website and installation -------------------------- -See https://wummel.github.io/patool/ for more info and downloads. -See [doc/install.md](https://github.com/wummel/patool/blob/main/doc/install.md) for detailed install instructions. +## Website + +See [https://wummel.github.io/patool/] for more info and downloads. + +## Installation and archive programs + +See [doc/install.md](https://github.com/wummel/patool/blob/main/doc/install.md) +for detailed install instructions and a list of suggested archive programs. + +## API -API ----- You can use patool functions from other Python applications. Log output uses a Python logging handler named "patool" and is [configured](https://github.com/wummel/patool/blob/main/patoolib/log.py) @@ -109,7 +112,7 @@ Note that extra options or customization for specific archive programs are not supported. -``` +```python import patoolib patoolib.extract_archive("archive.zip", outdir="/tmp") patoolib.test_archive("dist.tar.gz", verbosity=1) @@ -121,16 +124,15 @@ patoolib.is_archive("package.deb") ``` -See https://wummel.github.io/patool/ for detailed API documentation. +See [https://wummel.github.io/patool/] for detailed API documentation. + +## Test suite -Test suite ------------ -Patool has [extensive unit tests](https://github.com/wummel/patool/tree/master/tests) to ensure the code quality. -The tests are run on each code push by a github runner. +Patool has [extensive unit tests](https://github.com/wummel/patool/tree/master/tests) to ensure code quality. +The tests are run on each code push on different platforms and python versions. +## Bash completion -Bash completion ----------------- Install the argcomplete python package eg. on Debian/Ubuntu with `apt-get install python3-argcomplete`, then run @@ -138,7 +140,6 @@ After that typing `patool`, a `<SPACE>` and then `<TAB>` lists available options and commands. +## Development -Development ------------- See [doc/development.md](https://github.com/wummel/patool/blob/main/doc/development.md). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/patool.egg-info/SOURCES.txt new/patool-4.1.0/patool.egg-info/SOURCES.txt --- old/patool-4.0.5/patool.egg-info/SOURCES.txt 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/patool.egg-info/SOURCES.txt 2026-09-04 12:45:16.000000000 +0200 @@ -97,6 +97,7 @@ patoolib/programs/zoo.py patoolib/programs/zopfli.py patoolib/programs/zpaq.py +patoolib/programs/zpaqfranz.py patoolib/programs/zstd.py tests/__init__.py tests/test_cli.py @@ -111,6 +112,7 @@ tests/test_mime.py tests/test_repack.py tests/test_search.py +tests/test_shell_quoting.py tests/test_test.py tests/test_util.py tests/test_version.py @@ -184,6 +186,7 @@ tests/archives/test_zoo.py tests/archives/test_zopfli.py tests/archives/test_zpaq.py +tests/archives/test_zpaqfranz.py tests/data/p .7z tests/data/p.7z.foo tests/data/p.arc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/patoolib/__init__.py new/patool-4.1.0/patoolib/__init__.py --- old/patool-4.0.5/patoolib/__init__.py 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/patoolib/__init__.py 2026-09-04 12:45:16.000000000 +0200 @@ -19,8 +19,8 @@ import sys # check for compatible Python version before importing other packages -if not hasattr(sys, "version_info") or sys.version_info < (3, 11, 0, "final", 0): - raise SystemExit("This program requires Python 3.11 or later.") +if not hasattr(sys, "version_info") or sys.version_info < (3, 12, 0, "final", 0): + raise SystemExit("This program requires Python 3.12 or later.") import functools import inspect import os @@ -388,7 +388,10 @@ 'extract': ('unar',), }, 'zpaq': { - None: ('zpaq',), + None: ( + 'zpaqfranz', + 'zpaq', + ), }, "zstd": { None: ("zstd",), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/patoolib/cli.py new/patool-4.1.0/patoolib/cli.py --- old/patool-4.0.5/patoolib/cli.py 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/patoolib/cli.py 2026-09-04 12:45:16.000000000 +0200 @@ -325,8 +325,8 @@ except KeyboardInterrupt: log_error("aborted") res = 1 - except Exception: - log_internal_error() + except Exception as exc: + log_internal_error(exc) res = 2 return res diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/patoolib/configuration.py new/patool-4.1.0/patoolib/configuration.py --- old/patool-4.0.5/patoolib/configuration.py 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/patoolib/configuration.py 2026-09-04 12:45:16.000000000 +0200 @@ -2,7 +2,7 @@ """Define basic configuration data like version or application name.""" AppName: str = "patool" -Version: str = "4.0.5" +Version: str = "4.1.0" MyName: str = "Bastian Kleineidam" MyEmail: str = "[email protected]" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/patoolib/fileutil.py new/patool-4.1.0/patoolib/fileutil.py --- old/patool-4.0.5/patoolib/fileutil.py 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/patoolib/fileutil.py 2026-09-04 12:45:16.000000000 +0200 @@ -15,7 +15,6 @@ """File and directory utility functions.""" import os -import sys import shutil import stat import tempfile @@ -157,11 +156,6 @@ return getattr(func, '__name__', repr(func)) -def rmtree_log_error(func: Callable, path: str, exc: str) -> None: - """Error log function for shutil.rmtree().""" - log_error(f"Error in {funcname(func)}({path}): {exc[1]}") - - def rmtree_log_exc(func: Callable, path: str, excinfo) -> None: """Error log function for shutil.rmtree().""" log_error(f"Error in {funcname(func)}({path}): {excinfo}") @@ -172,10 +166,7 @@ Errors will be logged. """ make_user_readable(path) - if sys.version_info >= (3, 12, 0, "final", 0): - shutil.rmtree(path, onexc=rmtree_log_exc) - else: - shutil.rmtree(path, onerror=rmtree_log_error) + shutil.rmtree(path, onexc=rmtree_log_exc) def make_file_readable(filename: str) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/patoolib/log.py new/patool-4.1.0/patoolib/log.py --- old/patool-4.0.5/patoolib/log.py 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/patoolib/log.py 2026-09-04 12:45:16.000000000 +0200 @@ -78,14 +78,14 @@ EnvKeys = ("LANGUAGE", "LC_ALL", "LC_CTYPE", "LANG") -def log_internal_error() -> None: +def log_internal_error(exception) -> None: """Print internal error message.""" now = time.localtime() env = os.linesep.join( [f"{key}={os.getenv(key)!r}" for key in EnvKeys if os.getenv(key) is not None] ) if logger is not None: - logger.exception( + logger.error( encode_safe( f"""********** Oops, I did it again. ************* @@ -107,7 +107,8 @@ {env} ******** {configuration.AppName} internal error, over and out ******** """ - ) + ), + exc_info=exception, ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/patoolib/programs/py_tarfile.py new/patool-4.1.0/patoolib/programs/py_tarfile.py --- old/patool-4.0.5/patoolib/programs/py_tarfile.py 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/patoolib/programs/py_tarfile.py 2026-09-04 12:45:16.000000000 +0200 @@ -14,9 +14,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. """Archive commands for the tarfile Python module.""" -from .. import util, fileutil -import os -import sys +from .. import util import tarfile @@ -37,34 +35,12 @@ """Extract a TAR archive with the tarfile Python module.""" try: with tarfile.open(archive) as tfile: - if sys.version_info >= (3, 12, 0, "final", 0): - tfile.extractall(path=outdir, filter='data') - else: - safe_extract(tfile, outdir) + tfile.extractall(path=outdir, filter='data') except Exception as err: raise util.PatoolError(f"error extracting {archive}") from err return -def safe_extract(tfile, path): - """Helper function to ensure that TAR members will be extracted inside - the given path. - If a member will be extracted outside the path an Exception is raised. - """ - safe_members = [] - bad_members = [] - for member in tfile.getmembers(): - member_path = os.path.join(path, member.name) - if fileutil.is_within_directory(path, member_path): - safe_members.append(member) - else: - bad_members.append(member) - tfile.extractall(path, safe_members) - if bad_members: - filelist = ", ".join(member.name for member in bad_members) - raise Exception(f"Unsafe tarfile entries: {filelist}.") - - def create_tar(archive, compression, cmd, verbosity, interactive, filenames): """Create a TAR archive with the tarfile Python module.""" mode = get_tar_mode(compression) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/patoolib/programs/zpaqfranz.py new/patool-4.1.0/patoolib/programs/zpaqfranz.py --- old/patool-4.0.5/patoolib/programs/zpaqfranz.py 1970-01-01 01:00:00.000000000 +0100 +++ new/patool-4.1.0/patoolib/programs/zpaqfranz.py 2026-09-04 12:45:16.000000000 +0200 @@ -0,0 +1,40 @@ +# Copyright (C) 2026 Bastian Kleineidam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +"""Archive commands for the zpaqfranz program.""" + +import os + + +def extract_zpaq(archive, compression, cmd, verbosity, interactive, outdir): + """Extract a ZPAQ archive.""" + cmdlist = [cmd, 'x', os.path.abspath(archive)] + return (cmdlist, {'cwd': outdir}) + + +def list_zpaq(archive, compression, cmd, verbosity, interactive): + """List a ZPAQ archive.""" + return [cmd, 'l', archive] + + +def create_zpaq(archive, compression, cmd, verbosity, interactive, filenames): + """Create a ZPAQ archive.""" + cmdlist = [cmd, 'a', archive] + cmdlist.extend(filenames) + cmdlist.extend(['-method', '4']) + return cmdlist + + +# zpaq has no separate test mode, so use listing instead +test_zpaq = list_zpaq diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/patoolib/util.py new/patool-4.1.0/patoolib/util.py --- old/patool-4.0.5/patoolib/util.py 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/patoolib/util.py 2026-09-04 12:45:16.000000000 +0200 @@ -51,10 +51,8 @@ """Run command without error checking. @return: command return code """ - # Note that shell_quote_nt() result is not suitable for copy-paste - # (especially on Unix systems), but it looks nicer than shell_quote(). if verbosity >= 0: - info = " ".join(map(shell_quote_nt, cmd)) + info = " ".join(map(repr, cmd)) log_info(f"running {info}") if run_under_pythonw(): # prevent opening of additional consoles when running with pythonw.exe @@ -72,7 +70,7 @@ kwargs['stderr'] = subprocess.DEVNULL if kwargs: if verbosity > 0: - info = ", ".join(f"{k}={shell_quote(str(v))}" for k, v in kwargs.items()) + info = ", ".join(f"{k}={v!r}" for k, v in kwargs.items()) log_info(f" with {info}") if kwargs.get("shell"): # for shell calls the command must be a string @@ -102,18 +100,24 @@ def shell_quote_unix(value: str) -> str: - """Quote argument for Unix system.""" + """Quote argument for Unix system, suitable for filename creation.""" value = value.replace("'", r"'\''") return f"'{value}'" def shell_quote_nt(value: str) -> str: - """Quote argument for Windows system. Modeled after distutils - _nt_quote_args() function. - """ - if " " in value: - return f'"{value}"' - return value + """Quote argument for Windows systems, suitable for filename creation.""" + illegal_chars = '"<>|?*\t\n' + for c in illegal_chars: + if c in value: + raise ValueError(f"Cannot quote invalid character {c} in value {value!r}") + if value.endswith("\\"): + raise ValueError(f"Cannot quote invalid ending backslash in value {value!r}") + quoted_value = value + # quote percent with leading percent + quoted_value = quoted_value.replace('%', '%%') + # finally wrap in double quotes + return f'"{quoted_value}"' def p7zip_supports_rar(program: str) -> bool: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/pyproject.toml new/patool-4.1.0/pyproject.toml --- old/patool-4.0.5/pyproject.toml 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/pyproject.toml 2026-09-04 12:45:16.000000000 +0200 @@ -1,14 +1,10 @@ [project] name = "patool" -version = "4.0.5" +version = "4.1.0" requires-python = ">=3.12" dependencies = [] # these values are set by setup.py -dynamic = ["scripts", "authors", "keywords", "classifiers", "urls", "license", "readme", "description"] - -[build-system] -requires = ["setuptools-reproducible>=0.1"] -build-backend = "setuptools_reproducible" +dynamic = ["authors", "classifiers", "description", "keywords", "license", "readme", "scripts", "urls"] [project.optional-dependencies] # optional features @@ -19,37 +15,46 @@ # these modules are only needed for development [dependency-groups] dev = [ - # for using setuptools.setup() in setup.py - "setuptools==82.0.1", + # for upgrading the version number + "bump2version==1.0.1", + # Sphinx plugin for markdown parsing + "myst-parser==5.1.0", + # test optional functionality + "patool[argcompletion]", # for testing - "pytest==9.0.3", - # plugin to run tests in parallel - "pytest-xdist[psutil]==3.8.0", + "pytest==9.1.1", # plugin to run tests in random order "pytest-randomly==4.1.0", + # plugin to run tests in parallel + "pytest-xdist[psutil]==3.8.0", + # check for new packages + "python-update-checker==0.9.0", # for python code linting - "ruff==0.15.12", - # for python type checking - "ty==0.0.35", - # for generating a Python .whl file - "wheel==0.47.0", + "ruff==0.16.5", + # Markdown style checking and formatting + "rumdl==0.2.62", + # for using setuptools.setup() in setup.py + "setuptools==84.0.0", # for generating the web page "sphinx==9.1.0", - "myst-parser==5.0.0", - # for upgrading the version number - "bump2version==1.0.1", + # reformat toml files + "tombi==1.4.1", + # for python type checking + "ty==0.0.75", + # for generating a Python .whl file + "wheel==0.48.0", # for auditing github actions - "zizmor==1.24.1", - # check for new packages - "python-update-checker==0.6", - # test optional functionality - "patool[argcompletion]", + "zizmor==1.29.0", ] -[tool.patool] +[build-system] +requires = ["setuptools-reproducible>=0.1"] +build-backend = "setuptools_reproducible" + +[tool.project] # pin versions of Python and uv for development -python_version_dev = "3.14.5" -uv_version_dev = "0.11.14" +python_version_dev = "3.14.7" +uv_version_dev = "0.12.9" [tool.ty.environment] # patool uses some imports and attributes that are only available on windows diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/setup.py new/patool-4.1.0/setup.py --- old/patool-4.0.5/setup.py 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/setup.py 2026-09-04 12:45:16.000000000 +0200 @@ -17,8 +17,8 @@ import sys -if not hasattr(sys, "version_info") or sys.version_info < (3, 11, 0, "final", 0): - raise SystemExit("This program requires Python 3.11 or later.") +if not hasattr(sys, "version_info") or sys.version_info < (3, 12, 0, "final", 0): + raise SystemExit("This program requires Python 3.12 or later.") import os from setuptools import setup @@ -55,7 +55,7 @@ author_email=configdata["AuthorEmail"], license=configdata["License"], url=configdata["Url"], - python_requires=">=3.11", + python_requires=">=3.12", project_urls={ "Source": "https://github.com/wummel/patool", }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/tests/archives/test_zpaqfranz.py new/patool-4.1.0/tests/archives/test_zpaqfranz.py --- old/patool-4.0.5/tests/archives/test_zpaqfranz.py 1970-01-01 01:00:00.000000000 +0100 +++ new/patool-4.1.0/tests/archives/test_zpaqfranz.py 2026-09-04 12:45:16.000000000 +0200 @@ -0,0 +1,37 @@ +# Copyright (C) 2026 Bastian Kleineidam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +"""Test the zpaqfranz program""" + +from . import ArchiveTest, Content +from .. import needs_program + + +class TestZpaqfranz(ArchiveTest): + """Test class for the zpaqfranz program""" + + program = 'zpaqfranz' + + @needs_program(program) + def test_zpaq(self): + """Run archive commands with ZPAQ archive.""" + self.archive_commands('t.zpaq', check=Content.Multifile) + + @needs_program('file') + @needs_program(program) + def test_zpaq_file(self): + """Run archive commands with renamed ZPAQ archive.""" + self.archive_extract('t.zpaq.foo', check=Content.Multifile) + self.archive_test('t.zpaq.foo') + self.archive_list('t.zpaq.foo') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/tests/test_shell_quoting.py new/patool-4.1.0/tests/test_shell_quoting.py --- old/patool-4.0.5/tests/test_shell_quoting.py 1970-01-01 01:00:00.000000000 +0100 +++ new/patool-4.1.0/tests/test_shell_quoting.py 2026-09-04 12:45:16.000000000 +0200 @@ -0,0 +1,53 @@ +# Copyright (C) 2026 Bastian Kleineidam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +"""Test patool extract command.""" + +import unittest +import os +import shutil +from patoolib import fileutil, cli +from . import basedir, datadir, needs_program + + +class ShellQuotingTest(unittest.TestCase): + """Test extracting files with special characters.""" + + @needs_program('gzip') + def test_shell_quoting(self): + """Extract files with special characters in the filename.""" + basename = "test_" + for c in "&()^`'!%;$": + self._extract(basename + c + ".txt") + + def _extract(self, filename): + """Run cli function to extract a gzip archive.""" + tmpdir_in = fileutil.tmpdir(dir=basedir) + tmpdir_out = fileutil.tmpdir(dir=basedir) + try: + orig_archive = os.path.join(datadir, "t.txt.gz") + archive = os.path.join(tmpdir_in, filename + ".gz") + shutil.copy(orig_archive, archive) + args = [ + "-vv", + "--non-interactive", + "extract", + "--outdir", + tmpdir_out, + archive, + ] + cli.main(args=args) + finally: + fileutil.rmtree(tmpdir_in) + fileutil.rmtree(tmpdir_out) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/patool-4.0.5/tests/test_util.py new/patool-4.1.0/tests/test_util.py --- old/patool-4.0.5/tests/test_util.py 2026-05-18 10:55:30.000000000 +0200 +++ new/patool-4.1.0/tests/test_util.py 2026-09-04 12:45:16.000000000 +0200 @@ -29,13 +29,22 @@ log.log_error(msg) try: raise Exception(msg) - except Exception: - log.log_internal_error() + except Exception as exc: + log.log_internal_error(exc) - def test_quote(self): + def test_quote_unix(self): """Test util.shell_quote_unix()""" self.assertEqual(util.shell_quote_unix("a b"), "'a b'") + def test_quote_nt(self): + """Test util.shell_quote_nt()""" + self.assertEqual(util.shell_quote_nt("a b"), '"a b"') + for c in '&()^!': + self.assertEqual(util.shell_quote_nt(f"a{c}{c} b"), f'"a{c}{c} b"') + for c in '"<>|?*\t\n\\': + self.assertRaises(ValueError, util.shell_quote_nt, f"file{c}") + self.assertEqual(util.shell_quote_nt("a%USER%b"), '"a%%USER%%b"') + def test_strlist_with_or(self): """Test util.strlist()""" alist = []
