quinta-feira, 11 de dezembro de 2008

Primeiras impressões do Python 3.0

Olha pessoal, vou lhes dizer: A nova versão do Python vem com muitas diferenças, conforme foi dito em posts anteriores (Let's go Py3k, Py3k - Reforma geral). No entanto, somente hoje resolvi passar um tempo convertendo um código criado para a versão 2.5 para a versão 3, usando tanto o 2to3 que vêm com o compilador, ou manualmente, fritando um pouco o cérebro.

Algumas coisas eu tenho a dizer sobre essa nova versão. As mudanças são boas, mas são tão pequenas que as vezes esquecemos de fazer do jeito novo. Veja só na conversão do eowyyn, meu bot baseado na framework IRCpy...

vindemiatrix@sorvete:~/Eowyyn$ 2to3-3.0 eowyyn.py -w -v
RefactoringTool: Refactoring eowyyn.py
--- eowyyn.py (original)
+++ eowyyn.py (refactored)
@@ -73,7 +73,7 @@
while True:
try:
time.sleep(0.1)
- except (SystemExit, KeyboardInterrupt), v:
+ except (SystemExit, KeyboardInterrupt) as v:
x.Quit('%s se vai ...' % x.nickname)
time.sleep(1)
break
RefactoringTool: Wrote changes to eowyyn.py
RefactoringTool: Files that were modified:
RefactoringTool: eowyyn.py
vindemiatrix@sorvete:~/Eowyyn$ python3 eowyyn.py
Traceback (most recent call last):
File "eowyyn.py", line 12, in
from ircpy.irc import ClienteIrc
File "/home/vindemiatrix/Eowyyn/ircpy/irc.py", line 10, in
from nucleo import ClienteBase
ImportError: No module named nucleo

vindemiatrix@sorvete:~/Eowyyn$ 2to3-3.0 ircpy/irc.py -w
--- ircpy/irc.py (original)
+++ ircpy/irc.py (refactored)
@@ -7,7 +7,7 @@
Blog: tocadoelfo.blogspot.com
"""

-from nucleo import ClienteBase
+from .nucleo import ClienteBase

class ClienteIrc(ClienteBase):
"""
RefactoringTool: Files that were modified:
RefactoringTool: ircpy/irc.py

vindemiatrix@sorvete:~/Eowyyn$ 2to3-3.0 ircpy/nucleo.py -w
--- ircpy/nucleo.py (original)
+++ ircpy/nucleo.py (refactored)
@@ -139,9 +139,9 @@
self.log.debug('mensagem[cmd]: "%s"' % mensagem['cmd'])
self.log.debug('mensagem[params]: "%s"' % mensagem['params'])

- if self.__cmds.has_key(string.upper(mensagem['cmd'])):
+ if string.upper(mensagem['cmd']) in self.__cmds:
self.__cmds[string.upper(mensagem['cmd'])](mensagem)
- elif self.__saida_padrao <> None:
+ elif self.__saida_padrao != None:
self.__saida_padrao(mensagem)
else:
pass
@@ -164,7 +164,7 @@
self.log.info('def remove_parser(self, comando)')
self.log.debug('comando: "%s";' % (comando,))

- if self__cmds.has_key(comando):
+ if comando in self__cmds:
del self.__cmds[string.upper(comando)]
else:
raise
@@ -221,7 +221,7 @@
self.log.name = self.getName()

return True
- except Exception, e:
+ except Exception as e:
self.__tratar_erro(e)
return False

@@ -267,7 +267,7 @@
try:
self.sock.send(mensagem + self.fim)
return True
- except Exception, e:
+ except Exception as e:
self.__tratar_erro(e)
return False

@@ -280,7 +280,7 @@
try:
dados = self.sock.recv(1024)
self.__bufferiza(dados)
- except Exception, e:
+ except Exception as e:
self.__tratar_erro(e)
break
time.sleep(1)
RefactoringTool: Files that were modified:
RefactoringTool: ircpy/nucleo.py

vindemiatrix@sorvete:~/Eowyyn$ 2to3-3.0 ircpy/ident.py -w
RefactoringTool: No files need to be modified.


As mudanças mais visíveis nesse caso são relativas ao trabalho com dicionários, que agora ao meu ver ficou muito mais natural e o negócio das exceções, que tb melhorou um pouco e ficou mais claro. As vezes, esquecíamos de usar uma tupla para definir um método e acabávamos por escrever except SystemExit, KeyboardInterrupt, v, sendo que v não é um tipo de exceção. Nesse caso, na versão 2.5 e anteriores, a sintaxe correta seria except (SystemExit, KeyboardInterrupt), v.

Estranho né? Agora na nova definição da sintaxe do except, você declara esplicitamente o que é a exceção e o que é variável de controle, da seguinte forma: except (SystemExit, KeyboardInterrupt) as v. Bem mais simples e inteligível não?

Agora um problema surgiu que está me tirando o sono. Com a definição de que o tipo padrão de textos do Python 3.0 é o Unicode, temos agora de lidar com mais um problema. O das funções implementadas em C, que esperam como retorno o tipo string.
vindemiatrix@sorvete:~/Eowyyn$ python3 eowyyn.py
irc.freenode.net:6667 - Erro não tratado em "sendall() argument 1 must be string or buffer, not str".
Traceback (most recent call last):
File "/home/vindemiatrix/Eowyyn/ircpy/nucleo.py", line 268, in envia
self.sock.sendall(mensagem + self.fim)
TypeError: sendall() argument 1 must be string or buffer, not str
Traceback (most recent call last):
File "eowyyn.py", line 66, in
x.Nick('Eowyyn')
File "/home/vindemiatrix/Eowyyn/ircpy/irc.py", line 38, in Nick
self.envia('NICK %s' % (nickname,))
File "/home/vindemiatrix/Eowyyn/ircpy/nucleo.py", line 271, in envia
self.__tratar_erro(e)
File "/home/vindemiatrix/Eowyyn/ircpy/nucleo.py", line 257, in __tratar_erro
raise e
File "/home/vindemiatrix/Eowyyn/ircpy/nucleo.py", line 268, in envia
self.sock.send(mensagem + self.fim)
TypeError: sendall() argument 1 must be string or buffer, not str


Depois de muito procurar na internet sobre como resolver esse problema, foi que encontrei bem escondido na documentação sobre strings e codecs, que agora para você usar tipos baseados em charsets você precisa converter de Unicode para o charset e vice versa, como nesse exemplo, baseado no erro acima:
enviar = mensagem + self.fim
enviar = enviar.encode()
self.sock.send(enviar)


Não que isso seja difícil de implementar, o que não é. A questão é estar sempre atento que agora o Python trabalha com strings em Unicode e, para usar strings baseadas em charset, o tipo data.

As mudanças não param por aí. Estes foram os problemas mais decorrentes da conversão de meu projeto IRCpy para a versão 3.0 do Python. Eu não vou migrar ainda o código para a versão 3.0, mas deixarei disponível uma cópia do bot Eowyyn já implementando as mudanças para o Py3k no site da framework.

Abração, e até breve.

Principal fonte de consulta: What's new in Python 3.0