Domanda

Inserisci quanto segue in un file hello.py (e easy_install paramiko se non lo hai):

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

Compila la prima riga in modo appropriato.

Ora digita

python hello.py

e vedrai un po 'di output.

Ora invece digita

python

e quindi dal tipo di interprete

import hello

e voilà! Si blocca! Rimarrà immutato se avvolgi il codice in una funzione foo e fai import hello; hello.foo () invece.

Perché Paramiko si blocca quando viene utilizzato durante l'inizializzazione del modulo? In che modo Paramiko è a conoscenza del fatto che viene utilizzato durante l'inizializzazione del modulo in primo luogo?

È stato utile?

Soluzione

Paramiko utilizza thread separati per il trasporto sottostante. non dovresti mai avere un modulo che genera un thread come effetto collaterale dell'importazione. A quanto ho capito, è disponibile un unico blocco di importazione, quindi quando un thread figlio del tuo modulo tenta un'altra importazione, può bloccarsi indefinitamente, poiché il thread principale mantiene ancora il blocco. (Probabilmente ci sono altri gotcha di cui non sono a conoscenza)

In generale, i moduli non dovrebbero avere effetti collaterali di alcun tipo durante l'importazione, o otterrai risultati imprevedibili. Basta interrompere l'esecuzione con il trucco __name__ == '__main__' e andrà tutto bene.

[EDIT] Non riesco a creare un semplice test case che riproduca questo deadlock. Presumo ancora che sia un problema di threading con l'importazione, perché il codice di autenticazione è in attesa di un evento che non si attiva mai. Questo potrebbe essere un bug in paramiko, o python, ma la buona notizia è che non dovresti mai vederlo se fai le cose correttamente;)

Questo è un buon esempio del motivo per cui vuoi sempre ridurre al minimo gli effetti collaterali e perché le tecniche di programmazione funzionale stanno diventando sempre più diffuse.

Altri suggerimenti

Come JimB ha sottolineato che si tratta di un problema di importazione quando Python tenta di importare implicitamente il decodificatore str.decode ('utf-8') al primo utilizzo durante un tentativo di connessione ssh. Vedi la sezione Analisi per i dettagli.

In generale, non si può sottolineare abbastanza che si dovrebbe evitare di avere un modulo che genera automaticamente nuovi thread durante l'importazione. Se puoi, cerca di evitare il codice del modulo magico in generale poiché porta quasi sempre a effetti collaterali indesiderati.

  1. La soluzione semplice e sana per il tuo problema, come già accennato, è quella di mettere il tuo codice in un se __name__ == '__main__': che verrà eseguito solo se esegui questo modulo specifico e non verrai eseguito quando questo mmodule viene importato da altri moduli.

  2. (sconsigliato) Un'altra soluzione è semplicemente fare un falso str.decode ('utf-8') nel tuo codice prima di chiamare SSHClient.connect () - vedi analisi sotto .

Qual è la causa principale di questo problema?

Analisi (autenticazione password semplice)

Suggerimento: se si desidera eseguire il debug del threading nell'importazione di Python e impostare threading._VERBOSE = True

  1. paramiko.SSHClient (). connect (.., look_for_keys = False, ..) genera implicitamente un nuovo thread per la tua connessione. Puoi anche vedere questo se attivi l'output di debug per paramiko.transport .

[Thread-5] [paramiko.transport] DEBUG: thread iniziale (modalità client): 0x317f1d0L

  1. questo è sostanzialmente fatto come parte di SSHClient.connect () . Quando viene chiamato client.py:324::start_client () , viene creato un blocco transport.py:399::event=threading.Event () e il thread è avviato transport.py:400::self.start () . Nota che il metodo start () eseguirà quindi il metodo transport.py:1565::run () della classe.

  2. transport.py:1580::self._log (..) stampa il nostro messaggio di log " thread iniziale " e quindi passa a transport.py:1584::self._check_banner().

  3. check_banner fa una cosa. Recupera il banner ssh (prima risposta dal server) transport.py:1707::self.packetizer.readline(timeout) (nota che il timeout è solo un timeout di lettura socket), verifica la presenza di un avanzamento riga alla fine e altrimenti scade.

  4. Nel caso in cui sia stato ricevuto un banner server, tenta di decodificare utf-8 la stringa di risposta packet.py:287::return u (buf) ed è qui che si verifica il deadlock. u (s, encoding = 'utf-8') esegue uno str.decode ('utf-i') e importa implicitamente encodings.utf8 nelle codifiche : 99 tramite encodings.search_function finendo in un deadlock di importazione.

Quindi una soluzione sporca sarebbe semplicemente importare una volta il decodificatore utf-8 per non bloccare quell'importazione specifica a causa degli effetti collaterali dell'importazione del modulo. ( ''. Decodificare ( 'utf-8') )

Fissare

correzione sporca - non raccomandato

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

buona soluzione

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

ref tracker problema paramiko: numero 104

" " .decode (" utf-8 ") non ha funzionato per me, ho finito per farlo.

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

Ho un wrapper per paramiko con quello implementato. https://github.com/bucknerns/sshaolin

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top