1 # Tests invocation of the interpreter with various command line arguments
2 # Most tests are executed with environment variables ignored
3 # See test_cmd_line_script.py for testing of script execution
4
5 import os
6 import subprocess
7 import sys
8 import tempfile
9 import textwrap
10 import unittest
11 from test import support
12 from test.support import os_helper
13 from test.support.script_helper import (
14 spawn_python, kill_python, assert_python_ok, assert_python_failure,
15 interpreter_requires_environment
16 )
17
18 if not support.has_subprocess_support:
19 raise unittest.SkipTest("test module requires subprocess")
20
21 # Debug build?
22 Py_DEBUG = hasattr(sys, "gettotalrefcount")
23
24
25 # XXX (ncoghlan): Move to script_helper and make consistent with run_python
26 def _kill_python_and_exit_code(p):
27 data = kill_python(p)
28 returncode = p.wait()
29 return data, returncode
30
31
32 class ESC[4;38;5;81mCmdLineTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
33 def test_directories(self):
34 assert_python_failure('.')
35 assert_python_failure('< .')
36
37 def verify_valid_flag(self, cmd_line):
38 rc, out, err = assert_python_ok(cmd_line)
39 self.assertTrue(out == b'' or out.endswith(b'\n'))
40 self.assertNotIn(b'Traceback', out)
41 self.assertNotIn(b'Traceback', err)
42 return out
43
44 def test_help(self):
45 self.verify_valid_flag('-h')
46 self.verify_valid_flag('-?')
47 out = self.verify_valid_flag('--help')
48 lines = out.splitlines()
49 self.assertIn(b'usage', lines[0])
50 self.assertNotIn(b'PYTHONHOME', out)
51 self.assertNotIn(b'-X dev', out)
52 self.assertLess(len(lines), 50)
53
54 def test_help_env(self):
55 out = self.verify_valid_flag('--help-env')
56 self.assertIn(b'PYTHONHOME', out)
57
58 def test_help_xoptions(self):
59 out = self.verify_valid_flag('--help-xoptions')
60 self.assertIn(b'-X dev', out)
61
62 def test_help_all(self):
63 out = self.verify_valid_flag('--help-all')
64 lines = out.splitlines()
65 self.assertIn(b'usage', lines[0])
66 self.assertIn(b'PYTHONHOME', out)
67 self.assertIn(b'-X dev', out)
68
69 # The first line contains the program name,
70 # but the rest should be ASCII-only
71 b''.join(lines[1:]).decode('ascii')
72
73 def test_optimize(self):
74 self.verify_valid_flag('-O')
75 self.verify_valid_flag('-OO')
76
77 def test_site_flag(self):
78 self.verify_valid_flag('-S')
79
80 def test_version(self):
81 version = ('Python %d.%d' % sys.version_info[:2]).encode("ascii")
82 for switch in '-V', '--version', '-VV':
83 rc, out, err = assert_python_ok(switch)
84 self.assertFalse(err.startswith(version))
85 self.assertTrue(out.startswith(version))
86
87 def test_verbose(self):
88 # -v causes imports to write to stderr. If the write to
89 # stderr itself causes an import to happen (for the output
90 # codec), a recursion loop can occur.
91 rc, out, err = assert_python_ok('-v')
92 self.assertNotIn(b'stack overflow', err)
93 rc, out, err = assert_python_ok('-vv')
94 self.assertNotIn(b'stack overflow', err)
95
96 @unittest.skipIf(interpreter_requires_environment(),
97 'Cannot run -E tests when PYTHON env vars are required.')
98 def test_xoptions(self):
99 def get_xoptions(*args):
100 # use subprocess module directly because test.support.script_helper adds
101 # "-X faulthandler" to the command line
102 args = (sys.executable, '-E') + args
103 args += ('-c', 'import sys; print(sys._xoptions)')
104 out = subprocess.check_output(args)
105 opts = eval(out.splitlines()[0])
106 return opts
107
108 opts = get_xoptions()
109 self.assertEqual(opts, {})
110
111 opts = get_xoptions('-Xa', '-Xb=c,d=e')
112 self.assertEqual(opts, {'a': True, 'b': 'c,d=e'})
113
114 def test_showrefcount(self):
115 def run_python(*args):
116 # this is similar to assert_python_ok but doesn't strip
117 # the refcount from stderr. It can be replaced once
118 # assert_python_ok stops doing that.
119 cmd = [sys.executable]
120 cmd.extend(args)
121 PIPE = subprocess.PIPE
122 p = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE)
123 out, err = p.communicate()
124 p.stdout.close()
125 p.stderr.close()
126 rc = p.returncode
127 self.assertEqual(rc, 0)
128 return rc, out, err
129 code = 'import sys; print(sys._xoptions)'
130 # normally the refcount is hidden
131 rc, out, err = run_python('-c', code)
132 self.assertEqual(out.rstrip(), b'{}')
133 self.assertEqual(err, b'')
134 # "-X showrefcount" shows the refcount, but only in debug builds
135 rc, out, err = run_python('-I', '-X', 'showrefcount', '-c', code)
136 self.assertEqual(out.rstrip(), b"{'showrefcount': True}")
137 if Py_DEBUG:
138 # bpo-46417: Tolerate negative reference count which can occur
139 # because of bugs in C extensions. This test is only about checking
140 # the showrefcount feature.
141 self.assertRegex(err, br'^\[-?\d+ refs, \d+ blocks\]')
142 else:
143 self.assertEqual(err, b'')
144
145 def test_xoption_frozen_modules(self):
146 tests = {
147 ('=on', 'FrozenImporter'),
148 ('=off', 'SourceFileLoader'),
149 ('=', 'FrozenImporter'),
150 ('', 'FrozenImporter'),
151 }
152 for raw, expected in tests:
153 cmd = ['-X', f'frozen_modules{raw}',
154 '-c', 'import os; print(os.__spec__.loader, end="")']
155 with self.subTest(raw):
156 res = assert_python_ok(*cmd)
157 self.assertRegex(res.out.decode('utf-8'), expected)
158
159 def test_run_module(self):
160 # Test expected operation of the '-m' switch
161 # Switch needs an argument
162 assert_python_failure('-m')
163 # Check we get an error for a nonexistent module
164 assert_python_failure('-m', 'fnord43520xyz')
165 # Check the runpy module also gives an error for
166 # a nonexistent module
167 assert_python_failure('-m', 'runpy', 'fnord43520xyz')
168 # All good if module is located and run successfully
169 assert_python_ok('-m', 'timeit', '-n', '1')
170
171 def test_run_module_bug1764407(self):
172 # -m and -i need to play well together
173 # Runs the timeit module and checks the __main__
174 # namespace has been populated appropriately
175 p = spawn_python('-i', '-m', 'timeit', '-n', '1')
176 p.stdin.write(b'Timer\n')
177 p.stdin.write(b'exit()\n')
178 data = kill_python(p)
179 self.assertTrue(data.find(b'1 loop') != -1)
180 self.assertTrue(data.find(b'__main__.Timer') != -1)
181
182 def test_relativedir_bug46421(self):
183 # Test `python -m unittest` with a relative directory beginning with ./
184 # Note: We have to switch to the project's top module's directory, as per
185 # the python unittest wiki. We will switch back when we are done.
186 projectlibpath = os.path.dirname(__file__).removesuffix("test")
187 with os_helper.change_cwd(projectlibpath):
188 # Testing with and without ./
189 assert_python_ok('-m', 'unittest', "test/test_longexp.py")
190 assert_python_ok('-m', 'unittest', "./test/test_longexp.py")
191
192 def test_run_code(self):
193 # Test expected operation of the '-c' switch
194 # Switch needs an argument
195 assert_python_failure('-c')
196 # Check we get an error for an uncaught exception
197 assert_python_failure('-c', 'raise Exception')
198 # All good if execution is successful
199 assert_python_ok('-c', 'pass')
200
201 @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII')
202 def test_non_ascii(self):
203 # Test handling of non-ascii data
204 command = ("assert(ord(%r) == %s)"
205 % (os_helper.FS_NONASCII, ord(os_helper.FS_NONASCII)))
206 assert_python_ok('-c', command)
207
208 @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII')
209 def test_coding(self):
210 # bpo-32381: the -c command ignores the coding cookie
211 ch = os_helper.FS_NONASCII
212 cmd = f"# coding: latin1\nprint(ascii('{ch}'))"
213 res = assert_python_ok('-c', cmd)
214 self.assertEqual(res.out.rstrip(), ascii(ch).encode('ascii'))
215
216 # On Windows, pass bytes to subprocess doesn't test how Python decodes the
217 # command line, but how subprocess does decode bytes to unicode. Python
218 # doesn't decode the command line because Windows provides directly the
219 # arguments as unicode (using wmain() instead of main()).
220 @unittest.skipIf(sys.platform == 'win32',
221 'Windows has a native unicode API')
222 def test_undecodable_code(self):
223 undecodable = b"\xff"
224 env = os.environ.copy()
225 # Use C locale to get ascii for the locale encoding
226 env['LC_ALL'] = 'C'
227 env['PYTHONCOERCECLOCALE'] = '0'
228 code = (
229 b'import locale; '
230 b'print(ascii("' + undecodable + b'"), '
231 b'locale.getencoding())')
232 p = subprocess.Popen(
233 [sys.executable, "-c", code],
234 stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
235 env=env)
236 stdout, stderr = p.communicate()
237 if p.returncode == 1:
238 # _Py_char2wchar() decoded b'\xff' as '\udcff' (b'\xff' is not
239 # decodable from ASCII) and run_command() failed on
240 # PyUnicode_AsUTF8String(). This is the expected behaviour on
241 # Linux.
242 pattern = b"Unable to decode the command from the command line:"
243 elif p.returncode == 0:
244 # _Py_char2wchar() decoded b'\xff' as '\xff' even if the locale is
245 # C and the locale encoding is ASCII. It occurs on FreeBSD, Solaris
246 # and Mac OS X.
247 pattern = b"'\\xff' "
248 # The output is followed by the encoding name, an alias to ASCII.
249 # Examples: "US-ASCII" or "646" (ISO 646, on Solaris).
250 else:
251 raise AssertionError("Unknown exit code: %s, output=%a" % (p.returncode, stdout))
252 if not stdout.startswith(pattern):
253 raise AssertionError("%a doesn't start with %a" % (stdout, pattern))
254
255 @unittest.skipIf(sys.platform == 'win32',
256 'Windows has a native unicode API')
257 def test_invalid_utf8_arg(self):
258 # bpo-35883: Py_DecodeLocale() must escape b'\xfd\xbf\xbf\xbb\xba\xba'
259 # byte sequence with surrogateescape rather than decoding it as the
260 # U+7fffbeba character which is outside the [U+0000; U+10ffff] range of
261 # Python Unicode characters.
262 #
263 # Test with default config, in the C locale, in the Python UTF-8 Mode.
264 code = 'import sys, os; s=os.fsencode(sys.argv[1]); print(ascii(s))'
265
266 def run_default(arg):
267 cmd = [sys.executable, '-c', code, arg]
268 return subprocess.run(cmd, stdout=subprocess.PIPE, text=True)
269
270 def run_c_locale(arg):
271 cmd = [sys.executable, '-c', code, arg]
272 env = dict(os.environ)
273 env['LC_ALL'] = 'C'
274 return subprocess.run(cmd, stdout=subprocess.PIPE,
275 text=True, env=env)
276
277 def run_utf8_mode(arg):
278 cmd = [sys.executable, '-X', 'utf8', '-c', code, arg]
279 return subprocess.run(cmd, stdout=subprocess.PIPE, text=True)
280
281 valid_utf8 = 'e:\xe9, euro:\u20ac, non-bmp:\U0010ffff'.encode('utf-8')
282 # invalid UTF-8 byte sequences with a valid UTF-8 sequence
283 # in the middle.
284 invalid_utf8 = (
285 b'\xff' # invalid byte
286 b'\xc3\xff' # invalid byte sequence
287 b'\xc3\xa9' # valid utf-8: U+00E9 character
288 b'\xed\xa0\x80' # lone surrogate character (invalid)
289 b'\xfd\xbf\xbf\xbb\xba\xba' # character outside [U+0000; U+10ffff]
290 )
291 test_args = [valid_utf8, invalid_utf8]
292
293 for run_cmd in (run_default, run_c_locale, run_utf8_mode):
294 with self.subTest(run_cmd=run_cmd):
295 for arg in test_args:
296 proc = run_cmd(arg)
297 self.assertEqual(proc.stdout.rstrip(), ascii(arg))
298
299 @unittest.skipUnless((sys.platform == 'darwin' or
300 support.is_android), 'test specific to Mac OS X and Android')
301 def test_osx_android_utf8(self):
302 text = 'e:\xe9, euro:\u20ac, non-bmp:\U0010ffff'.encode('utf-8')
303 code = "import sys; print(ascii(sys.argv[1]))"
304
305 decoded = text.decode('utf-8', 'surrogateescape')
306 expected = ascii(decoded).encode('ascii') + b'\n'
307
308 env = os.environ.copy()
309 # C locale gives ASCII locale encoding, but Python uses UTF-8
310 # to parse the command line arguments on Mac OS X and Android.
311 env['LC_ALL'] = 'C'
312
313 p = subprocess.Popen(
314 (sys.executable, "-c", code, text),
315 stdout=subprocess.PIPE,
316 env=env)
317 stdout, stderr = p.communicate()
318 self.assertEqual(stdout, expected)
319 self.assertEqual(p.returncode, 0)
320
321 def test_non_interactive_output_buffering(self):
322 code = textwrap.dedent("""
323 import sys
324 out = sys.stdout
325 print(out.isatty(), out.write_through, out.line_buffering)
326 err = sys.stderr
327 print(err.isatty(), err.write_through, err.line_buffering)
328 """)
329 args = [sys.executable, '-c', code]
330 proc = subprocess.run(args, stdout=subprocess.PIPE,
331 stderr=subprocess.PIPE, text=True, check=True)
332 self.assertEqual(proc.stdout,
333 'False False False\n'
334 'False False True\n')
335
336 def test_unbuffered_output(self):
337 # Test expected operation of the '-u' switch
338 for stream in ('stdout', 'stderr'):
339 # Binary is unbuffered
340 code = ("import os, sys; sys.%s.buffer.write(b'x'); os._exit(0)"
341 % stream)
342 rc, out, err = assert_python_ok('-u', '-c', code)
343 data = err if stream == 'stderr' else out
344 self.assertEqual(data, b'x', "binary %s not unbuffered" % stream)
345 # Text is unbuffered
346 code = ("import os, sys; sys.%s.write('x'); os._exit(0)"
347 % stream)
348 rc, out, err = assert_python_ok('-u', '-c', code)
349 data = err if stream == 'stderr' else out
350 self.assertEqual(data, b'x', "text %s not unbuffered" % stream)
351
352 def test_unbuffered_input(self):
353 # sys.stdin still works with '-u'
354 code = ("import sys; sys.stdout.write(sys.stdin.read(1))")
355 p = spawn_python('-u', '-c', code)
356 p.stdin.write(b'x')
357 p.stdin.flush()
358 data, rc = _kill_python_and_exit_code(p)
359 self.assertEqual(rc, 0)
360 self.assertTrue(data.startswith(b'x'), data)
361
362 def test_large_PYTHONPATH(self):
363 path1 = "ABCDE" * 100
364 path2 = "FGHIJ" * 100
365 path = path1 + os.pathsep + path2
366
367 code = """if 1:
368 import sys
369 path = ":".join(sys.path)
370 path = path.encode("ascii", "backslashreplace")
371 sys.stdout.buffer.write(path)"""
372 rc, out, err = assert_python_ok('-S', '-c', code,
373 PYTHONPATH=path)
374 self.assertIn(path1.encode('ascii'), out)
375 self.assertIn(path2.encode('ascii'), out)
376
377 @unittest.skipIf(sys.flags.safe_path,
378 'PYTHONSAFEPATH changes default sys.path')
379 def test_empty_PYTHONPATH_issue16309(self):
380 # On Posix, it is documented that setting PATH to the
381 # empty string is equivalent to not setting PATH at all,
382 # which is an exception to the rule that in a string like
383 # "/bin::/usr/bin" the empty string in the middle gets
384 # interpreted as '.'
385 code = """if 1:
386 import sys
387 path = ":".join(sys.path)
388 path = path.encode("ascii", "backslashreplace")
389 sys.stdout.buffer.write(path)"""
390 rc1, out1, err1 = assert_python_ok('-c', code, PYTHONPATH="")
391 rc2, out2, err2 = assert_python_ok('-c', code, __isolated=False)
392 # regarding to Posix specification, outputs should be equal
393 # for empty and unset PYTHONPATH
394 self.assertEqual(out1, out2)
395
396 def test_displayhook_unencodable(self):
397 for encoding in ('ascii', 'latin-1', 'utf-8'):
398 env = os.environ.copy()
399 env['PYTHONIOENCODING'] = encoding
400 p = subprocess.Popen(
401 [sys.executable, '-i'],
402 stdin=subprocess.PIPE,
403 stdout=subprocess.PIPE,
404 stderr=subprocess.STDOUT,
405 env=env)
406 # non-ascii, surrogate, non-BMP printable, non-BMP unprintable
407 text = "a=\xe9 b=\uDC80 c=\U00010000 d=\U0010FFFF"
408 p.stdin.write(ascii(text).encode('ascii') + b"\n")
409 p.stdin.write(b'exit()\n')
410 data = kill_python(p)
411 escaped = repr(text).encode(encoding, 'backslashreplace')
412 self.assertIn(escaped, data)
413
414 def check_input(self, code, expected):
415 with tempfile.NamedTemporaryFile("wb+") as stdin:
416 sep = os.linesep.encode('ASCII')
417 stdin.write(sep.join((b'abc', b'def')))
418 stdin.flush()
419 stdin.seek(0)
420 with subprocess.Popen(
421 (sys.executable, "-c", code),
422 stdin=stdin, stdout=subprocess.PIPE) as proc:
423 stdout, stderr = proc.communicate()
424 self.assertEqual(stdout.rstrip(), expected)
425
426 def test_stdin_readline(self):
427 # Issue #11272: check that sys.stdin.readline() replaces '\r\n' by '\n'
428 # on Windows (sys.stdin is opened in binary mode)
429 self.check_input(
430 "import sys; print(repr(sys.stdin.readline()))",
431 b"'abc\\n'")
432
433 def test_builtin_input(self):
434 # Issue #11272: check that input() strips newlines ('\n' or '\r\n')
435 self.check_input(
436 "print(repr(input()))",
437 b"'abc'")
438
439 def test_output_newline(self):
440 # Issue 13119 Newline for print() should be \r\n on Windows.
441 code = """if 1:
442 import sys
443 print(1)
444 print(2)
445 print(3, file=sys.stderr)
446 print(4, file=sys.stderr)"""
447 rc, out, err = assert_python_ok('-c', code)
448
449 if sys.platform == 'win32':
450 self.assertEqual(b'1\r\n2\r\n', out)
451 self.assertEqual(b'3\r\n4\r\n', err)
452 else:
453 self.assertEqual(b'1\n2\n', out)
454 self.assertEqual(b'3\n4\n', err)
455
456 def test_unmached_quote(self):
457 # Issue #10206: python program starting with unmatched quote
458 # spewed spaces to stdout
459 rc, out, err = assert_python_failure('-c', "'")
460 self.assertRegex(err.decode('ascii', 'ignore'), 'SyntaxError')
461 self.assertEqual(b'', out)
462
463 def test_stdout_flush_at_shutdown(self):
464 # Issue #5319: if stdout.flush() fails at shutdown, an error should
465 # be printed out.
466 code = """if 1:
467 import os, sys, test.support
468 test.support.SuppressCrashReport().__enter__()
469 sys.stdout.write('x')
470 os.close(sys.stdout.fileno())"""
471 rc, out, err = assert_python_failure('-c', code)
472 self.assertEqual(b'', out)
473 self.assertEqual(120, rc)
474 self.assertRegex(err.decode('ascii', 'ignore'),
475 'Exception ignored in.*\nOSError: .*')
476
477 def test_closed_stdout(self):
478 # Issue #13444: if stdout has been explicitly closed, we should
479 # not attempt to flush it at shutdown.
480 code = "import sys; sys.stdout.close()"
481 rc, out, err = assert_python_ok('-c', code)
482 self.assertEqual(b'', err)
483
484 # Issue #7111: Python should work without standard streams
485
486 @unittest.skipIf(os.name != 'posix', "test needs POSIX semantics")
487 @unittest.skipIf(sys.platform == "vxworks",
488 "test needs preexec support in subprocess.Popen")
489 def _test_no_stdio(self, streams):
490 code = """if 1:
491 import os, sys
492 for i, s in enumerate({streams}):
493 if getattr(sys, s) is not None:
494 os._exit(i + 1)
495 os._exit(42)""".format(streams=streams)
496 def preexec():
497 if 'stdin' in streams:
498 os.close(0)
499 if 'stdout' in streams:
500 os.close(1)
501 if 'stderr' in streams:
502 os.close(2)
503 p = subprocess.Popen(
504 [sys.executable, "-E", "-c", code],
505 stdin=subprocess.PIPE,
506 stdout=subprocess.PIPE,
507 stderr=subprocess.PIPE,
508 preexec_fn=preexec)
509 out, err = p.communicate()
510 self.assertEqual(err, b'')
511 self.assertEqual(p.returncode, 42)
512
513 def test_no_stdin(self):
514 self._test_no_stdio(['stdin'])
515
516 def test_no_stdout(self):
517 self._test_no_stdio(['stdout'])
518
519 def test_no_stderr(self):
520 self._test_no_stdio(['stderr'])
521
522 def test_no_std_streams(self):
523 self._test_no_stdio(['stdin', 'stdout', 'stderr'])
524
525 def test_hash_randomization(self):
526 # Verify that -R enables hash randomization:
527 self.verify_valid_flag('-R')
528 hashes = []
529 if os.environ.get('PYTHONHASHSEED', 'random') != 'random':
530 env = dict(os.environ) # copy
531 # We need to test that it is enabled by default without
532 # the environment variable enabling it for us.
533 del env['PYTHONHASHSEED']
534 env['__cleanenv'] = '1' # consumed by assert_python_ok()
535 else:
536 env = {}
537 for i in range(3):
538 code = 'print(hash("spam"))'
539 rc, out, err = assert_python_ok('-c', code, **env)
540 self.assertEqual(rc, 0)
541 hashes.append(out)
542 hashes = sorted(set(hashes)) # uniq
543 # Rare chance of failure due to 3 random seeds honestly being equal.
544 self.assertGreater(len(hashes), 1,
545 msg='3 runs produced an identical random hash '
546 ' for "spam": {}'.format(hashes))
547
548 # Verify that sys.flags contains hash_randomization
549 code = 'import sys; print("random is", sys.flags.hash_randomization)'
550 rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='')
551 self.assertIn(b'random is 1', out)
552
553 rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='random')
554 self.assertIn(b'random is 1', out)
555
556 rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='0')
557 self.assertIn(b'random is 0', out)
558
559 rc, out, err = assert_python_ok('-R', '-c', code, PYTHONHASHSEED='0')
560 self.assertIn(b'random is 1', out)
561
562 def test_del___main__(self):
563 # Issue #15001: PyRun_SimpleFileExFlags() did crash because it kept a
564 # borrowed reference to the dict of __main__ module and later modify
565 # the dict whereas the module was destroyed
566 filename = os_helper.TESTFN
567 self.addCleanup(os_helper.unlink, filename)
568 with open(filename, "w", encoding="utf-8") as script:
569 print("import sys", file=script)
570 print("del sys.modules['__main__']", file=script)
571 assert_python_ok(filename)
572
573 def test_unknown_options(self):
574 rc, out, err = assert_python_failure('-E', '-z')
575 self.assertIn(b'Unknown option: -z', err)
576 self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1)
577 self.assertEqual(b'', out)
578 # Add "without='-E'" to prevent _assert_python to append -E
579 # to env_vars and change the output of stderr
580 rc, out, err = assert_python_failure('-z', without='-E')
581 self.assertIn(b'Unknown option: -z', err)
582 self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1)
583 self.assertEqual(b'', out)
584 rc, out, err = assert_python_failure('-a', '-z', without='-E')
585 self.assertIn(b'Unknown option: -a', err)
586 # only the first unknown option is reported
587 self.assertNotIn(b'Unknown option: -z', err)
588 self.assertEqual(err.splitlines().count(b'Unknown option: -a'), 1)
589 self.assertEqual(b'', out)
590
591 @unittest.skipIf(interpreter_requires_environment(),
592 'Cannot run -I tests when PYTHON env vars are required.')
593 def test_isolatedmode(self):
594 self.verify_valid_flag('-I')
595 self.verify_valid_flag('-IEPs')
596 rc, out, err = assert_python_ok('-I', '-c',
597 'from sys import flags as f; '
598 'print(f.no_user_site, f.ignore_environment, f.isolated, f.safe_path)',
599 # dummyvar to prevent extraneous -E
600 dummyvar="")
601 self.assertEqual(out.strip(), b'1 1 1 True')
602 with os_helper.temp_cwd() as tmpdir:
603 fake = os.path.join(tmpdir, "uuid.py")
604 main = os.path.join(tmpdir, "main.py")
605 with open(fake, "w", encoding="utf-8") as f:
606 f.write("raise RuntimeError('isolated mode test')\n")
607 with open(main, "w", encoding="utf-8") as f:
608 f.write("import uuid\n")
609 f.write("print('ok')\n")
610 # Use -E to ignore PYTHONSAFEPATH env var
611 self.assertRaises(subprocess.CalledProcessError,
612 subprocess.check_output,
613 [sys.executable, '-E', main], cwd=tmpdir,
614 stderr=subprocess.DEVNULL)
615 out = subprocess.check_output([sys.executable, "-I", main],
616 cwd=tmpdir)
617 self.assertEqual(out.strip(), b"ok")
618
619 def test_sys_flags_set(self):
620 # Issue 31845: a startup refactoring broke reading flags from env vars
621 for value, expected in (("", 0), ("1", 1), ("text", 1), ("2", 2)):
622 env_vars = dict(
623 PYTHONDEBUG=value,
624 PYTHONOPTIMIZE=value,
625 PYTHONDONTWRITEBYTECODE=value,
626 PYTHONVERBOSE=value,
627 )
628 dont_write_bytecode = int(bool(value))
629 code = (
630 "import sys; "
631 "sys.stderr.write(str(sys.flags)); "
632 f"""sys.exit(not (
633 sys.flags.debug == sys.flags.optimize ==
634 sys.flags.verbose ==
635 {expected}
636 and sys.flags.dont_write_bytecode == {dont_write_bytecode}
637 ))"""
638 )
639 with self.subTest(envar_value=value):
640 assert_python_ok('-c', code, **env_vars)
641
642 def test_set_pycache_prefix(self):
643 # sys.pycache_prefix can be set from either -X pycache_prefix or
644 # PYTHONPYCACHEPREFIX env var, with the former taking precedence.
645 NO_VALUE = object() # `-X pycache_prefix` with no `=PATH`
646 cases = [
647 # (PYTHONPYCACHEPREFIX, -X pycache_prefix, sys.pycache_prefix)
648 (None, None, None),
649 ('foo', None, 'foo'),
650 (None, 'bar', 'bar'),
651 ('foo', 'bar', 'bar'),
652 ('foo', '', None),
653 ('foo', NO_VALUE, None),
654 ]
655 for envval, opt, expected in cases:
656 exp_clause = "is None" if expected is None else f'== "{expected}"'
657 code = f"import sys; sys.exit(not sys.pycache_prefix {exp_clause})"
658 args = ['-c', code]
659 env = {} if envval is None else {'PYTHONPYCACHEPREFIX': envval}
660 if opt is NO_VALUE:
661 args[:0] = ['-X', 'pycache_prefix']
662 elif opt is not None:
663 args[:0] = ['-X', f'pycache_prefix={opt}']
664 with self.subTest(envval=envval, opt=opt):
665 with os_helper.temp_cwd():
666 assert_python_ok(*args, **env)
667
668 def run_xdev(self, *args, check_exitcode=True, xdev=True):
669 env = dict(os.environ)
670 env.pop('PYTHONWARNINGS', None)
671 env.pop('PYTHONDEVMODE', None)
672 env.pop('PYTHONMALLOC', None)
673
674 if xdev:
675 args = (sys.executable, '-X', 'dev', *args)
676 else:
677 args = (sys.executable, *args)
678 proc = subprocess.run(args,
679 stdout=subprocess.PIPE,
680 stderr=subprocess.STDOUT,
681 universal_newlines=True,
682 env=env)
683 if check_exitcode:
684 self.assertEqual(proc.returncode, 0, proc)
685 return proc.stdout.rstrip()
686
687 def test_xdev(self):
688 # sys.flags.dev_mode
689 code = "import sys; print(sys.flags.dev_mode)"
690 out = self.run_xdev("-c", code, xdev=False)
691 self.assertEqual(out, "False")
692 out = self.run_xdev("-c", code)
693 self.assertEqual(out, "True")
694
695 # Warnings
696 code = ("import warnings; "
697 "print(' '.join('%s::%s' % (f[0], f[2].__name__) "
698 "for f in warnings.filters))")
699 if Py_DEBUG:
700 expected_filters = "default::Warning"
701 else:
702 expected_filters = ("default::Warning "
703 "default::DeprecationWarning "
704 "ignore::DeprecationWarning "
705 "ignore::PendingDeprecationWarning "
706 "ignore::ImportWarning "
707 "ignore::ResourceWarning")
708
709 out = self.run_xdev("-c", code)
710 self.assertEqual(out, expected_filters)
711
712 out = self.run_xdev("-b", "-c", code)
713 self.assertEqual(out, f"default::BytesWarning {expected_filters}")
714
715 out = self.run_xdev("-bb", "-c", code)
716 self.assertEqual(out, f"error::BytesWarning {expected_filters}")
717
718 out = self.run_xdev("-Werror", "-c", code)
719 self.assertEqual(out, f"error::Warning {expected_filters}")
720
721 # Memory allocator debug hooks
722 try:
723 import _testcapi
724 except ImportError:
725 pass
726 else:
727 code = "import _testcapi; print(_testcapi.pymem_getallocatorsname())"
728 with support.SuppressCrashReport():
729 out = self.run_xdev("-c", code, check_exitcode=False)
730 if support.with_pymalloc():
731 alloc_name = "pymalloc_debug"
732 else:
733 alloc_name = "malloc_debug"
734 self.assertEqual(out, alloc_name)
735
736 # Faulthandler
737 try:
738 import faulthandler
739 except ImportError:
740 pass
741 else:
742 code = "import faulthandler; print(faulthandler.is_enabled())"
743 out = self.run_xdev("-c", code)
744 self.assertEqual(out, "True")
745
746 def check_warnings_filters(self, cmdline_option, envvar, use_pywarning=False):
747 if use_pywarning:
748 code = ("import sys; from test.support.import_helper import "
749 "import_fresh_module; "
750 "warnings = import_fresh_module('warnings', blocked=['_warnings']); ")
751 else:
752 code = "import sys, warnings; "
753 code += ("print(' '.join('%s::%s' % (f[0], f[2].__name__) "
754 "for f in warnings.filters))")
755 args = (sys.executable, '-W', cmdline_option, '-bb', '-c', code)
756 env = dict(os.environ)
757 env.pop('PYTHONDEVMODE', None)
758 env["PYTHONWARNINGS"] = envvar
759 proc = subprocess.run(args,
760 stdout=subprocess.PIPE,
761 stderr=subprocess.STDOUT,
762 universal_newlines=True,
763 env=env)
764 self.assertEqual(proc.returncode, 0, proc)
765 return proc.stdout.rstrip()
766
767 def test_warnings_filter_precedence(self):
768 expected_filters = ("error::BytesWarning "
769 "once::UserWarning "
770 "always::UserWarning")
771 if not Py_DEBUG:
772 expected_filters += (" "
773 "default::DeprecationWarning "
774 "ignore::DeprecationWarning "
775 "ignore::PendingDeprecationWarning "
776 "ignore::ImportWarning "
777 "ignore::ResourceWarning")
778
779 out = self.check_warnings_filters("once::UserWarning",
780 "always::UserWarning")
781 self.assertEqual(out, expected_filters)
782
783 out = self.check_warnings_filters("once::UserWarning",
784 "always::UserWarning",
785 use_pywarning=True)
786 self.assertEqual(out, expected_filters)
787
788 def check_pythonmalloc(self, env_var, name):
789 code = 'import _testcapi; print(_testcapi.pymem_getallocatorsname())'
790 env = dict(os.environ)
791 env.pop('PYTHONDEVMODE', None)
792 if env_var is not None:
793 env['PYTHONMALLOC'] = env_var
794 else:
795 env.pop('PYTHONMALLOC', None)
796 args = (sys.executable, '-c', code)
797 proc = subprocess.run(args,
798 stdout=subprocess.PIPE,
799 stderr=subprocess.STDOUT,
800 universal_newlines=True,
801 env=env)
802 self.assertEqual(proc.stdout.rstrip(), name)
803 self.assertEqual(proc.returncode, 0)
804
805 def test_pythonmalloc(self):
806 # Test the PYTHONMALLOC environment variable
807 pymalloc = support.with_pymalloc()
808 if pymalloc:
809 default_name = 'pymalloc_debug' if Py_DEBUG else 'pymalloc'
810 default_name_debug = 'pymalloc_debug'
811 else:
812 default_name = 'malloc_debug' if Py_DEBUG else 'malloc'
813 default_name_debug = 'malloc_debug'
814
815 tests = [
816 (None, default_name),
817 ('debug', default_name_debug),
818 ('malloc', 'malloc'),
819 ('malloc_debug', 'malloc_debug'),
820 ]
821 if pymalloc:
822 tests.extend((
823 ('pymalloc', 'pymalloc'),
824 ('pymalloc_debug', 'pymalloc_debug'),
825 ))
826
827 for env_var, name in tests:
828 with self.subTest(env_var=env_var, name=name):
829 self.check_pythonmalloc(env_var, name)
830
831 def test_pythondevmode_env(self):
832 # Test the PYTHONDEVMODE environment variable
833 code = "import sys; print(sys.flags.dev_mode)"
834 env = dict(os.environ)
835 env.pop('PYTHONDEVMODE', None)
836 args = (sys.executable, '-c', code)
837
838 proc = subprocess.run(args, stdout=subprocess.PIPE,
839 universal_newlines=True, env=env)
840 self.assertEqual(proc.stdout.rstrip(), 'False')
841 self.assertEqual(proc.returncode, 0, proc)
842
843 env['PYTHONDEVMODE'] = '1'
844 proc = subprocess.run(args, stdout=subprocess.PIPE,
845 universal_newlines=True, env=env)
846 self.assertEqual(proc.stdout.rstrip(), 'True')
847 self.assertEqual(proc.returncode, 0, proc)
848
849 @unittest.skipUnless(sys.platform == 'win32',
850 'bpo-32457 only applies on Windows')
851 def test_argv0_normalization(self):
852 args = sys.executable, '-c', 'print(0)'
853 prefix, exe = os.path.split(sys.executable)
854 executable = prefix + '\\.\\.\\.\\' + exe
855
856 proc = subprocess.run(args, stdout=subprocess.PIPE,
857 executable=executable)
858 self.assertEqual(proc.returncode, 0, proc)
859 self.assertEqual(proc.stdout.strip(), b'0')
860
861 def test_parsing_error(self):
862 args = [sys.executable, '-I', '--unknown-option']
863 proc = subprocess.run(args,
864 stdout=subprocess.PIPE,
865 stderr=subprocess.PIPE,
866 text=True)
867 err_msg = "unknown option --unknown-option\nusage: "
868 self.assertTrue(proc.stderr.startswith(err_msg), proc.stderr)
869 self.assertNotEqual(proc.returncode, 0)
870
871 def test_int_max_str_digits(self):
872 code = "import sys; print(sys.flags.int_max_str_digits, sys.get_int_max_str_digits())"
873
874 assert_python_failure('-X', 'int_max_str_digits', '-c', code)
875 assert_python_failure('-X', 'int_max_str_digits=foo', '-c', code)
876 assert_python_failure('-X', 'int_max_str_digits=100', '-c', code)
877 assert_python_failure('-X', 'int_max_str_digits', '-c', code,
878 PYTHONINTMAXSTRDIGITS='4000')
879
880 assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='foo')
881 assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='100')
882
883 def res2int(res):
884 out = res.out.strip().decode("utf-8")
885 return tuple(int(i) for i in out.split())
886
887 res = assert_python_ok('-c', code)
888 self.assertEqual(res2int(res), (-1, sys.get_int_max_str_digits()))
889 res = assert_python_ok('-X', 'int_max_str_digits=0', '-c', code)
890 self.assertEqual(res2int(res), (0, 0))
891 res = assert_python_ok('-X', 'int_max_str_digits=4000', '-c', code)
892 self.assertEqual(res2int(res), (4000, 4000))
893 res = assert_python_ok('-X', 'int_max_str_digits=100000', '-c', code)
894 self.assertEqual(res2int(res), (100000, 100000))
895
896 res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='0')
897 self.assertEqual(res2int(res), (0, 0))
898 res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='4000')
899 self.assertEqual(res2int(res), (4000, 4000))
900 res = assert_python_ok(
901 '-X', 'int_max_str_digits=6000', '-c', code,
902 PYTHONINTMAXSTRDIGITS='4000'
903 )
904 self.assertEqual(res2int(res), (6000, 6000))
905
906
907 @unittest.skipIf(interpreter_requires_environment(),
908 'Cannot run -I tests when PYTHON env vars are required.')
909 class ESC[4;38;5;81mIgnoreEnvironmentTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
910
911 def run_ignoring_vars(self, predicate, **env_vars):
912 # Runs a subprocess with -E set, even though we're passing
913 # specific environment variables
914 # Logical inversion to match predicate check to a zero return
915 # code indicating success
916 code = "import sys; sys.stderr.write(str(sys.flags)); sys.exit(not ({}))".format(predicate)
917 return assert_python_ok('-E', '-c', code, **env_vars)
918
919 def test_ignore_PYTHONPATH(self):
920 path = "should_be_ignored"
921 self.run_ignoring_vars("'{}' not in sys.path".format(path),
922 PYTHONPATH=path)
923
924 def test_ignore_PYTHONHASHSEED(self):
925 self.run_ignoring_vars("sys.flags.hash_randomization == 1",
926 PYTHONHASHSEED="0")
927
928 def test_sys_flags_not_set(self):
929 # Issue 31845: a startup refactoring broke reading flags from env vars
930 expected_outcome = """
931 (sys.flags.debug == sys.flags.optimize ==
932 sys.flags.dont_write_bytecode ==
933 sys.flags.verbose == sys.flags.safe_path == 0)
934 """
935 self.run_ignoring_vars(
936 expected_outcome,
937 PYTHONDEBUG="1",
938 PYTHONOPTIMIZE="1",
939 PYTHONDONTWRITEBYTECODE="1",
940 PYTHONVERBOSE="1",
941 PYTHONSAFEPATH="1",
942 )
943
944
945 class ESC[4;38;5;81mSyntaxErrorTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
946 def check_string(self, code):
947 proc = subprocess.run([sys.executable, "-"], input=code,
948 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
949 self.assertNotEqual(proc.returncode, 0)
950 self.assertNotEqual(proc.stderr, None)
951 self.assertIn(b"\nSyntaxError", proc.stderr)
952
953 def test_tokenizer_error_with_stdin(self):
954 self.check_string(b"(1+2+3")
955
956 def test_decoding_error_at_the_end_of_the_line(self):
957 self.check_string(br"'\u1f'")
958
959
960 def tearDownModule():
961 support.reap_children()
962
963
964 if __name__ == "__main__":
965 unittest.main()