@@ -49,8 +49,8 @@ def __init__(self, errno, strerror, characters_written=0):
49
49
self .characters_written = characters_written
50
50
51
51
52
- def open (file , mode = "r" , buffering = None , encoding = None , newline = None ,
53
- closefd = True ):
52
+ def open (file , mode = "r" , buffering = None , encoding = None , errors = None ,
53
+ newline = None , closefd = True ):
54
54
r"""Replacement for the built-in open function.
55
55
56
56
Args:
@@ -61,6 +61,7 @@ def open(file, mode="r", buffering=None, encoding=None, newline=None,
61
61
can be: 0 = unbuffered, 1 = line buffered,
62
62
larger = fully buffered.
63
63
encoding: optional string giving the text encoding.
64
+ errors: optional string giving the encoding error handling.
64
65
newline: optional newlines specifier; must be None, '', '\n', '\r'
65
66
or '\r\n'; all other values are illegal. It controls the
66
67
handling of line endings. It works as follows:
@@ -99,7 +100,7 @@ def open(file, mode="r", buffering=None, encoding=None, newline=None,
99
100
'U': universal newline mode (for backwards compatibility)
100
101
101
102
Constraints:
102
- - encoding must not be given when a binary mode is given
103
+ - encoding or errors must not be given when a binary mode is given
103
104
- buffering must not be zero when a text mode is given
104
105
105
106
Returns:
@@ -115,6 +116,8 @@ def open(file, mode="r", buffering=None, encoding=None, newline=None,
115
116
raise TypeError ("invalid buffering: %r" % buffering )
116
117
if encoding is not None and not isinstance (encoding , str ):
117
118
raise TypeError ("invalid encoding: %r" % encoding )
119
+ if errors is not None and not isinstance (errors , str ):
120
+ raise TypeError ("invalid errors: %r" % errors )
118
121
modes = set (mode )
119
122
if modes - set ("arwb+tU" ) or len (mode ) > len (modes ):
120
123
raise ValueError ("invalid mode: %r" % mode )
@@ -136,6 +139,8 @@ def open(file, mode="r", buffering=None, encoding=None, newline=None,
136
139
raise ValueError ("must have exactly one of read/write/append mode" )
137
140
if binary and encoding is not None :
138
141
raise ValueError ("binary mode doesn't take an encoding argument" )
142
+ if binary and errors is not None :
143
+ raise ValueError ("binary mode doesn't take an errors argument" )
139
144
if binary and newline is not None :
140
145
raise ValueError ("binary mode doesn't take a newline argument" )
141
146
raw = FileIO (file ,
@@ -177,7 +182,7 @@ def open(file, mode="r", buffering=None, encoding=None, newline=None,
177
182
buffer .name = file
178
183
buffer .mode = mode
179
184
return buffer
180
- text = TextIOWrapper (buffer , encoding , newline )
185
+ text = TextIOWrapper (buffer , encoding , errors , newline )
181
186
text .name = file
182
187
text .mode = mode
183
188
return text
@@ -1128,7 +1133,7 @@ class TextIOWrapper(TextIOBase):
1128
1133
1129
1134
_CHUNK_SIZE = 128
1130
1135
1131
- def __init__ (self , buffer , encoding = None , newline = None ):
1136
+ def __init__ (self , buffer , encoding = None , errors = None , newline = None ):
1132
1137
if newline not in (None , "" , "\n " , "\r " , "\r \n " ):
1133
1138
raise ValueError ("illegal newline value: %r" % (newline ,))
1134
1139
if encoding is None :
@@ -1148,8 +1153,15 @@ def __init__(self, buffer, encoding=None, newline=None):
1148
1153
if not isinstance (encoding , str ):
1149
1154
raise ValueError ("invalid encoding: %r" % encoding )
1150
1155
1156
+ if errors is None :
1157
+ errors = "strict"
1158
+ else :
1159
+ if not isinstance (errors , str ):
1160
+ raise ValueError ("invalid errors: %r" % errors )
1161
+
1151
1162
self .buffer = buffer
1152
1163
self ._encoding = encoding
1164
+ self ._errors = errors
1153
1165
self ._readuniversal = not newline
1154
1166
self ._readtranslate = newline is None
1155
1167
self ._readnl = newline
@@ -1164,6 +1176,10 @@ def __init__(self, buffer, encoding=None, newline=None):
1164
1176
def encoding (self ):
1165
1177
return self ._encoding
1166
1178
1179
+ @property
1180
+ def errors (self ):
1181
+ return self ._errors
1182
+
1167
1183
# A word about _snapshot. This attribute is either None, or a
1168
1184
# tuple (decoder_state, readahead, pending) where decoder_state is
1169
1185
# the second (integer) item of the decoder state, readahead is the
@@ -1206,7 +1222,7 @@ def write(self, s: str):
1206
1222
if haslf and self ._writetranslate and self ._writenl != "\n " :
1207
1223
s = s .replace ("\n " , self ._writenl )
1208
1224
# XXX What if we were just reading?
1209
- b = s .encode (self ._encoding )
1225
+ b = s .encode (self ._encoding , self . _errors )
1210
1226
self .buffer .write (b )
1211
1227
if haslf and self .isatty ():
1212
1228
self .flush ()
@@ -1220,7 +1236,7 @@ def _get_decoder(self):
1220
1236
if make_decoder is None :
1221
1237
raise IOError ("Can't find an incremental decoder for encoding %s" %
1222
1238
self ._encoding )
1223
- decoder = make_decoder () # XXX: errors
1239
+ decoder = make_decoder (self . _errors )
1224
1240
if self ._readuniversal :
1225
1241
decoder = IncrementalNewlineDecoder (decoder , self ._readtranslate )
1226
1242
self ._decoder = decoder
@@ -1447,9 +1463,11 @@ class StringIO(TextIOWrapper):
1447
1463
1448
1464
# XXX This is really slow, but fully functional
1449
1465
1450
- def __init__ (self , initial_value = "" , encoding = "utf-8" , newline = "\n " ):
1466
+ def __init__ (self , initial_value = "" , encoding = "utf-8" ,
1467
+ errors = "strict" , newline = "\n " ):
1451
1468
super (StringIO , self ).__init__ (BytesIO (),
1452
1469
encoding = encoding ,
1470
+ errors = errors ,
1453
1471
newline = newline )
1454
1472
if initial_value :
1455
1473
if not isinstance (initial_value , str ):
@@ -1459,4 +1477,4 @@ def __init__(self, initial_value="", encoding="utf-8", newline="\n"):
1459
1477
1460
1478
def getvalue (self ):
1461
1479
self .flush ()
1462
- return self .buffer .getvalue ().decode (self ._encoding )
1480
+ return self .buffer .getvalue ().decode (self ._encoding , self . _errors )
0 commit comments