Skip to content

Commit af50fa5

Browse files
committed
Update _pyio.py to CPython 3.10
1 parent c8a8d32 commit af50fa5

1 file changed

Lines changed: 70 additions & 36 deletions

File tree

Lib/_pyio.py

Lines changed: 70 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,40 @@
3636
# Does io.IOBase finalizer log the exception if the close() method fails?
3737
# The exception is ignored silently by default in release build.
3838
_IOBASE_EMITS_UNRAISABLE = (hasattr(sys, "gettotalrefcount") or sys.flags.dev_mode)
39+
# Does open() check its 'errors' argument?
40+
_CHECK_ERRORS = _IOBASE_EMITS_UNRAISABLE
3941

4042

43+
def text_encoding(encoding, stacklevel=2):
44+
"""
45+
A helper function to choose the text encoding.
46+
47+
When encoding is not None, just return it.
48+
Otherwise, return the default text encoding (i.e. "locale").
49+
50+
This function emits an EncodingWarning if *encoding* is None and
51+
sys.flags.warn_default_encoding is true.
52+
53+
This can be used in APIs with an encoding=None parameter
54+
that pass it to TextIOWrapper or open.
55+
However, please consider using encoding="utf-8" for new APIs.
56+
"""
57+
if encoding is None:
58+
encoding = "locale"
59+
if sys.flags.warn_default_encoding:
60+
import warnings
61+
warnings.warn("'encoding' argument not specified.",
62+
EncodingWarning, stacklevel + 1)
63+
return encoding
64+
65+
66+
# Wrapper for builtins.open
67+
#
68+
# Trick so that open() won't become a bound method when stored
69+
# as a class variable (as dbm.dumb does).
70+
#
71+
# See init_set_builtins_open() in Python/pylifecycle.c.
72+
@staticmethod
4173
def open(file, mode="r", buffering=-1, encoding=None, errors=None,
4274
newline=None, closefd=True, opener=None):
4375

@@ -246,6 +278,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
246278
result = buffer
247279
if binary:
248280
return result
281+
encoding = text_encoding(encoding)
249282
text = TextIOWrapper(buffer, encoding, errors, newline, line_buffering)
250283
result = text
251284
text.mode = mode
@@ -278,27 +311,20 @@ def _open_code_with_warning(path):
278311
open_code = _open_code_with_warning
279312

280313

281-
class DocDescriptor:
282-
"""Helper for builtins.open.__doc__
283-
"""
284-
def __get__(self, obj, typ=None):
285-
return (
286-
"open(file, mode='r', buffering=-1, encoding=None, "
287-
"errors=None, newline=None, closefd=True)\n\n" +
288-
open.__doc__)
289-
290-
class OpenWrapper:
291-
"""Wrapper for builtins.open
292-
293-
Trick so that open won't become a bound method when stored
294-
as a class variable (as dbm.dumb does).
295-
296-
See initstdio() in Python/pylifecycle.c.
297-
"""
298-
__doc__ = DocDescriptor()
299-
300-
def __new__(cls, *args, **kwargs):
301-
return open(*args, **kwargs)
314+
def __getattr__(name):
315+
if name == "OpenWrapper":
316+
# bpo-43680: Until Python 3.9, _pyio.open was not a static method and
317+
# builtins.open was set to OpenWrapper to not become a bound method
318+
# when set to a class variable. _io.open is a built-in function whereas
319+
# _pyio.open is a Python function. In Python 3.10, _pyio.open() is now
320+
# a static method, and builtins.open() is now io.open().
321+
import warnings
322+
warnings.warn('OpenWrapper is deprecated, use open instead',
323+
DeprecationWarning, stacklevel=2)
324+
global OpenWrapper
325+
OpenWrapper = open
326+
return OpenWrapper
327+
raise AttributeError(name)
302328

303329

304330
# In normal operation, both `UnsupportedOperation`s should be bound to the
@@ -802,6 +828,9 @@ def tell(self):
802828
return pos
803829

804830
def truncate(self, pos=None):
831+
self._checkClosed()
832+
self._checkWritable()
833+
805834
# Flush the stream. We're mixing buffered I/O with lower-level I/O,
806835
# and a flush may be necessary to synch both views of the current
807836
# file state.
@@ -1571,7 +1600,7 @@ def __init__(self, file, mode='r', closefd=True, opener=None):
15711600
raise IsADirectoryError(errno.EISDIR,
15721601
os.strerror(errno.EISDIR), file)
15731602
except AttributeError:
1574-
# Ignore the AttribueError if stat.S_ISDIR or errno.EISDIR
1603+
# Ignore the AttributeError if stat.S_ISDIR or errno.EISDIR
15751604
# don't exist.
15761605
pass
15771606
self._blksize = getattr(fdfstat, 'st_blksize', 0)
@@ -1999,19 +2028,22 @@ class TextIOWrapper(TextIOBase):
19992028
def __init__(self, buffer, encoding=None, errors=None, newline=None,
20002029
line_buffering=False, write_through=False):
20012030
self._check_newline(newline)
2002-
if encoding is None:
2031+
encoding = text_encoding(encoding)
2032+
2033+
if encoding == "locale":
20032034
try:
2004-
encoding = os.device_encoding(buffer.fileno())
2035+
encoding = os.device_encoding(buffer.fileno()) or "locale"
20052036
except (AttributeError, UnsupportedOperation):
20062037
pass
2007-
if encoding is None:
2008-
try:
2009-
import locale
2010-
except ImportError:
2011-
# Importing locale may fail if Python is being built
2012-
encoding = "ascii"
2013-
else:
2014-
encoding = locale.getpreferredencoding(False)
2038+
2039+
if encoding == "locale":
2040+
try:
2041+
import locale
2042+
except ImportError:
2043+
# Importing locale may fail if Python is being built
2044+
encoding = "utf-8"
2045+
else:
2046+
encoding = locale.getpreferredencoding(False)
20152047

20162048
if not isinstance(encoding, str):
20172049
raise ValueError("invalid encoding: %r" % encoding)
@@ -2026,6 +2058,8 @@ def __init__(self, buffer, encoding=None, errors=None, newline=None,
20262058
else:
20272059
if not isinstance(errors, str):
20282060
raise ValueError("invalid errors: %r" % errors)
2061+
if _CHECK_ERRORS:
2062+
codecs.lookup_error(errors)
20292063

20302064
self._buffer = buffer
20312065
self._decoded_chars = '' # buffer for text returned from decoder
@@ -2295,7 +2329,7 @@ def _read_chunk(self):
22952329
return not eof
22962330

22972331
def _pack_cookie(self, position, dec_flags=0,
2298-
bytes_to_feed=0, need_eof=0, chars_to_skip=0):
2332+
bytes_to_feed=0, need_eof=False, chars_to_skip=0):
22992333
# The meaning of a tell() cookie is: seek to position, set the
23002334
# decoder flags to dec_flags, read bytes_to_feed bytes, feed them
23012335
# into the decoder with need_eof as the EOF flag, then skip
@@ -2309,7 +2343,7 @@ def _unpack_cookie(self, bigint):
23092343
rest, dec_flags = divmod(rest, 1<<64)
23102344
rest, bytes_to_feed = divmod(rest, 1<<64)
23112345
need_eof, chars_to_skip = divmod(rest, 1<<64)
2312-
return position, dec_flags, bytes_to_feed, need_eof, chars_to_skip
2346+
return position, dec_flags, bytes_to_feed, bool(need_eof), chars_to_skip
23132347

23142348
def tell(self):
23152349
if not self._seekable:
@@ -2383,7 +2417,7 @@ def tell(self):
23832417
# (a point where the decoder has nothing buffered, so seek()
23842418
# can safely start from there and advance to this location).
23852419
bytes_fed = 0
2386-
need_eof = 0
2420+
need_eof = False
23872421
# Chars decoded since `start_pos`
23882422
chars_decoded = 0
23892423
for i in range(skip_bytes, len(next_input)):
@@ -2400,7 +2434,7 @@ def tell(self):
24002434
else:
24012435
# We didn't get enough decoded data; signal EOF to get more.
24022436
chars_decoded += len(decoder.decode(b'', final=True))
2403-
need_eof = 1
2437+
need_eof = True
24042438
if chars_decoded < chars_to_skip:
24052439
raise OSError("can't reconstruct logical file position")
24062440

0 commit comments

Comments
 (0)