The Bug
Pyright emits reportMissingModuleSource warnings for submodules implemented by the same native extension, even though:
-
The Python sources for prettypretty include typing stubs for prettypretty.color (the primary native module) and submodules such as prettypretty.color.gamut and prettypretty.color.style
-
They also include symbolic links for each of the submodules, which follow native library naming convention and point to the actual native library. E.g., prettypretty/color/gamut.abi3.so points to prettypretty/color.abi3.so.
- That technique is blessed by PEP 489 for just this use case.
- It seems to work otherwise: I can successfully
import prettypretty.color or import prettypretty.color.style as the first statement in a fresh interactive session.
Typing stubs, native library, and symbolic library links really should be enough to appease the type checker. If we really want two independent sources of truth for accepting native code, I suggest loading the native library and inspecting that. I must admit I'm not a fan of the symbolic links. I also don't know what to do about Windows. My understanding is that Windows finally does support symbolic links even for files (yay!), but that most Windows users leave that feature disabled because symbolic links might turn into a vector for exploits (oops!).
The Details
-
Prettypretty uses PyO3 for FFI. AFAIK, it doesn't currently use the multiphase extension initialization of PEP 489 but simply instantiates all module objects during initial loading.
-
VS Code or the command line as well as Pyright versions seem to make no difference. The warnings happen in:
- up-to-date VS Code (1.92.1) with Pylance 2024.8.1
- up-to-date VS Code (1.92.1) with Pylance 2024.8.1, Remote, SSH flavor
- a somewhat older NPM package for Pyright (1.1.361)
- the latest version of the package (1.1.375)
-
All other software is of similarly recent yet stable vintage:
- CPython 3.12.2
- stable Rust toolchain, 1.80.0
- PyO3 0.22.0
- macOS 14.5 and 14.6.1, Intel only, but can test Apple Silicon if necessary
-
The one thing I noticed in Pyright's verbose output is that it repeatedly tries to find the same source files. I assume it doesn't cache failures then. Otherwise, I couldn't glean anything else from the trace. It is attached below.
-
If you want to try for yourself, you can just:
- clone the repository
- in the root, run
./rr.sh install to install necessary tools
- once everything is installed, activate the just created virtual env
- run
./rr.sh to build the Python extension module
- run
./rr.sh check to check project, including with Pyright
While prettypretty does build and run on Windows for CI, the above steps only work with (macOS + Homebrew) or (Linux + APT) for now.
The Gory Details
Verbose command line output
Prettypretty uses # pyright: ignore [reportMissingModuleSource] comments to
avoid useless warnings. I did enable one of them for this run.
(.venv) rgrimm@Precious prettypretty % npm run pyright -- --verbose --pythonpath ./.venv/bin/python
> pyright
> pyright --verbose --pythonpath ./.venv/bin/python
Setting pythonPath for service "<default>": "/Users/rgrimm/Work/prettypretty/.venv/bin/python"
Loading pyproject.toml file at /Users/rgrimm/Work/prettypretty/pyproject.toml
Auto-excluding **/node_modules
Auto-excluding **/__pycache__
Auto-excluding **/.*
Search paths for file:///Users/rgrimm/Work/prettypretty
/Users/rgrimm/Work/prettypretty/node_modules/pyright/dist/typeshed-fallback/stdlib
/Users/rgrimm/Work/prettypretty
/Users/rgrimm/Work/prettypretty/src
/Users/rgrimm/Work/prettypretty/typings
/Users/rgrimm/Work/prettypretty/node_modules/pyright/dist/typeshed-fallback/stubs/...
/Users/rgrimm/.pyenv/versions/3.12.2/lib/python3.12
/Users/rgrimm/.pyenv/versions/3.12.2/lib/python3.12/lib-dynload
/Users/rgrimm/Work/prettypretty/.venv/lib/python3.12/site-packages
/Users/rgrimm/Work/prettypretty
Found 20 source files
Could not resolve source for '.color.style' in file '/Users/rgrimm/Work/prettypretty/prettypretty/color_types.py'
Attempting to resolve relative import
Attempting to resolve using root path 'file:///Users/rgrimm/Work/prettypretty/prettypretty'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color/__init__.pyi'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color/style.pyi'
Attempting to resolve using root path 'file:///Users/rgrimm/Work/prettypretty/prettypretty'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.abi3.so'
Resolved with native lib '/Users/rgrimm/Work/prettypretty/prettypretty/color.abi3.so'
Did not find file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.pyi' or 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.py'
Could not resolve source for '.color.style' in file '/Users/rgrimm/Work/prettypretty/prettypretty/grid.py'
Attempting to resolve relative import
Attempting to resolve using root path 'file:///Users/rgrimm/Work/prettypretty/prettypretty'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color/__init__.pyi'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color/style.pyi'
Attempting to resolve using root path 'file:///Users/rgrimm/Work/prettypretty/prettypretty'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.abi3.so'
Resolved with native lib '/Users/rgrimm/Work/prettypretty/prettypretty/color.abi3.so'
Did not find file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.pyi' or 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.py'
Could not resolve source for '.color.trans' in file '/Users/rgrimm/Work/prettypretty/prettypretty/theme.py'
Attempting to resolve relative import
Attempting to resolve using root path 'file:///Users/rgrimm/Work/prettypretty/prettypretty'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color/__init__.pyi'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color/trans.pyi'
Attempting to resolve using root path 'file:///Users/rgrimm/Work/prettypretty/prettypretty'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.abi3.so'
Resolved with native lib '/Users/rgrimm/Work/prettypretty/prettypretty/color.abi3.so'
Did not find file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.pyi' or 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.py'
Could not resolve source for '.color.style' in file '/Users/rgrimm/Work/prettypretty/prettypretty/terminal.py'
Attempting to resolve relative import
Attempting to resolve using root path 'file:///Users/rgrimm/Work/prettypretty/prettypretty'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color/__init__.pyi'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color/style.pyi'
Attempting to resolve using root path 'file:///Users/rgrimm/Work/prettypretty/prettypretty'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.abi3.so'
Resolved with native lib '/Users/rgrimm/Work/prettypretty/prettypretty/color.abi3.so'
Did not find file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.pyi' or 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.py'
Heap stats: total_memory_size=131072MB, total_free_size=72256MB, total_heap_size=131MB, used_heap_size=106MB, cross_worker_used_heap_size=106MB, total_physical_size=131MB, total_available_size=4036MB, heap_size_limit=4144MB
Could not resolve source for '.color.style' in file '/Users/rgrimm/Work/prettypretty/prettypretty/style_extras.py'
Attempting to resolve relative import
Attempting to resolve using root path 'file:///Users/rgrimm/Work/prettypretty/prettypretty'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color/__init__.pyi'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color/style.pyi'
Attempting to resolve using root path 'file:///Users/rgrimm/Work/prettypretty/prettypretty'
Resolved import with file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.abi3.so'
Resolved with native lib '/Users/rgrimm/Work/prettypretty/prettypretty/color.abi3.so'
Did not find file 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.pyi' or 'file:///Users/rgrimm/Work/prettypretty/prettypretty/color.py'
pyright 1.1.375
/Users/rgrimm/Work/prettypretty/prettypretty/plot.py
/Users/rgrimm/Work/prettypretty/prettypretty/plot.py:26:5 - warning: Import ".color.gamut" could not be resolved from source (reportMissingModuleSource)
0 errors, 1 warning, 0 informations
Completed in 1.495sec
The Bug
Pyright emits
reportMissingModuleSourcewarnings for submodules implemented by the same native extension, even though:The Python sources for prettypretty include typing stubs for
prettypretty.color(the primary native module) and submodules such asprettypretty.color.gamutandprettypretty.color.styleThey also include symbolic links for each of the submodules, which follow native library naming convention and point to the actual native library. E.g.,
prettypretty/color/gamut.abi3.sopoints toprettypretty/color.abi3.so.import prettypretty.colororimport prettypretty.color.styleas the first statement in a fresh interactive session.Typing stubs, native library, and symbolic library links really should be enough to appease the type checker. If we really want two independent sources of truth for accepting native code, I suggest loading the native library and inspecting that. I must admit I'm not a fan of the symbolic links. I also don't know what to do about Windows. My understanding is that Windows finally does support symbolic links even for files (yay!), but that most Windows users leave that feature disabled because symbolic links might turn into a vector for exploits (oops!).
The Details
Prettypretty uses PyO3 for FFI. AFAIK, it doesn't currently use the multiphase extension initialization of PEP 489 but simply instantiates all module objects during initial loading.
PyO3's level of integration between Rust and Python is impressive. But its module wrangling is disappointing:
__name__(which is the simple name of the submodule, not the fully qualified name it should be) and__package__(which is empty).sys.modulesregistry and are only accessible through attributes of the main module.__name__and__package__attributes as well as registering submodules insys.modules.VS Code or the command line as well as Pyright versions seem to make no difference. The warnings happen in:
All other software is of similarly recent yet stable vintage:
The one thing I noticed in Pyright's verbose output is that it repeatedly tries to find the same source files. I assume it doesn't cache failures then. Otherwise, I couldn't glean anything else from the trace. It is attached below.
If you want to try for yourself, you can just:
./rr.sh installto install necessary tools./rr.shto build the Python extension module./rr.sh checkto check project, including with PyrightWhile prettypretty does build and run on Windows for CI, the above steps only work with (macOS + Homebrew) or (Linux + APT) for now.
The Gory Details
Verbose command line output
Prettypretty uses
# pyright: ignore [reportMissingModuleSource]comments toavoid useless warnings. I did enable one of them for this run.