<?xml version="1.0" ?><entry xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.intertwingly.net/"><id>tag:ldpreload.com,2015-04-17:/blog/usr-bin-python-23</id><link href="https://ldpreload.com/blog/usr-bin-python-23" rel="alternate" type="text/html"/><title>A proposal for /usr/bin/python between Python 2 and 3</title><summary type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>I just got back from PyCon, where a few debian-python team members met to discuss some goals for Python packaging in Debian over the next release cycle. One item of interest is moving away from installing Python 2 by default (expecting it to be <a href="https://www.python.org/dev/peps/pep-0373/">desupported in 2020</a>), which raises questions …</p></div></summary><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>I just got back from PyCon, where a few debian-python team members met to discuss some goals for Python packaging in Debian over the next release cycle. One item of interest is moving away from installing Python 2 by default (expecting it to be <a href="https://www.python.org/dev/peps/pep-0373/">desupported in 2020</a>), which raises questions about what <code>/usr/bin/python</code> should mean. At the moment, it very strongly means Python 2 specifically, so there's a question about what it should be on a system with only Python 3 installed—should it become Python 3? Should it be nonexistent?</p>
<p><a href="https://www.python.org/dev/peps/pep-0394/">PEP 0394</a> recommends that Python 2 should be installed as <code>python2</code> and Python 3 as <code>python3</code>, and that <code>python</code> should mean Python 2 for now. I think it's important that <code>python</code> continue to mean Python 2, for the reasons described in the &quot;Migration Notes&quot; section of that PEP. In particular, I think it's very important that you should be able to install Python 2 (for the near future), even if your system did not ship with Python 2, and that the system version of Python 3 should not prevent you from using <code>python</code> to run Python 2 applications.</p>
<p>However, not shipping <code>/usr/bin/python</code> by default also has its downsides. I made a <a href="https://lists.debian.org/debian-python/2015/04/msg00046.html">suggestion on the debian-python list</a> for handling this: this blog post is a more detailed version of that proposal.</p>
<p>The basic motivation for this proposal is that third-party script authors love Python in part because it's just about universally available. <code>#!/usr/bin/env python</code>, today, is sort of like <code>#!/bin/sh</code>: you don't necessarily get nice things (Python 3-only features and bash-only features, respectively), but it works. If the <code>python</code> command stops existing, this is inconvenient for end users, who will need to manually edit scripts or install a version of Python themselves, and authors, who may just decide to use a worse language like <code>#!/bin/sh</code>. It's also bad for the Python language community, since it removes an incentive to use Python.</p>
<p>So it would be nice to keep the <code>python</code> command working usefully on both Python 2-only systems and Python 3-only systems. Fortunately, it's pretty doable to <a href="https://wiki.python.org/moin/PortingToPy3k/BilingualQuickRef">port code to work on both Python 2 and 3 with the same source</a>. Especially for third-party scripts with the goal of working out-of-the-box in as many places as possible, writing code to these restrictions isn't particularly onerous, since they needed to stay Python 2-compatible anyway. I've been writing a bunch of Python code recently as polyglot Python 2/3, and the biggest problem has been limiting myself to features in Python 2.7, not getting things to work in both versions of the language.</p>
<p>So here's the proposal: <strong>we install a wrapper binary as <code>python</code>, that defaults to launching Python 2</strong> (or reporting an error if Python 2 is not installed). However, <strong>Python 2/3-compatible scripts can include a specific marker indicating they can be run on Python 3</strong>, which makes these scripts able to run on both Python 2-only systems and Python 3-only systems.</p>
<p>This marker is based on the existing <code>coding</code>: syntax from <a href="https://www.python.org/dev/peps/pep-0263/">PEP 0263</a>: it's a &quot;magic comment&quot; on the first or second line of the form <code>pyversions=2.7+,3.3+</code>, indicating which Python major and minor versions are supported. A script compatible with both Python 2 and 3 should include one of these magic comments, and also include a shebang line launching just <code>python</code>. Here's an example based on one from PEP 0263:</p>
<div class="highlight"><pre><span/><code><span class="ch">#!/usr/bin/env python</span>
<span class="c1"># -*- coding=utf-8 -*- pyversions=2.6+,3.3+</span>

<span class="kn">from</span><span class="w"> </span><span class="nn">__future__</span><span class="w"> </span><span class="kn">import</span><span class="w"> </span><span class="n">print_function</span>

<span class="nb">print</span><span class="p">(</span><span class="sa">u</span><span class="s2">&quot;Hello world!&quot;</span><span class="p">)</span>
</code></pre></div>

<p>On new systems, the <code>python</code> command itself is a wrapper binary that knows what versions of the Python interpreter are installed. If it detects a <code>pyversions</code> comment, it will exec the newest Python interpreter compatible with the comment. For this example, if Python 3.3 is installed, it will launch that: if only Python 3.2 and 2.7 are installed, it will use Python 2.7, since Python 3.2 does not support the <code>u&quot;&quot;</code> literal syntax. Otherwise, it will assume that code is Python 2-only, and exec the newest Python 2 version installed. In either case, if a compatible interpreter cannot be found, it will print an error and exit.</p>
<p>On legacy systems, the <code>python</code> command refers to Python 2. So Python 2/3-compatible scripts will still be able to find an interpreter they are compatible with.</p>
<p>For legacy scripts that use a shebang stating just <code>python</code>, on both new and legacy systems, they will only ever run on Python 2, or not at all. This preserves the existing API, and avoids the poor user experience of running Python 2 scripts with the Python 3 interpreter, as PEP 0394 warns against doing.</p>
<p>However, the <code>python</code> wrapper supports running Python 2/3-compatible scripts with the Python 3 interpreter, which is useful for Python 3-only systems. A future version of Debian can choose to ship the Python 3 interpreter only, and remain compatible with third-party scripts that include the <code>pyversions</code> magic comment. Meanwhile, these third-party scripts can state <code>#!/usr/bin/env python</code> in their shebang, and remain compatible with legacy Python 2-only systems, including those (like Mac OS X) that do not include the <code>python2</code> symbolic link.</p>
<p>The <code>python</code> wrapper can run in three possible modes:</p>
<ul>
<li>As an interpreter, it follows the rules previously discussed. The &quot;interpreter&quot; mode covers both invocation via the shebang of a Python script, and invocation of the form <code>python script.py</code>.</li>
<li>For interactive use, it will just run the newest version of Python on the system. This allows, for instance, teachers of the language to have their students just run <code>python</code>, without the pedagogical speed bump of having to explain versions of the language. Running the latest major version of Python is safe, since the interpreter will print out its own version at startup.</li>
<li>Uses such as <code>python -c</code> are considered scripted use, since in this context, the <code>python</code> command is also serving as an API, and existing users of the API expect Python 2 just as much as scripts do. (Imagine, for instance, a shell script with <code>KEY=$(python -c &quot;import local_settings; print SECRET_KEY&quot;)</code>), which will fail if <code>python</code> means Python 3.) In this mode, the wrapper will instead look for an environment variable named <code>PYVERSIONS</code>. If this is set, it will be parsed as the value of the <code>pyversions</code> magic comment. So shell scripts that include Python 2/3-compatible <code>python -c</code> commands can e.g. <code>export PYVERSIONS=2.7+,3.3+</code> at the top of the script, and work on systems with either just Python 2 or just Python 3.</li>
</ul>
<p>The end result here is that third-party script authors can continue writing polyglot Python 2/3 code for the indefinite future, and be compatible with both existing Python 2-only distributions and newer Python 3-only distributions. Meanwhile, distributions can move towards installing Python 3 only by default and avoid installing Python 2 as far as possible, without closing off the ability to install Python 2 when needed, or breaking legacy Python 2-only code.</p>
<p>This is a good transition story for distributions like Debian, Ubuntu, and Fedora that are currently trying to move to Python 3 only, but don't want to break existing code. It's also a good transition story for distributions like Arch that have already started moving <code>/usr/bin/python</code> to Python 3: interactive and scripted use of the <code>python</code> command can continue to launch Python 3, but compatibility with third-party Python 2 scripts is regained. And most importantly, it's good for third-party script authors, who want to write one script that works on Debian, Ubuntu, Fedora, Arch, and Mac OS X alike, and for their end users.</p>
<p>I'm interested in initial feedback on this idea: if it generally seems like a good plan, I'll firm up the details and write it up as a formal PEP. There are a few details to work out, like how this interacts with the <code>py.exe</code> Windows launcher described in <a href="https://www.python.org/dev/peps/pep-0397/">PEP 0397</a> (if at all), but my focus is making <code>/usr/bin/python</code> useful and backwards-compatible on UNIX-like platforms.</p>
<p><strong>Edit 3 May 2015:</strong> I've uploaded a <a href="https://github.com/geofft/pythonmux">proof-of-concept implementation of this launcher to GitHub</a> to see what the overhead and complexity of this approach looks like.</p></div></content><updated planet:format="April 17, 2015 12:00 AM">2015-04-17T00:00:00Z</updated><published planet:format="April 17, 2015 12:00 AM">2015-04-17T00:00:00Z</published><category term="misc"/><author><name>Geoffrey Thomas</name></author><source><id>https://ldpreload.com/</id><link href="https://ldpreload.com/" rel="alternate" type="text/html"/><link href="https://ldpreload.com/feeds/all.atom.xml" rel="self" type="application/atom+xml"/><title>Geoffrey Thomas (geofft)</title><updated planet:format="December 18, 2018 12:00 AM">2018-12-18T00:00:00Z</updated><planet:bozo>false</planet:bozo><planet:format>atom10</planet:format><planet:items_per_page>60</planet:items_per_page><planet:http_etag>W/&quot;17ae121ba4ef3cf4c16eb98b537048cc&quot;</planet:http_etag><planet:encoding>utf-8</planet:encoding><planet:name>Geoffrey Thomas</planet:name><planet:css-id>geoffrey-thomas</planet:css-id><planet:days_per_page>0</planet:days_per_page><planet:http_last_modified>Tue, 27 Aug 2024 18:24:01 GMT</planet:http_last_modified><planet:http_status>200</planet:http_status></source></entry>