(2013-10-02) [pythonbrasil] compatibilidade entre python 2 e 3
Embed Size (px)
DESCRIPTION
Apresentação realizada durante a PythonBrasil[9]. Segue abaixo a descrição da palestra: Mudanças na especificação da linguagem fizeram o Python 3 incompatível com parte do código escrito para o Python 2. Algumas dessas incompatibilidades podem ser facilmente resolvidas através de traduções, sejam elas automáticas ou não. Porém, muitas dessas traduções trazem consigo uma incompatibilidade com a versão de origem do código (o Python 2, neste caso), o que nem sempre é desejável. É possível manter um único código compatível com o ambos o Python 2 e 3? Esta atividade se propõe a responder essa pergunta, e enfatizará os problemas e soluções que levaram à elaboração do módulo lazy_compat da AudioLazy (e.g. metaclasses, arredondamento de ponto flutuante).TRANSCRIPT
Compatibilidade entre
Python 2 e 3
Como portar seu cdigoem Python 2.xpara o Python 3.xsem torn-lo incompatvelcom o Python 2.x?
Com base na histria do pacote AudioLazy
Por qu?
Verses futuras do Python
Ampliar o pblico-alvo de seu projetoAudioLazyhttps://pypi.python.org/pypi/audiolazy/
Presso social e tecnoflicos!Software para uso cientficoNumPy
MatPlotLib
SciPy
Notcias recentesFlask!
TODOS j so compatveiscom o Python 3.x!
E outros 2800+ pacotes no PyPI...Python 2.x is the status quo, Python 3.x is the present and future of the language.www.python.org
Valores coletados dia 2013-10-02
Alguns nmeros
Compatibilidade entre Python 2.7 e outras verses
Compatibilidade entre Python 3 e outras verses
Por onde comear
Sute de testes
Conhecimento sobre as diferenas entre versesLer, estudar
Fuar, brincar, remoer, torturar a linguagem
Outras ferramentas e pacotesJ solucionaram o mesmo problema? Como?
Pacotes de auxlio compatibilizao
Dependncias funcionam em quais verses do Python?Diminuir restries
Parte 1
Tipos de diferenas
Nomes e localizaes
import Tkinter
master = Tkinter.Tk()master.mainloop()
import tkinter
master = tkinter.Tk()master.mainloop()
Apenas Python 2
Apenas Python 3
Traceback (most recent call last):[...]ImportError: No module named Tkinter
Rodando no Python 3
Rodando no Python 2
Traceback (most recent call last):[...]ImportError: No module named tkinter
Nomes e localizaes
com try...except
try:import tkinterexcept ImportError:import Tkinter as tkinter
master = tkinter.Tk()master.mainloop()
Nome nicoas no import
Atribuio
H critrios para uso do nome?PEP8
Nome no Python 3Verses futuras
Nome no Python 2Atual hbito
Ausente no Python 3
Quais nomes foram trocados?
Documentao para desenvolvedores!
Mdulo future_builtins
In [1]: import future_builtinsIn [2]: dir(future_builtins)Out[2]: ['__doc__','__file__','__name__','__package__','ascii','filter','hex','map','oct','zip']
Fazer zip/map/filter do Python 2 funcionar como no Python 3 (mdulo existe somente no Python 2)
try:from future_builtins import *except ImportError:pass # Python 3
Mdulo __future__
In [1]: import __future__In [2]: dir(__future__)Out[2]: [ ... ,'absolute_import','all_feature_names','division','generators','nested_scopes','print_function','unicode_literals','with_statement']
Linha inicial de seu cdigoImportar antes de outros imports
Existe no Python 2 e 3
Nomes e localizaes
com verificao prvia
sys.version_info
sys.modules
import sys
PYTHON2 = sys.version_info.major == 2if PYTHON2:builtins = sys.modules["__builtin__"]else:import builtins
Mdulo sys
In [1]: import sys
In [2]: sys.version_infoOut[2]: sys.version_info(major=3, minor=2, micro=4, releaselevel='final', serial=0)
In [3]: sys.version_info >= (3, 2)Out[3]: True
In [4]: sys.version_info >= (3, 3)Out[4]: False
In [5]: sys.versionOut[5]: '3.2.4 (default, May 8 2013, 20:55:18) \n[GCC 4.7.3]'
Nomes e localizaes
com getattr
Funes so objetos
Anlogo ao mtodo get de dicionrios
import itertools
xzip = getattr(itertools, "izip", zip)xmap = getattr(itertools, "imap", map)xfilter = getattr(itertools, "ifilter", filter)
# Usando o builtins visto anteriormentexrange = getattr(builtins, "xrange", range)
Monkeypatch / Mock
Usando getattr e atribuies
Compatibilizar cdigo de terceiros sem mud-los
Nem sempre possvel (tipos bsicos)e.g. Mtodo to_bytes do int (apenas Python 3)
import operatoroperator.div = getattr(operator,"div",operator.truediv)
In [1]: (317215).to_bytes(5, "big")Out[1]: b'\x00\x00\x04\xd7\x1f'
Funes e outros objetos ausentes
Reconstruir
Criar pela primeira veze.g. itertools.accumulateaudiolazy.accumulate
from functools import wraps
@wraps(range)def orange(*args, **kwargs):return list(range(*args, **kwargs))
Mtodos ausentes
Iterao sobre dicionrios
Itervel VS IteradorGotcha!
def iteritems(dictionary):try:return getattr(dictionary, "iteritems")()except AttributeError:return iter(getattr(dictionary, "items")())
str / int
no Python 3
String?str no Python 3
(unicode, str) no Python 2builtins.basestring
Inteiro?int no Python 3
(long, int) no Python 2
INT_TYPES = (int, getattr(builtins, "long", None)) \if PYTHON2 else (int,)
print(isinstance(something_here, INT_TYPES))
Utilizao comisinstance
Internalidades
Iteradores (e geradores)Mtodo next no Python 2
Mtodo __next__ no Python 3
Avaliao if obj: segue um mtodo de obj__nonzero__ no Python 2
__bool__ no Python 3
Funo do mtodo (unbound)Python 2classe.metodo.im_func
objeto.metodo.im_func
Python 3classe.metodo
objeto.metodo.__func__
Parte 2
Testes
Testes
Automatizadospy.test
nose
unittest
doctest
Cobertura de cdigoConfiabilidade
Dependnciasskip
xfail
Compatibilizar dependncia
Testes passando
Testes falhando
Skip automtico com
py.test
import pytest
def skipper(msg="There's something not supported ""in this environment"):def skip(*args, **kwargs):pytest.skip(msg.format(*args, **kwargs))return skip
operator.div = getattr(operator, "div",skipper("There's no ""operator.div"))
tox
[tox]envlist = py26,py27
[testenv]deps=pytestcommands=py.test
Standardize testing in Python
tox.ini
Parte 3
Diferenas importantes
!
Metaclasses
Classe cujas instncias so classes
Sintaxe diferenciada no Python 2 e 3
# Python 3bases = (object,)MyMeta = typeclass A(*bases, metaclass=MyMeta):pass
# Python 2class A(*bases):__metaclass__ = MyMeta
Metaclasses
Soluo padro:Criar uma classe vazia usando a metaclasse, e coloc-la junto s bases
Problemas:Construtor da classe pode falhar
Soluo alternativaMetaclasse falsanica base da nova classe
Construtor da metaclasse com 2 comportamentosAntes da obteno do dicionrio da classe
Depois (real instanciao)
Funo audiolazy.meta
>>> class BadMeta(type):... def __new__(mcls, name, bases, namespace):... if "bad" not in namespace:... raise Exception("Oops, not bad enough")... value = len(name)... def really_bad(self):... return self.bad() * value... namespace["really_bad"] = really_bad... return super(BadMeta, mcls).__new__(mcls, name, bases,... namespace)...>>> class Bady(meta(object, metaclass=BadMeta)):... def bad(self):... return "HUA "...>>> class BadGuy(Bady):... def bad(self):... return "R"...>>> issubclass(BadGuy, Bady)True>>> Bady().really_bad() # value = 4'HUA HUA HUA HUA '>>> BadGuy().really_bad() # value = 6'RRRRRR'
Funo meta
def meta(*bases, **kwargs):metaclass = kwargs.get("metaclass", type)if not bases:bases = (object,)class NewMeta(type):def __new__(mcls, name, mbases, namespace):if name:return metaclass.__new__(metaclass, name,Bases, namespace)return super(NewMeta, mcls).__new__(mcls, "",mbases, {})return NewMeta("", tuple(), {})
Passo 1 Criar classe com metaclasse fakeApenas para obter o namespace
Passo 2 Usar a metaclasse fornecida
Python 2Statement / comando
print >>f, string
print a, b
print string,
Python 3Funo
print(string, file = f)
print(a, b, sep= )
print(string, end= )
Soluo (parcial) imediatafrom __future__ import print_function
Almost there...
Unicode!!!
Nomes de varivel em unicode
*.py em UTF-8 (Python 3)No Python 2, em uma das 2 primeiras linhas:# coding: utf-8
Pensar no unicode (str do Python 3) como um objeto.Encode: codifica o unicode para uma string de bytes
Decode: dos bytes, obtm o unicode
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)http://www.joelonsoftware.com/articles/Unicode.html
Unicode!!!
utexto (Python 3.3 e 2.x)
from __future__ import unicode_literalsFunciona no Python 3.2
Converso manual (testar tipo)
Provavelmente o aspecto mais difcil durante a compatibilizaoos.urandom no simplekv (flask-kvsession)
Itertools e functools
reducefrom functools import reduce
zip, map, filter (e zip_longest)Python 2: Listas
Python 3: Comportamento tardio (lazy), similar ao izip, imap, ifilter do itertools do Python 2
ItertoolsNo possui mais izip, imap, ifilter, izip_longest
Novo accumulate
Diviso
1 / 20 no Python 2 (int)
0.5 no Python 3 (float)
1 // 20 no Python 2 (int)
0 no Python 3 (int)
Soluo imediatafrom __future__ import division
Parte 4
Diferenas inusitadas
Arredondamento de ponto flutuante
In [1]: round(.5)Out[1]: 1.0
In [2]: round(-.5)Out[2]: -1.0
Python 2
Python 3
Soluo? Depende do comportamento desejadoaudiolazy.rint
In [1]: round(.5)Out[1]: 0
In [2]: round(-.5)Out[2]: 0
Namespace da classe
Python 2Funciona ok
Python 3NameError: global name 'data' is not defined
class A(object):data = [1, 2, 3]data_powers = [[x ** n for x in data]for n in range(3)]
Compatibilizardata_powers = (lambda data: [])(data)
Deixar fora da classe
Colocar no __init__ ou no __new__ da metaclasse
Parte 4
Finalizao
Python 2.6 e 2.7
Apenas no Python 2.7Dict comprehensiondict((k, v) for k, v in my_iterable)
Set comprehensionset(el for el in my_iterable)
collections.OrderedDictpip install ordereddict
Outros features do Python 3.1 (backported)http://docs.python.org/dev/whatsnew/2.7.html
Comprehension com {}: apenas Python 2.7, 3.1 e mais recentes
six
Pacote de compatibilizaoFunes para iterar em dicionrios
Constantes com tipos para uso com isinstance
callable (Ausente no Python 3 at o 3.1)
Preocupao com Python 2.4 e 2.5
Avaliao tardiaNo importa nada toa
Engana anlise para auto-complete
Metaclasse (mantm um nvel hierrquico adicional)class A(with_metaclass(Meta, Base)) # Apenas uma base
Neste caso, a audiolazy.meta mais geral
Utilizado pelo MatPlotLib
nine
Favorece o Python 3
Para cdigo a partir do Python 2.6
Boilerplate sugerido
# -*- coding: utf-8 -*-from __future__ import (absolute_import, division,print_function,unicode_literals)from nine import (IS_PYTHON2, str, basestring,native_str, chr, integer_types, class_types,range, range_list, reraise, iterkeys, itervalues,iteritems, map, zip, filter, input,implements_iterator, implements_to_string,implements_repr, nine, nimport)
Alternativas compatibilizao
Converso automticaDistribute com 2to3 ou 3to2
Converso manualBranches para cada verso
IncompatibilidadeCdigo [parcialmente] restrito a verses especficasImports localizados apenas onde necessrio
Obrigado!
>>> from audiolazy import lazy_compat as compat>>> dir(compat)['INT_TYPES', 'NEXT_NAME', 'PYTHON2', 'SOME_GEN_TYPES', 'STR_TYPES', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__name__', '__package__', 'builtins', 'im_func', 'it', 'iteritems', 'itervalues', 'meta', 'orange', 'sys', 'types', 'xfilter', 'xmap', 'xrange', 'xzip', 'xzip_longest']
Perguntas?
https://github.com/danilobellini/[email protected] [dot] bellini [at] gmail [dot] com
Compatibilidade entre Python 2 e 32013-10-02 Danilo J. S. Bellini @danilobellini
Braslia DFCentro de Convenes Ulysses Guimares