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() which is a thin wrapper
19+ # to the built-in open() function
20+ pyfile_fromfd = _testlimitedcapi .pyfile_fromfd
21+ filename = __file__
22+ with open (filename , "rb" ) as fp :
23+ fd = fp .fileno ()
24+
25+ # FileIO
26+ fp .seek (0 )
27+ obj = pyfile_fromfd (fd , filename , "rb" , 0 , NULL , NULL , NULL , 0 )
28+ try :
29+ self .assertIsInstance (obj , _io .FileIO )
30+ self .assertEqual (obj .readline (), FIRST_LINE .encode ())
31+ finally :
32+ obj .close ()
33+
34+ # BufferedReader
35+ fp .seek (0 )
36+ obj = pyfile_fromfd (fd , filename , "rb" , 1024 , NULL , NULL , NULL , 0 )
37+ try :
38+ self .assertIsInstance (obj , _io .BufferedReader )
39+ self .assertEqual (obj .readline (), FIRST_LINE .encode ())
40+ finally :
41+ obj .close ()
42+
43+ # TextIOWrapper
44+ fp .seek (0 )
45+ obj = pyfile_fromfd (fd , filename , "r" , 1 ,
46+ "utf-8" , "replace" , NULL , 0 )
47+ try :
48+ self .assertIsInstance (obj , _io .TextIOWrapper )
49+ self .assertEqual (obj .encoding , "utf-8" )
50+ self .assertEqual (obj .errors , "replace" )
51+ self .assertEqual (obj .readline (), FIRST_LINE )
52+ finally :
53+ obj .close ()
54+
55+ def test_pyfile_getline (self ):
56+ # Test PyFile_GetLine(file, n): call file.readline()
57+ # and strip "\n" suffix if n < 0.
58+ pyfile_getline = _testlimitedcapi .pyfile_getline
59+
60+ # Test Unicode
61+ with open (__file__ , "r" ) as fp :
62+ fp .seek (0 )
63+ self .assertEqual (pyfile_getline (fp , - 1 ), FIRST_LINE .rstrip ())
64+ fp .seek (0 )
65+ self .assertEqual (pyfile_getline (fp , 0 ), FIRST_LINE )
66+ fp .seek (0 )
67+ self .assertEqual (pyfile_getline (fp , 6 ), FIRST_LINE [:6 ])
68+
69+ # Test bytes
70+ with open (__file__ , "rb" ) as fp :
71+ fp .seek (0 )
72+ self .assertEqual (pyfile_getline (fp , - 1 ),
73+ FIRST_LINE .rstrip ().encode ())
74+ fp .seek (0 )
75+ self .assertEqual (pyfile_getline (fp , 0 ), FIRST_LINE .encode ())
76+ fp .seek (0 )
77+ self .assertEqual (pyfile_getline (fp , 6 ), FIRST_LINE .encode ()[:6 ])
78+
79+ def test_pyfile_writestring (self ):
80+ # Test PyFile_WriteString(str, file): call file.write(str)
81+ writestr = _testlimitedcapi .pyfile_writestring
82+
83+ with io .StringIO () as fp :
84+ self .assertEqual (writestr ("a\xe9 \u20ac \U0010FFFF " .encode (), fp ), 0 )
85+ with self .assertRaises (UnicodeDecodeError ):
86+ writestr (b"\xff " , fp )
87+
88+ text = fp .getvalue ()
89+ self .assertEqual (text , "a\xe9 \u20ac \U0010FFFF " )
90+
91+ with self .assertRaises (SystemError ):
92+ writestr (b"abc" , NULL )
93+
94+ def test_pyfile_writeobject (self ):
95+ # Test PyFile_WriteObject(obj, file, flags):
96+ # - Call file.write(str(obj)) if flags equals Py_PRINT_RAW.
97+ # - Call file.write(repr(obj)) otherwise.
98+ writeobject = _testlimitedcapi .pyfile_writeobject
99+ Py_PRINT_RAW = 1
100+
101+ with io .StringIO () as fp :
102+ self .assertEqual (writeobject ("raw\n " , fp , Py_PRINT_RAW ), 0 )
103+ writeobject (NULL , fp , Py_PRINT_RAW )
104+
105+ self .assertEqual (writeobject ("repr" , fp , 0 ), 0 )
106+ writeobject (NULL , fp , 0 )
107+
108+ text = fp .getvalue ()
109+ self .assertEqual (text , "raw\n <NULL>'repr'<NULL>" )
110+
111+ # invalid file type
112+ for invalid_file in (123 , "abc" , object ()):
113+ with self .subTest (file = invalid_file ):
114+ with self .assertRaises (AttributeError ):
115+ writeobject ("abc" , invalid_file , Py_PRINT_RAW )
116+
117+ with self .assertRaises (TypeError ):
118+ writeobject ("abc" , NULL , 0 )
119+
120+ def test_pyobject_asfiledescriptor (self ):
121+ # Test PyObject_AsFileDescriptor(obj):
122+ # - Return obj if obj is an integer.
123+ # - Return obj.fileno() otherwise.
124+ asfd = _testlimitedcapi .pyobject_asfiledescriptor
125+
126+ self .assertEqual (asfd (123 ), 123 )
127+ self .assertEqual (asfd (0 ), 0 )
128+
129+ with open (__file__ , "rb" ) as fp :
130+ self .assertEqual (asfd (fp ), fp .fileno ())
131+
132+ # bool emits RuntimeWarning
133+ with warnings .catch_warnings (record = True ) as warns :
134+ warnings .simplefilter ('always' , RuntimeWarning )
135+ self .assertEqual (asfd (True ), 1 )
136+ self .assertEqual (len (warns ), 1 , warns )
137+ self .assertEqual (warns [0 ].category , RuntimeWarning )
138+ self .assertEqual (str (warns [0 ].message ),
139+ "bool is used as a file descriptor" )
140+
141+ class FakeFile :
142+ def __init__ (self , fd ):
143+ self .fd = fd
144+ def fileno (self ):
145+ return self .fd
146+
147+ # file descriptor must be positive
148+ with self .assertRaises (ValueError ):
149+ asfd (- 1 )
150+ with self .assertRaises (ValueError ):
151+ asfd (FakeFile (- 1 ))
152+
153+ # fileno() result must be an integer
154+ with self .assertRaises (TypeError ):
155+ asfd (FakeFile ("text" ))
156+
157+ # unsupported types
158+ for obj in ("string" , ["list" ], object ()):
159+ with self .subTest (obj = obj ):
160+ with self .assertRaises (TypeError ):
161+ asfd (obj )
162+
163+ # CRASHES asfd(NULL)
164+
165+ def test_pyfile_newstdprinter (self ):
166+ # Test PyFile_NewStdPrinter()
167+ pyfile_newstdprinter = _testcapi .pyfile_newstdprinter
168+ STDOUT_FD = 1
169+
170+ filename = os_helper .TESTFN
171+ self .addCleanup (os_helper .unlink , filename )
172+ old_stdout = os .dup (STDOUT_FD )
173+ try :
174+ with open (filename , "wb" ) as fp :
175+ # PyFile_NewStdPrinter() only accepts fileno(stdout)
176+ # or fileno(stderr) file descriptor.
177+ fd = fp .fileno ()
178+ os .dup2 (fd , STDOUT_FD )
179+
180+ file = pyfile_newstdprinter (STDOUT_FD )
181+ self .assertEqual (file .closed , False )
182+ self .assertIsNone (file .encoding )
183+ self .assertEqual (file .mode , "w" )
184+
185+ self .assertEqual (file .fileno (), STDOUT_FD )
186+ self .assertEqual (file .isatty (), False )
187+
188+ self .assertEqual (file .write ("text" ), 4 )
189+ self .assertEqual (file .write ("[\uDC80 ]" ), 8 )
190+
191+ # flush() is a no-op
192+ self .assertIsNone (file .flush ())
193+
194+ # close() is a no-op
195+ self .assertIsNone (file .close ())
196+ self .assertEqual (file .closed , False )
197+
198+ support .check_disallow_instantiation (self , type (file ))
199+ finally :
200+ os .dup2 (old_stdout , STDOUT_FD )
201+
202+ with open (filename , "r" ) as fp :
203+ self .assertEqual (fp .read (), r"text[\udc80]" )
204+
12205 def test_py_fopen (self ):
13206 # Test Py_fopen() and Py_fclose()
207+ py_fopen = _testcapi .py_fopen
14208
15209 with open (__file__ , "rb" ) as fp :
16210 source = fp .read ()
17211
18212 for filename in (__file__ , os .fsencode (__file__ )):
19213 with self .subTest (filename = filename ):
20- data = _testcapi . py_fopen (filename , "rb" )
214+ data = py_fopen (filename , "rb" )
21215 self .assertEqual (data , source [:256 ])
22216
23- data = _testcapi . py_fopen (os_helper .FakePath (filename ), "rb" )
217+ data = py_fopen (os_helper .FakePath (filename ), "rb" )
24218 self .assertEqual (data , source [:256 ])
25219
26220 filenames = [
@@ -43,41 +237,46 @@ def test_py_fopen(self):
43237 filename = None
44238 continue
45239 try :
46- data = _testcapi . py_fopen (filename , "rb" )
240+ data = py_fopen (filename , "rb" )
47241 self .assertEqual (data , source [:256 ])
48242 finally :
49243 os_helper .unlink (filename )
50244
51245 # embedded null character/byte in the filename
52246 with self .assertRaises (ValueError ):
53- _testcapi . py_fopen ("a\x00 b" , "rb" )
247+ py_fopen ("a\x00 b" , "rb" )
54248 with self .assertRaises (ValueError ):
55- _testcapi . py_fopen (b"a\x00 b" , "rb" )
249+ py_fopen (b"a\x00 b" , "rb" )
56250
57251 # non-ASCII mode failing with "Invalid argument"
58252 with self .assertRaises (OSError ):
59- _testcapi . py_fopen (__file__ , b"\xc2 \x80 " )
253+ py_fopen (__file__ , b"\xc2 \x80 " )
60254 with self .assertRaises (OSError ):
61255 # \x98 is invalid in cp1250, cp1251, cp1257
62256 # \x9d is invalid in cp1252-cp1255, cp1258
63- _testcapi . py_fopen (__file__ , b"\xc2 \x98 \xc2 \x9d " )
257+ py_fopen (__file__ , b"\xc2 \x98 \xc2 \x9d " )
64258 # UnicodeDecodeError can come from the audit hook code
65259 with self .assertRaises ((UnicodeDecodeError , OSError )):
66- _testcapi . py_fopen (__file__ , b"\x98 \x9d " )
260+ py_fopen (__file__ , b"\x98 \x9d " )
67261
68262 # invalid filename type
69263 for invalid_type in (123 , object ()):
70264 with self .subTest (filename = invalid_type ):
71265 with self .assertRaises (TypeError ):
72- _testcapi . py_fopen (invalid_type , "rb" )
266+ py_fopen (invalid_type , "rb" )
73267
74268 if support .MS_WINDOWS :
75269 with self .assertRaises (OSError ):
76270 # On Windows, the file mode is limited to 10 characters
77- _testcapi .py_fopen (__file__ , "rt+, ccs=UTF-8" )
271+ py_fopen (__file__ , "rt+, ccs=UTF-8" )
272+
273+ # CRASHES py_fopen(NULL, 'rb')
274+ # CRASHES py_fopen(__file__, NULL)
275+
276+ # TODO: Test Py_UniversalNewlineFgets()
78277
79- # CRASHES _testcapi.py_fopen(NULL, 'rb')
80- # CRASHES _testcapi.py_fopen(__file__, NULL )
278+ # PyFile_SetOpenCodeHook() and PyFile_OpenCode() are tested by
279+ # test_embed.test_open_code_hook( )
81280
82281
83282if __name__ == "__main__" :
0 commit comments