1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# Regression coverage for the `global X` ordering diagnostics, including the
# CPython quirk reported in https://github.com/pydantic/monty/issues/423:
# `import` bindings DO NOT count as "assigned to before global declaration"
# even though every other binding form does.
#
# Each `def` below stresses one rule. The function bodies are never executed
# — defining them is enough to trigger prepare-time scope validation. The
# file ends with the one form that IS expected to raise a SyntaxError, which
# the harness verifies through the `# Raise=` comment.
# --- Import bindings are accepted by CPython ---------------------------
# `import X`, `import X as Y`, and `from M import X [as Y]` are treated as
# "soft" bindings that don't conflict with a subsequent `global` in the same
# scope (issue #423). The `global` declaration takes precedence, making the
# import effectively rebind the module-level name.
def import_plain_then_global():
import os
global os # type: ignore[reportAssignmentBeforeGlobalDeclaration]
def import_as_then_global():
import os as alias_plain
global alias_plain # type: ignore[reportAssignmentBeforeGlobalDeclaration]
def from_import_then_global():
from os import path
global path # type: ignore[reportAssignmentBeforeGlobalDeclaration]
def from_import_as_then_global():
from os import path as alias_path
global alias_path # type: ignore[reportAssignmentBeforeGlobalDeclaration]
# --- Reads inside a nested scope don't pollute this scope --------------
# Reads inside a `lambda` body or nested `def` happen in a sub-scope, so
# they don't trigger this scope's "used prior to" check.
def lambda_read_then_global():
lambda: lambda_target
global lambda_target
def nested_def_read_then_global():
def nested_reader():
return nested_target
_ = nested_reader # keep pyright happy about the nested def
global nested_target
def nested_target():
pass
# --- The non-import binding form that DOES error -----------------------
# A plain assignment before `global` is the canonical SyntaxError.
def f():
x = 1
global x # type: ignore[reportAssignmentBeforeGlobalDeclaration]
f()
# Raise=SyntaxError("name 'x' is assigned to before global declaration")