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
4173def 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