From 5d0fb476cf7439f3ce003ca475f413499f4a0b88 Mon Sep 17 00:00:00 2001 From: stumpylog <797416+stumpylog@users.noreply.github.com> Date: Thu, 23 Jul 2026 15:42:57 -0700 Subject: [PATCH] Fix: narrow zstd compression level to the conventional -22..22 range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The raw library bounds (-131072, 22) come from an internal zstd implementation constant (-ZSTD_TARGETLENGTH_MAX), not a meaningful distinct level — deeper negative values than -22 buy nothing over -22 in practice, and the zstd CLI/community convention only uses -22..22. Exposing the raw range via --zip-compression-level would let a user pass a number like -50000 that "validates" but means nothing. --- docs/administration.md | 2 +- src/documents/export/compression.py | 9 ++++++++- src/documents/management/commands/document_exporter.py | 2 +- src/documents/tests/export/test_compression.py | 4 ++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/administration.md b/docs/administration.md index ae96d2860..7d95c9b5b 100644 --- a/docs/administration.md +++ b/docs/administration.md @@ -338,7 +338,7 @@ value set in `-zn` or `--zip-name`. The compression method for the zip can be set with `--zip-compression` (`stored`, `deflated` (default), `bzip2`, `lzma`, or `zstd`) and tuned with -`--zip-compression-level` (deflated: 0–9, bzip2: 1–9, zstd: -131072–22; ignored +`--zip-compression-level` (deflated: 0–9, bzip2: 1–9, zstd: -22–22; ignored for `stored` and `lzma`). Both options require `--zip`. !!! warning diff --git a/src/documents/export/compression.py b/src/documents/export/compression.py index 4760a16b1..e61eebac7 100644 --- a/src/documents/export/compression.py +++ b/src/documents/export/compression.py @@ -28,12 +28,19 @@ if ZSTD is not None: # Inclusive (min, max) level bounds per method; None => level not applicable. # Verified on CPython 3.14.3. +# +# zstd's raw library bounds are (-131072, 22) +# (compression.zstd.CompressionParameter.compression_level.bounds()) — the +# minimum is an internal implementation constant (-ZSTD_TARGETLENGTH_MAX), +# not a meaningful distinct "level"; deeper negative values than -22 buy +# nothing over -22 in practice. We expose the conventional zstd CLI range +# instead of the raw library bounds. LEVEL_BOUNDS: dict[str, tuple[int, int] | None] = { "stored": None, "deflated": (0, 9), "bzip2": (1, 9), "lzma": None, - "zstd": (-131072, 22), + "zstd": (-22, 22), } # zipfile compress_type id -> method name. 93 = current zstd id, 20 = legacy diff --git a/src/documents/management/commands/document_exporter.py b/src/documents/management/commands/document_exporter.py index 0d0f3f681..124e20fb1 100644 --- a/src/documents/management/commands/document_exporter.py +++ b/src/documents/management/commands/document_exporter.py @@ -213,7 +213,7 @@ class Command(CryptMixin, PaperlessCommand): default=None, help=( "Compression level for the export zip (requires --zip). " - "deflated: 0-9, bzip2: 1-9, zstd: -131072..22; ignored for " + "deflated: 0-9, bzip2: 1-9, zstd: -22..22; ignored for " "stored/lzma." ), ) diff --git a/src/documents/tests/export/test_compression.py b/src/documents/tests/export/test_compression.py index 6e40fe2bc..83dd4020a 100644 --- a/src/documents/tests/export/test_compression.py +++ b/src/documents/tests/export/test_compression.py @@ -78,6 +78,8 @@ class TestLevelError: ("deflated", 9), ("bzip2", 1), ("bzip2", 9), + ("zstd", -22), + ("zstd", 22), ("deflated", None), ("stored", None), ], @@ -100,6 +102,8 @@ class TestLevelError: ("deflated", -1), ("bzip2", 0), ("bzip2", 10), + ("zstd", -23), + ("zstd", 23), ], ) def test_out_of_range_levels_return_message(