Steem-Python 1.0.1 Does Not Properly Handle Steemd Websocket Server Errors

Project Information

This bug is related to the Github repository:
https://github.com/steemit/steem-python

I'm currently using the Steem Python Package version number 1.0.1, but I have reproduced these errors on 1.0.0 as well. I'm using Python version 3.5.2

Expected behavior

I expect that when instantiating an instance of the Steem class using Steem-Python that if an address to a steemd websocket server (RPC node) generates a server error (such as 503 Bad Gateway, or 404 Not Found, etc.) that:

  1. A more informative error is produced by the Steem class. Rather than simply giving the error "TypeError: 'NoneType' object is not subscriptable" an exception that informs of the server error would be more helpful.

  2. If an address to a websocket server produces a server error and is present in the list of nodes given to Steem during instantiation, steem should ignore the bad address and continue attempting connections using the next address in the list.

According to the documentation at http://steem.readthedocs.io/en/latest/steem.html

If you would like to override the official Steemit nodes (default), you can pass your own. When currently used node goes offline, Steemd will automatically fail-over to the next available node. ~Italics mine

nodes = [
    'https://steemd.yournode1.com',
    'https://steemd.yournode2.com',
]

s = Steemd(nodes)

Actual Behavior

When instantiating an instance of Steem using an address to a websocket server that is currently generating server errors Steem produces unpredictable behavior and exceptions that cannot be handled in a meaningful manner.

I can reproduce these errors on all three of my Ubuntu 16.04 servers:

When running the following script:

#!/usr/bin/python3

from steem import Steem

nodelist = ['https://steemd.minnowsupportproject.org', 
    'https://steemd.privex.io', 
    'https://gtg.steem.house:8090', 
    'rpc.steemliberator.com', 
    'steemd.pevo.science', 
    'steemd.steemgigs.org']

s = Steem(nodes=nodelist)

acct = s.get_account("ned")

print (acct['sbd_balance'])

The following error is encountered:

  File "test_connection.py", line 7, in <module>
    s = Steem(nodes)
  File "/home/ger/.local/lib/python3.5/site-packages/steem/steem.py", line 60, in __init__
    steemd_instance=self.steemd, no_broadcast=no_broadcast, **kwargs)
  File "/home/ger/.local/lib/python3.5/site-packages/steem/commit.py", line 96, in __init__
    self.wallet = Wallet(self.steemd, **kwargs)
  File "/home/ger/.local/lib/python3.5/site-packages/steem/wallet.py", line 61, in __init__
    self.prefix = self.steemd.chain_params["prefix"]
  File "/home/ger/.local/lib/python3.5/site-packages/steem/steemd.py", line 70, in chain_params
    chain = props["current_supply"].split(" ")[1]
TypeError: 'NoneType' object is not subscriptable

Depending on the type of server error the exception may be a never ending loop of exceptions. Below is a small snippet of a never ending loop.

Traceback (most recent call last):
  File "/home/ger/.local/lib/python3.5/site-packages/steembase/http_client.py", line 202, in call
    response = self.request(body=body)
  File "/usr/lib/python3/dist-packages/urllib3/poolmanager.py", line 162, in urlopen
    response = conn.urlopen(method, u.request_uri, **kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 630, in urlopen
    release_conn=release_conn, **response_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 610, in urlopen
    _stacktrace=sys.exc_info()[2])
  File "/usr/lib/python3/dist-packages/urllib3/util/retry.py", line 273, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='rpc.steemliberator.com', port=80): Max retries exceeded with url: / (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0xb6f1daec>: Failed to establish a new connection: [Errno 111] Connection refused',))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 137, in _new_conn
    (self.host, self.port), self.timeout, **extra_kw)
  File "/usr/lib/python3/dist-packages/urllib3/util/connection.py", line 91, in create_connection
    raise err
  File "/usr/lib/python3/dist-packages/urllib3/util/connection.py", line 81, in create_connection
    sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 560, in urlopen
    body=body, headers=headers)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 354, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/usr/lib/python3.5/http/client.py", line 1106, in request
    self._send_request(method, url, body, headers)
  File "/usr/lib/python3.5/http/client.py", line 1151, in _send_request
    self.endheaders(body)
  File "/usr/lib/python3.5/http/client.py", line 1102, in endheaders
    self._send_output(message_body)
  File "/usr/lib/python3.5/http/client.py", line 934, in _send_output
    self.send(msg)
  File "/usr/lib/python3.5/http/client.py", line 877, in send
    self.connect()
  File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 162, in connect
    conn = self._new_conn()
  File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 146, in _new_conn
    self, "Failed to establish a new connection: %s" % e)
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPConnection object at 0xb6f4046c>: Failed to establish a new connection: [Errno 111] Connection refused

Coincidentally, if I intentionally enter a spelling mistake into a node address such as "https://steemd.p_rivex.io" it creates the exact same error as above.

This is unexpected behavior.

Even if "try" and "except" statements are used to attempt to catch this error, instead of showing a never ending loop the system simply hangs forever.

The issue can be reproduced by running the script that connects to a steemd websocket server that generates an error. For example, today if I visit "steemd.minnowsupportproject.org" in a browser I get a 502 bad Gateway Error. Running this script with just "steemd.minnowsupportproject.org" as a node:

#!/usr/bin/python3

from steem import Steem

nodelist = ['https://steemd.minnowsupportproject.org']

s = Steem(nodes=nodelist)

acct = s.get_account("ned")

print (acct['sbd_balance'])
produces this error:
Traceback (most recent call last):
  File "test_connection.py", line 7, in <module>
    s = Steem(nodelist)
  File "/home/ger/.local/lib/python3.5/site-packages/steem/steem.py", line 60, in __init__
    steemd_instance=self.steemd, no_broadcast=no_broadcast, **kwargs)
  File "/home/ger/.local/lib/python3.5/site-packages/steem/commit.py", line 96, in __init__
    self.wallet = Wallet(self.steemd, **kwargs)
  File "/home/ger/.local/lib/python3.5/site-packages/steem/wallet.py", line 61, in __init__
    self.prefix = self.steemd.chain_params["prefix"]
  File "/home/ger/.local/lib/python3.5/site-packages/steem/steemd.py", line 70, in chain_params
    chain = props["current_supply"].split(" ")[1]
TypeError: 'NoneType' object is not subscriptable
However if I run the same script using a known good websocket server such as "https://steemd.pevo.science" then the script runs as expected.

Visiting this node in a browser displays:

11 eof_exception: End Of File
stringstream
    {}
    th_a  sstream.cpp:109 peek

    {"str":""}
    th_a  json.cpp:479 from_string
And the output of the script is as expected:
5753.498 SBD

To make matters worse, it seems that if a node server that generates an error is in the node list handed to an instantiation of steem, then an error is generated, regardless if there are working nodes in the list.

For example, if I run the script:

#!/usr/bin/python3

from steem import Steem

nodelist = ['https://steemd.minnowsupportproject.org', 'https://steemd.pevo.science']

s = Steem(nodes=nodelist)

acct = s.get_account("ned")

print (acct['sbd_balance'])

It generates the same errors as described above, seemingly getting stuck on the "bad" server and never attempting the next server in the node list.

Temporary Work-around

To fix this issue temporarily I am using urllib to test each server node address before using it to instantiate the Steem class. The following code works even thought the first node gives a 503 Gateway error:

#!/usr/bin/python3

import urllib.request
from urllib.error import HTTPError
from steem import Steem

nodelist = ['https://steemd.minnowsupportproject.org','https://steemd.pevo.science']

def goodnode(nodelist):
    for n in nodelist:
        req = urllib.request.Request(url=n)
        try:
            handler = urllib.request.urlopen(req)
        except HTTPError as e:
            pass
        else:
            return n

s = Steem(nodes=[goodnode(nodelist)])

acct = s.get_account("ned")

print (acct['sbd_balance'])

No recording of this bug or behavior

I made no recording since the previous description and code boxes should be enough information for a developer to reproduce this behavior.

Possible related issues already on GitHub

I did see two issues on GitHub that could possibly be related, but because the issues reported could not be reproduced and lacked sufficient detail it's uncertain.

https://github.com/steemit/steem-python/issues/224

https://github.com/steemit/steem-python/issues/236

My GitHub Account

https://github.com/artolabs

artolabs_bug_report.jpg


Visit: https://mike.artopium.com

Contact: https://discord.gg/97GKVFC

Sort:  
Loading...

Hey @learnelectronics
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Contributing on Utopian
Learn how to contribute on our website or by watching this tutorial on Youtube.

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Coin Marketplace

STEEM 0.21
TRX 0.20
JST 0.034
BTC 99331.95
ETH 3298.98
USDT 1.00
SBD 3.05