Почему Paramiko зависает, если использовать его при загрузке модуля?

StackOverflow https://stackoverflow.com/questions/443387

Вопрос

Поместите следующее в файл привет.pyeasy_install paramiko если у вас его нет):

hostname,username,password='fill','these','in'
import paramiko
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(hostname=hostname, username=username, password=password)
i,o,e = c.exec_command('ls /')
print(o.read())
c.close()

Заполните первую строку правильно.

Теперь введите

python hello.py

и вы увидите вывод ls.

Теперь вместо этого введите

python

а затем изнутри типа интерпретатора

import hello

и вуаля!Оно висит!Он исчезнет, ​​если вы обернете код в функцию. foo и делай import hello; hello.foo() вместо.

Почему Paramiko зависает при использовании при инициализации модуля? Откуда Парамико вообще знает, что он вообще используется во время инициализации модуля?

Это было полезно?

Решение

Парамико использует отдельные потоки для базового транспорта.Вам следует никогда есть модуль, который порождает поток как побочный эффект импорта.Насколько я понимаю, доступна одна блокировка импорта, поэтому, когда дочерний поток вашего модуля пытается выполнить еще один импорт, он может заблокироваться на неопределенный срок, поскольку ваш основной поток все еще удерживает блокировку.(Наверное, есть и другие ошибки, о которых я тоже не знаю)

В общем, модули не должны иметь каких-либо побочных эффектов при импорте, иначе вы получите непредсказуемые результаты.Просто отложите выполнение с помощью __name__ == '__main__' трюк, и все будет в порядке.

Редактировать] Я не могу создать простой тестовый пример, который воспроизводит этот тупик.Я все еще предполагаю, что это проблема с потоками при импорте, потому что код аутентификации ожидает события, которое никогда не срабатывает.Это может быть ошибка в paramiko или python, но хорошая новость в том, что вы никогда не увидите ее, если все сделаете правильно;)

Это хороший пример того, почему всегда хочется минимизировать побочные эффекты и почему методы функционального программирования становятся все более распространенными.

Другие советы

Как ДжимБ отметил, что это проблема с импортом когда Python пытается неявно импортировать str.decode('utf-8') декодер при первом использовании во время попытки подключения по SSH.Видеть Анализ раздел для подробностей.

В общем, нельзя не подчеркнуть, что вам следует избегать автоматического создания новых потоков при импорте модуля.Если можете, постарайтесь вообще избегать кода магического модуля, поскольку он почти всегда приводит к нежелательным побочным эффектам.

  1. Простое и разумное решение вашей проблемы, как уже упоминалось, — это поместить ваш код в if __name__ == '__main__': тело, которое будет выполнено только в том случае, если вы запустите этот конкретный модуль, и не будет выполняться, когда этот модуль импортируется другими модулями.

  2. (не рекомендуется). Еще одно решение — просто добавить в код фиктивный str.decode('utf-8') перед вызовом. SSHClient.connect() - см. анализ ниже.

Так в чем же основная причина этой проблемы?

Анализ (простая аутентификация по паролю)

Намекать:Если вы хотите отлаживать потоки при импорте Python и устанавливать threading._VERBOSE = True

  1. paramiko.SSHClient().connect(.., look_for_keys=False, ..) неявно порождает новый поток для вашего соединения.Вы также можете увидеть это, если включите вывод отладки для paramiko.transport.

[Thread-5 ] [paramiko.transport ] DEBUG : starting thread (client mode): 0x317f1d0L

  1. в основном это делается как часть SSHClient.connect().Когда client.py:324::start_client() вызывается, создается блокировка transport.py:399::event=threading.Event() и тред запускается transport.py:400::self.start().Обратите внимание, что start() затем метод выполнит класс transport.py:1565::run() метод.

  2. transport.py:1580::self._log(..) печатает наше сообщение журнала «Начальный поток», а затем переходит к transport.py:1584::self._check_banner().

  3. check_banner делает одну вещь.Он получает баннер ssh (первый ответ от сервера) transport.py:1707::self.packetizer.readline(timeout) (Обратите внимание, что тайм -аут - это просто тайм -аут для чтения в розетке), проверяет на линейную корзину в конце и в противном случае.

  4. В случае получения баннера сервера он пытается декодировать строку ответа в формате utf-8. packet.py:287::return u(buf) и вот тут-то и происходит тупик.А u(s, encoding='utf-8') выполняет str.decode('utf-i') и неявно импортирует encodings.utf8 в encodings:99 с помощью encodings.search_function оказались в тупике импорта.

Таким образом, грязным решением было бы просто импортировать декодер utf-8 один раз, чтобы не блокировать этот конкретный импорт из-за побочных эффектов импорта модуля.(''.decode('utf-8'))

Исправить

грязное исправление - не рекомендуется

import paramiko
hostname,username,password='fill','these','in'
''.decode('utf-8')  # dirty fix
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(hostname=hostname, username=username, password=password)
i,o,e = c.exec_command('ls /')
print(o.read())
c.close()

хорошее исправление

import paramiko
if __name__ == '__main__':
    hostname,username,password='fill','these','in'
    c = paramiko.SSHClient()
    c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    c.connect(hostname=hostname, username=username, password=password)
    i,o,e = c.exec_command('ls /')
    print(o.read())
    c.close()

ссылка Трекер проблем с парамико:выпуск 104

" .decode (" utf-8 ") у меня не работает, в итоге я это сделал.

from paramiko import py3compat
# dirty hack to fix threading import lock (issue 104) by preloading module
py3compat.u("dirty hack")

У меня есть оболочка для paramiko с этим реализовано. https://github.com/bucknerns/sshaolin

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top