Files
RustPython/Lib/test/test_global.py
Jeong, YunWon 3b4c5f65dd Align compiler and AST behavior with CPython (#8138)
* Align compiler and AST behavior with CPython

Update the compiler pipeline, AST module support, and related VM compile/eval paths toward CPython 3.14 behavior.

Use upstream Ruff parser crates directly and keep RustPython-specific syntax preflight handling in the compile path.

Refresh opcode metadata, snapshots, and targeted tests for the aligned bytecode and AST behavior.

* fix

* Fix PR 8138 CI failures

* Fix match mapping wildcard scan complexity

* fixes

* Use Ruff runtime AST fields

* Minimize residual patch diff

* refactor error handling

* Remove _ast conversion context wrappers

* Clean up _ast conversion glue and compiler warning escalation

Rename the Node conversion parameter `ctx` to `vm` throughout the _ast
module.

Remove the unused compile_program, compile_program_single,
compile_block_expression, and compile_expression forwarders; compile_top
stays as the entry point.

Stash an escalated compiler SyntaxWarning so a non-SyntaxWarning category
propagates unchanged instead of being rewritten to SyntaxError, matching
PyErr_ExceptionMatches(SyntaxWarning) in compiler_warn. Drop the now-unused
CompileWarningError::into_codegen_error.

Replace the PositionalArguments two-variant enum with a struct holding a
shared range and a PositionalArgumentsKind enum.

Assisted-by: Claude

* Represent compiler flags as a CompilerFlags bitflags

Replace the loose PY_CF_*/CO_* i32 constants in compile_mode with a
CompilerFlags bitflags covering the PyCF_* flags and __future__
CO_FUTURE_* bits. The Python-visible ast.PyCF_* attribute values are
derived from it via bits(); parser input modes stay plain consts.

builtins::compile and the compile_string flag handling test flags
through CompilerFlags::contains instead of raw bit math.

Assisted-by: Claude

* Fix PR 8138 test regressions

* Fix PR 8138 CI failures

* Avoid shell interpolation in Linux deps action

* Fix review findings in compiler/AST changes

- frame.rs: compute the MATCH_CLASS class name lazily so a successful
  match no longer takes the type name lock or allocates; the
  __match_args__-not-a-tuple branch reuses it instead of recomputing.
- _ast/constant.rs: add the missing leading space to the with_recursion
  label ("maximum recursion depth exceeded during compilation").
- sys.set_int_max_str_digits: restore the "maxdigits must be 0 or larger
  than {n}" error text.
- codegen: set CO_MAXBLOCKS to 20 in both ir.rs and compile.rs.
- _ast/exception.rs: extract the duplicated excepthandler node guard
  into ensure_excepthandler_node.
- codegen: correct the validate_keywords doc comment.
- Drop redundant "CPython" qualifiers from comments added in this branch.

Assisted-by: Claude
2026-06-26 18:22:08 +09:00

215 lines
6.5 KiB
Python
Vendored

"""This module includes tests for syntax errors that occur when a name
declared as `global` is used in ways that violate the language
specification, such as after assignment, usage, or annotation. The tests
verify that syntax errors are correctly raised for improper `global`
statements following variable use or assignment within functions.
Additionally, it tests various name-binding scenarios for global
variables to ensure correct behavior.
See `test_scope.py` for additional related behavioral tests covering
variable scoping and usage in different contexts.
"""
import contextlib
from test.support import check_syntax_error
from test.support.warnings_helper import check_warnings
from types import SimpleNamespace
import unittest
import warnings
class GlobalTests(unittest.TestCase):
def setUp(self):
self.enterContext(check_warnings())
warnings.filterwarnings("error", module="<test string>")
######################################################
### Syntax error cases as covered in Python/symtable.c
######################################################
def test_name_param(self):
prog_text = """\
def fn(name_param):
global name_param
"""
check_syntax_error(self, prog_text, lineno=2, offset=5)
def test_name_after_assign(self):
prog_text = """\
def fn():
name_assign = 1
global name_assign
"""
check_syntax_error(self, prog_text, lineno=3, offset=5)
def test_name_after_use(self):
prog_text = """\
def fn():
print(name_use)
global name_use
"""
check_syntax_error(self, prog_text, lineno=3, offset=5)
def test_name_annot(self):
prog_text_3 = """\
def fn():
name_annot: int
global name_annot
"""
check_syntax_error(self, prog_text_3, lineno=3, offset=5)
#############################################################
### Tests for global variables across all name binding cases,
### as described in executionmodel.rst
#############################################################
def test_assignment_statement(self):
global name_assignment_statement
value = object()
name_assignment_statement = value
self.assertIs(globals()["name_assignment_statement"], value)
del name_assignment_statement
def test_unpacking_assignment(self):
global name_unpacking_assignment
value = object()
_, name_unpacking_assignment = [None, value]
self.assertIs(globals()["name_unpacking_assignment"], value)
del name_unpacking_assignment
def test_assignment_expression(self):
global name_assignment_expression
value = object()
if name_assignment_expression := value:
pass
self.assertIs(globals()["name_assignment_expression"], value)
del name_assignment_expression
def test_iteration_variable(self):
global name_iteration_variable
value = object()
for name_iteration_variable in [value]:
pass
self.assertIs(globals()["name_iteration_variable"], value)
del name_iteration_variable
def test_func_def(self):
global name_func_def
def name_func_def():
pass
value = name_func_def
self.assertIs(globals()["name_func_def"], value)
del name_func_def
def test_class_def(self):
global name_class_def
class name_class_def:
pass
value = name_class_def
self.assertIs(globals()["name_class_def"], value)
del name_class_def
def test_type_alias(self):
global name_type_alias
type name_type_alias = tuple[int, int]
value = name_type_alias
self.assertIs(globals()["name_type_alias"], value)
del name_type_alias
def test_caught_exception(self):
global name_caught_exc
try:
1 / 0
except ZeroDivisionError as name_caught_exc:
value = name_caught_exc
# `name_caught_exc` is cleared automatically after the except block
self.assertIs(globals()["name_caught_exc"], value)
def test_caught_exception_group(self):
global name_caught_exc_group
try:
try:
1 / 0
except ZeroDivisionError as exc:
raise ExceptionGroup("eg", [exc])
except* ZeroDivisionError as name_caught_exc_group:
value = name_caught_exc_group
# `name_caught_exc` is cleared automatically after the except block
self.assertIs(globals()["name_caught_exc_group"], value)
def test_enter_result(self):
global name_enter_result
value = object()
with contextlib.nullcontext(value) as name_enter_result:
pass
self.assertIs(globals()["name_enter_result"], value)
del name_enter_result
def test_import_result(self):
global name_import_result
value = contextlib
import contextlib as name_import_result
self.assertIs(globals()["name_import_result"], value)
del name_import_result
def test_match(self):
global name_match
value = object()
match value:
case name_match:
pass
self.assertIs(globals()["name_match"], value)
del name_match
def test_match_as(self):
global name_match_as
value = object()
match value:
case _ as name_match_as:
pass
self.assertIs(globals()["name_match_as"], value)
del name_match_as
def test_match_seq(self):
global name_match_seq
value = object()
match (None, value):
case (_, name_match_seq):
pass
self.assertIs(globals()["name_match_seq"], value)
del name_match_seq
def test_match_map(self):
global name_match_map
value = object()
match {"key": value}:
case {"key": name_match_map}:
pass
self.assertIs(globals()["name_match_map"], value)
del name_match_map
def test_match_attr(self):
global name_match_attr
value = object()
match SimpleNamespace(key=value):
case SimpleNamespace(key=name_match_attr):
pass
self.assertIs(globals()["name_match_attr"], value)
del name_match_attr
def setUpModule():
unittest.enterModuleContext(warnings.catch_warnings())
warnings.filterwarnings("error", module="<test string>")
if __name__ == "__main__":
unittest.main()