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
|
||||
======================
|
||||
|
||||
Django 1.5 introduces a compatibility layer that allows the code to be run both
|
||||
in Python 2 (2.6/2.7) and Python 3 (>= 3.2) (*work in progress*).
|
||||
Django 1.5 is the first version of Django to support Python 3.
|
||||
|
||||
This document is not meant as a complete Python 2 to Python 3 migration guide.
|
||||
There are many existing resources you can read. But we describe some utilities
|
||||
and guidelines that we recommend you should use when you want to ensure your
|
||||
code can be run with both Python 2 and 3.
|
||||
The same code runs both on Python 2 (≥2.6.5) and Python 3 (≥3.2). To
|
||||
achieve this:
|
||||
|
||||
* http://docs.python.org/py3k/howto/pyporting.html
|
||||
* http://python3porting.com/
|
||||
- wherever possible, Django uses the six_ compatibility layer,
|
||||
- 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
|
||||
Python 2 and Python 3, you can import it from ``django.utils.py3`` where it
|
||||
will be automatically converted depending on your current Python version.
|
||||
Read the documentation of six_. It's the canonical compatibility library for
|
||||
supporting Python 2 and 3 in a single codebase.
|
||||
|
||||
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
|
||||
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
|
||||
have to be prefixed with the letter 'b'. To mimic the same behaviour in Python 2,
|
||||
we recommend you import ``unicode_literals`` from the ``__future__`` library::
|
||||
In Python 3, all strings are considered Unicode strings by default. Byte
|
||||
strings must be prefixed with the letter ``b``. In order to enable the same
|
||||
behavior in Python 2, every module must import ``unicode_literals`` from
|
||||
``__future__``::
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
my_string = "This is an unicode literal"
|
||||
my_bytestring = b"This is a bytestring"
|
||||
|
||||
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)
|
||||
Be cautious if you have to `slice bytestrings`_.
|
||||
|
||||
.. _slice bytestrings: http://docs.python.org/py3k/howto/pyporting.html#bytes-literals
|
||||
|
|
Loading…
Reference in New Issue