
Fixes https://github.com/astral-sh/ty/issues/311.
5.5 KiB
global
references
Implicit global in function
A name reference to a never-defined symbol in a function is implicitly a global lookup.
x = 1
def f():
reveal_type(x) # revealed: Unknown | Literal[1]
Explicit global in function
x = 1
def f():
global x
reveal_type(x) # revealed: Unknown | Literal[1]
Unassignable type in function
x: int = 1
def f():
y: int = 1
# error: [invalid-assignment] "Object of type `Literal[""]` is not assignable to `int`"
y = ""
global x
# error: [invalid-assignment] "Object of type `Literal[""]` is not assignable to `int`"
x = ""
global z
# error: [invalid-assignment] "Object of type `Literal[""]` is not assignable to `int`"
z = ""
z: int
Nested intervening scope
A global
statement causes lookup to skip any bindings in intervening scopes:
x: int = 1
def outer():
x: str = ""
def inner():
global x
reveal_type(x) # revealed: int
Narrowing
An assignment following a global
statement should narrow the type in the local scope after the
assignment.
x: int | None
def f():
global x
x = 1
reveal_type(x) # revealed: Literal[1]
Same for an if
statement:
x: int | None
def f():
# The `global` keyword isn't necessary here, but this is testing that it doesn't get in the way
# of narrowing.
global x
if x == 1:
y: int = x # allowed, because x cannot be None in this branch
nonlocal
and global
A binding cannot be both nonlocal
and global
. This should emit a semantic syntax error. CPython
marks the nonlocal
line, while mypy
, pyright
, and ruff
(PLE0115
) mark the global
line.
x = 1
def f():
x = 1
def g() -> None:
nonlocal x
global x # error: [invalid-syntax] "name `x` is nonlocal and global"
x = None
Global declaration after global
statement
def f():
global x
y = x
x = 1 # No error.
x = 2
Semantic syntax errors
Using a name prior to its global
declaration in the same scope is a syntax error.
x = 1
y = 2
def f():
print(x)
global x # error: [invalid-syntax] "name `x` is used prior to global declaration"
print(x)
def f():
global x
print(x)
global x # error: [invalid-syntax] "name `x` is used prior to global declaration"
print(x)
def f():
print(x)
global x, y # error: [invalid-syntax] "name `x` is used prior to global declaration"
print(x)
def f():
global x, y
print(x)
global x, y # error: [invalid-syntax] "name `x` is used prior to global declaration"
print(x)
def f():
x = 1
global x # error: [invalid-syntax] "name `x` is used prior to global declaration"
x = 1
def f():
global x
x = 1
global x # error: [invalid-syntax] "name `x` is used prior to global declaration"
x = 1
def f():
del x
global x, y # error: [invalid-syntax] "name `x` is used prior to global declaration"
del x
def f():
global x, y
del x
global x, y # error: [invalid-syntax] "name `x` is used prior to global declaration"
del x
def f():
del x
global x # error: [invalid-syntax] "name `x` is used prior to global declaration"
del x
def f():
global x
del x
global x # error: [invalid-syntax] "name `x` is used prior to global declaration"
del x
def f():
del x
global x, y # error: [invalid-syntax] "name `x` is used prior to global declaration"
del x
def f():
global x, y
del x
global x, y # error: [invalid-syntax] "name `x` is used prior to global declaration"
del x
def f():
print(f"{x=}")
global x # error: [invalid-syntax] "name `x` is used prior to global declaration"
# still an error in module scope
x = None
global x # error: [invalid-syntax] "name `x` is used prior to global declaration"
Local bindings override preceding global
bindings
x = 42
def f():
global x
reveal_type(x) # revealed: Unknown | Literal[42]
x = "56"
reveal_type(x) # revealed: Literal["56"]
Local assignment prevents falling back to the outer scope
x = 42
def f():
# error: [unresolved-reference] "Name `x` used when not defined"
reveal_type(x) # revealed: Unknown
x = "56"
reveal_type(x) # revealed: Literal["56"]
Annotating a global
binding is a syntax error
x: int = 1
def f():
global x
x: str = "foo" # error: [invalid-syntax] "annotated name `x` can't be global"
Global declarations affect the inferred type of the binding
Even if the global
declaration isn't used in an assignment, we conservatively assume it could be:
x = 1
def f():
global x
# TODO: reveal_type(x) # revealed: Unknown | Literal["1"]
Global variables need an explicit definition in the global scope
You're allowed to use the global
keyword to define new global variables that don't have any
explicit definition in the global scope, but we consider that fishy and prefer to lint on it:
x = 1
y: int
# z is neither bound nor declared in the global scope
def f():
global x, y, z # error: [unresolved-global] "Invalid global declaration of `z`: `z` has no declarations or bindings in the global scope"
You don't need a definition for implicit globals, but you do for built-ins:
def f():
global __file__ # allowed, implicit global
global int # error: [unresolved-global] "Invalid global declaration of `int`: `int` has no declarations or bindings in the global scope"