mirror of
https://github.com/python/cpython
synced 2024-09-16 00:07:33 +00:00
gh-50644: Forbid pickling of codecs streams (GH-109180)
Attempts to pickle or create a shallow or deep copy of codecs streams now raise a TypeError. Previously, copying failed with a RecursionError, while pickling produced wrong results that eventually caused unpickling to fail with a RecursionError.
This commit is contained in:
parent
71b6e2602c
commit
d6892c2b92
|
@ -414,6 +414,9 @@ def __enter__(self):
|
|||
def __exit__(self, type, value, tb):
|
||||
self.stream.close()
|
||||
|
||||
def __reduce_ex__(self, proto):
|
||||
raise TypeError("can't serialize %s" % self.__class__.__name__)
|
||||
|
||||
###
|
||||
|
||||
class StreamReader(Codec):
|
||||
|
@ -663,6 +666,9 @@ def __enter__(self):
|
|||
def __exit__(self, type, value, tb):
|
||||
self.stream.close()
|
||||
|
||||
def __reduce_ex__(self, proto):
|
||||
raise TypeError("can't serialize %s" % self.__class__.__name__)
|
||||
|
||||
###
|
||||
|
||||
class StreamReaderWriter:
|
||||
|
@ -750,6 +756,9 @@ def __enter__(self):
|
|||
def __exit__(self, type, value, tb):
|
||||
self.stream.close()
|
||||
|
||||
def __reduce_ex__(self, proto):
|
||||
raise TypeError("can't serialize %s" % self.__class__.__name__)
|
||||
|
||||
###
|
||||
|
||||
class StreamRecoder:
|
||||
|
@ -866,6 +875,9 @@ def __enter__(self):
|
|||
def __exit__(self, type, value, tb):
|
||||
self.stream.close()
|
||||
|
||||
def __reduce_ex__(self, proto):
|
||||
raise TypeError("can't serialize %s" % self.__class__.__name__)
|
||||
|
||||
### Shortcuts
|
||||
|
||||
def open(filename, mode='r', encoding=None, errors='strict', buffering=-1):
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import codecs
|
||||
import contextlib
|
||||
import copy
|
||||
import io
|
||||
import locale
|
||||
import pickle
|
||||
import sys
|
||||
import unittest
|
||||
import encodings
|
||||
|
@ -1771,6 +1773,61 @@ def test_readlines(self):
|
|||
f = self.reader(self.stream)
|
||||
self.assertEqual(f.readlines(), ['\ud55c\n', '\uae00'])
|
||||
|
||||
def test_copy(self):
|
||||
f = self.reader(Queue(b'\xed\x95\x9c\n\xea\xb8\x80'))
|
||||
with self.assertRaisesRegex(TypeError, 'StreamReader'):
|
||||
copy.copy(f)
|
||||
with self.assertRaisesRegex(TypeError, 'StreamReader'):
|
||||
copy.deepcopy(f)
|
||||
|
||||
def test_pickle(self):
|
||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
with self.subTest(protocol=proto):
|
||||
f = self.reader(Queue(b'\xed\x95\x9c\n\xea\xb8\x80'))
|
||||
with self.assertRaisesRegex(TypeError, 'StreamReader'):
|
||||
pickle.dumps(f, proto)
|
||||
|
||||
|
||||
class StreamWriterTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.writer = codecs.getwriter('utf-8')
|
||||
|
||||
def test_copy(self):
|
||||
f = self.writer(Queue(b''))
|
||||
with self.assertRaisesRegex(TypeError, 'StreamWriter'):
|
||||
copy.copy(f)
|
||||
with self.assertRaisesRegex(TypeError, 'StreamWriter'):
|
||||
copy.deepcopy(f)
|
||||
|
||||
def test_pickle(self):
|
||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
with self.subTest(protocol=proto):
|
||||
f = self.writer(Queue(b''))
|
||||
with self.assertRaisesRegex(TypeError, 'StreamWriter'):
|
||||
pickle.dumps(f, proto)
|
||||
|
||||
|
||||
class StreamReaderWriterTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.reader = codecs.getreader('latin1')
|
||||
self.writer = codecs.getwriter('utf-8')
|
||||
|
||||
def test_copy(self):
|
||||
f = codecs.StreamReaderWriter(Queue(b''), self.reader, self.writer)
|
||||
with self.assertRaisesRegex(TypeError, 'StreamReaderWriter'):
|
||||
copy.copy(f)
|
||||
with self.assertRaisesRegex(TypeError, 'StreamReaderWriter'):
|
||||
copy.deepcopy(f)
|
||||
|
||||
def test_pickle(self):
|
||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
with self.subTest(protocol=proto):
|
||||
f = codecs.StreamReaderWriter(Queue(b''), self.reader, self.writer)
|
||||
with self.assertRaisesRegex(TypeError, 'StreamReaderWriter'):
|
||||
pickle.dumps(f, proto)
|
||||
|
||||
|
||||
class EncodedFileTest(unittest.TestCase):
|
||||
|
||||
|
@ -3346,6 +3403,28 @@ def test_seeking_write(self):
|
|||
self.assertEqual(sr.readline(), b'abc\n')
|
||||
self.assertEqual(sr.readline(), b'789\n')
|
||||
|
||||
def test_copy(self):
|
||||
bio = io.BytesIO()
|
||||
codec = codecs.lookup('ascii')
|
||||
sr = codecs.StreamRecoder(bio, codec.encode, codec.decode,
|
||||
encodings.ascii.StreamReader, encodings.ascii.StreamWriter)
|
||||
|
||||
with self.assertRaisesRegex(TypeError, 'StreamRecoder'):
|
||||
copy.copy(sr)
|
||||
with self.assertRaisesRegex(TypeError, 'StreamRecoder'):
|
||||
copy.deepcopy(sr)
|
||||
|
||||
def test_pickle(self):
|
||||
q = Queue(b'')
|
||||
codec = codecs.lookup('ascii')
|
||||
sr = codecs.StreamRecoder(q, codec.encode, codec.decode,
|
||||
encodings.ascii.StreamReader, encodings.ascii.StreamWriter)
|
||||
|
||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
with self.subTest(protocol=proto):
|
||||
with self.assertRaisesRegex(TypeError, 'StreamRecoder'):
|
||||
pickle.dumps(sr, proto)
|
||||
|
||||
|
||||
@unittest.skipIf(_testinternalcapi is None, 'need _testinternalcapi module')
|
||||
class LocaleCodecTest(unittest.TestCase):
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Attempts to pickle or create a shallow or deep copy of :mod:`codecs` streams
|
||||
now raise a TypeError. Previously, copying failed with a RecursionError,
|
||||
while pickling produced wrong results that eventually caused unpickling
|
||||
to fail with a RecursionError.
|
Loading…
Reference in a new issue