Files
ruff/crates/red_knot_python_semantic/resources/mdtest/scopes/global.md
Brent Westbrook bfc1650198 [red-knot] Add mdtests for global statement (#17563)
## Summary

This is a first step toward `global` support in red-knot (#15385). I
went through all the matches for `global` in the `mypy/test-data`
directory, but I didn't find anything too interesting that wasn't
already covered by @carljm's suggestions on Discord. I still pulled in a
couple of cases for a little extra variety. I also included a section
from the
[PLE0118](https://docs.astral.sh/ruff/rules/load-before-global-declaration/)
tests in ruff that will become syntax errors once #17463 is merged and
we handle `global` statements.

I don't think I figured out how to use `@Todo` properly, so please let
me know if I need to fix that. I hope this is a good start to the test
suite otherwise.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
2025-04-23 17:18:42 -04:00

3.7 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
    # TODO: error: [invalid-assignment] "Object of type `Literal[""]` is not assignable to `int`"
    x = ""

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
        # TODO: revealed: int
        reveal_type(x)  # revealed: str

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]

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  # TODO: error: [invalid-syntax] "name 'x' is nonlocal and global"
        x = None

Global declaration after global statement

def f():
    global x
    # TODO this should also not be an error
    y = x  # error: [unresolved-reference] "Name `x` used when not defined"
    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

def f():
    print(x)  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x
    print(x)

def f():
    global x
    print(x)  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x
    print(x)

def f():
    print(x)  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x, y
    print(x)

def f():
    global x, y
    print(x)  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x, y
    print(x)

def f():
    x = 1  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x
    x = 1

def f():
    global x
    x = 1  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x
    x = 1

def f():
    del x  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x, y
    del x

def f():
    global x, y
    del x  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x, y
    del x

def f():
    del x  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x
    del x

def f():
    global x
    del x  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x
    del x

def f():
    del x  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x, y
    del x

def f():
    global x, y
    del x  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x, y
    del x

def f():
    print(f"{x=}")  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
    global x

# still an error in module scope
x = None  # TODO: error: [invalid-syntax] name `x` is used prior to global declaration
global x