.. productionlist:: python-grammar
global_stmt: "global" `identifier` ("," `identifier`)*
-The :keyword:`global` statement is a declaration which holds for the entire
-current code block. It means that the listed identifiers are to be interpreted
-as globals. It would be impossible to assign to a global variable without
+The :keyword:`global` causes the listed identifiers to be interpreted
+as globals. It would be impossible to assign to a global variable without
:keyword:`!global`, although free variables may refer to globals without being
declared global.
-Names listed in a :keyword:`global` statement must not be used in the same code
-block textually preceding that :keyword:`!global` statement.
-
-Names listed in a :keyword:`global` statement must not be defined as formal
-parameters, or as targets in :keyword:`with` statements or :keyword:`except` clauses, or in a :keyword:`for` target list, :keyword:`class`
-definition, function definition, :keyword:`import` statement, or
-:term:`variable annotations <variable annotation>`.
-
-.. impl-detail::
-
- The current implementation does not enforce some of these restrictions, but
- programs should not abuse this freedom, as future implementations may enforce
- them or silently change the meaning of the program.
+The global statement applies to the entire scope of a function or
+class body. A :exc:`SyntaxError` is raised if a variable is used or
+assigned to prior to its global declaration in the scope.
.. index::
pair: built-in function; exec
-"""Verify that warnings are issued for global statements following use."""
+"""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
self.enterContext(check_warnings())
warnings.filterwarnings("error", module="<test string>")
- def test1(self):
- prog_text_1 = """\
-def wrong1():
- a = 1
- b = 2
- global a
- global b
+ ######################################################
+ ### 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_1, lineno=4, offset=5)
+ check_syntax_error(self, prog_text, lineno=2, offset=5)
- def test2(self):
- prog_text_2 = """\
-def wrong2():
- print(x)
- global x
+ def test_name_after_assign(self):
+ prog_text = """\
+def fn():
+ name_assign = 1
+ global name_assign
"""
- check_syntax_error(self, prog_text_2, lineno=3, offset=5)
+ check_syntax_error(self, prog_text, lineno=3, offset=5)
- def test3(self):
- prog_text_3 = """\
-def wrong3():
- print(x)
- x = 2
- global x
+ def test_name_after_use(self):
+ prog_text = """\
+def fn():
+ print(name_use)
+ global name_use
"""
- check_syntax_error(self, prog_text_3, lineno=4, offset=5)
+ check_syntax_error(self, prog_text, lineno=3, offset=5)
- def test4(self):
- prog_text_4 = """\
-global x
-x = 2
+ def test_name_annot(self):
+ prog_text_3 = """\
+def fn():
+ name_annot: int
+ global name_annot
"""
- # this should work
- compile(prog_text_4, "<test string>", "exec")
+ 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():