crates/ty_python_semantic/resources/mdtest/scopes/global.md
global referencesA name reference to a never-defined symbol in a function is implicitly a global lookup.
x = 1
def f():
reveal_type(x) # revealed: Literal[1]
x = 1
def f():
global x
reveal_type(x) # revealed: Literal[1]
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
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
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
A nested function should resolve a global name through the enclosing scope, even if that scope
conditionally rebinds it. Here, the early return means inner only sees the original module
binding:
x = 1
def outer(flag: bool) -> None:
global x
if flag:
x = 2
return
def inner() -> None:
reveal_type(x) # revealed: Literal[1]
Without the early return, the nested function should see both possible bindings. This is a known limitation: we currently infer only the rebound value instead of the union of both:
x = 1
def outer(flag: bool) -> None:
global x
if flag:
x = 2
def inner() -> None:
# TODO: should be `Literal[1, 2]`
reveal_type(x) # revealed: Literal[2]
inner()
nonlocal and globalA 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 statementdef f():
global x
y = x
x = 1 # No error.
x = 2
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"
global bindingsx = 42
def f():
global x
reveal_type(x) # revealed: Literal[42]
x = "56"
reveal_type(x) # revealed: Literal["56"]
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"]
global binding is a syntax errorx: int = 1
def f():
global x
x: str = "foo" # error: [invalid-syntax] "annotated name `x` can't be global"
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"]
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"
Even if a global declaration is unresolved at module scope, nested eager scopes in the same
function should still see a rebinding that already happened:
def factory():
global x # error: [unresolved-global] "Invalid global declaration of `x`: `x` has no declarations or bindings in the global scope"
x = 1
class C:
reveal_type(x) # revealed: Literal[1]
If we try to access a variable in a class before it has been defined, the lookup will fall back to global.
import secrets
x: str = "a"
def f(x: int, y: int):
class C:
reveal_type(x) # revealed: int
class D:
x = None
reveal_type(x) # revealed: None
class E:
reveal_type(x) # revealed: str
x = None
# error: [unresolved-reference]
reveal_type(y) # revealed: Unknown
y = None
# Declarations count as definitions, even if there's no binding.
class F:
reveal_type(x) # revealed: str
x: int
reveal_type(x) # revealed: str
# Explicitly `nonlocal` variables don't count, even if they're bound.
class G:
nonlocal x
reveal_type(x) # revealed: int
x = 42
reveal_type(x) # revealed: Literal[42]
# Possibly-unbound variables get unioned with the fallback lookup.
class H:
if secrets.randbelow(2):
x = None
reveal_type(x) # revealed: None | str