mirror of https://github.com/python/cpython.git
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):
|
def __exit__(self, type, value, tb):
|
||||||
self.stream.close()
|
self.stream.close()
|
||||||
|
|
||||||
|
def __reduce_ex__(self, proto):
|
||||||
|
raise TypeError("can't serialize %s" % self.__class__.__name__)
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
class StreamReader(Codec):
|
class StreamReader(Codec):
|
||||||
|
@ -663,6 +666,9 @@ def __enter__(self):
|
||||||
def __exit__(self, type, value, tb):
|
def __exit__(self, type, value, tb):
|
||||||
self.stream.close()
|
self.stream.close()
|
||||||
|
|
||||||
|
def __reduce_ex__(self, proto):
|
||||||
|
raise TypeError("can't serialize %s" % self.__class__.__name__)
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
class StreamReaderWriter:
|
class StreamReaderWriter:
|
||||||
|
@ -750,6 +756,9 @@ def __enter__(self):
|
||||||
def __exit__(self, type, value, tb):
|
def __exit__(self, type, value, tb):
|
||||||
self.stream.close()
|
self.stream.close()
|
||||||
|
|
||||||
|
def __reduce_ex__(self, proto):
|
||||||
|
raise TypeError("can't serialize %s" % self.__class__.__name__)
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
class StreamRecoder:
|
class StreamRecoder:
|
||||||
|
@ -866,6 +875,9 @@ def __enter__(self):
|
||||||
def __exit__(self, type, value, tb):
|
def __exit__(self, type, value, tb):
|
||||||
self.stream.close()
|
self.stream.close()
|
||||||
|
|
||||||
|
def __reduce_ex__(self, proto):
|
||||||
|
raise TypeError("can't serialize %s" % self.__class__.__name__)
|
||||||
|
|
||||||
### Shortcuts
|
### Shortcuts
|
||||||
|
|
||||||
def open(filename, mode='r', encoding=None, errors='strict', buffering=-1):
|
def open(filename, mode='r', encoding=None, errors='strict', buffering=-1):
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import codecs
|
import codecs
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import copy
|
||||||
import io
|
import io
|
||||||
import locale
|
import locale
|
||||||
|
import pickle
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
import encodings
|
import encodings
|
||||||
|
@ -1771,6 +1773,61 @@ def test_readlines(self):
|
||||||
f = self.reader(self.stream)
|
f = self.reader(self.stream)
|
||||||
self.assertEqual(f.readlines(), ['\ud55c\n', '\uae00'])
|
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):
|
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'abc\n')
|
||||||
self.assertEqual(sr.readline(), b'789\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')
|
@unittest.skipIf(_testinternalcapi is None, 'need _testinternalcapi module')
|
||||||
class LocaleCodecTest(unittest.TestCase):
|
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 New Issue