ยปCore Development>Code coverage>Lib/test/test_cmd_line.py

Python code coverage for Lib/test/test_cmd_line.py

#countcontent
1n/a# Tests invocation of the interpreter with various command line arguments
2n/a# Most tests are executed with environment variables ignored
3n/a# See test_cmd_line_script.py for testing of script execution
4n/a
5n/aimport test.support, unittest
6n/aimport os
7n/aimport sys
8n/aimport subprocess
9n/aimport tempfile
10n/afrom test.support import script_helper, is_android
11n/afrom test.support.script_helper import (spawn_python, kill_python, assert_python_ok,
12n/a assert_python_failure)
13n/a
14n/a
15n/a# XXX (ncoghlan): Move to script_helper and make consistent with run_python
16n/adef _kill_python_and_exit_code(p):
17n/a data = kill_python(p)
18n/a returncode = p.wait()
19n/a return data, returncode
20n/a
21n/aclass CmdLineTest(unittest.TestCase):
22n/a def test_directories(self):
23n/a assert_python_failure('.')
24n/a assert_python_failure('< .')
25n/a
26n/a def verify_valid_flag(self, cmd_line):
27n/a rc, out, err = assert_python_ok(*cmd_line)
28n/a self.assertTrue(out == b'' or out.endswith(b'\n'))
29n/a self.assertNotIn(b'Traceback', out)
30n/a self.assertNotIn(b'Traceback', err)
31n/a
32n/a def test_optimize(self):
33n/a self.verify_valid_flag('-O')
34n/a self.verify_valid_flag('-OO')
35n/a
36n/a def test_site_flag(self):
37n/a self.verify_valid_flag('-S')
38n/a
39n/a def test_usage(self):
40n/a rc, out, err = assert_python_ok('-h')
41n/a self.assertIn(b'usage', out)
42n/a
43n/a def test_version(self):
44n/a version = ('Python %d.%d' % sys.version_info[:2]).encode("ascii")
45n/a for switch in '-V', '--version', '-VV':
46n/a rc, out, err = assert_python_ok(switch)
47n/a self.assertFalse(err.startswith(version))
48n/a self.assertTrue(out.startswith(version))
49n/a
50n/a def test_verbose(self):
51n/a # -v causes imports to write to stderr. If the write to
52n/a # stderr itself causes an import to happen (for the output
53n/a # codec), a recursion loop can occur.
54n/a rc, out, err = assert_python_ok('-v')
55n/a self.assertNotIn(b'stack overflow', err)
56n/a rc, out, err = assert_python_ok('-vv')
57n/a self.assertNotIn(b'stack overflow', err)
58n/a
59n/a def test_xoptions(self):
60n/a def get_xoptions(*args):
61n/a # use subprocess module directly because test.support.script_helper adds
62n/a # "-X faulthandler" to the command line
63n/a args = (sys.executable, '-E') + args
64n/a args += ('-c', 'import sys; print(sys._xoptions)')
65n/a out = subprocess.check_output(args)
66n/a opts = eval(out.splitlines()[0])
67n/a return opts
68n/a
69n/a opts = get_xoptions()
70n/a self.assertEqual(opts, {})
71n/a
72n/a opts = get_xoptions('-Xa', '-Xb=c,d=e')
73n/a self.assertEqual(opts, {'a': True, 'b': 'c,d=e'})
74n/a
75n/a def test_showrefcount(self):
76n/a def run_python(*args):
77n/a # this is similar to assert_python_ok but doesn't strip
78n/a # the refcount from stderr. It can be replaced once
79n/a # assert_python_ok stops doing that.
80n/a cmd = [sys.executable]
81n/a cmd.extend(args)
82n/a PIPE = subprocess.PIPE
83n/a p = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE)
84n/a out, err = p.communicate()
85n/a p.stdout.close()
86n/a p.stderr.close()
87n/a rc = p.returncode
88n/a self.assertEqual(rc, 0)
89n/a return rc, out, err
90n/a code = 'import sys; print(sys._xoptions)'
91n/a # normally the refcount is hidden
92n/a rc, out, err = run_python('-c', code)
93n/a self.assertEqual(out.rstrip(), b'{}')
94n/a self.assertEqual(err, b'')
95n/a # "-X showrefcount" shows the refcount, but only in debug builds
96n/a rc, out, err = run_python('-X', 'showrefcount', '-c', code)
97n/a self.assertEqual(out.rstrip(), b"{'showrefcount': True}")
98n/a if hasattr(sys, 'gettotalrefcount'): # debug build
99n/a self.assertRegex(err, br'^\[\d+ refs, \d+ blocks\]')
100n/a else:
101n/a self.assertEqual(err, b'')
102n/a
103n/a def test_run_module(self):
104n/a # Test expected operation of the '-m' switch
105n/a # Switch needs an argument
106n/a assert_python_failure('-m')
107n/a # Check we get an error for a nonexistent module
108n/a assert_python_failure('-m', 'fnord43520xyz')
109n/a # Check the runpy module also gives an error for
110n/a # a nonexistent module
111n/a assert_python_failure('-m', 'runpy', 'fnord43520xyz')
112n/a # All good if module is located and run successfully
113n/a assert_python_ok('-m', 'timeit', '-n', '1')
114n/a
115n/a def test_run_module_bug1764407(self):
116n/a # -m and -i need to play well together
117n/a # Runs the timeit module and checks the __main__
118n/a # namespace has been populated appropriately
119n/a p = spawn_python('-i', '-m', 'timeit', '-n', '1')
120n/a p.stdin.write(b'Timer\n')
121n/a p.stdin.write(b'exit()\n')
122n/a data = kill_python(p)
123n/a self.assertTrue(data.find(b'1 loop') != -1)
124n/a self.assertTrue(data.find(b'__main__.Timer') != -1)
125n/a
126n/a def test_run_code(self):
127n/a # Test expected operation of the '-c' switch
128n/a # Switch needs an argument
129n/a assert_python_failure('-c')
130n/a # Check we get an error for an uncaught exception
131n/a assert_python_failure('-c', 'raise Exception')
132n/a # All good if execution is successful
133n/a assert_python_ok('-c', 'pass')
134n/a
135n/a @unittest.skipUnless(test.support.FS_NONASCII, 'need support.FS_NONASCII')
136n/a def test_non_ascii(self):
137n/a # Test handling of non-ascii data
138n/a command = ("assert(ord(%r) == %s)"
139n/a % (test.support.FS_NONASCII, ord(test.support.FS_NONASCII)))
140n/a assert_python_ok('-c', command)
141n/a
142n/a # On Windows, pass bytes to subprocess doesn't test how Python decodes the
143n/a # command line, but how subprocess does decode bytes to unicode. Python
144n/a # doesn't decode the command line because Windows provides directly the
145n/a # arguments as unicode (using wmain() instead of main()).
146n/a @unittest.skipIf(sys.platform == 'win32',
147n/a 'Windows has a native unicode API')
148n/a def test_undecodable_code(self):
149n/a undecodable = b"\xff"
150n/a env = os.environ.copy()
151n/a # Use C locale to get ascii for the locale encoding
152n/a env['LC_ALL'] = 'C'
153n/a code = (
154n/a b'import locale; '
155n/a b'print(ascii("' + undecodable + b'"), '
156n/a b'locale.getpreferredencoding())')
157n/a p = subprocess.Popen(
158n/a [sys.executable, "-c", code],
159n/a stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
160n/a env=env)
161n/a stdout, stderr = p.communicate()
162n/a if p.returncode == 1:
163n/a # _Py_char2wchar() decoded b'\xff' as '\udcff' (b'\xff' is not
164n/a # decodable from ASCII) and run_command() failed on
165n/a # PyUnicode_AsUTF8String(). This is the expected behaviour on
166n/a # Linux.
167n/a pattern = b"Unable to decode the command from the command line:"
168n/a elif p.returncode == 0:
169n/a # _Py_char2wchar() decoded b'\xff' as '\xff' even if the locale is
170n/a # C and the locale encoding is ASCII. It occurs on FreeBSD, Solaris
171n/a # and Mac OS X.
172n/a pattern = b"'\\xff' "
173n/a # The output is followed by the encoding name, an alias to ASCII.
174n/a # Examples: "US-ASCII" or "646" (ISO 646, on Solaris).
175n/a else:
176n/a raise AssertionError("Unknown exit code: %s, output=%a" % (p.returncode, stdout))
177n/a if not stdout.startswith(pattern):
178n/a raise AssertionError("%a doesn't start with %a" % (stdout, pattern))
179n/a
180n/a @unittest.skipUnless((sys.platform == 'darwin' or
181n/a is_android), 'test specific to Mac OS X and Android')
182n/a def test_osx_android_utf8(self):
183n/a def check_output(text):
184n/a decoded = text.decode('utf-8', 'surrogateescape')
185n/a expected = ascii(decoded).encode('ascii') + b'\n'
186n/a
187n/a env = os.environ.copy()
188n/a # C locale gives ASCII locale encoding, but Python uses UTF-8
189n/a # to parse the command line arguments on Mac OS X and Android.
190n/a env['LC_ALL'] = 'C'
191n/a
192n/a p = subprocess.Popen(
193n/a (sys.executable, "-c", "import sys; print(ascii(sys.argv[1]))", text),
194n/a stdout=subprocess.PIPE,
195n/a env=env)
196n/a stdout, stderr = p.communicate()
197n/a self.assertEqual(stdout, expected)
198n/a self.assertEqual(p.returncode, 0)
199n/a
200n/a # test valid utf-8
201n/a text = 'e:\xe9, euro:\u20ac, non-bmp:\U0010ffff'.encode('utf-8')
202n/a check_output(text)
203n/a
204n/a # test invalid utf-8
205n/a text = (
206n/a b'\xff' # invalid byte
207n/a b'\xc3\xa9' # valid utf-8 character
208n/a b'\xc3\xff' # invalid byte sequence
209n/a b'\xed\xa0\x80' # lone surrogate character (invalid)
210n/a )
211n/a check_output(text)
212n/a
213n/a def test_unbuffered_output(self):
214n/a # Test expected operation of the '-u' switch
215n/a for stream in ('stdout', 'stderr'):
216n/a # Binary is unbuffered
217n/a code = ("import os, sys; sys.%s.buffer.write(b'x'); os._exit(0)"
218n/a % stream)
219n/a rc, out, err = assert_python_ok('-u', '-c', code)
220n/a data = err if stream == 'stderr' else out
221n/a self.assertEqual(data, b'x', "binary %s not unbuffered" % stream)
222n/a # Text is line-buffered
223n/a code = ("import os, sys; sys.%s.write('x\\n'); os._exit(0)"
224n/a % stream)
225n/a rc, out, err = assert_python_ok('-u', '-c', code)
226n/a data = err if stream == 'stderr' else out
227n/a self.assertEqual(data.strip(), b'x',
228n/a "text %s not line-buffered" % stream)
229n/a
230n/a def test_unbuffered_input(self):
231n/a # sys.stdin still works with '-u'
232n/a code = ("import sys; sys.stdout.write(sys.stdin.read(1))")
233n/a p = spawn_python('-u', '-c', code)
234n/a p.stdin.write(b'x')
235n/a p.stdin.flush()
236n/a data, rc = _kill_python_and_exit_code(p)
237n/a self.assertEqual(rc, 0)
238n/a self.assertTrue(data.startswith(b'x'), data)
239n/a
240n/a def test_large_PYTHONPATH(self):
241n/a path1 = "ABCDE" * 100
242n/a path2 = "FGHIJ" * 100
243n/a path = path1 + os.pathsep + path2
244n/a
245n/a code = """if 1:
246n/a import sys
247n/a path = ":".join(sys.path)
248n/a path = path.encode("ascii", "backslashreplace")
249n/a sys.stdout.buffer.write(path)"""
250n/a rc, out, err = assert_python_ok('-S', '-c', code,
251n/a PYTHONPATH=path)
252n/a self.assertIn(path1.encode('ascii'), out)
253n/a self.assertIn(path2.encode('ascii'), out)
254n/a
255n/a def test_empty_PYTHONPATH_issue16309(self):
256n/a # On Posix, it is documented that setting PATH to the
257n/a # empty string is equivalent to not setting PATH at all,
258n/a # which is an exception to the rule that in a string like
259n/a # "/bin::/usr/bin" the empty string in the middle gets
260n/a # interpreted as '.'
261n/a code = """if 1:
262n/a import sys
263n/a path = ":".join(sys.path)
264n/a path = path.encode("ascii", "backslashreplace")
265n/a sys.stdout.buffer.write(path)"""
266n/a rc1, out1, err1 = assert_python_ok('-c', code, PYTHONPATH="")
267n/a rc2, out2, err2 = assert_python_ok('-c', code, __isolated=False)
268n/a # regarding to Posix specification, outputs should be equal
269n/a # for empty and unset PYTHONPATH
270n/a self.assertEqual(out1, out2)
271n/a
272n/a def test_displayhook_unencodable(self):
273n/a for encoding in ('ascii', 'latin-1', 'utf-8'):
274n/a # We are testing a PYTHON environment variable here, so we can't
275n/a # use -E, -I, or script_helper (which uses them). So instead we do
276n/a # poor-man's isolation by deleting the PYTHON vars from env.
277n/a env = {key:value for (key,value) in os.environ.copy().items()
278n/a if not key.startswith('PYTHON')}
279n/a env['PYTHONIOENCODING'] = encoding
280n/a p = subprocess.Popen(
281n/a [sys.executable, '-i'],
282n/a stdin=subprocess.PIPE,
283n/a stdout=subprocess.PIPE,
284n/a stderr=subprocess.STDOUT,
285n/a env=env)
286n/a # non-ascii, surrogate, non-BMP printable, non-BMP unprintable
287n/a text = "a=\xe9 b=\uDC80 c=\U00010000 d=\U0010FFFF"
288n/a p.stdin.write(ascii(text).encode('ascii') + b"\n")
289n/a p.stdin.write(b'exit()\n')
290n/a data = kill_python(p)
291n/a escaped = repr(text).encode(encoding, 'backslashreplace')
292n/a self.assertIn(escaped, data)
293n/a
294n/a def check_input(self, code, expected):
295n/a with tempfile.NamedTemporaryFile("wb+") as stdin:
296n/a sep = os.linesep.encode('ASCII')
297n/a stdin.write(sep.join((b'abc', b'def')))
298n/a stdin.flush()
299n/a stdin.seek(0)
300n/a with subprocess.Popen(
301n/a (sys.executable, "-c", code),
302n/a stdin=stdin, stdout=subprocess.PIPE) as proc:
303n/a stdout, stderr = proc.communicate()
304n/a self.assertEqual(stdout.rstrip(), expected)
305n/a
306n/a def test_stdin_readline(self):
307n/a # Issue #11272: check that sys.stdin.readline() replaces '\r\n' by '\n'
308n/a # on Windows (sys.stdin is opened in binary mode)
309n/a self.check_input(
310n/a "import sys; print(repr(sys.stdin.readline()))",
311n/a b"'abc\\n'")
312n/a
313n/a def test_builtin_input(self):
314n/a # Issue #11272: check that input() strips newlines ('\n' or '\r\n')
315n/a self.check_input(
316n/a "print(repr(input()))",
317n/a b"'abc'")
318n/a
319n/a def test_output_newline(self):
320n/a # Issue 13119 Newline for print() should be \r\n on Windows.
321n/a code = """if 1:
322n/a import sys
323n/a print(1)
324n/a print(2)
325n/a print(3, file=sys.stderr)
326n/a print(4, file=sys.stderr)"""
327n/a rc, out, err = assert_python_ok('-c', code)
328n/a
329n/a if sys.platform == 'win32':
330n/a self.assertEqual(b'1\r\n2\r\n', out)
331n/a self.assertEqual(b'3\r\n4', err)
332n/a else:
333n/a self.assertEqual(b'1\n2\n', out)
334n/a self.assertEqual(b'3\n4', err)
335n/a
336n/a def test_unmached_quote(self):
337n/a # Issue #10206: python program starting with unmatched quote
338n/a # spewed spaces to stdout
339n/a rc, out, err = assert_python_failure('-c', "'")
340n/a self.assertRegex(err.decode('ascii', 'ignore'), 'SyntaxError')
341n/a self.assertEqual(b'', out)
342n/a
343n/a def test_stdout_flush_at_shutdown(self):
344n/a # Issue #5319: if stdout.flush() fails at shutdown, an error should
345n/a # be printed out.
346n/a code = """if 1:
347n/a import os, sys, test.support
348n/a test.support.SuppressCrashReport().__enter__()
349n/a sys.stdout.write('x')
350n/a os.close(sys.stdout.fileno())"""
351n/a rc, out, err = assert_python_failure('-c', code)
352n/a self.assertEqual(b'', out)
353n/a self.assertEqual(120, rc)
354n/a self.assertRegex(err.decode('ascii', 'ignore'),
355n/a 'Exception ignored in.*\nOSError: .*')
356n/a
357n/a def test_closed_stdout(self):
358n/a # Issue #13444: if stdout has been explicitly closed, we should
359n/a # not attempt to flush it at shutdown.
360n/a code = "import sys; sys.stdout.close()"
361n/a rc, out, err = assert_python_ok('-c', code)
362n/a self.assertEqual(b'', err)
363n/a
364n/a # Issue #7111: Python should work without standard streams
365n/a
366n/a @unittest.skipIf(os.name != 'posix', "test needs POSIX semantics")
367n/a def _test_no_stdio(self, streams):
368n/a code = """if 1:
369n/a import os, sys
370n/a for i, s in enumerate({streams}):
371n/a if getattr(sys, s) is not None:
372n/a os._exit(i + 1)
373n/a os._exit(42)""".format(streams=streams)
374n/a def preexec():
375n/a if 'stdin' in streams:
376n/a os.close(0)
377n/a if 'stdout' in streams:
378n/a os.close(1)
379n/a if 'stderr' in streams:
380n/a os.close(2)
381n/a p = subprocess.Popen(
382n/a [sys.executable, "-E", "-c", code],
383n/a stdin=subprocess.PIPE,
384n/a stdout=subprocess.PIPE,
385n/a stderr=subprocess.PIPE,
386n/a preexec_fn=preexec)
387n/a out, err = p.communicate()
388n/a self.assertEqual(test.support.strip_python_stderr(err), b'')
389n/a self.assertEqual(p.returncode, 42)
390n/a
391n/a def test_no_stdin(self):
392n/a self._test_no_stdio(['stdin'])
393n/a
394n/a def test_no_stdout(self):
395n/a self._test_no_stdio(['stdout'])
396n/a
397n/a def test_no_stderr(self):
398n/a self._test_no_stdio(['stderr'])
399n/a
400n/a def test_no_std_streams(self):
401n/a self._test_no_stdio(['stdin', 'stdout', 'stderr'])
402n/a
403n/a def test_hash_randomization(self):
404n/a # Verify that -R enables hash randomization:
405n/a self.verify_valid_flag('-R')
406n/a hashes = []
407n/a if os.environ.get('PYTHONHASHSEED', 'random') != 'random':
408n/a env = dict(os.environ) # copy
409n/a # We need to test that it is enabled by default without
410n/a # the environment variable enabling it for us.
411n/a del env['PYTHONHASHSEED']
412n/a env['__cleanenv'] = '1' # consumed by assert_python_ok()
413n/a else:
414n/a env = {}
415n/a for i in range(3):
416n/a code = 'print(hash("spam"))'
417n/a rc, out, err = assert_python_ok('-c', code, **env)
418n/a self.assertEqual(rc, 0)
419n/a hashes.append(out)
420n/a hashes = sorted(set(hashes)) # uniq
421n/a # Rare chance of failure due to 3 random seeds honestly being equal.
422n/a self.assertGreater(len(hashes), 1,
423n/a msg='3 runs produced an identical random hash '
424n/a ' for "spam": {}'.format(hashes))
425n/a
426n/a # Verify that sys.flags contains hash_randomization
427n/a code = 'import sys; print("random is", sys.flags.hash_randomization)'
428n/a rc, out, err = assert_python_ok('-c', code)
429n/a self.assertEqual(rc, 0)
430n/a self.assertIn(b'random is 1', out)
431n/a
432n/a def test_del___main__(self):
433n/a # Issue #15001: PyRun_SimpleFileExFlags() did crash because it kept a
434n/a # borrowed reference to the dict of __main__ module and later modify
435n/a # the dict whereas the module was destroyed
436n/a filename = test.support.TESTFN
437n/a self.addCleanup(test.support.unlink, filename)
438n/a with open(filename, "w") as script:
439n/a print("import sys", file=script)
440n/a print("del sys.modules['__main__']", file=script)
441n/a assert_python_ok(filename)
442n/a
443n/a def test_unknown_options(self):
444n/a rc, out, err = assert_python_failure('-E', '-z')
445n/a self.assertIn(b'Unknown option: -z', err)
446n/a self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1)
447n/a self.assertEqual(b'', out)
448n/a # Add "without='-E'" to prevent _assert_python to append -E
449n/a # to env_vars and change the output of stderr
450n/a rc, out, err = assert_python_failure('-z', without='-E')
451n/a self.assertIn(b'Unknown option: -z', err)
452n/a self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1)
453n/a self.assertEqual(b'', out)
454n/a rc, out, err = assert_python_failure('-a', '-z', without='-E')
455n/a self.assertIn(b'Unknown option: -a', err)
456n/a # only the first unknown option is reported
457n/a self.assertNotIn(b'Unknown option: -z', err)
458n/a self.assertEqual(err.splitlines().count(b'Unknown option: -a'), 1)
459n/a self.assertEqual(b'', out)
460n/a
461n/a @unittest.skipIf(script_helper.interpreter_requires_environment(),
462n/a 'Cannot run -I tests when PYTHON env vars are required.')
463n/a def test_isolatedmode(self):
464n/a self.verify_valid_flag('-I')
465n/a self.verify_valid_flag('-IEs')
466n/a rc, out, err = assert_python_ok('-I', '-c',
467n/a 'from sys import flags as f; '
468n/a 'print(f.no_user_site, f.ignore_environment, f.isolated)',
469n/a # dummyvar to prevent extraneous -E
470n/a dummyvar="")
471n/a self.assertEqual(out.strip(), b'1 1 1')
472n/a with test.support.temp_cwd() as tmpdir:
473n/a fake = os.path.join(tmpdir, "uuid.py")
474n/a main = os.path.join(tmpdir, "main.py")
475n/a with open(fake, "w") as f:
476n/a f.write("raise RuntimeError('isolated mode test')\n")
477n/a with open(main, "w") as f:
478n/a f.write("import uuid\n")
479n/a f.write("print('ok')\n")
480n/a self.assertRaises(subprocess.CalledProcessError,
481n/a subprocess.check_output,
482n/a [sys.executable, main], cwd=tmpdir,
483n/a stderr=subprocess.DEVNULL)
484n/a out = subprocess.check_output([sys.executable, "-I", main],
485n/a cwd=tmpdir)
486n/a self.assertEqual(out.strip(), b"ok")
487n/a
488n/adef test_main():
489n/a test.support.run_unittest(CmdLineTest)
490n/a test.support.reap_children()
491n/a
492n/aif __name__ == "__main__":
493n/a test_main()