Search code examples
pythonnetwork-programminglast.fmpylast

Handling network errors from an external API across an application


My app depends heavily on an external API (last.fm), for which it uses a python wrapper, pylast.

The network is prone to some instability, however, and when network connection fails, all breaks down.

Such network configuration goes at project/config.py:

class DevelopmentConfig(BaseConfig):    
    # LastFm credentials
    LASTFM_API_KEY = "mykey"
    LASTFM_API_SECRET = "mysecret"
    LASTFM_USERNAME = "myusername"
    LASTFM_PWD_HASH = "fEl@O5R$#^GFIbij1"

and is initialized at tags.py like so:

from project.config import DevelopmentConfig

last = pylast.LastFMNetwork(
    api_key = DevelopmentConfig.LASTFM_API_KEY, 
    api_secret =DevelopmentConfig.LASTFM_API_SECRET, 
    username = DevelopmentConfig.LASTFM_USERNAME, 
    password_hash = pylast.md5(DevelopmentConfig.LASTFM_PWD_HASH))

How do I protect my app and handle errors like the one below, so the app does not break if network connection fails?

Full log traceback:

Traceback (most recent call last):
   File "/usr/lib/python3.6/site-packages/pylast/__init__.py", line 843, in _download_response
     method='POST', url=HOST_SUBDIR, body=data, headers=headers)
   File "/usr/lib/python3.6/http/client.py", line 1239, in request
     self._send_request(method, url, body, headers, encode_chunked)
   File "/usr/lib/python3.6/http/client.py", line 1285, in _send_request
     self.endheaders(body, encode_chunked=encode_chunked)
   File "/usr/lib/python3.6/http/client.py", line 1234, in endheaders
     self._send_output(message_body, encode_chunked=encode_chunked)
   File "/usr/lib/python3.6/http/client.py", line 1026, in _send_output
     self.send(msg)
   File "/usr/lib/python3.6/http/client.py", line 964, in send
     self.connect()
   File "/usr/lib/python3.6/http/client.py", line 1392, in connect
     super().connect()
   File "/usr/lib/python3.6/http/client.py", line 936, in connect
     (self.host,self.port), self.timeout, self.source_address)
   File "/usr/lib/python3.6/socket.py", line 704, in create_connection
     for res in getaddrinfo(host, port, 0, SOCK_STREAM):
   File "/usr/lib/python3.6/socket.py", line 745, in getaddrinfo
     for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
 socket.gaierror: [Errno -2] Name does not resolve

 During handling of the above exception, another exception occurred:

 Traceback (most recent call last):
   File "/usr/src/app/manage.py", line 28, in <module>
     app = create_app()  
   File "/usr/src/app/project/__init__.py", line 27, in create_app
     from .api import routes, models
   File "/usr/src/app/project/api/routes/__init__.py", line 2, in <module>
     from .register import register_bp
   File "/usr/src/app/project/api/routes/register.py", line 7, in <module>
     from project.api.classifiers.rec_pipeline.charts import project
   File "/usr/src/app/project/api/classifiers/rec_pipeline/charts.py", line 2, in <module>
     from .collab import collab_filter
   File "/usr/src/app/project/api/classifiers/rec_pipeline/collab.py", line 6, in <module>
     from project.api.models.methods import Collaborative_Filtering
   File "/usr/src/app/project/api/models/methods.py", line 14, in <module>
     from project.api.resources.genius import lyrics
   File "/usr/src/app/project/api/resources/genius/lyrics.py", line 21, in <module>
     from project.api.resources.lastfm.seeds.tags import *
   File "/usr/src/app/project/api/resources/lastfm/seeds/tags.py", line 15, in <module>
     password_hash = pylast.md5(DevelopmentConfig.LASTFM_PWD_HASH))
   File "/usr/lib/python3.6/site-packages/pylast/__init__.py", line 636, in __init__
     "user": "user/%(name)s",
   File "/usr/lib/python3.6/site-packages/pylast/__init__.py", line 177, in __init__
     self.username, self.password_hash)
   File "/usr/lib/python3.6/site-packages/pylast/__init__.py", line 982, in get_session_key
     doc = request.execute()
   File "/usr/lib/python3.6/site-packages/pylast/__init__.py", line 862, in execute
     response = self._download_response()
   File "/usr/lib/python3.6/site-packages/pylast/__init__.py", line 845, in _download_response
     raise NetworkError(self.network, e)
 pylast.NetworkError: NetworkError: [Errno -2] Name does not resolve

EDIT:

based on the comments below, so far I'm trying:

try:
    last = pylast.LastFMNetwork(
        api_key = DevelopmentConfig.LASTFM_API_KEY, 
        api_secret =DevelopmentConfig.LASTFM_API_SECRET, 
        username = DevelopmentConfig.LASTFM_USERNAME, 
        password_hash = pylast.md5(DevelopmentConfig.LASTFM_PWD_HASH))
except socket.gaierror as e:
    print (e)

but I don't know if this is the more stable or reliable solution.


Solution

  • You can do something like this:

    attempts = 3
    while attempts > 0:
        try:
            attempts = attempts - 1
            do_network_stuff
        except exception as e:  # those you wish to handle
            print(e)  # or more graceful handling