1+ import  io 
12import  os 
23import  unittest 
4+ import  warnings 
35from  test  import  support 
4- from  test .support  import  import_helper , os_helper 
6+ from  test .support  import  import_helper , os_helper ,  warnings_helper 
57
6- _testcapi  =  import_helper .import_module ('_testcapi' )
78
9+ _testcapi  =  import_helper .import_module ('_testcapi' )
10+ _testlimitedcapi  =  import_helper .import_module ('_testlimitedcapi' )
11+ _io  =  import_helper .import_module ('_io' )
812NULL  =  None 
13+ STDOUT_FD  =  1 
14+ 
15+ with  open (__file__ , 'rb' ) as  fp :
16+     FIRST_LINE  =  next (fp ).decode ()
17+ FIRST_LINE_NORM  =  FIRST_LINE .rstrip () +  '\n ' 
918
1019
1120class  CAPIFileTest (unittest .TestCase ):
21+     def  test_pyfile_fromfd (self ):
22+         # Test PyFile_FromFd() which is a thin wrapper to _io.open() 
23+         pyfile_fromfd  =  _testlimitedcapi .pyfile_fromfd 
24+         filename  =  __file__ 
25+         with  open (filename , "rb" ) as  fp :
26+             fd  =  fp .fileno ()
27+ 
28+             # FileIO 
29+             fp .seek (0 )
30+             obj  =  pyfile_fromfd (fd , filename , "rb" , 0 , NULL , NULL , NULL , 0 )
31+             try :
32+                 self .assertIsInstance (obj , _io .FileIO )
33+                 self .assertEqual (obj .readline (), FIRST_LINE .encode ())
34+             finally :
35+                 obj .close ()
36+ 
37+             # BufferedReader 
38+             fp .seek (0 )
39+             obj  =  pyfile_fromfd (fd , filename , "rb" , 1024 , NULL , NULL , NULL , 0 )
40+             try :
41+                 self .assertIsInstance (obj , _io .BufferedReader )
42+                 self .assertEqual (obj .readline (), FIRST_LINE .encode ())
43+             finally :
44+                 obj .close ()
45+ 
46+             # TextIOWrapper 
47+             fp .seek (0 )
48+             obj  =  pyfile_fromfd (fd , filename , "r" , 1 ,
49+                                 "utf-8" , "replace" , NULL , 0 )
50+             try :
51+                 self .assertIsInstance (obj , _io .TextIOWrapper )
52+                 self .assertEqual (obj .encoding , "utf-8" )
53+                 self .assertEqual (obj .errors , "replace" )
54+                 self .assertEqual (obj .readline (), FIRST_LINE_NORM )
55+             finally :
56+                 obj .close ()
57+ 
58+     def  test_pyfile_getline (self ):
59+         # Test PyFile_GetLine(file, n): call file.readline() 
60+         # and strip "\n" suffix if n < 0. 
61+         pyfile_getline  =  _testlimitedcapi .pyfile_getline 
62+ 
63+         # Test Unicode 
64+         with  open (__file__ , "r" ) as  fp :
65+             fp .seek (0 )
66+             self .assertEqual (pyfile_getline (fp , - 1 ),
67+                              FIRST_LINE_NORM .rstrip ('\n ' ))
68+             fp .seek (0 )
69+             self .assertEqual (pyfile_getline (fp , 0 ),
70+                              FIRST_LINE_NORM )
71+             fp .seek (0 )
72+             self .assertEqual (pyfile_getline (fp , 6 ),
73+                              FIRST_LINE_NORM [:6 ])
74+ 
75+         # Test bytes 
76+         with  open (__file__ , "rb" ) as  fp :
77+             fp .seek (0 )
78+             self .assertEqual (pyfile_getline (fp , - 1 ),
79+                              FIRST_LINE .rstrip ('\n ' ).encode ())
80+             fp .seek (0 )
81+             self .assertEqual (pyfile_getline (fp , 0 ),
82+                              FIRST_LINE .encode ())
83+             fp .seek (0 )
84+             self .assertEqual (pyfile_getline (fp , 6 ),
85+                              FIRST_LINE .encode ()[:6 ])
86+ 
87+     def  test_pyfile_writestring (self ):
88+         # Test PyFile_WriteString(str, file): call file.write(str) 
89+         writestr  =  _testlimitedcapi .pyfile_writestring 
90+ 
91+         with  io .StringIO () as  fp :
92+             self .assertEqual (writestr ("a\xe9 \u20ac \U0010FFFF " .encode (), fp ), 0 )
93+             with  self .assertRaises (UnicodeDecodeError ):
94+                 writestr (b"\xff " , fp )
95+             with  self .assertRaises (UnicodeDecodeError ):
96+                 writestr ("\udc80 " .encode ("utf-8" , "surrogatepass" ), fp )
97+ 
98+             text  =  fp .getvalue ()
99+             self .assertEqual (text , "a\xe9 \u20ac \U0010FFFF " )
100+ 
101+         with  self .assertRaises (SystemError ):
102+             writestr (b"abc" , NULL )
103+ 
104+     def  test_pyfile_writeobject (self ):
105+         # Test PyFile_WriteObject(obj, file, flags): 
106+         # - Call file.write(str(obj)) if flags equals Py_PRINT_RAW. 
107+         # - Call file.write(repr(obj)) otherwise. 
108+         writeobject  =  _testlimitedcapi .pyfile_writeobject 
109+         Py_PRINT_RAW  =  1 
110+ 
111+         with  io .StringIO () as  fp :
112+             # Test flags=Py_PRINT_RAW 
113+             self .assertEqual (writeobject ("raw" , fp , Py_PRINT_RAW ), 0 )
114+             writeobject (NULL , fp , Py_PRINT_RAW )
115+ 
116+             # Test flags=0 
117+             self .assertEqual (writeobject ("repr" , fp , 0 ), 0 )
118+             writeobject (NULL , fp , 0 )
119+ 
120+             text  =  fp .getvalue ()
121+             self .assertEqual (text , "raw<NULL>'repr'<NULL>" )
122+ 
123+         # invalid file type 
124+         for  invalid_file  in  (123 , "abc" , object ()):
125+             with  self .subTest (file = invalid_file ):
126+                 with  self .assertRaises (AttributeError ):
127+                     writeobject ("abc" , invalid_file , Py_PRINT_RAW )
128+ 
129+         with  self .assertRaises (TypeError ):
130+             writeobject ("abc" , NULL , 0 )
131+ 
132+     def  test_pyobject_asfiledescriptor (self ):
133+         # Test PyObject_AsFileDescriptor(obj): 
134+         # - Return obj if obj is an integer. 
135+         # - Return obj.fileno() otherwise. 
136+         # File descriptor must be >= 0. 
137+         asfd  =  _testlimitedcapi .pyobject_asfiledescriptor 
138+ 
139+         self .assertEqual (asfd (123 ), 123 )
140+         self .assertEqual (asfd (0 ), 0 )
141+ 
142+         with  open (__file__ , "rb" ) as  fp :
143+             self .assertEqual (asfd (fp ), fp .fileno ())
144+ 
145+         # bool emits RuntimeWarning 
146+         msg  =  r"bool is used as a file descriptor" 
147+         with  warnings_helper .check_warnings ((msg , RuntimeWarning )):
148+             self .assertEqual (asfd (True ), 1 )
149+ 
150+         class  FakeFile :
151+             def  __init__ (self , fd ):
152+                 self .fd  =  fd 
153+             def  fileno (self ):
154+                 return  self .fd 
155+ 
156+         # file descriptor must be positive 
157+         with  self .assertRaises (ValueError ):
158+             asfd (- 1 )
159+         with  self .assertRaises (ValueError ):
160+             asfd (FakeFile (- 1 ))
161+ 
162+         # fileno() result must be an integer 
163+         with  self .assertRaises (TypeError ):
164+             asfd (FakeFile ("text" ))
165+ 
166+         # unsupported types 
167+         for  obj  in  ("string" , ["list" ], object ()):
168+             with  self .subTest (obj = obj ):
169+                 with  self .assertRaises (TypeError ):
170+                     asfd (obj )
171+ 
172+         # CRASHES asfd(NULL) 
173+ 
174+     def  test_pyfile_newstdprinter (self ):
175+         # Test PyFile_NewStdPrinter() 
176+         pyfile_newstdprinter  =  _testcapi .pyfile_newstdprinter 
177+ 
178+         file  =  pyfile_newstdprinter (STDOUT_FD )
179+         self .assertEqual (file .closed , False )
180+         self .assertIsNone (file .encoding )
181+         self .assertEqual (file .mode , "w" )
182+ 
183+         self .assertEqual (file .fileno (), STDOUT_FD )
184+         self .assertEqual (file .isatty (), os .isatty (STDOUT_FD ))
185+ 
186+         # flush() is a no-op 
187+         self .assertIsNone (file .flush ())
188+ 
189+         # close() is a no-op 
190+         self .assertIsNone (file .close ())
191+         self .assertEqual (file .closed , False )
192+ 
193+         support .check_disallow_instantiation (self , type (file ))
194+ 
195+     def  test_pyfile_newstdprinter_write (self ):
196+         # Test the write() method of PyFile_NewStdPrinter() 
197+         pyfile_newstdprinter  =  _testcapi .pyfile_newstdprinter 
198+ 
199+         filename  =  os_helper .TESTFN 
200+         self .addCleanup (os_helper .unlink , filename )
201+ 
202+         try :
203+             old_stdout  =  os .dup (STDOUT_FD )
204+         except  OSError  as  exc :
205+             # os.dup(STDOUT_FD) is not supported on WASI 
206+             self .skipTest (f"os.dup() failed with { exc !r}  )
207+ 
208+         try :
209+             with  open (filename , "wb" ) as  fp :
210+                 # PyFile_NewStdPrinter() only accepts fileno(stdout) 
211+                 # or fileno(stderr) file descriptor. 
212+                 fd  =  fp .fileno ()
213+                 os .dup2 (fd , STDOUT_FD )
214+ 
215+                 file  =  pyfile_newstdprinter (STDOUT_FD )
216+                 self .assertEqual (file .write ("text" ), 4 )
217+                 # The surrogate character is encoded with 
218+                 # the "surrogateescape" error handler 
219+                 self .assertEqual (file .write ("[\udc80 ]" ), 8 )
220+         finally :
221+             os .dup2 (old_stdout , STDOUT_FD )
222+             os .close (old_stdout )
223+ 
224+         with  open (filename , "r" ) as  fp :
225+             self .assertEqual (fp .read (), "text[\\ udc80]" )
226+ 
12227    def  test_py_fopen (self ):
13228        # Test Py_fopen() and Py_fclose() 
229+         py_fopen  =  _testcapi .py_fopen 
14230
15231        with  open (__file__ , "rb" ) as  fp :
16232            source  =  fp .read ()
17233
18234        for  filename  in  (__file__ , os .fsencode (__file__ )):
19235            with  self .subTest (filename = filename ):
20-                 data  =  _testcapi . py_fopen (filename , "rb" )
236+                 data  =  py_fopen (filename , "rb" )
21237                self .assertEqual (data , source [:256 ])
22238
23-                 data  =  _testcapi . py_fopen (os_helper .FakePath (filename ), "rb" )
239+                 data  =  py_fopen (os_helper .FakePath (filename ), "rb" )
24240                self .assertEqual (data , source [:256 ])
25241
26242        filenames  =  [
@@ -43,41 +259,46 @@ def test_py_fopen(self):
43259                    filename  =  None 
44260                    continue 
45261                try :
46-                     data  =  _testcapi . py_fopen (filename , "rb" )
262+                     data  =  py_fopen (filename , "rb" )
47263                    self .assertEqual (data , source [:256 ])
48264                finally :
49265                    os_helper .unlink (filename )
50266
51267        # embedded null character/byte in the filename 
52268        with  self .assertRaises (ValueError ):
53-             _testcapi . py_fopen ("a\x00 b" , "rb" )
269+             py_fopen ("a\x00 b" , "rb" )
54270        with  self .assertRaises (ValueError ):
55-             _testcapi . py_fopen (b"a\x00 b" , "rb" )
271+             py_fopen (b"a\x00 b" , "rb" )
56272
57273        # non-ASCII mode failing with "Invalid argument" 
58274        with  self .assertRaises (OSError ):
59-             _testcapi . py_fopen (__file__ , b"\xc2 \x80 " )
275+             py_fopen (__file__ , b"\xc2 \x80 " )
60276        with  self .assertRaises (OSError ):
61277            # \x98 is invalid in cp1250, cp1251, cp1257 
62278            # \x9d is invalid in cp1252-cp1255, cp1258 
63-             _testcapi . py_fopen (__file__ , b"\xc2 \x98 \xc2 \x9d " )
279+             py_fopen (__file__ , b"\xc2 \x98 \xc2 \x9d " )
64280        # UnicodeDecodeError can come from the audit hook code 
65281        with  self .assertRaises ((UnicodeDecodeError , OSError )):
66-             _testcapi . py_fopen (__file__ , b"\x98 \x9d " )
282+             py_fopen (__file__ , b"\x98 \x9d " )
67283
68284        # invalid filename type 
69285        for  invalid_type  in  (123 , object ()):
70286            with  self .subTest (filename = invalid_type ):
71287                with  self .assertRaises (TypeError ):
72-                     _testcapi . py_fopen (invalid_type , "rb" )
288+                     py_fopen (invalid_type , "rb" )
73289
74290        if  support .MS_WINDOWS :
75291            with  self .assertRaises (OSError ):
76292                # On Windows, the file mode is limited to 10 characters 
77-                 _testcapi .py_fopen (__file__ , "rt+, ccs=UTF-8" )
293+                 py_fopen (__file__ , "rt+, ccs=UTF-8" )
294+ 
295+         # CRASHES py_fopen(NULL, 'rb') 
296+         # CRASHES py_fopen(__file__, NULL) 
297+ 
298+     # TODO: Test Py_UniversalNewlineFgets() 
78299
79-          # CRASHES _testcapi.py_fopen(NULL, 'rb') 
80-          # CRASHES _testcapi.py_fopen(__file__, NULL )
300+     # PyFile_SetOpenCodeHook() and PyFile_OpenCode() are tested by 
301+     # test_embed.test_open_code_hook( )
81302
82303
83304if  __name__  ==  "__main__" :
0 commit comments