8000 Type of `global` variable is not narrowed in function scopes · Issue #311 · astral-sh/ty · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Type of global variable is not narrowed in function scopes #311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ion-elgreco opened this issue May 10, 2025 · 2 comments
Open

Type of global variable is not narrowed in function scopes #311

ion-elgreco opened this issue May 10, 2025 · 2 comments
Labels
bug < 8000 /a> Something isn't working narrowing related to flow-sensitive type narrowing

Comments

@ion-elgreco
Copy link
ion-elgreco commented May 10, 2025

Summary

class Instance: ...

_instance: Instance | None = None

def get_instance() -> Instance:
    """Lazy static to get the instance"""
    global _instance
    if _instance is None:
        _instance = Instance()
    return _instance

https://play.ty.dev/f0b802a2-9e21-438d-8946-6386a45b45b5

We already checked that if it's None we assign a value to it, so the return value can't be possibly None anymore

@AlexWaygood AlexWaygood added bug Something isn't working narrowing related to flow-sensitive type narrowing labels May 10, 2025
@AlexWaygood AlexWaygood changed the title Return type incorrectly recognized as None Type of global variable is not narrowed in function scopes May 10, 2025
@sharkdp
Copy link
Contributor
sharkdp commented May 12, 2025

Thank you for reporting this. The problem here is that narrowing on globals is inherently unsafe. Consider this:

x: int | None = 1

def reset_x() -> None:
    global x
    x = None

def f() -> None:
    global x

    if x is not None:
        reset_x()
        reveal_type(x)  # ?

Other type checkers such as mypy and pyright report int here, but at runtime, x is None. We might still support this eventually. See #164 for a similar discussion.

@AlexWaygood
Copy link
Member

The problem here is that narrowing on globals is inherently unsafe.

This is true, and it's also possible that code from another thread could mutate the global in a concurrent program.

The unsafety here also applies to narrowing of attributes and subscript expressions, however. You already linked to #164, but just to spell it out:

class Foo:
    x: int | None = 1

def reset_x(t: Foo) -> None:
    t.x = None

def f(t: Foo) -> None:
    if t.x is not None:
        reset_x(t)
        reveal_type(x)  # ?

But we intend to support narrowing for attributes and subscripts nonetheless, since a lot of real-world code relies on it, users strongly expect it to work, and the practical experience of existing type checkers has been that the theoretical unsoundness here rarely causes practical issues in Python code.

I think narrowing for global variables, as suggested in this issue, has more or less the same set of considerations as narrowing for attributes or subscripts. I can't see a strong justification for doing attribute/subscript narrowing, but not global-variable narrowing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working narrowing related to flow-sensitive type narrowing
Projects
None yet
Development

No branches or pull requests

3 participants
0