Por que paramiko jeito se você usá-lo durante o carregamento de um módulo?
-
22-07-2019 - |
Pergunta
Coloque o seguinte em um arquivo hello.py (e easy_install paramiko
se você não tem isso):
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()
Preencha a primeira linha de forma adequada.
Agora digite
python hello.py
e você verá alguma saída ls.
Agora, em vez digite
python
e, em seguida, a partir do tipo de interpretador
import hello
e voila! Ele trava! Ele vai despendurar se você coloque o código em um foo
função e fazer import hello; hello.foo()
vez.
Por que paramiko jeito quando utilizados dentro de inicialização do módulo? Como é paramiko mesmo ciente de que ele está sendo usado durante a inicialização do módulo em primeiro lugar?
Solução
paramiko usa segmentos separados para o transporte subjacente. Você deve não tem um módulo que gera um thread como um efeito colateral de importação. Pelo que entendi, não há um único bloqueio de importação disponível, por isso, quando um segmento de criança do seu módulo tenta outra importação, ele pode bloquear indefinidamente, porque o seu segmento principal ainda mantém o bloqueio. (Há provavelmente outras armadilhas que eu não estou ciente de também)
Em geral, os módulos não deve ter efeitos colaterais de qualquer tipo ao importar, ou você está indo para obter resultados imprevisíveis. Apenas adiar a execução com o truque __name__ == '__main__'
, e você vai ficar bem.
[EDIT] Eu não consigo criar um caso de teste simples que reproduz esse impasse. Eu ainda assumir que é um problema de segmentação com importação, porque o código de autenticação está à espera de um evento que nunca incêndios. Este pode ser um bug no paramiko, ou Python, mas a boa notícia é que você não deve nunca vê-lo se você fizer as coisas corretamente;)
Este é um exemplo bom porque você sempre quer minimizar os efeitos colaterais, e por técnicas de programação funcionais estão se tornando mais prevalente.
Outras dicas
Como JimB apontou que é um edição import quando tenta python importar implicitamente a descodificador str.decode('utf-8')
na primeira utilização durante uma tentativa de conexão SSH. Veja Análise para obter detalhes.
Em geral, não se pode forçar bastante que você deve evitar ter um módulo automaticamente gerando novos tópicos na importação. Se você puder, para tentar evitar o código do módulo magia em geral, uma vez que quase sempre leva a efeitos colaterais indesejados.
-
O fácil - e sã - correção para o problema - como já mencionado - é colocar seu código em um corpo
if __name__ == '__main__':
que só será executado se você executar este módulo específico e não vai ser executado quando este mmodule é importado por outros módulos. -
(não recomendado) Outra correção é apenas fazer uma str.decode manequim ( 'utf-8') em seu código antes de chamar
SSHClient.connect()
-. Veja análise abaixo
Assim que é a causa raiz do problema?
Análise (auth senha simples)
Dica: Se você quiser enfiar depuração na importação python e conjunto threading._VERBOSE = True
-
paramiko.SSHClient().connect(.., look_for_keys=False, ..)
gera implicitamente um novo segmento para a sua conexão. Você também pode ver isso se você ativar a saída de depuração paraparamiko.transport
.
[Thread-5 ] [paramiko.transport ] DEBUG : starting thread (client mode): 0x317f1d0L
-
Este é basicamente feito como parte de
SSHClient.connect()
. Quandoclient.py:324::start_client()
é chamado, um bloqueio é criadotransport.py:399::event=threading.Event()
eo segmento é iniciadotransport.py:400::self.start()
. Note que o métodostart()
irá então executar métodotransport.py:1565::run()
da classe. -
transport.py:1580::self._log(..)
imprime a nossa mensagem de log "a partir thread" e então começa atransport.py:1584::self._check_banner()
. -
check_banner
faz uma coisa. Ele recupera a bandeira ssh (primeira resposta do servidor)transport.py:1707::self.packetizer.readline(timeout)
(note que o tempo limite é apenas um limite do soquete ler), cheques para um avanço de linha no final e de outra forma expira. -
No caso de um banner servidor foi recebido, ele tenta utf-8 decodificar o
packet.py:287::return u(buf)
cadeia de resposta e é onde o impasse acontece. Ou(s, encoding='utf-8')
faz um str.decode ( 'utf-i') e implicitamente importaçõesencodings.utf8
emencodings:99
viaencodings.search_function
acabar em um impasse de importação.
Assim, uma correção sujo seria apenas para importar o decodificador utf-8 uma vez, a fim de não bloquear em que a importação specifiy devido a efeitos colaterais de importação do módulo. (''.decode('utf-8')
)
Fix
correção sujo - não é 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()
boa correção
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()
"". Decodificação ( "utf-8") não funcionou para mim, eu acabei fazendo isso.
from paramiko import py3compat
# dirty hack to fix threading import lock (issue 104) by preloading module
py3compat.u("dirty hack")
Eu tenho um wrapper para paramiko com que implementou. https://github.com/bucknerns/sshaolin