python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.22k stars 2.78k forks source link

Document that stubtest will not flag private names #15414

Open rayzchen opened 1 year ago

rayzchen commented 1 year ago

Feature

I would like to have TypeVars in my stubs that do not appear in my source code, but stubtest will flag the typevar as not present at runtime. Therefore, I decided to put them all in if TYPE_CHECKING statements at the beginning of stubs, like:

from typing import TYPE_CHECKING, TypeVar, Generic

if TYPE_CHECKING:
    T = TypeVar("T")

class SomeClass(Generic[T]):
    ...

Pitch

It is not ideal to create a regex for the TypeVar variable names in an allowlist, and it would be much easier to add other type aliases that aren't in the source code but are in stub files.

AlexWaygood commented 1 year ago

The standard solution to this problem — and the solution we use extensively in typeshed — is to give the TypeVars in your stubs names with leading underscores (_T instead of T). If stubtest sees a name in the stub that doesn't exist at runtime, it won't complain about it if the name starts with a leading underscore — it will assume that the name is "private to the stub".

It looks like this trick isn't documented at https://mypy.readthedocs.io/en/stable/stubtest.html, however; maybe we should document it!

hauntsaninja commented 1 year ago

Yes, Alex's suggestion will also e.g. prevent IDEs from suggesting the symbol in completions

rayzchen commented 1 year ago

How would I stub this code without this error?

class Foo:
    class _Bar: pass
    Bar = _Bar()
    del _Bar

a = Foo.Bar

When I use the type Foo._Bar, stubtest complains that it cannot find Foo._Bar at runtime.

JelleZijlstra commented 1 year ago

That doesn't sound like something stubtest could reasonably support. Use the stubtest allowlist.

rayzchen commented 1 year ago

Is the allowlist the only way to silence errors? Or is there a way to silence errors relating to certain lines?

hauntsaninja commented 1 year ago

The allowlist is the only way to silence a stubtest error. That said, there are other things you could do to make a stubtest error go away, for instance, using if TYPE_CHECKING to change what mypy sees, or doing things like if not os.environ.get("KEEP_THINGS_AROUND_FOR_STUBTEST"): del _Bar

JelleZijlstra commented 1 year ago

Reopening to reflect @AlexWaygood's suggestion above to improve the documentation; @rayzchen's issue is remote from the original purpose of the issue.