Skip to content

Commit 6bf0d32

Browse files
authored
Merge pull request #1542 from njgheorghita/type-hints-utils-complete
Finish type hints in web3._utils
2 parents 5d7abc2 + 2462afe commit 6bf0d32

File tree

16 files changed

+322
-153
lines changed

16 files changed

+322
-153
lines changed

setup.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,12 @@
7777
"ipfshttpclient>=0.4.12,<1",
7878
"jsonschema>=3.0.0,<4.0.0",
7979
"lru-dict>=1.1.6,<2.0.0",
80-
# remove mypy_extensions after python_requires>=3.8
81-
# see web3._utils.compat
82-
"mypy_extensions>=0.4.1,<1.0.0",
8380
"protobuf>=3.10.0,<4",
8481
"pypiwin32>=223;platform_system=='Windows'",
8582
"requests>=2.16.0,<3.0.0",
8683
"websockets>=8.1.0,<9.0.0",
84+
# remove mypy_extensions & typing-extensions after python_requires>=3.8
85+
# see web3._utils.compat
8786
"typing-extensions>=3.7.4.1,<4",
8887
],
8988
setup_requires=['setuptools-markdown'],

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,4 @@ extras=linter
6363
commands=
6464
flake8 {toxinidir}/web3 {toxinidir}/ens {toxinidir}/ethpm {toxinidir}/tests
6565
isort --recursive --check-only --diff {toxinidir}/web3/ {toxinidir}/ens/ {toxinidir}/ethpm/ {toxinidir}/tests/
66-
mypy -p web3._utils.abi -p web3._utils.admin -p web3._utils.blocks -p web3._utils.caching -p web3._utils.contracts -p web3._utils.datatypes -p web3._utils.decorators -p web3._utils.empty -p web3._utils.ens -p web3._utils.events -p web3._utils.filters -p web3._utils.method_formatters -p web3._utils.module_testing -p web3._utils.request -p web3._utils.shh -p web3._utils.threads -p web3._utils.transactions -p web3._utils.txpool -p web3._utils.validation -p web3._utils.windows -p web3.providers -p web3.main -p web3.contract -p web3.datastructures -p web3.eth -p web3.exceptions -p web3.geth -p web3.iban -p web3.logs -p web3.manager -p web3.module -p web3.net -p web3.parity -p web3.middleware -p web3.method -p web3.pm -p web3.auto -p web3.gas_strategies -p web3.testing -p web3.tools -p web3.version -p ethpm -p ens --config-file {toxinidir}/mypy.ini
66+
mypy -p web3._utils -p web3.providers -p web3.main -p web3.contract -p web3.datastructures -p web3.eth -p web3.exceptions -p web3.geth -p web3.iban -p web3.logs -p web3.manager -p web3.module -p web3.net -p web3.parity -p web3.middleware -p web3.method -p web3.pm -p web3.auto -p web3.gas_strategies -p web3.testing -p web3.tools -p web3.version -p ethpm -p ens --config-file {toxinidir}/mypy.ini

web3/_utils/admin.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
Tuple,
55
)
66

7-
from typing_extensions import (
7+
from web3._utils.compat import (
88
Protocol,
99
)
10-
1110
from web3._utils.rpc_abi import (
1211
RPC,
1312
)

web3/_utils/compat/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# remove once web3 supports python>=3.8
22
# TypedDict was added to typing in 3.8
33
try:
4-
from typing import Literal, TypedDict # type: ignore
4+
from typing import Literal, Protocol, TypedDict # type: ignore
55
except ImportError:
6-
from mypy_extensions import TypedDict # noqa: F401
7-
from typing_extensions import Literal # noqa: F401
6+
from typing_extensions import Literal, Protocol, TypedDict # type: ignore # noqa: F401

web3/_utils/encoding.py

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
# String encodings and numeric representations
22
import json
33
import re
4+
from typing import (
5+
Any,
6+
Callable,
7+
Dict,
8+
Iterable,
9+
Sequence,
10+
Type,
11+
Union,
12+
)
413

514
from eth_abi.encoding import (
615
BaseArrayEncoder,
716
)
17+
from eth_typing import (
18+
HexStr,
19+
Primitives,
20+
TypeStr,
21+
)
822
from eth_utils import (
923
add_0x_prefix,
1024
big_endian_to_int,
@@ -47,7 +61,7 @@
4761
)
4862

4963

50-
def hex_encode_abi_type(abi_type, value, force_size=None):
64+
def hex_encode_abi_type(abi_type: TypeStr, value: Any, force_size: int=None) -> HexStr:
5165
"""
5266
Encodes value into a hex string in format of abi_type
5367
"""
@@ -57,7 +71,9 @@ def hex_encode_abi_type(abi_type, value, force_size=None):
5771
data_size = force_size or size_of_type(abi_type)
5872
if is_array_type(abi_type):
5973
sub_type = sub_type_of_array_type(abi_type)
60-
return "".join([remove_0x_prefix(hex_encode_abi_type(sub_type, v, 256)) for v in value])
74+
return HexStr(
75+
"".join([remove_0x_prefix(hex_encode_abi_type(sub_type, v, 256)) for v in value])
76+
)
6177
elif is_bool_type(abi_type):
6278
return to_hex_with_size(value, data_size)
6379
elif is_uint_type(abi_type):
@@ -79,7 +95,7 @@ def hex_encode_abi_type(abi_type, value, force_size=None):
7995
)
8096

8197

82-
def to_hex_twos_compliment(value, bit_size):
98+
def to_hex_twos_compliment(value: Any, bit_size: int) -> HexStr:
8399
"""
84100
Converts integer value to twos compliment hex representation with given bit_size
85101
"""
@@ -88,34 +104,34 @@ def to_hex_twos_compliment(value, bit_size):
88104

89105
value = (1 << bit_size) + value
90106
hex_value = hex(value)
91-
hex_value = hex_value.rstrip("L")
107+
hex_value = HexStr(hex_value.rstrip("L"))
92108
return hex_value
93109

94110

95-
def to_hex_with_size(value, bit_size):
111+
def to_hex_with_size(value: Any, bit_size: int) -> HexStr:
96112
"""
97113
Converts a value to hex with given bit_size:
98114
"""
99115
return pad_hex(to_hex(value), bit_size)
100116

101117

102-
def pad_hex(value, bit_size):
118+
def pad_hex(value: Any, bit_size: int) -> HexStr:
103119
"""
104120
Pads a hex string up to the given bit_size
105121
"""
106122
value = remove_0x_prefix(value)
107123
return add_0x_prefix(value.zfill(int(bit_size / 4)))
108124

109125

110-
def trim_hex(hexstr):
126+
def trim_hex(hexstr: HexStr) -> HexStr:
111127
if hexstr.startswith('0x0'):
112-
hexstr = re.sub('^0x0+', '0x', hexstr)
128+
hexstr = HexStr(re.sub('^0x0+', '0x', hexstr))
113129
if hexstr == '0x':
114-
hexstr = '0x0'
130+
hexstr = HexStr('0x0')
115131
return hexstr
116132

117133

118-
def to_int(value=None, hexstr=None, text=None):
134+
def to_int(value: Primitives=None, hexstr: HexStr=None, text: str=None) -> int:
119135
"""
120136
Converts value to it's integer representation.
121137
@@ -142,14 +158,14 @@ def to_int(value=None, hexstr=None, text=None):
142158

143159

144160
@curry
145-
def pad_bytes(fill_with, num_bytes, unpadded):
161+
def pad_bytes(fill_with: bytes, num_bytes: int, unpadded: bytes) -> bytes:
146162
return unpadded.rjust(num_bytes, fill_with)
147163

148164

149165
zpad_bytes = pad_bytes(b'\0')
150166

151167

152-
def to_bytes(primitive=None, hexstr=None, text=None):
168+
def to_bytes(primitive: Primitives=None, hexstr: HexStr=None, text: str=None) -> bytes:
153169
assert_one_val(primitive, hexstr=hexstr, text=text)
154170

155171
if is_boolean(primitive):
@@ -160,14 +176,14 @@ def to_bytes(primitive=None, hexstr=None, text=None):
160176
return to_bytes(hexstr=to_hex(primitive))
161177
elif hexstr is not None:
162178
if len(hexstr) % 2:
163-
hexstr = '0x0' + remove_0x_prefix(hexstr)
179+
hexstr = HexStr('0x0' + remove_0x_prefix(hexstr))
164180
return decode_hex(hexstr)
165181
elif text is not None:
166182
return text.encode('utf-8')
167183
raise TypeError("expected an int in first arg, or keyword of hexstr or text")
168184

169185

170-
def to_text(primitive=None, hexstr=None, text=None):
186+
def to_text(primitive: Primitives=None, hexstr: HexStr=None, text: str=None) -> str:
171187
assert_one_val(primitive, hexstr=hexstr, text=text)
172188

173189
if hexstr is not None:
@@ -185,13 +201,15 @@ def to_text(primitive=None, hexstr=None, text=None):
185201

186202

187203
@curry
188-
def text_if_str(to_type, text_or_primitive):
204+
def text_if_str(
205+
to_type: Callable[..., str], text_or_primitive: Union[Primitives, HexStr, str]
206+
) -> str:
189207
"""
190208
Convert to a type, assuming that strings can be only unicode text (not a hexstr)
191209
192210
@param to_type is a function that takes the arguments (primitive, hexstr=hexstr, text=text),
193211
eg~ to_bytes, to_text, to_hex, to_int, etc
194-
@param hexstr_or_primitive in bytes, str, or int.
212+
@param text_or_primitive in bytes, str, or int.
195213
"""
196214
if isinstance(text_or_primitive, str):
197215
(primitive, text) = (None, text_or_primitive)
@@ -201,17 +219,19 @@ def text_if_str(to_type, text_or_primitive):
201219

202220

203221
@curry
204-
def hexstr_if_str(to_type, hexstr_or_primitive):
222+
def hexstr_if_str(
223+
to_type: Callable[..., HexStr], hexstr_or_primitive: Union[Primitives, HexStr, str]
224+
) -> HexStr:
205225
"""
206226
Convert to a type, assuming that strings can be only hexstr (not unicode text)
207227
208228
@param to_type is a function that takes the arguments (primitive, hexstr=hexstr, text=text),
209229
eg~ to_bytes, to_text, to_hex, to_int, etc
210-
@param text_or_primitive in bytes, str, or int.
230+
@param hexstr_or_primitive in bytes, str, or int.
211231
"""
212232
if isinstance(hexstr_or_primitive, str):
213233
(primitive, hexstr) = (None, hexstr_or_primitive)
214-
if remove_0x_prefix(hexstr) and not is_hex(hexstr):
234+
if remove_0x_prefix(HexStr(hexstr)) and not is_hex(hexstr):
215235
raise ValueError(
216236
"when sending a str, it must be a hex string. Got: {0!r}".format(
217237
hexstr_or_primitive,
@@ -230,21 +250,21 @@ class FriendlyJsonSerde:
230250
information on which fields failed, to show more
231251
helpful information in the raised error messages.
232252
"""
233-
def _json_mapping_errors(self, mapping):
253+
def _json_mapping_errors(self, mapping: Dict[Any, Any]) -> Iterable[str]:
234254
for key, val in mapping.items():
235255
try:
236256
self._friendly_json_encode(val)
237257
except TypeError as exc:
238258
yield "%r: because (%s)" % (key, exc)
239259

240-
def _json_list_errors(self, iterable):
260+
def _json_list_errors(self, iterable: Iterable[Any]) -> Iterable[str]:
241261
for index, element in enumerate(iterable):
242262
try:
243263
self._friendly_json_encode(element)
244264
except TypeError as exc:
245265
yield "%d: because (%s)" % (index, exc)
246266

247-
def _friendly_json_encode(self, obj, cls=None):
267+
def _friendly_json_encode(self, obj: Dict[Any, Any], cls: Type[json.JSONEncoder]=None) -> str:
248268
try:
249269
encoded = json.dumps(obj, cls=cls)
250270
return encoded
@@ -258,7 +278,7 @@ def _friendly_json_encode(self, obj, cls=None):
258278
else:
259279
raise full_exception
260280

261-
def json_decode(self, json_str):
281+
def json_decode(self, json_str: str) -> Dict[Any, Any]:
262282
try:
263283
decoded = json.loads(json_str)
264284
return decoded
@@ -268,14 +288,14 @@ def json_decode(self, json_str):
268288
# so we have to re-raise the same type.
269289
raise json.decoder.JSONDecodeError(err_msg, exc.doc, exc.pos)
270290

271-
def json_encode(self, obj, cls=None):
291+
def json_encode(self, obj: Dict[Any, Any], cls: Type[json.JSONEncoder]=None) -> str:
272292
try:
273293
return self._friendly_json_encode(obj, cls=cls)
274294
except TypeError as exc:
275295
raise TypeError("Could not encode to JSON: {}".format(exc))
276296

277297

278-
def to_4byte_hex(hex_or_str_or_bytes):
298+
def to_4byte_hex(hex_or_str_or_bytes: Union[HexStr, str, bytes, int]) -> HexStr:
279299
size_of_4bytes = 4 * 8
280300
byte_str = hexstr_if_str(to_bytes, hex_or_str_or_bytes)
281301
if len(byte_str) > 4:
@@ -289,15 +309,15 @@ def to_4byte_hex(hex_or_str_or_bytes):
289309
class DynamicArrayPackedEncoder(BaseArrayEncoder):
290310
is_dynamic = True
291311

292-
def encode(self, value):
312+
def encode(self, value: Sequence[Any]) -> bytes:
293313
encoded_elements = self.encode_elements(value)
294314
encoded_value = encoded_elements
295315

296316
return encoded_value
297317

298318

299319
# TODO: Replace with eth-abi packed encoder once web3 requires eth-abi>=2
300-
def encode_single_packed(_type, value):
320+
def encode_single_packed(_type: TypeStr, value: Any) -> bytes:
301321
import codecs
302322
from eth_abi import (
303323
grammar as abi_type_parser,
@@ -315,18 +335,19 @@ def encode_single_packed(_type, value):
315335
return codecs.encode(value, 'utf8')
316336
elif abi_type.base == "bytes":
317337
return value
338+
return None
318339

319340

320341
class Web3JsonEncoder(json.JSONEncoder):
321-
def default(self, obj):
342+
def default(self, obj: Any) -> Union[Dict[Any, Any], HexStr]:
322343
if isinstance(obj, AttributeDict):
323344
return {k: v for k, v in obj.items()}
324345
if isinstance(obj, HexBytes):
325-
return obj.hex()
346+
return HexStr(obj.hex())
326347
return json.JSONEncoder.default(self, obj)
327348

328349

329-
def to_json(obj):
350+
def to_json(obj: Dict[Any, Any]) -> str:
330351
'''
331352
Convert a complex object (like a transaction object) to a JSON string
332353
'''

0 commit comments

Comments
 (0)