typeshed icon indicating copy to clipboard operation
typeshed copied to clipboard

pyright tests: doesn't support `__getattr__` for resolving submodules

Open srittau opened this issue 3 years ago • 7 comments

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?

srittau avatar Jun 09 '21 09:06 srittau

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.

erictraut avatar Jun 09 '21 14:06 erictraut

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).

srittau avatar Jun 09 '21 14:06 srittau

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.

erictraut avatar Jun 09 '21 14:06 erictraut

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?

Akuli avatar Jun 09 '21 14:06 Akuli

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.

srittau avatar Jun 09 '21 14:06 srittau

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).

jakebailey avatar Jun 09 '21 19:06 jakebailey

(retitled the issue to be more specific, feel free to change if I misunderstood what's going on here)

hauntsaninja avatar Jun 20 '22 23:06 hauntsaninja