Skip to content
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

mypy reports no issues on access of undefined variable #18708

Open
jgogstad opened this issue Feb 19, 2025 · 5 comments
Open

mypy reports no issues on access of undefined variable #18708

jgogstad opened this issue Feb 19, 2025 · 5 comments
Labels
bug mypy got something wrong topic-possibly-undefined possibly-undefined error code

Comments

@jgogstad
Copy link

Bug Report

To Reproduce

# foo.py
def print_foo():
    print(foo)

if __name__ == '__main__':
    foo = "foobar"

https://mypy-play.net/?mypy=latest&python=3.12&gist=d8a24e474f61a151f549728e14173383

Expected Behavior

mypy should report an error

the name foo is not guaranteed to be part of the scope in print_foo. E.g. the following code will break

import foo
foo.print_foo()

Actual Behavior

mypy outputs "Success: no issues found in 1 source file"

Your Environment

see playground link

  • Mypy version used: 1.15
  • Mypy command-line flags: none (but happens with --strict also)
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used: 3.11, 3.12, 3.13
@jgogstad jgogstad added the bug mypy got something wrong label Feb 19, 2025
@tusharsadhwani
Copy link
Contributor

tusharsadhwani commented Feb 19, 2025

It's hard for a type checker to catch such things as these are runtime behaviour dependent.

Here's another even simpler example:

https://mypy-play.net/?mypy=latest&python=3.12&gist=f2e5e3b988e9a6cac9f9c8d54d26d75e&enable-error-code=possibly-undefined

class C:
    x = 1
    del x

print(C.x)

Deducing that x doesn't exist on C anymore in the general case is a fairly hard problem, and just solving it for this one case, or just for the if __name__ == '__main__' case won't really be a good solution.


My way of working around this is to always use a main function to avoid having optionally defined globals in my python file:

def main():
    ...

if __name__ == '__main__':
    main()

@A5rocks
Copy link
Collaborator

A5rocks commented Feb 20, 2025

At the very least possibly-undefined should warn on this (and doesn't, see https://mypy-play.net/?mypy=latest&python=3.12&gist=d8a24e474f61a151f549728e14173383&enable-error-code=possibly-undefined).

... well at least maybe it should, I don't know if the fallout from this wouldn't make it worth it.

@A5rocks A5rocks added the topic-possibly-undefined possibly-undefined error code label Feb 20, 2025
@jgogstad
Copy link
Author

It's hard for a type checker to catch such things as these are runtime behaviour dependent.

I think there is a crucial difference between the examples. In the class C example, the statically determined scope of the module contains C.x, so it's not outrageous that print(C.x) typechecks—anything else feels like it's approaching the halting problem to me, but this is not my area of expertise. In the test_foo/foo example, the statically determined scope of test_foo does not contain foo.

I understand that the typechecker attempts to determine the dynamic scope in both cases and fails in both cases (and it's great that it attempts to determine this!). In the first example neither the dynamic nor the static scope contains the attribute, yet the code typechecks, so that feels definitely like a bug.

Does that make sense?

My way of working around this is to always use a main function to avoid having optionally defined globals in my python file:

That's a good workaround, just never write to the module namespace, thanks

@tusharsadhwani
Copy link
Contributor

In the test_foo/foo example, the statically determined scope of test_foo does not contain foo.

I don't think that's true. The last line in the file creates foo in the module scope of test_foo. Optionally, but it still may exist.

@tusharsadhwani
Copy link
Contributor

If you mean to say that conditionally instantiated variables should not be assumed to exist in a scope, it'll break a lot more stuff.

For example:

if TYPE_CHECKING:
  from module import MyClass
def foo(x: int):
  if x < 0:
    abs_x = abs(x)
  else:
    return x

  return abs_x

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-possibly-undefined possibly-undefined error code
Projects
None yet
Development

No branches or pull requests

3 participants