Pourquoi Paramiko se bloque-t-il si vous l'utilisez pendant le chargement d'un module?
-
22-07-2019 - |
Question
Placez les éléments suivants dans un fichier hello.py (et easy_install paramiko
si vous ne l'avez pas encore obtenu):
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()
Remplissez la première ligne de manière appropriée.
Maintenant tapez
python hello.py
et vous verrez une sortie ls.
À la place, tapez
python
puis à partir du type d'interprète
import hello
et le tour est joué! Ça pend! Cela se dissipera si vous enveloppez le code dans une fonction foo
et que vous faites import hello; hello.foo ()
à la place.
Pourquoi Paramiko se bloque-t-il lorsqu'il est utilisé dans l'initialisation du module? Comment Paramiko sait-il qu'il est utilisé lors de l'initialisation du module?
La solution
Paramiko utilise des threads distincts pour le transport sous-jacent. Vous devriez jamais avoir un module qui génère un thread en tant qu'effet secondaire de l'importation. Si j'ai bien compris, un seul verrou d'importation est disponible. Ainsi, lorsqu'un thread enfant de votre module tente une autre importation, il peut se bloquer indéfiniment, car votre thread principal détient toujours le verrou. (Il y a probablement d'autres pièges que je ne connais pas trop)
En règle générale, les modules ne devraient pas avoir d’effets secondaires lors de l’importation, sinon vous obtiendrez des résultats imprévisibles. Attendez simplement l'exécution avec le truc __ name__ == '__main __'
, et tout ira bien pour vous.
[EDIT] Je n'arrive pas à créer un cas de test simple reproduisant cette impasse. Je suppose toujours que c'est un problème de thread avec l'importation, car le code d'autorisation attend un événement qui ne se déclenche jamais. Cela peut être un bug dans paramiko, ou python, mais la bonne nouvelle est que vous ne devriez jamais le voir si vous faites les choses correctement;)
C’est un bon exemple de la raison pour laquelle vous souhaitez toujours minimiser les effets secondaires et des techniques de programmation fonctionnelle qui se répandent de plus en plus.
Autres conseils
Comme JimB a souligné qu'il s'agit d'un problème d'importation lorsque python tente d'importer implicitement le Décodeur str.decode ('utf-8')
lors de la première utilisation lors d'une tentative de connexion ssh. Voir la section Analyse pour plus de détails.
En général, on ne saurait trop insister pour que vous évitiez qu'un module ne génère automatiquement de nouveaux threads lors de l'importation. Si vous le pouvez, essayez d’éviter le code de module magique en général car il entraîne presque toujours des effets secondaires indésirables.
-
Comme cela a déjà été mentionné, la solution simple à votre problème consiste à placer votre code dans un
si __name__ == '__main __':
qui ne sera exécuté que si vous exécutez ce module spécifique et ne sera pas exécuté lorsque ce module est importé par d'autres modules. -
(non recommandé) Une autre solution consiste à créer un code factice str.decode ('utf-8') dans votre code avant d'appeler
SSHClient.connect ()
- voir l'analyse ci-dessous. .
Alors, quelle est la cause première de ce problème?
Analyse (authentification par mot de passe simple)
Conseil: si vous souhaitez déboguer un thread dans une importation Python et définir threading._VERBOSE = True
-
paramiko.SSHClient (). connect (.., look_for_keys = False, ..)
génère implicitement un nouveau thread pour votre connexion. Vous pouvez également le voir si vous activez la sortie de débogage pourparamiko.transport
.
[Thread-5] [paramiko.transport] DEBUG: thread de démarrage (mode client): 0x317f1d0L
-
Ceci est essentiellement fait dans le cadre de
SSHClient.connect ()
. Lorsqueclient.py:324::start_client ()
est appelé, un verrou est créétransport.py:399::event=threading.Event ()
et le fil est a commencétransport.py:400::self.start ()
. Notez que la méthodestart ()
exécutera ensuite la méthodetransport.py:1565::run ()
de la classe. -
transport.py:1580::self._log (..)
imprime le message de notre journal, "fil de départ". puis passe àtransport.py:1584::self._check_banner ()
. -
check_banner
fait une chose. Il récupère la bannière ssh (première réponse du serveur)transport.py:1707::self.packetizer.readline (délai d'attente)
(notez que le délai d'attente est simplement un délai d'attente de lecture de socket), recherche un saut de ligne à la fin et sinon expire. -
Au cas où une bannière de serveur serait reçue, elle essaiera de décoder la chaîne de réponse
packet.py:287::return u (buf)
et l’endroit dans lequel l’interblocage se produit. Leu (s, encoding = 'utf-8')
effectue un str.decode ('utf-i') et importe implicitementencodings.utf8
dans les encodages. : 99
viaencodings.search_function
aboutissant dans une impasse d'importation.
Donc, un correctif serait de simplement importer une fois le décodeur utf-8 afin de ne pas bloquer cette importation spécifique en raison d'effets secondaires d'importation de module. ( ''. decode ('utf-8')
)
Correction
correctif sale - non recommandé
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()
bonne solution
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()
".decode (" utf-8 ") n'a pas fonctionné pour moi, j'ai fini par le faire.
from paramiko import py3compat
# dirty hack to fix threading import lock (issue 104) by preloading module
py3compat.u("dirty hack")
J'ai un wrapper pour paramiko avec cette implémentation. https://github.com/bucknerns/sshaolin