594 lines
46 KiB
HTML
594 lines
46 KiB
HTML
|
||
<!DOCTYPE html>
|
||
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<title>Porting Python 2 Code to Python 3 — Python 3.7.4 documentation</title>
|
||
<link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
||
|
||
<script type="text/javascript" id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
|
||
<script type="text/javascript" src="../_static/jquery.js"></script>
|
||
<script type="text/javascript" src="../_static/underscore.js"></script>
|
||
<script type="text/javascript" src="../_static/doctools.js"></script>
|
||
<script type="text/javascript" src="../_static/language_data.js"></script>
|
||
|
||
<script type="text/javascript" src="../_static/sidebar.js"></script>
|
||
|
||
<link rel="search" type="application/opensearchdescription+xml"
|
||
title="Search within Python 3.7.4 documentation"
|
||
href="../_static/opensearch.xml"/>
|
||
<link rel="author" title="About these documents" href="../about.html" />
|
||
<link rel="index" title="Index" href="../genindex.html" />
|
||
<link rel="search" title="Search" href="../search.html" />
|
||
<link rel="copyright" title="Copyright" href="../copyright.html" />
|
||
<link rel="next" title="Porting Extension Modules to Python 3" href="cporting.html" />
|
||
<link rel="prev" title="Python HOWTOs" href="index.html" />
|
||
<link rel="shortcut icon" type="image/png" href="../_static/py.png" />
|
||
<link rel="canonical" href="https://docs.python.org/3/howto/pyporting.html" />
|
||
|
||
<script type="text/javascript" src="../_static/copybutton.js"></script>
|
||
<script type="text/javascript" src="../_static/switchers.js"></script>
|
||
|
||
|
||
|
||
<style>
|
||
@media only screen {
|
||
table.full-width-table {
|
||
width: 100%;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
|
||
</head><body>
|
||
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
accesskey="I">index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="cporting.html" title="Porting Extension Modules to Python 3"
|
||
accesskey="N">next</a> |</li>
|
||
<li class="right" >
|
||
<a href="index.html" title="Python HOWTOs"
|
||
accesskey="P">previous</a> |</li>
|
||
<li><img src="../_static/py.png" alt=""
|
||
style="vertical-align: middle; margin-top: -1px"/></li>
|
||
<li><a href="https://www.python.org/">Python</a> »</li>
|
||
<li>
|
||
<span class="language_switcher_placeholder">en</span>
|
||
<span class="version_switcher_placeholder">3.7.4</span>
|
||
<a href="../index.html">Documentation </a> »
|
||
</li>
|
||
|
||
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Python HOWTOs</a> »</li>
|
||
<li class="right">
|
||
|
||
|
||
<div class="inline-search" style="display: none" role="search">
|
||
<form class="inline-search" action="../search.html" method="get">
|
||
<input placeholder="Quick search" type="text" name="q" />
|
||
<input type="submit" value="Go" />
|
||
<input type="hidden" name="check_keywords" value="yes" />
|
||
<input type="hidden" name="area" value="default" />
|
||
</form>
|
||
</div>
|
||
<script type="text/javascript">$('.inline-search').show(0);</script>
|
||
|
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="document">
|
||
<div class="documentwrapper">
|
||
<div class="bodywrapper">
|
||
<div class="body" role="main">
|
||
|
||
<div class="section" id="porting-python-2-code-to-python-3">
|
||
<span id="pyporting-howto"></span><h1>Porting Python 2 Code to Python 3<a class="headerlink" href="#porting-python-2-code-to-python-3" title="Permalink to this headline">¶</a></h1>
|
||
<dl class="field-list simple">
|
||
<dt class="field-odd">author</dt>
|
||
<dd class="field-odd"><p>Brett Cannon</p>
|
||
</dd>
|
||
</dl>
|
||
<div class="topic">
|
||
<p class="topic-title first">Abstract</p>
|
||
<p>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 figure out how best to support both
|
||
Python 2 & 3 simultaneously.</p>
|
||
<p>If you are looking to port an extension module instead of pure Python code,
|
||
please see <a class="reference internal" href="cporting.html#cporting-howto"><span class="std std-ref">Porting Extension Modules to Python 3</span></a>.</p>
|
||
<p>If you would like to read one core Python developer’s take on why Python 3
|
||
came into existence, you can read Nick Coghlan’s <a class="reference external" href="https://ncoghlan-devs-python-notes.readthedocs.io/en/latest/python3/questions_and_answers.html">Python 3 Q & A</a> or
|
||
Brett Cannon’s <a class="reference external" href="https://snarky.ca/why-python-3-exists">Why Python 3 exists</a>.</p>
|
||
<p>For help with porting, you can email the <a class="reference external" href="https://mail.python.org/mailman/listinfo/python-porting">python-porting</a> mailing list with
|
||
questions.</p>
|
||
</div>
|
||
<div class="section" id="the-short-explanation">
|
||
<h2>The Short Explanation<a class="headerlink" href="#the-short-explanation" title="Permalink to this headline">¶</a></h2>
|
||
<p>To make your project be single-source Python 2/3 compatible, the basic steps
|
||
are:</p>
|
||
<ol class="arabic simple">
|
||
<li><p>Only worry about supporting Python 2.7</p></li>
|
||
<li><p>Make sure you have good test coverage (<a class="reference external" href="https://pypi.org/project/coverage">coverage.py</a> can help;
|
||
<code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">coverage</span></code>)</p></li>
|
||
<li><p>Learn the differences between Python 2 & 3</p></li>
|
||
<li><p>Use <a class="reference external" href="http://python-future.org/automatic_conversion.html">Futurize</a> (or <a class="reference external" href="https://python-modernize.readthedocs.io/">Modernize</a>) to update your code (e.g. <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">future</span></code>)</p></li>
|
||
<li><p>Use <a class="reference external" href="https://pypi.org/project/pylint">Pylint</a> to help make sure you don’t regress on your Python 3 support
|
||
(<code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">pylint</span></code>)</p></li>
|
||
<li><p>Use <a class="reference external" href="https://pypi.org/project/caniusepython3">caniusepython3</a> to find out which of your dependencies are blocking your
|
||
use of Python 3 (<code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">caniusepython3</span></code>)</p></li>
|
||
<li><p>Once your dependencies are no longer blocking you, use continuous integration
|
||
to make sure you stay compatible with Python 2 & 3 (<a class="reference external" href="https://pypi.org/project/tox">tox</a> can help test
|
||
against multiple versions of Python; <code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">tox</span></code>)</p></li>
|
||
<li><p>Consider using optional static type checking to make sure your type usage
|
||
works in both Python 2 & 3 (e.g. use <a class="reference external" href="http://mypy-lang.org/">mypy</a> to check your typing under both
|
||
Python 2 & Python 3).</p></li>
|
||
</ol>
|
||
</div>
|
||
<div class="section" id="details">
|
||
<h2>Details<a class="headerlink" href="#details" title="Permalink to this headline">¶</a></h2>
|
||
<p>A key point about supporting Python 2 & 3 simultaneously is that you can start
|
||
<strong>today</strong>! Even if your dependencies are not supporting Python 3 yet that does
|
||
not mean you can’t modernize your code <strong>now</strong> to support Python 3. Most changes
|
||
required to support Python 3 lead to cleaner code using newer practices even in
|
||
Python 2 code.</p>
|
||
<p>Another key point is that modernizing your Python 2 code to also support
|
||
Python 3 is largely automated for you. While you might have to make some API
|
||
decisions thanks to Python 3 clarifying text data versus binary data, the
|
||
lower-level work is now mostly done for you and thus can at least benefit from
|
||
the automated changes immediately.</p>
|
||
<p>Keep those key points in mind while you read on about the details of porting
|
||
your code to support Python 2 & 3 simultaneously.</p>
|
||
<div class="section" id="drop-support-for-python-2-6-and-older">
|
||
<h3>Drop support for Python 2.6 and older<a class="headerlink" href="#drop-support-for-python-2-6-and-older" title="Permalink to this headline">¶</a></h3>
|
||
<p>While you can make Python 2.5 work with Python 3, it is <strong>much</strong> easier if you
|
||
only have to work with Python 2.7. If dropping Python 2.5 is not an
|
||
option then the <a class="reference external" href="https://pypi.org/project/six">six</a> project can help you support Python 2.5 & 3 simultaneously
|
||
(<code class="docutils literal notranslate"><span class="pre">pip</span> <span class="pre">install</span> <span class="pre">six</span></code>). Do realize, though, that nearly all the projects listed
|
||
in this HOWTO will not be available to you.</p>
|
||
<p>If you are able to skip Python 2.5 and older, then the required changes
|
||
to your code should continue to look and feel like idiomatic Python code. At
|
||
worst you will have to use a function instead of a method in some instances or
|
||
have to import a function instead of using a built-in one, but otherwise the
|
||
overall transformation should not feel foreign to you.</p>
|
||
<p>But you should aim for only supporting Python 2.7. Python 2.6 is no longer
|
||
freely supported and thus is not receiving bugfixes. This means <strong>you</strong> will have
|
||
to work around any issues you come across with Python 2.6. There are also some
|
||
tools mentioned in this HOWTO which do not support Python 2.6 (e.g., <a class="reference external" href="https://pypi.org/project/pylint">Pylint</a>),
|
||
and this will become more commonplace as time goes on. It will simply be easier
|
||
for you if you only support the versions of Python that you have to support.</p>
|
||
</div>
|
||
<div class="section" id="make-sure-you-specify-the-proper-version-support-in-your-setup-py-file">
|
||
<h3>Make sure you specify the proper version support in your <code class="docutils literal notranslate"><span class="pre">setup.py</span></code> file<a class="headerlink" href="#make-sure-you-specify-the-proper-version-support-in-your-setup-py-file" title="Permalink to this headline">¶</a></h3>
|
||
<p>In your <code class="docutils literal notranslate"><span class="pre">setup.py</span></code> file you should have the proper <a class="reference external" href="https://pypi.org/classifiers">trove classifier</a>
|
||
specifying what versions of Python you support. As your project does not support
|
||
Python 3 yet you should at least have
|
||
<code class="docutils literal notranslate"><span class="pre">Programming</span> <span class="pre">Language</span> <span class="pre">::</span> <span class="pre">Python</span> <span class="pre">::</span> <span class="pre">2</span> <span class="pre">::</span> <span class="pre">Only</span></code> specified. Ideally you should
|
||
also specify each major/minor version of Python that you do support, e.g.
|
||
<code class="docutils literal notranslate"><span class="pre">Programming</span> <span class="pre">Language</span> <span class="pre">::</span> <span class="pre">Python</span> <span class="pre">::</span> <span class="pre">2.7</span></code>.</p>
|
||
</div>
|
||
<div class="section" id="have-good-test-coverage">
|
||
<h3>Have good test coverage<a class="headerlink" href="#have-good-test-coverage" title="Permalink to this headline">¶</a></h3>
|
||
<p>Once you have your code supporting the oldest version of Python 2 you want it
|
||
to, you will want to make sure your test suite has good coverage. A good rule of
|
||
thumb is that if you want to be confident enough in your test suite that any
|
||
failures that appear after having tools rewrite your code are actual bugs in the
|
||
tools and not in your code. If you want a number to aim for, try to get over 80%
|
||
coverage (and don’t feel bad if you find it hard to get better than 90%
|
||
coverage). If you don’t already have a tool to measure test coverage then
|
||
<a class="reference external" href="https://pypi.org/project/coverage">coverage.py</a> is recommended.</p>
|
||
</div>
|
||
<div class="section" id="learn-the-differences-between-python-2-3">
|
||
<h3>Learn the differences between Python 2 & 3<a class="headerlink" href="#learn-the-differences-between-python-2-3" title="Permalink to this headline">¶</a></h3>
|
||
<p>Once you have your code well-tested you are ready to begin porting your code to
|
||
Python 3! But to fully understand how your code is going to change and what
|
||
you want to look out for while you code, you will want to learn what changes
|
||
Python 3 makes in terms of Python 2. Typically the two best ways of doing that
|
||
is reading the <a class="reference external" href="https://docs.python.org/3/whatsnew/index.html">“What’s New”</a> doc for each release of Python 3 and the
|
||
<a class="reference external" href="http://python3porting.com/">Porting to Python 3</a> book (which is free online). There is also a handy
|
||
<a class="reference external" href="http://python-future.org/compatible_idioms.html">cheat sheet</a> from the Python-Future project.</p>
|
||
</div>
|
||
<div class="section" id="update-your-code">
|
||
<h3>Update your code<a class="headerlink" href="#update-your-code" title="Permalink to this headline">¶</a></h3>
|
||
<p>Once you feel like you know what is different in Python 3 compared to Python 2,
|
||
it’s time to update your code! You have a choice between two tools in porting
|
||
your code automatically: <a class="reference external" href="http://python-future.org/automatic_conversion.html">Futurize</a> and <a class="reference external" href="https://python-modernize.readthedocs.io/">Modernize</a>. Which tool you choose will
|
||
depend on how much like Python 3 you want your code to be. <a class="reference external" href="http://python-future.org/automatic_conversion.html">Futurize</a> does its
|
||
best to make Python 3 idioms and practices exist in Python 2, e.g. backporting
|
||
the <code class="docutils literal notranslate"><span class="pre">bytes</span></code> type from Python 3 so that you have semantic parity between the
|
||
major versions of Python. <a class="reference external" href="https://python-modernize.readthedocs.io/">Modernize</a>,
|
||
on the other hand, is more conservative and targets a Python 2/3 subset of
|
||
Python, directly relying on <a class="reference external" href="https://pypi.org/project/six">six</a> to help provide compatibility. As Python 3 is
|
||
the future, it might be best to consider Futurize to begin adjusting to any new
|
||
practices that Python 3 introduces which you are not accustomed to yet.</p>
|
||
<p>Regardless of which tool you choose, they will update your code to run under
|
||
Python 3 while staying compatible with the version of Python 2 you started with.
|
||
Depending on how conservative you want to be, you may want to run the tool over
|
||
your test suite first and visually inspect the diff to make sure the
|
||
transformation is accurate. After you have transformed your test suite and
|
||
verified that all the tests still pass as expected, then you can transform your
|
||
application code knowing that any tests which fail is a translation failure.</p>
|
||
<p>Unfortunately the tools can’t automate everything to make your code work under
|
||
Python 3 and so there are a handful of things you will need to update manually
|
||
to get full Python 3 support (which of these steps are necessary vary between
|
||
the tools). Read the documentation for the tool you choose to use to see what it
|
||
fixes by default and what it can do optionally to know what will (not) be fixed
|
||
for you and what you may have to fix on your own (e.g. using <code class="docutils literal notranslate"><span class="pre">io.open()</span></code> over
|
||
the built-in <code class="docutils literal notranslate"><span class="pre">open()</span></code> function is off by default in Modernize). Luckily,
|
||
though, there are only a couple of things to watch out for which can be
|
||
considered large issues that may be hard to debug if not watched for.</p>
|
||
<div class="section" id="division">
|
||
<h4>Division<a class="headerlink" href="#division" title="Permalink to this headline">¶</a></h4>
|
||
<p>In Python 3, <code class="docutils literal notranslate"><span class="pre">5</span> <span class="pre">/</span> <span class="pre">2</span> <span class="pre">==</span> <span class="pre">2.5</span></code> and not <code class="docutils literal notranslate"><span class="pre">2</span></code>; all division between <code class="docutils literal notranslate"><span class="pre">int</span></code> values
|
||
result in a <code class="docutils literal notranslate"><span class="pre">float</span></code>. This change has actually been planned since Python 2.2
|
||
which was released in 2002. Since then users have been encouraged to add
|
||
<code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">__future__</span> <span class="pre">import</span> <span class="pre">division</span></code> to any and all files which use the <code class="docutils literal notranslate"><span class="pre">/</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">//</span></code> operators or to be running the interpreter with the <code class="docutils literal notranslate"><span class="pre">-Q</span></code> flag. If you
|
||
have not been doing this then you will need to go through your code and do two
|
||
things:</p>
|
||
<ol class="arabic simple">
|
||
<li><p>Add <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">__future__</span> <span class="pre">import</span> <span class="pre">division</span></code> to your files</p></li>
|
||
<li><p>Update any division operator as necessary to either use <code class="docutils literal notranslate"><span class="pre">//</span></code> to use floor
|
||
division or continue using <code class="docutils literal notranslate"><span class="pre">/</span></code> and expect a float</p></li>
|
||
</ol>
|
||
<p>The reason that <code class="docutils literal notranslate"><span class="pre">/</span></code> isn’t simply translated to <code class="docutils literal notranslate"><span class="pre">//</span></code> automatically is that if
|
||
an object defines a <code class="docutils literal notranslate"><span class="pre">__truediv__</span></code> method but not <code class="docutils literal notranslate"><span class="pre">__floordiv__</span></code> then your
|
||
code would begin to fail (e.g. a user-defined class that uses <code class="docutils literal notranslate"><span class="pre">/</span></code> to
|
||
signify some operation but not <code class="docutils literal notranslate"><span class="pre">//</span></code> for the same thing or at all).</p>
|
||
</div>
|
||
<div class="section" id="text-versus-binary-data">
|
||
<h4>Text versus binary data<a class="headerlink" href="#text-versus-binary-data" title="Permalink to this headline">¶</a></h4>
|
||
<p>In Python 2 you could use the <code class="docutils literal notranslate"><span class="pre">str</span></code> type for both text and binary data.
|
||
Unfortunately this confluence of two different concepts could lead to brittle
|
||
code which sometimes worked for either kind of data, sometimes not. It also
|
||
could lead to confusing APIs if people didn’t explicitly state that something
|
||
that accepted <code class="docutils literal notranslate"><span class="pre">str</span></code> accepted either text or binary data instead of one
|
||
specific type. This complicated the situation especially for anyone supporting
|
||
multiple languages as APIs wouldn’t bother explicitly supporting <code class="docutils literal notranslate"><span class="pre">unicode</span></code>
|
||
when they claimed text data support.</p>
|
||
<p>To make the distinction between text and binary data clearer and more
|
||
pronounced, Python 3 did what most languages created in the age of the internet
|
||
have done and made text and binary data distinct types that cannot blindly be
|
||
mixed together (Python predates widespread access to the internet). For any code
|
||
that deals only with text or only binary data, this separation doesn’t pose an
|
||
issue. But for code that has to deal with both, it does mean you might have to
|
||
now care about when you are using text compared to binary data, which is why
|
||
this cannot be entirely automated.</p>
|
||
<p>To start, you will need to decide which APIs take text and which take binary
|
||
(it is <strong>highly</strong> recommended you don’t design APIs that can take both due to
|
||
the difficulty of keeping the code working; as stated earlier it is difficult to
|
||
do well). In Python 2 this means making sure the APIs that take text can work
|
||
with <code class="docutils literal notranslate"><span class="pre">unicode</span></code> and those that work with binary data work with the
|
||
<code class="docutils literal notranslate"><span class="pre">bytes</span></code> type from Python 3 (which is a subset of <code class="docutils literal notranslate"><span class="pre">str</span></code> in Python 2 and acts
|
||
as an alias for <code class="docutils literal notranslate"><span class="pre">bytes</span></code> type in Python 2). Usually the biggest issue is
|
||
realizing which methods exist on which types in Python 2 & 3 simultaneously
|
||
(for text that’s <code class="docutils literal notranslate"><span class="pre">unicode</span></code> in Python 2 and <code class="docutils literal notranslate"><span class="pre">str</span></code> in Python 3, for binary
|
||
that’s <code class="docutils literal notranslate"><span class="pre">str</span></code>/<code class="docutils literal notranslate"><span class="pre">bytes</span></code> in Python 2 and <code class="docutils literal notranslate"><span class="pre">bytes</span></code> in Python 3). The following
|
||
table lists the <strong>unique</strong> methods of each data type across Python 2 & 3
|
||
(e.g., the <code class="docutils literal notranslate"><span class="pre">decode()</span></code> method is usable on the equivalent binary data type in
|
||
either Python 2 or 3, but it can’t be used by the textual data type consistently
|
||
between Python 2 and 3 because <code class="docutils literal notranslate"><span class="pre">str</span></code> in Python 3 doesn’t have the method). Do
|
||
note that as of Python 3.5 the <code class="docutils literal notranslate"><span class="pre">__mod__</span></code> method was added to the bytes type.</p>
|
||
<table class="docutils align-center">
|
||
<colgroup>
|
||
<col style="width: 53%" />
|
||
<col style="width: 47%" />
|
||
</colgroup>
|
||
<tbody>
|
||
<tr class="row-odd"><td><p><strong>Text data</strong></p></td>
|
||
<td><p><strong>Binary data</strong></p></td>
|
||
</tr>
|
||
<tr class="row-even"><td><p></p></td>
|
||
<td><p>decode</p></td>
|
||
</tr>
|
||
<tr class="row-odd"><td><p>encode</p></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr class="row-even"><td><p>format</p></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr class="row-odd"><td><p>isdecimal</p></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr class="row-even"><td><p>isnumeric</p></td>
|
||
<td></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Making the distinction easier to handle can be accomplished by encoding and
|
||
decoding between binary data and text at the edge of your code. This means that
|
||
when you receive text in binary data, you should immediately decode it. And if
|
||
your code needs to send text as binary data then encode it as late as possible.
|
||
This allows your code to work with only text internally and thus eliminates
|
||
having to keep track of what type of data you are working with.</p>
|
||
<p>The next issue is making sure you know whether the string literals in your code
|
||
represent text or binary data. You should add a <code class="docutils literal notranslate"><span class="pre">b</span></code> prefix to any
|
||
literal that presents binary data. For text you should add a <code class="docutils literal notranslate"><span class="pre">u</span></code> prefix to
|
||
the text literal. (there is a <a class="reference internal" href="../library/__future__.html#module-__future__" title="__future__: Future statement definitions"><code class="xref py py-mod docutils literal notranslate"><span class="pre">__future__</span></code></a> import to force all unspecified
|
||
literals to be Unicode, but usage has shown it isn’t as effective as adding a
|
||
<code class="docutils literal notranslate"><span class="pre">b</span></code> or <code class="docutils literal notranslate"><span class="pre">u</span></code> prefix to all literals explicitly)</p>
|
||
<p>As part of this dichotomy you also need to be careful about opening files.
|
||
Unless you have been working on Windows, there is a chance you have not always
|
||
bothered to add the <code class="docutils literal notranslate"><span class="pre">b</span></code> mode when opening a binary file (e.g., <code class="docutils literal notranslate"><span class="pre">rb</span></code> for
|
||
binary reading). Under Python 3, binary files and text files are clearly
|
||
distinct and mutually incompatible; see the <a class="reference internal" href="../library/io.html#module-io" title="io: Core tools for working with streams."><code class="xref py py-mod docutils literal notranslate"><span class="pre">io</span></code></a> module for details.
|
||
Therefore, you <strong>must</strong> make a decision of whether a file will be used for
|
||
binary access (allowing binary data to be read and/or written) or textual access
|
||
(allowing text data to be read and/or written). You should also use <a class="reference internal" href="../library/io.html#io.open" title="io.open"><code class="xref py py-func docutils literal notranslate"><span class="pre">io.open()</span></code></a>
|
||
for opening files instead of the built-in <a class="reference internal" href="../library/functions.html#open" title="open"><code class="xref py py-func docutils literal notranslate"><span class="pre">open()</span></code></a> function as the <a class="reference internal" href="../library/io.html#module-io" title="io: Core tools for working with streams."><code class="xref py py-mod docutils literal notranslate"><span class="pre">io</span></code></a>
|
||
module is consistent from Python 2 to 3 while the built-in <a class="reference internal" href="../library/functions.html#open" title="open"><code class="xref py py-func docutils literal notranslate"><span class="pre">open()</span></code></a> function
|
||
is not (in Python 3 it’s actually <a class="reference internal" href="../library/io.html#io.open" title="io.open"><code class="xref py py-func docutils literal notranslate"><span class="pre">io.open()</span></code></a>). Do not bother with the
|
||
outdated practice of using <a class="reference internal" href="../library/codecs.html#codecs.open" title="codecs.open"><code class="xref py py-func docutils literal notranslate"><span class="pre">codecs.open()</span></code></a> as that’s only necessary for
|
||
keeping compatibility with Python 2.5.</p>
|
||
<p>The constructors of both <code class="docutils literal notranslate"><span class="pre">str</span></code> and <code class="docutils literal notranslate"><span class="pre">bytes</span></code> have different semantics for the
|
||
same arguments between Python 2 & 3. Passing an integer to <code class="docutils literal notranslate"><span class="pre">bytes</span></code> in Python 2
|
||
will give you the string representation of the integer: <code class="docutils literal notranslate"><span class="pre">bytes(3)</span> <span class="pre">==</span> <span class="pre">'3'</span></code>.
|
||
But in Python 3, an integer argument to <code class="docutils literal notranslate"><span class="pre">bytes</span></code> will give you a bytes object
|
||
as long as the integer specified, filled with null bytes:
|
||
<code class="docutils literal notranslate"><span class="pre">bytes(3)</span> <span class="pre">==</span> <span class="pre">b'\x00\x00\x00'</span></code>. A similar worry is necessary when passing a
|
||
bytes object to <code class="docutils literal notranslate"><span class="pre">str</span></code>. In Python 2 you just get the bytes object back:
|
||
<code class="docutils literal notranslate"><span class="pre">str(b'3')</span> <span class="pre">==</span> <span class="pre">b'3'</span></code>. But in Python 3 you get the string representation of the
|
||
bytes object: <code class="docutils literal notranslate"><span class="pre">str(b'3')</span> <span class="pre">==</span> <span class="pre">"b'3'"</span></code>.</p>
|
||
<p>Finally, the indexing of binary data requires careful handling (slicing does
|
||
<strong>not</strong> require any special handling). In Python 2,
|
||
<code class="docutils literal notranslate"><span class="pre">b'123'[1]</span> <span class="pre">==</span> <span class="pre">b'2'</span></code> while in Python 3 <code class="docutils literal notranslate"><span class="pre">b'123'[1]</span> <span class="pre">==</span> <span class="pre">50</span></code>. Because binary data
|
||
is simply a collection of binary numbers, Python 3 returns the integer value for
|
||
the byte you index on. But in Python 2 because <code class="docutils literal notranslate"><span class="pre">bytes</span> <span class="pre">==</span> <span class="pre">str</span></code>, indexing
|
||
returns a one-item slice of bytes. The <a class="reference external" href="https://pypi.org/project/six">six</a> project has a function
|
||
named <code class="docutils literal notranslate"><span class="pre">six.indexbytes()</span></code> which will return an integer like in Python 3:
|
||
<code class="docutils literal notranslate"><span class="pre">six.indexbytes(b'123',</span> <span class="pre">1)</span></code>.</p>
|
||
<p>To summarize:</p>
|
||
<ol class="arabic simple">
|
||
<li><p>Decide which of your APIs take text and which take binary data</p></li>
|
||
<li><p>Make sure that your code that works with text also works with <code class="docutils literal notranslate"><span class="pre">unicode</span></code> and
|
||
code for binary data works with <code class="docutils literal notranslate"><span class="pre">bytes</span></code> in Python 2 (see the table above
|
||
for what methods you cannot use for each type)</p></li>
|
||
<li><p>Mark all binary literals with a <code class="docutils literal notranslate"><span class="pre">b</span></code> prefix, textual literals with a <code class="docutils literal notranslate"><span class="pre">u</span></code>
|
||
prefix</p></li>
|
||
<li><p>Decode binary data to text as soon as possible, encode text as binary data as
|
||
late as possible</p></li>
|
||
<li><p>Open files using <a class="reference internal" href="../library/io.html#io.open" title="io.open"><code class="xref py py-func docutils literal notranslate"><span class="pre">io.open()</span></code></a> and make sure to specify the <code class="docutils literal notranslate"><span class="pre">b</span></code> mode when
|
||
appropriate</p></li>
|
||
<li><p>Be careful when indexing into binary data</p></li>
|
||
</ol>
|
||
</div>
|
||
<div class="section" id="use-feature-detection-instead-of-version-detection">
|
||
<h4>Use feature detection instead of version detection<a class="headerlink" href="#use-feature-detection-instead-of-version-detection" title="Permalink to this headline">¶</a></h4>
|
||
<p>Inevitably you will have code that has to choose what to do based on what
|
||
version of Python is running. The best way to do this is with feature detection
|
||
of whether the version of Python you’re running under supports what you need.
|
||
If for some reason that doesn’t work then you should make the version check be
|
||
against Python 2 and not Python 3. To help explain this, let’s look at an
|
||
example.</p>
|
||
<p>Let’s pretend that you need access to a feature of <a class="reference external" href="https://docs.python.org/3/library/importlib.html#module-importlib">importlib</a> that
|
||
is available in Python’s standard library since Python 3.3 and available for
|
||
Python 2 through <a class="reference external" href="https://pypi.org/project/importlib2">importlib2</a> on PyPI. You might be tempted to write code to
|
||
access e.g. the <code class="docutils literal notranslate"><span class="pre">importlib.abc</span></code> module by doing the following:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">sys</span>
|
||
|
||
<span class="k">if</span> <span class="n">sys</span><span class="o">.</span><span class="n">version_info</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
|
||
<span class="kn">from</span> <span class="nn">importlib</span> <span class="k">import</span> <span class="n">abc</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="kn">from</span> <span class="nn">importlib2</span> <span class="k">import</span> <span class="n">abc</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The problem with this code is what happens when Python 4 comes out? It would
|
||
be better to treat Python 2 as the exceptional case instead of Python 3 and
|
||
assume that future Python versions will be more compatible with Python 3 than
|
||
Python 2:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">sys</span>
|
||
|
||
<span class="k">if</span> <span class="n">sys</span><span class="o">.</span><span class="n">version_info</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">></span> <span class="mi">2</span><span class="p">:</span>
|
||
<span class="kn">from</span> <span class="nn">importlib</span> <span class="k">import</span> <span class="n">abc</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="kn">from</span> <span class="nn">importlib2</span> <span class="k">import</span> <span class="n">abc</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The best solution, though, is to do no version detection at all and instead rely
|
||
on feature detection. That avoids any potential issues of getting the version
|
||
detection wrong and helps keep you future-compatible:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
|
||
<span class="kn">from</span> <span class="nn">importlib</span> <span class="k">import</span> <span class="n">abc</span>
|
||
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
|
||
<span class="kn">from</span> <span class="nn">importlib2</span> <span class="k">import</span> <span class="n">abc</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="prevent-compatibility-regressions">
|
||
<h3>Prevent compatibility regressions<a class="headerlink" href="#prevent-compatibility-regressions" title="Permalink to this headline">¶</a></h3>
|
||
<p>Once you have fully translated your code to be compatible with Python 3, you
|
||
will want to make sure your code doesn’t regress and stop working under
|
||
Python 3. This is especially true if you have a dependency which is blocking you
|
||
from actually running under Python 3 at the moment.</p>
|
||
<p>To help with staying compatible, any new modules you create should have
|
||
at least the following block of code at the top of it:</p>
|
||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="k">import</span> <span class="n">absolute_import</span>
|
||
<span class="kn">from</span> <span class="nn">__future__</span> <span class="k">import</span> <span class="n">division</span>
|
||
<span class="kn">from</span> <span class="nn">__future__</span> <span class="k">import</span> <span class="n">print_function</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>You can also run Python 2 with the <code class="docutils literal notranslate"><span class="pre">-3</span></code> flag to be warned about various
|
||
compatibility issues your code triggers during execution. If you turn warnings
|
||
into errors with <code class="docutils literal notranslate"><span class="pre">-Werror</span></code> then you can make sure that you don’t accidentally
|
||
miss a warning.</p>
|
||
<p>You can also use the <a class="reference external" href="https://pypi.org/project/pylint">Pylint</a> project and its <code class="docutils literal notranslate"><span class="pre">--py3k</span></code> flag to lint your code
|
||
to receive warnings when your code begins to deviate from Python 3
|
||
compatibility. This also prevents you from having to run <a class="reference external" href="https://python-modernize.readthedocs.io/">Modernize</a> or <a class="reference external" href="http://python-future.org/automatic_conversion.html">Futurize</a>
|
||
over your code regularly to catch compatibility regressions. This does require
|
||
you only support Python 2.7 and Python 3.4 or newer as that is Pylint’s
|
||
minimum Python version support.</p>
|
||
</div>
|
||
<div class="section" id="check-which-dependencies-block-your-transition">
|
||
<h3>Check which dependencies block your transition<a class="headerlink" href="#check-which-dependencies-block-your-transition" title="Permalink to this headline">¶</a></h3>
|
||
<p><strong>After</strong> you have made your code compatible with Python 3 you should begin to
|
||
care about whether your dependencies have also been ported. The <a class="reference external" href="https://pypi.org/project/caniusepython3">caniusepython3</a>
|
||
project was created to help you determine which projects
|
||
– directly or indirectly – are blocking you from supporting Python 3. There
|
||
is both a command-line tool as well as a web interface at
|
||
<a class="reference external" href="https://caniusepython3.com">https://caniusepython3.com</a>.</p>
|
||
<p>The project also provides code which you can integrate into your test suite so
|
||
that you will have a failing test when you no longer have dependencies blocking
|
||
you from using Python 3. This allows you to avoid having to manually check your
|
||
dependencies and to be notified quickly when you can start running on Python 3.</p>
|
||
</div>
|
||
<div class="section" id="update-your-setup-py-file-to-denote-python-3-compatibility">
|
||
<h3>Update your <code class="docutils literal notranslate"><span class="pre">setup.py</span></code> file to denote Python 3 compatibility<a class="headerlink" href="#update-your-setup-py-file-to-denote-python-3-compatibility" title="Permalink to this headline">¶</a></h3>
|
||
<p>Once your code works under Python 3, you should update the classifiers in
|
||
your <code class="docutils literal notranslate"><span class="pre">setup.py</span></code> to contain <code class="docutils literal notranslate"><span class="pre">Programming</span> <span class="pre">Language</span> <span class="pre">::</span> <span class="pre">Python</span> <span class="pre">::</span> <span class="pre">3</span></code> and to not
|
||
specify sole Python 2 support. This will tell anyone using your code that you
|
||
support Python 2 <strong>and</strong> 3. Ideally you will also want to add classifiers for
|
||
each major/minor version of Python you now support.</p>
|
||
</div>
|
||
<div class="section" id="use-continuous-integration-to-stay-compatible">
|
||
<h3>Use continuous integration to stay compatible<a class="headerlink" href="#use-continuous-integration-to-stay-compatible" title="Permalink to this headline">¶</a></h3>
|
||
<p>Once you are able to fully run under Python 3 you will want to make sure your
|
||
code always works under both Python 2 & 3. Probably the best tool for running
|
||
your tests under multiple Python interpreters is <a class="reference external" href="https://pypi.org/project/tox">tox</a>. You can then integrate
|
||
tox with your continuous integration system so that you never accidentally break
|
||
Python 2 or 3 support.</p>
|
||
<p>You may also want to use the <code class="docutils literal notranslate"><span class="pre">-bb</span></code> flag with the Python 3 interpreter to
|
||
trigger an exception when you are comparing bytes to strings or bytes to an int
|
||
(the latter is available starting in Python 3.5). By default type-differing
|
||
comparisons simply return <code class="docutils literal notranslate"><span class="pre">False</span></code>, but if you made a mistake in your
|
||
separation of text/binary data handling or indexing on bytes you wouldn’t easily
|
||
find the mistake. This flag will raise an exception when these kinds of
|
||
comparisons occur, making the mistake much easier to track down.</p>
|
||
<p>And that’s mostly it! At this point your code base is compatible with both
|
||
Python 2 and 3 simultaneously. Your testing will also be set up so that you
|
||
don’t accidentally break Python 2 or 3 compatibility regardless of which version
|
||
you typically run your tests under while developing.</p>
|
||
</div>
|
||
<div class="section" id="consider-using-optional-static-type-checking">
|
||
<h3>Consider using optional static type checking<a class="headerlink" href="#consider-using-optional-static-type-checking" title="Permalink to this headline">¶</a></h3>
|
||
<p>Another way to help port your code is to use a static type checker like
|
||
<a class="reference external" href="http://mypy-lang.org/">mypy</a> or <a class="reference external" href="https://github.com/google/pytype">pytype</a> on your code. These tools can be used to analyze your code as
|
||
if it’s being run under Python 2, then you can run the tool a second time as if
|
||
your code is running under Python 3. By running a static type checker twice like
|
||
this you can discover if you’re e.g. misusing binary data type in one version
|
||
of Python compared to another. If you add optional type hints to your code you
|
||
can also explicitly state whether your APIs use textual or binary data, helping
|
||
to make sure everything functions as expected in both versions of Python.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||
<div class="sphinxsidebarwrapper">
|
||
<h3><a href="../contents.html">Table of Contents</a></h3>
|
||
<ul>
|
||
<li><a class="reference internal" href="#">Porting Python 2 Code to Python 3</a><ul>
|
||
<li><a class="reference internal" href="#the-short-explanation">The Short Explanation</a></li>
|
||
<li><a class="reference internal" href="#details">Details</a><ul>
|
||
<li><a class="reference internal" href="#drop-support-for-python-2-6-and-older">Drop support for Python 2.6 and older</a></li>
|
||
<li><a class="reference internal" href="#make-sure-you-specify-the-proper-version-support-in-your-setup-py-file">Make sure you specify the proper version support in your <code class="docutils literal notranslate"><span class="pre">setup.py</span></code> file</a></li>
|
||
<li><a class="reference internal" href="#have-good-test-coverage">Have good test coverage</a></li>
|
||
<li><a class="reference internal" href="#learn-the-differences-between-python-2-3">Learn the differences between Python 2 & 3</a></li>
|
||
<li><a class="reference internal" href="#update-your-code">Update your code</a><ul>
|
||
<li><a class="reference internal" href="#division">Division</a></li>
|
||
<li><a class="reference internal" href="#text-versus-binary-data">Text versus binary data</a></li>
|
||
<li><a class="reference internal" href="#use-feature-detection-instead-of-version-detection">Use feature detection instead of version detection</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#prevent-compatibility-regressions">Prevent compatibility regressions</a></li>
|
||
<li><a class="reference internal" href="#check-which-dependencies-block-your-transition">Check which dependencies block your transition</a></li>
|
||
<li><a class="reference internal" href="#update-your-setup-py-file-to-denote-python-3-compatibility">Update your <code class="docutils literal notranslate"><span class="pre">setup.py</span></code> file to denote Python 3 compatibility</a></li>
|
||
<li><a class="reference internal" href="#use-continuous-integration-to-stay-compatible">Use continuous integration to stay compatible</a></li>
|
||
<li><a class="reference internal" href="#consider-using-optional-static-type-checking">Consider using optional static type checking</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
|
||
<h4>Previous topic</h4>
|
||
<p class="topless"><a href="index.html"
|
||
title="previous chapter">Python HOWTOs</a></p>
|
||
<h4>Next topic</h4>
|
||
<p class="topless"><a href="cporting.html"
|
||
title="next chapter">Porting Extension Modules to Python 3</a></p>
|
||
<div role="note" aria-label="source link">
|
||
<h3>This Page</h3>
|
||
<ul class="this-page-menu">
|
||
<li><a href="../bugs.html">Report a Bug</a></li>
|
||
<li>
|
||
<a href="https://github.com/python/cpython/blob/3.7/Doc/howto/pyporting.rst"
|
||
rel="nofollow">Show Source
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="clearer"></div>
|
||
</div>
|
||
<div class="related" role="navigation" aria-label="related navigation">
|
||
<h3>Navigation</h3>
|
||
<ul>
|
||
<li class="right" style="margin-right: 10px">
|
||
<a href="../genindex.html" title="General Index"
|
||
>index</a></li>
|
||
<li class="right" >
|
||
<a href="../py-modindex.html" title="Python Module Index"
|
||
>modules</a> |</li>
|
||
<li class="right" >
|
||
<a href="cporting.html" title="Porting Extension Modules to Python 3"
|
||
>next</a> |</li>
|
||
<li class="right" >
|
||
<a href="index.html" title="Python HOWTOs"
|
||
>previous</a> |</li>
|
||
<li><img src="../_static/py.png" alt=""
|
||
style="vertical-align: middle; margin-top: -1px"/></li>
|
||
<li><a href="https://www.python.org/">Python</a> »</li>
|
||
<li>
|
||
<span class="language_switcher_placeholder">en</span>
|
||
<span class="version_switcher_placeholder">3.7.4</span>
|
||
<a href="../index.html">Documentation </a> »
|
||
</li>
|
||
|
||
<li class="nav-item nav-item-1"><a href="index.html" >Python HOWTOs</a> »</li>
|
||
<li class="right">
|
||
|
||
|
||
<div class="inline-search" style="display: none" role="search">
|
||
<form class="inline-search" action="../search.html" method="get">
|
||
<input placeholder="Quick search" type="text" name="q" />
|
||
<input type="submit" value="Go" />
|
||
<input type="hidden" name="check_keywords" value="yes" />
|
||
<input type="hidden" name="area" value="default" />
|
||
</form>
|
||
</div>
|
||
<script type="text/javascript">$('.inline-search').show(0);</script>
|
||
|
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
<div class="footer">
|
||
© <a href="../copyright.html">Copyright</a> 2001-2019, Python Software Foundation.
|
||
<br />
|
||
The Python Software Foundation is a non-profit corporation.
|
||
<a href="https://www.python.org/psf/donations/">Please donate.</a>
|
||
<br />
|
||
Last updated on Jul 13, 2019.
|
||
<a href="../bugs.html">Found a bug</a>?
|
||
<br />
|
||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 2.0.1.
|
||
</div>
|
||
|
||
</body>
|
||
</html> |