Skip to content

Commit 271acac

Browse files
fanquakePastaPastaPasta
authored andcommitted
Merge bitcoin#18210: test: type hints in Python tests
bd7e530 This PR adds initial support for type hints checking in python scripts. (Kiminuo) Pull request description: This PR adds initial support for type hints checking in python scripts. Support for type hints was introduced in Python 3.5. Type hints make it easier to read and review code in my opinion. Also an IDE may discover a potential bug sooner. Yet, as PEP 484 says: "It should also be emphasized that Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention." [Mypy](https://mypy.readthedocs.io/en/latest/index.html) is used in `lint-python.sh` to do the type checking. The package is standard so there is little chance that it will be abandoned. Mypy checks that type hints in source code are correct when they are not, it fails with an error. **Notes:** * [--ignore-missing-imports](https://mypy.readthedocs.io/en/latest/command_line.html#cmdoption-mypy-ignore-missing-imports) switch is passed on to `mypy` checker for now. The effect of this is that one does not need `# type: ignore` for `import zmq`. More information about import processing can be found [here](https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports). This can be changed in a follow-up PR, if it is deemed useful. * We are stuck with Python 3.5 until 04/2021 (see https://packages.ubuntu.com/xenial/python3). When Python version is bumped to 3.6+, one can change: ```python _opcode_instances = [] # type: List[CScriptOp] ``` to ```python _opcode_instances:List[CScriptOp] = [] ``` for type hints that are **not** function parameters and function return types. **Useful resources:** * https://docs.python.org/3.5/library/typing.html * https://www.python.org/dev/peps/pep-0484/ ACKs for top commit: fanquake: ACK bd7e530 - the type checking is not the most robust (there are things it fails to detect), but I think this is worth adopting (in a limited capacity while we maintain 3.5 compat). MarcoFalke: ACK bd7e530 fine with me Tree-SHA512: 21ef213915fb1dec6012f59ef17484e6c9e0abf542a316b63d5f21a7778ad5ebabf8961ef5fc8e5414726c2ee9c6ae07c7353fb4dd337f8fcef5791199c8987a
1 parent 3b7140e commit 271acac

File tree

8 files changed

+19
-7
lines changed

8 files changed

+19
-7
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ linux-build
128128
win32-build
129129
test/config.ini
130130
test/cache/*
131+
test/.mypy_cache/
131132

132133
!src/leveldb*/Makefile
133134

ci/lint/04_install.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ travis_retry pip3 install codespell==1.17.1
1414
travis_retry pip3 install flake8==3.8.3
1515
travis_retry pip3 install vulture==2.3
1616
travis_retry pip3 install yq
17+
travis_retry pip3 install mypy==0.700
1718

1819
SHELLCHECK_VERSION=v0.6.0
1920
curl -s "https://storage.googleapis.com/shellcheck/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar --xz -xf - --directory /tmp/

test/functional/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ don't have test cases for.
2626
The Travis linter also checks this, but [possibly not in all cases](https://github.com/bitcoin/bitcoin/pull/14884#discussion_r239585126).
2727
- See [the python lint script](/test/lint/lint-python.sh) that checks for violations that
2828
could lead to bugs and issues in the test code.
29+
- Use [type hints](https://docs.python.org/3/library/typing.html) in your code to improve code readability
30+
and to detect possible bugs earlier.
2931
- Avoid wildcard imports
3032
- Use a module-level docstring to describe what the test is testing, and how it
3133
is testing it.
32-
- When subclassing the BitcoinTestFramwork, place overrides for the
34+
- When subclassing the BitcoinTestFramework, place overrides for the
3335
`set_test_params()`, `add_options()` and `setup_xxxx()` methods at the top of
3436
the subclass, then locally-defined helper methods, then the `run_test()` method.
3537
- Use `f'{x}'` for string formatting in preference to `'{}'.format(x)` or `'%s' % x`.
@@ -45,7 +47,7 @@ don't have test cases for.
4547
- `rpc` for tests for individual RPC methods or features, eg `rpc_listtransactions.py`
4648
- `tool` for tests for tools, eg `tool_wallet.py`
4749
- `wallet` for tests for wallet features, eg `wallet_keypool.py`
48-
- use an underscore to separate words
50+
- Use an underscore to separate words
4951
- exception: for tests for specific RPCs or command line options which don't include underscores, name the test after the exact RPC or argument name, eg `rpc_decodescript.py`, not `rpc_decode_script.py`
5052
- Don't use the redundant word `test` in the name, eg `interface_zmq.py`, not `interface_zmq_test.py`
5153

test/functional/data/invalid_txs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"""
2222
import abc
2323

24+
from typing import Optional
2425
from test_framework.messages import (
2526
COutPoint,
2627
CTransaction,
@@ -39,7 +40,7 @@ class BadTxTemplate:
3940
__metaclass__ = abc.ABCMeta
4041

4142
# The expected error code given by bitcoind upon submission of the tx.
42-
reject_reason = ""
43+
reject_reason = "" # type: Optional[str]
4344

4445
# Only specified if it differs from mempool acceptance error.
4546
block_reject_reason = ""

test/functional/test_framework/script.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"""
99
import struct
1010
import unittest
11+
from typing import List, Dict
1112

1213
from .messages import (
1314
CTransaction,
@@ -19,7 +20,7 @@
1920
from .ripemd160 import ripemd160
2021

2122
MAX_SCRIPT_ELEMENT_SIZE = 520
22-
OPCODE_NAMES = {}
23+
OPCODE_NAMES = {} # type: Dict[CScriptOp, str]
2324

2425
def hash160(s):
2526
return ripemd160(sha256(s))
@@ -35,7 +36,7 @@ def bn2vch(v):
3536
# Serialize to bytes
3637
return encoded_v.to_bytes(n_bytes, 'little')
3738

38-
_opcode_instances = []
39+
_opcode_instances = [] # type: List[CScriptOp]
3940
class CScriptOp(int):
4041
"""A single script opcode"""
4142
__slots__ = ()

test/functional/test_framework/test_framework.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
112112
113113
This class also contains various public and private helper methods."""
114114

115+
chain = None # type: str
116+
setup_clean_chain = None # type: bool
117+
115118
def __init__(self):
116119
"""Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method"""
117120
self.chain: str = 'regtest'
@@ -460,7 +463,7 @@ def run_test(self):
460463

461464
# Public helper methods. These can be accessed by the subclass test scripts.
462465

463-
def add_nodes(self, num_nodes, extra_args=None, *, rpchost=None, binary=None, binary_cli=None, versions=None):
466+
def add_nodes(self, num_nodes: int, extra_args=None, *, rpchost=None, binary=None, binary_cli=None, versions=None):
464467
"""Instantiate TestNode objects.
465468
466469
Should only be called once after the nodes have been specified in

test/functional/test_runner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
if os.name != 'nt' or sys.getwindowsversion() >= (10, 0, 14393):
4444
if os.name == 'nt':
4545
import ctypes
46-
kernel32 = ctypes.windll.kernel32
46+
kernel32 = ctypes.windll.kernel32 # type: ignore
4747
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
4848
STD_OUTPUT_HANDLE = -11
4949
STD_ERROR_HANDLE = -12

test/lint/lint-python.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# Check for specified flake8 warnings in python files.
88

99
export LC_ALL=C
10+
export MYPY_CACHE_DIR="${BASE_ROOT_DIR}/test/.mypy_cache"
1011

1112
enabled=(
1213
E101 # indentation contains mixed spaces and tabs
@@ -103,3 +104,5 @@ PYTHONWARNINGS="ignore" $FLAKECMD --ignore=B,C,E,F,I,N,W --select=$(IFS=","; ech
103104
echo "$@"
104105
fi
105106
)
107+
108+
mypy --ignore-missing-imports $(git ls-files "test/functional/*.py")

0 commit comments

Comments
 (0)