mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-19 01:42:58 +01:00
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/
This commit is contained in:
parent
9bc7751cad
commit
bd7e530f01
1
.gitignore
vendored
1
.gitignore
vendored
@ -127,6 +127,7 @@ linux-build
|
||||
win32-build
|
||||
test/config.ini
|
||||
test/cache/*
|
||||
test/.mypy_cache/
|
||||
|
||||
!src/leveldb*/Makefile
|
||||
|
||||
|
@ -9,6 +9,7 @@ export LC_ALL=C
|
||||
travis_retry pip3 install codespell==1.15.0
|
||||
travis_retry pip3 install flake8==3.7.8
|
||||
travis_retry pip3 install yq
|
||||
travis_retry pip3 install mypy==0.700
|
||||
|
||||
SHELLCHECK_VERSION=v0.6.0
|
||||
curl -s "https://storage.googleapis.com/shellcheck/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar --xz -xf - --directory /tmp/
|
||||
|
@ -26,10 +26,12 @@ don't have test cases for.
|
||||
The Travis linter also checks this, but [possibly not in all cases](https://github.com/bitcoin/bitcoin/pull/14884#discussion_r239585126).
|
||||
- See [the python lint script](/test/lint/lint-python.sh) that checks for violations that
|
||||
could lead to bugs and issues in the test code.
|
||||
- Use [type hints](https://docs.python.org/3/library/typing.html) in your code to improve code readability
|
||||
and to detect possible bugs earlier.
|
||||
- Avoid wildcard imports
|
||||
- Use a module-level docstring to describe what the test is testing, and how it
|
||||
is testing it.
|
||||
- When subclassing the BitcoinTestFramwork, place overrides for the
|
||||
- When subclassing the BitcoinTestFramework, place overrides for the
|
||||
`set_test_params()`, `add_options()` and `setup_xxxx()` methods at the top of
|
||||
the subclass, then locally-defined helper methods, then the `run_test()` method.
|
||||
- Use `'{}'.format(x)` for string formatting, not `'%s' % x`.
|
||||
@ -45,7 +47,7 @@ don't have test cases for.
|
||||
- `rpc` for tests for individual RPC methods or features, eg `rpc_listtransactions.py`
|
||||
- `tool` for tests for tools, eg `tool_wallet.py`
|
||||
- `wallet` for tests for wallet features, eg `wallet_keypool.py`
|
||||
- use an underscore to separate words
|
||||
- Use an underscore to separate words
|
||||
- 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`
|
||||
- Don't use the redundant word `test` in the name, eg `interface_zmq.py`, not `interface_zmq_test.py`
|
||||
|
||||
|
@ -21,6 +21,7 @@ Invalid tx cases not covered here can be found by running:
|
||||
"""
|
||||
import abc
|
||||
|
||||
from typing import Optional
|
||||
from test_framework.messages import (
|
||||
COutPoint,
|
||||
CTransaction,
|
||||
@ -56,7 +57,7 @@ class BadTxTemplate:
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
# The expected error code given by bitcoind upon submission of the tx.
|
||||
reject_reason = ""
|
||||
reject_reason = "" # type: Optional[str]
|
||||
|
||||
# Only specified if it differs from mempool acceptance error.
|
||||
block_reject_reason = ""
|
||||
|
@ -295,7 +295,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
|
||||
return func_wrapper
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_non_witness_transaction(self):
|
||||
"""See if sending a regular transaction works, and create a utxo to use in later tests."""
|
||||
# Mine a block with an anyone-can-spend coinbase,
|
||||
@ -324,7 +324,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.append(UTXO(tx.sha256, 0, 49 * 100000000))
|
||||
self.nodes[0].generate(1)
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_unnecessary_witness_before_segwit_activation(self):
|
||||
"""Verify that blocks with witnesses are rejected before activation."""
|
||||
|
||||
@ -355,7 +355,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.pop(0)
|
||||
self.utxo.append(UTXO(tx.sha256, 0, tx.vout[0].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_block_relay(self):
|
||||
"""Test that block requests to NODE_WITNESS peer are with MSG_WITNESS_FLAG.
|
||||
|
||||
@ -451,7 +451,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.old_node.announce_tx_and_wait_for_getdata(block4.vtx[0])
|
||||
assert block4.sha256 not in self.old_node.getdataset
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_v0_outputs_arent_spendable(self):
|
||||
"""Test that v0 outputs aren't spendable before segwit activation.
|
||||
|
||||
@ -533,7 +533,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.pop(0)
|
||||
self.utxo.append(UTXO(txid, 2, value))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_getblocktemplate_before_lockin(self):
|
||||
txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16)
|
||||
|
||||
@ -559,7 +559,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_blocks()
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_witness_tx_relay_before_segwit_activation(self):
|
||||
|
||||
# Generate a transaction that doesn't require a witness, but send it
|
||||
@ -601,7 +601,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.pop(0)
|
||||
self.utxo.append(UTXO(tx_hash, 0, tx_value))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_standardness_v0(self):
|
||||
"""Test V0 txout standardness.
|
||||
|
||||
@ -679,7 +679,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
|
||||
assert_equal(len(self.nodes[1].getrawmempool()), 0)
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def advance_to_segwit_active(self):
|
||||
"""Mine enough blocks to activate segwit."""
|
||||
assert not softfork_active(self.nodes[0], 'segwit')
|
||||
@ -690,7 +690,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
assert softfork_active(self.nodes[0], 'segwit')
|
||||
self.segwit_active = True
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_p2sh_witness(self):
|
||||
"""Test P2SH wrapped witness programs."""
|
||||
|
||||
@ -759,7 +759,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.pop(0)
|
||||
self.utxo.append(UTXO(spend_tx.sha256, 0, spend_tx.vout[0].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_witness_commitments(self):
|
||||
"""Test witness commitments.
|
||||
|
||||
@ -849,7 +849,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.pop(0)
|
||||
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_block_malleability(self):
|
||||
|
||||
# Make sure that a block that has too big a virtual size
|
||||
@ -889,7 +889,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]
|
||||
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_witness_block_size(self):
|
||||
# TODO: Test that non-witness carrying blocks can't exceed 1MB
|
||||
# Skipping this test for now; this is covered in p2p-fullblocktest.py
|
||||
@ -967,7 +967,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.pop(0)
|
||||
self.utxo.append(UTXO(block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_submit_block(self):
|
||||
"""Test that submitblock adds the nonce automatically when possible."""
|
||||
block = self.build_next_block()
|
||||
@ -1003,7 +1003,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
# Tip should not advance!
|
||||
assert self.nodes[0].getbestblockhash() != block_2.hash
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_extra_witness_data(self):
|
||||
"""Test extra witness data in a transaction."""
|
||||
|
||||
@ -1076,7 +1076,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.pop(0)
|
||||
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_max_witness_push_length(self):
|
||||
"""Test that witness stack can only allow up to 520 byte pushes."""
|
||||
|
||||
@ -1113,7 +1113,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.pop()
|
||||
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_max_witness_program_length(self):
|
||||
"""Test that witness outputs greater than 10kB can't be spent."""
|
||||
|
||||
@ -1161,7 +1161,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.pop()
|
||||
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_witness_input_length(self):
|
||||
"""Test that vin length must match vtxinwit length."""
|
||||
|
||||
@ -1243,7 +1243,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.pop()
|
||||
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_tx_relay_after_segwit_activation(self):
|
||||
"""Test transaction relay after segwit activation.
|
||||
|
||||
@ -1336,7 +1336,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.pop(0)
|
||||
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_segwit_versions(self):
|
||||
"""Test validity of future segwit version transactions.
|
||||
|
||||
@ -1418,7 +1418,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
# Add utxo to our list
|
||||
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_premature_coinbase_witness_spend(self):
|
||||
|
||||
block = self.build_next_block()
|
||||
@ -1453,7 +1453,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
test_witness_block(self.nodes[0], self.test_node, block2, accepted=True)
|
||||
self.sync_blocks()
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_uncompressed_pubkey(self):
|
||||
"""Test uncompressed pubkey validity in segwit transactions.
|
||||
|
||||
@ -1558,7 +1558,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
|
||||
self.utxo.append(UTXO(tx5.sha256, 0, tx5.vout[0].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_signature_version_1(self):
|
||||
|
||||
key = ECKey()
|
||||
@ -1740,7 +1740,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
for i in range(len(tx.vout)):
|
||||
self.utxo.append(UTXO(tx.sha256, i, tx.vout[i].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_non_standard_witness_blinding(self):
|
||||
"""Test behavior of unnecessary witnesses in transactions does not blind the node for the transaction"""
|
||||
|
||||
@ -1794,7 +1794,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.utxo.pop(0)
|
||||
self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue))
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_non_standard_witness(self):
|
||||
"""Test detection of non-standard P2WSH witness"""
|
||||
pad = chr(1).encode('latin-1')
|
||||
@ -1894,7 +1894,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
|
||||
self.utxo.pop(0)
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_upgrade_after_activation(self):
|
||||
"""Test the behavior of starting up a segwit-aware node after the softfork has activated."""
|
||||
|
||||
@ -1916,7 +1916,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
assert_equal(self.nodes[0].getblock(block_hash), self.nodes[2].getblock(block_hash))
|
||||
height -= 1
|
||||
|
||||
@subtest
|
||||
@subtest # type: ignore
|
||||
def test_witness_sigops(self):
|
||||
"""Test sigop counting is correct inside witnesses."""
|
||||
|
||||
|
@ -9,6 +9,7 @@ This file is modified from python-bitcoinlib.
|
||||
import hashlib
|
||||
import struct
|
||||
import unittest
|
||||
from typing import List, Dict
|
||||
|
||||
from .messages import (
|
||||
CTransaction,
|
||||
@ -21,7 +22,7 @@ from .messages import (
|
||||
)
|
||||
|
||||
MAX_SCRIPT_ELEMENT_SIZE = 520
|
||||
OPCODE_NAMES = {}
|
||||
OPCODE_NAMES = {} # type: Dict[CScriptOp, str]
|
||||
|
||||
def hash160(s):
|
||||
return hashlib.new('ripemd160', sha256(s)).digest()
|
||||
@ -37,7 +38,7 @@ def bn2vch(v):
|
||||
# Serialize to bytes
|
||||
return encoded_v.to_bytes(n_bytes, 'little')
|
||||
|
||||
_opcode_instances = []
|
||||
_opcode_instances = [] # type: List[CScriptOp]
|
||||
class CScriptOp(int):
|
||||
"""A single script opcode"""
|
||||
__slots__ = ()
|
||||
|
@ -91,6 +91,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
||||
|
||||
This class also contains various public and private helper methods."""
|
||||
|
||||
chain = None # type: str
|
||||
setup_clean_chain = None # type: bool
|
||||
|
||||
def __init__(self):
|
||||
"""Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method"""
|
||||
self.chain = 'regtest'
|
||||
@ -407,7 +410,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
||||
|
||||
# Public helper methods. These can be accessed by the subclass test scripts.
|
||||
|
||||
def add_nodes(self, num_nodes, extra_args=None, *, rpchost=None, binary=None, binary_cli=None, versions=None):
|
||||
def add_nodes(self, num_nodes: int, extra_args=None, *, rpchost=None, binary=None, binary_cli=None, versions=None):
|
||||
"""Instantiate TestNode objects.
|
||||
|
||||
Should only be called once after the nodes have been specified in
|
||||
|
@ -42,7 +42,7 @@ except UnicodeDecodeError:
|
||||
if os.name != 'nt' or sys.getwindowsversion() >= (10, 0, 14393):
|
||||
if os.name == 'nt':
|
||||
import ctypes
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
kernel32 = ctypes.windll.kernel32 # type: ignore
|
||||
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
||||
STD_OUTPUT_HANDLE = -11
|
||||
STD_ERROR_HANDLE = -12
|
||||
|
@ -7,6 +7,7 @@
|
||||
# Check for specified flake8 warnings in python files.
|
||||
|
||||
export LC_ALL=C
|
||||
export MYPY_CACHE_DIR="${BASE_ROOT_DIR}/test/.mypy_cache"
|
||||
|
||||
enabled=(
|
||||
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 "
|
||||
echo "$@"
|
||||
fi
|
||||
)
|
||||
|
||||
mypy --ignore-missing-imports $(git ls-files "test/functional/*.py")
|
Loading…
Reference in New Issue
Block a user