summaryrefslogtreecommitdiff
path: root/docs/topics/python3.txt
diff options
context:
space:
mode:
authorAndrew Godwin <andrew@aeracode.org>2012-07-26 18:58:10 +0100
committerAndrew Godwin <andrew@aeracode.org>2012-07-26 18:58:10 +0100
commit4a2e80fff44d0eb1856b593ac5f31ab1492b3e45 (patch)
tree9bc87a682dc488e6555792c1d4bb53f5f3cdc880 /docs/topics/python3.txt
parent959a3f9791d780062c4efe8765404a8ef95e87f0 (diff)
parentab6cd1c839b136cbc94178da433b2e97ab7f6061 (diff)
Merge branch 'master' of github.com:django/django into schema-alteration
Conflicts: django/db/backends/postgresql_psycopg2/base.py
Diffstat (limited to 'docs/topics/python3.txt')
-rw-r--r--docs/topics/python3.txt283
1 files changed, 84 insertions, 199 deletions
diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt
index 974ddb0e88..3f799edac7 100644
--- a/docs/topics/python3.txt
+++ b/docs/topics/python3.txt
@@ -2,251 +2,136 @@
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. The same code
+runs both on Python 2 (≥ 2.6.5) and Python 3 (≥ 3.2), thanks to the six_
+compatibility layer and ``unicode_literals``.
-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.
+.. _six: http://packages.python.org/six/
-* http://docs.python.org/py3k/howto/pyporting.html
-* http://python3porting.com/
+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`_.
+Rather, it describes guidelines that apply to Django's code and are
+recommended for pluggable apps that run with both Python 2 and 3.
-django.utils.py3
-================
+.. _Python's official porting guide: http://docs.python.org/py3k/howto/pyporting.html
-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.
+Syntax requirements
+===================
-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
+Unicode
+-------
- 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.
+In Python 3, all strings are considered Unicode by default. The ``unicode``
+type from Python 2 is called ``str`` in Python 3, and ``str`` becomes
+``bytes``.
-String handling
-===============
+You mustn't use the ``u`` prefix before a unicode string literal because it's
+a syntax error in Python 3.2. You must prefix byte strings with ``b``.
-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 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::
+Be cautious if you have to `slice bytestrings`_.
- if isinstance(foo, basestring):
- print("foo is a string")
+.. _slice bytestrings: http://docs.python.org/py3k/howto/pyporting.html#bytes-literals
- # I want to convert a number to a Unicode string
- bar = 45
- bar_string = unicode(bar)
+Exceptions
+----------
-Should be replaced by::
+When you capture exceptions, use the ``as`` keyword::
- 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
+ try:
+ ...
+ except MyException as exc:
+ ...
- # Old Python 2 code
- my_var = long(333463247234623)
- if isinstance(my_var, (int, long)):
- # ...
+This older syntax was removed in Python 3::
-Should be replaced by:
+ try:
+ ...
+ except MyException, exc:
+ ...
-.. code-block:: python
+The syntax to reraise an exception with a different traceback also changed.
+Use :func:`six.reraise`.
- from django.utils.py3 import long_type, integer_types
- my_var = long_type(333463247234623)
- if isinstance(my_var, integer_types):
- # ...
+.. module: django.utils.six
+Writing compatible code with six
+================================
-Changed module locations
-========================
+six_ is the canonical compatibility library for supporting Python 2 and 3 in
+a single codebase. Read its documentation!
-The following modules have changed their location in Python 3. Therefore, it is
-recommended to import them from the ``django.utils.py3`` compatibility layer:
+:mod:`six` is bundled with Django: you can import it as :mod:`django.utils.six`.
-=============================== ====================================== ======================
-Python 2 Python3 django.utils.py3
-=============================== ====================================== ======================
-Cookie http.cookies cookies
+Here are the most common changes required to write compatible code.
-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
+String types
+------------
-htmlentitydefs.name2codepoint html.entities.name2codepoint name2codepoint
-HTMLParser html.parser HTMLParser
-cPickle/pickle pickle pickle
-thread/dummy_thread _thread/_dummy_thread thread
+The ``basestring`` and ``unicode`` types were removed in Python 3, and the
+meaning of ``str`` changed. To test these types, use the following idioms::
-os.getcwdu os.getcwd getcwdu
-itertools.izip zip zip
-sys.maxint sys.maxsize maxsize
-unichr chr unichr
-xrange range xrange
-=============================== ====================================== ======================
+ isinstance(myvalue, six.string_types) # replacement for basestring
+ isinstance(myvalue, six.text_type) # replacement for unicode
+ isinstance(myvalue, bytes) # replacement for str
+Python ≥ 2.6 provides ``bytes`` as an alias for ``str``, so you don't need
+:attr:`six.binary_type`.
-Ouptut encoding now Unicode
-===========================
+``long``
+--------
-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::
+The ``long`` type no longer exists in Python 3. ``1L`` is a syntax error. Use
+:data:`six.integer_types` check if a value is an integer or a long::
- from django.utils.py3 import OutputIO
+ isinstance(myvalue, six.integer_types) # replacement for (int, long)
- try:
- old_stdout = sys.stdout
- out = OutputIO()
- sys.stdout = out
- # Do stuff which produces standard output
- result = out.getvalue()
- finally:
- sys.stdout = old_stdout
+``xrange``
+----------
-Dict iteritems/itervalues/iterkeys
-==================================
+Import :func:`six.moves.xrange` wherever you use ``xrange``.
-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``::
+Moved modules
+-------------
- from django.utils.py3 import iteritems, itervalues, iterkeys
+Some modules were renamed in Python 3. The :mod:`django.utils.six.moves
+<six.moves>` module provides a compatible location to import them.
- 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):
- # ...
+In addition to six' defaults, Django's version provides ``dummy_thread`` as
+``_dummy_thread``.
-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
+PY3
+---
-Metaclass
-=========
+If you need different code in Python 2 and Python 3, check :data:`six.PY3`::
-The syntax for declaring metaclasses has changed in Python 3.
-``django.utils.py3`` offers a compatible way to declare metaclasses::
+ if six.PY3:
+ # do stuff Python 3-wise
+ else:
+ # do stuff Python 2-wise
- from django.utils.py3 import with_metaclass
+This is a last resort solution when :mod:`six` doesn't provide an appropriate
+function.
- class MyClass(with_metaclass(SubClass1, SubClass2,...)):
- # ...
+.. module:: django.utils.six
-Re-raising exceptions
+Customizations of six
=====================
-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
+The version of six bundled with Django includes a few additional tools:
- reraise(Exception, Exception(msg), traceback)
+.. function:: iterlists(MultiValueDict)
+ Returns an iterator over the lists of values of a
+ :class:`~django.utils.datastructures.MultiValueDict`. This replaces
+ :meth:`~django.utils.datastructures.MultiValueDict.iterlists()` on Python
+ 2 and :meth:`~django.utils.datastructures.MultiValueDict.lists()` on
+ Python 3.