1+ import io
12import os
23import unittest
4+ import warnings
35from test import support
46from test .support import import_helper , os_helper
57
6- _testcapi = import_helper .import_module ('_testcapi' )
78
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' )
813NULL = None
914
1015
1116class 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+
12196 def test_py_fopen (self ):
13197 # Test Py_fopen() and Py_fclose()
198+ py_fopen = _testcapi .py_fopen
14199
15200 with open (__file__ , "rb" ) as fp :
16201 source = fp .read ()
17202
18203 for filename in (__file__ , os .fsencode (__file__ )):
19204 with self .subTest (filename = filename ):
20- data = _testcapi . py_fopen (filename , "rb" )
205+ data = py_fopen (filename , "rb" )
21206 self .assertEqual (data , source [:256 ])
22207
23- data = _testcapi . py_fopen (os_helper .FakePath (filename ), "rb" )
208+ data = py_fopen (os_helper .FakePath (filename ), "rb" )
24209 self .assertEqual (data , source [:256 ])
25210
26211 filenames = [
@@ -43,41 +228,46 @@ def test_py_fopen(self):
43228 filename = None
44229 continue
45230 try :
46- data = _testcapi . py_fopen (filename , "rb" )
231+ data = py_fopen (filename , "rb" )
47232 self .assertEqual (data , source [:256 ])
48233 finally :
49234 os_helper .unlink (filename )
50235
51236 # embedded null character/byte in the filename
52237 with self .assertRaises (ValueError ):
53- _testcapi . py_fopen ("a\x00 b" , "rb" )
238+ py_fopen ("a\x00 b" , "rb" )
54239 with self .assertRaises (ValueError ):
55- _testcapi . py_fopen (b"a\x00 b" , "rb" )
240+ py_fopen (b"a\x00 b" , "rb" )
56241
57242 # non-ASCII mode failing with "Invalid argument"
58243 with self .assertRaises (OSError ):
59- _testcapi . py_fopen (__file__ , b"\xc2 \x80 " )
244+ py_fopen (__file__ , b"\xc2 \x80 " )
60245 with self .assertRaises (OSError ):
61246 # \x98 is invalid in cp1250, cp1251, cp1257
62247 # \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 " )
64249 # UnicodeDecodeError can come from the audit hook code
65250 with self .assertRaises ((UnicodeDecodeError , OSError )):
66- _testcapi . py_fopen (__file__ , b"\x98 \x9d " )
251+ py_fopen (__file__ , b"\x98 \x9d " )
67252
68253 # invalid filename type
69254 for invalid_type in (123 , object ()):
70255 with self .subTest (filename = invalid_type ):
71256 with self .assertRaises (TypeError ):
72- _testcapi . py_fopen (invalid_type , "rb" )
257+ py_fopen (invalid_type , "rb" )
73258
74259 if support .MS_WINDOWS :
75260 with self .assertRaises (OSError ):
76261 # 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()
78268
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( )
81271
82272
83273if __name__ == "__main__" :
0 commit comments