diff options
| author | Andrew Godwin <andrew@aeracode.org> | 2012-07-26 18:58:10 +0100 |
|---|---|---|
| committer | Andrew Godwin <andrew@aeracode.org> | 2012-07-26 18:58:10 +0100 |
| commit | 4a2e80fff44d0eb1856b593ac5f31ab1492b3e45 (patch) | |
| tree | 9bc87a682dc488e6555792c1d4bb53f5f3cdc880 /docs/topics/python3.txt | |
| parent | 959a3f9791d780062c4efe8765404a8ef95e87f0 (diff) | |
| parent | ab6cd1c839b136cbc94178da433b2e97ab7f6061 (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.txt | 283 |
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. |
