(2013-10-02) [pythonbrasil] compatibilidade entre python 2 e 3

of 40 /40
1 Compatibilidade entre Python 2 e 3 Compatibilidade entre Python 2 e 3 2013-10-02 – Danilo J. S. Bellini – @danilobellini 2013-10-02 – Danilo J. S. Bellini – @danilobellini Brasília – DF Brasília – DF Centro de Convenções Centro de Convenções Ulysses Guimarães Ulysses Guimarães Compatibilidade entre Compatibilidade entre Python 2 e 3 Python 2 e 3 Como portar seu código em Python 2.x para o Python 3.x sem torná-lo incompatível com o Python 2.x? Com base na história do pacote AudioLazy

Author: danilo-bellini

Post on 09-Jul-2015

330 views

Category:

Technology


2 download

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

Print

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