1

I use asyncio for connecting with other peers in a bittorrent client im working on. When some peer cant be connected, the program crashes with ConnectionRefusedError and TimeoutError exceptions. Is it normal that some peers cant be connected or theres something wrong with my code. If that's normal, how should i handle the exceptions? I've tried to put try except around loop.create_connection() but that didnt do anything.

Here's my code:

class Torrent():
    def __init__(self, torrent_file, loop):
        self.torrent = Torrent(torrent_file)
        self.peers = self.get_peers()
        self.loop = loop

    ...

    def connect_to_peers(self):
        tasks = []
        for peer in self.peers:
            try:
                # returns a coroutine
                connection = self.loop.create_connection(PeerProtocol, peer['host'], peer['port'])
                tasks.append(asyncio.Task(connection))
            except ConnectionRefusedError:
                print('caught')
            except TimeoutError:
                print('timeout error')

        return tasks


def main():
    loop = asyncio.get_event_loop()

    filename = 'street-fighter.torrent'
    client = TorrentClient(filename, loop)
    tasks = client.connect_to_peers()

    try:
        loop.run_until_complete(asyncio.wait(tasks))
    except KeyboardInterrupt:
        pass

class PeerProtocol(asyncio.Protocol):

    def connection_made(self, transport):
        host, port = transport.get_extra_info('peername')
        print('connected with {}:{}'.format(host, port))

    def connection_lost(self, exc):
        print('disconnected...')
        print('exc: {}'.format(exc))

Here's the output:

connected with 80.94.76.7:14122
connected with 174.110.236.233:45308
connected with 78.177.119.170:27311
connected with 95.15.59.242:21426
disconnected...
exc: [Errno 54] Connection reset by peer
disconnected...
exc: [Errno 54] Connection reset by peer
disconnected...
exc: None
disconnected...
exc: None
Task exception was never retrieved
future: <Task finished coro=<BaseEventLoop.create_connection() done, defined at /Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py:548> exception=TimeoutError(60, "Connect call failed ('93.34.49.17', 13311)")>
Traceback (most recent call last):
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 645, in create_connection
    raise exceptions[0]
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 632, in create_connection
    yield from self.sock_connect(sock, address)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 358, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 290, in _wakeup
    future.result()
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 436, in _sock_connect_cb
    raise OSError(err, 'Connect call failed %s' % (address,))
TimeoutError: [Errno 60] Connect call failed ('93.34.49.17', 13311)
Task exception was never retrieved
future: <Task finished coro=<BaseEventLoop.create_connection() done, defined at /Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py:548> exception=TimeoutError(60, "Connect call failed ('197.29.6.31', 50735)")>
Traceback (most recent call last):
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 645, in create_connection
    raise exceptions[0]
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 632, in create_connection
    yield from self.sock_connect(sock, address)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 358, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 290, in _wakeup
    future.result()
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 436, in _sock_connect_cb
    raise OSError(err, 'Connect call failed %s' % (address,))
TimeoutError: [Errno 60] Connect call failed ('197.29.6.31', 50735)
Task exception was never retrieved
future: <Task finished coro=<BaseEventLoop.create_connection() done, defined at /Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py:548> exception=TimeoutError(60, "Connect call failed ('195.174.165.47', 61567)")>
Traceback (most recent call last):
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 645, in create_connection
    raise exceptions[0]
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 632, in create_connection
    yield from self.sock_connect(sock, address)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 358, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 290, in _wakeup
    future.result()
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 436, in _sock_connect_cb
    raise OSError(err, 'Connect call failed %s' % (address,))
TimeoutError: [Errno 60] Connect call failed ('195.174.165.47', 61567)
Task exception was never retrieved
future: <Task finished coro=<BaseEventLoop.create_connection() done, defined at /Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py:548> exception=ConnectionRefusedError(61, "Connect call failed ('69.122.194.81', 6881)")>
Traceback (most recent call last):
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 645, in create_connection
    raise exceptions[0]
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 632, in create_connection
    yield from self.sock_connect(sock, address)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 358, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 290, in _wakeup
    future.result()
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 436, in _sock_connect_cb
    raise OSError(err, 'Connect call failed %s' % (address,))
ConnectionRefusedError: [Errno 61] Connect call failed ('69.122.194.81', 6881)
Task exception was never retrieved
future: <Task finished coro=<BaseEventLoop.create_connection() done, defined at /Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py:548> exception=TimeoutError(60, "Connect call failed ('41.210.123.12', 48319)")>
Traceback (most recent call last):
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 645, in create_connection
    raise exceptions[0]
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 632, in create_connection
    yield from self.sock_connect(sock, address)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 358, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 290, in _wakeup
    future.result()
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 436, in _sock_connect_cb
    raise OSError(err, 'Connect call failed %s' % (address,))
TimeoutError: [Errno 60] Connect call failed ('41.210.123.12', 48319)
Task exception was never retrieved
future: <Task finished coro=<BaseEventLoop.create_connection() done, defined at /Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py:548> exception=TimeoutError(60, "Connect call failed ('78.174.159.195', 35414)")>
Traceback (most recent call last):
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 645, in create_connection
    raise exceptions[0]
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 632, in create_connection
    yield from self.sock_connect(sock, address)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 358, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 290, in _wakeup
    future.result()
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 436, in _sock_connect_cb
    raise OSError(err, 'Connect call failed %s' % (address,))
TimeoutError: [Errno 60] Connect call failed ('78.174.159.195', 35414)
Task exception was never retrieved
future: <Task finished coro=<BaseEventLoop.create_connection() done, defined at /Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py:548> exception=TimeoutError(60, "Connect call failed ('85.103.126.106', 22665)")>
Traceback (most recent call last):
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 645, in create_connection
    raise exceptions[0]
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 632, in create_connection
    yield from self.sock_connect(sock, address)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 358, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 290, in _wakeup
    future.result()
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 436, in _sock_connect_cb
    raise OSError(err, 'Connect call failed %s' % (address,))
TimeoutError: [Errno 60] Connect call failed ('85.103.126.106', 22665)
Task exception was never retrieved
future: <Task finished coro=<BaseEventLoop.create_connection() done, defined at /Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py:548> exception=TimeoutError(60, "Connect call failed ('81.228.224.142', 13570)")>
Traceback (most recent call last):
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 645, in create_connection
    raise exceptions[0]
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 632, in create_connection
    yield from self.sock_connect(sock, address)
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 358, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 290, in _wakeup
    future.result()
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/Users/shangsunset/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 436, in _sock_connect_cb
    raise OSError(err, 'Connect call failed %s' % (address,))
TimeoutError: [Errno 60] Connect call failed ('81.228.224.142', 13570)

I'm very new to asyncio and not sure if i'm doing it the right way.

Thanks.

shangsunset
  • 1,585
  • 4
  • 22
  • 38

1 Answers1

3
  1. asyncio.Task just creates task and return immediately. You should await for created task to get it's result (including case when it raises exception). See this answer for details.
  2. I would advice you to use asyncio.ensure_future instead of asyncio.Task
  3. If you want to execute some coroutines parallel asyncio.gather is common way to do it. Tasks usually need when you want to start some coroutine "in background".

Based on all of it code may look some way like this (I didn't test it):

class Torrent():

    # ...

    async def connect_to_peer(self, peer):
        try:
            # await, here exception would be raised
            await self.loop.create_connection(
                PeerProtocol, 
                peer['host'], 
                peer['port']
            )
        except ConnectionRefusedError:
            print('caught')
        except TimeoutError:
            print('timeout error')


    async def connect_to_peers(self):  # async function
        await asyncio.gather(
            *[self.connect_to_peer(peer) for peer in self.peers], 
            loop=self.loop  # fixed here!
        )
        # Btw, you can add param return_exceptions=True to get exceptions
        # in results here instead of ignoring it inside connect_to_peer

# ...

loop.run_until_complete(client.connect_to_peers())
Community
  • 1
  • 1
Mikhail Gerasimov
  • 36,989
  • 16
  • 116
  • 159
  • Hi, thanks so much for your answer. I tried the code you provided, but got `TypeError: A Future, a coroutine or an awaitable is required `. from `loop.run_until_complete(...)`. I read that `asyncio.gather()` returns a future, but why is `client.connect_to_peers()` not a `future` here? – shangsunset May 19 '16 at 22:16
  • @shangyeshen did you define `connect_to_peers` with `async def`? `client.connect_to_peers()` is not a Future, but a `coroutine` and should be valid argument for `run_until_complete`. – Mikhail Gerasimov May 19 '16 at 23:53
  • yea just like what you had in the answer. theres one more error message: `sys:1: RuntimeWarning: coroutine 'TorrentClient.connect_to_peer' was never awaited ` – shangsunset May 20 '16 at 00:58
  • @shangyeshen my fail! `self.loop` should be passed in `asyncio.gather` as named argument. Now should work. – Mikhail Gerasimov May 20 '16 at 07:22