<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="/assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Jason Madden (Posts about package)</title><link>https://seecoresoftware.com/</link><description></description><atom:link href="https://seecoresoftware.com/blog/categories/package.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2021 &lt;a href="mailto:jason@seecoresoftware.com"&gt;Jason Madden&lt;/a&gt; </copyright><lastBuildDate>Sat, 04 Dec 2021 18:43:35 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Pre-release Version Pins are Sticky</title><link>https://seecoresoftware.com/blog/2018/06/prerelease-version-pin.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;div&gt;&lt;p&gt;The popular Python web framework &lt;a class="reference external" href="http://trypyramid.com"&gt;Pyramid&lt;/a&gt; recently had &lt;a class="reference external" href="https://github.com/Pylons/pyramid/issues/3220"&gt;a bug&lt;/a&gt;.
Installing a released version of Pyramid, such as 1.8 or 1.9, somehow
resulted in installing a &lt;em&gt;pre&lt;/em&gt;-release version of one of its most
important dependencies, &lt;a class="reference external" href="http://webob.readthedocs.io/en/stable/"&gt;WebOb&lt;/a&gt;. This was surprising and potentially
disruptive in a production environment. Having been surprised by a
similar bug in a different project, I followed this with interest.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/prerelease-version-pin.html#what-was-happening" id="id1"&gt;What Was Happening&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/prerelease-version-pin.html#what-was-so-surprising" id="id2"&gt;What Was So Surprising&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/prerelease-version-pin.html#what-was-going-on" id="id3"&gt;What Was Going On&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/prerelease-version-pin.html#what-the-fix-was" id="id4"&gt;What The Fix Was&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="what-was-happening"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/prerelease-version-pin.html#id1"&gt;What Was Happening&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I'll quote from the &lt;a class="reference external" href="https://github.com/Pylons/pyramid/issues/3220#issue-289334209"&gt;bug description&lt;/a&gt; here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since WebOb 1.8.0rc1 is released, Pyramid 1.8 and 1.9 versions install
a the rc version of WebOb, instead of the latest stable 1.7.4.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.7&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_8be2119bf2644928ba519a319698d146-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; pip install &lt;span class="s1"&gt;'pyramid &amp;lt; 1.8'&lt;/span&gt;
&lt;a name="rest_code_8be2119bf2644928ba519a319698d146-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;Collecting WebOb&amp;gt;=1.3.1 (from pyramid&amp;lt;1.8)&lt;/span&gt;
&lt;a name="rest_code_8be2119bf2644928ba519a319698d146-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;    Using cached WebOb-1.7.4-py2.py3-none-any.whl&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;1.8&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_54f4229f8b504f15832c78a83ea95777-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; pip install &lt;span class="s1"&gt;'pyramid &amp;lt; 1.9'&lt;/span&gt;
&lt;a name="rest_code_54f4229f8b504f15832c78a83ea95777-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;Collecting WebOb&amp;gt;=1.7.0rc2 (from pyramid&amp;lt;1.9)&lt;/span&gt;
&lt;a name="rest_code_54f4229f8b504f15832c78a83ea95777-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;    Using cached WebOb-1.8.0rc1-py2.py3-none-any.whl&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;1.9&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_4eef8e7b4fa440f5813d42f0d7e57098-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; pip install &lt;span class="s1"&gt;'pyramid'&lt;/span&gt;
&lt;a name="rest_code_4eef8e7b4fa440f5813d42f0d7e57098-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;Collecting WebOb&amp;gt;=1.7.0rc2 (from pyramid)&lt;/span&gt;
&lt;a name="rest_code_4eef8e7b4fa440f5813d42f0d7e57098-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;    Using cached WebOb-1.8.0rc1-py2.py3-none-any.whl&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;A simple &lt;tt class="docutils literal"&gt;pip install webob&lt;/tt&gt; installs the stable 1.7.4.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="what-was-so-surprising"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/prerelease-version-pin.html#id2"&gt;What Was So Surprising&lt;/a&gt;&lt;/h2&gt;
&lt;div class="sidebar"&gt;
&lt;p class="first sidebar-title"&gt;Python Version Numbers&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.python.org/dev/peps/pep-0440"&gt;PEP 440&lt;/a&gt; is the standard that specifies how version numbers are
to be parsed and interpreted in Python packaging. Unlike some other
packaging environments (npm), the Python standard was designed to
be extremely flexible, taking into account existing version numbers
and permitting both &lt;a class="reference external" href="https://semver.org"&gt;Semantic Versioning&lt;/a&gt; and &lt;a class="reference external" href="http://calver.org"&gt;Calendar
Versioning&lt;/a&gt;.&lt;/p&gt;
&lt;p class="last"&gt;The PEP defines how to order version numbers, and what constitutes
a release versus a pre-release. Under this scheme, &lt;tt class="docutils literal"&gt;1.8&lt;/tt&gt; is a
released version (equivalent to &lt;tt class="docutils literal"&gt;1.8.0&lt;/tt&gt;) that succeeds (is
greater than) &lt;tt class="docutils literal"&gt;1.7.5&lt;/tt&gt;. &lt;tt class="docutils literal"&gt;1.9a1&lt;/tt&gt; is an alpha pre-release which
can be succeeded by &lt;tt class="docutils literal"&gt;1.9a2&lt;/tt&gt; for a second alpha release, which can
in turn be succeeded by &lt;tt class="docutils literal"&gt;1.9b1&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;1.9rc1&lt;/tt&gt; for the first beta
and release candidate, respectively. &lt;tt class="docutils literal"&gt;1.9&lt;/tt&gt; is a release that
succeeds all of those versions.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In other words, the released version of WebOb was 1.7.4, and it was
expected that installing a released version of Pyramid would install
released versions  of its dependencies. But instead, installing the
released version of Pyramid installed the pre-release version of
WebOb.&lt;/p&gt;
&lt;p&gt;This was surprising because, beginning with pip version 1.4, pip &lt;a class="reference external" href="https://pip.pypa.io/en/stable/reference/pip_install/#pre-release-versions"&gt;does
not install pre-release versions&lt;/a&gt; by default. If you want a
pre-release version, you have to opt-in with the special &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--pre&lt;/span&gt;&lt;/tt&gt;
command line argument. Release 1.4 of pip was all the way &lt;a class="reference external" href="https://pip.pypa.io/en/stable/news/#id45"&gt;back in
2013&lt;/a&gt;, an eternity in terms of the Python packaging ecosystem.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="what-was-going-on"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/prerelease-version-pin.html#id3"&gt;What Was Going On&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;During development of Pyramid 1.8, Pyramid had needed access to a
feature only available in newer versions of WebOb (1.7). WebOb 1.7
wasn't released yet, but there was a pre-release available, 1.7.0.rc2.
Thus 1.7.0rc2 &lt;a class="reference external" href="https://github.com/Pylons/pyramid/commit/2ae320a4390940014dd049d2fc5f8312a31e2961"&gt;was listed as the dependency version&lt;/a&gt; for Pyramid
(&lt;tt class="docutils literal"&gt;WebOb &amp;gt;= 1.7.0rc2&lt;/tt&gt;), and Pyramid 1.8 and 1.9 were released with
that dependency version. The expectation was that the rule about
pre-releases not being installed by default would continue to hold:
when there was a 1.7.0 final released, that would be installed, and
because 1.8.0rc1 was a pre-release, but 1.7.x was &lt;em&gt;not&lt;/em&gt;, and was also
greater than 1.7.0rc1, the 1.7.x release would be installed.&lt;/p&gt;
&lt;p&gt;As detailed in a &lt;a class="reference external" href="https://github.com/pypa/pip/issues/4969"&gt;pip bug report&lt;/a&gt;, it turns out that's an incorrect
expectation. The authors of of &lt;a class="reference external" href="http://www.python.org/dev/peps/pep-0440"&gt;PEP 440&lt;/a&gt; and pip weighed in and
explained that the intent was that once you opt in to pre-releases,
whether implicitly with the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--pre&lt;/span&gt;&lt;/tt&gt; command line argument, or
explicitly in your dependency versions, you opt in to all
pre-releases. 1.8.0rc1 is greater than 1.7.4, and meets the
requirement (&lt;tt class="docutils literal"&gt;WebOb &amp;gt;= 1.7.0rc2&lt;/tt&gt;), so that's what you get.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="what-the-fix-was"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/prerelease-version-pin.html#id4"&gt;What The Fix Was&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The fix in Pyramid was to remove the &lt;tt class="docutils literal"&gt;rc2&lt;/tt&gt; pre-release qualifier
from their dependency list, thus opting out of pre-releases. Because
1.7.0 final had been released, this had the desired effect of having
access to the needed feature, while also not opening up end-users to
unexpected pre-release versions.&lt;/p&gt;
&lt;p&gt;To me, the take away for deployments is to be explicit about your
expected dependencies, even transitive ones. This can be in a pip
&lt;a class="reference external" href="https://caremad.io/posts/2013/07/setup-vs-requirement/"&gt;requirements.txt&lt;/a&gt; or a buildout &lt;a class="reference external" href="http://docs.buildout.org/en/latest/getting-started.html#pinned-versions"&gt;versions.cfg&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;</description><category>install</category><category>package</category><category>pip</category><category>prerelease</category><category>python</category><category>setup.py</category><guid>https://seecoresoftware.com/blog/2018/06/prerelease-version-pin.html</guid><pubDate>Wed, 13 Jun 2018 18:10:07 GMT</pubDate></item></channel></rss>