Pregunta

Ponga lo siguiente en un archivo hello.py (y easy_install paramiko si no lo tiene):

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

Complete la primera línea apropiadamente.

Ahora escriba

python hello.py

y verá algunos resultados de ls.

Ahora, en su lugar, escriba

python

y luego desde el tipo de intérprete

import hello

y listo! Se cuelga! Se destrabará si envuelve el código en una función foo y hace import hello; hello.foo () en su lugar.

¿Por qué se cuelga Paramiko cuando se usa dentro de la inicialización del módulo? ¿Cómo es que Paramiko sabe que se está utilizando durante la inicialización del módulo en primer lugar?

¿Fue útil?

Solución

Paramiko usa hilos separados para el transporte subyacente. Debería nunca tener un módulo que genere un subproceso como efecto secundario de la importación. Según tengo entendido, hay un solo bloqueo de importación disponible, por lo que cuando un subproceso secundario de su módulo intenta otra importación, puede bloquearse indefinidamente, porque su subproceso principal aún mantiene el bloqueo. (Probablemente hay otras trampas que no conozco también)

En general, los módulos no deberían tener efectos secundarios de ningún tipo al importar, o obtendrá resultados impredecibles. Simplemente espere la ejecución con el truco __name__ == '__main__' , y estará bien.

[EDITAR] Parece que no puedo crear un caso de prueba simple que reproduzca este punto muerto. Todavía supongo que es un problema de subprocesos con la importación, porque el código de autenticación está esperando un evento que nunca se activa. Esto puede ser un error en paramiko o python, pero la buena noticia es que nunca deberías verlo si haces las cosas correctamente;)

Este es un buen ejemplo de por qué siempre desea minimizar los efectos secundarios y por qué las técnicas de programación funcional son cada vez más frecuentes.

Otros consejos

Como JimB señaló que es un problema de importación cuando python intenta importar de forma implícita Decodificador str.decode ('utf-8') en el primer uso durante un intento de conexión ssh. Consulte la sección Análisis para más detalles.

En general, uno no puede insistir lo suficiente como para evitar que un módulo genere automáticamente nuevos hilos en la importación. Si puede, trate de evitar el código del módulo mágico en general, ya que casi siempre produce efectos secundarios no deseados.

  1. La solución fácil y sensata para su problema, como ya se mencionó, es colocar su código en un if __name__ == '__main__': que solo se ejecutará si ejecuta este módulo específico y no se ejecutará cuando este módulo sea importado por otros módulos.

  2. (no recomendado) Otra solución es hacer un str.decode ficticio ('utf-8') en su código antes de llamar a SSHClient.connect () - vea el análisis a continuación .

Entonces, ¿cuál es la causa raíz de este problema?

Análisis (autenticación de contraseña simple)

Sugerencia: si desea depurar subprocesos en la importación de Python y establezca threading._VERBOSE = True

  1. paramiko.SSHClient (). connect (.., look_for_keys = False, ..) genera implícitamente un nuevo hilo para su conexión. También puede ver esto si activa la salida de depuración para paramiko.transport .

[Thread-5] [paramiko.transport] DEPURACIÓN: hilo de inicio (modo cliente): 0x317f1d0L

  1. esto se hace básicamente como parte de SSHClient.connect () . Cuando se llama a client.py:324::start_client () , se crea un bloqueo transport.py:399::event=threading.Event () y el hilo es inició transport.py:400::self.start () . Tenga en cuenta que el método start () ejecutará el método transport.py:1565::run () de la clase.

  2. transport.py:1580::self._log (..) imprime nuestro mensaje de registro " hilo de inicio " y luego procede a transport.py:1584::self._check_banner().

  3. check_banner hace una cosa. Recupera el banner ssh (primera respuesta del servidor) transport.py:1707::self.packetizer.readline(timeout) (tenga en cuenta que el tiempo de espera es solo un tiempo de espera de lectura de socket), comprueba un salto de línea al final y de lo contrario se agota el tiempo de espera.

  4. En caso de que se reciba un banner de servidor, intenta decodificar utf-8 la cadena de respuesta packet.py:287::return u (buf) y ahí es donde ocurre el punto muerto. u (s, encoding = 'utf-8') realiza un str.decode ('utf-i') e importa implícitamente encodings.utf8 en codificaciones : 99 a través de encodings.search_function que termina en un punto muerto de importación.

Entonces, una solución sucia sería importar el decodificador utf-8 una vez para no bloquear esa importación específica debido a los efectos secundarios de la importación del módulo. ( '' .decode ('utf-8') )

Fix

arreglo sucio - no recomendado

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

buena solución

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 rastreador de problemas de paramiko: problema 104

" " .decode (" utf-8 ") no funcionó para mí, terminé haciendo esto.

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

Tengo un contenedor para paramiko con eso implementado. https://github.com/bucknerns/sshaolin

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top