17db96d56Sopenharmony_ci.. _pyporting-howto:
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ci*********************************
47db96d56Sopenharmony_ciPorting Python 2 Code to Python 3
57db96d56Sopenharmony_ci*********************************
67db96d56Sopenharmony_ci
77db96d56Sopenharmony_ci:author: Brett Cannon
87db96d56Sopenharmony_ci
97db96d56Sopenharmony_ci.. topic:: Abstract
107db96d56Sopenharmony_ci
117db96d56Sopenharmony_ci   With Python 3 being the future of Python while Python 2 is still in active
127db96d56Sopenharmony_ci   use, it is good to have your project available for both major releases of
137db96d56Sopenharmony_ci   Python. This guide is meant to help you figure out how best to support both
147db96d56Sopenharmony_ci   Python 2 & 3 simultaneously.
157db96d56Sopenharmony_ci
167db96d56Sopenharmony_ci   If you are looking to port an extension module instead of pure Python code,
177db96d56Sopenharmony_ci   please see :ref:`cporting-howto`.
187db96d56Sopenharmony_ci
197db96d56Sopenharmony_ci   If you would like to read one core Python developer's take on why Python 3
207db96d56Sopenharmony_ci   came into existence, you can read Nick Coghlan's `Python 3 Q & A`_ or
217db96d56Sopenharmony_ci   Brett Cannon's `Why Python 3 exists`_.
227db96d56Sopenharmony_ci
237db96d56Sopenharmony_ci
247db96d56Sopenharmony_ci   For help with porting, you can view the archived python-porting_ mailing list.
257db96d56Sopenharmony_ci
267db96d56Sopenharmony_ciThe Short Explanation
277db96d56Sopenharmony_ci=====================
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ciTo make your project be single-source Python 2/3 compatible, the basic steps
307db96d56Sopenharmony_ciare:
317db96d56Sopenharmony_ci
327db96d56Sopenharmony_ci#. Only worry about supporting Python 2.7
337db96d56Sopenharmony_ci#. Make sure you have good test coverage (coverage.py_ can help;
347db96d56Sopenharmony_ci   ``python -m pip install coverage``)
357db96d56Sopenharmony_ci#. Learn the differences between Python 2 & 3
367db96d56Sopenharmony_ci#. Use Futurize_ (or Modernize_) to update your code (e.g. ``python -m pip install future``)
377db96d56Sopenharmony_ci#. Use Pylint_ to help make sure you don't regress on your Python 3 support
387db96d56Sopenharmony_ci   (``python -m pip install pylint``)
397db96d56Sopenharmony_ci#. Use caniusepython3_ to find out which of your dependencies are blocking your
407db96d56Sopenharmony_ci   use of Python 3 (``python -m pip install caniusepython3``)
417db96d56Sopenharmony_ci#. Once your dependencies are no longer blocking you, use continuous integration
427db96d56Sopenharmony_ci   to make sure you stay compatible with Python 2 & 3 (tox_ can help test
437db96d56Sopenharmony_ci   against multiple versions of Python; ``python -m pip install tox``)
447db96d56Sopenharmony_ci#. Consider using optional static type checking to make sure your type usage
457db96d56Sopenharmony_ci   works in both Python 2 & 3 (e.g. use mypy_ to check your typing under both
467db96d56Sopenharmony_ci   Python 2 & Python 3; ``python -m pip install mypy``).
477db96d56Sopenharmony_ci
487db96d56Sopenharmony_ci.. note::
497db96d56Sopenharmony_ci
507db96d56Sopenharmony_ci   Note: Using ``python -m pip install`` guarantees that the ``pip`` you invoke
517db96d56Sopenharmony_ci   is the one installed for the Python currently in use, whether it be
527db96d56Sopenharmony_ci   a system-wide ``pip`` or one installed within a
537db96d56Sopenharmony_ci   :ref:`virtual environment <tut-venv>`.
547db96d56Sopenharmony_ci
557db96d56Sopenharmony_ciDetails
567db96d56Sopenharmony_ci=======
577db96d56Sopenharmony_ci
587db96d56Sopenharmony_ciA key point about supporting Python 2 & 3 simultaneously is that you can start
597db96d56Sopenharmony_ci**today**! Even if your dependencies are not supporting Python 3 yet that does
607db96d56Sopenharmony_cinot mean you can't modernize your code **now** to support Python 3. Most changes
617db96d56Sopenharmony_cirequired to support Python 3 lead to cleaner code using newer practices even in
627db96d56Sopenharmony_ciPython 2 code.
637db96d56Sopenharmony_ci
647db96d56Sopenharmony_ciAnother key point is that modernizing your Python 2 code to also support
657db96d56Sopenharmony_ciPython 3 is largely automated for you. While you might have to make some API
667db96d56Sopenharmony_cidecisions thanks to Python 3 clarifying text data versus binary data, the
677db96d56Sopenharmony_cilower-level work is now mostly done for you and thus can at least benefit from
687db96d56Sopenharmony_cithe automated changes immediately.
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_ciKeep those key points in mind while you read on about the details of porting
717db96d56Sopenharmony_ciyour code to support Python 2 & 3 simultaneously.
727db96d56Sopenharmony_ci
737db96d56Sopenharmony_ci
747db96d56Sopenharmony_ciDrop support for Python 2.6 and older
757db96d56Sopenharmony_ci-------------------------------------
767db96d56Sopenharmony_ci
777db96d56Sopenharmony_ciWhile you can make Python 2.5 work with Python 3, it is **much** easier if you
787db96d56Sopenharmony_cionly have to work with Python 2.7. If dropping Python 2.5 is not an
797db96d56Sopenharmony_cioption then the six_ project can help you support Python 2.5 & 3 simultaneously
807db96d56Sopenharmony_ci(``python -m pip install six``). Do realize, though, that nearly all the projects listed
817db96d56Sopenharmony_ciin this HOWTO will not be available to you.
827db96d56Sopenharmony_ci
837db96d56Sopenharmony_ciIf you are able to skip Python 2.5 and older, then the required changes
847db96d56Sopenharmony_cito your code should continue to look and feel like idiomatic Python code. At
857db96d56Sopenharmony_ciworst you will have to use a function instead of a method in some instances or
867db96d56Sopenharmony_cihave to import a function instead of using a built-in one, but otherwise the
877db96d56Sopenharmony_cioverall transformation should not feel foreign to you.
887db96d56Sopenharmony_ci
897db96d56Sopenharmony_ciBut you should aim for only supporting Python 2.7. Python 2.6 is no longer
907db96d56Sopenharmony_cifreely supported and thus is not receiving bugfixes. This means **you** will have
917db96d56Sopenharmony_cito work around any issues you come across with Python 2.6. There are also some
927db96d56Sopenharmony_citools mentioned in this HOWTO which do not support Python 2.6 (e.g., Pylint_),
937db96d56Sopenharmony_ciand this will become more commonplace as time goes on. It will simply be easier
947db96d56Sopenharmony_cifor you if you only support the versions of Python that you have to support.
957db96d56Sopenharmony_ci
967db96d56Sopenharmony_ci
977db96d56Sopenharmony_ciMake sure you specify the proper version support in your ``setup.py`` file
987db96d56Sopenharmony_ci--------------------------------------------------------------------------
997db96d56Sopenharmony_ci
1007db96d56Sopenharmony_ciIn your ``setup.py`` file you should have the proper `trove classifier`_
1017db96d56Sopenharmony_cispecifying what versions of Python you support. As your project does not support
1027db96d56Sopenharmony_ciPython 3 yet you should at least have
1037db96d56Sopenharmony_ci``Programming Language :: Python :: 2 :: Only`` specified. Ideally you should
1047db96d56Sopenharmony_cialso specify each major/minor version of Python that you do support, e.g.
1057db96d56Sopenharmony_ci``Programming Language :: Python :: 2.7``.
1067db96d56Sopenharmony_ci
1077db96d56Sopenharmony_ci
1087db96d56Sopenharmony_ciHave good test coverage
1097db96d56Sopenharmony_ci-----------------------
1107db96d56Sopenharmony_ci
1117db96d56Sopenharmony_ciOnce you have your code supporting the oldest version of Python 2 you want it
1127db96d56Sopenharmony_cito, you will want to make sure your test suite has good coverage. A good rule of
1137db96d56Sopenharmony_cithumb is that if you want to be confident enough in your test suite that any
1147db96d56Sopenharmony_cifailures that appear after having tools rewrite your code are actual bugs in the
1157db96d56Sopenharmony_citools and not in your code. If you want a number to aim for, try to get over 80%
1167db96d56Sopenharmony_cicoverage (and don't feel bad if you find it hard to get better than 90%
1177db96d56Sopenharmony_cicoverage). If you don't already have a tool to measure test coverage then
1187db96d56Sopenharmony_cicoverage.py_ is recommended.
1197db96d56Sopenharmony_ci
1207db96d56Sopenharmony_ci
1217db96d56Sopenharmony_ciLearn the differences between Python 2 & 3
1227db96d56Sopenharmony_ci-------------------------------------------
1237db96d56Sopenharmony_ci
1247db96d56Sopenharmony_ciOnce you have your code well-tested you are ready to begin porting your code to
1257db96d56Sopenharmony_ciPython 3! But to fully understand how your code is going to change and what
1267db96d56Sopenharmony_ciyou want to look out for while you code, you will want to learn what changes
1277db96d56Sopenharmony_ciPython 3 makes in terms of Python 2. Typically the two best ways of doing that
1287db96d56Sopenharmony_ciis reading the :ref:`"What's New" <whatsnew-index>` doc for each release of Python 3 and the
1297db96d56Sopenharmony_ci`Porting to Python 3`_ book (which is free online). There is also a handy
1307db96d56Sopenharmony_ci`cheat sheet`_ from the Python-Future project.
1317db96d56Sopenharmony_ci
1327db96d56Sopenharmony_ci
1337db96d56Sopenharmony_ciUpdate your code
1347db96d56Sopenharmony_ci----------------
1357db96d56Sopenharmony_ci
1367db96d56Sopenharmony_ciOnce you feel like you know what is different in Python 3 compared to Python 2,
1377db96d56Sopenharmony_ciit's time to update your code! You have a choice between two tools in porting
1387db96d56Sopenharmony_ciyour code automatically: Futurize_ and Modernize_. Which tool you choose will
1397db96d56Sopenharmony_cidepend on how much like Python 3 you want your code to be. Futurize_ does its
1407db96d56Sopenharmony_cibest to make Python 3 idioms and practices exist in Python 2, e.g. backporting
1417db96d56Sopenharmony_cithe ``bytes`` type from Python 3 so that you have semantic parity between the
1427db96d56Sopenharmony_cimajor versions of Python. Modernize_,
1437db96d56Sopenharmony_cion the other hand, is more conservative and targets a Python 2/3 subset of
1447db96d56Sopenharmony_ciPython, directly relying on six_ to help provide compatibility. As Python 3 is
1457db96d56Sopenharmony_cithe future, it might be best to consider Futurize to begin adjusting to any new
1467db96d56Sopenharmony_cipractices that Python 3 introduces which you are not accustomed to yet.
1477db96d56Sopenharmony_ci
1487db96d56Sopenharmony_ciRegardless of which tool you choose, they will update your code to run under
1497db96d56Sopenharmony_ciPython 3 while staying compatible with the version of Python 2 you started with.
1507db96d56Sopenharmony_ciDepending on how conservative you want to be, you may want to run the tool over
1517db96d56Sopenharmony_ciyour test suite first and visually inspect the diff to make sure the
1527db96d56Sopenharmony_citransformation is accurate. After you have transformed your test suite and
1537db96d56Sopenharmony_civerified that all the tests still pass as expected, then you can transform your
1547db96d56Sopenharmony_ciapplication code knowing that any tests which fail is a translation failure.
1557db96d56Sopenharmony_ci
1567db96d56Sopenharmony_ciUnfortunately the tools can't automate everything to make your code work under
1577db96d56Sopenharmony_ciPython 3 and so there are a handful of things you will need to update manually
1587db96d56Sopenharmony_cito get full Python 3 support (which of these steps are necessary vary between
1597db96d56Sopenharmony_cithe tools). Read the documentation for the tool you choose to use to see what it
1607db96d56Sopenharmony_cifixes by default and what it can do optionally to know what will (not) be fixed
1617db96d56Sopenharmony_cifor you and what you may have to fix on your own (e.g. using ``io.open()`` over
1627db96d56Sopenharmony_cithe built-in ``open()`` function is off by default in Modernize). Luckily,
1637db96d56Sopenharmony_cithough, there are only a couple of things to watch out for which can be
1647db96d56Sopenharmony_ciconsidered large issues that may be hard to debug if not watched for.
1657db96d56Sopenharmony_ci
1667db96d56Sopenharmony_ci
1677db96d56Sopenharmony_ciDivision
1687db96d56Sopenharmony_ci++++++++
1697db96d56Sopenharmony_ci
1707db96d56Sopenharmony_ciIn Python 3, ``5 / 2 == 2.5`` and not ``2``; all division between ``int`` values
1717db96d56Sopenharmony_ciresult in a ``float``. This change has actually been planned since Python 2.2
1727db96d56Sopenharmony_ciwhich was released in 2002. Since then users have been encouraged to add
1737db96d56Sopenharmony_ci``from __future__ import division`` to any and all files which use the ``/`` and
1747db96d56Sopenharmony_ci``//`` operators or to be running the interpreter with the ``-Q`` flag. If you
1757db96d56Sopenharmony_cihave not been doing this then you will need to go through your code and do two
1767db96d56Sopenharmony_cithings:
1777db96d56Sopenharmony_ci
1787db96d56Sopenharmony_ci#. Add ``from __future__ import division`` to your files
1797db96d56Sopenharmony_ci#. Update any division operator as necessary to either use ``//`` to use floor
1807db96d56Sopenharmony_ci   division or continue using ``/`` and expect a float
1817db96d56Sopenharmony_ci
1827db96d56Sopenharmony_ciThe reason that ``/`` isn't simply translated to ``//`` automatically is that if
1837db96d56Sopenharmony_cian object defines a ``__truediv__`` method but not ``__floordiv__`` then your
1847db96d56Sopenharmony_cicode would begin to fail (e.g. a user-defined class that uses ``/`` to
1857db96d56Sopenharmony_cisignify some operation but not ``//`` for the same thing or at all).
1867db96d56Sopenharmony_ci
1877db96d56Sopenharmony_ci
1887db96d56Sopenharmony_ciText versus binary data
1897db96d56Sopenharmony_ci+++++++++++++++++++++++
1907db96d56Sopenharmony_ci
1917db96d56Sopenharmony_ciIn Python 2 you could use the ``str`` type for both text and binary data.
1927db96d56Sopenharmony_ciUnfortunately this confluence of two different concepts could lead to brittle
1937db96d56Sopenharmony_cicode which sometimes worked for either kind of data, sometimes not. It also
1947db96d56Sopenharmony_cicould lead to confusing APIs if people didn't explicitly state that something
1957db96d56Sopenharmony_cithat accepted ``str`` accepted either text or binary data instead of one
1967db96d56Sopenharmony_cispecific type. This complicated the situation especially for anyone supporting
1977db96d56Sopenharmony_cimultiple languages as APIs wouldn't bother explicitly supporting ``unicode``
1987db96d56Sopenharmony_ciwhen they claimed text data support.
1997db96d56Sopenharmony_ci
2007db96d56Sopenharmony_ciTo make the distinction between text and binary data clearer and more
2017db96d56Sopenharmony_cipronounced, Python 3 did what most languages created in the age of the internet
2027db96d56Sopenharmony_cihave done and made text and binary data distinct types that cannot blindly be
2037db96d56Sopenharmony_cimixed together (Python predates widespread access to the internet). For any code
2047db96d56Sopenharmony_cithat deals only with text or only binary data, this separation doesn't pose an
2057db96d56Sopenharmony_ciissue. But for code that has to deal with both, it does mean you might have to
2067db96d56Sopenharmony_cinow care about when you are using text compared to binary data, which is why
2077db96d56Sopenharmony_cithis cannot be entirely automated.
2087db96d56Sopenharmony_ci
2097db96d56Sopenharmony_ciTo start, you will need to decide which APIs take text and which take binary
2107db96d56Sopenharmony_ci(it is **highly** recommended you don't design APIs that can take both due to
2117db96d56Sopenharmony_cithe difficulty of keeping the code working; as stated earlier it is difficult to
2127db96d56Sopenharmony_cido well). In Python 2 this means making sure the APIs that take text can work
2137db96d56Sopenharmony_ciwith ``unicode`` and those that work with binary data work with the
2147db96d56Sopenharmony_ci``bytes`` type from Python 3 (which is a subset of ``str`` in Python 2 and acts
2157db96d56Sopenharmony_cias an alias for ``bytes`` type in Python 2). Usually the biggest issue is
2167db96d56Sopenharmony_cirealizing which methods exist on which types in Python 2 & 3 simultaneously
2177db96d56Sopenharmony_ci(for text that's ``unicode`` in Python 2 and ``str`` in Python 3, for binary
2187db96d56Sopenharmony_cithat's ``str``/``bytes`` in Python 2 and ``bytes`` in Python 3). The following
2197db96d56Sopenharmony_citable lists the **unique** methods of each data type across Python 2 & 3
2207db96d56Sopenharmony_ci(e.g., the ``decode()`` method is usable on the equivalent binary data type in
2217db96d56Sopenharmony_cieither Python 2 or 3, but it can't be used by the textual data type consistently
2227db96d56Sopenharmony_cibetween Python 2 and 3 because ``str`` in Python 3 doesn't have the method). Do
2237db96d56Sopenharmony_cinote that as of Python 3.5 the ``__mod__`` method was added to the bytes type.
2247db96d56Sopenharmony_ci
2257db96d56Sopenharmony_ci======================== =====================
2267db96d56Sopenharmony_ci**Text data**            **Binary data**
2277db96d56Sopenharmony_ci------------------------ ---------------------
2287db96d56Sopenharmony_ci\                        decode
2297db96d56Sopenharmony_ci------------------------ ---------------------
2307db96d56Sopenharmony_ciencode
2317db96d56Sopenharmony_ci------------------------ ---------------------
2327db96d56Sopenharmony_ciformat
2337db96d56Sopenharmony_ci------------------------ ---------------------
2347db96d56Sopenharmony_ciisdecimal
2357db96d56Sopenharmony_ci------------------------ ---------------------
2367db96d56Sopenharmony_ciisnumeric
2377db96d56Sopenharmony_ci======================== =====================
2387db96d56Sopenharmony_ci
2397db96d56Sopenharmony_ciMaking the distinction easier to handle can be accomplished by encoding and
2407db96d56Sopenharmony_cidecoding between binary data and text at the edge of your code. This means that
2417db96d56Sopenharmony_ciwhen you receive text in binary data, you should immediately decode it. And if
2427db96d56Sopenharmony_ciyour code needs to send text as binary data then encode it as late as possible.
2437db96d56Sopenharmony_ciThis allows your code to work with only text internally and thus eliminates
2447db96d56Sopenharmony_cihaving to keep track of what type of data you are working with.
2457db96d56Sopenharmony_ci
2467db96d56Sopenharmony_ciThe next issue is making sure you know whether the string literals in your code
2477db96d56Sopenharmony_cirepresent text or binary data. You should add a ``b`` prefix to any
2487db96d56Sopenharmony_ciliteral that presents binary data. For text you should add a ``u`` prefix to
2497db96d56Sopenharmony_cithe text literal. (there is a :mod:`__future__` import to force all unspecified
2507db96d56Sopenharmony_ciliterals to be Unicode, but usage has shown it isn't as effective as adding a
2517db96d56Sopenharmony_ci``b`` or ``u`` prefix to all literals explicitly)
2527db96d56Sopenharmony_ci
2537db96d56Sopenharmony_ciAs part of this dichotomy you also need to be careful about opening files.
2547db96d56Sopenharmony_ciUnless you have been working on Windows, there is a chance you have not always
2557db96d56Sopenharmony_cibothered to add the ``b`` mode when opening a binary file (e.g., ``rb`` for
2567db96d56Sopenharmony_cibinary reading).  Under Python 3, binary files and text files are clearly
2577db96d56Sopenharmony_cidistinct and mutually incompatible; see the :mod:`io` module for details.
2587db96d56Sopenharmony_ciTherefore, you **must** make a decision of whether a file will be used for
2597db96d56Sopenharmony_cibinary access (allowing binary data to be read and/or written) or textual access
2607db96d56Sopenharmony_ci(allowing text data to be read and/or written). You should also use :func:`io.open`
2617db96d56Sopenharmony_cifor opening files instead of the built-in :func:`open` function as the :mod:`io`
2627db96d56Sopenharmony_cimodule is consistent from Python 2 to 3 while the built-in :func:`open` function
2637db96d56Sopenharmony_ciis not (in Python 3 it's actually :func:`io.open`). Do not bother with the
2647db96d56Sopenharmony_cioutdated practice of using :func:`codecs.open` as that's only necessary for
2657db96d56Sopenharmony_cikeeping compatibility with Python 2.5.
2667db96d56Sopenharmony_ci
2677db96d56Sopenharmony_ciThe constructors of both ``str`` and ``bytes`` have different semantics for the
2687db96d56Sopenharmony_cisame arguments between Python 2 & 3. Passing an integer to ``bytes`` in Python 2
2697db96d56Sopenharmony_ciwill give you the string representation of the integer: ``bytes(3) == '3'``.
2707db96d56Sopenharmony_ciBut in Python 3, an integer argument to ``bytes`` will give you a bytes object
2717db96d56Sopenharmony_cias long as the integer specified, filled with null bytes:
2727db96d56Sopenharmony_ci``bytes(3) == b'\x00\x00\x00'``. A similar worry is necessary when passing a
2737db96d56Sopenharmony_cibytes object to ``str``. In Python 2 you just get the bytes object back:
2747db96d56Sopenharmony_ci``str(b'3') == b'3'``. But in Python 3 you get the string representation of the
2757db96d56Sopenharmony_cibytes object: ``str(b'3') == "b'3'"``.
2767db96d56Sopenharmony_ci
2777db96d56Sopenharmony_ciFinally, the indexing of binary data requires careful handling (slicing does
2787db96d56Sopenharmony_ci**not** require any special handling). In Python 2,
2797db96d56Sopenharmony_ci``b'123'[1] == b'2'`` while in Python 3 ``b'123'[1] == 50``. Because binary data
2807db96d56Sopenharmony_ciis simply a collection of binary numbers, Python 3 returns the integer value for
2817db96d56Sopenharmony_cithe byte you index on. But in Python 2 because ``bytes == str``, indexing
2827db96d56Sopenharmony_cireturns a one-item slice of bytes. The six_ project has a function
2837db96d56Sopenharmony_cinamed ``six.indexbytes()`` which will return an integer like in Python 3:
2847db96d56Sopenharmony_ci``six.indexbytes(b'123', 1)``.
2857db96d56Sopenharmony_ci
2867db96d56Sopenharmony_ciTo summarize:
2877db96d56Sopenharmony_ci
2887db96d56Sopenharmony_ci#. Decide which of your APIs take text and which take binary data
2897db96d56Sopenharmony_ci#. Make sure that your code that works with text also works with ``unicode`` and
2907db96d56Sopenharmony_ci   code for binary data works with ``bytes`` in Python 2 (see the table above
2917db96d56Sopenharmony_ci   for what methods you cannot use for each type)
2927db96d56Sopenharmony_ci#. Mark all binary literals with a ``b`` prefix, textual literals with a ``u``
2937db96d56Sopenharmony_ci   prefix
2947db96d56Sopenharmony_ci#. Decode binary data to text as soon as possible, encode text as binary data as
2957db96d56Sopenharmony_ci   late as possible
2967db96d56Sopenharmony_ci#. Open files using :func:`io.open` and make sure to specify the ``b`` mode when
2977db96d56Sopenharmony_ci   appropriate
2987db96d56Sopenharmony_ci#. Be careful when indexing into binary data
2997db96d56Sopenharmony_ci
3007db96d56Sopenharmony_ci
3017db96d56Sopenharmony_ciUse feature detection instead of version detection
3027db96d56Sopenharmony_ci++++++++++++++++++++++++++++++++++++++++++++++++++
3037db96d56Sopenharmony_ci
3047db96d56Sopenharmony_ciInevitably you will have code that has to choose what to do based on what
3057db96d56Sopenharmony_civersion of Python is running. The best way to do this is with feature detection
3067db96d56Sopenharmony_ciof whether the version of Python you're running under supports what you need.
3077db96d56Sopenharmony_ciIf for some reason that doesn't work then you should make the version check be
3087db96d56Sopenharmony_ciagainst Python 2 and not Python 3. To help explain this, let's look at an
3097db96d56Sopenharmony_ciexample.
3107db96d56Sopenharmony_ci
3117db96d56Sopenharmony_ciLet's pretend that you need access to a feature of :mod:`importlib` that
3127db96d56Sopenharmony_ciis available in Python's standard library since Python 3.3 and available for
3137db96d56Sopenharmony_ciPython 2 through importlib2_ on PyPI. You might be tempted to write code to
3147db96d56Sopenharmony_ciaccess e.g. the :mod:`importlib.abc` module by doing the following::
3157db96d56Sopenharmony_ci
3167db96d56Sopenharmony_ci  import sys
3177db96d56Sopenharmony_ci
3187db96d56Sopenharmony_ci  if sys.version_info[0] == 3:
3197db96d56Sopenharmony_ci      from importlib import abc
3207db96d56Sopenharmony_ci  else:
3217db96d56Sopenharmony_ci      from importlib2 import abc
3227db96d56Sopenharmony_ci
3237db96d56Sopenharmony_ciThe problem with this code is what happens when Python 4 comes out? It would
3247db96d56Sopenharmony_cibe better to treat Python 2 as the exceptional case instead of Python 3 and
3257db96d56Sopenharmony_ciassume that future Python versions will be more compatible with Python 3 than
3267db96d56Sopenharmony_ciPython 2::
3277db96d56Sopenharmony_ci
3287db96d56Sopenharmony_ci  import sys
3297db96d56Sopenharmony_ci
3307db96d56Sopenharmony_ci  if sys.version_info[0] > 2:
3317db96d56Sopenharmony_ci      from importlib import abc
3327db96d56Sopenharmony_ci  else:
3337db96d56Sopenharmony_ci      from importlib2 import abc
3347db96d56Sopenharmony_ci
3357db96d56Sopenharmony_ciThe best solution, though, is to do no version detection at all and instead rely
3367db96d56Sopenharmony_cion feature detection. That avoids any potential issues of getting the version
3377db96d56Sopenharmony_cidetection wrong and helps keep you future-compatible::
3387db96d56Sopenharmony_ci
3397db96d56Sopenharmony_ci  try:
3407db96d56Sopenharmony_ci      from importlib import abc
3417db96d56Sopenharmony_ci  except ImportError:
3427db96d56Sopenharmony_ci      from importlib2 import abc
3437db96d56Sopenharmony_ci
3447db96d56Sopenharmony_ci
3457db96d56Sopenharmony_ciPrevent compatibility regressions
3467db96d56Sopenharmony_ci---------------------------------
3477db96d56Sopenharmony_ci
3487db96d56Sopenharmony_ciOnce you have fully translated your code to be compatible with Python 3, you
3497db96d56Sopenharmony_ciwill want to make sure your code doesn't regress and stop working under
3507db96d56Sopenharmony_ciPython 3. This is especially true if you have a dependency which is blocking you
3517db96d56Sopenharmony_cifrom actually running under Python 3 at the moment.
3527db96d56Sopenharmony_ci
3537db96d56Sopenharmony_ciTo help with staying compatible, any new modules you create should have
3547db96d56Sopenharmony_ciat least the following block of code at the top of it::
3557db96d56Sopenharmony_ci
3567db96d56Sopenharmony_ci    from __future__ import absolute_import
3577db96d56Sopenharmony_ci    from __future__ import division
3587db96d56Sopenharmony_ci    from __future__ import print_function
3597db96d56Sopenharmony_ci
3607db96d56Sopenharmony_ciYou can also run Python 2 with the ``-3`` flag to be warned about various
3617db96d56Sopenharmony_cicompatibility issues your code triggers during execution. If you turn warnings
3627db96d56Sopenharmony_ciinto errors with ``-Werror`` then you can make sure that you don't accidentally
3637db96d56Sopenharmony_cimiss a warning.
3647db96d56Sopenharmony_ci
3657db96d56Sopenharmony_ciYou can also use the Pylint_ project and its ``--py3k`` flag to lint your code
3667db96d56Sopenharmony_cito receive warnings when your code begins to deviate from Python 3
3677db96d56Sopenharmony_cicompatibility. This also prevents you from having to run Modernize_ or Futurize_
3687db96d56Sopenharmony_ciover your code regularly to catch compatibility regressions. This does require
3697db96d56Sopenharmony_ciyou only support Python 2.7 and Python 3.4 or newer as that is Pylint's
3707db96d56Sopenharmony_ciminimum Python version support.
3717db96d56Sopenharmony_ci
3727db96d56Sopenharmony_ci
3737db96d56Sopenharmony_ciCheck which dependencies block your transition
3747db96d56Sopenharmony_ci----------------------------------------------
3757db96d56Sopenharmony_ci
3767db96d56Sopenharmony_ci**After** you have made your code compatible with Python 3 you should begin to
3777db96d56Sopenharmony_cicare about whether your dependencies have also been ported. The caniusepython3_
3787db96d56Sopenharmony_ciproject was created to help you determine which projects
3797db96d56Sopenharmony_ci-- directly or indirectly -- are blocking you from supporting Python 3. There
3807db96d56Sopenharmony_ciis both a command-line tool as well as a web interface at
3817db96d56Sopenharmony_cihttps://caniusepython3.com.
3827db96d56Sopenharmony_ci
3837db96d56Sopenharmony_ciThe project also provides code which you can integrate into your test suite so
3847db96d56Sopenharmony_cithat you will have a failing test when you no longer have dependencies blocking
3857db96d56Sopenharmony_ciyou from using Python 3. This allows you to avoid having to manually check your
3867db96d56Sopenharmony_cidependencies and to be notified quickly when you can start running on Python 3.
3877db96d56Sopenharmony_ci
3887db96d56Sopenharmony_ci
3897db96d56Sopenharmony_ciUpdate your ``setup.py`` file to denote Python 3 compatibility
3907db96d56Sopenharmony_ci--------------------------------------------------------------
3917db96d56Sopenharmony_ci
3927db96d56Sopenharmony_ciOnce your code works under Python 3, you should update the classifiers in
3937db96d56Sopenharmony_ciyour ``setup.py`` to contain ``Programming Language :: Python :: 3`` and to not
3947db96d56Sopenharmony_cispecify sole Python 2 support. This will tell anyone using your code that you
3957db96d56Sopenharmony_cisupport Python 2 **and** 3. Ideally you will also want to add classifiers for
3967db96d56Sopenharmony_cieach major/minor version of Python you now support.
3977db96d56Sopenharmony_ci
3987db96d56Sopenharmony_ci
3997db96d56Sopenharmony_ciUse continuous integration to stay compatible
4007db96d56Sopenharmony_ci---------------------------------------------
4017db96d56Sopenharmony_ci
4027db96d56Sopenharmony_ciOnce you are able to fully run under Python 3 you will want to make sure your
4037db96d56Sopenharmony_cicode always works under both Python 2 & 3. Probably the best tool for running
4047db96d56Sopenharmony_ciyour tests under multiple Python interpreters is tox_. You can then integrate
4057db96d56Sopenharmony_citox with your continuous integration system so that you never accidentally break
4067db96d56Sopenharmony_ciPython 2 or 3 support.
4077db96d56Sopenharmony_ci
4087db96d56Sopenharmony_ciYou may also want to use the ``-bb`` flag with the Python 3 interpreter to
4097db96d56Sopenharmony_citrigger an exception when you are comparing bytes to strings or bytes to an int
4107db96d56Sopenharmony_ci(the latter is available starting in Python 3.5). By default type-differing
4117db96d56Sopenharmony_cicomparisons simply return ``False``, but if you made a mistake in your
4127db96d56Sopenharmony_ciseparation of text/binary data handling or indexing on bytes you wouldn't easily
4137db96d56Sopenharmony_cifind the mistake. This flag will raise an exception when these kinds of
4147db96d56Sopenharmony_cicomparisons occur, making the mistake much easier to track down.
4157db96d56Sopenharmony_ci
4167db96d56Sopenharmony_ciAnd that's mostly it! At this point your code base is compatible with both
4177db96d56Sopenharmony_ciPython 2 and 3 simultaneously. Your testing will also be set up so that you
4187db96d56Sopenharmony_cidon't accidentally break Python 2 or 3 compatibility regardless of which version
4197db96d56Sopenharmony_ciyou typically run your tests under while developing.
4207db96d56Sopenharmony_ci
4217db96d56Sopenharmony_ci
4227db96d56Sopenharmony_ciConsider using optional static type checking
4237db96d56Sopenharmony_ci--------------------------------------------
4247db96d56Sopenharmony_ci
4257db96d56Sopenharmony_ciAnother way to help port your code is to use a static type checker like
4267db96d56Sopenharmony_cimypy_ or pytype_ on your code. These tools can be used to analyze your code as
4277db96d56Sopenharmony_ciif it's being run under Python 2, then you can run the tool a second time as if
4287db96d56Sopenharmony_ciyour code is running under Python 3. By running a static type checker twice like
4297db96d56Sopenharmony_cithis you can discover if you're e.g. misusing binary data type in one version
4307db96d56Sopenharmony_ciof Python compared to another. If you add optional type hints to your code you
4317db96d56Sopenharmony_cican also explicitly state whether your APIs use textual or binary data, helping
4327db96d56Sopenharmony_cito make sure everything functions as expected in both versions of Python.
4337db96d56Sopenharmony_ci
4347db96d56Sopenharmony_ci
4357db96d56Sopenharmony_ci.. _caniusepython3: https://pypi.org/project/caniusepython3
4367db96d56Sopenharmony_ci.. _cheat sheet: https://python-future.org/compatible_idioms.html
4377db96d56Sopenharmony_ci.. _coverage.py: https://pypi.org/project/coverage
4387db96d56Sopenharmony_ci.. _Futurize: https://python-future.org/automatic_conversion.html
4397db96d56Sopenharmony_ci.. _importlib2: https://pypi.org/project/importlib2
4407db96d56Sopenharmony_ci.. _Modernize: https://python-modernize.readthedocs.io/
4417db96d56Sopenharmony_ci.. _mypy: https://mypy-lang.org/
4427db96d56Sopenharmony_ci.. _Porting to Python 3: http://python3porting.com/
4437db96d56Sopenharmony_ci.. _Pylint: https://pypi.org/project/pylint
4447db96d56Sopenharmony_ci
4457db96d56Sopenharmony_ci.. _Python 3 Q & A: https://ncoghlan-devs-python-notes.readthedocs.io/en/latest/python3/questions_and_answers.html
4467db96d56Sopenharmony_ci
4477db96d56Sopenharmony_ci.. _pytype: https://github.com/google/pytype
4487db96d56Sopenharmony_ci.. _python-future: https://python-future.org/
4497db96d56Sopenharmony_ci.. _python-porting: https://mail.python.org/pipermail/python-porting/
4507db96d56Sopenharmony_ci.. _six: https://pypi.org/project/six
4517db96d56Sopenharmony_ci.. _tox: https://pypi.org/project/tox
4527db96d56Sopenharmony_ci.. _trove classifier: https://pypi.org/classifiers
4537db96d56Sopenharmony_ci
4547db96d56Sopenharmony_ci.. _Why Python 3 exists: https://snarky.ca/why-python-3-exists
455