O Django 1.5 foi a primeira versão do Django a dar suporte ao Python 3. O mesmo código roda tanto no Python 2 (≥ 2.6.5) quanto no Python 3 (≥ 3.2), graças a camada de compatibilidade do six.
Este documento visa principalmente os autores de aplicações plugáveis que desejem dar suporte ao Python 2 e 3. Ele também descreve regras aplicadas no código do Django.
Este documento assume que você já está familiarizado com as mudanças entre o Python 2 e o Python 3. Se você não está, leia primeiro o Python’s official porting guide. Revisar o seu conhecimento sobre manipulação de unicode no Python 2 e 3 irá ajudar; a apresentação Pragmatic Unicode é uma boa referência.
O Django utiliza a estratégia Python 2/3 Compatible Source. Claro, se você não precisar se manter compatível com o Python 2, você está livre para escolher outra estratégia para o seu código. Mas criadores de apps plugáveis são encorajados a usar a mesma estratégia de porte que o Django utiliza.
Escrever código compatível é muito mais simples se você objetivar o Python ≥ 2.6. O Django 1.5 introduziu ferramentas de compatibilidade como o django.utils.six
, que é uma versão customizada do módulo six
. Por conveniência, aliases compatíveis com versões futuras foram introduzidos no Django 1.4.2. Se sua aplicação tira proveito dessas ferramentas, ela irá requerer Django ≥ 1.4.2.
Evidentemente, escrever código-fonte compatível adiciona algum overhead, e isso pode causar frustração. Desenvolvedores Django descobriram que tentar escrever código em Python 3 que seja compatível com o Python 2 é muito mais recompensador que o contrário. Além de deixar o seu código melhor preparado para o futuro, as vantagens do Python 3 (como a manipulação sensata de strings) começam a aparecer rapidamente. Lidar com o Python 2 se tornou um requerimento de compatibilidade com versões anteriores, e nós desenvolvedores estamos acostumados a lidar com esse tipo de limitação.
As ferramentas de porte fornecidas pelo Python são inspiradas por essa filosofia, e refletidas em todo esse guia.
Estes passos consistem em:
from __future__ import unicode_literals
no topo dos seus módulos Python – o ideal é colocar ele em todo e qualquer módulo, caso contrário você vai ficar checando o topo dos seus arquivos para ver qual modo está sendo usado.u
antes de strings unicode;b
antes de bytestrings.Realizar essas mudanças sistematicamente garante compatibilidade com versões anteriores.
Entretanto, como o Django só expôe interfaces unicode para o programador, aplicações Django geralmente não precisam de bytestrings. Com exceção de dados binários ou interfaces orientadas a bytes, o Python 3 desencoraja o uso de bytestrings. O Python 2 faz bytestrings e strings unicode efetivamente intercambiáveis, desde que elas só contenham dados em ASCII. Tire vantagem disso para usar strings unicode sempre que possível e evite os prefixos b
.
Nota
O prefixo u
no Python 2 é um erro sintático no Python 3.2 mas ele será permitido novamente no Python 3.3 graças a PEP 414. Assim, essa transformação é opcional se o seu alvo é Python ≥ 3.3. Mas ainda é recomendado pela fisolofia de “escrever código Python 3”.
O tipo unicode do Python 2 foi renomeado para str
no Python 3, str()
foi renomeado para bytes
, e basestring desapareceu. A biblioteca six fornece ferramentas para lidar com essas mudanças.
O Django também contém várias classes e funções relacionada a strings nos módulos django.utils.encoding
e django.utils.safestring
. Seus nomes utilizam as palavras str
, que não significa a mesma coisa no Python 2 e Python 3, e unicode
, que não existe no Python 3. Para evitar ambiguidade e confusão esses conceitos foram renomeados para bytes
e text
.
Essas são as mudanças de nomes no módulo django.utils.encoding
:
Nome antigo | Novo nome |
---|---|
smart_str |
smart_bytes |
smart_unicode |
smart_text |
force_unicode |
force_text |
Para compatibilidade com versões anteriores, os nomes antigos ainda funcionam no Python 3. Sob o Python 3, smart_str
é um alias para smart_text
.
Para compatibilidade com versões anteriores, os novos nomes funcionam a partir do Django 1.4.2.
Nota
O módulo django.utils.encoding
foi profundamente refatorado no Django 1.5 para fornecer uma API mais consistente. Verifique sua documentação para mais detalhes.
O módulo django.utils.safestring
é mais usado pelas funções mark_safe()
e mark_for_escaping()
, que não mudaram. Caso você esteja usando as partes internas, essas são as mudanças de nomes:
Nome antigo | Novo nome |
---|---|
EscapeString |
EscapeBytes |
EscapeUnicode |
EscapeText |
SafeString |
SafeBytes |
SafeUnicode |
SafeText |
Para compatibilidade com versões anteriores, os nomes antigos ainda funcionam no Python 2. Já no Python 3, EscapeString
e SafeString
são aliases para EscapeText
e SafeText
respectivamente.
Para compatibilidade com versões anteriores, os novos nomes funcionam a partir do Django 1.4.2.
__str__()
e __unicode__()
¶No Python 2, o modelo de objetos especifica os métodos __str__()
e ` __unicode__()`_ . Se esses métodos existem, eles devem retornar str
(bytes) e unicode
(text) respectivamente.
O comando print
e a chamada embutida do método __str__()
na classe str
para determinar a representação compreensível por humanos de um objeto. As chamadas embutidas unicode
` __unicode__()`_ se existir, e caso contrário reverter para o método __str__()
e decodificar o resultado usando o encoding do sistema. Por outro lado, a classe base Model
automaticamente deriva o método __str__()
do ` __unicode__()`_ fazendo o encoding para UTF-8.
No Python 3, existe apenas o método __str__()
, que deve retornar str
(text).
(Também é possível definir o método __bytes__()
, mas aplicações Django tem pouco uso para esse método, já que elas dificilmente precisam lidar com bytes
.)
O Django fornece uma maneira mais simples para definir os métodos __str__()
e ` __unicode__()`_ que funcionam no Python 2 e 3: você deve definir um método __str__()
retornando texto e adicionar o decorator python_2_unicode_compatible()
.
No Python 3, o decorator não faz nada. No Python 2, ele define os métodos apropriados para ` __unicode__()`_ e __str__()
(trocando o método __str__()
original no processo). Segue um exemplo:
from __future__ import unicode_literals
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible
class MyClass(object):
def __str__(self):
return "Instance of my class"
Essa técnica é a que melhor se encaixa na filosofia de portabilidade do Django.
Para compatibilidade com versões futuras, esse decorator está disponível a partir do Django 1.4.2.
Finalmente, repare que o método __repr__()
deve retornar um str
em todas as versões do Python.
dict
e similares¶Os métodos dict.keys()
, dict.items()
e dict.values()
retornam listas no Python 2 e iteradores no Python 3. A classe QueryDict
e classes declaradas em django.utils.datastructures
similares a classe dict
se comportam da mesma maneira no Python 3.
A biblioteca six fornece funções de compatibilidade para lidar com essas mudanças: iterkeys()
, iteritems()
, e itervalues()
. Ela também contém uma função iterlists
não documentada que funciona bem para a classe django.utils.datastructures.MultiValueDict
e suas subclasses.
HttpRequest
e HttpResponse
.¶De acordo com a PEP 3333:
str
bytes
.Especificamente, o atributo HttpResponse.content
contém bytes
, o que pode ser um problema se você compará-lo com um str
nos seus testes. A solução preferida é confiar nos métodos assertContains()
e assertNotContains()
. Esses métodos aceitam uma response e uma string unicode como argumentos.
As diretrizes a seguir são aplicadas no código-fonte do Django. Elas são também recomendadas para aplicações de terceiros que seguem a mesma estratégia de portabilidade.
No Python 3, todas as strings são consideradas Unicode por padrão. O tipo unicode
do Python 2 é chamado de str
no Python 3, e str
se tornou bytes
.
Você não deve usar o prefixo u
antes de uma string literal unicode porque isso é um erro de síntaxe no Python 3.2. Você deve usar o prefixo b
em strings do tipo byte
.
Para habilitar o mesmo compartamento no Python 2, todo módulo deve importar unicode_literals
from __future__
:
from __future__ import unicode_literals
my_string = "This is an unicode literal"
my_bytestring = b"This is a bytestring"
Se você precisar de uma string do tipo byte no Python 2 e uma string unicode no Python 3, use a classe interna str
:
str('my string')
No Python 3, não existem conversões automáticas entre str
e bytes
, e o módulo codecs
se tornou mais rigoroso. O método str.encode()
sempre retorna bytes
, e bytes.decode
sempre retorna str
. Como consequência, o padrão a seguir algumas vezes é necessário:
value = value.encode('ascii', 'ignore').decode('ascii')
Seja cauteloso se você tiver que lidar com index bytestrings.
Quando você capturar exceções, use a keyword as
:
try:
...
except MyException as exc:
...
Essa sintaxe antiga foi removida no Python 3:
try:
...
except MyException, exc: # Don't do that!
...
A sintaxe para relançar uma exceção com um traceback diferente também mudou. Utilize a função six.reraise()
.
Utilize os padrões abaixo para manipular métodos mágicos renomeados no Python 3.
class MyIterator(six.Iterator):
def __iter__(self):
return self # implement some logic here
def __next__(self):
raise StopIteration # implement some logic here
class MyBoolean(object):
def __bool__(self):
return True # implement some logic here
def __nonzero__(self): # Python 2 compatibility
return type(self).__bool__(self)
class MyDivisible(object):
def __truediv__(self, other):
return self / other # implement some logic here
def __div__(self, other): # Python 2 compatibility
return type(self).__truediv__(self, other)
def __itruediv__(self, other):
return self // other # implement some logic here
def __idiv__(self, other): # Python 2 compatibility
return type(self).__itruediv__(self, other)
Métodos especiais são procurados na classe a não na instância para refletir o comportamento do interpretador Python.
A biblioteca six é a biblioteca de compatibilidade canônica para suportar o Python 2 e 3 em uma única base. Leia a sua documentação!
A versão customizada da biblioteca
é distribuída com o Django a partir da versão 1.4.2. Você pode importá-la como django.utils.six
.
Essas são as mudanças necessárias mais comuns para se escrever código compatível.
Os tipos foram removidos no Python 3, e o significado de str
mudou. Para testar esses tipos, use as expressões a seguir:
isinstance(myvalue, six.string_types) # replacement for basestring
isinstance(myvalue, six.text_type) # replacement for unicode
isinstance(myvalue, bytes) # replacement for str
O Python ≥ 2.6 fornece bytes
como um alias para str
, de modo que você não precisa usar o six.binary_type
.
long
¶O tipo long
não existe mais no Python 3. 1L
é um erro sintático. Utilize a verificação six.integer_types
se o valor for um integer ou um long:
isinstance(myvalue, six.integer_types) # replacement for (int, long)
xrange
¶Se você usa xrange
no Python 2, faça o import do six.moves.range
e use ele no lugar. Você pode também importar six.moves.xrange
(ele é equivalente ao six.moves.range
) mas a primeira técnica permite que você simplesmente remova o import ao deixar de suportar o Python 2.
six
customizada do Django¶A versão do six disponibilizada com o Django (django.utils.six
) inclui algumas customizações somente para uso interno.
dez 02, 2017