Repository: ethereum/web3.py

Issue:
eth.getTransaction() throws error
* Version: 3.13.3
* Python: 3.6.2
* OS: Arch Linux

### What was wrong?

I'm trying to get transaction by hash on Rinkeby testnet from Geth v1.6.7 node:

```python
>>> tx_hash = '0xe5854bda43d2dff2c4c809e044ad5ac9c1f34ff22dbf0931459858575896c967'
>>> web3.eth.getTransaction(tx_hash)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/venv/lib/python3.6/site-packages/web3/eth.py", line 160, in getTransaction
    [transaction_hash],
  File "/venv/lib/python3.6/site-packages/web3/manager.py", line 112, in request_blocking
    response = self._make_request(method, params)
  File "/venv/lib/python3.6/site-packages/web3/manager.py", line 95, in _make_request
    return make_request_fn(method, params)
  File "/venv/lib/python3.6/site-packages/web3/middleware/attrdict.py", line 20, in middleware
    response = make_request(method, params)
  File "/venv/lib/python3.6/site-packages/web3/middleware/formatting.py", line 32, in middleware
    formatter(response['result']),
  File "cytoolz/functoolz.pyx", line 236, in cytoolz.functoolz.curry.__call__ (cytoolz/functoolz.c:4144)
  File "cytoolz/functoolz.pyx", line 232, in cytoolz.functoolz.curry.__call__ (cytoolz/functoolz.c:3996)
  File "/venv/lib/python3.6/site-packages/web3/utils/formatters.py", line 45, in apply_formatter_if
    return formatter(value)
  File "cytoolz/functoolz.pyx", line 236, in cytoolz.functoolz.curry.__call__ (cytoolz/functoolz.c:4144)
  File "cytoolz/functoolz.pyx", line 232, in cytoolz.functoolz.curry.__call__ (cytoolz/functoolz.c:3996)
  File "/venv/lib/python3.6/site-packages/eth_utils/functional.py", line 33, in inner
    return callback(fn(*args, **kwargs))
  File "/venv/lib/python3.6/site-packages/web3/utils/formatters.py", line 55, in apply_formatters_to_dict
    yield key, formatters[key](item)
  File "cytoolz/functoolz.pyx", line 236, in cytoolz.functoolz.curry.__call__ (cytoolz/functoolz.c:4144)
  File "cytoolz/functoolz.pyx", line 232, in cytoolz.functoolz.curry.__call__ (cytoolz/functoolz.c:3996)
  File "/venv/lib/python3.6/site-packages/web3/utils/formatters.py", line 44, in apply_formatter_if
    if condition(value):
  File "cytoolz/functoolz.pyx", line 608, in cytoolz.functoolz.complement.__call__ (cytoolz/functoolz.c:10825)
  File "/venv/lib/python3.6/site-packages/web3/middleware/pythonic.py", line 64, in is_create_address
    return decode_hex(value) == b""
  File "/venv/lib/python3.6/site-packages/eth_utils/string.py", line 78, in inner
    return force_obj_to_bytes(fn(*args, **kwargs))
  File "/venv/lib/python3.6/site-packages/eth_utils/hexidecimal.py", line 22, in decode_hex
    raise TypeError('Value must be an instance of str or unicode')
TypeError: Value must be an instance of str or unicode
```

In Geth console:

```js
> eth.getTransaction('0xe5854bda43d2dff2c4c809e044ad5ac9c1f34ff22dbf0931459858575896c967')
{
  blockHash: "0xea841221179e37ca9cc23424b64201d8805df327c3296a513e9f1fe6faa5ffb3",
  blockNumber: 3911,
  from: "0xbedcf417ff2752d996d2ade98b97a6f0bef4beb9",
  gas: 4700000,
  gasPrice: 20000000000,
  hash: "0xe5854bda43d2dff2c4c809e044ad5ac9c1f34ff22dbf0931459858575896c967",
  input: "0x6060604052341561000c57fe5b5b6101828061001c6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063230925601461003b575bfe5b341561004357fe5b61008360048080356000191690602001909190803560ff1690602001909190803560001916906020019091908035600019169060200190919050506100c5565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000600185858585604051806000526020016040526000604051602001526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000866161da5a03f1151561014257fe5b50506020604051035190505b9493505050505600a165627a7a7230582054abc8e7b2d8ea0972823aa9f0df23ecb80ca0b58be9f31b7348d411aaf585be0029",
  nonce: 0,
  r: "0xe675b8854c227bb7bd0c5db4b64b1846447a972e1f87e7420f8644dacc3f36ec",
  s: "0x69c5abc563cb59f6b770dbd52a72f0ce24640067b66cbcecd9ae161a031c1ec8",
  to: null,
  transactionIndex: 0,
  v: "0x2b",
  value: 0
}
```



Discussion:
The key feature of this transaction appears to be that it's a contract creation (ie~ `to` is null). I am looking for a test case for that right now, and will add one if I don't find it.

Resolving patch:
diff --git a/web3/middleware/pythonic.py b/web3/middleware/pythonic.py
--- a/web3/middleware/pythonic.py
+++ b/web3/middleware/pythonic.py
@@ -12,6 +12,7 @@
 )
 
 from eth_utils import (
+    is_address,
     to_normalized_address,
     is_integer,
     is_null,
@@ -19,7 +20,6 @@
     is_string,
     is_bytes,
     is_list_like,
-    decode_hex,
 )
 
 from web3.utils.formatters import (
@@ -60,13 +60,6 @@ def bytes_to_ascii(value):
 transaction_params_formatter = apply_formatters_to_dict(TRANSACTION_PARAMS_FORMATTERS)
 
 
-def is_create_address(value):
-    return decode_hex(value) == b""
-
-
-is_not_create_address = complement(is_create_address)
-
-
 def is_array_of_strings(value):
     if not is_list_like(value):
         return False
@@ -87,7 +80,7 @@ def is_array_of_dicts(value):
     'gasPrice': to_integer_if_hex,
     'value': to_integer_if_hex,
     'from': to_normalized_address,
-    'to': apply_formatter_if(to_normalized_address, is_not_create_address),
+    'to': apply_formatter_if(to_normalized_address, is_address),
     'hash': to_ascii_if_bytes,
 }
 
@@ -208,6 +201,7 @@ def is_array_of_strings(value):
     (apply_formatter_to_array(to_ascii_if_bytes), is_array_of_strings),
 ))
 
+
 pythonic_middleware = construct_formatting_middleware(
     request_formatters={
         # Eth
diff --git a/web3/providers/tester.py b/web3/providers/tester.py
--- a/web3/providers/tester.py
+++ b/web3/providers/tester.py
@@ -1,6 +1,12 @@
+from cytoolz.functoolz import (
+    compose,
+    complement,
+)
+
 from eth_utils import (
     is_integer,
     is_string,
+    decode_hex,
 )
 
 from web3.middleware import (
@@ -15,6 +21,7 @@
     hex_to_integer,
     apply_formatter_if,
     apply_formatter_at_index,
+    apply_formatters_to_dict,
 )
 
 from .base import BaseProvider  # noqa: E402
@@ -29,9 +36,29 @@ def is_testrpc_available():
         return False
 
 
+def static_return(value):
+    def inner(*args, **kwargs):
+        return value
+    return inner
+
+
+def static_result(value):
+    def inner(*args, **kwargs):
+        return {'result': value}
+    return inner
+
+
 to_integer_if_hex = apply_formatter_if(hex_to_integer, is_string)
 
 
+TRANSACTION_FORMATTERS = {
+    'to': apply_formatter_if(
+        static_return(None),
+        compose(complement(bool), decode_hex),
+    ),
+}
+
+
 ethtestrpc_middleware = construct_formatting_middleware(
     request_formatters={
         'eth_uninstallFilter': apply_formatter_at_index(to_integer_if_hex, 0),
@@ -42,6 +69,7 @@ def is_testrpc_available():
         # Eth
         'eth_newFilter': apply_formatter_if(hex, is_integer),
         'eth_protocolVersion': apply_formatter_if(str, is_integer),
+        'eth_getTransactionByHash': apply_formatters_to_dict(TRANSACTION_FORMATTERS),
         # Net
         'net_version': apply_formatter_if(str, is_integer),
     },


Test patch:
diff --git a/web3/utils/module_testing/eth_module.py b/web3/utils/module_testing/eth_module.py
--- a/web3/utils/module_testing/eth_module.py
+++ b/web3/utils/module_testing/eth_module.py
@@ -214,6 +214,12 @@ def test_eth_getTransactionByHash(self, web3, mined_txn_hash):
         transaction = web3.eth.getTransaction(mined_txn_hash)
         assert transaction['hash'] == mined_txn_hash
 
+    def test_eth_getTransactionByHash_contract_creation(self,
+                                                        web3,
+                                                        math_contract_deploy_txn_hash):
+        transaction = web3.eth.getTransaction(math_contract_deploy_txn_hash)
+        assert transaction['to'] is None
+
     def test_eth_getTransactionByBlockHashAndIndex(self, web3, block_with_txn, mined_txn_hash):
         transaction = web3.eth.getTransactionFromBlock(block_with_txn['hash'], 0)
         assert transaction['hash'] == mined_txn_hash