graphviz typing error
Information
graphviz_draw has wrong documentation of return type both in type annotation and docstring. It is marked as PIL.Image when in fact it should be PIL.Image.Image.
This confused me for a good while, since the module is both camel cased and has the same name as the correct return type...
"Doctesting" with pyright, pyright --verifytypes rustworkx, reports the wrong documentation in the type annotation (mypy -p rustworkx misses it due to a # type: ignore on that line) .
- rustworkx version: 0.15.1
- Python version: 3.13
Steps to reproduce the problem
Use graphviz_draw anywhere and check your script and check with pyright or mypy, the return type is inferred incorrectly.
# script.py
from __future__ import annotations
from typing import reveal_type
import rustworkx
g: rustworkx.PyGraph[int, int] = rustworkx.PyGraph()
img = rustworkx.visualization.graphviz_draw(g)
reveal_type(img)
In runtime python script.py reports correctly Runtime type is 'PngImageFile' (PngImageFile is a subtype if Image.Image).
The two type checkers fail, one has missing inferred type and the other is wrong:
- If ran with
pyrightthen output isscript.py:8:13 - information: Type of "img" is "Unknown". - If ran with
mypythen output isscript.py:8: note: Revealed type is "None".
I need to check if this still applies to main with the latest commit after #1291. I believe the answer will be no.
None is a valid return type for that function though. If you specific a file it just writes to it. Maybe we should use @overload to split the file case with the in-memory case
Ah, sorry. I forgot to check main before opening the issue. It does look as though this is solved in main.
An @overload can be really nice here actually. If you're at it, you can also remove the uneeded # type: ignore if you want.
Thanks again for the quick response and a great package :)
I have quite a few typing PRs open. Once Matthew has time to review them, I will work on the @overload and removing the # type: ignore.
I think the ignore was there initially because Pillow didn't have type stubs initially. And then we never updated it
Both mypy and pyright can be configured to emit an error if there is an unusued # type: ignore.
For mypy --warn-unused-ignores or warn_unused_ignores. For pyright reportUnnecessaryTypeIgnoreComment.
I have revisited this with pyright and overall the results do not seem very useful to me.
For graphviz specifically:
/home/ivan/.venv/lib64/python3.13/site-packages/rustworkx/visualization/graphviz.pyi:17:23 - error: Type of parameter "graph" is partially unknown
Parameter type is "PyDiGraph[_S@graphviz_draw, _T@graphviz_draw] | PyGraph[_S@graphviz_draw, _T@graphviz_draw]"
We annotated the argument with graph: PyDiGraph[_S, _T] | PyGraph[_S, _T], which for me seems to explicitly tell what the argument is. I found https://github.com/microsoft/pyright/issues/698 but that gave me more questions then doubts given we are using TypeVar
That's very surprising, @IvanIsCoding. How did you run pyright?
When I use rustworkx v0.16 with all the type-annotations improvements ️(♥️), pyright seems to be happy even in strict mode.
In a fresh project I ran pip install rustworkx pyright pillow pydot and checked the script
# t.py
from __future__ import annotations
from typing import reveal_type
import rustworkx
import rustworkx.visualization
g1: rustworkx.PyGraph[int, int] = rustworkx.PyGraph()
img = rustworkx.visualization.graphviz_draw(g1)
reveal_type(img)
g2 = rustworkx.PyGraph[int, int]()
img = rustworkx.visualization.graphviz_draw(g2)
reveal_type(img)
g3 = rustworkx.PyGraph()
img = rustworkx.visualization.graphviz_draw(g3)
reveal_type(img)
Adding pyrightconfig.json to project root
{
"typeCheckingMode": "strict",
}
I get no errors:
(.venv) $ pyright t.py
/home/barak-katzir/dev/rustworkx-check/t.py
/home/barak-katzir/dev/rustworkx-check/t.py:9:13 - information: Type of "img" is "Image | None"
/home/barak-katzir/dev/rustworkx-check/t.py:13:13 - information: Type of "img" is "Image | None"
/home/barak-katzir/dev/rustworkx-check/t.py:17:13 - information: Type of "img" is "Image | None"
0 errors, 0 warnings, 3 informations
Maybe pyright is confused by the rustworkx shim?
I did notice that the script I listed in the original post above still doesn't pass pyright, but for a different reason.
pyright doesn't detect the rustworkx.visualization module without explicitly importing it, which is why I added the import rustworkx.visualization to the t.py test script.
@barakatzir I just ran pyright --verifytypes rustworkx with the latest rustworkx distributed in PyPI and this is what I got: https://pastebin.com/TZjZDjYq
There are lots of errors ranging from errors propagating from the NumPy stubs to many other complaints about rustworkx. I will try to understand and possibly address them but overall for now I will say the output from mypy has been more useful for me
Wow! That is a very long list. I don't remember it being that long. I'll have a look at it after a good night's sleep.
Okay, so pyright indeed has an... opinionated default for whether to recurse through dependencies in --verifytypes, but this can be turned off with --ignoreexternal. If I do so I find a single error, a missing annotation of the aspect_ratio parameter in bipartite_layout function in rustworkx/__init__.pyi. I used the same strict setting as before:
(.venv) $ pyright --verifytypes rustworkx --ignoreexternal
Module name: "rustworkx"
Package directory: "/home/barak-katzir/dev/rustworkx-check/.venv/lib/python3.12/site-packages/rustworkx"
Module directory: "/home/barak-katzir/dev/rustworkx-check/.venv/lib/python3.12/site-packages/rustworkx"
Path of py.typed file: "/home/barak-katzir/dev/rustworkx-check/.venv/lib/python3.12/site-packages/rustworkx/py.typed"
Public modules: 7
rustworkx
rustworkx.generators
rustworkx.rustworkx
rustworkx.visit
rustworkx.visualization
rustworkx.visualization.graphviz
rustworkx.visualization.matplotlib
Symbols used in public interface:
rustworkx.bipartite_layout
/home/barak-katzir/dev/rustworkx-check/.venv/lib/python3.12/site-packages/rustworkx/__init__.pyi:461:5 - error: Type annotation for parameter "aspect_ratio" is missing
Symbols exported by "rustworkx": 707
With known type: 706
With ambiguous type: 0
With unknown type: 1
(Ignoring unknown types imported from other packages)
Other symbols referenced but not exported by "rustworkx": 59
With known type: 59
With ambiguous type: 0
With unknown type: 0
Symbols without documentation:
Functions without docstring: 586
Functions without default param: 288
Classes without docstring: 89
Type completeness score: 99.9%
Completed in 1.258sec
When I locally fix the annotation, pyright does not emit errors.
Note that if you want to add both mypy and pyright to do type checking they can clash in their pragma comment syntax.
If you do plan to add both I suggest you set "enableTypeIgnoreComments": false in pyright so that it is suppressed with # pyright: ignore[...] only.
This is only really problematic if you want them to emit an error for unnecessary ignores and their diagnosis differ (which does happen), but I think it is better to have separate ignores nonetheless (for pyright the setting is "reportUnnecessaryTypeIgnoreComment": "error", in the config and for mypy it is warn_unused_ignores = true).
I'm mentioning to hopefully save you time. I wasted a good hour on re-ignoring stuff in a private project.