typeshed
typeshed copied to clipboard
pyright tests: doesn't support `__getattr__` for resolving submodules
Example: stubs/paramiko/paramiko/kex_ecdh_nist.pyi
contains imports from cryptography
, but pyright can't find cryptograpy
, probably because it's "hidden" in stubs/cryptography
. Cc @erictraut and @jakebailey: Do you have any ideas how we could fix this?
Can you point to a specific example?
On initial investigation, it looks like paramiko
is accessing some modules in cryptography
that are missing stubs. For example stubs/paramiko/paramiko/kex_curve25519.pyi
imports from cryptography.hazmat.primitives.asymmetric.x25519
which has a stub, and the import succeeds. But stubs/paramiko/paramiko/kex_ecdh_nist.pyi
imports from cryptography.hazmat.primitives.asymmetric.ec2
, and there is no stub for that module, so the import fails.
I blame my morning tiredness for missing the 2
at the end of that filename. That said, cryptography/cryptography/hazmat/primitives/asymmetric/__init__.pyi
contains a __getattr__()
definition, meaning that all non-existing sub-modules should exist (as Any
).
Pyright doesn't currently apply the module-level __getattr__
for submodule resolutions. Doing so would be quite expensive at analysis time because it would need to parse and bind every module along the import path to determine whether any of the parent modules contain this directive, so I'd prefer not to implement this unless it's really needed. This is the first time I've heard of a stub that depends on that behavior.
Suppose a stub does from foo.bar import baz
. First we find a folder named foo/
, and this is the only step where the entire import path is needed. Then, to make this work, we would look for def __getattr__
in foo/__init__.py
. Most of the time it shouldn't make things slower, because it's only needed in the less likely case that foo/bar.py
doesn't exist. What am I missing?
The good news is that the ec2
module does not seem to exist at runtime, either, so I'm just going to fix the import. There is no immediate need to fix this and we'll see what the future brings. We should leave this issue open, though.
This likely negatively affects the new PIL stubs (#5594); in the ones I wrote that ship with Pylance (https://github.com/microsoft/python-type-stubs/tree/main/PIL), I made the stub py.typed
with partial
to avoid warnings about unresolved imports (for modules I didn't explicitly mark as incomplete).
In general, we assume that stub packages that aren't partial to be complete, including that a given module doesn't exist. The import resolver doesn't really have access to the analysis info like "this file is marked as incomplete via a __getattr__
" (it doesn't read the contents at all, it's purely based on the filesystem layout).
(retitled the issue to be more specific, feel free to change if I misunderstood what's going on here)