Skip to content

Commit b275373

Browse files
committed
add type declarations, checks, fixes for bugs revealed by mypy
1 parent 2f7311e commit b275373

20 files changed

Lines changed: 595 additions & 388 deletions

‎bitcointx/__init__.py‎

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from abc import ABCMeta
1818
from contextlib import contextmanager
1919
from collections import OrderedDict
20-
from typing import Tuple, Optional
20+
from typing import Dict, List, Tuple, Optional, Type
2121

2222
import bitcointx.core
2323
import bitcointx.core.script
@@ -39,8 +39,8 @@ class ChainParamsMeta(ABCMeta):
3939
('WALLET_DISPATCHER', issubclass,
4040
bitcointx.wallet.WalletCoinClassDispatcher),
4141
)
42-
_registered_classes = OrderedDict()
43-
_common_base_cls = None
42+
_registered_classes: Dict[str, 'ChainParamsBase'] = OrderedDict()
43+
_common_base_cls: Optional['ChainParamsBase'] = None
4444

4545
def __new__(cls, cls_name, bases, dct, name=None):
4646
"""check that the chainparams class uses unique base class
@@ -94,12 +94,12 @@ def __new__(cls, cls_name, bases, dct, name=None):
9494
return cls_instance
9595

9696

97-
def find_chain_params(*, name=None):
97+
def find_chain_params(*, name=None) -> Optional['ChainParamsBase']:
9898
return ChainParamsMeta._registered_classes.get(name)
9999

100100

101-
def get_registered_chain_params():
102-
result = []
101+
def get_registered_chain_params() -> List['ChainParamsBase']:
102+
result: List[ChainParamsBase] = []
103103
for param_cls in ChainParamsMeta._registered_classes.values():
104104
if param_cls not in result:
105105
result.append(param_cls)
@@ -110,7 +110,11 @@ def get_registered_chain_params():
110110
class ChainParamsBase(metaclass=ChainParamsMeta):
111111
"""All chain param classes must be a subclass of this class."""
112112

113-
def get_confdir_path(self):
113+
NAME: str
114+
RPC_PORT: int
115+
WALLET_DISPATCHER: Type[bitcointx.wallet.WalletCoinClassDispatcher]
116+
117+
def get_confdir_path(self) -> str:
114118
"""Return default location for config directory"""
115119
name = self.NAME.split('/')[0]
116120

@@ -122,12 +126,12 @@ def get_confdir_path(self):
122126

123127
return os.path.expanduser('~/.{}'.format(name))
124128

125-
def get_config_path(self):
129+
def get_config_path(self) -> str:
126130
"""Return default location for config file"""
127131
name = self.NAME.split('/')[0]
128132
return '{}/{}.conf'.format(self.get_confdir_path(), name)
129133

130-
def get_datadir_extra_name(self):
134+
def get_datadir_extra_name(self) -> str:
131135
"""Return appropriate dir name to find data for the chain,
132136
and .cookie file. For mainnet, it will be an empty string -
133137
because data directory is the same as config directory.
@@ -138,11 +142,11 @@ def get_datadir_extra_name(self):
138142
return name_parts[1]
139143

140144
@property
141-
def name(self):
145+
def name(self) -> str:
142146
return self.NAME
143147

144148
@property
145-
def readable_name(self):
149+
def readable_name(self) -> str:
146150
name_parts = self.NAME.split('/')
147151
name_parts[0] = name_parts[0].capitalize()
148152
return ' '.join(name_parts)
@@ -205,6 +209,7 @@ def select_chain_params(params, **kwargs) -> PrevAndNewParams:
205209
params_cls = find_chain_params(name=params)
206210
if params_cls is None:
207211
raise ValueError('Unknown chain %r' % params)
212+
assert isinstance(params_cls, type)
208213
params = params_cls(**kwargs)
209214
elif isinstance(params, type):
210215
params = params(**kwargs)

‎bitcointx/base58.py‎

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"""Base58 encoding and decoding"""
1717

1818
import binascii
19-
2019
import bitcointx.core
2120

2221
B58_DIGITS = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
@@ -41,7 +40,7 @@ class InvalidBase58Error(Base58Error):
4140
pass
4241

4342

44-
def encode(b):
43+
def encode(b: bytes) -> str:
4544
"""Encode bytes to a base58-encoded string"""
4645

4746
# Convert big-endian bytes to integer
@@ -52,7 +51,7 @@ def encode(b):
5251
while n > 0:
5352
n, r = divmod(n, 58)
5453
res.append(B58_DIGITS[r])
55-
res = ''.join(res[::-1])
54+
res_str = ''.join(res[::-1])
5655

5756
# Encode leading zeros as base58 zeros
5857
czero = 0
@@ -62,10 +61,10 @@ def encode(b):
6261
pad += 1
6362
else:
6463
break
65-
return B58_DIGITS[0] * pad + res
64+
return B58_DIGITS[0] * pad + res_str
6665

6766

68-
def decode(s):
67+
def decode(s: str) -> bytes:
6968
"""Decode a base58-encoding string, returning bytes"""
7069
if not s:
7170
return b''
@@ -110,7 +109,7 @@ class CBase58Data(bytes):
110109

111110
base58_prefix = b''
112111

113-
def __new__(cls, s):
112+
def __new__(cls, s) -> 'CBase58Data':
114113
k = decode(s)
115114
if len(k) < 4:
116115
raise Base58Error('data too short')
@@ -128,7 +127,7 @@ def __init__(self, s):
128127
__init__() with None in place of the string.
129128
"""
130129

131-
def __str__(self):
130+
def __str__(self) -> str:
132131
"""Convert to string"""
133132
check = bitcointx.core.Hash(self.base58_prefix + self)[0:4]
134133
return encode(self.base58_prefix + self + check)
@@ -168,21 +167,23 @@ def base58_match_prefix(cls, data):
168167
'base58 prefix does not match any known base58 address class')
169168

170169
@classmethod
171-
def from_bytes(cls, data):
170+
def from_bytes(cls, data: bytes):
172171
"""Instantiate from data"""
173-
self = bytes.__new__(cls, data)
172+
# mypy cannot handle arguments to `bytes.__new__()` at the moment,
173+
# issue: https://github.com/python/typeshed/issues/2630
174+
self = bytes.__new__(cls, data) # type: ignore
174175
self.__init__(None)
175176
return self
176177

177-
def to_bytes(self):
178+
def to_bytes(self) -> bytes:
178179
"""Convert to bytes instance
179180
180181
Note that it's the data represented that is converted;
181182
the prefix is not included.
182183
"""
183184
return b'' + self
184185

185-
def __repr__(self):
186+
def __repr__(self) -> str:
186187
return '%s(%r)' % (self.__class__.__name__, str(self))
187188

188189

‎bitcointx/bech32.py‎

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,16 @@ class CBech32Data(bytes):
2929
3030
Includes a witver and checksum.
3131
"""
32-
bech32_hrp = None
32+
bech32_hrp: str
3333

34-
def __new__(cls, s):
34+
def __new__(cls, s: str):
3535
"""from bech32 addr to """
3636
if cls.bech32_hrp is None:
3737
raise TypeError(
3838
'CBech32Data subclasses should define bech32_hrp attribute')
3939
witver, data = decode(cls.bech32_hrp, s)
40-
if witver is None and data is None:
40+
if witver is None or data is None:
41+
assert witver is None and data is None
4142
raise Bech32Error('Bech32 decoding error')
4243

4344
return cls.from_bytes(data, witver=witver)
@@ -50,30 +51,39 @@ def __init__(self, s):
5051
__init__() with None in place of the string.
5152
"""
5253

54+
witver: int
55+
5356
@classmethod
54-
def from_bytes(cls, witprog, witver=None):
57+
def from_bytes(cls, witprog: bytes, witver=None):
5558
"""Instantiate from witver and data"""
5659
if witver is None or not (0 <= witver <= 16):
5760
raise ValueError(
5861
'witver must be in range 0 to 16 inclusive; got %r' % witver)
59-
self = bytes.__new__(cls, witprog)
62+
# mypy cannot handle arguments to `bytes.__new__()` at the moment,
63+
# issue: https://github.com/python/typeshed/issues/2630
64+
self = bytes.__new__(cls, witprog) # type: ignore
6065
self.witver = witver
6166

6267
return self
6368

64-
def to_bytes(self):
69+
def to_bytes(self) -> bytes:
6570
"""Convert to bytes instance
6671
6772
Note that it's the data represented that is converted; the checkum and
6873
witver is not included.
6974
"""
7075
return b'' + self
7176

72-
def __str__(self):
77+
def __str__(self) -> str:
7378
"""Convert to string"""
74-
return encode(self.__class__.bech32_hrp, self.witver, self)
75-
76-
def __repr__(self):
79+
result = encode(self.__class__.bech32_hrp, self.witver, self)
80+
if result is None:
81+
raise AssertionError(
82+
'encode should not fail, this is data that '
83+
'was successfully decoded earlier')
84+
return result
85+
86+
def __repr__(self) -> str:
7787
return '%s(%r)' % (self.__class__.__name__, str(self))
7888

7989

0 commit comments

Comments
 (0)