TypeError: требуется Future или сопрограмма

Я пытаюсь сделать автопереподключение клиента ssh на asyncssh. (SshConnectManager должен оставаться в фоновом режиме и при необходимости проводить сеансы ssh)

class SshConnectManager(object):
def __init__(self, host, username, password, port=22):
    self._host = host
    self._username = username
    self._password = password
    self._port = port

    self.conn = None
    asyncio.async(self.start_connection)

@asyncio.coroutine
def start_connection(self):
    try:
        Client = self._create_ssh_client()
        self.conn, _ = yield from asyncssh.create_connection(Client,
                                                        self._host, port=self._port,
                                                        username=self._username,
                                                        password=self._password)
    except Exception as e:
        print("Connection error! {}".format(e))
        asyncio.async(self.start_connection())

def _create_ssh_client(self):
    class MySSHClient(asyncssh.SSHClient):
        parent = self
        def connection_lost(self, exc):
            self.parent._handle_connection_lost(exc)
    return MySSHClient

def _handle_connection_lost(self, exc):
    print('Connection lost on {}'.format(self.host))
    print(exc)
    asyncio.async(self.start_connection)


ssh1 = SshConnectManager(settings.host, settings.username, settings.password, settings.port)

asyncio.get_event_loop().run_until_complete(...)

Пожалуйста, не смотрите на _create_ssh_client или другие "хаки"

Проблема в следующем:

$ python3 main.py 
Traceback (most recent call last):
  File "main.py", line 75, in <module>
    ssh1 = SshConnectManager(settings.host, settings.username, settings.password, settings.port)
  File "main.py", line 22, in __init__
    asyncio.async(self.start_connection)
  File "/usr/lib/python3.4/asyncio/tasks.py", line 565, in async
    raise TypeError('A Future or coroutine is required')
TypeError: A Future or coroutine is required

Но self.start_connection - это корутин! Или нет? Или как еще можно запустить асинхронную задачу из кода синхронизации?


person kolko    schedule 26.05.2015    source источник
comment
Вы смотрели строку 565 из /usr/lib/python3.4/asyncio/tasks.py?   -  person boardrider    schedule 26.05.2015
comment
да. Но почему start_connection не является корутином?   -  person kolko    schedule 26.05.2015
comment
Ой. Нужно позвонить, чтобы получить корутин ...   -  person kolko    schedule 26.05.2015
comment
@kolko Похоже, вы догадались, что вам нужно сделать asyncio.async(self.start_connection()) (отсутствовали конечные скобки). Вы также сделали это правильно в другом месте кода, так что я предполагаю, что это была просто опечатка?   -  person dano    schedule 26.05.2015
comment
Это не была опечатка :) Я был виноват. И забудьте попробовать добавить скобки во время отладки.   -  person kolko    schedule 27.05.2015
comment
@kolko В этом случае вы можете добавить решение в качестве ответа и принять его :)   -  person dano    schedule 27.05.2015


Ответы (1)


Спасибо @dano и @boardrider за помощь в комментариях. Ошибка заключалась в том, что функция возврата @ asyncio.coroutine, которую нужно вызвать, чтобы получить объект генератора. Я забываю это сделать.

Фиксированная версия:

class SshConnectManager(object):
def __init__(self, host, username, password, port=22):
    self._host = host
    self._username = username
    self._password = password
    self._port = port

    self.conn = None
    # FIX HERE
    asyncio.async(self.start_connection())

@asyncio.coroutine
def start_connection(self):
    try:
        Client = self._create_ssh_client()
        self.conn, _ = yield from asyncssh.create_connection(Client,
                                                        self._host, port=self._port,
                                                        username=self._username,
                                                        password=self._password)
    except Exception as e:
        print("Connection error! {}".format(e))
        asyncio.async(self.start_connection())

def _create_ssh_client(self):
    class MySSHClient(asyncssh.SSHClient):
        parent = self
        def connection_lost(self, exc):
            self.parent._handle_connection_lost(exc)
    return MySSHClient

def _handle_connection_lost(self, exc):
    print('Connection lost on {}'.format(self.host))
    print(exc)
    # AND HERE
    asyncio.async(self.start_connection())


ssh1 = SshConnectManager(settings.host, settings.username, settings.password, settings.port)

asyncio.get_event_loop().run_until_complete(...)

P.S. Но я не понимаю, почему декоратор сопрограмм не может вернуть декоратор с именем. (Это сбивает меня с толку, я путаю это со скрученными обратными вызовами).

И я нашел, как это запомнить, простой случай, если start_connection может получать аргументы:

@asyncio.coroutine
def start_connection(self, some_arg):
    pass

Итак, я могу просто написать:

asyncio.async(self.start_connection(some_val))

и не нужно делать дополнительные атрибуты в функции asyncio.async

person kolko    schedule 28.05.2015