Repository: ethereum/web3.py

Issue:
Deprecate and/or remove the `eth-testrpc` based providers.
With no plans to support `eth-testrpc` and the plan to replace it with `eth-tester` I think it's time to add some deprecations warnings to the providers that use `eth-testrpc` if they aren't already present and then to remove them.

We should also update the test suite to not depend on `eth-testrpc` anymore.




Discussion:
This is an option for getting #166 unstuck.
#530 looks nearly complete. Unfortunately, I don't think it actually unsticks 166, because we also need to swap in the py-evm backend and remove the pyethereum backend. It looks like that will need some work, too.

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -26,11 +26,13 @@
         "pysha3>=1.0.0,<2.0.0",
         "requests>=2.12.4,<3.0.0",
         "rlp>=0.4.7,<1.0.0",
-        "eth-tester>=0.1.0b11,<0.2.0",
     ],
     setup_requires=['setuptools-markdown'],
     extras_require={
-        'tester': ["eth-testrpc>=1.3.3,<2.0.0"],
+        'tester': [
+            "eth-tester[py-evm]==0.1.0b13",
+        ],
+        'testrpc': ["eth-testrpc>=1.3.3,<2.0.0"],
         'platform_system=="Windows"': [
             'pypiwin32'  # TODO: specify a version number, move under install_requires
         ],
diff --git a/web3/providers/eth_tester/middleware.py b/web3/providers/eth_tester/middleware.py
--- a/web3/providers/eth_tester/middleware.py
+++ b/web3/providers/eth_tester/middleware.py
@@ -4,8 +4,10 @@
     assoc,
     complement,
     compose,
-    partial,
+    curry,
     identity,
+    partial,
+    pipe,
 )
 
 from eth_utils import (
@@ -251,23 +253,55 @@ def is_named_block(value):
 })
 
 
+def guess_from(web3, transaction):
+    coinbase = web3.eth.coinbase
+    if coinbase is not None:
+        return coinbase
+
+    try:
+        return web3.eth.accounts[0]
+    except KeyError as e:
+        # no accounts available to pre-fill, carry on
+        pass
+
+    return None
+
+
+def guess_gas(web3, transaction):
+    return web3.eth.estimateGas(transaction) * 2
+
+
+@curry
+def fill_default(field, guess_func, web3, transaction):
+    if field in transaction and transaction[field] is not None:
+        return transaction
+    else:
+        guess_val = guess_func(web3, transaction)
+        return assoc(transaction, field, guess_val)
+
+
 def default_transaction_fields_middleware(make_request, web3):
+    fill_default_from = fill_default('from', guess_from, web3)
+    fill_default_gas = fill_default('gas', guess_gas, web3)
+
     def middleware(method, params):
-        if method in (
-            'eth_call',
+        # TODO send call to eth-tester without gas, and remove guess_gas entirely
+        if method == 'eth_call':
+            filled_transaction = pipe(
+                params[0],
+                fill_default_from,
+                fill_default_gas,
+            )
+            return make_request(method, [filled_transaction] + params[1:])
+        elif method in (
             'eth_estimateGas',
             'eth_sendTransaction',
         ):
-            transaction = params[0]
-            if 'from' not in transaction:
-                if web3.eth.coinbase:
-                    default_from = web3.eth.coinbase
-                elif web3.eth.accounts:
-                    default_from = web3.eth.accounts[0]
-                else:
-                    default_from = None
-                if default_from:
-                    default_txn = assoc(transaction, 'from', default_from)
-                    return make_request(method, [default_txn])
-        return make_request(method, params)
+            filled_transaction = pipe(
+                params[0],
+                fill_default_from,
+            )
+            return make_request(method, [filled_transaction] + params[1:])
+        else:
+            return make_request(method, params)
     return middleware


Test patch:
diff --git a/tests/core/contracts/test_contract_buildTransaction.py b/tests/core/contracts/test_contract_buildTransaction.py
--- a/tests/core/contracts/test_contract_buildTransaction.py
+++ b/tests/core/contracts/test_contract_buildTransaction.py
@@ -1,5 +1,9 @@
 # -*- coding: utf-8 -*-
 
+from cytoolz import (
+    dissoc,
+)
+
 import pytest
 
 # Ignore warning in pyethereum 1.6 - will go away with the upgrade
@@ -17,42 +21,43 @@ def math_contract(web3, MathContract):
 
 def test_build_transaction_with_contract_no_arguments(web3, math_contract, buildTransaction):
     txn = buildTransaction(contract=math_contract, contract_function='increment')
-    assert txn == {
+    assert dissoc(txn, 'gas') == {
         'to': math_contract.address,
         'data': '0xd09de08a',
         'value': 0,
-        'gas': 43120,
         'gasPrice': 1,
         'chainId': 1
     }
 
 
-def test_build_transaction_with_contract_class_method(web3,
-                                                      MathContract,
-                                                      math_contract,
-                                                      buildTransaction):
-    txn = buildTransaction(contract=MathContract,
-                           contract_function='increment',
-                           tx_params={'to': math_contract.address})
-    assert txn == {
+def test_build_transaction_with_contract_class_method(
+        web3,
+        MathContract,
+        math_contract,
+        buildTransaction):
+    txn = buildTransaction(
+        contract=MathContract,
+        contract_function='increment',
+        tx_params={'to': math_contract.address},
+    )
+    assert dissoc(txn, 'gas') == {
         'to': math_contract.address,
         'data': '0xd09de08a',
         'value': 0,
-        'gas': 43120,
         'gasPrice': 1,
         'chainId': 1
     }
 
 
-def test_build_transaction_with_contract_default_account_is_set(web3,
-                                                                math_contract,
-                                                                buildTransaction):
+def test_build_transaction_with_contract_default_account_is_set(
+        web3,
+        math_contract,
+        buildTransaction):
     txn = buildTransaction(contract=math_contract, contract_function='increment')
-    assert txn == {
+    assert dissoc(txn, 'gas') == {
         'to': math_contract.address,
         'data': '0xd09de08a',
         'value': 0,
-        'gas': 43120,
         'gasPrice': 1,
         'chainId': 1
     }
@@ -63,11 +68,10 @@ def my_gas_price_strategy(web3, transaction_params):
         return 5
     web3.eth.setGasPriceStrategy(my_gas_price_strategy)
     txn = buildTransaction(contract=math_contract, contract_function='increment')
-    assert txn == {
+    assert dissoc(txn, 'gas') == {
         'to': math_contract.address,
         'data': '0xd09de08a',
         'value': 0,
-        'gas': 43120,
         'gasPrice': 5,
         'chainId': 1
     }
@@ -97,31 +101,31 @@ def test_build_transaction_with_contract_to_address_supplied_errors(web3,
         (
             {}, (5,), {}, {
                 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005',  # noqa: E501
-                'value': 0, 'gas': 43242, 'gasPrice': 1, 'chainId': 1
+                'value': 0, 'gasPrice': 1, 'chainId': 1
             }, False
         ),
         (
             {'gas': 800000}, (5,), {}, {
                 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005',  # noqa: E501
-                'value': 0, 'gas': 800000, 'gasPrice': 1, 'chainId': 1
+                'value': 0, 'gasPrice': 1, 'chainId': 1
             }, False
         ),
         (
             {'gasPrice': 21000000000}, (5,), {}, {
                 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005',  # noqa: E501
-                'value': 0, 'gas': 43242, 'gasPrice': 21000000000, 'chainId': 1
+                'value': 0, 'gasPrice': 21000000000, 'chainId': 1
             }, False
         ),
         (
             {'nonce': 7}, (5,), {}, {
                 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005',  # noqa: E501
-                'value': 0, 'gas': 43242, 'gasPrice': 1, 'nonce': 7, 'chainId': 1
+                'value': 0, 'gasPrice': 1, 'nonce': 7, 'chainId': 1
             }, True
         ),
         (
             {'value': 20000}, (5,), {}, {
                 'data': '0x7cf5dab00000000000000000000000000000000000000000000000000000000000000005',  # noqa: E501
-                'value': 20000, 'gas': 43242, 'gasPrice': 1, 'chainId': 1
+                'value': 20000, 'gasPrice': 1, 'chainId': 1
             }, False
         ),
     ),
@@ -150,4 +154,8 @@ def test_build_transaction_with_contract_with_arguments(web3, skip_if_testrpc, m
                            tx_params=transaction_args)
     expected['to'] = math_contract.address
     assert txn is not None
-    assert txn == expected
+    if 'gas' in transaction_args:
+        assert txn['gas'] == transaction_args['gas']
+    else:
+        assert 'gas' in txn
+    assert dissoc(txn, 'gas') == expected
diff --git a/tests/core/contracts/test_contract_call_interface.py b/tests/core/contracts/test_contract_call_interface.py
--- a/tests/core/contracts/test_contract_call_interface.py
+++ b/tests/core/contracts/test_contract_call_interface.py
@@ -21,6 +21,7 @@ def deploy(web3, Contract, args=None):
     deploy_receipt = web3.eth.getTransactionReceipt(deploy_txn)
     assert deploy_receipt is not None
     contract = Contract(address=deploy_receipt['contractAddress'])
+    assert len(web3.eth.getCode(contract.address)) > 0
     return contract
 
 
diff --git a/tests/core/contracts/test_contract_estimateGas.py b/tests/core/contracts/test_contract_estimateGas.py
--- a/tests/core/contracts/test_contract_estimateGas.py
+++ b/tests/core/contracts/test_contract_estimateGas.py
@@ -34,5 +34,5 @@ def test_contract_estimateGas(web3, math_contract, estimateGas):
     try:
         assert abs(gas_estimate - 21472) < 200  # Geth
     except AssertionError:
-        assert abs(gas_estimate - 43020) < 200  # TestRPC
+        assert abs(gas_estimate - 32772) < 200  # eth-tester with py-evm
         pass