Skip to content
Merged
Show file tree
Hide file tree
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
3 changes: 2 additions & 1 deletion Lib/test/list_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from functools import cmp_to_key

from test import seq_tests
from test.support import ALWAYS_EQ, NEVER_EQ
from test.support import ALWAYS_EQ, NEVER_EQ, run_with_limited_c_stack
from test.support import skip_emscripten_stack_overflow, skip_wasi_stack_overflow


Expand Down Expand Up @@ -60,6 +60,7 @@ def test_repr(self):
self.assertEqual(str(a2), "[0, 1, 2, [...], 3]")
self.assertEqual(repr(a2), "[0, 1, 2, [...], 3]")

@run_with_limited_c_stack(200_000)
@skip_wasi_stack_overflow()
@skip_emscripten_stack_overflow()
def test_repr_deep(self):
Expand Down
1 change: 1 addition & 0 deletions Lib/test/mapping_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ def __repr__(self):
d = self._full_mapping({1: BadRepr()})
self.assertRaises(Exc, repr, d)

@support.run_with_limited_c_stack()
@support.skip_wasi_stack_overflow()
@support.skip_emscripten_stack_overflow()
@support.skip_if_sanitizer("requires deep stack", ub=True)
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,11 @@ def test_deprecation_warning(self):
"The 'January' attribute is deprecated, use 'JANUARY' instead"
):
calendar.January
with self.assertWarnsRegex(
DeprecationWarning,
"The 'February' attribute is deprecated, use 'FEBRUARY' instead"
):
calendar.February

def test_isleap(self):
# Make sure that the return is right for a few years, and
Expand Down
173 changes: 117 additions & 56 deletions Lib/test/test_codeop.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,64 @@
"""
import unittest
import warnings
from test.support import warnings_helper
from test.support import subTests, warnings_helper
from textwrap import dedent
import functools

from codeop import compile_command, PyCF_DONT_IMPLY_DEDENT
from codeop import compile_command, CommandCompiler, Compile
from codeop import PyCF_DONT_IMPLY_DEDENT, PyCF_ONLY_AST
import ast


WRAPPING_COMPILERS = [compile_command, CommandCompiler()]
RAW_COMPILERS = [Compile()]
COMPILERS = WRAPPING_COMPILERS + RAW_COMPILERS

class CodeopTests(unittest.TestCase):

def assertValid(self, str, symbol='single'):
class CodeopTests(unittest.TestCase):
def assertValid(self, str, symbol='single', *, compiler):
'''succeed iff str is a valid piece of code'''
expected = compile(str, "<input>", symbol, PyCF_DONT_IMPLY_DEDENT)
self.assertEqual(compile_command(str, "<input>", symbol), expected)
self.assertEqual(compiler(str, "<input>", symbol), expected)

def assertIncomplete(self, str, symbol='single'):
def assertIncomplete(self, str, symbol='single', *, compiler):
'''succeed iff str is the start of a valid piece of code'''
self.assertEqual(compile_command(str, symbol=symbol), None)

def assertInvalid(self, str, symbol='single', is_syntax=1):
if compiler in WRAPPING_COMPILERS:
self.assertEqual(compiler(str, "<input>", symbol=symbol), None)
else:
# Compile has should raise like built-in compile
with self.assertRaises(SyntaxError) as cm_original_error:
compile(str, "<input>", symbol, compiler.flags)
expected_error = cm_original_error.exception
with self.assertRaises(type(expected_error)) as cm_wrapped_error:
compiler(str, "<input>", symbol=symbol)
self.assertEqual(
expected_error.args,
cm_wrapped_error.exception.args
)

def assertInvalid(self, str, symbol='single', is_syntax=1, *, compiler):
'''succeed iff str is the start of an invalid piece of code'''
try:
compile_command(str,symbol=symbol)
compiler(str,"<input>", symbol=symbol)
self.fail("No exception raised for invalid code")
except SyntaxError:
self.assertTrue(is_syntax)
except OverflowError:
self.assertTrue(not is_syntax)

def test_valid(self):
av = self.assertValid

# special case
self.assertEqual(compile_command(""),
compile("pass", "<input>", 'single',
PyCF_DONT_IMPLY_DEDENT))
self.assertEqual(compile_command("\n"),
compile("pass", "<input>", 'single',
PyCF_DONT_IMPLY_DEDENT))

@subTests('compiler', WRAPPING_COMPILERS)
def test_empty(self, compiler):
self.assertEqual(
compiler("", "<input>", 'single'),
compile("pass", "<input>", 'single', PyCF_DONT_IMPLY_DEDENT))
self.assertEqual(
compiler("\n", "<input>", 'single'),
compile("pass", "<input>", 'single', PyCF_DONT_IMPLY_DEDENT))

@subTests('compiler', COMPILERS)
def test_valid(self, compiler):
av = functools.partial(self.assertValid, compiler=compiler)
av("a = 1")
av("\na = 1")
av("a = 1\n")
Expand Down Expand Up @@ -93,8 +114,9 @@ def test_valid(self):
av("@a.b.c\ndef f():\n pass\n")

@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: <code object <module> at 0xc99532080 file "<input>", line 1> != None
def test_incomplete(self):
ai = self.assertIncomplete
@subTests('compiler', COMPILERS)
def test_incomplete(self, compiler):
ai = functools.partial(self.assertIncomplete, compiler=compiler)

ai("(a **")
ai("(a,b,")
Expand Down Expand Up @@ -227,8 +249,9 @@ def test_incomplete(self):
ai('a = f"""')
ai('a = \\')

def test_invalid(self):
ai = self.assertInvalid
@subTests('compiler', COMPILERS)
def test_invalid(self, compiler):
ai = functools.partial(self.assertInvalid, compiler=compiler)
ai("a b")

ai("a @")
Expand Down Expand Up @@ -264,68 +287,106 @@ def test_invalid(self):

ai("[i for i in range(10)] = (1, 2, 3)")

def test_invalid_exec(self):
ai = self.assertInvalid
@subTests('compiler', COMPILERS)
def test_invalid_exec(self, compiler):
ai = functools.partial(self.assertInvalid, compiler=compiler)
ai("raise = 4", symbol="exec")
ai('def a-b', symbol='exec')
ai('await?', symbol='exec')
ai('=!=', symbol='exec')
ai('a await raise b', symbol='exec')
ai('a await raise b?+1', symbol='exec')

def test_filename(self):
self.assertEqual(compile_command("a = 1\n", "abc").co_filename,
compile("a = 1\n", "abc", 'single').co_filename)
self.assertNotEqual(compile_command("a = 1\n", "abc").co_filename,
compile("a = 1\n", "def", 'single').co_filename)

def test_warning(self):
@subTests('compiler', COMPILERS)
def test_filename(self, compiler):
self.assertEqual(
compiler("a = 1\n", "abc", "single").co_filename,
compile("a = 1\n", "abc", 'single').co_filename
)
self.assertNotEqual(
compiler("a = 1\n", "abc", "single").co_filename,
compile("a = 1\n", "def", 'single').co_filename
)

def assertReturnsModule(self, code, compiler):
retval = compiler(code, "<input>", 'exec', PyCF_ONLY_AST)
self.assertIsInstance(retval, ast.Module)

@subTests('compiler', RAW_COMPILERS)
def test_ast_return_value(self, compiler):
validate_ast = self.assertReturnsModule
validate_ast("x = 5", compiler)
validate_ast("\nx = 5", compiler)
validate_ast("x = 5\n", compiler)
validate_ast("x = 5\n\n", compiler)
validate_ast("\n\nx = 5\n\n", compiler)

@subTests('compiler', COMPILERS)
def test_warning(self, compiler):
# Test that the warning is only returned once.
with warnings_helper.check_warnings(
('"is" with \'str\' literal', SyntaxWarning),
('"\\\\e" is an invalid escape sequence', SyntaxWarning),
) as w:
compile_command(r"'\e' is 0")
self.assertEqual(len(w.warnings), 2)
compiler(r"'\e' is 0", "<input>", "single")
self.assertEqual(len(w.warnings), 2)

# bpo-41520: check SyntaxWarning treated as an SyntaxError
with warnings.catch_warnings(), self.assertRaises(SyntaxError):
warnings.simplefilter('error', SyntaxWarning)
compile_command('1 is 1', symbol='exec')
compiler('1 is 1', "<input>", 'exec')

# Check SyntaxWarning treated as an SyntaxError
with warnings.catch_warnings(), self.assertRaises(SyntaxError):
warnings.simplefilter('error', SyntaxWarning)
compile_command(r"'\e'", symbol='exec')
compiler(r"'\e'", "<input>", 'exec')

def test_incomplete_warning(self):
@subTests('compiler', WRAPPING_COMPILERS)
def test_incomplete_warning(self, compiler):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
self.assertIncomplete("'\\e' + (")
compiler("'\\e' + (")
self.assertEqual(w, [])

@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 0 != 1
def test_invalid_warning(self):
@subTests('compiler', RAW_COMPILERS)
def test_raw_raises_error(self, compiler):
warnings_cm = warnings_helper.check_warnings(
('"\\\\e" is an invalid esceape sequence', SyntaxWarning)
)
with self.assertRaises(SyntaxError), warnings_cm as w:
compiler("'\\e' + (", "<input>", 'single')
self.assertEqual(len(w.warnings), 1)

@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 0 != 1
@subTests('compiler', COMPILERS)
def test_invalid_warning(self, compiler):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
self.assertInvalid("'\\e' 1")
self.assertInvalid("'\\e' 1", compiler=compiler)
self.assertEqual(len(w), 1)
self.assertEqual(w[0].category, SyntaxWarning)
self.assertRegex(str(w[0].message), 'invalid escape sequence')
self.assertEqual(w[0].filename, '<input>')

def assertSyntaxErrorMatches(self, code, message):
with self.subTest(code):
with self.assertRaisesRegex(SyntaxError, message):
compile_command(code, symbol='exec')

def test_syntax_errors(self):
self.assertSyntaxErrorMatches(
dedent("""\
for warning in w:
self.assertEqual(warning.category, SyntaxWarning)
self.assertRegex(str(warning), 'invalid escape sequence')
self.assertEqual(warning.filename, '<input>')

@subTests('compiler', COMPILERS)
def test_syntax_errors(self, compiler):
code = dedent("""\
def foo(x,x):
pass
"""), "duplicate argument 'x' in function definition")

""")
message = "duplicate argument 'x' in function definition"
with self.assertRaisesRegex(SyntaxError, message):
compiler(code, "<input>", 'exec')

@subTests('compiler', RAW_COMPILERS)
def test_future_imports(self, compiler):
original_flags = compiler.flags
compiler('from __future__ import annotations', "<input>", 'single')
self.assertGreater(compiler.flags, original_flags)
# reset flags to ensure test has no side-effects
compiler.flags = original_flags


if __name__ == "__main__":
Expand Down
65 changes: 45 additions & 20 deletions Lib/test/test_compileall.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,24 @@ def temporary_pycache_prefix(self):
finally:
sys.pycache_prefix = old_prefix

@contextlib.contextmanager
def no_pycache_prefix(self):
"""Ignore any ambient pycache prefix for the duration of the test.

Some tests assume bytecode is written next to the source in a
__pycache__ directory. When the test suite is run with
PYTHONPYCACHEPREFIX set, neutralize it both in this process (used by
cache_from_source) and in any spawned subprocesses.
"""
old_prefix = sys.pycache_prefix
sys.pycache_prefix = None
try:
with os_helper.EnvironmentVarGuard() as env:
env.unset('PYTHONPYCACHEPREFIX')
yield
finally:
sys.pycache_prefix = old_prefix

def _get_run_args(self, args):
return [*support.optim_args_from_interpreter_flags(),
'-S', '-m', 'compileall',
Expand Down Expand Up @@ -648,15 +666,16 @@ def test_legacy_paths(self):
def test_multiple_runs(self):
# Bug 8527 reported that multiple calls produced empty
# __pycache__/__pycache__ directories.
self.assertRunOK('-q', self.pkgdir)
# Verify the __pycache__ directory contents.
self.assertTrue(os.path.exists(self.pkgdir_cachedir))
cachecachedir = os.path.join(self.pkgdir_cachedir, '__pycache__')
self.assertFalse(os.path.exists(cachecachedir))
# Call compileall again.
self.assertRunOK('-q', self.pkgdir)
self.assertTrue(os.path.exists(self.pkgdir_cachedir))
self.assertFalse(os.path.exists(cachecachedir))
with self.no_pycache_prefix():
self.assertRunOK('-q', self.pkgdir)
# Verify the __pycache__ directory contents.
self.assertTrue(os.path.exists(self.pkgdir_cachedir))
cachecachedir = os.path.join(self.pkgdir_cachedir, '__pycache__')
self.assertFalse(os.path.exists(cachecachedir))
# Call compileall again.
self.assertRunOK('-q', self.pkgdir)
self.assertTrue(os.path.exists(self.pkgdir_cachedir))
self.assertFalse(os.path.exists(cachecachedir))

@without_source_date_epoch # timestamp invalidation test
def test_force(self):
Expand Down Expand Up @@ -729,10 +748,13 @@ def test_symlink_loop(self):
script_helper.make_pkg(pkg)
os.symlink('.', os.path.join(pkg, 'evil'))
os.symlink('.', os.path.join(pkg, 'evil2'))
self.assertRunOK('-q', self.pkgdir)
self.assertCompiled(os.path.join(
self.pkgdir, 'spam', 'evil', 'evil2', '__init__.py'
))
# This relies on the __pycache__ layout (shared across the symlinked
# paths), so neutralize any ambient PYTHONPYCACHEPREFIX.
with self.no_pycache_prefix():
self.assertRunOK('-q', self.pkgdir)
self.assertCompiled(os.path.join(
self.pkgdir, 'spam', 'evil', 'evil2', '__init__.py'
))

def test_quiet(self):
noisy = self.assertRunOK(self.pkgdir)
Expand Down Expand Up @@ -819,13 +841,16 @@ def test_include_on_stdin(self):
f2 = script_helper.make_script(self.pkgdir, 'f2', '')
f3 = script_helper.make_script(self.pkgdir, 'f3', '')
f4 = script_helper.make_script(self.pkgdir, 'f4', '')
p = script_helper.spawn_python(*(self._get_run_args(()) + ['-i', '-']))
p.stdin.write((f3+os.linesep).encode('ascii'))
script_helper.kill_python(p)
self.assertNotCompiled(f1)
self.assertNotCompiled(f2)
self.assertCompiled(f3)
self.assertNotCompiled(f4)
# spawn_python() runs with -E, ignoring PYTHONPYCACHEPREFIX, so make
# cache_from_source() in this process agree by neutralizing it too.
with self.no_pycache_prefix():
p = script_helper.spawn_python(*(self._get_run_args(()) + ['-i', '-']))
p.stdin.write((f3+os.linesep).encode('ascii'))
script_helper.kill_python(p)
self.assertNotCompiled(f1)
self.assertNotCompiled(f2)
self.assertCompiled(f3)
self.assertNotCompiled(f4)

def test_compiles_as_much_as_possible(self):
bingfn = script_helper.make_script(self.pkgdir, 'bing', 'syntax(error')
Expand Down
Loading
Loading