GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — `BZ2Decompressor` and `zlib._ZlibDecompressor` set `needs_input=True` while output is still buffered · Issue #159999 · python/cpython

BZ2Decompressor and zlib._ZlibDecompressor set needs_input=True while output is still buffered #159999

New issue
Copy link
New issue
Copy link
Open
Open
BZ2Decompressor and zlib._ZlibDecompressor set needs_input=True while output is still buffered#159999
Copy link
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@pganssle
pganssle
opened on Oct 21, 2026
Issue body actions

Bug description

When max_length cuts the output short and the call also consumes the whole input buffer, bz2.BZ2Decompressor and zlib._ZlibDecompressor set needs_input to True even though a decompress(b'') call would return more data. The documented meaning of the flag is the opposite: False means "there is buffered output, call again with b''"; True means "all output has been produced, feed more input".

>>> import bz2, zlib
>>> stream = bz2.compress(b"\x00\x10 0@" * 2)
>>> d = bz2.BZ2Decompressor()
>>> d.decompress(stream[:40], max_length=0)
b''
>>> d.needs_input, d.eof
(True, False)
>>> d.decompress(b"")   # Expecting: b'' if needs_input is to be believed
b'\x00\x10 0@\x00\x10 0@'
>>> d = zlib._ZlibDecompressor()
>>> d.decompress(zlib.compress(b"\x00")[:4], max_length=0)
b''
>>> d.needs_input
True
>>> d.decompress(b"")
b'\x00'

lzma.LZMADecompressor and compression.zstd.ZstdDecompressor get this right. Compare lzma on the same kind of input:

>>> import lzma
>>> d = lzma.LZMADecompressor()
>>> d.decompress(lzma.compress(b"\x00" * 21), max_length=2)
b'\x00\x00'
>>> d.needs_input
False

The cause is that both modules decide needs_input from the input buffer alone. In _bz2module.c the branch that handles "input exhausted" unconditionally stores 1: Modules/_bz2module.c lines 539-542. zlibmodule.c has the same branch for _ZlibDecompressor: Modules/zlibmodule.c lines 1646-1649. _lzmamodule.c checks whether the output buffer was also full before it decides, with a comment explaining why: Modules/_lzmamodule.c lines 1069-1082. Porting that check to the other two modules looks like the whole fix.

As a practical matter I don't see a way for this to lose data for a caller following the documented protocol: the stream's trailer has not been consumed yet in these states, so the caller still has input to feed and will get the buffered output then. It does make needs_input unreliable as a "no more buffered output" signal, which is what max_length users (including DecompressReader in compression/_common/_streams.py, which raises EOFError rather than call decompress(b'') when needs_input is true at end of file) rely on. So it is worth fixing, but it is not urgent.

Found by the Hypothesis tests in #159960, which record it as test_needs_input_means_drained under expectedFailure for the bz2 and zlib backends. The reproducers depend on where the compressor places block boundaries; the ones above are for zlib 1.3.2 and libbz2 1.0.8.

CPython versions tested on

CPython main (4e3d5ec)

Operating systems tested on

Linux

Linked PRs

  • gh-159999: Fix needs_input for bz2 and zlib decompressors #160696

Activity

  1. pganssle
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Oct 21, 2026
  2. pganssle
    added a commit that references this issue on Oct 21, 2026

    Fix the known-bug tests on debug builds and zlib-ng

    e795310
  3. aisk
    added
    extension-modulesC modules in the Modules dirC modules in the Modules dir
    on Oct 21, 2026
  4. aisk
    added this to Compression issues 🗜on Oct 21, 2026
  5. pganssle
    added 2 commits that reference this issue on Oct 23, 2026

    Fix the known-bug tests on debug builds and zlib-ng

    0442665

    Pin the known compression bugs as expected-failure examples

    c197707
  6. iamsaugatpandey commented on Oct 26, 2026

    @iamsaugatpandey
    iamsaugatpandey
    on Oct 26, 2026
    Contributor
    More actions

    I reproduced this on main, for both BZ2Decompressor and zlib._ZlibDecompressor.

    There's a second case in bz2 that the report doesn't cover: decompress_buf() checks for exhausted input before checking for a full output buffer. So even with no max_length, decompress() can return only the first 32 KiB of a block's output when the input runs out at that moment (feeding the stream one byte at a time hits it about once per block). lzma had the same problem and fixed it in bpo-21872 (GH-14048).

    Fixing the zlib flag also showed that gzip._GzipReader relied on the old behaviour: on valid data it could raise EOFError when decompress(b"") returned nothing. I'm opening a PR that fixes both decompressors the way lzma does, and changes gzip to raise EOFError only when reading the file returns no data, as DecompressReader already does.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirC modules in the Modules dirtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    • Compression issues 🗜
      Status
      No status
      Show more project fields

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.