websockets
websockets copied to clipboard
Lazy import
In the __init__.py the function lazy_import is used to import local modules. The problem with this is that typehints are not showing up which makes coding the project a lot more difficult and less professional. I'm not entirely sure how lazy_import is importing these modules but whatever it is doing is not registering through my typechecker. I am using Pylance and Python visual studio code as my typechecker.

From https://websockets.readthedocs.io/en/stable/reference/index.html:
For convenience, many public APIs can be imported from the websockets package. However, this feature is incompatible with static code analysis. It breaks autocompletion in an IDE or type checking with mypy. If you’re using such tools, use the real import paths.
e.g. websockets.client.WebSocketClientProtocol
websockets.client.WebSocketClientProtocolThis still shows up asAny
It works for me. I suspect it's an issue with your mypy setup.
myk@mYk:websockets/src ‹main›$ cat issue1183.py
import websockets.client
reveal_type(websockets.client.WebSocketClientProtocol)
myk@mYk:websockets/src ‹main›$ mypy issue1183.py
issue1183.py:3: note: Revealed type is "def (*, logger: Union[logging.Logger, logging.LoggerAdapter[Any], None] =, origin: Union[websockets.typing.Origin, None] =, extensions: Union[typing.Sequence[websockets.extensions.base.ClientExtensionFactory], None] =, subprotocols: Union[typing.Sequence[websockets.typing.Subprotocol], None] =, extra_headers: Union[websockets.datastructures.Headers, typing.Mapping[builtins.str, builtins.str], typing.Iterable[Tuple[builtins.str, builtins.str]], websockets.datastructures.SupportsKeysAndGetItem, None] =, **kwargs: Any) -> websockets.legacy.client.WebSocketClientProtocol"
Success: no issues found in 1 source file
Wait, you're using pylance, not mypy. I don't know much about pylance. I still suspect that it's failing to import websocktes.
Wait, you're using pylance, not mypy. I don't know much about pylance. I still suspect that it's failing to import websocktes.
Pylance is very popular when it comes to python development, so I doubt that I am the only person with this problem. Perhaps you could do some testing or just import the modules normally in the __init__?
I will do some testing before reverting the conditional import mechanism. (FYI it's designed to provide an implementation on top of trio without making trio a hard dependency of websockets. But that's a few years in the future, probably...)
Could you provide reproduction instructions please?
I expect something along these lines:
- Create a virtualenv and install websockets
- What are you using? poetry? python -m venv?
- Open a project in Visual Studio Code
- Any specific setup? Extensions to install/enable?
- Tell Visual Studio Code to load the virtualenv
- Not sure if anything specific is needed here?
I will do some testing before reverting the conditional import mechanism. (FYI it's designed to provide an implementation on top of trio without making trio a hard dependency of websockets. But that's a few years in the future, probably...)
Could you provide reproduction instructions please?
I expect something along these lines:
Create a virtualenv and install websockets
- What are you using? poetry? python -m venv?
Open a project in Visual Studio Code
- Any specific setup? Extensions to install/enable?
Tell Visual Studio Code to load the virtualenv
- Not sure if anything specific is needed here?
Sorry for the late response. Visual studio code is more of a text based editor than an ide so no complicated steps are required. Just install visual studio code and the python extension which will also install pylance. Then just install websockets with pip (no venv required) and view the type hints. Pylance is a great type checker and I have had no problems with any modules except for this one.
Pylance is a slight edit of pyright, the note in docs about:
For convenience, many public APIs can be imported from the websockets package. However, this feature is incompatible with static code analysis. It breaks autocompletion in an IDE or type checking with mypy. If you’re using such tools, use the real import paths.
which matches this behaviour, I don't see the difference.
This package can just import directly from the other modules though with
# __init__.py
from .file import Member
Thanks to PEP 690, I will be able drop the lazy imports mechanism when I drop support for Python < 3.12. Given that Python 3.7 is still supported and that Python gets a minor version every other year (I believe?) that's about 10 years from now :-/
@CaedenPH This works for me:
I suspect your original problem was the same as this one (which I'm demonstrating with the standard library to rule out websockets):
You need to import websockets.client if you want autocompletion inside this module, not just import websockets.
Yes, for backwards-compatibility reasons, there are "magic" imports in websockets. If I could send a message to myself-from-eight-years-ago, I wouldn't expose all API in the root package. Now it's too late :-(
Reopening to check if I can clarify the documentation further or make it easier to discover.
Thanks to PEP 690, I will be able drop the lazy imports mechanism when I drop support for Python < 3.12. Given that Python 3.7 is still supported and that Python gets a minor version every other year (I believe?) that's about 10 years from now :-/
I don't see how that mindset even works, I said there is no need for "lazy imports", you can just use normal imports, no? It has the same maintainability as "lazy imports", just does not need to be so special and actually works with typing.
Yes, I couldn't agree more. Just use normal imports. This is what the documentation that I just added recommends.
Then what is https://github.com/aaugustin/websockets/blob/main/src/websockets/init.py#L56 still there for? Just use relative imports to add them to the main package
It's that way because:
- Long ago the library was simple and small and it was OK to cram everything in the toplevel namespace.
- Now it's growing to support several implementations — Sans-I/O, asyncio - 2 versions, sockets & threads, trio, perhaps curio. Some of this work is public, other parts aren't. But I don't want trio to be a hard dependency of websockets.
- I want to keep backwards compatibility for importing from the root package.
Ah, one more reason — as the library was undergoing significant restructuring, managing renames and deprecation warnings in a central place was much easier.
439dafa6 should fix it going forwards.