Skip to content

Commit bd7e530

Browse files
committed
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 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. Useful resources: * https://docs.python.org/3.5/library/typing.html * https://www.python.org/dev/peps/pep-0484/
1 parent 9bc7751 commit bd7e530

File tree

9 files changed

+45
-33
lines changed

9 files changed

+45
-33
lines changed

.gitignore

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

131132
!src/leveldb*/Makefile
132133

ci/lint/04_install.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export LC_ALL=C
99
travis_retry pip3 install codespell==1.15.0
1010
travis_retry pip3 install flake8==3.7.8
1111
travis_retry pip3 install yq
12+
travis_retry pip3 install mypy==0.700
1213

1314
SHELLCHECK_VERSION=v0.6.0
1415
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 `'{}'.format(x)` for string formatting, not `'%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,
@@ -56,7 +57,7 @@ class BadTxTemplate:
5657
__metaclass__ = abc.ABCMeta
5758

5859
# The expected error code given by bitcoind upon submission of the tx.
59-
reject_reason = ""
60+
reject_reason = "" # type: Optional[str]
6061

6162
# Only specified if it differs from mempool acceptance error.
6263
block_reject_reason = ""

test/functional/p2p_segwit.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ def func_wrapper(self, *args, **kwargs):
295295

296296
return func_wrapper
297297

298-
@subtest
298+
@subtest # type: ignore
299299
def test_non_witness_transaction(self):
300300
"""See if sending a regular transaction works, and create a utxo to use in later tests."""
301301
# Mine a block with an anyone-can-spend coinbase,
@@ -324,7 +324,7 @@ def test_non_witness_transaction(self):
324324
self.utxo.append(UTXO(tx.sha256, 0, 49 * 100000000))
325325
self.nodes[0].generate(1)
326326

327-
@subtest
327+
@subtest # type: ignore
328328
def test_unnecessary_witness_before_segwit_activation(self):
329329
"""Verify that blocks with witnesses are rejected before activation."""
330330

@@ -355,7 +355,7 @@ def test_unnecessary_witness_before_segwit_activation(self):
355355
self.utxo.pop(0)
356356
self.utxo.append(UTXO(tx.sha256, 0, tx.vout[0].nValue))
357357

358-
@subtest
358+
@subtest # type: ignore
359359
def test_block_relay(self):
360360
"""Test that block requests to NODE_WITNESS peer are with MSG_WITNESS_FLAG.
361361
@@ -451,7 +451,7 @@ def test_block_relay(self):
451451
self.old_node.announce_tx_and_wait_for_getdata(block4.vtx[0])
452452
assert block4.sha256 not in self.old_node.getdataset
453453

454-
@subtest
454+
@subtest # type: ignore
455455
def test_v0_outputs_arent_spendable(self):
456456
"""Test that v0 outputs aren't spendable before segwit activation.
457457
@@ -533,7 +533,7 @@ def test_v0_outputs_arent_spendable(self):
533533
self.utxo.pop(0)
534534
self.utxo.append(UTXO(txid, 2, value))
535535

536-
@subtest
536+
@subtest # type: ignore
537537
def test_getblocktemplate_before_lockin(self):
538538
txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16)
539539

@@ -559,7 +559,7 @@ def test_getblocktemplate_before_lockin(self):
559559
self.nodes[0].generate(1)
560560
self.sync_blocks()
561561

562-
@subtest
562+
@subtest # type: ignore
563563
def test_witness_tx_relay_before_segwit_activation(self):
564564

565565
# Generate a transaction that doesn't require a witness, but send it
@@ -601,7 +601,7 @@ def test_witness_tx_relay_before_segwit_activation(self):
601601
self.utxo.pop(0)
602602
self.utxo.append(UTXO(tx_hash, 0, tx_value))
603603

604-
@subtest
604+
@subtest # type: ignore
605605
def test_standardness_v0(self):
606606
"""Test V0 txout standardness.
607607
@@ -679,7 +679,7 @@ def test_standardness_v0(self):
679679
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
680680
assert_equal(len(self.nodes[1].getrawmempool()), 0)
681681

682-
@subtest
682+
@subtest # type: ignore
683683
def advance_to_segwit_active(self):
684684
"""Mine enough blocks to activate segwit."""
685685
assert not softfork_active(self.nodes[0], 'segwit')
@@ -690,7 +690,7 @@ def advance_to_segwit_active(self):
690690
assert softfork_active(self.nodes[0], 'segwit')
691691
self.segwit_active = True
692692

693-
@subtest
693+
@subtest # type: ignore
694694
def test_p2sh_witness(self):
695695
"""Test P2SH wrapped witness programs."""
696696

@@ -759,7 +759,7 @@ def test_p2sh_witness(self):
759759
self.utxo.pop(0)
760760
self.utxo.append(UTXO(spend_tx.sha256, 0, spend_tx.vout[0].nValue))
761761

762-
@subtest
762+
@subtest # type: ignore
763763
def test_witness_commitments(self):
764764
"""Test witness commitments.
765765
@@ -849,7 +849,7 @@ def test_witness_commitments(self):
849849
self.utxo.pop(0)
850850
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
851851

852-
@subtest
852+
@subtest # type: ignore
853853
def test_block_malleability(self):
854854

855855
# Make sure that a block that has too big a virtual size
@@ -889,7 +889,7 @@ def test_block_malleability(self):
889889
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]
890890
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
891891

892-
@subtest
892+
@subtest # type: ignore
893893
def test_witness_block_size(self):
894894
# TODO: Test that non-witness carrying blocks can't exceed 1MB
895895
# Skipping this test for now; this is covered in p2p-fullblocktest.py
@@ -967,7 +967,7 @@ def test_witness_block_size(self):
967967
self.utxo.pop(0)
968968
self.utxo.append(UTXO(block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue))
969969

970-
@subtest
970+
@subtest # type: ignore
971971
def test_submit_block(self):
972972
"""Test that submitblock adds the nonce automatically when possible."""
973973
block = self.build_next_block()
@@ -1003,7 +1003,7 @@ def test_submit_block(self):
10031003
# Tip should not advance!
10041004
assert self.nodes[0].getbestblockhash() != block_2.hash
10051005

1006-
@subtest
1006+
@subtest # type: ignore
10071007
def test_extra_witness_data(self):
10081008
"""Test extra witness data in a transaction."""
10091009

@@ -1076,7 +1076,7 @@ def test_extra_witness_data(self):
10761076
self.utxo.pop(0)
10771077
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
10781078

1079-
@subtest
1079+
@subtest # type: ignore
10801080
def test_max_witness_push_length(self):
10811081
"""Test that witness stack can only allow up to 520 byte pushes."""
10821082

@@ -1113,7 +1113,7 @@ def test_max_witness_push_length(self):
11131113
self.utxo.pop()
11141114
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
11151115

1116-
@subtest
1116+
@subtest # type: ignore
11171117
def test_max_witness_program_length(self):
11181118
"""Test that witness outputs greater than 10kB can't be spent."""
11191119

@@ -1161,7 +1161,7 @@ def test_max_witness_program_length(self):
11611161
self.utxo.pop()
11621162
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
11631163

1164-
@subtest
1164+
@subtest # type: ignore
11651165
def test_witness_input_length(self):
11661166
"""Test that vin length must match vtxinwit length."""
11671167

@@ -1243,7 +1243,7 @@ def serialize_with_witness(self):
12431243
self.utxo.pop()
12441244
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
12451245

1246-
@subtest
1246+
@subtest # type: ignore
12471247
def test_tx_relay_after_segwit_activation(self):
12481248
"""Test transaction relay after segwit activation.
12491249
@@ -1336,7 +1336,7 @@ def test_tx_relay_after_segwit_activation(self):
13361336
self.utxo.pop(0)
13371337
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
13381338

1339-
@subtest
1339+
@subtest # type: ignore
13401340
def test_segwit_versions(self):
13411341
"""Test validity of future segwit version transactions.
13421342
@@ -1418,7 +1418,7 @@ def test_segwit_versions(self):
14181418
# Add utxo to our list
14191419
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
14201420

1421-
@subtest
1421+
@subtest # type: ignore
14221422
def test_premature_coinbase_witness_spend(self):
14231423

14241424
block = self.build_next_block()
@@ -1453,7 +1453,7 @@ def test_premature_coinbase_witness_spend(self):
14531453
test_witness_block(self.nodes[0], self.test_node, block2, accepted=True)
14541454
self.sync_blocks()
14551455

1456-
@subtest
1456+
@subtest # type: ignore
14571457
def test_uncompressed_pubkey(self):
14581458
"""Test uncompressed pubkey validity in segwit transactions.
14591459
@@ -1558,7 +1558,7 @@ def test_uncompressed_pubkey(self):
15581558
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
15591559
self.utxo.append(UTXO(tx5.sha256, 0, tx5.vout[0].nValue))
15601560

1561-
@subtest
1561+
@subtest # type: ignore
15621562
def test_signature_version_1(self):
15631563

15641564
key = ECKey()
@@ -1740,7 +1740,7 @@ def test_signature_version_1(self):
17401740
for i in range(len(tx.vout)):
17411741
self.utxo.append(UTXO(tx.sha256, i, tx.vout[i].nValue))
17421742

1743-
@subtest
1743+
@subtest # type: ignore
17441744
def test_non_standard_witness_blinding(self):
17451745
"""Test behavior of unnecessary witnesses in transactions does not blind the node for the transaction"""
17461746

@@ -1794,7 +1794,7 @@ def test_non_standard_witness_blinding(self):
17941794
self.utxo.pop(0)
17951795
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
17961796

1797-
@subtest
1797+
@subtest # type: ignore
17981798
def test_non_standard_witness(self):
17991799
"""Test detection of non-standard P2WSH witness"""
18001800
pad = chr(1).encode('latin-1')
@@ -1894,7 +1894,7 @@ def test_non_standard_witness(self):
18941894

18951895
self.utxo.pop(0)
18961896

1897-
@subtest
1897+
@subtest # type: ignore
18981898
def test_upgrade_after_activation(self):
18991899
"""Test the behavior of starting up a segwit-aware node after the softfork has activated."""
19001900

@@ -1916,7 +1916,7 @@ def test_upgrade_after_activation(self):
19161916
assert_equal(self.nodes[0].getblock(block_hash), self.nodes[2].getblock(block_hash))
19171917
height -= 1
19181918

1919-
@subtest
1919+
@subtest # type: ignore
19201920
def test_witness_sigops(self):
19211921
"""Test sigop counting is correct inside witnesses."""
19221922

test/functional/test_framework/script.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import hashlib
1010
import struct
1111
import unittest
12+
from typing import List, Dict
1213

1314
from .messages import (
1415
CTransaction,
@@ -21,7 +22,7 @@
2122
)
2223

2324
MAX_SCRIPT_ELEMENT_SIZE = 520
24-
OPCODE_NAMES = {}
25+
OPCODE_NAMES = {} # type: Dict[CScriptOp, str]
2526

2627
def hash160(s):
2728
return hashlib.new('ripemd160', sha256(s)).digest()
@@ -37,7 +38,7 @@ def bn2vch(v):
3738
# Serialize to bytes
3839
return encoded_v.to_bytes(n_bytes, 'little')
3940

40-
_opcode_instances = []
41+
_opcode_instances = [] # type: List[CScriptOp]
4142
class CScriptOp(int):
4243
"""A single script opcode"""
4344
__slots__ = ()

test/functional/test_framework/test_framework.py

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

94+
chain = None # type: str
95+
setup_clean_chain = None # type: bool
96+
9497
def __init__(self):
9598
"""Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method"""
9699
self.chain = 'regtest'
@@ -407,7 +410,7 @@ def run_test(self):
407410

408411
# Public helper methods. These can be accessed by the subclass test scripts.
409412

410-
def add_nodes(self, num_nodes, extra_args=None, *, rpchost=None, binary=None, binary_cli=None, versions=None):
413+
def add_nodes(self, num_nodes: int, extra_args=None, *, rpchost=None, binary=None, binary_cli=None, versions=None):
411414
"""Instantiate TestNode objects.
412415
413416
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
@@ -42,7 +42,7 @@
4242
if os.name != 'nt' or sys.getwindowsversion() >= (10, 0, 14393):
4343
if os.name == 'nt':
4444
import ctypes
45-
kernel32 = ctypes.windll.kernel32
45+
kernel32 = ctypes.windll.kernel32 # type: ignore
4646
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
4747
STD_OUTPUT_HANDLE = -11
4848
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
@@ -96,3 +97,5 @@ PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=$(IFS=","; echo "
9697
echo "$@"
9798
fi
9899
)
100+
101+
mypy --ignore-missing-imports $(git ls-files "test/functional/*.py")

0 commit comments

Comments
 (0)