<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Posts about best practices</title><link>https://chriswarrick.com/</link><atom:link href="https://chriswarrick.com/blog/tags/best-practices.xml" rel="self" type="application/rss+xml" /><description>A rarely updated blog, mostly about programming.</description><lastBuildDate>Sat, 03 Apr 2021 11:00:00 GMT</lastBuildDate><generator>https://github.com/Kwpolska/YetAnotherBlogGenerator</generator><item><title>Python Virtual Environments in Five Minutes</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/</link><pubDate>Tue, 04 Sep 2018 18:15:00 GMT</pubDate><guid>https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/</guid><description>
In Python, virtual environments are used to isolate projects from each other
(if they require different versions of the same library, for example). They let
you install and manage packages without administrative privileges, and without
conflicting with the system package manager.  They also allow to quickly create
an environment somewhere else with the same dependencies.
Virtual environments are a crucial tool for any Python developer. And at that,
a very simple tool to work with.
</description><content:encoded><![CDATA[
<p>In Python, virtual environments are used to isolate projects from each other
(if they require different versions of the same library, for example). They let
you install and manage packages without administrative privileges, and without
conflicting with the system package manager.  They also allow to quickly create
an environment somewhere else with the same dependencies.</p>
<p>Virtual environments are a crucial tool for any Python developer. And at that,
a very simple tool to work with.</p>



<p>Let’s get started!</p>
<section id="install">
<h1>Install</h1>
<p>The best tool that can be used to create virtual environments is the
<a class="reference external" href="https://docs.python.org/3/library/venv.html">venv</a> module, which is part of
the standard library since Python 3.3.</p>
<p><code class="docutils literal">venv</code> is built into Python, and most users don’t need to install anything.
However, Debian/Ubuntu users will need to run <code class="docutils literal">sudo <span class="pre">apt-get</span> install <span class="pre">python3-venv</span></code> to make it work (due to Debian not installing some components
that <code class="docutils literal">venv</code> needs by default). <a class="brackets" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#footnote-1" id="footnote-reference-1" role="doc-noteref"><span class="fn-bracket">[</span>1<span class="fn-bracket">]</span></a></p>
<p>The alternative (and original, and previously standard) virtual environment tool is <a class="reference external" href="https://virtualenv.pypa.io/">virtualenv</a>. It works with Python 2.7, and has a couple
extra fetures (that you generally won’t need). virtualenv can be installed with your system package manager, or <code class="docutils literal">pip install <span class="pre">--user</span> virtualenv</code>.</p>
<p>Which one to use? Probably <code class="docutils literal">venv</code>. Both tools achieve the same goal in similar
ways. And if one of them does not work, you can try the other and it might just
work better.</p>
<p><em>(Terminology note: most of the time, the names of both tools are used
interchargeably, “venv” was often used as an abbreviation for “virtualenv”
before the stdlib tool was created)</em></p>
</section>
<section id="create">
<h1>Create</h1>
<p>To create a virtual environment named <code class="docutils literal">env</code>, you need to run the <code class="docutils literal">venv</code>
tool with the Python you want to use in that environment.</p>
<div class="code"><pre class="code text"><a id="rest_code_1fb767790a68420a97f0697df45d2e92-1" name="rest_code_1fb767790a68420a97f0697df45d2e92-1" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_1fb767790a68420a97f0697df45d2e92-1"></a>Linux:   $ python3 -m venv env
<a id="rest_code_1fb767790a68420a97f0697df45d2e92-2" name="rest_code_1fb767790a68420a97f0697df45d2e92-2" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_1fb767790a68420a97f0697df45d2e92-2"></a>Windows: &gt; py -m venv env
</pre></div>
<p>or, if you’re using <code class="docutils literal">virtualenv</code>:</p>
<div class="code"><pre class="code text"><a id="rest_code_d2d40ad9883b450e9264cf578d6f68af-1" name="rest_code_d2d40ad9883b450e9264cf578d6f68af-1" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_d2d40ad9883b450e9264cf578d6f68af-1"></a>$ python3 -m virtualenv env
<a id="rest_code_d2d40ad9883b450e9264cf578d6f68af-2" name="rest_code_d2d40ad9883b450e9264cf578d6f68af-2" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_d2d40ad9883b450e9264cf578d6f68af-2"></a>&gt; py -m virtualenv env
</pre></div>
<p>Afterwards, you will end up with a folder named <code class="docutils literal">env</code> that contains folders
named <code class="docutils literal">bin</code> (<code class="docutils literal">Scripts</code> on Windows — contains executables and scripts
installed by packages, including
<code class="docutils literal">python</code>), <code class="docutils literal">lib</code> (contains code), and <code class="docutils literal">include</code> (contains C headers).</p>
<p>Both tools install <code class="docutils literal">pip</code> and <code class="docutils literal">setuptools</code>, but <code class="docutils literal">venv</code> does not ship with
<code class="docutils literal">wheel</code>. In addition, the default versions tend to be more-or-less outdated.
Let’s upgrade them real quick: <a class="brackets" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#footnote-2" id="footnote-reference-2" role="doc-noteref"><span class="fn-bracket">[</span>2<span class="fn-bracket">]</span></a></p>
<div class="code"><pre class="code text"><a id="rest_code_39b9e0a93e0746fbac8d7b7f11734578-1" name="rest_code_39b9e0a93e0746fbac8d7b7f11734578-1" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_39b9e0a93e0746fbac8d7b7f11734578-1"></a>$ env/bin/python -m pip install --upgrade pip setuptools wheel
<a id="rest_code_39b9e0a93e0746fbac8d7b7f11734578-2" name="rest_code_39b9e0a93e0746fbac8d7b7f11734578-2" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_39b9e0a93e0746fbac8d7b7f11734578-2"></a>&gt; env\Scripts\python -m pip install --upgrade pip setuptools wheel
</pre></div>
<section id="where-to-store-virtual-environments">
<h2>Where to store virtual environments?</h2>
<p>While the tools allow you to put your virtual environments anywhere in the
system, it is not a desirable thing to do. There are two options:</p>
<ol class="arabic simple">
<li><p>Have one global place for them, like <code class="docutils literal">~/virtualenvs</code>.</p></li>
<li><p>Store them in each project’s directory, like <code class="docutils literal"><span class="pre">~/git/foobar/.venv</span></code>.</p></li>
</ol>
<p>The first option can be easier to manage, there are tools that can help manage
those (eg. <code class="docutils literal">virtualenvwrapper</code>, shell auto-activation scripts, or the
<code class="docutils literal">workon</code> functions described below).  The second option is equally easy to
work with, but comes with one caveat — you must add the venv directory to your
<code class="docutils literal">.gitignore</code> file (or <code class="docutils literal">.git/info/exclude</code> if you don’t want to commit
changes to <code class="docutils literal">.gitignore</code>), since you don’t want it in your repository (it’s
binary bloat, and works only on your machine).</p>
<p>If you pick the global virtual environment store option, you can use the following short
function (put it in <code class="docutils literal">.bashrc</code> / <code class="docutils literal">.zshrc</code> / your shell configuration file)
to get a simple way to activate an environment (by running <code class="docutils literal">workon foo</code>).
<code class="docutils literal">virtualenvwrapper</code> also has a <code class="docutils literal">workon</code> feature, although I don’t think
<code class="docutils literal">virtualenvwrapper</code> is really necessary and too helpful — the <code class="docutils literal">workon</code>
feature is handy though, and so here’s a way to do it without
<code class="docutils literal">virtualenvwrapper</code>:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-1"><code data-line-number="1"></code></a></td><td class="code"><code><a id="rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-1" name="rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-1"></a><span class="w"> </span><span class="nb">export</span><span class="w"> </span><span class="nv">WORKON_HOME</span><span class="o">=</span>~/virtualenvs
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-2"><code data-line-number="2"></code></a></td><td class="code"><code><a id="rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-2" name="rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-2"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-3"><code data-line-number="3"></code></a></td><td class="code"><code><a id="rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-3" name="rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-3"></a><span class="w"> </span><span class="k">function</span><span class="w"> </span>workon<span class="w"> </span><span class="o">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-4"><code data-line-number="4"></code></a></td><td class="code"><code><a id="rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-4" name="rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-4"></a><span class="w">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="nb">source</span><span class="w"> </span><span class="s2">&quot;</span><span class="nv">$WORKON_HOME</span><span class="s2">/</span><span class="nv">$1</span><span class="s2">/bin/activate&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-5"><code data-line-number="5"></code></a></td><td class="code"><code><a id="rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-5" name="rest_code_d4c94a5f476e4e5ab3fe0a7682a1bdb7-5"></a><span class="w"> </span><span class="o">}</span>
</code></td></tr></table></div><p>And for PowerShell fans, here’s a <code class="docutils literal">workon.ps1</code> script:</p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_33d18c86da274b70953d97d327e4e0e7-1"><code data-line-number="1"></code></a></td><td class="code"><code><a id="rest_code_33d18c86da274b70953d97d327e4e0e7-1" name="rest_code_33d18c86da274b70953d97d327e4e0e7-1"></a> <span class="nv">$WORKON_HOME</span> <span class="p">=</span> <span class="s2">&quot;$home\virtualenvs&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_33d18c86da274b70953d97d327e4e0e7-2"><code data-line-number="2"></code></a></td><td class="code"><code><a id="rest_code_33d18c86da274b70953d97d327e4e0e7-2" name="rest_code_33d18c86da274b70953d97d327e4e0e7-2"></a> <span class="p">&amp;</span> <span class="s2">&quot;$WORKON_HOME\</span><span class="p">$(</span><span class="nv">$args</span><span class="p">[</span><span class="n">0</span><span class="p">])</span><span class="s2">\Scripts\activate.ps1&quot;</span>
</code></td></tr></table></div><p>And for cmd.exe fans… you should switch to PowerShell, it’s a very nice and
friendly shell (though perhaps requiring some effort to learn how to be
productive with it).</p>
</section>
</section>
<section id="use">
<h1>Use</h1>
<p>There are three ways of working with virtual environments interactively (in a
shell):</p>
<ul class="simple">
<li><p>activation (run <code class="docutils literal">source env/bin/activate</code> on *nix;
<code class="docutils literal">env\Scripts\activate</code> on Windows) — it simplifies work and requires less
typing, although it can sometimes fail to work properly. (After installing
scripts, <code class="docutils literal">hash <span class="pre">-r</span></code> may be necessary on *nix to use them.)</p></li>
<li><p>executing <code class="docutils literal">env/bin/python</code> (<code class="docutils literal">env\Scripts\python</code>) and other scripts directly, as
activation only changes <code class="docutils literal">$PATH</code> and some helper variables — those variables
are not mandatory for operation, running the correct <code class="docutils literal">python</code> is, and that
method is failsafe.</p></li>
<li><p><a class="reference external" href="https://gist.github.com/datagrok/2199506">in subshells</a> (IMO, it’s bad UX)</p></li>
</ul>
<p>Whichever method you use, you must remember that without doing any of these
things, you will still be working with the system Python.</p>
<p>For non-interactive work (eg. crontab entries, system services, etc.),
activation and subshells are not viable solutions. In these cases, you must
always use the full path to Python.</p>
<p>Here are some usage examples (paths can be relative, of course):</p>
<div class="code"><pre class="code text"><a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-1" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-1" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-1"></a>## *nix, activation ##
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-2" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-2" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-2"></a>$ source /path/to/env/bin/activate
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-3" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-3" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-3"></a>(env)$ pip install Django
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-4" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-4" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-4"></a>(env)$ deactivate
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-5" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-5" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-5"></a>
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-6" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-6" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-6"></a>## *nix, manual execution ##
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-7" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-7" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-7"></a>$ /path/to/env/bin/pip install Django
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-8" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-8" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-8"></a>
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-9" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-9" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-9"></a>## Windows, activation ##
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-10" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-10" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-10"></a>&gt; C:\path\to\env\Scripts\activate
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-11" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-11" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-11"></a>(venv)&gt; pip install Django
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-12" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-12" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-12"></a>(venv)&gt; deactivate
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-13" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-13" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-13"></a>
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-14" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-14" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-14"></a>## Windows, manual execution ##
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-15" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-15" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-15"></a>&gt; C:\path\to\env\Scripts\pip install Django
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-16" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-16" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-16"></a>
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-17" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-17" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-17"></a>## Windows, updating pip/setuptools/wheel ##
<a id="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-18" name="rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-18" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_99f32ee1312e43b19d00feb1c5e6ae7f-18"></a>&gt; C:\path\to\env\Scripts\python -m pip install -U pip setuptools wheel
</pre></div>
<p>The same principle applies to running Python itself, or any other script
installed by a package. (With Django’s <code class="docutils literal">manage.py</code>, calling it as
<code class="docutils literal">./manage.py</code> requires activation, or you can run
<code class="docutils literal">venv/bin/python manage.py</code>.)</p>
<section id="moving-renaming-copying-environments">
<h2>Moving/renaming/copying environments?</h2>
<p>If you try to copy or rename a virtual environment, you will discover that the
copied environment does not work. This is because a virtual environment is
closely tied to both the Python it was created with, and the location it was
created in. (The “relocatable” option of <code class="docutils literal">virtualenv</code> does not work and is deprecated.) <a class="brackets" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#footnote-3" id="footnote-reference-3" role="doc-noteref"><span class="fn-bracket">[</span>3<span class="fn-bracket">]</span></a></p>
<p>However, this is very easy to fix. Instead of moving/copying, just create a new
environment in the new location. Then, run <code class="docutils literal">pip freeze &gt; requirements.txt</code> in
the old environment to create a list of packages installed in it. With that,
you can just run <code class="docutils literal">pip install <span class="pre">-r</span> requirements.txt</code> in the new environment to
install packages from the saved list. (Of course, you can copy <code class="docutils literal">requirements.txt</code>
between machines. In many cases, it will just work; sometimes, you might need a few
modifications to <code class="docutils literal">requirements.txt</code> to remove OS-specific stuff.)</p>
<div class="code"><pre class="code text"><a id="rest_code_118b2d54954242548dd7d3e590824a4c-1" name="rest_code_118b2d54954242548dd7d3e590824a4c-1" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_118b2d54954242548dd7d3e590824a4c-1"></a>$ oldenv/bin/pip freeze &gt; requirements.txt
<a id="rest_code_118b2d54954242548dd7d3e590824a4c-2" name="rest_code_118b2d54954242548dd7d3e590824a4c-2" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_118b2d54954242548dd7d3e590824a4c-2"></a>$ python3 -m venv newenv
<a id="rest_code_118b2d54954242548dd7d3e590824a4c-3" name="rest_code_118b2d54954242548dd7d3e590824a4c-3" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_118b2d54954242548dd7d3e590824a4c-3"></a>$ newenv/bin/pip install -r requirements.txt
<a id="rest_code_118b2d54954242548dd7d3e590824a4c-4" name="rest_code_118b2d54954242548dd7d3e590824a4c-4" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#rest_code_118b2d54954242548dd7d3e590824a4c-4"></a>(You may rm -rf oldenv now if you desire)
</pre></div>
<p>Note that it might also be necessary to re-create your virtual environment
after a Python upgrade, <a class="brackets" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#footnote-4" id="footnote-reference-4" role="doc-noteref"><span class="fn-bracket">[</span>4<span class="fn-bracket">]</span></a> so it might be handy to keep an up-to-date
<code class="docutils literal">requirements.txt</code> for your virtual environments (for many projects, it makes
sense to put that in the repository).</p>
<p>To manage those <code class="docutils literal">requirements.txt</code> files in a more orgnized yet still simple
way, you might be interested in <a class="reference external" href="https://github.com/jazzband/pip-tools">pip-tools</a>.</p>
</section>
</section>
<section id="frequently-asked-questions">
<h1>Frequently Asked Questions</h1>
<section id="im-using-virtualenv-do-i-need-to-install-it-for-each-python-i-want-to-use-it-with">
<h2>I’m using virtualenv. Do I need to install it for each Python I want to use it with?</h2>
<p>In most cases, you can use <code class="docutils literal">virtualenv <span class="pre">-p</span> pythonX env</code> to specify a different
Python version, but with some Python version combinations, that approach might
be unsuccessful. (The <code class="docutils literal">venv</code> module is tied to the Python version it’s
installed in.)</p>
</section>
<section id="im-the-only-user-on-my-system-do-i-still-need-virtual-environments">
<h2>I’m the only user on my system. Do I still need virtual environments?</h2>
<p>Yes, you do. First, you will still need separation between projects, sooner or
later.  Moreover, if you were to install packages system-wide with pip, you
might end up causing conflicts between packages installed by the system package
manager and by pip. Running <code class="docutils literal">sudo pip</code> is never a good idea because of this.</p>
</section>
<section id="im-using-docker-do-i-still-need-virtual-environments">
<h2>I’m using Docker. Do I still need virtual environments?</h2>
<p>They are still a good idea in that case. They protect you against any bad
system-wide Python packages your OS image might have (and one popular base OS
is famous for those). They don’t introduce any extra overhead, while allowing
to have a clean environment and the ability to re-create it outside of Docker
(eg. for local development without Docker)</p>
</section>
<section id="what-about-pipenv">
<h2>What about Pipenv?</h2>
<p>Pipenv is a dependency management tool. It isn’t compatible with most workflows, and comes with many issues. In my opinion, it’s not worth using (Also, that thing about it being an officially recommended tool? Turns out it’s not true.)</p>
<p>I also wrote a blog post detailing concerns with that tool, titled <a class="reference external" href="https://chriswarrick.com/blog/2018/07/17/pipenv-promises-a-lot-delivers-very-little/">Pipenv: promises a lot, delivers very little</a>.</p>
<p>Consider using <a class="reference external" href="https://github.com/jazzband/pip-tools">pip-tools</a> instead.</p>
</section>
</section>
<section id="footnotes">
<h1>Footnotes</h1>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="footnote-1" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#footnote-reference-1">1</a><span class="fn-bracket">]</span></span>
<p>The thing you’re actually installing is <code class="docutils literal">ensurepip</code>. In general, Debian isn’t exactly friendly with Python packaging.</p>
</aside>
<aside class="footnote brackets" id="footnote-2" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#footnote-reference-2">2</a><span class="fn-bracket">]</span></span>
<p>On Windows, you <em>must</em> run <code class="docutils literal">python <span class="pre">-m</span> pip</code> instead of <code class="docutils literal">pip</code> if you want to upgrade the package manager itself.</p>
</aside>
<aside class="footnote brackets" id="footnote-3" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#footnote-reference-3">3</a><span class="fn-bracket">]</span></span>
<p>All script shebangs contain the direct path to the environment’s Python executable.  Many things in the virtual environment are symlinks that point to the original Python.</p>
</aside>
<aside class="footnote brackets" id="footnote-4" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/#footnote-reference-4">4</a><span class="fn-bracket">]</span></span>
<p>Definitely after a minor version (3.x → 3.y) upgrade, sometimes (I’m looking at you Homebrew) after a patch version upgrade (3.x.y → 3.x.z) as well.</p>
</aside>
</aside>
</section>
]]></content:encoded><category>Python</category><category>best practices</category><category>devel</category><category>guide</category><category>Python</category><category>venv</category><category>virtual environments</category><category>virtualenv</category></item><item><title>Spawning subprocesses smartly and securely</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/</link><pubDate>Sat, 02 Sep 2017 18:40:00 GMT</pubDate><guid>https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/</guid><description>
As part of your code, you may be inclined to call a command to do
something. But is it always a good idea? How to do it safely? What happens
behind the scenes?
</description><content:encoded><![CDATA[
<p>As part of your code, you may be inclined to call a command to do
something. But is it always a good idea? How to do it safely? What happens
behind the scenes?</p>



<p>This article is written from a general perspective, with a Unix/C bias and a
very slight Python bias. The problems mentioned apply to all languages in most
environments, including Windows.</p>
<section id="use-the-right-tool-for-the-job">
<h1>Use the right tool for the job</h1>
<p>By calling another process, you introduce a third-party dependency.
That dependency isn’t controlled by your code, and your code becomes more fragile.
The problems include:</p>
<ul class="simple">
<li><p>the program is not installed, or even available, for the user’s OS of choice</p></li>
<li><p>the program is not in the <code class="docutils literal">$PATH</code> your process gets</p></li>
<li><p>the hard-coded path is not correct on the end user’s system</p></li>
<li><p>the program is in a different version (eg. GNU vs. BSD, updates/patches),
which means different option names or other behaviors</p></li>
<li><p>the program’s output is not what you expected due to user config (including
locale)</p></li>
<li><p>error reporting is based on numeric exit codes, and the meaning of those
differs between programs (<em>if</em> they have meaning besides 0/1 in the first
place)</p></li>
</ul>
<p>On the other hand, if your code uses a lot of subprocesses, perhaps you should
stay with Bash. You can do the harder parts with Python, Ruby, or some other
language by calling them from within your Bash script.</p>
</section>
<section id="dont-spawn-subprocesses-if-theres-an-alternative">
<h1>Don’t spawn subprocesses if there’s an alternative</h1>
<p>Spawning a subprocess always incurs a (minor) <a class="brackets" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#footnote-1" id="footnote-reference-1" role="doc-noteref"><span class="fn-bracket">[</span>1<span class="fn-bracket">]</span></a> performance hit minor
compared to the alternatives. With that in mind, and the resiliency issues
listed above, you should always try to find an alternative for the
external command.</p>
<p>The simplest ones are the basic Unix utilities. Replace <code class="docutils literal">grep</code>, <code class="docutils literal">sed</code> and
<code class="docutils literal">awk</code> with string operations and regular expressions. Filesystem utilities
will have equivalents — for Python, in <code class="docutils literal">os</code> or <code class="docutils literal">shutil</code>. Your language of
choice can also handle things like networking (don’t call <code class="docutils literal">curl</code>), file
compression, working with date/time…</p>
<p>Similarly, you should check if there are packages available that already do
what you want — library bindings or re-implementations. And if there isn’t,
perhaps you could help the world by writing one of those and sharing it?</p>
<p>One more important thing: if the program uses the same language as your code,
then you should try to import the code and run it from the same process instead
of spawning a process, if this is feasible.</p>
</section>
<section id="security-considerations-shells-spaces-and-command-injection">
<h1>Security considerations: shells, spaces, and command injection</h1>
<p>We come to the most important part of this article: how to spawn subprocesses
without compromising your system. When you spawn a subprocess on a typical Unix
system,  <code class="docutils literal">fork()</code> is called, and your process is copied. Many modern Unix
systems have a copy-on-write implementation of that syscall, meaning that the
operation does not result in copying all the memory of the host process over.
Forking is (almost) immediately followed by calling <code class="docutils literal">execve()</code> (or a helper
function from the exec family) <a class="brackets" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#footnote-2" id="footnote-reference-2" role="doc-noteref"><span class="fn-bracket">[</span>2<span class="fn-bracket">]</span></a> in the child process — that function
<em>transforms the calling process into a new process</em> <a class="brackets" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#footnote-3" id="footnote-reference-3" role="doc-noteref"><span class="fn-bracket">[</span>3<span class="fn-bracket">]</span></a>. This technique is
called <em>fork-exec</em> and is the typical way to spawn a new process on Unix. <a class="brackets" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#footnote-4" id="footnote-reference-4" role="doc-noteref"><span class="fn-bracket">[</span>4<span class="fn-bracket">]</span></a></p>
<p>There are two ways to access this API, from the C perspective:</p>
<ul>
<li><p>directly, by calling <code class="docutils literal">fork()</code> and <code class="docutils literal"><span class="pre">exec*()</span></code> (or <code class="docutils literal">posix_spawn()</code>), and providing an array of
arguments passed to the process, or</p></li>
<li><p>through the shell (<code class="docutils literal">sh</code>), usually by calling <code class="docutils literal">system()</code>. As Linux’s
manpage for <code class="docutils literal">system(3)</code> puts it,</p>
<blockquote>
<p>The <code class="docutils literal">system()</code> library function uses <code class="docutils literal">fork(2)</code> to create a child process that executes the shell command specified in command using <code class="docutils literal">execl(3)</code> as follows:</p>
<div class="code"><pre class="code c"><a id="rest_code_ba3def1e6d134f318af0424fea199c9a-1" name="rest_code_ba3def1e6d134f318af0424fea199c9a-1" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_ba3def1e6d134f318af0424fea199c9a-1"></a><span class="n">execl</span><span class="p">(</span><span class="s">&quot;/bin/sh&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;sh&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;-c&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">command</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span>
</pre></div>
</blockquote>
</li>
</ul>
<p>If you go through the shell, you pass one string argument, whereas <code class="docutils literal"><span class="pre">exec*()</span></code> demands you to specify arguments separately. Let’s write a sample program to print all the arguments it receives. I’ll do it in Python to get a more readable output.</p>
<div class="code"><pre class="code python"><a id="rest_code_ce72efbf58624ae598862ad3319d5d48-1" name="rest_code_ce72efbf58624ae598862ad3319d5d48-1" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_ce72efbf58624ae598862ad3319d5d48-1"></a><span class="ch">#!/usr/bin/env python3</span>
<a id="rest_code_ce72efbf58624ae598862ad3319d5d48-2" name="rest_code_ce72efbf58624ae598862ad3319d5d48-2" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_ce72efbf58624ae598862ad3319d5d48-2"></a><span class="kn">import</span><span class="w"> </span><span class="nn">sys</span>
<a id="rest_code_ce72efbf58624ae598862ad3319d5d48-3" name="rest_code_ce72efbf58624ae598862ad3319d5d48-3" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_ce72efbf58624ae598862ad3319d5d48-3"></a><span class="nb">print</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">)</span>
</pre></div>
<p>Let’s see what appears:</p>
<div class="code"><pre class="code text"><a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-1" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-1" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-1"></a>$ ./argv.py foo bar
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-2" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-2" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-2"></a>[&#39;./argv.py&#39;, &#39;foo&#39;, &#39;bar&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-3" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-3" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-3"></a>$ ./argv.py &#39;foo bar&#39;
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-4" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-4" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-4"></a>[&#39;./argv.py&#39;, &#39;foo bar&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-5" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-5" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-5"></a>$ ./argv.py foo\ bar baz
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-6" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-6" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-6"></a>[&#39;./argv.py&#39;, &#39;foo bar&#39;, &#39;baz&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-7" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-7" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-7"></a>
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-8" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-8" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-8"></a>$ ./argv.py $(date)
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-9" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-9" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-9"></a>[&#39;./argv.py&#39;, &#39;Sat&#39;, &#39;Sep&#39;, &#39;2&#39;, &#39;16:54:52&#39;, &#39;CEST&#39;, &#39;2017&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-10" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-10" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-10"></a>$ ./argv.py &quot;$(date)&quot;
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-11" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-11" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-11"></a>[&#39;./argv.py&#39;, &#39;Sat Sep  2 16:54:52 CEST 2017&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-12" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-12" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-12"></a>
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-13" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-13" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-13"></a>$ ./argv.py /usr/*
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-14" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-14" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-14"></a>[&#39;./argv.py&#39;, &#39;/usr/X11&#39;, &#39;/usr/X11R6&#39;, &#39;/usr/bin&#39;, &#39;/usr/include&#39;, &#39;/usr/lib&#39;, &#39;/usr/libexec&#39;, &#39;/usr/local&#39;, &#39;/usr/sbin&#39;, &#39;/usr/share&#39;, &#39;/usr/standalone&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-15" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-15" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-15"></a>$ ./argv.py &quot;/usr/*&quot;
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-16" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-16" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-16"></a>[&#39;./argv.py&#39;, &#39;/usr/*&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-17" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-17" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-17"></a>
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-18" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-18" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-18"></a>$ ./argv.py $EDITOR
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-19" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-19" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-19"></a>[&#39;./argv.py&#39;, &#39;nvim&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-20" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-20" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-20"></a>
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-21" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-21" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-21"></a>$ $PWD/argv.py foo bar
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-22" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-22" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-22"></a>[&#39;/Users/kwpolska/Desktop/blog/subprocess/argv.py&#39;, &#39;foo&#39;, &#39;bar&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-23" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-23" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-23"></a>$ ./argv.py a{b,c}d
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-24" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-24" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-24"></a>[&#39;./argv.py&#39;, &#39;abd&#39;, &#39;acd&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-25" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-25" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-25"></a>
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-26" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-26" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-26"></a>$ python argv.py foo bar | cat
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-27" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-27" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-27"></a>[&#39;argv.py&#39;, &#39;foo&#39;, &#39;bar&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-28" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-28" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-28"></a>$ python argv.py foo bar &gt; foo.txt
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-29" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-29" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-29"></a>$ cat foo.txt
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-30" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-30" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-30"></a>[&#39;argv.py&#39;, &#39;foo&#39;, &#39;bar&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-31" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-31" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-31"></a>
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-32" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-32" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-32"></a>$ ./argv.py foo; ls /usr
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-33" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-33" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-33"></a>[&#39;./argv.py&#39;, &#39;foo&#39;]
<a id="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-34" name="rest_code_8db936d5a5784d2eba2d26845ebcc5c4-34" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#rest_code_8db936d5a5784d2eba2d26845ebcc5c4-34"></a>X11@        X11R6@      bin/        include/    lib/        libexec/    local/      sbin/       share/      standalone/
</pre></div>
<p>As you can see, the following things are handled by the shell (the process is unaware of this occurring):</p>
<ul class="simple">
<li><p>quotes and escapes</p></li>
<li><p>expanding expressions in braces</p></li>
<li><p>expanding variables</p></li>
<li><p>wildcards (glob, <code class="docutils literal">*</code>)</p></li>
<li><p>redirections and pipes (<code class="docutils literal">&gt; &gt;&gt; |</code>)</p></li>
<li><p>command substitution (backticks or <code class="docutils literal"><span class="pre">$(…)</span></code>)</p></li>
<li><p>running multiple commands on the same line (<code class="docutils literal">; &amp;&amp; || &amp;</code>)</p></li>
</ul>
<p>The list is full of potential vulnerabilities. If end users are in control of
the arguments passed, and you go through the shell, they can
<strong>execute arbitrary commands</strong> or even <strong>get full shell access</strong>. Even in other
cases, you’ll have to <em>depend on the shell’s parsing</em>, which introduces an
unnecessary indirection.</p>
</section>
<section id="tl-dr-how-to-do-this-properly-in-your-language-of-choice">
<h1>TL;DR: How to do this properly in your language of choice</h1>
<p>To ensure spawning subprocess is done securely, <strong>do not use the shell in between</strong>. If you need any of the operations I listed above as part of your command — wildcards, pipes, etc. — you will need to take care of them in your code; most languages have those features built-in.</p>
<dl class="simple dl-horizontal">
<dt>In C (Unix)</dt>
<dd><p>Perform fork-exec by yourself, or use <code class="docutils literal">posix_spawn()</code>. This also lets you communicate with the process if you open a pipe and make it stdout of the child process. Never use <code class="docutils literal">system()</code>.</p>
</dd>
<dt>In Python</dt>
<dd><p>Use the subprocess module. Always pass <code class="docutils literal">shell=False</code> and give it a <em>list</em> of arguments. With asyncio, use <code class="docutils literal">asyncio.create_subprocess_exec</code> (and not <code class="docutils literal">_shell</code>), but note it takes <code class="docutils literal">*args</code> and not a list. Never use <code class="docutils literal">os.system</code> and <code class="docutils literal">os.popen</code>.</p>
</dd>
<dt>In Ruby</dt>
<dd><p>Pass arrays to <code class="docutils literal">IO.popen</code>. Pass multiple arguments to <code class="docutils literal">system()</code> (<code class="docutils literal"><span class="pre">system([&quot;ls&quot;,</span> <span class="pre">&quot;ls&quot;])</span></code> or <code class="docutils literal"><span class="pre">system(&quot;ls&quot;,</span> <span class="pre">&quot;-l&quot;)</span></code>). Never use <code class="docutils literal">%x{command}</code> or backticks.</p>
</dd>
<dt>In Java</dt>
<dd><p>Pass arrays to <code class="docutils literal">Runtime.exec</code>. Pass multiple arguments or list to <code class="docutils literal">ProcessBuilder</code>.</p>
</dd>
<dt>In PHP</dt>
<dd><p>All the standard methods go through the shell. Try <code class="docutils literal">escapeshellcmd()</code>, <code class="docutils literal">escapeshellarg()</code> — or better, switch to Python. Or anything, really.</p>
</dd>
<dt>In Go</dt>
<dd><p><code class="docutils literal">os/exec</code> and <code class="docutils literal">os.StartProcess</code> are safe.</p>
</dd>
<dt>In Node.js</dt>
<dd><p>Use <code class="docutils literal">child_process.execFile</code> or <code class="docutils literal">child_process.spawn</code> with <code class="docutils literal">shell</code> set to false.</p>
</dd>
<dt>Elsewhere</dt>
<dd><p>You should be able to specify multiple strings (using variadic arguments,
arrays, or otherwise standard data structures of your language of choice) as
the command line. Otherwise, you might be running into something
shell-related.</p>
</dd>
</dl>
</section>
<section id="the-part-where-i-pretend-i-know-something-about-windows">
<h1>The part where I pretend I know something about Windows</h1>
<p>On Windows, argument lists are always passed to processes as strings (Python
joins them semi-intelligently if it gets a list). Redirections and variables
work in shell mode, but globs (asterisks) are always left for the called
process to handle.</p>
<p>Some useful functions are implemented as shell built-ins — in that case, you
need to call it via the shell.</p>
<p>Internals: There is no <code class="docutils literal">fork()</code> on Windows. Instead, <code class="docutils literal">CreateProcess()</code>,
<code class="docutils literal">ShellExecute()</code>, or lower-level <code class="docutils literal"><span class="pre">spawn*()</span></code> functions are used. <code class="docutils literal">cmd.exe
/c</code> is called in shell calls.</p>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="footnote-1" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#footnote-reference-1">1</a><span class="fn-bracket">]</span></span>
<p>Unless your operating system does not implement copy-on-write forking — in that case, you might even run out of memory if you use too much of it.</p>
</aside>
<aside class="footnote brackets" id="footnote-2" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#footnote-reference-2">2</a><span class="fn-bracket">]</span></span>
<p>The function that does the real work is <code class="docutils literal">execve()</code>, which takes an exact path, an array of arguments, and takes environment variables as input. Other variants can also perform a <code class="docutils literal">$PATH</code> search, take argv as variadic arguments, and inherit environment from the current process. <code class="docutils literal">execl()</code> does the last two.</p>
</aside>
<aside class="footnote brackets" id="footnote-3" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#footnote-reference-3">3</a><span class="fn-bracket">]</span></span>
<p>Quoted from <code class="docutils literal">execve(2)</code> <a class="reference external" href="https://www.freebsd.org/cgi/man.cgi?query=execve&amp;sektion=2">man page</a> from FreeBSD.</p>
</aside>
<aside class="footnote brackets" id="footnote-4" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="https://chriswarrick.com/blog/2017/09/02/spawning-subprocesses-smartly-and-securely/#footnote-reference-4">4</a><span class="fn-bracket">]</span></span>
<p>An alternative is <code class="docutils literal">posix_spawn()</code>, but it usually does fork-exec, unless your platform does not support forking.</p>
</aside>
</aside>
</section>
]]></content:encoded><category>Programming</category><category>best practices</category><category>C</category><category>devel</category><category>guide</category><category>Linux</category><category>Python</category><category>security</category><category>subprocess</category><category>Unix</category></item><item><title>Setting up a Python development environment</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2017/07/03/setting-up-a-python-development-environment/</link><pubDate>Mon, 03 Jul 2017 10:40:00 GMT</pubDate><guid>https://chriswarrick.com/blog/2017/07/03/setting-up-a-python-development-environment/</guid><description>
Setting up Python is usually simple, but there are some places where newcomers
(and experienced users) need to be careful. What versions are there? What’s the
difference between Python, CPython, Anaconda, PyPy? Those and many other
questions may stump new developers, or people wanting to use Python.
</description><content:encoded><![CDATA[
<p>Setting up Python is usually simple, but there are some places where newcomers
(and experienced users) need to be careful. What versions are there? What’s the
difference between Python, CPython, Anaconda, PyPy? Those and many other
questions may stump new developers, or people wanting to use Python.</p>



<p>Note: this guide is opinionated.</p>
<section id="glossary-and-questions">
<h1>Glossary and questions</h1>
<section id="python-versions-2-vs-3">
<h2>Python versions: 2 vs 3</h2>
<p>The Python community has undergone sort of a <em>schism</em> in recent years. Python
3, released in 2008, broke backwards compatibility: deprecated some bad
constructs and libraries (eg. <code class="docutils literal">raw_input()</code> became <code class="docutils literal">input()</code> and the
original Python 2 function that ran code input by users is gone; <code class="docutils literal">print()</code>
became a function; many things that returned lists now are iterators — <code class="docutils literal">zip</code>,
<code class="docutils literal">range</code>), and completely remodelled strings (which are now Unicode by
default, and the interpreter behavior is stricter when the wrong type is used)</p>
<p>For new code, you should use Python 3. <a class="reference external" href="https://python3wos.appspot.com/">Most popular packages support Python 3</a>, and many of them support both Pythons at
the same time. The early bugs were ironed out in the first few point releases,
some features that made porting easier were added (back).</p>
<p>But what if you end up needing Python 2 later? No problem: you can learn the
differences in a short time, and with the help of a few libraries (eg. <code class="docutils literal">six</code>)
you can easily write code that is compatible with Python 2 and 3 at the same
time, using the same codebase (most libraries out there do that).</p>
<p>Python 2 will go EOL and lose official support and updates in 2020.</p>
<p>Read more: <a class="reference external" href="https://wiki.python.org/moin/Python2orPython3">Python 2 or Python 3 on Python Wiki</a></p>
</section>
<section id="can-i-run-multiple-pythons-on-the-same-machine">
<h2>Can I run multiple Pythons on the same machine?</h2>
<p>Yes. Note that multiple Python interpreters are completely separate: they have
their own pip and packages, and you can’t run Python 2 code in a Python 3
interpreter. You need to specify which interpreter to use when installing
packages and running some scripts (eg. <code class="docutils literal">pip2</code>, <code class="docutils literal">pip3</code> or <code class="docutils literal">python3 <span class="pre">-m</span> pip</code>).</p>
<p>It’s best to limit yourself to the latest Python 2 and 3 versions. Python is
backwards-compatible within the major release, so Python 2.7 runs code
written with older 2.x versions in mind.</p>
</section>
<section id="implementations">
<h2>Implementations</h2>
<p>A programming language is an abstract construct. To run code written in that
language, an interpreter or compiler needs to be written. In Python’s case,
there’s a plethora of implementations. Some of them are:</p>
<ul class="simple">
<li><p><strong>CPython</strong> is the reference implementation. This is the implementation
distributed on <a class="reference external" href="https://python.org/">https://python.org/</a> and as part of many operating systems.
Most Python features are first implemented in CPython, and then they are
ported to other implementations.  If you don’t know what to choose, use
CPython.</p></li>
<li><p><strong>PyPy</strong> is a fast implementation, written in a subset of Python. It’s compatible with
Python 2.7 and 3.5 (beta support). It can run all pure Python code, and many
extension libraries that use CFFI.</p></li>
<li><p><strong>IronPython</strong> is a .NET CLR implementation. It can integrate with .NET code.</p></li>
<li><p><strong>Jython</strong> is a Java JVM implementation. It can integrate with Java code, as
well as other JVM languages.</p></li>
</ul>
<p>Read more: <a class="reference external" href="https://wiki.python.org/moin/PythonImplementations">Python Implementations on Python Wiki</a></p>
</section>
<section id="distributions">
<h2>Distributions</h2>
<p>There are also Python (CPython) distributions. They ship the CPython
interpreter and add some extra packages/features.  They are maintained by other
communities or corporate entities.</p>
<p>The most popular third-party distribution is <a class="reference external" href="https://www.continuum.io/downloads">Anaconda</a> from Continuum Analytics. It’s popular
for data scientists, and includes over 100 packages, with extra pre-built
binaries available from the <code class="docutils literal">conda</code> package manager.</p>
<p>I personally recommend to avoid Anaconda:</p>
<ul class="simple">
<li><p>Most packages have binary wheels for Windows, macOS and Linux (yes, Linux!)
making the installation as simple as <code class="docutils literal">pip install numpy</code>.</p></li>
<li><p>You waste disk space for packages Anaconda installs that you won’t ever need.</p></li>
<li><p>It’s provided by some random for-profit company.</p></li>
<li><p>I’ve seen bugs that were not reproducible outside of Anaconda.</p></li>
<li><p>You can still do data science using the official distribution. There’s
nothing special about Anaconda.</p></li>
</ul>
<p>Read more: <a class="reference external" href="https://wiki.python.org/moin/PythonDistributions">Python distributions on Python Wiki</a></p>
</section>
<section id="can-i-make-exe-files-from-python-programs">
<h2>Can I make .exe files from Python programs?</h2>
<p>Yes, you can. There are tools for this — <a class="reference external" href="http://www.pyinstaller.org/">PyInstaller</a> is the best one. Note that you usually need to
run it on the destination operating system. And remember that “compiling” to
exe files like that <strong>is not</strong> a security measure — your source code is still
easily recoverable. (It’s not a security measure in other languages either,
even if getting source code back might be more expensive/tricky in those.)</p>
</section>
<section id="where-to-learn-python-where-to-get-help">
<h2>Where to learn Python? Where to get help?</h2>
<p>The choice of learning material is important. If you get a bad book, it might
discourage you from learning (because it’s boring), or may teach you
bad/outdated practices.</p>
<p>If you can already program in another language, I recommend the <a class="reference external" href="https://docs.python.org/3/tutorial/">official
Python tutorial</a>. For newcomers to
programming, I recommend <a class="reference external" href="http://greenteapress.com/wp/think-python-2e/">Think Python</a> or <a class="reference external" href="https://automatetheboringstuff.com/">Automate the Boring Stuff
with Python</a>.  They teach Python 3, and
(mostly) best practices.</p>
<p>If you need help, try <code class="docutils literal">#python</code> on freenode IRC, the <a class="reference external" href="https://mail.python.org/mailman/listinfo/tutor">Tutor</a> or <a class="reference external" href="https://mail.python.org/mailman/listinfo/python-list">Python-list</a> mailing lists, or a bunch of other communities. (I’m a regular on <code class="docutils literal">#python</code>)</p>
</section>
</section>
<section id="installing-python">
<h1>Installing Python</h1>
<p>This guide will focus on installing CPython 2.7 and 3.x (latest), using the standard
distribution. This choice is satisfactory for most people. Third-party
distributions, while handy in some cases, are not needed for most. (See
<a class="reference internal" href="https://chriswarrick.com/blog/2017/07/03/setting-up-a-python-development-environment/#distributions">Distributions</a> for arguments)</p>
<p>Throughout this guide, I’ll refer to the Python interpreter executable as
<code class="docutils literal">python</code>. The exact name depends on your system and desired version. On most
OSes, <code class="docutils literal">python</code> is Python 2 and <code class="docutils literal">python3</code> is 3; <code class="docutils literal">python2</code> should also
exist.  On Arch Linux, <code class="docutils literal">python</code> is Python 3. On Windows, use the <code class="docutils literal">py</code>
launcher.</p>
<section id="windows">
<h2>Windows</h2>
<p>Download the installer(s): <a class="reference external" href="https://www.python.org/downloads/">https://www.python.org/downloads/</a></p>
<p>Those installers come with <code class="docutils literal">pip</code>, and modern Python 3.x versions come with
the <code class="docutils literal">py</code> launcher.  You can use that launcher to pick a specific Python
version, eg.:</p>
<ul class="simple">
<li><p><code class="docutils literal">py <span class="pre">-3</span> <span class="pre">-m</span> pip install &lt;package&gt;</code></p></li>
<li><p><code class="docutils literal">py <span class="pre">-2</span> somefile.py</code></p></li>
<li><p><code class="docutils literal">py <span class="pre">-2.7</span></code></p></li>
<li><p><code class="docutils literal">py</code> (default system version)</p></li>
</ul>
<p>It’s recommended for most use, and mandatory for upgrading pip.</p>
<p>The 32-bit versions are more versatile. Most packages support both (the only
exception I’m aware of is Tensorflow, which only allows 64-bit Python 3.5 as of
now).</p>
</section>
<section id="macos">
<h2>macOS</h2>
<p>macOS ships with Python 2.7.10 (as of macOS Sierra). It’s not the latest
version; it’s good enough for most people, but I still recommend installing
your own (the system Python doesn’t include <code class="docutils literal">pip</code>, for example). You can
install the latest 2.7 version, as well as Python 3, using a package manager. I
recommend Homebrew — it’s the most popular solution, and lets you install many
other packages.</p>
<p><strong>DO NOT</strong> use the python.org installers: they do not have uninstallers, so you
will have outdated versions lying around after some time. There is no
auto-update as well.  <strong>DO NOT</strong> attempt to remove the system-installed Python,
this will only damage your system and you’ll need to reinstall.</p>
<p>If you already have a package manager installed (MacPorts, Fink), don’t install
a new one and just use the existing one.</p>
<ol class="arabic simple">
<li><p>Install <a class="reference external" href="https://brew.sh/">Homebrew</a>.</p></li>
<li><p>Run <code class="docutils literal">brew install python python3</code>.</p></li>
<li><p>You should now have <code class="docutils literal">python</code>, <code class="docutils literal">python3</code>, <code class="docutils literal">pip</code> and <code class="docutils literal">pip3</code>.</p></li>
</ol>
<p>To update Homebrew and Python, run <code class="docutils literal">brew update</code>.</p>
</section>
<section id="linux-and-other-unix-like-oses">
<h2>Linux (and other Unix-like OSes)</h2>
<p>On Linux, there usually are good enough packages in your OS repositories. You
should be able to install the appropriate package for Python (2 and/or 3).
Most (if not all) distributions require Python — <strong>do not</strong> remove the
pre-installed packages, and be careful not to overwrite them with something
newer.</p>
<p>If the version that ships with your distribution is too old, there are some
options. There might be some repositories with better versions, eg. the
<a class="reference external" href="https://launchpad.net/~fkrull/+archive/ubuntu/deadsnakes">deadsnakes PPA</a>
for Ubuntu. Then there’s the other option of compiling Python. There
are some tools to help with this, like <code class="docutils literal">pyenv</code> or <code class="docutils literal">pythonz</code> (they can also
manage multiple Python versions), or you can do it manually.
The instructions depend on your exact requirements, but here’s a summary:</p>
<ol class="arabic simple">
<li><p>Download the <a class="reference external" href="https://www.python.org/downloads/source/">source distribution from Python.org</a> and unpack it. Go into the unpacked source directory.</p></li>
<li><p>Ensure you’ve got a functional C compiler and Python’s dependencies. You can
usually use your system’s package manager to install the build dependencies
of your system Python. Some dependencies are optional (eg. <code class="docutils literal">sqlite3</code>
requires SQLite headers).</p></li>
<li><p>Run <code class="docutils literal">./configure <span class="pre">--prefix=/opt/python3.6</span></code> and then <code class="docutils literal">make</code>. (You may add other options to both. It will
take a while.)</p></li>
<li><p>Run <code class="docutils literal">make altinstall</code> as root. Avoid <code class="docutils literal">make install</code>, as it can override
<code class="docutils literal">python</code> executables.</p></li>
</ol>
<p>Remember: compiling Python should be considered a <strong>last resort</strong>, unless you
have very specific Python version requirements.</p>
</section>
</section>
<section id="installing-packages">
<h1>Installing packages</h1>
<p>To install third-party packages, you should use pip, the Python package
manager. If you’re using Windows or macOS (from Homebrew), pip is included with
your copy of Python.  If you’re on Linux and installed Python from a system
repository, install the correct system package (<code class="docutils literal"><span class="pre">python-pip</span></code>,
<code class="docutils literal"><span class="pre">python3-pip</span></code>). If you compiled your own Python, pip is also included.</p>
<p>To run pip, use <code class="docutils literal">py <span class="pre">-m</span> pip</code> (Windows), <code class="docutils literal">python <span class="pre">-m</span> pip</code> (other platforms),
or the short <code class="docutils literal">pip</code>/<code class="docutils literal">pip3</code> commands.</p>
<p><strong>NEVER use sudo pip.</strong> This can cause numerous problems:</p>
<ul class="simple">
<li><p>conflicts between packages installed by pip and your system package
manager</p></li>
<li><p>pip modifying system packages, leading to issues when updating them, or
breaking dependencies</p></li>
<li><p>no isolation between package versions, which is sometimes needed to satisfy
dependencies</p></li>
</ul>
<p>Note that a package install is specific to the Python interpreter used to run
<code class="docutils literal">pip</code>. Packages installed to a virtualenv are separate from system packages;
packages installed for “global” Python 2.7 are separate from 3.6 packages.
Virtual environments generally don’t use the system packages, unless
specifically enabled during creation.</p>
<p>Some distros have popular packages in their repositories. Sometimes they’re
good; in other cases they’re terribly outdated or they lack important
components, making package managers angry and sick of supporting a 2-year-old
version. (Especially since most bugs are closed with “we’ve fixed that long
ago”)</p>
<section id="user-installs">
<h2>User installs</h2>
<p>At a small scale, you can install packages with pip for a single user.  Use
<code class="docutils literal">pip install <span class="pre">--user</span> PACKAGE</code> to do this. If your package installs <a class="reference external" href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/">scripts</a>,
they will be installed to <code class="docutils literal"><span class="pre">~/.local/bin</span></code> on Linux, and
<code class="docutils literal">~/Library/Python/X.Y/bin</code> on macOS (X.Y is Python version), or you can use
<code class="docutils literal">python <span class="pre">-m</span></code> if the package supports it.</p>
<p>For most people and projects, virtual environments are better. There are,
however, use cases for putting some packages user-wide — if you don’t work on
projects, but instead are doing one-off research projects, those are better
suited by user-wide installs.</p>
</section>
<section id="virtual-environments">
<h2>Virtual environments</h2>
<p class="lead">I wrote a newer, more detailed post about virtualenvs: <a class="reference external" href="https://chriswarrick.com/blog/2018/09/04/python-virtual-environments/">Python Virtual
Environments in Five Minutes</a></p>
<p>Virtual environments are the best way to install and manage Python packages.
Advantages include:</p>
<ul class="simple">
<li><p>Isolation of projects and their requirements: if one app/package requires
library version X, but another requires version Y, they can live in separate
virtual environments</p></li>
<li><p>Independent from system-wide packages</p></li>
<li><p>Lightweight (an empty virtualenv is about 10 MB)</p></li>
<li><p>Simple to re-create in any place (<code class="docutils literal">pip freeze &gt; requirements.txt</code> → <code class="docutils literal">pip install <span class="pre">-r</span> requirements.txt</code>)</p></li>
</ul>
<section id="tools-and-management">
<h3>Tools and management</h3>
<p>There are two tools to facilitate creation of virtual environments: the older
<a class="reference external" href="https://virtualenv.pypa.io/en/stable/">virtualenv</a> project, and the newer
<code class="docutils literal">venv</code> module. The <code class="docutils literal">venv</code> module is shipped with Python 3.x; some
distributions may put it in a separate package or remove it altogether. Use
whichever works for you.  Virtualenv is compatible with more Python versions
and cannot be broken by incompetent OS package maintainers (<code class="docutils literal">venv</code> requires
an extra package on Debian).</p>
<p>There are multiple schools of thought regarding virtualenv placement and
content. Myself, I use <a class="reference external" href="https://virtualenvwrapper.readthedocs.io/en/latest/">virtualenvwrapper</a> to manage virtualenvs
and put them in <code class="docutils literal">~/virtualenvs</code>. Other people put virtualenvs inside their
git repositories (but they <em>must</em> be in <code class="docutils literal">.gitignore</code>) Virtualenvs should only contain packages
installed with <code class="docutils literal">pip</code> so they can be recreated quickly.</p>
<p>I also use the <code class="docutils literal">virtualenvwrapper</code> plugin for Oh My Zsh, which also
activates virtualenvs with the same name as a git repo, or the environment
named by a <code class="docutils literal">.venv</code> file.</p>
</section>
<section id="installation-and-usage">
<h3>Installation and usage</h3>
<p>To install virtualenv user-wide, use <code class="docutils literal">pip install <span class="pre">--user</span> virtualenv</code>. You can
then use it with <code class="docutils literal">python <span class="pre">-m</span> virtualenv DIRECTORY</code>. You may pass extra
options, eg. interpreter to use (<code class="docutils literal"><span class="pre">-p</span> python3</code>). Sometimes you need to install
virtualenv for every Python version; usually, one copy is enough.</p>
<p>How to use them? This is a subject of heated debate in the Python community.</p>
<ul class="simple">
<li><p>Some people believe that activating (<code class="docutils literal">source bin/activate</code> on *nix;
<code class="docutils literal">Scripts\activate</code> on Windows) is the right thing to do and simplifies work.</p></li>
<li><p>Others think that you should use <code class="docutils literal">bin/python</code> (or other scripts in that
directory) directly, as activation only changes <code class="docutils literal">$PATH</code> and some helper
variables — those variables are not mandatory for operation, running
the correct <code class="docutils literal">python</code> is.</p></li>
<li><p>Others still think <a class="reference external" href="https://gist.github.com/datagrok/2199506">virtualenvs should be used in subshells</a>.</p></li>
</ul>
<p>In my opinion, if activating virtualenvs works in your environment, you should
do it — it’s the most convenient option. There are, however, cases when
activation fails, or is otherwise impossible — calling <code class="docutils literal">bin/python</code> directly
is your best bet in that case. If you are working inside shell scripts, do not
activate virtualenvs.  I’m not a fan of the subshell option, because it
complicates stuff if you work on multiple projects, and requires tracking usage
manually.</p>
</section>
<section id="upgrading-and-moving">
<h3>Upgrading and moving</h3>
<p>Upgrading the system Python may make your virtualenvs unusable.
For patch version upgrades, you can just update symlinks (see <a class="reference external" href="https://github.com/Kwpolska/scripts/blob/master/fix-venvs.sh">fix-venvs.sh</a>).
However, if the minor version changes, it’s best to re-create the virtualenv
(you need to create <code class="docutils literal">requirements.txt</code> ahead of time).</p>
<p>You cannot move a virtualenv between directories/machines or rename
virtualenvs. You need to use <code class="docutils literal">pip freeze &gt; requirements.txt</code>, create a new
virtualenv, and run <code class="docutils literal">pip install <span class="pre">-r</span> requirements.txt</code> (you can then delete
the old environment with a simple <code class="docutils literal">rm <span class="pre">-rf</span></code>)</p>
</section>
</section>
<section id="packages-with-c-extensions-binary">
<h2>Packages with C extensions (binary)</h2>
<p>The situation improved drastically in the past year or so. Nowadays, almost
all packages have a pre-compiled package available in PyPI. Those packages work
for Windows, macOS, and Linux. There are packages for some of the most
common <em>offenders</em>, including Pillow, lxml, PyQt5, numpy… However, there might
still be packages without wheels on PyPI.</p>
<p>If there is no wheel for a package and you are on Windows, check out <a class="reference external" href="http://www.lfd.uci.edu/~gohlke/pythonlibs/">Christoph
Gohlke’s unofficial binaries</a>.
If you can’t find any wheels online, you would have to resort to compiling it
manually — this requires installing Visual Studio (Visual C++) in a version
that matches your Python, and it’s kind of a pain to do.</p>
<p>If you are not on Windows, you must install a C compiler and toolchain.
If you get a warning about missing <code class="docutils literal">Python.h</code>, install the appropriate development
package — for example, <code class="docutils literal"><span class="pre">python-dev</span></code> or <code class="docutils literal"><span class="pre">python3-dev</span></code>) on Debian/Ubuntu,
<code class="docutils literal"><span class="pre">python-devel</span></code> or <code class="docutils literal"><span class="pre">python3-devel</span></code> on RHEL/Fedora. The package you’re trying
to install might have other dependencies that you need to install (the
<code class="docutils literal"><span class="pre">-dev(el)</span></code> part is important, too)</p>
</section>
<section id="other-stuff">
<h2>Other stuff</h2>
<p>If you’re working on a project, use <code class="docutils literal">pip install <span class="pre">-e</span> .</code> inside the project
directory to install the package in your environment in development (editable)
mode. This loads code directly from your repository — you don’t need to
re-install on every change; you might need to re-install when your version
number changes.</p>
</section>
</section>
<section id="editors-and-ides">
<h1>Editors and IDEs</h1>
<p>Another important thing a developer should take care of is the choice of an
editor. This is an important decision, and is the reason for many holy wars in
the programmer community.</p>
<p>A good editor should have syntax highlighting for all languages you need to
work with. It should also have features like visual block/multiple selections,
sophisticated find-and-replace, file finding, code completion, and many more minor
but helpful features.</p>
<p>Then there’s the difference between IDEs and text editors. Text editors are
simpler, whereas IDEs try to include many extra things not necessarily related
to writing code. IDEs often use more resources, but you won’t notice it with a
modern computer (especially with a SSD).</p>
<p>The best IDE out there is <a class="reference external" href="https://www.jetbrains.com/pycharm/">PyCharm</a> from
JetBrains. It has both a free Community and paid Professional edition. The
JetBrains folks are experts at IDEs — they have fully-fledged tools for many
languages. Their Python solution offers a plethora of options that aid
programmers in their work.  Also, if you work with Java, or otherwise more than
one IDEA-supported language, then install IntelliJ IDEA and the Python plugin
(which has the same features as PyCharm).  Students can get <a class="reference external" href="https://www.jetbrains.com/student/">free
Professional/Ultimate licenses for JetBrains products</a>.</p>
<p>I also spend a lot of time in <a class="reference external" href="http://www.vim.org/">Vim</a> (<a class="reference external" href="https://neovim.io/">neovim</a>/<a class="reference external" href="http://vimr.org/">VimR</a> to be precise). Vim is the
most powerful text editor out there, and with the right set of plugins it can
beat IDEs at speed and productivity. Vim has a steep learning curve, but it’s
worth it — you can do large changes with just a few keystrokes. Vim is
considered so good that many IDEs (Visual Studio, IntelliJ IDEA/PyCharm) have
Vim emulation plugins.</p>
<p>Another option is <a class="reference external" href="https://code.visualstudio.com/">Visual Studio Code</a> — it’s
a text editor, but can offer many IDE-like features with the right set of
plugins. It’s Electron-based architecture, or effectively being based on top of
Google’s Chromium, is unfortunate and can lead to terrible performance on
lower-end machines, and on higher-end ones in some cases. (In my experience,
it’s better than Atom.) You can also try <a class="reference external" href="https://www.sublimetext.com/">Sublime Text</a> ($80).</p>
<p>But really, almost any editor will do. But please <strong>avoid</strong> IDLE, the editor
included with Python. It lacks some of the most basic things — it doesn’t even
have an option to show line numbers. Not to mention its ugliness. Also, don’t
use Notepad and TextEdit. Those are too simple, and Notepad has encoding
issues.</p>
</section>
<section id="update-history">
<h1>Update history</h1>
<dl class="simple">
<dt>2018-09-21</dt>
<dd><p>Link to python-virtual-environments post.</p>
</dd>
<dt>2017-07-19</dt>
<dd><p>Better description of problems caused by using sudo pip.</p>
</dd>
<dt>2017-07-10</dt>
<dd><p>Added notes about not removing built-in Pythons.</p>
</dd>
<dt>2017-07-07</dt>
<dd><p>Spelling fixes and updates to the virtualenv usage section.</p>
</dd>
</dl>
</section>
]]></content:encoded><category>Python</category><category>best practices</category><category>devel</category><category>guide</category><category>guide</category><category>Python</category></item><item><title>Python Apps the Right Way: entry points and scripts</title><dc:creator>Chris Warrick</dc:creator><link>https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/</link><pubDate>Mon, 15 Sep 2014 16:00:00 GMT</pubDate><guid>https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/</guid><description>
There are multiple ways to write an app in Python.  However, not all of them
provide your users with the best experience.
One of the problems some people encounter is writing launch scripts.  The
best way to handle this is the Entry Points mechanism of Setuptools, and a
__main__.py file.  It’s quite easy to implement.  If you’re interested,
read on to learn more!
</description><content:encoded><![CDATA[
<p>There are multiple ways to write an app in Python.  However, not all of them
provide your users with the best experience.</p>
<p>One of the problems some people encounter is <em>writing launch scripts</em>.  The
best way to handle this is the <em>Entry Points</em> mechanism of Setuptools, and a
<code class="docutils literal">__main__.py</code> file.  It’s quite easy to implement.  If you’re interested,
read on to learn more!</p>



<section id="requirements-and-desired-results">
<h1>Requirements and Desired Results</h1>
<p>You will need:</p>
<ul>
<li><p>a Python project</p></li>
<li><p>a setup.py file using <a class="reference external" href="https://pypi.python.org/pypi/setuptools">setuptools</a></p></li>
<li><p>the following directory structure:</p>
<ul class="list-nobullets">
    <li>
    <a href="https://chriswarrick.com/listings/entry_points_project/"><i class="bi bi-folder-fill"></i>
    entry_points_project/</a>
    <li>
        <ul class="list-nobullets">
        <li>
        <a href="https://chriswarrick.com/listings/entry_points_project/my_project/"><i class="bi bi-folder-fill"></i> my_project/</a>
            <li>
            <ul class="list-nobullets">
                <li>
                <a href="https://chriswarrick.com/listings/entry_points_project/my_project/__init__.py.html"><i class="bi bi-file-earmark-code-fill"></i>
                __init__.py</a>
                </li>
                <li>
                <a href="https://chriswarrick.com/listings/entry_points_project/my_project/__main__.py.html"><i class="bi bi-file-earmark-code-fill"></i>
                __main__.py</a>
                </li>
            </ul>
            </li>
        <li>
        <a href="https://chriswarrick.com/listings/entry_points_project/setup.py.html"><i class="bi bi-file-earmark-code-fill"></i> setup.py</a>
        </li>
        </ul>
    </li>
    </ul></li>
</ul>
<p>(<code class="docutils literal">entry_points_project</code> is also where the README and other auxiliary files
go, while <code class="docutils literal">my_project</code> contains all the Python code.)</p>
<p>When you’re done, you will have a project that can be executed by:</p>
<ul class="simple">
<li><p><code class="docutils literal">python <span class="pre">-m</span> my_project</code></p></li>
<li><p><code class="docutils literal">my_project</code></p></li>
</ul>
<p>Provided that you have your Python directory and its <code class="docutils literal">Scripts\</code> subdirectory on
the %PATH%, this will <strong>also work in Windows</strong>.</p>
 <div class="panel panel-info">
   <div class="panel-heading">
     <h3 class="panel-title">Looking for a project template?</h3>
   </div>
   <div class="panel-body">
    If you want to create a well-structured project with release automation and
some other goodies, check out my <a href="https://github.com/Kwpolska/python-project-template" style="font-weight: bold;">Python Project Template</a>.
   </div>
 </div></section>
<section id="step-1-create-a-main-py-file">
<h1>Step 1: create a <code class="docutils literal">__main__.py</code> file</h1>
<p>In order to implement the first desired result, you need to create a
<code class="docutils literal">__main__.py</code> file in your package.  This file needs to contain a <code class="docutils literal">main()</code>
function that takes no arguments, and also a special passage to determine code
to run:</p>
<p><a class="reference external" href="link://listing/listings/entry_points_project/my_project/__main__.py">entry_points_project/my_project/__main__.py</a>  <a class="reference external" href="link://listing_source/listings/entry_points_project/my_project/__main__.py">(Source)</a></p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-1" name="rest_code_d0a7bdde7283412db1d179ad410001ec-1"></a><span class="kn">import</span><span class="w"> </span><span class="nn">sys</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-2" name="rest_code_d0a7bdde7283412db1d179ad410001ec-2"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-3" name="rest_code_d0a7bdde7283412db1d179ad410001ec-3"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-4" name="rest_code_d0a7bdde7283412db1d179ad410001ec-4"></a><span class="k">def</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-5" name="rest_code_d0a7bdde7283412db1d179ad410001ec-5"></a><span class="w">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="sd">&quot;&quot;&quot;The main routine.&quot;&quot;&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-6" name="rest_code_d0a7bdde7283412db1d179ad410001ec-6"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="k">if</span> <span class="n">args</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-7" name="rest_code_d0a7bdde7283412db1d179ad410001ec-7"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">args</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-8" name="rest_code_d0a7bdde7283412db1d179ad410001ec-8"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-9" name="rest_code_d0a7bdde7283412db1d179ad410001ec-9"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;This is the main routine.&quot;</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-10" name="rest_code_d0a7bdde7283412db1d179ad410001ec-10"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;It should do something interesting.&quot;</span><span class="p">)</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-11"><code data-line-number="11"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-11" name="rest_code_d0a7bdde7283412db1d179ad410001ec-11"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-12"><code data-line-number="12"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-12" name="rest_code_d0a7bdde7283412db1d179ad410001ec-12"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1"># Do argument parsing here (eg. with argparse) and anything else</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-13"><code data-line-number="13"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-13" name="rest_code_d0a7bdde7283412db1d179ad410001ec-13"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="c1"># you want your project to do. Return values are exit codes.</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-14"><code data-line-number="14"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-14" name="rest_code_d0a7bdde7283412db1d179ad410001ec-14"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-15"><code data-line-number="15"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-15" name="rest_code_d0a7bdde7283412db1d179ad410001ec-15"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-16"><code data-line-number="16"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-16" name="rest_code_d0a7bdde7283412db1d179ad410001ec-16"></a><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_d0a7bdde7283412db1d179ad410001ec-17"><code data-line-number="17"></code></a></td><td class="code"><code><a id="rest_code_d0a7bdde7283412db1d179ad410001ec-17" name="rest_code_d0a7bdde7283412db1d179ad410001ec-17"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
</code></td></tr></table></div><ol class="arabic simple">
<li><p>The <code class="docutils literal">if __name__ == &quot;__main__&quot;:</code> idiom, as <a class="reference external" href="https://docs.python.org/3/library/__main__.html">documented here</a>, is used to check whether
this is executed as the top-level file, or if it has been imported by someone
else (in this case, executing the <code class="docutils literal">main()</code> function is not always intended).</p></li>
<li><p>The <code class="docutils literal">main()</code> function must not take any arguments, because that’s how
<code class="docutils literal">entry_points</code> executes things.</p></li>
</ol>
</section>
<section id="step-2-adjust-setup-py-accordingly">
<h1>Step 2: adjust <code class="docutils literal">setup.py</code> accordingly</h1>
<p>This is the real deal: create the entry points in your <code class="docutils literal">setup.py</code> file.</p>
<p><a class="reference external" href="link://listing/listings/entry_points_project/setup.py">entry_points_project/setup.py</a>  <a class="reference external" href="link://listing_source/listings/entry_points_project/setup.py">(Source)</a></p>
<div class="code"><table class="codetable"><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_c3236443b6d14709bd22e3b2b15c29c3-1"><code data-line-number=" 1"></code></a></td><td class="code"><code><a id="rest_code_c3236443b6d14709bd22e3b2b15c29c3-1" name="rest_code_c3236443b6d14709bd22e3b2b15c29c3-1"></a><span class="kn">from</span><span class="w"> </span><span class="nn">setuptools</span><span class="w"> </span><span class="kn">import</span> <span class="n">setup</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_c3236443b6d14709bd22e3b2b15c29c3-2"><code data-line-number=" 2"></code></a></td><td class="code"><code><a id="rest_code_c3236443b6d14709bd22e3b2b15c29c3-2" name="rest_code_c3236443b6d14709bd22e3b2b15c29c3-2"></a>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_c3236443b6d14709bd22e3b2b15c29c3-3"><code data-line-number=" 3"></code></a></td><td class="code"><code><a id="rest_code_c3236443b6d14709bd22e3b2b15c29c3-3" name="rest_code_c3236443b6d14709bd22e3b2b15c29c3-3"></a><span class="n">setup</span><span class="p">(</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_c3236443b6d14709bd22e3b2b15c29c3-4"><code data-line-number=" 4"></code></a></td><td class="code"><code><a id="rest_code_c3236443b6d14709bd22e3b2b15c29c3-4" name="rest_code_c3236443b6d14709bd22e3b2b15c29c3-4"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">name</span><span class="o">=</span><span class="s2">&quot;my_project&quot;</span><span class="p">,</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_c3236443b6d14709bd22e3b2b15c29c3-5"><code data-line-number=" 5"></code></a></td><td class="code"><code><a id="rest_code_c3236443b6d14709bd22e3b2b15c29c3-5" name="rest_code_c3236443b6d14709bd22e3b2b15c29c3-5"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">version</span><span class="o">=</span><span class="s2">&quot;0.1.0&quot;</span><span class="p">,</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_c3236443b6d14709bd22e3b2b15c29c3-6"><code data-line-number=" 6"></code></a></td><td class="code"><code><a id="rest_code_c3236443b6d14709bd22e3b2b15c29c3-6" name="rest_code_c3236443b6d14709bd22e3b2b15c29c3-6"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">packages</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;my_project&quot;</span><span class="p">],</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_c3236443b6d14709bd22e3b2b15c29c3-7"><code data-line-number=" 7"></code></a></td><td class="code"><code><a id="rest_code_c3236443b6d14709bd22e3b2b15c29c3-7" name="rest_code_c3236443b6d14709bd22e3b2b15c29c3-7"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="n">entry_points</span><span class="o">=</span><span class="p">{</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_c3236443b6d14709bd22e3b2b15c29c3-8"><code data-line-number=" 8"></code></a></td><td class="code"><code><a id="rest_code_c3236443b6d14709bd22e3b2b15c29c3-8" name="rest_code_c3236443b6d14709bd22e3b2b15c29c3-8"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;console_scripts&quot;</span><span class="p">:</span> <span class="p">[</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_c3236443b6d14709bd22e3b2b15c29c3-9"><code data-line-number=" 9"></code></a></td><td class="code"><code><a id="rest_code_c3236443b6d14709bd22e3b2b15c29c3-9" name="rest_code_c3236443b6d14709bd22e3b2b15c29c3-9"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="s2">&quot;my_project = my_project.__main__:main&quot;</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_c3236443b6d14709bd22e3b2b15c29c3-10"><code data-line-number="10"></code></a></td><td class="code"><code><a id="rest_code_c3236443b6d14709bd22e3b2b15c29c3-10" name="rest_code_c3236443b6d14709bd22e3b2b15c29c3-10"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">]</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_c3236443b6d14709bd22e3b2b15c29c3-11"><code data-line-number="11"></code></a></td><td class="code"><code><a id="rest_code_c3236443b6d14709bd22e3b2b15c29c3-11" name="rest_code_c3236443b6d14709bd22e3b2b15c29c3-11"></a>&nbsp;&nbsp;&nbsp;&nbsp;<span class="p">},</span>
</code></td></tr><tr><td class="linenos linenodiv"><a href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_c3236443b6d14709bd22e3b2b15c29c3-12"><code data-line-number="12"></code></a></td><td class="code"><code><a id="rest_code_c3236443b6d14709bd22e3b2b15c29c3-12" name="rest_code_c3236443b6d14709bd22e3b2b15c29c3-12"></a><span class="p">)</span>
</code></td></tr></table></div><ol class="arabic simple">
<li><p>You must use setuptools, otherwise this won’t work.</p></li>
<li><p>The most important piece of code is the <code class="docutils literal">entry_points</code> declaration
(unsurprisingly).</p></li>
<li><p>The declaration reads</p></li>
</ol>
<div class="code"><pre class="code text"><a id="rest_code_a39bdd0254594347ad5e67fddd274e39-1" name="rest_code_a39bdd0254594347ad5e67fddd274e39-1" href="https://chriswarrick.com/blog/2014/09/15/python-apps-the-right-way-entry_points-and-scripts/#rest_code_a39bdd0254594347ad5e67fddd274e39-1"></a>&quot;name_of_executable = module.with:function_to_execute&quot;
</pre></div>
<ol class="arabic simple" start="4">
<li><p>If you are developing a GUI application (in Tkinter, PyQt/PySide,
wxPython, PyGTK, PyGame…), you should change the declaration to
<code class="docutils literal">gui_scripts</code>.  On *nix, this makes no difference, but on Windows, it
means that running your script by opening the created <code class="docutils literal">.exe</code> files does
not show a console window. Note that stdout/stderr do not work in that mode
under Windows, which can lead to spurious application crashes.  (GUI-only
processes cannot use stdout/stderr because they don’t have a console
attached)</p></li>
<li><p>You can create <strong>multiple scripts</strong> this way.  You can also have multiple
<code class="docutils literal">console_scripts</code> <em>and</em> <code class="docutils literal">gui_scripts</code> in one setup file.</p></li>
</ol>
<p class="text-muted">All <a class="reference external" href="https://chriswarrick.com/listings/entry_points_project/">code samples</a> are freely reusable, but if you mention where you got them from, it’d be really nice.</p>
</section>
]]></content:encoded><category>Python</category><category>best practices</category><category>devel</category><category>guide</category><category>Python</category></item></channel></rss>