@@ -205,62 +205,84 @@ def test_excepthook(self):
205
205
# Python/pythonrun.c::PyErr_PrintEx() is tricky.
206
206
207
207
208
+ def raise_system_exit (* args , ** kwargs ):
209
+ raise SystemExit (* args , ** kwargs )
210
+
211
+
208
212
class SysModuleTest (unittest .TestCase ):
209
213
210
214
def tearDown (self ):
211
215
test .support .reap_children ()
212
216
213
217
def test_exit (self ):
214
- # call with two arguments
218
+ # call with two arguments is only forbidden for sys.exit()
215
219
self .assertRaises (TypeError , sys .exit , 42 , 42 )
220
+ with self .subTest ('sys.exit' ):
221
+ self .do_test_exit (sys .exit )
222
+ with self .subTest ('raise SystemExit' ):
223
+ self .do_test_exit (raise_system_exit )
216
224
225
+ def do_test_exit (self , sys_exit_raiser ):
217
226
# call without argument
218
227
with self .assertRaises (SystemExit ) as cm :
219
- sys . exit ()
228
+ sys_exit_raiser ()
220
229
self .assertIsNone (cm .exception .code )
230
+ with self .assertRaises (SystemExit ) as cm :
231
+ sys_exit_raiser (None )
232
+ self .assertIsNone (cm .exception .code )
233
+
234
+ # call with integer argument
235
+ with self .assertRaises (SystemExit ) as cm :
236
+ sys_exit_raiser (42 )
237
+ self .assertEqual (cm .exception .code , 42 )
238
+
239
+ # gh-133548: call with tuple argument with one entry
240
+ with self .assertRaises (SystemExit ) as cm :
241
+ sys_exit_raiser ((42 ,))
242
+ self .assertEqual (cm .exception .code , (42 ,))
243
+
244
+ # call with string argument
245
+ with self .assertRaises (SystemExit ) as cm :
246
+ sys_exit_raiser ("exit" )
247
+ self .assertEqual (cm .exception .code , "exit" )
248
+
249
+ # call with tuple argument with two entries
250
+ with self .assertRaises (SystemExit ) as cm :
251
+ sys_exit_raiser ((42 , 42 ))
252
+ self .assertEqual (cm .exception .args , ((42 , 42 ),))
253
+ self .assertEqual (cm .exception .code , (42 , 42 ))
254
+
255
+ def test_exit_message (self ):
256
+ with self .subTest ('sys.exit' ):
257
+ self .do_test_exit_message ("sys.exit" )
258
+ with self .subTest ('raise SystemExit' ):
259
+ self .do_test_exit_message ("raise SystemExit" )
260
+
261
+ def do_test_exit_message (self , call_statement ):
262
+ def sys_exit_impl (value = '' , prolog = '' ):
263
+ return f'import sys\n { prolog } \n { call_statement } ({ value } )'
221
264
222
- rc , out , err = assert_python_ok ('-c' , 'import sys; sys.exit()' )
265
+ rc , out , err = assert_python_ok ('-c' , sys_exit_impl () )
223
266
self .assertEqual (rc , 0 )
224
267
self .assertEqual (out , b'' )
225
268
self .assertEqual (err , b'' )
226
269
227
270
# gh-125842: Windows uses 32-bit unsigned integers for exit codes
228
271
# so a -1 exit code is sometimes interpreted as 0xffff_ffff.
229
- rc , out , err = assert_python_failure ('-c' , 'import sys; sys.exit (0xffff_ffff)' )
272
+ rc , out , err = assert_python_failure ('-c' , sys_exit_impl (0xffff_ffff ))
230
273
self .assertIn (rc , (- 1 , 0xff , 0xffff_ffff ))
231
274
self .assertEqual (out , b'' )
232
275
self .assertEqual (err , b'' )
233
276
234
277
# Overflow results in a -1 exit code, which may be converted to 0xff
235
278
# or 0xffff_ffff.
236
- rc , out , err = assert_python_failure ('-c' , 'import sys; sys.exit (2**128)' )
279
+ rc , out , err = assert_python_failure ('-c' , sys_exit_impl (2 ** 128 ))
237
280
self .assertIn (rc , (- 1 , 0xff , 0xffff_ffff ))
238
281
self .assertEqual (out , b'' )
239
282
self .assertEqual (err , b'' )
240
283
241
- # call with integer argument
242
- with self .assertRaises (SystemExit ) as cm :
243
- sys .exit (42 )
244
- self .assertEqual (cm .exception .code , 42 )
245
-
246
- # call with tuple argument with one entry
247
- # entry will be unpacked
248
- with self .assertRaises (SystemExit ) as cm :
249
- sys .exit ((42 ,))
250
- self .assertEqual (cm .exception .code , 42 )
251
-
252
- # call with string argument
253
- with self .assertRaises (SystemExit ) as cm :
254
- sys .exit ("exit" )
255
- self .assertEqual (cm .exception .code , "exit" )
256
-
257
- # call with tuple argument with two entries
258
- with self .assertRaises (SystemExit ) as cm :
259
- sys .exit ((17 , 23 ))
260
- self .assertEqual (cm .exception .code , (17 , 23 ))
261
-
262
- # test that the exit machinery handles SystemExits properly
263
- rc , out , err = assert_python_failure ('-c' , 'raise SystemExit(47)' )
284
+ # test that the exit machinery handles custom codes properly
285
+ rc , out , err = assert_python_failure ('-c' , sys_exit_impl (47 ))
264
286
self .assertEqual (rc , 47 )
265
287
self .assertEqual (out , b'' )
266
288
self .assertEqual (err , b'' )
@@ -274,20 +296,20 @@ def check_exit_message(code, expected, **env_vars):
274
296
# test that stderr buffer is flushed before the exit message is written
275
297
# into stderr
276
298
check_exit_message (
277
- r'import sys; sys.stderr.write("unflushed,"); sys.exit("message")' ,
278
- b"unflushed,message" )
299
+ sys_exit_impl ("'message'" , 'sys.stderr.write("unflushed,")' ),
300
+ b"unflushed,message"
301
+ )
279
302
280
303
# test that the exit message is written with backslashreplace error
281
304
# handler to stderr
282
- check_exit_message (
283
- r'import sys; sys.exit("surrogates:\uDCFF")' ,
284
- b"surrogates:\\ udcff" )
305
+ check_exit_message (sys_exit_impl (r"'surrogates:\uDCFF'" ),
306
+ b"surrogates:\\ udcff" )
285
307
286
308
# test that the unicode message is encoded to the stderr encoding
287
309
# instead of the default encoding (utf8)
288
- check_exit_message (
289
- r'import sys; sys.exit("h\xe9")' ,
290
- b"h \xe9 " , PYTHONIOENCODING = 'latin-1' )
310
+ check_exit_message (sys_exit_impl ( r"'h\xe9'" ), b"h \xe9 " ,
311
+ PYTHONIOENCODING = 'latin-1' )
312
+
291
313
292
314
@support .requires_subprocess ()
293
315
def test_exit_codes_under_repl (self ):
0 commit comments