mirror of
https://github.com/python/cpython.git
synced 2025-08-20 08:41:07 +00:00
[3.12] gh-129726: Break gzip.GzipFile
reference loop (GH-130055) (#130670)
gh-129726: Break `gzip.GzipFile` reference loop (GH-130055)
A reference loop was resulting in the `fileobj` held by the `GzipFile`
being closed before the `GzipFile`.
The issue started with gh-89550 in 3.12, but was hidden in most cases
until 3.13 when gh-62948 made it more visible.
(cherry picked from commit 7f39137662
)
Co-authored-by: Cody Maloney <cmaloney@users.noreply.github.com>
This commit is contained in:
parent
107e08dfb1
commit
500ea3b0ee
3 changed files with 28 additions and 5 deletions
17
Lib/gzip.py
17
Lib/gzip.py
|
@ -5,11 +5,15 @@ but random access is not allowed."""
|
||||||
|
|
||||||
# based on Andrew Kuchling's minigzip.py distributed with the zlib module
|
# based on Andrew Kuchling's minigzip.py distributed with the zlib module
|
||||||
|
|
||||||
import struct, sys, time, os
|
import _compression
|
||||||
import zlib
|
|
||||||
import builtins
|
import builtins
|
||||||
import io
|
import io
|
||||||
import _compression
|
import os
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import weakref
|
||||||
|
import zlib
|
||||||
|
|
||||||
__all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"]
|
__all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"]
|
||||||
|
|
||||||
|
@ -124,10 +128,13 @@ class BadGzipFile(OSError):
|
||||||
class _WriteBufferStream(io.RawIOBase):
|
class _WriteBufferStream(io.RawIOBase):
|
||||||
"""Minimal object to pass WriteBuffer flushes into GzipFile"""
|
"""Minimal object to pass WriteBuffer flushes into GzipFile"""
|
||||||
def __init__(self, gzip_file):
|
def __init__(self, gzip_file):
|
||||||
self.gzip_file = gzip_file
|
self.gzip_file = weakref.ref(gzip_file)
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
return self.gzip_file._write_raw(data)
|
gzip_file = self.gzip_file()
|
||||||
|
if gzip_file is None:
|
||||||
|
raise RuntimeError("lost gzip_file")
|
||||||
|
return gzip_file._write_raw(data)
|
||||||
|
|
||||||
def seekable(self):
|
def seekable(self):
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
|
|
||||||
import array
|
import array
|
||||||
import functools
|
import functools
|
||||||
|
import gc
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
from subprocess import PIPE, Popen
|
from subprocess import PIPE, Popen
|
||||||
|
from test.support import catch_unraisable_exception
|
||||||
from test.support import import_helper
|
from test.support import import_helper
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
from test.support import _4G, bigmemtest, requires_subprocess
|
from test.support import _4G, bigmemtest, requires_subprocess
|
||||||
|
@ -836,6 +838,17 @@ class TestGzip(BaseTest):
|
||||||
self.assertEqual(gzip.decompress(data), message * 2)
|
self.assertEqual(gzip.decompress(data), message * 2)
|
||||||
|
|
||||||
|
|
||||||
|
def test_refloop_unraisable(self):
|
||||||
|
# Ensure a GzipFile referring to a temporary fileobj deletes cleanly.
|
||||||
|
# Previously an unraisable exception would occur on close because the
|
||||||
|
# fileobj would be closed before the GzipFile as the result of a
|
||||||
|
# reference loop. See issue gh-129726
|
||||||
|
with catch_unraisable_exception() as cm:
|
||||||
|
gzip.GzipFile(fileobj=io.BytesIO(), mode="w")
|
||||||
|
gc.collect()
|
||||||
|
self.assertIsNone(cm.unraisable)
|
||||||
|
|
||||||
|
|
||||||
class TestOpen(BaseTest):
|
class TestOpen(BaseTest):
|
||||||
def test_binary_modes(self):
|
def test_binary_modes(self):
|
||||||
uncompressed = data1 * 50
|
uncompressed = data1 * 50
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fix :class:`gzip.GzipFile` raising an unraisable exception during garbage
|
||||||
|
collection when referring to a temporary object by breaking the reference
|
||||||
|
loop with :mod:`weakref`.
|
Loading…
Add table
Add a link
Reference in a new issue