-
Notifications
You must be signed in to change notification settings - Fork 36.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MyPy, Next Steps #19389
Comments
Just a note for anyone looking into this. Make sure that whatever changes you plan on making are compatible with Python 3.5, as that is our minimum supported version of Python. |
Why would you use stub files for in-tree modules? Why not add the type annotations in the implementation file? |
This is a very important point, I've updated the issue to reflect it. In particular this means that we must use type comments, as opposed to type hinting, in variables that aren't function parameters. (i.e.
afaik we will need to do both, and this is simply a limitation of MyPy's design. I'm more than happy to be proven wrong! |
I am open to take over this issue if there is no one actively working on it. |
I have some of the library parts mypy typed (I think messages.py and script.py), if anyone wants them I can share. |
I generated the stub files using stubgen, but they do not seem to be python 3.5 compatible. I cannot think of a better solution than to write a script to change non-function-parameter type hints (i.e. |
@ghorbanian Thanks for giving this a look! Yup, that's exactly what I mean by "scripted diff". I'll go check out #19532. |
I am keen on helping out with this issue. Where do I start? Should I go away and learn C++ and Python first? |
I just want to point out two things because of my experience:
|
Since the release of PyZMQ 21.0, the library now contains mypy stubs:
This should negate the need to ignore zmq imports. |
I created a PR to remove the most of remaining Merging it will leave us with just a few suppressed places: spv@laptop bitcoin $ grep -EIR 'type:.*ignore' . 2>/dev/null | grep -v venv
./contrib/devtools/security-check.py:import lief #type:ignore
./contrib/devtools/symbol-check.py:import lief #type:ignore
./test/functional/test_runner.py:if os.name != "nt" or sys.getwindowsversion() >= (10, 0, 14393): # type:ignore
./test/functional/test_runner.py: kernel32 = ctypes.windll.kernel32 # type: ignore
./test/functional/combine_logs.py: import jinja2 # type:ignore |
Anything left to do here that is a good first issue? If yes, the issue text should be updated or a new issue be filed (and this one closed). |
These 2 Windows-specific lines are easy to fix
https://mypy.readthedocs.io/en/stable/common_issues.html#python-version-and-system-platform-checks After that it can be closed, I believe, as only untyped libs (lief and jinja2) are left. UPD: for the ignored imports the description suggests generating stub files, so the issue can't be closed. But needs to be updated anyway. |
If someone wants to fix those, seems fine to just create a pull. Though, I think there is no risk if they are left unfixed. It is not like color formatting in the test framework output is any critical feature.
I am not sure if we want to maintain stub files for third party python packages that we use. Is the effort going to be worth the additional checks? |
Agree, no risks at all. Just a low-hanging fruit for those looking for the "good first issue".
Agree with this as well. The description should be updated to reflect this though. |
Ok, I am just going ahead and closing this. Maybe a new issue can be started if people want to discuss whether we want to maintain stub files for 3rd party packages. |
…corator 467fe57 test: Correct MyPy typing for subtest decorator (Pavel Safronov) Pull request description: This is the part of the effort to make python typing correct bitcoin/bitcoin#19389 The typing of the `subtest` decorator within `p2p_segwit.py` test file was incorrect. Since `subtest` function is defined as a member of the class, it expects `self` as a first argument, and it is not provided. Hence the typing errors (that are currently suppressed by `type: ignore`). ``` (venv) vagrant@ubuntu-focal:/vagrant/test/functional$ mypy p2p_segwit.py p2p_segwit.py:298: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:327: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:358: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:447: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:519: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:561: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:659: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:670: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:737: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:826: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:866: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:941: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:977: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1052: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1089: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1136: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1220: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1312: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1406: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1440: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1543: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1729: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1782: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1881: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1983: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:2027: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" Found 26 errors in 1 file (checked 1 source file) ``` However, the tests are passing, because there is no `self` argument passed when it is called as a decorator. There is also suppressed pylint error `# noqa: N805` pointing to the same issue. ``` N805 first argument of a method should be named 'self' ``` So the solution is to move the `subtest` definition outside the class, so the `self` argument is no longer required. After doing so, both mypy and unittests are successfully passing: ``` (venv) vagrant@ubuntu-focal:/vagrant/test/functional$ mypy p2p_segwit.py Success: no issues found in 1 source file ``` ``` (venv) vagrant@ubuntu-focal:/vagrant/test/functional$ ./test_runner.py p2p_segwit Temporary test directory at /tmp/test_runner__🏃_20211103_011449 Running Unit Tests for Test Framework Modules .......... ---------------------------------------------------------------------- Ran 10 tests in 0.546s OK Remaining jobs: [p2p_segwit.py] 1/1 - p2p_segwit.py passed, Duration: 81 s TEST | STATUS | DURATION p2p_segwit.py | ✓ Passed | 81 s ALL | ✓ Passed | 81 s (accumulated) Runtime: 81 s ``` ``` ACKs for top commit: fanquake: ACK 467fe57 Tree-SHA512: e4c3e2d284f47a6bfbf4af22d4021123cdd9c2ea16ec90a91b466ad1a5af615bb4e15959e6cf56c788701d7e7cbda91a8ffc4347965095c3384eae3d28f261af
467fe57 test: Correct MyPy typing for subtest decorator (Pavel Safronov) Pull request description: This is the part of the effort to make python typing correct bitcoin#19389 The typing of the `subtest` decorator within `p2p_segwit.py` test file was incorrect. Since `subtest` function is defined as a member of the class, it expects `self` as a first argument, and it is not provided. Hence the typing errors (that are currently suppressed by `type: ignore`). ``` (venv) vagrant@ubuntu-focal:/vagrant/test/functional$ mypy p2p_segwit.py p2p_segwit.py:298: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:327: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:358: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:447: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:519: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:561: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:659: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:670: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:737: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:826: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:866: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:941: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:977: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1052: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1089: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1136: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1220: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1312: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1406: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1440: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1543: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1729: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1782: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1881: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:1983: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" p2p_segwit.py:2027: error: Argument 1 to "subtest" has incompatible type "Callable[[SegWitTest], Any]"; expected "SegWitTest" Found 26 errors in 1 file (checked 1 source file) ``` However, the tests are passing, because there is no `self` argument passed when it is called as a decorator. There is also suppressed pylint error `# noqa: N805` pointing to the same issue. ``` N805 first argument of a method should be named 'self' ``` So the solution is to move the `subtest` definition outside the class, so the `self` argument is no longer required. After doing so, both mypy and unittests are successfully passing: ``` (venv) vagrant@ubuntu-focal:/vagrant/test/functional$ mypy p2p_segwit.py Success: no issues found in 1 source file ``` ``` (venv) vagrant@ubuntu-focal:/vagrant/test/functional$ ./test_runner.py p2p_segwit Temporary test directory at /tmp/test_runner__🏃_20211103_011449 Running Unit Tests for Test Framework Modules .......... ---------------------------------------------------------------------- Ran 10 tests in 0.546s OK Remaining jobs: [p2p_segwit.py] 1/1 - p2p_segwit.py passed, Duration: 81 s TEST | STATUS | DURATION p2p_segwit.py | ✓ Passed | 81 s ALL | ✓ Passed | 81 s (accumulated) Runtime: 81 s ``` ``` ACKs for top commit: fanquake: ACK 467fe57 Tree-SHA512: e4c3e2d284f47a6bfbf4af22d4021123cdd9c2ea16ec90a91b466ad1a5af615bb4e15959e6cf56c788701d7e7cbda91a8ffc4347965095c3384eae3d28f261af
MyPy was added in #18210, allowing for static type checking of type hints in our testing framework. However, there is still some work to be done before we can take advantage of these new capabilities. 🚀
Right now,
lint-python.sh
runs MyPy with the--ignore-imports
flag. This is done primarily to ignore the lack of type hinting forzmq
, but it hides many errors in our own code too.To begin to see what's needed, first run
mypy test/functional
. You'll see two sets of errors. One complains aboutSkipping analyzing 'zmq'
. This one can be ignored for now by changing the identifiedimport zmq
lines toimport zmq # type: ignore
.The other states that it
Cannot find implementation or library stub for module named 'data'
. Remember that for a second.Now go into
test/functional/test_framework
and runmypy mininode.py
. Observe two moreCannot find implementation or library stub for module
errors, now fortest_framework.messages
andtest_framework.util
.What's happening here is that MyPy is failing to handle importing our
test_framework
package. We'll need to do a few things to fix this.mypy_path
we need to tell MyPy where to find ourtest_framework
package..pyi
) for the modules intest_framework
# type: ignore
toimport zmq
--ignore-missing-imports
simply hides too many errors.Resources
Important Notes
Our minimum supported python version is 3.5, so any changes must be compatible. In particular this means that we must use type comments, as opposed to type hinting, in variables that aren't function parameters. (i.e.
foo # type: int
as opposed tofoo: int
). This will complicate the use of MonkeyType, as it doesn't support type comments. It may be useful to make a scripted diff between type comments and type hints.Useful skills:
MyPy experience
Want to work on this issue?
For guidance on contributing, please read CONTRIBUTING.md before opening your pull request.
The text was updated successfully, but these errors were encountered: