1
+ import io
1
2
import os
2
3
import unittest
4
+ import warnings
3
5
from test import support
4
6
from test .support import import_helper , os_helper
5
7
6
- _testcapi = import_helper .import_module ('_testcapi' )
7
8
9
+ FIRST_LINE = 'import io\n ' # First line of this file
10
+ _testcapi = import_helper .import_module ('_testcapi' )
11
+ _testlimitedcapi = import_helper .import_module ('_testlimitedcapi' )
12
+ _io = import_helper .import_module ('_io' )
8
13
NULL = None
9
14
10
15
11
16
class CAPIFileTest (unittest .TestCase ):
17
+ def test_pyfile_fromfd (self ):
18
+ # Test PyFile_FromFd()
19
+ pyfile_fromfd = _testlimitedcapi .pyfile_fromfd
20
+ filename = __file__
21
+ with open (filename , "rb" ) as fp :
22
+ fd = fp .fileno ()
23
+
24
+ # FileIO
25
+ fp .seek (0 )
26
+ obj = pyfile_fromfd (fd , filename , "rb" , 0 , NULL , NULL , NULL , 0 )
27
+ try :
28
+ self .assertIsInstance (obj , _io .FileIO )
29
+ self .assertEqual (obj .readline (), FIRST_LINE .encode ())
30
+ finally :
31
+ obj .close ()
32
+
33
+ # BufferedReader
34
+ fp .seek (0 )
35
+ obj = pyfile_fromfd (fd , filename , "rb" , 1024 , NULL , NULL , NULL , 0 )
36
+ try :
37
+ self .assertIsInstance (obj , _io .BufferedReader )
38
+ self .assertEqual (obj .readline (), FIRST_LINE .encode ())
39
+ finally :
40
+ obj .close ()
41
+
42
+ # TextIOWrapper
43
+ fp .seek (0 )
44
+ obj = pyfile_fromfd (fd , filename , "r" , 1 , "utf-8" , NULL , NULL , 0 )
45
+ try :
46
+ self .assertIsInstance (obj , _io .TextIOWrapper )
47
+ self .assertEqual (obj .readline (), FIRST_LINE )
48
+ finally :
49
+ obj .close ()
50
+
51
+ def test_pyfile_getline (self ):
52
+ # Test PyFile_GetLine()
53
+ pyfile_getline = _testlimitedcapi .pyfile_getline
54
+
55
+ # Test Unicode
56
+ with open (__file__ , "r" ) as fp :
57
+ fp .seek (0 )
58
+ self .assertEqual (pyfile_getline (fp , - 1 ), FIRST_LINE .rstrip ())
59
+ fp .seek (0 )
60
+ self .assertEqual (pyfile_getline (fp , 0 ), FIRST_LINE )
61
+ fp .seek (0 )
62
+ self .assertEqual (pyfile_getline (fp , 6 ), FIRST_LINE [:6 ])
63
+
64
+ # Test bytes
65
+ with open (__file__ , "rb" ) as fp :
66
+ fp .seek (0 )
67
+ self .assertEqual (pyfile_getline (fp , - 1 ),
68
+ FIRST_LINE .rstrip ().encode ())
69
+ fp .seek (0 )
70
+ self .assertEqual (pyfile_getline (fp , 0 ), FIRST_LINE .encode ())
71
+ fp .seek (0 )
72
+ self .assertEqual (pyfile_getline (fp , 6 ), FIRST_LINE .encode ()[:6 ])
73
+
74
+ def test_pyfile_writestring (self ):
75
+ # Test PyFile_WriteString()
76
+ writestr = _testlimitedcapi .pyfile_writestring
77
+
78
+ with io .StringIO () as fp :
79
+ self .assertEqual (writestr ("a\xe9 \u20ac \U0010FFFF " .encode (), fp ), 0 )
80
+ with self .assertRaises (UnicodeDecodeError ):
81
+ writestr (b"\xff " , fp )
82
+
83
+ text = fp .getvalue ()
84
+ self .assertEqual (text , "a\xe9 \u20ac \U0010FFFF " )
85
+
86
+ with self .assertRaises (SystemError ):
87
+ writestr (b"abc" , NULL )
88
+
89
+ def test_pyfile_writeobject (self ):
90
+ # Test PyFile_WriteObject()
91
+ writeobject = _testlimitedcapi .pyfile_writeobject
92
+ Py_PRINT_RAW = 1
93
+
94
+ with io .StringIO () as fp :
95
+ self .assertEqual (writeobject ("raw\n " , fp , Py_PRINT_RAW ), 0 )
96
+ writeobject (NULL , fp , Py_PRINT_RAW )
97
+
98
+ self .assertEqual (writeobject ("repr" , fp , 0 ), 0 )
99
+ writeobject (NULL , fp , 0 )
100
+
101
+ text = fp .getvalue ()
102
+ self .assertEqual (text , "raw\n <NULL>'repr'<NULL>" )
103
+
104
+ # invalid file type
105
+ for invalid_file in (123 , "abc" , object ()):
106
+ with self .subTest (file = invalid_file ):
107
+ with self .assertRaises (AttributeError ):
108
+ writeobject ("abc" , invalid_file , Py_PRINT_RAW )
109
+
110
+ with self .assertRaises (TypeError ):
111
+ writeobject ("abc" , NULL , 0 )
112
+
113
+ def test_pyobject_asfiledescriptor (self ):
114
+ # Test PyObject_AsFileDescriptor()
115
+ asfd = _testlimitedcapi .pyobject_asfiledescriptor
116
+
117
+ self .assertEqual (asfd (123 ), 123 )
118
+ self .assertEqual (asfd (0 ), 0 )
119
+
120
+ with open (__file__ , "rb" ) as fp :
121
+ self .assertEqual (asfd (fp ), fp .fileno ())
122
+
123
+ # bool emits RuntimeWarning
124
+ with warnings .catch_warnings (record = True ) as warns :
125
+ warnings .simplefilter ('always' , RuntimeWarning )
126
+ self .assertEqual (asfd (True ), 1 )
127
+ self .assertEqual (len (warns ), 1 , warns )
128
+ self .assertEqual (warns [0 ].category , RuntimeWarning )
129
+ self .assertEqual (str (warns [0 ].message ),
130
+ "bool is used as a file descriptor" )
131
+
132
+ class FakeFile :
133
+ def __init__ (self , fd ):
134
+ self .fd = fd
135
+ def fileno (self ):
136
+ return self .fd
137
+
138
+ # file descriptor must be positive
139
+ with self .assertRaises (ValueError ):
140
+ asfd (- 1 )
141
+ with self .assertRaises (ValueError ):
142
+ asfd (FakeFile (- 1 ))
143
+
144
+ # fileno() result must be an integer
145
+ with self .assertRaises (TypeError ):
146
+ asfd (FakeFile ("text" ))
147
+
148
+ # unsupported types
149
+ for obj in ("string" , ["list" ], object ()):
150
+ with self .subTest (obj = obj ):
151
+ with self .assertRaises (TypeError ):
152
+ asfd (obj )
153
+
154
+ # CRASHES asfd(NULL)
155
+
156
+ def test_pyfile_newstdprinter (self ):
157
+ # Test PyFile_NewStdPrinter()
158
+ pyfile_newstdprinter = _testcapi .pyfile_newstdprinter
159
+ STDOUT_FD = 1
160
+
161
+ filename = os_helper .TESTFN
162
+ self .addCleanup (os_helper .unlink , filename )
163
+ old_stdout = os .dup (STDOUT_FD )
164
+ try :
165
+ with open (filename , "wb" ) as fp :
166
+ # PyFile_NewStdPrinter() only accepts fileno(stdout)
167
+ # or fileno(stderr) file descriptor.
168
+ fd = fp .fileno ()
169
+ os .dup2 (fd , STDOUT_FD )
170
+
171
+ file = pyfile_newstdprinter (STDOUT_FD )
172
+ self .assertEqual (file .closed , False )
173
+ self .assertIsNone (file .encoding )
174
+ self .assertEqual (file .mode , "w" )
175
+
176
+ self .assertEqual (file .fileno (), STDOUT_FD )
177
+ self .assertEqual (file .isatty (), False )
178
+
179
+ self .assertEqual (file .write ("text" ), 4 )
180
+ self .assertEqual (file .write ("[\uDC80 ]" ), 8 )
181
+
182
+ # flush() is a no-op
183
+ self .assertIsNone (file .flush ())
184
+
185
+ # close() is a no-op
186
+ self .assertIsNone (file .close ())
187
+ self .assertEqual (file .closed , False )
188
+
189
+ support .check_disallow_instantiation (self , type (file ))
190
+ finally :
191
+ os .dup2 (old_stdout , STDOUT_FD )
192
+
193
+ with open (filename , "r" ) as fp :
194
+ self .assertEqual (fp .read (), r"text[\udc80]" )
195
+
12
196
def test_py_fopen (self ):
13
197
# Test Py_fopen() and Py_fclose()
198
+ py_fopen = _testcapi .py_fopen
14
199
15
200
with open (__file__ , "rb" ) as fp :
16
201
source = fp .read ()
17
202
18
203
for filename in (__file__ , os .fsencode (__file__ )):
19
204
with self .subTest (filename = filename ):
20
- data = _testcapi . py_fopen (filename , "rb" )
205
+ data = py_fopen (filename , "rb" )
21
206
self .assertEqual (data , source [:256 ])
22
207
23
- data = _testcapi . py_fopen (os_helper .FakePath (filename ), "rb" )
208
+ data = py_fopen (os_helper .FakePath (filename ), "rb" )
24
209
self .assertEqual (data , source [:256 ])
25
210
26
211
filenames = [
@@ -43,41 +228,46 @@ def test_py_fopen(self):
43
228
filename = None
44
229
continue
45
230
try :
46
- data = _testcapi . py_fopen (filename , "rb" )
231
+ data = py_fopen (filename , "rb" )
47
232
self .assertEqual (data , source [:256 ])
48
233
finally :
49
234
os_helper .unlink (filename )
50
235
51
236
# embedded null character/byte in the filename
52
237
with self .assertRaises (ValueError ):
53
- _testcapi . py_fopen ("a\x00 b" , "rb" )
238
+ py_fopen ("a\x00 b" , "rb" )
54
239
with self .assertRaises (ValueError ):
55
- _testcapi . py_fopen (b"a\x00 b" , "rb" )
240
+ py_fopen (b"a\x00 b" , "rb" )
56
241
57
242
# non-ASCII mode failing with "Invalid argument"
58
243
with self .assertRaises (OSError ):
59
- _testcapi . py_fopen (__file__ , b"\xc2 \x80 " )
244
+ py_fopen (__file__ , b"\xc2 \x80 " )
60
245
with self .assertRaises (OSError ):
61
246
# \x98 is invalid in cp1250, cp1251, cp1257
62
247
# \x9d is invalid in cp1252-cp1255, cp1258
63
- _testcapi . py_fopen (__file__ , b"\xc2 \x98 \xc2 \x9d " )
248
+ py_fopen (__file__ , b"\xc2 \x98 \xc2 \x9d " )
64
249
# UnicodeDecodeError can come from the audit hook code
65
250
with self .assertRaises ((UnicodeDecodeError , OSError )):
66
- _testcapi . py_fopen (__file__ , b"\x98 \x9d " )
251
+ py_fopen (__file__ , b"\x98 \x9d " )
67
252
68
253
# invalid filename type
69
254
for invalid_type in (123 , object ()):
70
255
with self .subTest (filename = invalid_type ):
71
256
with self .assertRaises (TypeError ):
72
- _testcapi . py_fopen (invalid_type , "rb" )
257
+ py_fopen (invalid_type , "rb" )
73
258
74
259
if support .MS_WINDOWS :
75
260
with self .assertRaises (OSError ):
76
261
# On Windows, the file mode is limited to 10 characters
77
- _testcapi .py_fopen (__file__ , "rt+, ccs=UTF-8" )
262
+ py_fopen (__file__ , "rt+, ccs=UTF-8" )
263
+
264
+ # CRASHES py_fopen(NULL, 'rb')
265
+ # CRASHES py_fopen(__file__, NULL)
266
+
267
+ # TODO: Test Py_UniversalNewlineFgets()
78
268
79
- # CRASHES _testcapi.py_fopen(NULL, 'rb')
80
- # CRASHES _testcapi.py_fopen(__file__, NULL )
269
+ # PyFile_SetOpenCodeHook() and PyFile_OpenCode() are tested by
270
+ # test_embed.test_open_code_hook( )
81
271
82
272
83
273
if __name__ == "__main__" :
0 commit comments