Coisas que aprendi portando código para Python 3

Coisas que aprendi portando código para Python 3

Palestra a apresentar no encontro da comunidade PythonRio: http://pythonrio.python.org.br
Atualizada em 15 Set 2016, para apresentação na Python Floripa

Dfd7b9492f5c5e49dca373bfdd7a3b1a?s=128

Elias Dorneles

June 25, 2016
Tweet

Transcript

  1. 4.

    Por que Python 3? • É uma linguagem melhor ◦

    tuple unpacking ◦ APIs mais consistentes ◦ async/await ◦ sintaxe para tipagem gradual ◦ mais fácil para iniciantes • É o futuro do ecossistema ◦ seus usuários querem PY3
  2. 6.

    Estratégias de migração 1. Migrar de vez para Python 3

    ◦ para código com baixo risco 2. Fork para versão Python 3 ◦ para código estável, que receba poucas modificações 3. Python 2 & 3 com mesma base de código ◦ para código mantido e sendo usado por outros
  3. 8.

    Importante ter uma boa cobertura de testes • Não precisa

    ser 100%, mas é bom ter o suficiente para pegar regressões importantes • Rode os testes em Python 2 & Python 3 ◦ comece ignorando os que ainda não passam em Python 3, vá consertando aos poucos • Lembre que os testes podem estar incorretos em Python 3 e precisarem mudar também
  4. 10.

    Foque em 2.7 e 3.3+ • Caso ainda esteja em

    2.6 ou anterior, migre primeiro para 2.7+ • Ignore 3.x menores que 3.3 ◦ não tinham suporte apropriado (sintático & stdlib) para fazer código compatível com 2 ◦ vendors (distros) em geral suportam 3.3+ • Dica para instalar várias versões de Python: ◦ http://saghul.github.io/pythonz (CPython, PyPy, etc)
  5. 11.

    Use six! https://pypi.python.org/pypi/six • Melhor biblioteca para Python 2 &

    3 no mesmo código* • Tudo em um arquivo, fácil de copiar e colar dentro do projeto se necessário • Funções utils, constantes, shims, aliases • Vocabulário para portar • Learn it, love it! <3
  6. 12.

    Retrocompatibilidade deixa tudo mais difícil • Em alguns casos, pode

    ser necessário quebrar ◦ unicode vs bytes força corrigir alguns problemas ◦ quando for bug mesmo, é aceitável quebrar • Prepare-se para fazer alguns ciclos de depreciação ◦ marcar código como obsoleto, emitindo warnings ◦ dica: DeprecationWarning é ignorada por default, crie uma nova class MyDeprecationWarning(Warning)
  7. 14.

    Vamos falar de codificação de caracteres! “O Mínimo Absoluto Que

    Todo Desenvolvedor De Software Absolutamente, Positivamente Precisa Saber Sobre Unicode E Conjuntos de Caracteres (Sem Desculpas!)” by Joel Spolsky Em inglês: http://www.joelonsoftware.com/articles/Unicode.html Tradução: http://local.joelonsoftware.com/wiki/O_M%C3%ADnimo_Absoluto_Que_Todo_Desenvolvedor_De_Soft ware_Absolutamente,_Positivamente_Precisa_Saber_Sobre_Unicode_E_Conjuntos_de_Caracteres_(S em_Desculpas!)
  8. 15.
  9. 16.
  10. 17.

    Erros de codificação em Python 2 >>> s = 'coração'

    >>> s 'cora\xc3\xa7\xc3\xa3o' >>> type(s) <type 'str'> >>> unicode(s) Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)
  11. 18.

    Erros de codificação em Python 2 >>> s = 'coração'

    >>> s.decode('utf-8') u'cora\xe7\xe3o' >>> type(s.decode('utf-8')) <type 'unicode'> >>> s.decode('utf-8').encode('utf-8') 'cora\xc3\xa7\xc3\xa3o' >>> type(s.decode('utf-8').encode('utf-8')) <type 'str'>
  12. 19.

    Mojibake em Python 2 >>> s = 'coração' >>> s.decode('latin1')

    u'cora\xc3\xa7\xc3\xa3o' >>> print s.decode('latin1') coração >>> s.encode('latin1') Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)
  13. 20.

    Erros de codificação em Python 3 >>> s = 'coração'

    >>> s 'coração' >>> type(s) <class 'str'> >>> s.encode('utf-8') b'cora\xc3\xa7\xc3\xa3o' >>> type(s.encode('utf-8')) <class 'bytes'>
  14. 21.

    Mojibake em Python 3 >>> s = 'coração' >>> s.encode('utf-8').decode('latin1')

    # Mojibake! 'coração' >>> s.encode('latin1') b'cora\xe7\xe3o' >>> s.encode('latin1').decode('utf-8') Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe7 in position 4: invalid continuation byte
  15. 22.

    Unicode vs bytes: o desafio Para cada “string”, você precisa

    decidir se: • deve ser só texto? (unicode em PY2, str em PY3) • deve ser só binário? (str/bytes em PY2, bytes em PY3) • deve ser string nativa? (str em PY2 & PY3) Fica mais simples quando se escolhe ou texto ou binário. É importante boa cobertura, e checar corretude dos testes também.
  16. 23.

    Comparações de tipos diferentes falham em PY3 >>> max(1, 2,

    '3') '3' >>> max(1, 2, '3') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unorderable types: str() > int() Python 2 Python 3
  17. 24.

    Comparações de tipos diferentes falham em PY3 >>> '' ==

    b'' True >>> '' == b'' False Python 2 Python 3
  18. 25.

    foo == 'bar' Cuidado com testes com asserções: Pode passar

    em Python 3 mas ainda estar incorreto!
  19. 27.

    Use os imports do __future__ • Exceção: evite unicode_literals ◦

    funções da stdlib em PY2 esperando bytes acabam recebendo um objeto unicode ◦ mais interessante quando portando de PY3 -> PY2 from __future__ import ( print_function, absolute_import, division )
  20. 28.

    Para projetos grandes, comece pelas beiradas • Comece a portar

    as partes com menos dependências • Exemplo: ◦ escolha um módulo em utils ◦ revise o código e os testes ◦ corrija os testes, adicione mais alguns ◦ porte • É uma boa documentar decisões sobre bytes vs unicode ◦ Scrapy: começou com utils, depois Request/Response
  21. 29.

    Recursos na Web (em inglês) • Livro gratuito: http://python3porting.com •

    Guia opinionado: https://docs.python.org/dev/howto/pyporting.html • Razões para usar Python 3: http://asmeurer.github.io/python3-presentation/slides.html • Sobre unicode_literals: http://python-future.org/imports.html#should-i-import-unico de-literals