Skip to content

Commit a0dad2d

Browse files
author
Paul Robinson
authored
pull master into asyncify-contract (#2389)
1 parent 1bfda9f commit a0dad2d

File tree

92 files changed

+628
-426
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+628
-426
lines changed

Dockerfile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
FROM python:3.6
1+
FROM python:3.9
22

33
# Set up code directory
4-
RUN mkdir -p /usr/src/app
54
WORKDIR /usr/src/app
65

76
# Install Linux dependencies

docker-compose.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ services:
33
sandbox:
44
build:
55
context: .
6-
environment:
7-
PARITY_VERSION: v2.3.5
86
volumes:
97
- .:/code
108
command: tail -f /dev/null

docs/conf.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@
5252
master_doc = 'index'
5353

5454
# General information about the project.
55-
project = u'Web3.py'
56-
copyright = u'2018, Piper Merriam, Jason Carver'
55+
project = 'Web3.py'
56+
copyright = '2018, Piper Merriam, Jason Carver'
5757

5858
__version__ = setup_version
5959
# The version info for the project you're documenting, acts as replacement for
@@ -222,8 +222,8 @@
222222
# (source start file, target name, title,
223223
# author, documentclass [howto, manual, or own class]).
224224
latex_documents = [
225-
('index', 'Populus.tex', u'Populus Documentation',
226-
u'Piper Merriam', 'manual'),
225+
('index', 'Populus.tex', 'Populus Documentation',
226+
'Piper Merriam', 'manual'),
227227
]
228228

229229
# The name of an image file (relative to this directory) to place at the top of
@@ -252,8 +252,8 @@
252252
# One entry per manual page. List of tuples
253253
# (source start file, name, description, authors, manual section).
254254
man_pages = [
255-
('index', 'web3', u'Web3.py Documentation',
256-
[u'Piper Merriam'], 1)
255+
('index', 'web3', 'Web3.py Documentation',
256+
['Piper Merriam'], 1)
257257
]
258258

259259
# If true, show URL addresses after external links.
@@ -266,8 +266,8 @@
266266
# (source start file, target name, title, author,
267267
# dir menu entry, description, category)
268268
texinfo_documents = [
269-
('index', 'Web3.py', u'Web3.py Documentation',
270-
u'Piper Merriam', 'Web3.py', 'Backend agnostic Ethereum client interactions.',
269+
('index', 'Web3.py', 'Web3.py Documentation',
270+
'Piper Merriam', 'Web3.py', 'Backend agnostic Ethereum client interactions.',
271271
'Miscellaneous'),
272272
]
273273

docs/examples.rst

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,7 @@ The script can be run with: ``python ./eventscanner.py <your JSON-RPC API URL>``
992992
# from our in-memory cache
993993
block_when = get_block_when(block_number)
994994
995-
logger.debug("Processing event %s, block:%d count:%d", evt["event"], evt["blockNumber"])
995+
logger.debug(f"Processing event {evt["event"]}, block: {evt["blockNumber"]} count: {evt["blockNumber"]}")
996996
processed = self.state.process_event(block_when, evt)
997997
all_processed.append(processed)
998998
@@ -1064,8 +1064,8 @@ The script can be run with: ``python ./eventscanner.py <your JSON-RPC API URL>``
10641064
# Print some diagnostics to logs to try to fiddle with real world JSON-RPC API performance
10651065
estimated_end_block = current_block + chunk_size
10661066
logger.debug(
1067-
"Scanning token transfers for blocks: %d - %d, chunk size %d, last chunk scan took %f, last logs found %d",
1068-
current_block, estimated_end_block, chunk_size, last_scan_duration, last_logs_found)
1067+
f"Scanning token transfers for blocks: {current_block} - {estimated_end_block}, chunk size {chunk_size}, last chunk scan took {last_scan_duration}, last logs found {last_logs_found}"
1068+
)
10691069
10701070
start = time.time()
10711071
actual_end_block, end_block_timestamp, new_entries = self.scan_chunk(current_block, estimated_end_block)
@@ -1116,12 +1116,7 @@ The script can be run with: ``python ./eventscanner.py <your JSON-RPC API URL>``
11161116
if i < retries - 1:
11171117
# Give some more verbose info than the default middleware
11181118
logger.warning(
1119-
"Retrying events for block range %d - %d (%d) failed with %s, retrying in %s seconds",
1120-
start_block,
1121-
end_block,
1122-
end_block-start_block,
1123-
e,
1124-
delay)
1119+
f"Retrying events for block range {start_block} - {end_block} ({end_block-start_block}) failed with {e} , retrying in {delay} seconds")
11251120
# Decrease the `eth_getBlocks` range
11261121
end_block = start_block + ((end_block - start_block) // 2)
11271122
# Let the JSON-RPC to recover e.g. from restart
@@ -1175,7 +1170,7 @@ The script can be run with: ``python ./eventscanner.py <your JSON-RPC API URL>``
11751170
toBlock=to_block
11761171
)
11771172
1178-
logger.debug("Querying eth_getLogs with the following parameters: %s", event_filter_params)
1173+
logger.debug(f"Querying eth_getLogs with the following parameters: {event_filter_params}")
11791174
11801175
# Call JSON-RPC API on your Ethereum node.
11811176
# get_logs() returns raw AttributedDict entries

docs/v4_migration.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ printing out new block hashes as they appear:
6868
.. code-block:: python
6969
7070
>>> def new_block_callback(block_hash):
71-
... print "New Block: {0}".format(block_hash)
71+
... print(f"New Block: {block_hash}")
7272
...
7373
>>> new_block_filter = web3.eth.filter('latest')
7474
>>> new_block_filter.watch(new_block_callback)
@@ -79,7 +79,7 @@ In v4, that same logic:
7979
8080
>>> new_block_filter = web3.eth.filter('latest')
8181
>>> for block_hash in new_block_filter.get_new_entries():
82-
... print("New Block: {}".format(block_hash))
82+
... print(f"New Block: {block_hash}")
8383
8484
The caller is responsible for polling the results from ``get_new_entries()``.
8585
See :ref:`asynchronous_filters` for examples of filter-event handling with web3 v4.

docs/web3.eth.account.rst

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Hosted Private Key
3939
not your Ether" in the wise words of Andreas Antonopoulos.
4040

4141
Some Common Uses for Local Private Keys
42-
-------------------------------------------
42+
---------------------------------------
4343

4444
A very common reason to work with local private keys is to interact
4545
with a hosted node.
@@ -55,6 +55,59 @@ Using private keys usually involves ``w3.eth.account`` in one way or another. Re
5555
or see a full list of things you can do in the docs for
5656
:class:`eth_account.Account <eth_account.account.Account>`.
5757

58+
Read a private key from an environment variable
59+
-----------------------------------------------
60+
61+
In this example we pass the private key to our Python application in an
62+
`environment variable <https://en.wikipedia.org/wiki/Environment_variable>`_.
63+
This private key is then added to the transaction signing keychain
64+
with ``Signing`` middleware.
65+
66+
If unfamiliar, note that you can `export your private keys from Metamask and other wallets <https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key>`_.
67+
68+
.. warning ::
69+
70+
- **Never** share your private keys.
71+
- **Never** put your private keys in source code.
72+
- **Never** commit private keys to a Git repository.
73+
74+
Example ``account_test_script.py``
75+
76+
.. code-block:: python
77+
78+
import os
79+
from eth_account import Account
80+
from eth_account.signers.local import LocalAccount
81+
from web3.auto import w3
82+
from web3.middleware import construct_sign_and_send_raw_middleware
83+
84+
private_key = os.environ.get("PRIVATE_KEY")
85+
assert private_key is not None, "You must set PRIVATE_KEY environment variable"
86+
assert private_key.startswith("0x"), "Private key must start with 0x hex prefix"
87+
88+
account: LocalAccount = Account.from_key(private_key)
89+
w3.middleware_onion.add(construct_sign_and_send_raw_middleware(account))
90+
91+
print(f"Your hot wallet address is {account.address}")
92+
93+
Example how to run this in UNIX shell:
94+
95+
.. code-block:: shell
96+
97+
# Generate a new 256-bit random integer using openssl UNIX command that acts as a private key.
98+
# You can also do:
99+
# python -c "from web3 import Web3; w3 = Web3(); acc = w3.eth.account.create(); print(f'private key={w3.toHex(acc.key)}, account={acc.address}')"
100+
# Store this in a safe place, like in your password manager.
101+
export PRIVATE_KEY=0x`openssl rand -hex 32`
102+
103+
# Run our script
104+
python account_test_script.py
105+
106+
This will print::
107+
108+
Your hot wallet address is 0x27C8F899bb69E1501BBB96d09d7477a2a7518918
109+
110+
58111
.. _extract_geth_pk:
59112

60113
Extract private key from geth keyfile

docs/web3.eth.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ The following methods are available on the ``web3.eth`` namespace.
364364
365365
assert rlp_account == HexaryTrie.get_from_proof(
366366
root, trie_key, format_proof_nodes(proof.accountProof)
367-
), "Failed to verify account proof {}".format(proof.address)
367+
), f"Failed to verify account proof {proof.address}"
368368
369369
for storage_proof in proof.storageProof:
370370
trie_key = keccak(pad_bytes(b'\x00', 32, storage_proof.key))
@@ -376,7 +376,7 @@ The following methods are available on the ``web3.eth`` namespace.
376376
377377
assert rlp_value == HexaryTrie.get_from_proof(
378378
root, trie_key, format_proof_nodes(storage_proof.proof)
379-
), "Failed to verify storage proof {}".format(storage_proof.key)
379+
), f"Failed to verify storage proof {storage_proof.key}"
380380
381381
return True
382382

ens/main.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,10 @@ def setup_name(
227227
address = resolved
228228
elif resolved and address != resolved and resolved != EMPTY_ADDR_HEX:
229229
raise AddressMismatch(
230-
"Could not set address %r to point to name, because the name resolves to %r. "
231-
"To change the name for an existing address, call setup_address() first." % (
232-
address, resolved
233-
)
230+
f"Could not set address {address!r} to point to name, "
231+
f"because the name resolves to {resolved!r}. "
232+
"To change the name for an existing address, call "
233+
"setup_address() first."
234234
)
235235
if is_none_or_zero_address(address):
236236
address = self.owner(name)
@@ -336,9 +336,8 @@ def _assert_control(self, account: ChecksumAddress, name: str,
336336
parent_owned: Optional[str] = None) -> None:
337337
if not address_in(account, self.w3.eth.accounts):
338338
raise UnauthorizedError(
339-
"in order to modify %r, you must control account %r, which owns %r" % (
340-
name, account, parent_owned or name
341-
)
339+
f"in order to modify {name!r}, you must control account"
340+
f" {account!r}, which owns {parent_owned or name!r}"
342341
)
343342

344343
def _first_owner(self, name: str) -> Tuple[Optional[ChecksumAddress], Sequence[str], str]:
@@ -375,7 +374,7 @@ def _claim_ownership(
375374
label_to_hash(label),
376375
owner
377376
).transact(transact)
378-
owned = "%s.%s" % (label, owned)
377+
owned = f"{label}.{owned}"
379378

380379
def _set_resolver(
381380
self,

ens/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def sha3_text(val: Union[str, bytes]) -> HexBytes:
140140
def label_to_hash(label: str) -> HexBytes:
141141
label = normalize_name(label)
142142
if '.' in label:
143-
raise ValueError("Cannot generate hash for label %r with a '.'" % label)
143+
raise ValueError(f"Cannot generate hash for label {label!r} with a '.'")
144144
return Web3().keccak(text=label)
145145

146146

ethpm/contract.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def __init__(self, address: bytes, **kwargs: Any) -> None:
5656
)
5757
validate_address(address)
5858
# type ignored to allow for undefined **kwargs on `Contract` base class __init__
59-
super(LinkableContract, self).__init__(address=address, **kwargs) # type: ignore
59+
super().__init__(address=address, **kwargs) # type: ignore
6060

6161
@classmethod
6262
def factory(
@@ -69,15 +69,15 @@ def factory(
6969
if not is_prelinked_bytecode(to_bytes(hexstr=bytecode), dep_link_refs):
7070
needs_bytecode_linking = True
7171
kwargs = assoc(kwargs, "needs_bytecode_linking", needs_bytecode_linking)
72-
return super(LinkableContract, cls).factory(w3, class_name, **kwargs)
72+
return super().factory(w3, class_name, **kwargs)
7373

7474
@classmethod
7575
def constructor(cls, *args: Any, **kwargs: Any) -> ContractConstructor:
7676
if cls.needs_bytecode_linking:
7777
raise BytecodeLinkingError(
7878
"Contract cannot be deployed until its bytecode is linked."
7979
)
80-
return super(LinkableContract, cls).constructor(*args, **kwargs)
80+
return super().constructor(*args, **kwargs)
8181

8282
@classmethod
8383
def link_bytecode(cls, attr_dict: Dict[str, str]) -> Type["LinkableContract"]:
@@ -111,7 +111,7 @@ def validate_attr_dict(self, attr_dict: Dict[str, str]) -> None:
111111
"""
112112
Validates that ContractType keys in attr_dict reference existing manifest ContractTypes.
113113
"""
114-
attr_dict_names = list(attr_dict.keys())
114+
attr_dict_names = attr_dict.keys()
115115

116116
if not self.unlinked_references and not self.linked_references:
117117
raise BytecodeLinkingError(
@@ -122,8 +122,8 @@ def validate_attr_dict(self, attr_dict: Dict[str, str]) -> None:
122122
linked_refs = self.linked_references or ({},)
123123
all_link_refs = unlinked_refs + linked_refs
124124

125-
all_link_names = [ref["name"] for ref in all_link_refs if ref]
126-
if set(attr_dict_names) != set(all_link_names):
125+
all_link_names = {ref["name"] for ref in all_link_refs if ref}
126+
if attr_dict_names != all_link_names:
127127
raise BytecodeLinkingError(
128128
"All link references must be defined when calling "
129129
"`link_bytecode` on a contract factory."

ethpm/tools/builder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ def normalize_compiler_output(compiler_output: Dict[str, Any]) -> Dict[str, Any]
498498
]
499499
paths, names = zip(*paths_and_names)
500500
if len(names) != len(set(names)):
501-
duplicates = set([name for name in names if names.count(name) > 1])
501+
duplicates = {name for name in names if names.count(name) > 1}
502502
raise ManifestBuildingError(
503503
f"Duplicate contract types: {duplicates} were found in the compiler output."
504504
)

ethpm/validation/manifest.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ def _load_schema_data() -> Dict[str, Any]:
6666

6767

6868
def extract_contract_types_from_deployments(deployment_data: List[Any]) -> Set[str]:
69-
contract_types = set(
69+
contract_types = {
7070
deployment["contractType"]
7171
for chain_deployments in deployment_data
7272
for deployment in chain_deployments.values()
73-
)
73+
}
7474
return contract_types
7575

7676

@@ -108,11 +108,11 @@ def validate_manifest_deployments(manifest: Dict[str, Any]) -> None:
108108
"""
109109
Validate that a manifest's deployments contracts reference existing contractTypes.
110110
"""
111-
if set(("contractTypes", "deployments")).issubset(manifest):
112-
all_contract_types = list(manifest["contractTypes"].keys())
113-
all_deployments = list(manifest["deployments"].values())
111+
if {"contractTypes", "deployments"}.issubset(manifest):
112+
all_contract_types = manifest["contractTypes"].keys()
113+
all_deployments = manifest["deployments"].values()
114114
all_deployment_names = extract_contract_types_from_deployments(all_deployments)
115-
missing_contract_types = set(all_deployment_names).difference(
115+
missing_contract_types = all_deployment_names.difference(
116116
all_contract_types
117117
)
118118
if missing_contract_types:

newsfragments/2369.misc.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bump docker base image to ``3.9`` and remove parity tests from docker.

newsfragments/2372.misc.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
improve the code base by:
2+
- using `f-strings`.
3+
- removing redundant `set()` calls.
4+
- making error messages more readable in the code.
5+
- removing python2 `u` prefix.
6+
- other cleanups.

newsfragments/2376.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add async default_chain_id and chain_id setter

newsfragments/2380.doc.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Document reading private keys from environment variables
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add ``attach_methods()`` to ``Module`` class to facilitate attaching methods to modules.

tests/core/contracts/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,7 @@ def invoke_contract(api_call_desig='call',
10241024
tx_params={}):
10251025
allowable_call_desig = ['call', 'transact', 'estimateGas', 'buildTransaction']
10261026
if api_call_desig not in allowable_call_desig:
1027-
raise ValueError("allowable_invoke_method must be one of: %s" % allowable_call_desig)
1027+
raise ValueError(f"allowable_invoke_method must be one of: {allowable_call_desig}")
10281028

10291029
function = contract.functions[contract_function]
10301030
result = getattr(function(*func_args, **func_kwargs), api_call_desig)(tx_params)

tests/core/contracts/test_contract_ambiguous_functions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def test_find_or_get_functions_by_type(w3, method, args, repr_func, expected):
153153
(
154154
'get_function_by_selector',
155155
(b'\x00' * (4 + 1), ),
156-
r'expected value of size 4 bytes. Got: %s bytes' % (4 + 1),
156+
f'expected value of size 4 bytes. Got: {(4 + 1)} bytes',
157157
ValueError
158158
),
159159
(

tests/core/contracts/test_contract_call_interface.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ def test_reflect_fixed_value(fixed_reflection_contract, function, value):
726726
('reflect_short_u', Decimal('0.01'), "no matching argument types"),
727727
(
728728
'reflect_short_u',
729-
Decimal('1e-%d' % (DEFAULT_DECIMALS + 1)),
729+
Decimal(f'1e-{DEFAULT_DECIMALS + 1}'),
730730
"no matching argument types",
731731
),
732732
('reflect_short_u', Decimal('25.4' + '9' * DEFAULT_DECIMALS), "no matching argument types"),

0 commit comments

Comments
 (0)