Frage

Setzen Sie die folgenden in eine Datei hello.py (und easy_install paramiko wenn Sie es nicht vor):

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()

Geben Sie hier die erste Zeile entsprechend.

Geben Sie nun

python hello.py

und Sie werden einige ls-Ausgabe.

Jetzt anstelle Typ

python

und dann aus dem Interpreter Typ

import hello

und voila! Es hängt! Es wird unhang wenn Sie den Code in einer Funktion foo wickeln und sie import hello; hello.foo() statt.

Warum hängt paramiko wenn im Modul Initialisierung verwendet? Wie ist paramiko auch bewusst, dass es bei der Initialisierung des Moduls in erster Linie verwendet wird?

War es hilfreich?

Lösung

paramiko verwendet separates Threads für den zugrunde liegenden Transport. Sie sollte nie hat ein Modul, das einen Faden als Nebeneffekt des Imports laicht. Wie ich es verstehe, gibt es eine einzige Importsperre zur Verfügung, so dass, wenn ein Kind Thread aus Ihrem Modul einer weiteren Import versucht, kann es auf unbestimmte Zeit sperren, weil Ihr Hauptthread noch die Sperre hält. (Es gibt wahrscheinlich andere gotchas, die ich nicht bewusst zu bin)

In der Regel Module sollten keine Nebenwirkungen jeglicher Art haben beim Import, oder Sie gehen zu unvorhersehbaren Ergebnissen kommen. Nur halten Sie die Ausführung mit dem __name__ == '__main__' Trick, und Sie werden in Ordnung sein.

[EDIT] Ich kann nicht scheinen, einen einfachen Testfall zu erstellen, die aus dieser Sackgasse wiedergibt. Ich gehe davon nach wie vor, es ist ein Threading-Problem mit dem Import, weil der Auth-Code wird auf ein Ereignis warten, die nie ausgelöst. Dies kann ein Fehler in paramiko sein, oder Python, aber die gute Nachricht ist, dass Sie nicht immer es sehen sollen, wenn Sie die Dinge richtig tun;)

Dies ist ein gutes Beispiel dafür, warum Sie immer Nebenwirkungen minimieren wollen, und warum funktionale Programmiertechniken häufiger geworden sind.

Andere Tipps

Wie JimB wies darauf hin, es ist eine Import Ausgabe , wenn Python versucht, die implizit zu importieren str.decode('utf-8') Decoder bei der ersten Verwendung während einer SSH Verbindungsversuch. Siehe Analyse Abschnitt.

In der Regel kann man nicht genug betonen, die Sie vermeiden sollten automatisch Laichen neue Themen beim Import ein Modul mit. Wenn Sie können, versuchen im Allgemeinen Magie-Modul-Code zu vermeiden, da es fast immer führt zu unerwünschten Nebenwirkungen.

  1. Die einfach - und gesund - fix für Ihr Problem - wie bereits erwähnt - ist der Code in einem if __name__ == '__main__': Körper zu setzen, die nur dann ausgeführt werden, wenn Sie dieses spezielle Modul ausführen und werden nicht ausgeführt werden, wenn diese mmodule importiert von anderen Modulen.

  2. (nicht empfohlen) Eine andere Lösung ist nur ein Dummy str.decode tun ( 'utf-8') in Ihrem Code, bevor Sie SSHClient.connect() nennen - vgl. Analyse unter

Also was ist die Ursache dieses Problems?

Analyse (einfaches Passwort Auth)

Hinweis: Wenn Sie in Python Import debuggen Einfädeln und setzt threading._VERBOSE = True

  1. paramiko.SSHClient().connect(.., look_for_keys=False, ..) laicht implizit einen neuen Thread für Ihre Verbindung. Sie können dies auch sehen, wenn Sie auf Debug-Ausgabe für paramiko.transport drehen.

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

  1. Dies ist im Grunde als Teil SSHClient.connect() getan. Wenn client.py:324::start_client() aufgerufen wird, wird eine Sperre erstellt transport.py:399::event=threading.Event() und der Thread gestartet transport.py:400::self.start(). Beachten Sie, dass die start() Methode wird dann die transport.py:1565::run() Methode der Klasse auszuführen.

  2. transport.py:1580::self._log(..) druckt den unsere Log-Nachricht "Anfangsfaden" und geht dann transport.py:1584::self._check_banner().

  3. check_banner ist eine Sache. Es ruft die ssh Banner (erste Antwort vom Server) transport.py:1707::self.packetizer.readline(timeout) (beachten Sie, dass das Timeout ist nur eine Steckdose lesen Timeout), geprüft, ob ein Zeilenvorschub am Ende und sonst mal aus.

  4. Falls ein Server Banner empfangen wurde, versucht es auf UTF-8 dekodieren die Antwortzeichenfolge packet.py:287::return u(buf) und das ist, wo die Blockade geschieht. Die u(s, encoding='utf-8') hat eine str.decode ( 'utf-i') und implizit importiert encodings.utf8 in encodings:99 über encodings.search_function in einer Import Sackgasse enden.

So ein schmutziges fix wäre, nur die einmal utf-8-Decoder zu importieren, um auf diesem specifiy Import nicht zu blockieren aufgrund Imports Nebenwirkungen Modul. (''.decode('utf-8'))

Fix

schmutzig fix - nicht empfohlen

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()

gute fix

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()

paramiko issue tracker: Ausgabe 104

"". Dekodieren ( "utf-8") nicht für mich arbeiten, ich endete dies zu tun.

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

Ich habe einen Wrapper für paramiko damit umgesetzt. https://github.com/bucknerns/sshaolin

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top