Skip to content

Update test_cmd_line from Cpython v3.11.2 #4801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 9, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 176 additions & 31 deletions Lib/test/test_cmd_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@
import subprocess
import sys
import tempfile
import textwrap
import unittest
from test import support
from test.support import os_helper
from test.support.script_helper import (
spawn_python, kill_python, assert_python_ok, assert_python_failure,
interpreter_requires_environment
)
from test.support import os_helper

if not support.has_subprocess_support:
raise unittest.SkipTest("test module requires subprocess")

# Debug build?
Py_DEBUG = hasattr(sys, "gettotalrefcount")
Expand All @@ -25,38 +28,63 @@ def _kill_python_and_exit_code(p):
returncode = p.wait()
return data, returncode


class CmdLineTest(unittest.TestCase):
def test_directories(self):
assert_python_failure('.')
assert_python_failure('< .')

def verify_valid_flag(self, cmd_line):
rc, out, err = assert_python_ok(*cmd_line)
rc, out, err = assert_python_ok(cmd_line)
self.assertTrue(out == b'' or out.endswith(b'\n'))
self.assertNotIn(b'Traceback', out)
self.assertNotIn(b'Traceback', err)
return out

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_optimize(self):
self.verify_valid_flag('-O')
self.verify_valid_flag('-OO')
def test_help(self):
self.verify_valid_flag('-h')
self.verify_valid_flag('-?')
out = self.verify_valid_flag('--help')
lines = out.splitlines()
self.assertIn(b'usage', lines[0])
self.assertNotIn(b'PYTHONHOME', out)
self.assertNotIn(b'-X dev', out)
self.assertLess(len(lines), 50)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_site_flag(self):
self.verify_valid_flag('-S')
def test_help_env(self):
out = self.verify_valid_flag('--help-env')
self.assertIn(b'PYTHONHOME', out)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_help_xoptions(self):
out = self.verify_valid_flag('--help-xoptions')
self.assertIn(b'-X dev', out)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_usage(self):
rc, out, err = assert_python_ok('-h')
def test_help_all(self):
out = self.verify_valid_flag('--help-all')
lines = out.splitlines()
self.assertIn(b'usage', lines[0])
self.assertIn(b'PYTHONHOME', out)
self.assertIn(b'-X dev', out)

# The first line contains the program name,
# but the rest should be ASCII-only
b''.join(lines[1:]).decode('ascii')

def test_optimize(self):
self.verify_valid_flag('-O')
self.verify_valid_flag('-OO')

def test_site_flag(self):
self.verify_valid_flag('-S')

# NOTE: RUSTPYTHON version never starts with Python
@unittest.expectedFailure
def test_version(self):
Expand Down Expand Up @@ -114,13 +142,32 @@ def run_python(*args):
self.assertEqual(out.rstrip(), b'{}')
self.assertEqual(err, b'')
# "-X showrefcount" shows the refcount, but only in debug builds
rc, out, err = run_python('-X', 'showrefcount', '-c', code)
rc, out, err = run_python('-I', '-X', 'showrefcount', '-c', code)
self.assertEqual(out.rstrip(), b"{'showrefcount': True}")
if Py_DEBUG:
self.assertRegex(err, br'^\[\d+ refs, \d+ blocks\]')
# bpo-46417: Tolerate negative reference count which can occur
# because of bugs in C extensions. This test is only about checking
# the showrefcount feature.
self.assertRegex(err, br'^\[-?\d+ refs, \d+ blocks\]')
else:
self.assertEqual(err, b'')

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_xoption_frozen_modules(self):
tests = {
('=on', 'FrozenImporter'),
('=off', 'SourceFileLoader'),
('=', 'FrozenImporter'),
('', 'FrozenImporter'),
}
for raw, expected in tests:
cmd = ['-X', f'frozen_modules{raw}',
'-c', 'import os; print(os.__spec__.loader, end="")']
with self.subTest(raw):
res = assert_python_ok(*cmd)
self.assertRegex(res.out.decode('utf-8'), expected)

def test_run_module(self):
# Test expected operation of the '-m' switch
# Switch needs an argument
Expand All @@ -146,6 +193,16 @@ def test_run_module_bug1764407(self):
self.assertTrue(data.find(b'1 loop') != -1)
self.assertTrue(data.find(b'__main__.Timer') != -1)

def test_relativedir_bug46421(self):
# Test `python -m unittest` with a relative directory beginning with ./
# Note: We have to switch to the project's top module's directory, as per
# the python unittest wiki. We will switch back when we are done.
projectlibpath = os.path.dirname(__file__).removesuffix("test")
with os_helper.change_cwd(projectlibpath):
# Testing with and without ./
assert_python_ok('-m', 'unittest', "test/test_longexp.py")
assert_python_ok('-m', 'unittest', "./test/test_longexp.py")

def test_run_code(self):
# Test expected operation of the '-c' switch
# Switch needs an argument
Expand All @@ -162,6 +219,14 @@ def test_non_ascii(self):
% (os_helper.FS_NONASCII, ord(os_helper.FS_NONASCII)))
assert_python_ok('-c', command)

@unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII')
def test_coding(self):
# bpo-32381: the -c command ignores the coding cookie
ch = os_helper.FS_NONASCII
cmd = f"# coding: latin1\nprint(ascii('{ch}'))"
res = assert_python_ok('-c', cmd)
self.assertEqual(res.out.rstrip(), ascii(ch).encode('ascii'))

# On Windows, pass bytes to subprocess doesn't test how Python decodes the
# command line, but how subprocess does decode bytes to unicode. Python
# doesn't decode the command line because Windows provides directly the
Expand All @@ -179,7 +244,7 @@ def test_undecodable_code(self):
code = (
b'import locale; '
b'print(ascii("' + undecodable + b'"), '
b'locale.getpreferredencoding())')
b'locale.getencoding())')
p = subprocess.Popen(
[sys.executable, "-c", code],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
Expand Down Expand Up @@ -214,7 +279,6 @@ def test_invalid_utf8_arg(self):
#
# Test with default config, in the C locale, in the Python UTF-8 Mode.
code = 'import sys, os; s=os.fsencode(sys.argv[1]); print(ascii(s))'
base_cmd = [sys.executable, '-c', code]

# TODO: RUSTPYTHON
@unittest.expectedFailure
Expand Down Expand Up @@ -277,6 +341,23 @@ def test_osx_android_utf8(self):
self.assertEqual(stdout, expected)
self.assertEqual(p.returncode, 0)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_non_interactive_output_buffering(self):
code = textwrap.dedent("""
import sys
out = sys.stdout
print(out.isatty(), out.write_through, out.line_buffering)
err = sys.stderr
print(err.isatty(), err.write_through, err.line_buffering)
""")
args = [sys.executable, '-c', code]
proc = subprocess.run(args, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, text=True, check=True)
self.assertEqual(proc.stdout,
'False False False\n'
'False False True\n')

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_unbuffered_output(self):
Expand Down Expand Up @@ -320,6 +401,8 @@ def test_large_PYTHONPATH(self):
self.assertIn(path1.encode('ascii'), out)
self.assertIn(path2.encode('ascii'), out)

@unittest.skipIf(sys.flags.safe_path,
'PYTHONSAFEPATH changes default sys.path')
def test_empty_PYTHONPATH_issue16309(self):
# On Posix, it is documented that setting PATH to the
# empty string is equivalent to not setting PATH at all,
Expand Down Expand Up @@ -369,23 +452,25 @@ def check_input(self, code, expected):
stdout, stderr = proc.communicate()
self.assertEqual(stdout.rstrip(), expected)

@unittest.skipIf(sys.platform == "win32", "AssertionError: b"'abc\\r'" != b"'abc'"")
# TODO: RUSTPYTHON
@unittest.skipIf(sys.platform.startswith('win'), "TODO: RUSTPYTHON windows has \n troubles")
def test_stdin_readline(self):
# Issue #11272: check that sys.stdin.readline() replaces '\r\n' by '\n'
# on Windows (sys.stdin is opened in binary mode)
self.check_input(
"import sys; print(repr(sys.stdin.readline()))",
b"'abc\\n'")

@unittest.skipIf(sys.platform == "win32", "AssertionError: b"'abc\\r'" != b"'abc'"")
# TODO: RUSTPYTHON
@unittest.skipIf(sys.platform.startswith('win'), "TODO: RUSTPYTHON windows has \n troubles")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think other tests are marked TODO: RUSTPYTHON, universal newlines, but I like this description better, as it's a clearer statement of the problem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this can probably be opened as an issue, seems like it wouldn't be too difficult to fix.

def test_builtin_input(self):
# Issue #11272: check that input() strips newlines ('\n' or '\r\n')
self.check_input(
"print(repr(input()))",
b"'abc'")

# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(sys.platform.startswith('win'), "TODO: RUSTPYTHON windows has \n troubles")
def test_output_newline(self):
# Issue 13119 Newline for print() should be \r\n on Windows.
code = """if 1:
Expand All @@ -398,10 +483,10 @@ def test_output_newline(self):

if sys.platform == 'win32':
self.assertEqual(b'1\r\n2\r\n', out)
self.assertEqual(b'3\r\n4', err)
self.assertEqual(b'3\r\n4\r\n', err)
else:
self.assertEqual(b'1\n2\n', out)
self.assertEqual(b'3\n4', err)
self.assertEqual(b'3\n4\n', err)

def test_unmached_quote(self):
# Issue #10206: python program starting with unmatched quote
Expand Down Expand Up @@ -459,7 +544,7 @@ def preexec():
stderr=subprocess.PIPE,
preexec_fn=preexec)
out, err = p.communicate()
self.assertEqual(support.strip_python_stderr(err), b'')
self.assertEqual(err, b'')
self.assertEqual(p.returncode, 42)

# TODO: RUSTPYTHON
Expand Down Expand Up @@ -527,7 +612,7 @@ def test_del___main__(self):
# the dict whereas the module was destroyed
filename = os_helper.TESTFN
self.addCleanup(os_helper.unlink, filename)
with open(filename, "w") as script:
with open(filename, "w", encoding="utf-8") as script:
print("import sys", file=script)
print("del sys.modules['__main__']", file=script)
assert_python_ok(filename)
Expand Down Expand Up @@ -558,24 +643,25 @@ def test_unknown_options(self):
'Cannot run -I tests when PYTHON env vars are required.')
def test_isolatedmode(self):
self.verify_valid_flag('-I')
self.verify_valid_flag('-IEs')
self.verify_valid_flag('-IEPs')
rc, out, err = assert_python_ok('-I', '-c',
'from sys import flags as f; '
'print(f.no_user_site, f.ignore_environment, f.isolated)',
'print(f.no_user_site, f.ignore_environment, f.isolated, f.safe_path)',
# dummyvar to prevent extraneous -E
dummyvar="")
self.assertEqual(out.strip(), b'1 1 1')
self.assertEqual(out.strip(), b'1 1 1 True')
with os_helper.temp_cwd() as tmpdir:
fake = os.path.join(tmpdir, "uuid.py")
main = os.path.join(tmpdir, "main.py")
with open(fake, "w") as f:
with open(fake, "w", encoding="utf-8") as f:
f.write("raise RuntimeError('isolated mode test')\n")
with open(main, "w") as f:
with open(main, "w", encoding="utf-8") as f:
f.write("import uuid\n")
f.write("print('ok')\n")
# Use -E to ignore PYTHONSAFEPATH env var
self.assertRaises(subprocess.CalledProcessError,
subprocess.check_output,
[sys.executable, main], cwd=tmpdir,
[sys.executable, '-E', main], cwd=tmpdir,
stderr=subprocess.DEVNULL)
out = subprocess.check_output([sys.executable, "-I", main],
cwd=tmpdir)
Expand Down Expand Up @@ -716,7 +802,8 @@ def test_xdev(self):

def check_warnings_filters(self, cmdline_option, envvar, use_pywarning=False):
if use_pywarning:
code = ("import sys; from test.support.import_helper import import_fresh_module; "
code = ("import sys; from test.support.import_helper import "
"import_fresh_module; "
"warnings = import_fresh_module('warnings', blocked=['_warnings']); ")
else:
code = "import sys, warnings; "
Expand Down Expand Up @@ -846,6 +933,43 @@ def test_parsing_error(self):
self.assertTrue(proc.stderr.startswith(err_msg), proc.stderr)
self.assertNotEqual(proc.returncode, 0)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_int_max_str_digits(self):
code = "import sys; print(sys.flags.int_max_str_digits, sys.get_int_max_str_digits())"

assert_python_failure('-X', 'int_max_str_digits', '-c', code)
assert_python_failure('-X', 'int_max_str_digits=foo', '-c', code)
assert_python_failure('-X', 'int_max_str_digits=100', '-c', code)
assert_python_failure('-X', 'int_max_str_digits', '-c', code,
PYTHONINTMAXSTRDIGITS='4000')

assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='foo')
assert_python_failure('-c', code, PYTHONINTMAXSTRDIGITS='100')

def res2int(res):
out = res.out.strip().decode("utf-8")
return tuple(int(i) for i in out.split())

res = assert_python_ok('-c', code)
self.assertEqual(res2int(res), (-1, sys.get_int_max_str_digits()))
res = assert_python_ok('-X', 'int_max_str_digits=0', '-c', code)
self.assertEqual(res2int(res), (0, 0))
res = assert_python_ok('-X', 'int_max_str_digits=4000', '-c', code)
self.assertEqual(res2int(res), (4000, 4000))
res = assert_python_ok('-X', 'int_max_str_digits=100000', '-c', code)
self.assertEqual(res2int(res), (100000, 100000))

res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='0')
self.assertEqual(res2int(res), (0, 0))
res = assert_python_ok('-c', code, PYTHONINTMAXSTRDIGITS='4000')
self.assertEqual(res2int(res), (4000, 4000))
res = assert_python_ok(
'-X', 'int_max_str_digits=6000', '-c', code,
PYTHONINTMAXSTRDIGITS='4000'
)
self.assertEqual(res2int(res), (6000, 6000))


@unittest.skipIf(interpreter_requires_environment(),
'Cannot run -I tests when PYTHON env vars are required.')
Expand Down Expand Up @@ -874,20 +998,41 @@ def test_sys_flags_not_set(self):
# Issue 31845: a startup refactoring broke reading flags from env vars
expected_outcome = """
(sys.flags.debug == sys.flags.optimize ==
sys.flags.dont_write_bytecode == sys.flags.verbose == 0)
sys.flags.dont_write_bytecode ==
sys.flags.verbose == sys.flags.safe_path == 0)
"""
self.run_ignoring_vars(
expected_outcome,
PYTHONDEBUG="1",
PYTHONOPTIMIZE="1",
PYTHONDONTWRITEBYTECODE="1",
PYTHONVERBOSE="1",
PYTHONSAFEPATH="1",
)


def test_main():
support.run_unittest(CmdLineTest, IgnoreEnvironmentTest)
class SyntaxErrorTests(unittest.TestCase):
def check_string(self, code):
proc = subprocess.run([sys.executable, "-"], input=code,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.assertNotEqual(proc.returncode, 0)
self.assertNotEqual(proc.stderr, None)
self.assertIn(b"\nSyntaxError", proc.stderr)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_tokenizer_error_with_stdin(self):
self.check_string(b"(1+2+3")

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_decoding_error_at_the_end_of_the_line(self):
self.check_string(br"'\u1f'")


def tearDownModule():
support.reap_children()


if __name__ == "__main__":
test_main()
unittest.main()