diff options
| author | Claude Paroz <claude@2xlibre.net> | 2012-06-07 18:36:26 +0200 |
|---|---|---|
| committer | Claude Paroz <claude@2xlibre.net> | 2012-06-07 18:36:53 +0200 |
| commit | 5e6ded2e58597fa324c550bad35a30ee630ce223 (patch) | |
| tree | f3ca5fe9c7a811d0757b8849476f090f08a6359e /docs/topics/python3.txt | |
| parent | 87ff89d12d109066ff355ed267787d32a6b5aeb7 (diff) | |
Fixed #18363 -- Added Python 3 compatibility layer.
Thanks Vinay Sajip for the support of his django3 branch
and Alex Gaynor, kezabelle, YorikSar for the review.
Diffstat (limited to 'docs/topics/python3.txt')
| -rw-r--r-- | docs/topics/python3.txt | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt new file mode 100644 index 0000000000..974ddb0e88 --- /dev/null +++ b/docs/topics/python3.txt @@ -0,0 +1,252 @@ +====================== +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*). + +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. + +* http://docs.python.org/py3k/howto/pyporting.html +* http://python3porting.com/ + +django.utils.py3 +================ + +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. + +PY3 +--- + +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 +=============== + +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:: + + 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 +=============================== ====================================== ====================== + + +Ouptut 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) + |
