2011-02-03 18:01:54 -04:00
|
|
|
.. _pyporting-howto:
|
|
|
|
|
|
|
|
*********************************
|
|
|
|
Porting Python 2 Code to Python 3
|
|
|
|
*********************************
|
|
|
|
|
|
|
|
:author: Brett Cannon
|
|
|
|
|
|
|
|
.. topic:: Abstract
|
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
With Python 3 being the future of Python while Python 2 is still in active
|
|
|
|
use, it is good to have your project available for both major releases of
|
|
|
|
Python. This guide is meant to help you choose which strategy works best
|
|
|
|
for your project to support both Python 2 & 3 along with how to execute
|
|
|
|
that strategy.
|
2011-02-03 18:01:54 -04:00
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
If you are looking to port an extension module instead of pure Python code,
|
|
|
|
please see http://docs.python.org/py3k/howto/cporting.html .
|
2011-02-03 18:01:54 -04:00
|
|
|
|
|
|
|
|
|
|
|
Choosing a Strategy
|
|
|
|
===================
|
|
|
|
When a project makes the decision that it's time to support both Python 2 & 3,
|
|
|
|
a decision needs to be made as to how to go about accomplishing that goal.
|
|
|
|
Which strategy goes with will depend on how large the project's existing
|
|
|
|
codebase is and how much divergence you want from your Python 2 codebase from
|
|
|
|
your Python 3 one (e.g., starting a new version with Python 3).
|
|
|
|
|
|
|
|
If your project is brand-new or does not have a large codebase, then you may
|
|
|
|
want to consider writing/porting :ref:`all of your code for Python 3
|
|
|
|
and use 3to2 <use_3to2>` to port your code for Python 2.
|
|
|
|
|
|
|
|
If your project has a pre-existing Python 2 codebase and you would like Python
|
|
|
|
3 support to start off a new branch or version of your project, then you will
|
|
|
|
most likely want to :ref:`port using 2to3 <use_2to3>`. This will allow you port
|
|
|
|
your Python 2 code to Python 3 in a semi-automated fashion and begin to
|
|
|
|
maintain it separately from your Python 2 code. This approach can also work if
|
|
|
|
your codebase is small and/or simple enough for the translation to occur
|
|
|
|
quickly.
|
|
|
|
|
|
|
|
Finally, if you want to maintain Python 2 and Python 3 versions of your project
|
|
|
|
simultaneously and with no differences, then you can write :ref:`Python 2/3
|
|
|
|
source-compatible code <use_same_source>`. While the code is not quite as
|
|
|
|
idiomatic as it would be written just for Python 3 or automating the port from
|
|
|
|
Python 2, it does makes it easier to continue to do rapid development
|
|
|
|
regardless of what major version of Python you are developing against at the
|
|
|
|
time.
|
|
|
|
|
|
|
|
Regardless of which approach you choose, porting is probably not as hard or
|
|
|
|
time-consuming as you might initially think. You can also tackle the problem
|
|
|
|
piece-meal as a good portion of porting is simply updating your code to follow
|
|
|
|
current best practices in a Python 2/3 compatible way.
|
|
|
|
|
|
|
|
|
|
|
|
Universal Bits of Advice
|
|
|
|
------------------------
|
|
|
|
Regardless of what strategy you pick, there are a few things you should
|
|
|
|
consider.
|
|
|
|
|
|
|
|
One is make sure you have a robust test suite. You need to make sure everything
|
|
|
|
continues to work, just like when you support a new minor version of Python.
|
|
|
|
This means making sure your test suite is thorough and is ported properly
|
|
|
|
between Python 2 & 3. You will also most likely want to use something like tox_
|
|
|
|
to automate testing between both a Python 2 and Python 3 VM.
|
|
|
|
|
|
|
|
Two, once your project has Python 3 support, make sure to add the proper
|
|
|
|
classifier on the Cheeseshop_ (PyPI_). To have your project listed as Python 3
|
|
|
|
compatible it must have the
|
|
|
|
`Python 3 classifier <http://pypi.python.org/pypi?:action=browse&c=533>`_
|
|
|
|
(from
|
|
|
|
http://techspot.zzzeek.org/2011/01/24/zzzeek-s-guide-to-python-3-porting/)::
|
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
setup(
|
|
|
|
name='Your Library',
|
|
|
|
version='1.0',
|
|
|
|
classifiers=[
|
|
|
|
# make sure to use :: Python *and* :: Python :: 3 so
|
|
|
|
# that pypi can list the package on the python 3 page
|
|
|
|
'Programming Language :: Python',
|
|
|
|
'Programming Language :: Python :: 3'
|
|
|
|
],
|
|
|
|
packages=['yourlibrary'],
|
|
|
|
# make sure to add custom_fixers to the MANIFEST.in
|
|
|
|
include_package_data=True,
|
|
|
|
# ...
|
|
|
|
)
|
2011-02-03 18:01:54 -04:00
|
|
|
|
|
|
|
|
|
|
|
Doing so will cause your project to show up in the
|
|
|
|
`Python 3 packages list
|
|
|
|
<http://pypi.python.org/pypi?:action=browse&c=533&show=all>`_. You will know
|
|
|
|
you set the classifier properly as visiting your project page on the Cheeseshop
|
|
|
|
will show a Python 3 logo in the upper-left corner of the page.
|
|
|
|
|
|
|
|
Three, the six_ project provides a library which helps iron out differences
|
|
|
|
between Python 2 & 3. If you find there is a sticky point that is a continual
|
|
|
|
point of contention in your translation or maintenance of code, consider using
|
|
|
|
a source-compatible solution relying on six. If you have to create your own
|
|
|
|
Python 2/3 compatible solution, you can use ``sys.version_info[0] >= 3`` as a
|
|
|
|
guard.
|
|
|
|
|
|
|
|
Four, read all the approaches. Just because some bit of advice applies to one
|
|
|
|
approach more than another doesn't mean that some advice doesn't apply to other
|
|
|
|
strategies.
|
|
|
|
|
|
|
|
Five, drop support for older Python versions if possible. While not a
|
|
|
|
requirement, `Python 2.5`_) introduced a lot of useful syntax and libraries
|
|
|
|
which have become idiomatic in Python 3. `Python 2.6`_ introduced future
|
|
|
|
statements which makes compatibility much easier if you are going from Python 2
|
|
|
|
to 3.
|
|
|
|
`Python 2.7`_ continues the trend in the stdlib. So choose the newest version
|
|
|
|
of Python for which you believe you believe can be your minimum support version
|
|
|
|
and work from there.
|
|
|
|
|
|
|
|
|
|
|
|
.. _tox: http://codespeak.net/tox/
|
|
|
|
.. _Cheeseshop:
|
|
|
|
.. _PyPI: http://pypi.python.org/
|
|
|
|
.. _six: http://packages.python.org/six
|
|
|
|
.. _Python 2.7: http://www.python.org/2.7.x
|
|
|
|
.. _Python 2.6: http://www.python.org/2.6.x
|
|
|
|
.. _Python 2.5: http://www.python.org/2.5.x
|
|
|
|
.. _Python 2.4: http://www.python.org/2.4.x
|
|
|
|
|
|
|
|
|
|
|
|
.. _use_3to2:
|
|
|
|
|
|
|
|
Python 3 and 3to2
|
|
|
|
=================
|
|
|
|
If you are starting a new project or your codebase is small enough, you may
|
|
|
|
want to consider writing your code for Python 3 and backporting to Python 2
|
|
|
|
using 3to2_. Thanks to Python 3 being more strict about things than Python 2
|
|
|
|
(e.g., bytes vs. strings), the source translation can be easier and more
|
|
|
|
straightforward than from Python 2 to 3. Plus it gives you more direct
|
|
|
|
experience developing in Python 3 which, since it is the future of Python, is a
|
|
|
|
good thing long-term.
|
|
|
|
|
|
|
|
A drawback of this approach is that 3to2 is a third-party project. This means
|
|
|
|
that the Python core developers (and thus this guide) can make no promises
|
|
|
|
about how well 3to2 works at any time. There is nothing to suggest, though,
|
|
|
|
that 3to2 is not a high-quality project.
|
|
|
|
|
|
|
|
|
|
|
|
.. _3to2: https://bitbucket.org/amentajo/lib3to2/overview
|
|
|
|
|
|
|
|
|
|
|
|
.. _use_2to3:
|
|
|
|
|
|
|
|
Python 2 and 2to3
|
|
|
|
=================
|
|
|
|
Included with Python since 2.6, 2to3_ tool (and :mod:`lib2to3` module) helps
|
|
|
|
with porting Python 2 to Python 3 by performing various source translations.
|
|
|
|
This is a perfect solution for projects which wish to branch their Python 3
|
|
|
|
code from their Python 2 codebase and maintain them as independent codebases.
|
|
|
|
You can even begin preparing to use this approach today by writing
|
|
|
|
future-compatible Python code which works cleanly in Python 2 in conjunction
|
|
|
|
with 2to3; all steps outlined below will work with Python 2 code up to the
|
|
|
|
point when the actual use of 2to3 occurs.
|
|
|
|
|
|
|
|
Use of 2to3 as an on-demand translation step at install time is also possible,
|
|
|
|
preventing the need to maintain a separate Python 3 codebase, but this approach
|
|
|
|
does come with some drawbacks. While users will only have to pay the
|
|
|
|
translation cost once at installation, you as a developer will need to pay the
|
|
|
|
cost regularly during development. If your codebase is sufficiently large
|
|
|
|
enough then the translation step ends up acting like a compilation step,
|
|
|
|
robbing you of the rapid development process you are used to with Python.
|
|
|
|
Obviously the time required to translate a project will vary, so do an
|
|
|
|
experimental translation just to see how long it takes to evaluate whether you
|
|
|
|
prefer this approach compared to using :ref:`use_same_source` or simply keeping
|
|
|
|
a separate Python 3 codebase.
|
|
|
|
|
|
|
|
Below are the typical steps taken by a project which uses a 2to3-based approach
|
|
|
|
to supporting Python 2 & 3.
|
|
|
|
|
|
|
|
|
|
|
|
Support Python 2.7
|
|
|
|
------------------
|
|
|
|
As a first step, make sure that your project is compatible with `Python 2.7`_.
|
|
|
|
This is just good to do as Python 2.7 is the last release of Python 2 and thus
|
|
|
|
will be used for a rather long time. It also allows for use of the ``-3`` flag
|
|
|
|
to Python to help discover places in your code which 2to3 cannot handle but are
|
|
|
|
known to cause issues.
|
|
|
|
|
|
|
|
Try to Support Python 2.6 and Newer Only
|
|
|
|
----------------------------------------
|
|
|
|
While not possible for all projects, if you can support `Python 2.6`_ and newer
|
|
|
|
**only**, your life will be much easier. Various future statements, stdlib
|
|
|
|
additions, etc. exist only in Python 2.6 and later which greatly assist in
|
|
|
|
porting to Python 3. But if you project must keep support for `Python 2.5`_ (or
|
|
|
|
even `Python 2.4`_) then it is still possible to port to Python 3.
|
|
|
|
|
|
|
|
Below are the benefits you gain if you only have to support Python 2.6 and
|
|
|
|
newer. Some of these options are personal choice while others are
|
|
|
|
**strongly** recommended (the ones that are more for personal choice are
|
|
|
|
labeled as such). If you continue to support older versions of Python then you
|
|
|
|
at least need to watch out for situations that these solutions fix.
|
|
|
|
|
|
|
|
|
|
|
|
``from __future__ import division``
|
|
|
|
'''''''''''''''''''''''''''''''''''
|
|
|
|
While the exact same outcome can be had by using the ``-Qnew`` argument to
|
|
|
|
Python, using this future statement lifts the requirement that your users use
|
|
|
|
the flag to get the expected behavior of division in Python 3 (e.g., ``1/2 ==
|
|
|
|
0.5; 1//2 == 0``).
|
|
|
|
|
|
|
|
|
|
|
|
``from __future__ import absolute_imports``
|
|
|
|
'''''''''''''''''''''''''''''''''''''''''''
|
|
|
|
Implicit relative imports (e.g., importing ``spam.bacon`` from within
|
|
|
|
``spam.eggs`` with the statement ``import bacon``) does not work in Python 3.
|
|
|
|
This future statement moves away from that and allows the use of explicit
|
|
|
|
relative imports (e.g., ``from . import bacon``).
|
|
|
|
|
|
|
|
|
|
|
|
``from __future__ import print_function``
|
|
|
|
'''''''''''''''''''''''''''''''''''''''''
|
|
|
|
This is a personal choice. 2to3 handles the translation from the print
|
|
|
|
statement to the print function rather well so this is an optional step. This
|
|
|
|
future statement does help, though, with getting used to typing
|
|
|
|
``print('Hello, World')`` instead of ``print 'Hello, World'``.
|
|
|
|
|
|
|
|
|
|
|
|
``from __future__ import unicode_literals``
|
|
|
|
'''''''''''''''''''''''''''''''''''''''''''
|
|
|
|
Another personal choice. You can always mark what you want to be a (unicode)
|
|
|
|
string with a ``u`` prefix to get the same effect. But regardless of whether
|
|
|
|
you use this future statement or not, you **must** make sure you know exactly
|
|
|
|
which Python 2 strings you want to be bytes, and which are to be strings. This
|
|
|
|
means you should, **at minimum** mark all strings that are meant to be text
|
|
|
|
strings with a ``u`` prefix if you do not use this future statement.
|
|
|
|
|
|
|
|
|
|
|
|
Bytes literals
|
|
|
|
''''''''''''''
|
|
|
|
This is a **very** important one. The ability to prefix Python 2 strings that
|
|
|
|
are meant to contain bytes with a ``b`` prefix help to very clearly delineate
|
|
|
|
what is and is not a Python 3 string. When you run 2to3 on code, all Python 2
|
|
|
|
strings become Python 3 strings **unless** they are prefixed with ``b``.
|
|
|
|
|
|
|
|
There are some differences between byte literals in Python 2 and those in
|
|
|
|
Python 3 thanks to the bytes type just being an alias to ``str`` in Python 2.
|
|
|
|
Probably the biggest "gotcha" is that indexing results in different values. In
|
|
|
|
Python 2, the value of ``b'py'[1]`` is ``'y'``, while in Python 3 it's ``121``.
|
|
|
|
You can avoid this disparity by always slicing at the size of a single element:
|
|
|
|
``b'py'[1:2]`` is ``'y'`` in Python 2 and ``b'y'`` in Python 3 (i.e., close
|
|
|
|
enough).
|
|
|
|
|
|
|
|
You cannot concatenate bytes and strings in Python 3. But since in Python
|
|
|
|
2 has bytes aliased to ``str``, it will succeed: ``b'a' + u'b'`` works in
|
|
|
|
Python 2, but ``b'a' + 'b'`` in Python 3 is a :exc:`TypeError`. A similar issue
|
|
|
|
also comes about when doing comparisons between bytes and strings.
|
|
|
|
|
|
|
|
|
|
|
|
:mod:`io` Module
|
|
|
|
''''''''''''''''
|
|
|
|
The built-in ``open()`` function in Python 2 always returns a Python 2 string,
|
|
|
|
not a unicode string. This is problematic as Python 3's :func:`open` returns a
|
|
|
|
string if a file is not opened as binary and bytes if it is.
|
|
|
|
|
|
|
|
To help with compatibility, use :func:`io.open` instead of the built-in
|
|
|
|
``open()``. Since :func:`io.open` is essentially the same function in both
|
|
|
|
Python 2 and Python 3 it will help iron out any issues that might arise.
|
|
|
|
|
|
|
|
|
|
|
|
Handle Common "Gotchas"
|
|
|
|
-----------------------
|
|
|
|
There are a few things that just consistently come up as sticking points for
|
|
|
|
people which 2to3 cannot handle automatically or can easily be done in Python 2
|
|
|
|
to help modernize your code.
|
|
|
|
|
|
|
|
|
|
|
|
Subclass ``object``
|
|
|
|
'''''''''''''''''''
|
|
|
|
New-style classes have been around since Python 2.2. You need to make sure you
|
|
|
|
are subclassing from ``object`` to avoid odd edge cases involving method
|
|
|
|
resolution order, etc. This continues to be totally valid in Python 3 (although
|
|
|
|
unneeded as all classes implicitly inherit from ``object``).
|
|
|
|
|
|
|
|
|
|
|
|
Deal With the Bytes/String Dichotomy
|
|
|
|
''''''''''''''''''''''''''''''''''''
|
|
|
|
One of the biggest issues people have when porting code to Python 3 is handling
|
|
|
|
the bytes/string dichotomy. Because Python 2 allowed the ``str`` type to hold
|
|
|
|
textual data, people have over the years been rather loose in their delineation
|
|
|
|
of what ``str`` instances held text compared to bytes. In Python 3 you cannot
|
|
|
|
be so care-free anymore and need to properly handle the difference. The key
|
|
|
|
handling this issue to to make sure that **every** string literal in your
|
|
|
|
Python 2 code is either syntactically of functionally marked as either bytes or
|
|
|
|
text data. After this is done you then need to make sure your APIs are designed
|
|
|
|
to either handle a specific type or made to be properly polymorphic.
|
|
|
|
|
|
|
|
|
|
|
|
Mark Up Python 2 String Literals
|
|
|
|
********************************
|
|
|
|
|
|
|
|
First thing you must do is designate every single string literal in Python 2
|
|
|
|
as either textual or bytes data. If you are only supporting Python 2.6 or
|
|
|
|
newer, this can be accomplished by marking bytes literals with a ``b`` prefix
|
|
|
|
and then designating textual data with a ``u`` prefix or using the
|
|
|
|
``unicode_literals`` future statement.
|
|
|
|
|
|
|
|
If your project supports versions of Python pre-dating 2.6, then you should use
|
|
|
|
the six_ project and its ``b()`` function to denote bytes literals. For text
|
|
|
|
literals you can either use six's ``u()`` function or use a ``u`` prefix.
|
|
|
|
|
|
|
|
|
|
|
|
Decide what APIs Will Accept
|
|
|
|
****************************
|
|
|
|
In Python 2 it was very easy to accidentally create an API that accepted both
|
|
|
|
bytes and textual data. But in Python 3, thanks to the more strict handling of
|
|
|
|
disparate types, this loose usage of bytes and text together tends to fail.
|
|
|
|
|
|
|
|
Take the dict ``{b'a': 'bytes', u'a': 'text'}`` in Python 2.6. It creates the
|
|
|
|
dict ``{u'a': 'text'}`` since ``b'a' == u'a'``. But in Python 3 the equivalent
|
|
|
|
dict creates ``{b'a': 'bytes', 'a': 'text'}``, i.e., no lost data. Similar
|
|
|
|
issues can crop up when transitioning Python 2 code to Python 3.
|
|
|
|
|
|
|
|
This means you need to choose what an API is going to accept and create and
|
|
|
|
consistently stick to that API in both Python 2 and 3.
|
|
|
|
|
|
|
|
|
|
|
|
``__str__()``/``__unicode__()``
|
|
|
|
'''''''''''''''''''''''''''''''
|
|
|
|
In Python 2, objects can specify both a string and unicode representation of
|
|
|
|
themselves. In Python 3, though, there is only a string representation. This
|
|
|
|
becomes an issue as people can inadvertantly do things in their ``__str__()``
|
|
|
|
methods which have unpredictable results (e.g., infinite recursion if you
|
|
|
|
happen to use the ``unicode(self).encode('utf8')`` idiom as the body of your
|
|
|
|
``__str__()`` method).
|
|
|
|
|
|
|
|
There are two ways to solve this issue. One is to use a custom 2to3 fixer. The
|
|
|
|
blog post at http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/
|
|
|
|
specifies how to do this. That will allow 2to3 to change all instances of ``def
|
|
|
|
__unicode(self): ...`` to ``def __str__(self): ...``. This does require you
|
|
|
|
define your ``__str__()`` method in Python 2 before your ``__unicode__()``
|
|
|
|
method.
|
|
|
|
|
|
|
|
The other option is to use a mixin class. This allows you to only define a
|
|
|
|
``__unicode__()`` method for your class and let the mixin derive
|
|
|
|
``__str__()`` for you (code from
|
|
|
|
http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/)::
|
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
import sys
|
2011-02-03 18:01:54 -04:00
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
class UnicodeMixin(object):
|
2011-02-03 18:01:54 -04:00
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
"""Mixin class to handle defining the proper __str__/__unicode__
|
|
|
|
methods in Python 2 or 3."""
|
2011-02-03 18:01:54 -04:00
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
if sys.version_info[0] >= 3: # Python 3
|
|
|
|
def __str__(self):
|
|
|
|
return self.__unicode__()
|
|
|
|
else: # Python 2
|
|
|
|
def __str__(self):
|
|
|
|
return self.__unicode__().encode('utf8')
|
2011-02-03 18:01:54 -04:00
|
|
|
|
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
class Spam(UnicodeMixin):
|
2011-02-03 18:01:54 -04:00
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
def __unicode__(self):
|
|
|
|
return u'spam-spam-bacon-spam' # 2to3 will remove the 'u' prefix
|
2011-02-03 18:01:54 -04:00
|
|
|
|
|
|
|
|
|
|
|
Specify when opening a file as binary
|
|
|
|
'''''''''''''''''''''''''''''''''''''
|
|
|
|
Unless you have been working on Windows, there is a chance you have not always
|
|
|
|
bothered to add the ``b`` mode when opening a file (e.g., ``
|
|
|
|
|
|
|
|
|
|
|
|
Use :func:``codecs.open()``
|
|
|
|
'''''''''''''''''''''''''''
|
|
|
|
If you are not able to limit your Python 2 compatibility to 2.6 or newer (and
|
|
|
|
thus get to use :func:`io.open`), then you should make sure you use
|
|
|
|
:func:`codecs.open` over the built-in ``open()`` function. This will make sure
|
|
|
|
that you get back unicode strings in Python 2 when reading in text and an
|
|
|
|
instance of ``str`` when dealing with bytes.
|
|
|
|
|
|
|
|
|
|
|
|
Don't Index on Exceptions
|
|
|
|
'''''''''''''''''''''''''
|
|
|
|
In Python 2, the following worked::
|
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
>>> exc = Exception(1, 2, 3)
|
|
|
|
>>> exc.args[1]
|
|
|
|
2
|
|
|
|
>>> exc[1] # Python 2 only!
|
|
|
|
2
|
2011-02-03 18:01:54 -04:00
|
|
|
|
|
|
|
But in Python 3, indexing directly off of an exception is an error. You need to
|
|
|
|
make sure to only index on :attr:`BaseException.args` attribute which is a
|
|
|
|
sequence containing all arguments passed to the :meth:`__init__` method.
|
|
|
|
|
|
|
|
Even better is to use documented attributes the exception provides.
|
|
|
|
|
|
|
|
|
|
|
|
Don't use ``__getslice__`` & Friends
|
|
|
|
''''''''''''''''''''''''''''''''''''
|
|
|
|
Been deprecated for a while, but Python 3 finally drops support for
|
|
|
|
``__getslice__()``, etc. Move completely over to :meth:`__getitem__` and
|
|
|
|
friends.
|
|
|
|
|
|
|
|
|
|
|
|
Stop Using :mod:`doctest`
|
|
|
|
'''''''''''''''''''''''''
|
|
|
|
While 2to3 tries to port doctests properly, it's a rather tough thing to do. It
|
|
|
|
is probably best to simply convert your critical doctests to :mod:`unittest`.
|
|
|
|
|
|
|
|
|
|
|
|
Eliminate ``-3`` Warnings
|
|
|
|
-------------------------
|
|
|
|
When you run your application's test suite, run it using the ``-3`` flag passed
|
|
|
|
to Python. This will cause various warnings to be raised during execution about
|
|
|
|
things that 2to3 cannot handle automatically (e.g., modules that have been
|
|
|
|
removed). Try to eliminate those warnings to make your code even more portable
|
|
|
|
to Python 3.
|
|
|
|
|
|
|
|
|
|
|
|
Run 2to3
|
|
|
|
--------
|
|
|
|
Once you have made your Python 2 code future-compatible with Python 3, it's
|
|
|
|
time to use 2to3_ to actually port your code.
|
|
|
|
|
|
|
|
|
|
|
|
Manually
|
|
|
|
''''''''
|
|
|
|
To manually convert source code using 2to3_, you use the ``2to3`` script that
|
|
|
|
is installed with Python 2.6 and later.::
|
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
2to3 <directory or file to convert>
|
2011-02-03 18:01:54 -04:00
|
|
|
|
|
|
|
This will cause 2to3 to write out a diff with all of the fixers applied for the
|
|
|
|
converted source code. If you would like 2to3 to go ahead and apply the changes
|
|
|
|
you can pass it the ``-w`` flag::
|
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
2to3 -w <stuff to convert>
|
2011-02-03 18:01:54 -04:00
|
|
|
|
|
|
|
There are other flags available to control exactly which fixers are applied,
|
|
|
|
etc.
|
|
|
|
|
|
|
|
|
|
|
|
During Installation
|
|
|
|
'''''''''''''''''''
|
|
|
|
When a user installs your project for Python 3, you can have either
|
|
|
|
:mod:`distutils` or Distribute_ run 2to3_ on your behalf.
|
|
|
|
For distutils, use the following idiom::
|
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
try: # Python 3
|
|
|
|
from distutils.command.build_py import build_py_2to3 as build_py
|
|
|
|
except ImportError: # Python 2
|
|
|
|
from distutils.command.build_py import build_py
|
2011-02-03 18:01:54 -04:00
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
setup(cmdclass = {'build_py':build_py},
|
|
|
|
# ...
|
|
|
|
)
|
2011-02-03 18:01:54 -04:00
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
For Distribute::
|
2011-02-03 18:01:54 -04:00
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
setup(use_2to3=True,
|
|
|
|
# ...
|
|
|
|
)
|
2011-02-03 18:01:54 -04:00
|
|
|
|
|
|
|
This will allow you to not have to distribute a separate Python 3 version of
|
|
|
|
your project. It does require, though, that when you perform development that
|
|
|
|
you at least build your project and use the built Python 3 source for testing.
|
|
|
|
|
|
|
|
|
|
|
|
Verify & Test
|
|
|
|
-------------
|
|
|
|
At this point you should (hopefully) have your project converted in such a way
|
|
|
|
that it works in Python 3. Verify it by running your unit tests and making sure
|
|
|
|
nothing has gone awry. If you miss something then figure out how to fix it in
|
|
|
|
Python 3, backport to your Python 2 code, and run your code through 2to3 again
|
|
|
|
to verify the fix transforms properly.
|
|
|
|
|
|
|
|
|
|
|
|
.. _2to3: http://docs.python.org/py3k/library/2to3.html
|
|
|
|
.. _Distribute: http://packages.python.org/distribute/
|
|
|
|
|
|
|
|
|
|
|
|
.. _use_same_source:
|
|
|
|
|
|
|
|
Python 2/3 Compatible Source
|
|
|
|
============================
|
|
|
|
While it may seem counter-intuitive, you can write Python code which is
|
|
|
|
source-compatible between Python 2 & 3. It does lead to code that is not
|
|
|
|
entirely idiomatic Python (e.g., having to extract the currently raised
|
|
|
|
exception from ``sys.exc_info()[1]``), but it can be run under Python 2
|
|
|
|
**and** Python 3 without using 2to3_ as a translation step. This allows you to
|
|
|
|
continue to have a rapid development process regardless of whether you are
|
|
|
|
developing under Python 2 or Python 3. Whether this approach or using
|
|
|
|
:ref:`use_2to3` works best for you will be a per-project decision.
|
|
|
|
|
|
|
|
To get a complete idea of what issues you will need to deal with, see the
|
|
|
|
`What's New in Python 3.0`_. Others have reorganized the data in other formats
|
|
|
|
such as http://docs.pythonsprints.com/python3_porting/py-porting.html .
|
|
|
|
|
|
|
|
The following are some steps to take to try to support both Python 2 & 3 from
|
|
|
|
the same source code.
|
|
|
|
|
|
|
|
|
|
|
|
.. _What's New in Python 3.0: http://docs.python.org/release/3.0/whatsnew/3.0.html
|
|
|
|
|
|
|
|
|
|
|
|
Follow The Steps for Using 2to3_ (sans 2to3)
|
|
|
|
--------------------------------------------
|
|
|
|
All of the steps outlined in how to
|
|
|
|
:ref:`port Python 2 code with 2to3 <use_2to3>` apply
|
|
|
|
to creating a Python 2/3 codebase. This includes trying only support Python 2.6
|
|
|
|
or newer (the :mod:`__future__` statements work in Python 3 without issue),
|
|
|
|
eliminating warnings that are triggered by ``-3``, etc.
|
|
|
|
|
|
|
|
Essentially you should cover all of the steps short of running 2to3 itself.
|
|
|
|
|
|
|
|
|
|
|
|
Use six_
|
|
|
|
--------
|
|
|
|
The six_ project contains many things to help you write portable Python code.
|
|
|
|
You should make sure to read its documentation from beginning to end and use
|
|
|
|
any and all features it provides. That way you will minimize any mistakes you
|
|
|
|
might make in writing cross-version code.
|
|
|
|
|
|
|
|
|
|
|
|
Capturing the Currently Raised Exception
|
|
|
|
----------------------------------------
|
|
|
|
One change between Python 2 and 3 that will require changing how you code is
|
|
|
|
accessing the currently raised exception. In Python 2 the syntax to access the
|
|
|
|
current exception is::
|
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
try:
|
|
|
|
raise Exception()
|
|
|
|
except Exception, exc:
|
|
|
|
# Current exception is 'exc'
|
|
|
|
pass
|
2011-02-03 18:01:54 -04:00
|
|
|
|
|
|
|
This syntax changed in Python 3 to::
|
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
try:
|
|
|
|
raise Exception()
|
|
|
|
except Exception as exc:
|
|
|
|
# Current exception is 'exc'
|
|
|
|
pass
|
2011-02-03 18:01:54 -04:00
|
|
|
|
|
|
|
Because of this syntax change you must change to capturing the current
|
|
|
|
exception to::
|
|
|
|
|
2011-02-03 18:14:58 -04:00
|
|
|
try:
|
|
|
|
raise Exception()
|
|
|
|
except Exception:
|
|
|
|
import sys
|
|
|
|
exc = sys.exc_info()[1]
|
|
|
|
# Current exception is 'exc'
|
|
|
|
pass
|
2011-02-03 18:01:54 -04:00
|
|
|
|
|
|
|
You can get more information about the raised exception from
|
|
|
|
:func:`sys.exc_info` than simply the current exception instance, but you most
|
|
|
|
likely don't need it. One very key point to understand, though, is **do not
|
|
|
|
save the traceback to a variable without deleting it**! Because tracebacks
|
|
|
|
contain references to the current executing frame you will inadvertently create
|
|
|
|
a circular reference, prevent everything in the frame from being garbage
|
|
|
|
collected. This can be a massive memory leak if you are not careful. Simply
|
|
|
|
index into the returned value from :func:`sys.version_info` instead of
|
|
|
|
assigning the tuple it returns to a variable.
|
|
|
|
|
|
|
|
|
|
|
|
Other Resources
|
|
|
|
===============
|
|
|
|
The authors of the following blogs posts and wiki pages deserve special thanks
|
|
|
|
for making public their tips for porting Python 2 code to Python 3 (and thus
|
|
|
|
helping provide information for this document):
|
|
|
|
|
|
|
|
* http://docs.pythonsprints.com/python3_porting/py-porting.html
|
|
|
|
* http://techspot.zzzeek.org/2011/01/24/zzzeek-s-guide-to-python-3-porting/
|
|
|
|
* http://dabeaz.blogspot.com/2011/01/porting-py65-and-my-superboard-to.html
|
|
|
|
* http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/
|
|
|
|
* http://lucumr.pocoo.org/2010/2/11/porting-to-python-3-a-guide/
|
|
|
|
* http://wiki.python.org/moin/PortingPythonToPy3k
|
|
|
|
|
|
|
|
If you feel there is something missing from this document that should be added,
|
|
|
|
please email the python-porting_ mailing list.
|
|
|
|
|
|
|
|
.. _python-porting: http://mail.python.org/mailman/listinfo/python-porting
|