parent
01c392623d
commit
8b01909841
|
@ -1,109 +0,0 @@
|
||||||
# Compatibility layer for running Django both in 2.x and 3.x
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
if sys.version_info[0] < 3:
|
|
||||||
PY3 = False
|
|
||||||
# Changed module locations
|
|
||||||
from urlparse import (urlparse, urlunparse, urljoin, urlsplit, urlunsplit,
|
|
||||||
urldefrag, parse_qsl)
|
|
||||||
from urllib import (quote, unquote, quote_plus, urlopen, urlencode,
|
|
||||||
url2pathname, urlretrieve, unquote_plus)
|
|
||||||
from urllib2 import (Request, OpenerDirector, UnknownHandler, HTTPHandler,
|
|
||||||
HTTPSHandler, HTTPDefaultErrorHandler, FTPHandler,
|
|
||||||
HTTPError, HTTPErrorProcessor)
|
|
||||||
import urllib2
|
|
||||||
import Cookie as cookies
|
|
||||||
try:
|
|
||||||
import cPickle as pickle
|
|
||||||
except ImportError:
|
|
||||||
import pickle
|
|
||||||
try:
|
|
||||||
import thread
|
|
||||||
except ImportError:
|
|
||||||
import dummy_thread as thread
|
|
||||||
from htmlentitydefs import name2codepoint
|
|
||||||
import HTMLParser
|
|
||||||
from os import getcwdu
|
|
||||||
from itertools import izip as zip
|
|
||||||
unichr = unichr
|
|
||||||
xrange = xrange
|
|
||||||
maxsize = sys.maxint
|
|
||||||
|
|
||||||
# Type aliases
|
|
||||||
string_types = basestring,
|
|
||||||
text_type = unicode
|
|
||||||
integer_types = int, long
|
|
||||||
long_type = long
|
|
||||||
|
|
||||||
from io import BytesIO as OutputIO
|
|
||||||
|
|
||||||
# Glue code for syntax differences
|
|
||||||
def reraise(tp, value, tb=None):
|
|
||||||
exec("raise tp, value, tb")
|
|
||||||
|
|
||||||
def with_metaclass(meta, base=object):
|
|
||||||
class _DjangoBase(base):
|
|
||||||
__metaclass__ = meta
|
|
||||||
return _DjangoBase
|
|
||||||
|
|
||||||
iteritems = lambda o: o.iteritems()
|
|
||||||
itervalues = lambda o: o.itervalues()
|
|
||||||
iterkeys = lambda o: o.iterkeys()
|
|
||||||
|
|
||||||
# n() is useful when python3 needs a str (unicode), and python2 str (bytes)
|
|
||||||
n = lambda s: s.encode('utf-8')
|
|
||||||
|
|
||||||
else:
|
|
||||||
PY3 = True
|
|
||||||
import builtins
|
|
||||||
|
|
||||||
# Changed module locations
|
|
||||||
from urllib.parse import (urlparse, urlunparse, urlencode, urljoin,
|
|
||||||
urlsplit, urlunsplit, quote, unquote,
|
|
||||||
quote_plus, unquote_plus, parse_qsl,
|
|
||||||
urldefrag)
|
|
||||||
from urllib.request import (urlopen, url2pathname, Request, OpenerDirector,
|
|
||||||
UnknownHandler, HTTPHandler, HTTPSHandler,
|
|
||||||
HTTPDefaultErrorHandler, FTPHandler,
|
|
||||||
HTTPError, HTTPErrorProcessor, urlretrieve)
|
|
||||||
import urllib.request as urllib2
|
|
||||||
import http.cookies as cookies
|
|
||||||
import pickle
|
|
||||||
try:
|
|
||||||
import _thread as thread
|
|
||||||
except ImportError:
|
|
||||||
import _dummy_thread as thread
|
|
||||||
from html.entities import name2codepoint
|
|
||||||
import html.parser as HTMLParser
|
|
||||||
from os import getcwd as getcwdu
|
|
||||||
zip = zip
|
|
||||||
unichr = chr
|
|
||||||
xrange = range
|
|
||||||
maxsize = sys.maxsize
|
|
||||||
|
|
||||||
# Type aliases
|
|
||||||
string_types = str,
|
|
||||||
text_type = str
|
|
||||||
integer_types = int,
|
|
||||||
long_type = int
|
|
||||||
|
|
||||||
from io import StringIO as OutputIO
|
|
||||||
|
|
||||||
# Glue code for syntax differences
|
|
||||||
def reraise(tp, value, tb=None):
|
|
||||||
if value.__traceback__ is not tb:
|
|
||||||
raise value.with_traceback(tb)
|
|
||||||
raise value
|
|
||||||
|
|
||||||
def with_metaclass(meta, base=object):
|
|
||||||
ns = dict(base=base, meta=meta)
|
|
||||||
exec("""class _DjangoBase(base, metaclass=meta):
|
|
||||||
pass""", ns)
|
|
||||||
return ns["_DjangoBase"]
|
|
||||||
|
|
||||||
iteritems = lambda o: o.items()
|
|
||||||
itervalues = lambda o: o.values()
|
|
||||||
iterkeys = lambda o: o.keys()
|
|
||||||
|
|
||||||
n = lambda s: s
|
|
|
@ -0,0 +1,353 @@
|
||||||
|
"""Utilities for writing code that runs on Python 2 and 3"""
|
||||||
|
|
||||||
|
import operator
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
|
||||||
|
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||||
|
__version__ = "1.1.0"
|
||||||
|
|
||||||
|
|
||||||
|
# True if we are running on Python 3.
|
||||||
|
PY3 = sys.version_info[0] == 3
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
string_types = str,
|
||||||
|
integer_types = int,
|
||||||
|
class_types = type,
|
||||||
|
text_type = str
|
||||||
|
binary_type = bytes
|
||||||
|
|
||||||
|
MAXSIZE = sys.maxsize
|
||||||
|
else:
|
||||||
|
string_types = basestring,
|
||||||
|
integer_types = (int, long)
|
||||||
|
class_types = (type, types.ClassType)
|
||||||
|
text_type = unicode
|
||||||
|
binary_type = str
|
||||||
|
|
||||||
|
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
|
||||||
|
class X(object):
|
||||||
|
def __len__(self):
|
||||||
|
return 1 << 31
|
||||||
|
try:
|
||||||
|
len(X())
|
||||||
|
except OverflowError:
|
||||||
|
# 32-bit
|
||||||
|
MAXSIZE = int((1 << 31) - 1)
|
||||||
|
else:
|
||||||
|
# 64-bit
|
||||||
|
MAXSIZE = int((1 << 63) - 1)
|
||||||
|
del X
|
||||||
|
|
||||||
|
|
||||||
|
def _add_doc(func, doc):
|
||||||
|
"""Add documentation to a function."""
|
||||||
|
func.__doc__ = doc
|
||||||
|
|
||||||
|
|
||||||
|
def _import_module(name):
|
||||||
|
"""Import module, returning the module after the last dot."""
|
||||||
|
__import__(name)
|
||||||
|
return sys.modules[name]
|
||||||
|
|
||||||
|
|
||||||
|
class _LazyDescr(object):
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def __get__(self, obj, tp):
|
||||||
|
result = self._resolve()
|
||||||
|
setattr(obj, self.name, result)
|
||||||
|
# This is a bit ugly, but it avoids running this again.
|
||||||
|
delattr(tp, self.name)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class MovedModule(_LazyDescr):
|
||||||
|
|
||||||
|
def __init__(self, name, old, new=None):
|
||||||
|
super(MovedModule, self).__init__(name)
|
||||||
|
if PY3:
|
||||||
|
if new is None:
|
||||||
|
new = name
|
||||||
|
self.mod = new
|
||||||
|
else:
|
||||||
|
self.mod = old
|
||||||
|
|
||||||
|
def _resolve(self):
|
||||||
|
return _import_module(self.mod)
|
||||||
|
|
||||||
|
|
||||||
|
class MovedAttribute(_LazyDescr):
|
||||||
|
|
||||||
|
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
|
||||||
|
super(MovedAttribute, self).__init__(name)
|
||||||
|
if PY3:
|
||||||
|
if new_mod is None:
|
||||||
|
new_mod = name
|
||||||
|
self.mod = new_mod
|
||||||
|
if new_attr is None:
|
||||||
|
if old_attr is None:
|
||||||
|
new_attr = name
|
||||||
|
else:
|
||||||
|
new_attr = old_attr
|
||||||
|
self.attr = new_attr
|
||||||
|
else:
|
||||||
|
self.mod = old_mod
|
||||||
|
if old_attr is None:
|
||||||
|
old_attr = name
|
||||||
|
self.attr = old_attr
|
||||||
|
|
||||||
|
def _resolve(self):
|
||||||
|
module = _import_module(self.mod)
|
||||||
|
return getattr(module, self.attr)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class _MovedItems(types.ModuleType):
|
||||||
|
"""Lazy loading of moved objects"""
|
||||||
|
|
||||||
|
|
||||||
|
_moved_attributes = [
|
||||||
|
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
|
||||||
|
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
|
||||||
|
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
|
||||||
|
MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
|
||||||
|
MovedAttribute("reduce", "__builtin__", "functools"),
|
||||||
|
MovedAttribute("StringIO", "StringIO", "io"),
|
||||||
|
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
|
||||||
|
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
|
||||||
|
|
||||||
|
MovedModule("builtins", "__builtin__"),
|
||||||
|
MovedModule("configparser", "ConfigParser"),
|
||||||
|
MovedModule("copyreg", "copy_reg"),
|
||||||
|
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
|
||||||
|
MovedModule("http_cookies", "Cookie", "http.cookies"),
|
||||||
|
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
|
||||||
|
MovedModule("html_parser", "HTMLParser", "html.parser"),
|
||||||
|
MovedModule("http_client", "httplib", "http.client"),
|
||||||
|
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
|
||||||
|
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
|
||||||
|
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
|
||||||
|
MovedModule("cPickle", "cPickle", "pickle"),
|
||||||
|
MovedModule("queue", "Queue"),
|
||||||
|
MovedModule("reprlib", "repr"),
|
||||||
|
MovedModule("socketserver", "SocketServer"),
|
||||||
|
MovedModule("tkinter", "Tkinter"),
|
||||||
|
MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
|
||||||
|
MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
|
||||||
|
MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
|
||||||
|
MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
|
||||||
|
MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
|
||||||
|
MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
|
||||||
|
MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
|
||||||
|
MovedModule("tkinter_colorchooser", "tkColorChooser",
|
||||||
|
"tkinter.colorchooser"),
|
||||||
|
MovedModule("tkinter_commondialog", "tkCommonDialog",
|
||||||
|
"tkinter.commondialog"),
|
||||||
|
MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
|
||||||
|
MovedModule("tkinter_font", "tkFont", "tkinter.font"),
|
||||||
|
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
|
||||||
|
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
|
||||||
|
"tkinter.simpledialog"),
|
||||||
|
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
|
||||||
|
MovedModule("winreg", "_winreg"),
|
||||||
|
]
|
||||||
|
for attr in _moved_attributes:
|
||||||
|
setattr(_MovedItems, attr.name, attr)
|
||||||
|
del attr
|
||||||
|
|
||||||
|
moves = sys.modules["six.moves"] = _MovedItems("moves")
|
||||||
|
|
||||||
|
|
||||||
|
def add_move(move):
|
||||||
|
"""Add an item to six.moves."""
|
||||||
|
setattr(_MovedItems, move.name, move)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_move(name):
|
||||||
|
"""Remove item from six.moves."""
|
||||||
|
try:
|
||||||
|
delattr(_MovedItems, name)
|
||||||
|
except AttributeError:
|
||||||
|
try:
|
||||||
|
del moves.__dict__[name]
|
||||||
|
except KeyError:
|
||||||
|
raise AttributeError("no such move, %r" % (name,))
|
||||||
|
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
_meth_func = "__func__"
|
||||||
|
_meth_self = "__self__"
|
||||||
|
|
||||||
|
_func_code = "__code__"
|
||||||
|
_func_defaults = "__defaults__"
|
||||||
|
|
||||||
|
_iterkeys = "keys"
|
||||||
|
_itervalues = "values"
|
||||||
|
_iteritems = "items"
|
||||||
|
else:
|
||||||
|
_meth_func = "im_func"
|
||||||
|
_meth_self = "im_self"
|
||||||
|
|
||||||
|
_func_code = "func_code"
|
||||||
|
_func_defaults = "func_defaults"
|
||||||
|
|
||||||
|
_iterkeys = "iterkeys"
|
||||||
|
_itervalues = "itervalues"
|
||||||
|
_iteritems = "iteritems"
|
||||||
|
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
def get_unbound_function(unbound):
|
||||||
|
return unbound
|
||||||
|
|
||||||
|
|
||||||
|
advance_iterator = next
|
||||||
|
|
||||||
|
def callable(obj):
|
||||||
|
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
|
||||||
|
else:
|
||||||
|
def get_unbound_function(unbound):
|
||||||
|
return unbound.im_func
|
||||||
|
|
||||||
|
|
||||||
|
def advance_iterator(it):
|
||||||
|
return it.next()
|
||||||
|
|
||||||
|
callable = callable
|
||||||
|
_add_doc(get_unbound_function,
|
||||||
|
"""Get the function out of a possibly unbound function""")
|
||||||
|
|
||||||
|
|
||||||
|
get_method_function = operator.attrgetter(_meth_func)
|
||||||
|
get_method_self = operator.attrgetter(_meth_self)
|
||||||
|
get_function_code = operator.attrgetter(_func_code)
|
||||||
|
get_function_defaults = operator.attrgetter(_func_defaults)
|
||||||
|
|
||||||
|
|
||||||
|
def iterkeys(d):
|
||||||
|
"""Return an iterator over the keys of a dictionary."""
|
||||||
|
return getattr(d, _iterkeys)()
|
||||||
|
|
||||||
|
def itervalues(d):
|
||||||
|
"""Return an iterator over the values of a dictionary."""
|
||||||
|
return getattr(d, _itervalues)()
|
||||||
|
|
||||||
|
def iteritems(d):
|
||||||
|
"""Return an iterator over the (key, value) pairs of a dictionary."""
|
||||||
|
return getattr(d, _iteritems)()
|
||||||
|
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
def b(s):
|
||||||
|
return s.encode("latin-1")
|
||||||
|
def u(s):
|
||||||
|
return s
|
||||||
|
if sys.version_info[1] <= 1:
|
||||||
|
def int2byte(i):
|
||||||
|
return bytes((i,))
|
||||||
|
else:
|
||||||
|
# This is about 2x faster than the implementation above on 3.2+
|
||||||
|
int2byte = operator.methodcaller("to_bytes", 1, "big")
|
||||||
|
import io
|
||||||
|
StringIO = io.StringIO
|
||||||
|
BytesIO = io.BytesIO
|
||||||
|
else:
|
||||||
|
def b(s):
|
||||||
|
return s
|
||||||
|
def u(s):
|
||||||
|
return unicode(s, "unicode_escape")
|
||||||
|
int2byte = chr
|
||||||
|
import StringIO
|
||||||
|
StringIO = BytesIO = StringIO.StringIO
|
||||||
|
_add_doc(b, """Byte literal""")
|
||||||
|
_add_doc(u, """Text literal""")
|
||||||
|
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
import builtins
|
||||||
|
exec_ = getattr(builtins, "exec")
|
||||||
|
|
||||||
|
|
||||||
|
def reraise(tp, value, tb=None):
|
||||||
|
if value.__traceback__ is not tb:
|
||||||
|
raise value.with_traceback(tb)
|
||||||
|
raise value
|
||||||
|
|
||||||
|
|
||||||
|
print_ = getattr(builtins, "print")
|
||||||
|
del builtins
|
||||||
|
|
||||||
|
else:
|
||||||
|
def exec_(code, globs=None, locs=None):
|
||||||
|
"""Execute code in a namespace."""
|
||||||
|
if globs is None:
|
||||||
|
frame = sys._getframe(1)
|
||||||
|
globs = frame.f_globals
|
||||||
|
if locs is None:
|
||||||
|
locs = frame.f_locals
|
||||||
|
del frame
|
||||||
|
elif locs is None:
|
||||||
|
locs = globs
|
||||||
|
exec("""exec code in globs, locs""")
|
||||||
|
|
||||||
|
|
||||||
|
exec_("""def reraise(tp, value, tb=None):
|
||||||
|
raise tp, value, tb
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def print_(*args, **kwargs):
|
||||||
|
"""The new-style print function."""
|
||||||
|
fp = kwargs.pop("file", sys.stdout)
|
||||||
|
if fp is None:
|
||||||
|
return
|
||||||
|
def write(data):
|
||||||
|
if not isinstance(data, basestring):
|
||||||
|
data = str(data)
|
||||||
|
fp.write(data)
|
||||||
|
want_unicode = False
|
||||||
|
sep = kwargs.pop("sep", None)
|
||||||
|
if sep is not None:
|
||||||
|
if isinstance(sep, unicode):
|
||||||
|
want_unicode = True
|
||||||
|
elif not isinstance(sep, str):
|
||||||
|
raise TypeError("sep must be None or a string")
|
||||||
|
end = kwargs.pop("end", None)
|
||||||
|
if end is not None:
|
||||||
|
if isinstance(end, unicode):
|
||||||
|
want_unicode = True
|
||||||
|
elif not isinstance(end, str):
|
||||||
|
raise TypeError("end must be None or a string")
|
||||||
|
if kwargs:
|
||||||
|
raise TypeError("invalid keyword arguments to print()")
|
||||||
|
if not want_unicode:
|
||||||
|
for arg in args:
|
||||||
|
if isinstance(arg, unicode):
|
||||||
|
want_unicode = True
|
||||||
|
break
|
||||||
|
if want_unicode:
|
||||||
|
newline = unicode("\n")
|
||||||
|
space = unicode(" ")
|
||||||
|
else:
|
||||||
|
newline = "\n"
|
||||||
|
space = " "
|
||||||
|
if sep is None:
|
||||||
|
sep = space
|
||||||
|
if end is None:
|
||||||
|
end = newline
|
||||||
|
for i, arg in enumerate(args):
|
||||||
|
if i:
|
||||||
|
write(sep)
|
||||||
|
write(arg)
|
||||||
|
write(end)
|
||||||
|
|
||||||
|
_add_doc(reraise, """Reraise an exception.""")
|
||||||
|
|
||||||
|
|
||||||
|
def with_metaclass(meta, base=object):
|
||||||
|
"""Create a base class with a metaclass."""
|
||||||
|
return meta("NewBase", (base,), {})
|
|
@ -2,251 +2,48 @@
|
||||||
Python 3 compatibility
|
Python 3 compatibility
|
||||||
======================
|
======================
|
||||||
|
|
||||||
Django 1.5 introduces a compatibility layer that allows the code to be run both
|
Django 1.5 is the first version of Django to support Python 3.
|
||||||
in Python 2 (2.6/2.7) and Python 3 (>= 3.2) (*work in progress*).
|
|
||||||
|
|
||||||
This document is not meant as a complete Python 2 to Python 3 migration guide.
|
The same code runs both on Python 2 (≥2.6.5) and Python 3 (≥3.2). To
|
||||||
There are many existing resources you can read. But we describe some utilities
|
achieve this:
|
||||||
and guidelines that we recommend you should use when you want to ensure your
|
|
||||||
code can be run with both Python 2 and 3.
|
|
||||||
|
|
||||||
* http://docs.python.org/py3k/howto/pyporting.html
|
- wherever possible, Django uses the six_ compatibility layer,
|
||||||
* http://python3porting.com/
|
- all modules declare ``from __future__ import unicode_literals``.
|
||||||
|
|
||||||
django.utils.py3
|
.. _six: http://packages.python.org/six/
|
||||||
|
|
||||||
|
This document is not meant as a Python 2 to Python 3 migration guide. There
|
||||||
|
are many existing resources, including `Python's official porting guide`_. But
|
||||||
|
it describes guidelines that apply to Django's code and are recommended for
|
||||||
|
pluggable apps that run with both Python 2 and 3.
|
||||||
|
|
||||||
|
.. _Python's official porting guide: http://docs.python.org/py3k/howto/pyporting.html
|
||||||
|
|
||||||
|
.. module: django.utils.six
|
||||||
|
|
||||||
|
django.utils.six
|
||||||
================
|
================
|
||||||
|
|
||||||
Whenever a symbol or module has different semantics or different locations on
|
Read the documentation of six_. It's the canonical compatibility library for
|
||||||
Python 2 and Python 3, you can import it from ``django.utils.py3`` where it
|
supporting Python 2 and 3 in a single codebase.
|
||||||
will be automatically converted depending on your current Python version.
|
|
||||||
|
|
||||||
PY3
|
``six`` is bundled with Django: you can import it as :mod:`django.utils.six`.
|
||||||
---
|
|
||||||
|
|
||||||
If you need to know anywhere in your code if you are running Python 3 or a
|
.. _string-handling:
|
||||||
previous Python 2 version, you can check the ``PY3`` boolean variable::
|
|
||||||
|
|
||||||
from django.utils.py3 import PY3
|
|
||||||
|
|
||||||
if PY3:
|
|
||||||
# Do stuff Python 3-wise
|
|
||||||
else:
|
|
||||||
# Do stuff Python 2-wise
|
|
||||||
|
|
||||||
This should be considered as a last resort solution when it is not possible
|
|
||||||
to import a compatible name from django.utils.py3, as described in the sections
|
|
||||||
below.
|
|
||||||
|
|
||||||
String handling
|
String handling
|
||||||
===============
|
===============
|
||||||
|
|
||||||
In Python 3, all strings are considered Unicode strings by default. Byte strings
|
In Python 3, all strings are considered Unicode strings by default. Byte
|
||||||
have to be prefixed with the letter 'b'. To mimic the same behaviour in Python 2,
|
strings must be prefixed with the letter ``b``. In order to enable the same
|
||||||
we recommend you import ``unicode_literals`` from the ``__future__`` library::
|
behavior in Python 2, every module must import ``unicode_literals`` from
|
||||||
|
``__future__``::
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
my_string = "This is an unicode literal"
|
my_string = "This is an unicode literal"
|
||||||
my_bytestring = b"This is a bytestring"
|
my_bytestring = b"This is a bytestring"
|
||||||
|
|
||||||
Be cautious if you have to slice bytestrings.
|
Be cautious if you have to `slice bytestrings`_.
|
||||||
See http://docs.python.org/py3k/howto/pyporting.html#bytes-literals
|
|
||||||
|
|
||||||
Different expected strings
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Some method parameters have changed the expected string type of a parameter.
|
|
||||||
For example, ``strftime`` format parameter expects a bytestring on Python 2 but
|
|
||||||
a normal (Unicode) string on Python 3. For these cases, ``django.utils.py3``
|
|
||||||
provides a ``n()`` function which encodes the string parameter only with
|
|
||||||
Python 2.
|
|
||||||
|
|
||||||
>>> from __future__ import unicode_literals
|
|
||||||
>>> from datetime import datetime
|
|
||||||
|
|
||||||
>>> print(datetime.date(2012, 5, 21).strftime(n("%m → %Y")))
|
|
||||||
05 → 2012
|
|
||||||
|
|
||||||
Renamed types
|
|
||||||
=============
|
|
||||||
|
|
||||||
Several types are named differently in Python 2 and Python 3. In order to keep
|
|
||||||
compatibility while using those types, import their corresponding aliases from
|
|
||||||
``django.utils.py3``.
|
|
||||||
|
|
||||||
=========== ========= =====================
|
|
||||||
Python 2 Python 3 django.utils.py3
|
|
||||||
=========== ========= =====================
|
|
||||||
basestring, str, string_types (tuple)
|
|
||||||
unicode str text_type
|
|
||||||
int, long int, integer_types (tuple)
|
|
||||||
long int long_type
|
|
||||||
=========== ========= =====================
|
|
||||||
|
|
||||||
String aliases
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Code sample::
|
|
||||||
|
|
||||||
if isinstance(foo, basestring):
|
|
||||||
print("foo is a string")
|
|
||||||
|
|
||||||
# I want to convert a number to a Unicode string
|
|
||||||
bar = 45
|
|
||||||
bar_string = unicode(bar)
|
|
||||||
|
|
||||||
Should be replaced by::
|
|
||||||
|
|
||||||
from django.utils.py3 import string_types, text_type
|
|
||||||
|
|
||||||
if isinstance(foo, string_types):
|
|
||||||
print("foo is a string")
|
|
||||||
|
|
||||||
# I want to convert a number to a Unicode string
|
|
||||||
bar = 45
|
|
||||||
bar_string = text_type(bar)
|
|
||||||
|
|
||||||
No more long type
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
``long`` and ``int`` types have been unified in Python 3, meaning that ``long``
|
|
||||||
is no longer available. ``django.utils.py3`` provides both ``long_type`` and
|
|
||||||
``integer_types`` aliases. For example:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
# Old Python 2 code
|
|
||||||
my_var = long(333463247234623)
|
|
||||||
if isinstance(my_var, (int, long)):
|
|
||||||
# ...
|
|
||||||
|
|
||||||
Should be replaced by:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from django.utils.py3 import long_type, integer_types
|
|
||||||
|
|
||||||
my_var = long_type(333463247234623)
|
|
||||||
if isinstance(my_var, integer_types):
|
|
||||||
# ...
|
|
||||||
|
|
||||||
|
|
||||||
Changed module locations
|
|
||||||
========================
|
|
||||||
|
|
||||||
The following modules have changed their location in Python 3. Therefore, it is
|
|
||||||
recommended to import them from the ``django.utils.py3`` compatibility layer:
|
|
||||||
|
|
||||||
=============================== ====================================== ======================
|
|
||||||
Python 2 Python3 django.utils.py3
|
|
||||||
=============================== ====================================== ======================
|
|
||||||
Cookie http.cookies cookies
|
|
||||||
|
|
||||||
urlparse.urlparse urllib.parse.urlparse urlparse
|
|
||||||
urlparse.urlunparse urllib.parse.urlunparse urlunparse
|
|
||||||
urlparse.urljoin urllib.parse.urljoin urljoin
|
|
||||||
urlparse.urlsplit urllib.parse.urlsplit urlsplit
|
|
||||||
urlparse.urlunsplit urllib.parse.urlunsplit urlunsplit
|
|
||||||
urlparse.urldefrag urllib.parse.urldefrag urldefrag
|
|
||||||
urlparse.parse_qsl urllib.parse.parse_qsl parse_qsl
|
|
||||||
urllib.quote urllib.parse.quote quote
|
|
||||||
urllib.unquote urllib.parse.unquote unquote
|
|
||||||
urllib.quote_plus urllib.parse.quote_plus quote_plus
|
|
||||||
urllib.unquote_plus urllib.parse.unquote_plus unquote_plus
|
|
||||||
urllib.urlencode urllib.parse.urlencode urlencode
|
|
||||||
urllib.urlopen urllib.request.urlopen urlopen
|
|
||||||
urllib.url2pathname urllib.request.url2pathname url2pathname
|
|
||||||
urllib.urlretrieve urllib.request.urlretrieve urlretrieve
|
|
||||||
urllib2 urllib.request urllib2
|
|
||||||
urllib2.Request urllib.request.Request Request
|
|
||||||
urllib2.OpenerDirector urllib.request.OpenerDirector OpenerDirector
|
|
||||||
urllib2.UnknownHandler urllib.request.UnknownHandler UnknownHandler
|
|
||||||
urllib2.HTTPHandler urllib.request.HTTPHandler HTTPHandler
|
|
||||||
urllib2.HTTPSHandler urllib.request.HTTPSHandler HTTPSHandler
|
|
||||||
urllib2.HTTPDefaultErrorHandler urllib.request.HTTPDefaultErrorHandler HTTPDefaultErrorHandler
|
|
||||||
urllib2.FTPHandler urllib.request.FTPHandler FTPHandler
|
|
||||||
urllib2.HTTPError urllib.request.HTTPError HTTPError
|
|
||||||
urllib2.HTTPErrorProcessor urllib.request.HTTPErrorProcessor HTTPErrorProcessor
|
|
||||||
|
|
||||||
htmlentitydefs.name2codepoint html.entities.name2codepoint name2codepoint
|
|
||||||
HTMLParser html.parser HTMLParser
|
|
||||||
cPickle/pickle pickle pickle
|
|
||||||
thread/dummy_thread _thread/_dummy_thread thread
|
|
||||||
|
|
||||||
os.getcwdu os.getcwd getcwdu
|
|
||||||
itertools.izip zip zip
|
|
||||||
sys.maxint sys.maxsize maxsize
|
|
||||||
unichr chr unichr
|
|
||||||
xrange range xrange
|
|
||||||
=============================== ====================================== ======================
|
|
||||||
|
|
||||||
|
|
||||||
Output encoding now Unicode
|
|
||||||
===========================
|
|
||||||
|
|
||||||
If you want to catch stdout/stderr output, the output content is UTF-8 encoded
|
|
||||||
in Python 2, while it is Unicode strings in Python 3. You can use the OutputIO
|
|
||||||
stream to capture this output::
|
|
||||||
|
|
||||||
from django.utils.py3 import OutputIO
|
|
||||||
|
|
||||||
try:
|
|
||||||
old_stdout = sys.stdout
|
|
||||||
out = OutputIO()
|
|
||||||
sys.stdout = out
|
|
||||||
# Do stuff which produces standard output
|
|
||||||
result = out.getvalue()
|
|
||||||
finally:
|
|
||||||
sys.stdout = old_stdout
|
|
||||||
|
|
||||||
Dict iteritems/itervalues/iterkeys
|
|
||||||
==================================
|
|
||||||
|
|
||||||
The iteritems(), itervalues() and iterkeys() methods of dictionaries do not
|
|
||||||
exist any more in Python 3, simply because they represent the default items()
|
|
||||||
values() and keys() behavior in Python 3. Therefore, to keep compatibility,
|
|
||||||
use similar functions from ``django.utils.py3``::
|
|
||||||
|
|
||||||
from django.utils.py3 import iteritems, itervalues, iterkeys
|
|
||||||
|
|
||||||
my_dict = {'a': 21, 'b': 42}
|
|
||||||
for key, value in iteritems(my_dict):
|
|
||||||
# ...
|
|
||||||
for value in itervalues(my_dict):
|
|
||||||
# ...
|
|
||||||
for key in iterkeys(my_dict):
|
|
||||||
# ...
|
|
||||||
|
|
||||||
Note that in Python 3, dict.keys(), dict.items() and dict.values() return
|
|
||||||
"views" instead of lists. Wrap them into list() if you really need their return
|
|
||||||
values to be in a list.
|
|
||||||
|
|
||||||
http://docs.python.org/release/3.0.1/whatsnew/3.0.html#views-and-iterators-instead-of-lists
|
|
||||||
|
|
||||||
Metaclass
|
|
||||||
=========
|
|
||||||
|
|
||||||
The syntax for declaring metaclasses has changed in Python 3.
|
|
||||||
``django.utils.py3`` offers a compatible way to declare metaclasses::
|
|
||||||
|
|
||||||
from django.utils.py3 import with_metaclass
|
|
||||||
|
|
||||||
class MyClass(with_metaclass(SubClass1, SubClass2,...)):
|
|
||||||
# ...
|
|
||||||
|
|
||||||
Re-raising exceptions
|
|
||||||
=====================
|
|
||||||
|
|
||||||
One of the syntaxes to raise exceptions (raise E, V, T) is gone in Python 3.
|
|
||||||
This is especially used in very specific cases where you want to re-raise a
|
|
||||||
different exception that the initial one, while keeping the original traceback.
|
|
||||||
So, instead of::
|
|
||||||
|
|
||||||
raise Exception, Exception(msg), traceback
|
|
||||||
|
|
||||||
Use::
|
|
||||||
|
|
||||||
from django.utils.py3 import reraise
|
|
||||||
|
|
||||||
reraise(Exception, Exception(msg), traceback)
|
|
||||||
|
|
||||||
|
.. _slice bytestrings: http://docs.python.org/py3k/howto/pyporting.html#bytes-literals
|
||||||
|
|
Loading…
Reference in New Issue