<?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>tartley.com (Posts about python)</title><link>https://www.tartley.com/</link><description></description><atom:link href="https://www.tartley.com/tags/python.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:tartley @ tartley dot com"&gt;Jonathan Hartley&lt;/a&gt; </copyright><lastBuildDate>Wed, 04 Feb 2026 02:12:46 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Code in Place</title><link>https://www.tartley.com/posts/code-in-place/</link><dc:creator>Jonathan Hartley</dc:creator><description>&lt;p&gt;&lt;span style="float: left"&gt;
&lt;a href="https://digitalcredential.stanford.edu/check/33D48CE3B18525700116E06E5009FAAA4331AEE03E75C6F2D0A3E235B454A6A7dlVFMkhoaCt6Z1g3Q0c5U2ozTnh3aEZqSm96RTFWU1dRNUpWZzVqYnBvRlZKV0VE"&gt;&lt;img alt="Code in Place completion" src="https://www.tartley.com/files/2025/code-in-place.webp"&gt;&lt;/a&gt;
&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Code in Place 2025 has ended. I participated as a Section Leader, tutoring a small group of beginner programmers through learning basic coding in Python. We covered fundamental programming - variables and loops and conditionals and data-structures, and used them to cajole small on-screen robots into moving around and performing tasks, and to draw some graphics with lines and filled shapes.&lt;/p&gt;
&lt;p&gt;The course was open to all, so there were many working members of the public alongside the students. While the course is a redrafting of the famous Stanford CS106A course content, these were not generally computer science students, but instead were studying some other subject, and wanted to add a little bit of programming as an extra skill on the side. &lt;/p&gt;
&lt;p&gt;The interesting thing about Code in Place is that it aims to investigate &lt;em&gt;scaling&lt;/em&gt; the teaching experience, without falling prey to the usual failure modes of Massive Open-Access Online Courses (MOOCs), of poor feedback on student work and high drop-out rates. They did this by recruiting 4,000 teaching volunteers like me to act as Section Leaders. We guided the 40,000 students through the material in small classes of ~10 students, and provided 1:1 guidance and question answering in forums.&lt;/p&gt;
&lt;p&gt;The application for section leader involved some basic competency questions, and an interesting task to record a video of me teaching some basic programming concepts. This all helped me to understand that I need to scale down my expectations of what is "self-evident", reminding me that beginners need to be taught &lt;em&gt;from the beginning&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;While AI was used, it was in a thoughtful, advisory capacity. For students, providing explinations of terse technical errors in their own code, and giving feedback on successfully completed tasks, to improve their solutions.&lt;/p&gt;
&lt;p&gt;Once the classes started, I heard complaints in the teachers' forums that other classes suffered from some absenteeism, or students simply not doing the work. But my students were absolutely fabulous, reliably showing up on time, doing all the exercises, and engaging honestly and curiously in the classes. I think that students were sorted into age groups that tend to be not too dissimilar to their Section Leader, so perhaps I had an older-than-average cohort, which might partly explain how I got so lucky.&lt;/p&gt;
&lt;p&gt;Regardless of how it happened, it was a thrillingly rewarding experience, seeing people rapidly grow their abilities, and become confident where they had originally been fearful. I'm already looking forward to next year's course!&lt;/p&gt;
&lt;p&gt;&lt;br style="clear: left"&gt;&lt;/p&gt;</description><category>python</category><category>software</category><category>teaching</category><guid>https://www.tartley.com/posts/code-in-place/</guid><pubDate>Mon, 07 Jul 2025 14:03:35 GMT</pubDate></item><item><title>Integer Division With Recurring Decimals</title><link>https://www.tartley.com/posts/integer-division-with-recurring-decimals/</link><dc:creator>Jonathan Hartley</dc:creator><description>&lt;p&gt;I've been doing some programming tests and puzzles while job hunting lately. One
quick challenge was quite nice, reminding me a bit of &lt;a href="https://projecteuler.net/"&gt;Project
Euler&lt;/a&gt; questions, and I nerd sniped myself into doing
a 2nd pass at it here.&lt;/p&gt;
&lt;h3&gt;Question&lt;/h3&gt;
&lt;p&gt;Produce a Python function which takes two integers, &lt;code&gt;numerator&lt;/code&gt; and
&lt;code&gt;denominator&lt;/code&gt;, and returns the result of their division as a decimal fraction in
a string. E.g:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"0.25"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the decimal places contain an infinite recurring pattern of digits, then
enclose the recurring digits in parentheses. E.g:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"0.(3)"&lt;/span&gt;
&lt;span class="n"&gt;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"0.(142857)"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Wrong approaches&lt;/h3&gt;
&lt;p&gt;Evaluating the division using normal floats is going to trip you up in several
ways with the limited precision.&lt;/p&gt;
&lt;p&gt;For one, a large enough denominator might have a recurring sequence which is
longer than the number of decimal places you have available (more on this
later), which makes it impossible to detect recurring sequences by examining the
division's decimal digits.&lt;/p&gt;
&lt;p&gt;Worse, the inherent imprecision of floating point, e.g. if a simple division
like 10/3 comes back as 3.3333333333333335, then examining the trailing digits
of this looking for recurring digits is going to be problematic.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;decimal&lt;/code&gt; module does markedly improve precision and control. But
infinitely repeating sequences are still going to return results like
&lt;code&gt;Decimal(20) / Decimal(3) -&amp;gt; Decimal('6.666666666666666666666666667')&lt;/code&gt;, which is
going to trip us up.&lt;/p&gt;
&lt;p&gt;We can sidestep all these complexities if we see that the question is asking us
to perform this division ourselves, longhand. We are going back to elementary
school! Wheee!&lt;/p&gt;
&lt;h3&gt;Better&lt;/h3&gt;
&lt;p&gt;Let's just do basic division first, ignoring infinite or recurring digits:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numerator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;denominator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Accumulate parts of our result here&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;int_part&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numerator&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;denominator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numerator&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;denominator&lt;/span&gt;
        &lt;span class="n"&gt;numerator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;int_part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# If there is no remainder, we are done&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

        &lt;span class="c1"&gt;# Add a decimal point after our first integer part&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only confusing parts of this are that &lt;code&gt;int_part&lt;/code&gt; might contain several
digits on the first iteration, but is only ever one decimal digit thereafter.
Plus we have to be careful to get the ordering right for our checks to exit
the loop, versus appending the decimal point to the output, to avoid weird
looking outputs like &lt;code&gt;divide(6, 2) -&amp;gt; "3."&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Trying this out:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="s1"&gt;'0.25'&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It works! But we haven't yet handled infinite decimals, they result in an
infinite loop:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Hangs!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Recurring digits&lt;/h3&gt;
&lt;p&gt;Because we're dividing integers, we cannot get infinitely varying decimal
places. If we have an infinite number of decimal places, it must be because
of a cycle of one or more recurring digits. To detect such a cycle we have to
notice a couple of things.&lt;/p&gt;
&lt;p&gt;First, simply seeing a digit in the output which we have seen before is not
enough. Looking at the three assignments at the start of the above while-loop,
which capture our state:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;int_part&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numerator&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;denominator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numerator&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;denominator&lt;/span&gt;
&lt;span class="n"&gt;numerator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code&gt;int_part&lt;/code&gt; gets the value of each successive decimal digit. However
if it takes on the same value as in a previous iteration, the accompanying
remainder might be different, and it is the remainder which is used to
generate the numerator for the next iteration, and hence generate the
sequence of digits after this.&lt;/p&gt;
&lt;p&gt;So, as we already knew from common sense, two iterations with the same
&lt;code&gt;int_part&lt;/code&gt; may go on to produce different sequences of subsequent digits.
However, The value of &lt;code&gt;remainder&lt;/code&gt; is the only thing which determines the inputs
to our next iteration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;int_part&lt;/code&gt; depends on &lt;code&gt;numerator&lt;/code&gt; and on &lt;code&gt;denominator&lt;/code&gt; (which is constant)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;numerator&lt;/code&gt; depends on &lt;code&gt;remainder&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hence, two iterations might produce different digits, but produce the same
remainder, and from that point onwards, they will be in lockstep. If we find two
such iterations, then we have detected an infinite recurring cycle of digits.&lt;/p&gt;
&lt;p&gt;So, before the loop begins, initialize a dict:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# Remainders seen to date, mapped to their position in the result&lt;/span&gt;
&lt;span class="n"&gt;remainders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then inside the loop, after everything else, use our new dict to detect if we
have seen the current remainder before:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# If we have seen this remainder before, we are now in exactly the&lt;/span&gt;
&lt;span class="c1"&gt;# same state as then, so we have found a recurring digit sequence.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;remainders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# We have found a cycle of decimal digits! Insert parens into our results,&lt;/span&gt;
    &lt;span class="c1"&gt;# from the last seen position of this remainder, up to the current digit.&lt;/span&gt;
    &lt;span class="n"&gt;last_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remainders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;remainder&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;last_pos&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"("&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;last_pos&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;")"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;
&lt;span class="c1"&gt;# Remember the position at which we saw this remainder&lt;/span&gt;
&lt;span class="n"&gt;remainders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;remainder&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Trying this out:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;0.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;0.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;142857&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;OMG it works!&lt;/p&gt;
&lt;h3&gt;Defensive coding&lt;/h3&gt;
&lt;p&gt;We're putatively done, but the grumpy old dev in me is uncomfortable leaving
that &lt;code&gt;while True&lt;/code&gt; in there. By deduction, we always must eventually hit the &lt;code&gt;if
&amp;lt;condition&amp;gt;: break&lt;/code&gt; to escape from it, so ostensibly it's fine. But if I have a
bug in the code or my reasoning, then it might lead to an infinite loop, in some
scenario I'm not thinking of. Can we limit the number of iterations in some
other, foolproof way? Turns out we can.&lt;/p&gt;
&lt;p&gt;We've seen already that a repeated value of &lt;code&gt;remainder&lt;/code&gt; means we can break
from the loop. Also, notice that &lt;code&gt;remainder&lt;/code&gt;, given by:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numerator&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;denominator&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;can only take values from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;denominator - 1&lt;/code&gt;. So it can have &lt;code&gt;denominator&lt;/code&gt;
possible values, and this is the maximum number of iterations we will ever need.&lt;/p&gt;
&lt;p&gt;Hence, we can safely replace the &lt;code&gt;while(True)&lt;/code&gt; with:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;denominator&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Splendid! Much less anxiety-inducing&lt;/p&gt;
&lt;p&gt;The source is on &lt;a href="https://github.com/tartley/division"&gt;github&lt;/a&gt;.&lt;/p&gt;</description><category>geek</category><category>math</category><category>python</category><category>software</category><guid>https://www.tartley.com/posts/integer-division-with-recurring-decimals/</guid><pubDate>Mon, 03 Mar 2025 18:42:50 GMT</pubDate></item><item><title>SVG Trees using recursive Python functions</title><link>https://www.tartley.com/posts/svg-trees-using-recursive-python-functions/</link><dc:creator>Jonathan Hartley</dc:creator><description>&lt;p&gt;Inspired by a woodland hike under the first blue skies we've seen this year, I
got home and showed the kiddo how to draw an SVG tree with recursive functions
in Python.&lt;/p&gt;
&lt;p&gt;At first the generated shape looked kinda lumpy and uninspiring, but it did
demonstrate the principle. We were thinking of calling it a day, but I did a
little bit of tweaking on parameters to control how each branch differs in
length and direction from its parent. Suddenly, the generated shape really came
alive, and started to look a lot more like the trees we'd seen on our hike that
afternoon.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Silhouette of tree against a blue sky, drawn by a Python program" src="https://www.tartley.com/files/2025/tree-art.lossy.webp"&gt;&lt;/p&gt;
&lt;p&gt;This image uses a recursion depth of 18, yielding 2^18 twigs, i.e. 250,000,
which generates a 100MB SVG file. This takes about ten seconds to generate, and
another ten to display in an SVG viewer. Alternatively, I can convert the SVG to
a lossy webp, as displayed here, which is only 280kB and displays instantly.&lt;/p&gt;
&lt;p&gt;Pushing the generation to greater recursion depth makes my SVG viewer and
conversion tools start to stutter and barf. Presumably I could be smarter about
the SVG I generate -- maybe generating the outline of the tree as points on
fewer, more complex polygons, instead of a polygon for each branch segment? No
matter, the artifact is the thing here, and it's done now.&lt;/p&gt;
&lt;p&gt;Source is at &lt;a href="https://github.com/tartley/tree-art"&gt;https://github.com/tartley/tree-art&lt;/a&gt;.&lt;/p&gt;</description><category>creative</category><category>geek</category><category>genart</category><category>graphics</category><category>python</category><category>software</category><category>svg</category><guid>https://www.tartley.com/posts/svg-trees-using-recursive-python-functions/</guid><pubDate>Fri, 28 Feb 2025 17:10:27 GMT</pubDate></item><item><title>Structured Pattern Matching in Python</title><link>https://www.tartley.com/posts/structured-pattern-matching-in-python/</link><dc:creator>Jonathan Hartley</dc:creator><description>&lt;p&gt;I read through descriptions of
&lt;a href="https://docs.python.org/3.11/reference/compound_stmts.html#the-match-statement"&gt;structured pattern matching&lt;/a&gt;
when it was added in Python 3.10 a couple of years ago, and have studiously
avoided it ever since. It seemed like a language feature that's amazingly
useful in one or two places, like writing a parser, say, and is a horrifically
over-complicated mis-step just about everywhere else.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; A day after writing this I see that Guido van
Rossum wrote exactly that,
&lt;a href="https://github.com/gvanrossum/patma/blob/master/examples/expr.py"&gt;a parser&lt;/a&gt;,
to showcase the feature. I'm guessing he writes a lot of parsers. I definitely
don't write enough of them to think this language feature is worth the extra
complexity it brings.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Regardless, I really ought to remember how it works, so this is my attempt to
make the details stick, by writing about it.&lt;/p&gt;
&lt;p&gt;If you're not me, you really ought to be reading about it from the source instead:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://peps.python.org/pep-0634/"&gt;PEP 643&lt;/a&gt;: Specification.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://peps.python.org/pep-0635/"&gt;PEP 635&lt;/a&gt;: Motivation and rationale.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://peps.python.org/pep-0636/"&gt;PEP 636&lt;/a&gt;: A tutorial.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Basic structure&lt;/h2&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;EXPRESSION&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PATTERN1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PATTERN2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This evaluates the &lt;code&gt;match&lt;/code&gt; EXPRESSION, then tries to match it against each
&lt;code&gt;case&lt;/code&gt; PATTERN, executing the body of the first case that matches, falling back
to the optional final &lt;code&gt;_&lt;/code&gt; default case. (&lt;code&gt;match&lt;/code&gt; and &lt;code&gt;case&lt;/code&gt; are not keywords,
except in the context of a match...case block, so you can continue using them
as variable names elsewhere.)&lt;/p&gt;
&lt;p&gt;But what are PATTERNs, and how are they tested for a match?&lt;/p&gt;
&lt;h2&gt;Patterns&lt;/h2&gt;
&lt;p&gt;Patterns can be any of the following. As becomes increasingly obvious down the
list, the real power of this feature comes from composing each of these
patterns with the others. For complicated patterns, parentheses can be used to
indicate order of operations.&lt;/p&gt;
&lt;h3&gt;Literals&lt;/h3&gt;
&lt;p&gt;Like other languages' traditional &lt;code&gt;switch&lt;/code&gt; statement:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;mycommand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'start'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'stop'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;CommandNotFoundError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mycommand&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Such literal case patterns may be strings (including raw and byte-strings, but
not f-strings), numbers, booleans or None.&lt;/p&gt;
&lt;p&gt;Such cases are compared with equality:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mf"&gt;123.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# matches!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;except for booleans and None, which are compared using &lt;code&gt;is&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__eq__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;

&lt;span class="n"&gt;myfalse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;myfalse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Doesn't match, even though myfalse == False&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Variable names&lt;/h3&gt;
&lt;p&gt;We can replace a literal with a variable name, to capture the value of the match
expression.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'start'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'stop'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# New variable 'unknown' is assigned the value of command&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The 'default' case pattern &lt;code&gt;_&lt;/code&gt; is just a special case variable name which
binds no name.&lt;/p&gt;
&lt;p&gt;Beware the common error of using "constants" as the case pattern:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;NOT_FOUND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;

&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;NOT_FOUND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# bad&lt;/span&gt;
        &lt;span class="n"&gt;handle_404&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above case is intended to test for &lt;code&gt;error == NOT_FOUND&lt;/code&gt;, but instead
assigns the variable &lt;code&gt;NOT_FOUND = error&lt;/code&gt;. The best defense is to always include
a default catch-all case at the end, which causes the above &lt;code&gt;NOT_FOUND&lt;/code&gt; case to
produce a SyntaxError:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;NOT_FOUND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;

&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;NOT_FOUND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;handle_404&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;SyntaxError&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;capture&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'NOT_FOUND'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;makes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;unreachable&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To use a 'constant' in a case pattern like this, qualify it with a dotted name,
such as by using an &lt;code&gt;enum.Enum&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NOT_FOUND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# correctly matches&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Sequences&lt;/h3&gt;
&lt;p&gt;Using a list-like or tuple-like syntax, matches must have the right number of
items. Like Python's existing iterable unpacking feature. Use &lt;code&gt;*&lt;/code&gt; to match the
rest of a sequence. Included variable names are set if a case matches by all
other criteria.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'start'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# New variable name=command[1]&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stop'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# New variable name=command[1]&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stop'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# New variables name=command[1], delay=command[2]&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stop'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# New variables name=command[1], delay=command[2] &amp;amp; extra=command[3:]&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;BadCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Mappings&lt;/h3&gt;
&lt;p&gt;Using a dict-like syntax. The match expression must must contain a
corresponding mapping, and can contain other keys, too. Use &lt;code&gt;**&lt;/code&gt; to match the
rest of a mapping.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'host'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt;
        &lt;span class="c1"&gt;# 'config' must contain key 'host'. New variable hostname=config['host']&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'port'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;portnumber&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt;
        &lt;span class="c1"&gt;# 'config' must contain key 'port'. New variable portnumber=config['port']&lt;/span&gt;
        &lt;span class="c1"&gt;# Remember we only use the first matching case.&lt;/span&gt;
        &lt;span class="c1"&gt;# If 'config' contains 'host', then this 'port' case will not match.&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'scheme'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;extras&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt;
        &lt;span class="c1"&gt;# new variables 'scheme' and 'extras' are assigned.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Case patterns may contain more than one key-value pair. The match expression must
contain all of them to match.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;'host'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'port'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;portnumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Objects and their attributes&lt;/h3&gt;
&lt;p&gt;Using class syntax, the value must match an isinstance check with the given class:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Click&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="c1"&gt;# handle click&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;KeyPress&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="c1"&gt;# handle key press&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Beware the common error of omitting the parentheses:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;myval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Click&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# bad&lt;/span&gt;
        &lt;span class="c1"&gt;# handle clicks&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above case is intended to test for &lt;code&gt;isinstance(myval, Click)&lt;/code&gt;, but instead
creates a new var, &lt;code&gt;Click = myval&lt;/code&gt;. The best defence against this error is to
always include a default catch-all at the end, which makes the &lt;code&gt;Click&lt;/code&gt; catch-all
produce an error by making subsequent patterns unreachable.&lt;/p&gt;
&lt;p&gt;Attribute values for the class can be given, which must also match.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;KeyPress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'q'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;KeyPress&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;handle_keypress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Values can also be passed as positional args to the class-like case syntax:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;KeyPress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'q'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the class is a namedtuple or dataclass, then positional args to a class-like
case pattern can automatically be handled using the unambiguous ordering of its
attributes:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dash'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'golden'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dash'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'golden'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# matches&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But for regular classes, the ordering of the class attributes is ambiguous.
To fix this, add a &lt;code&gt;__match_args__&lt;/code&gt; attribute on the class, a tuple which
specifies which class attributes, in which order, can be specified in a case
pattern:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;KeyPress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;__match_args__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'key_name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'release'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KeyPress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'q'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;KeyPress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'q'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# matches!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you might expect, the literal positional args can be replaced with variable
names to capture attribute values instead:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;KeyPress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;# names unimportant, order matters&lt;/span&gt;
        &lt;span class="n"&gt;handle_keypress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Positional sub-patterns behave slightly differently for builtins &lt;code&gt;bool&lt;/code&gt;,
&lt;code&gt;bytearray&lt;/code&gt;, &lt;code&gt;bytes&lt;/code&gt;, &lt;code&gt;dict&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;frozenset&lt;/code&gt;, &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;set&lt;/code&gt;,
&lt;code&gt;str&lt;/code&gt;, and &lt;code&gt;tuple&lt;/code&gt;. A positional value is matched by equality against the match
expression itself, rather than an attribute on it:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# matches&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;123.0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# would also match if it wasn't shadowed&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Similarly, a positional variable is assigned the value of the match expression
itself, not an attribute on that value:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The values passed as keyword or positional args to class-like case patterns can
be more than just literals or variable names. In fact they can use &lt;em&gt;any&lt;/em&gt; of the
listed pattern types. For example, they could be a nested instance of this
class-like syntax:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;

&lt;span class="n"&gt;mycar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;mycar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))):&lt;/span&gt;
        &lt;span class="c1"&gt;# matches, and captures 'x' and 'y'&lt;/span&gt;

&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Combine patterns using &lt;code&gt;|&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;To match either one pattern or another:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s1"&gt;'true'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s1"&gt;'on'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s1"&gt;'yes'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# matches any of those values&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Capture sub-patterns using &lt;code&gt;as&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;We've seen how we can either match against a value, or capture the value using
a variable name. We can do both using &lt;code&gt;as&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'a'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s1"&gt;'b'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# matches either value, captures what the value actually was&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This might not be much use when capturing the whole match expression like that.
If the match expression is just a variable, then we could instead simply refer
to that variable. But using &lt;code&gt;as&lt;/code&gt; can be useful when the match expression is
lengthy or has side-effects:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_next&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;KeyDown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;key_event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;or to capture just a component of the whole expression. Contrived example:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s1"&gt;'b'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# matchs ['a', 'c'] or ['b', 'c'], and captures the first letter in 'ab'&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;An &lt;code&gt;if&lt;/code&gt; guard clause&lt;/h4&gt;
&lt;p&gt;Add arbitrary conditions to the match:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# matches integers less than 100&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Or, alternatively:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# matches integers less than 100&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Complications&lt;/h2&gt;
&lt;p&gt;This feature seems rife with complexity. The flexible syntax of case patterns
forms a new mini-language, embedded within Python. It has many similarities to
Python, but also many initially unintuitive differences.&lt;/p&gt;
&lt;p&gt;For example, a class-like case pattern such as &lt;code&gt;case Click():&lt;/code&gt;. Anywhere else
in the language, the expression like &lt;code&gt;Click(...)&lt;/code&gt; would create an instance of the
&lt;code&gt;Click&lt;/code&gt; class. In a case statement, it instead is doing things like
&lt;code&gt;isinstance&lt;/code&gt; and &lt;code&gt;hasattr&lt;/code&gt; checks.&lt;/p&gt;
&lt;p&gt;Similarly, including variable names doesn't return the variable value as in
ordinary Python. Instead it binds a value as that name. This is the source of
the annoying gotcha described above, that bare "constants" like &lt;code&gt;NOT_FOUND&lt;/code&gt;
behave very unexpectedly when used as case expressions.&lt;/p&gt;
&lt;p&gt;There are a few places in real-world code where structured pattern matching
will produce nicer code than the equivalent using nested &lt;code&gt;elif&lt;/code&gt;s. But equally,
there are a lot of places where the &lt;code&gt;elif&lt;/code&gt;s are a more natural match.
Developers now get to choose which they're going to use, and then later
disagree with each other about it, or simply change their mind, and end up
converting code from one to the other.&lt;/p&gt;
&lt;p&gt;If this was a simple feature, with low overheads, then I'd forgive its
inclusion in the language, accepting the costs in return for the marginal and
unevenly distributed benefits.&lt;/p&gt;
&lt;p&gt;But it's really not simple. In addition to Python programmers all having to
do an exercise like this post just to add it to their mental toolbox, it needs
maintenance effort, not just in CPython but in other implementations too, and
needs handling by tools such as syntax highlighters, type checkers. It really
doesn't seem like a net win to me, &lt;em&gt;unless&lt;/em&gt; you're writing way more parsers
than the average programmer, which no doubt the champions of this feature are.&lt;/p&gt;</description><category>geek</category><category>python</category><category>software</category><guid>https://www.tartley.com/posts/structured-pattern-matching-in-python/</guid><pubDate>Sat, 29 Jul 2023 23:15:26 GMT</pubDate></item><item><title>TIL: Format Python Snippets with Black.</title><link>https://www.tartley.com/posts/format-python-snippets-with-black/</link><dc:creator>Jonathan Hartley</dc:creator><description>&lt;p&gt;Black, the opinionated Python code formatter, can easily be invoked from your
editor to reformat a whole file. For example, from Vim:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c"&gt;" Black(Python) format the whole file&lt;/span&gt;
&lt;span class="nb"&gt;nnoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;leader&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;b&lt;/span&gt; :&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;$&lt;span class="p"&gt;!&lt;/span&gt;black &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;q&lt;/span&gt; &lt;span class="p"&gt;-&amp;lt;&lt;/span&gt;CR&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But often you'd like to reformat just a section of the file, while leaving
everything else intact. In principle, it's easy to tell Vim to just send the
current visual selection:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c"&gt;" Black(Python) format the visual selection&lt;/span&gt;
xnoremap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;Leader&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;b&lt;/span&gt; :&lt;span class="p"&gt;!&lt;/span&gt;black &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;q&lt;/span&gt; &lt;span class="p"&gt;-&amp;lt;&lt;/span&gt;CR&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(Note that both the above Vim configuration snippets map the same key
sequence -- leader (commonly comma) followed by lower case b. These can be
defined simultaneously, because the second one uses 'xnoremap', meaning it is
used only while a visual selection exists, while the first uses 'nnoremap', so
is used all other times.)&lt;/p&gt;
&lt;p&gt;But if the given code starts with an indent on the first line, for example if
it comes from lines in the middle of a function, then this won't work. Black
parses the given code into a Python abstract syntax tree (AST), and a leading
indent is a syntax error - it's just not valid Python.&lt;/p&gt;
&lt;p&gt;I filed a hopeful &lt;a href="https://github.com/psf/black/issues/1352"&gt;issue with Black&lt;/a&gt;,
suggesting they could handle this case, but it was a long shot and hasn't
gained much enthusiasm.&lt;/p&gt;
&lt;p&gt;So, I present a tiny Python3 wrapper, &lt;em&gt;enblacken&lt;/em&gt;, which:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unindents the given code such that the first line has no indent.&lt;/li&gt;
&lt;li&gt;Passes the result to Black.&lt;/li&gt;
&lt;li&gt;Reindents Black's output, by the same amount as the original unindent.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See &lt;a href="https://github.com/tartley/dotfiles/blob/main/bin/enblacken"&gt;enblacken on github&lt;/a&gt;&lt;/p&gt;</description><category>geek</category><category>python</category><category>software</category><category>terminal</category><category>til</category><category>vim</category><guid>https://www.tartley.com/posts/format-python-snippets-with-black/</guid><pubDate>Tue, 09 Jun 2020 19:36:58 GMT</pubDate></item><item><title>Rhythmbox plugin: "Announce"</title><link>https://www.tartley.com/posts/rhythmbox-plugin-announce/</link><dc:creator>Jonathan Hartley</dc:creator><description>&lt;p&gt;I use the Linux music player "Rhythmbox". This morning I wrote a plugin
for it, called "Announce":&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/tartley/rhythmbox-plugin-announce"&gt;https://github.com/tartley/rhythmbox-plugin-announce&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Every time a new song starts to play, it announces the title using
speech synthesis. I like it when I'm listening to some new music I'm not
familiar with, but am away from the computer. Then I can still know
which track is which.&lt;/p&gt;
&lt;p&gt;If the album or artist names are different from the previous track, then
it includes those in the announcement, too.&lt;/p&gt;</description><category>geek</category><category>python</category><category>software</category><guid>https://www.tartley.com/posts/rhythmbox-plugin-announce/</guid><pubDate>Mon, 16 May 2016 01:34:48 GMT</pubDate></item><item><title>PyRochesterMN</title><link>https://www.tartley.com/posts/pyrochestermn/</link><dc:creator>Jonathan Hartley</dc:creator><description>&lt;p&gt;Announcing the world's newest Python User Group, PyRochesterMN, based in
Rochester, Minnesota, USA.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; We've had a couple of name &amp;amp; hosting changes since:&lt;/p&gt;
&lt;p&gt;&lt;del&gt;&lt;a href="http://www.meetup.com/PyRochesterMN"&gt;http://www.meetup.com/PyRochesterMN&lt;/a&gt;&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;del&gt;&lt;a href="http://www.meetup.com/RochesTech"&gt;http://www.meetup.com/RochesTech&lt;/a&gt;&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.reddit.com/r/rochestertech/"&gt;https://www.reddit.com/r/rochestertech/&lt;/a&gt;&lt;/p&gt;</description><category>geek</category><category>meetup</category><category>python</category><category>software</category><guid>https://www.tartley.com/posts/pyrochestermn/</guid><pubDate>Thu, 21 Jan 2016 03:01:27 GMT</pubDate></item><item><title>Thoughts on Nylas' "How We Deploy Code"</title><link>https://www.tartley.com/posts/thoughts-on-nylas-how-we-deploy-code/</link><dc:creator>Jonathan Hartley</dc:creator><description>&lt;p&gt;Some thoughts on &lt;a href="https://nylas.com/blog/packaging-deploying-python"&gt;Nylas' post "How We Deploy
Code."&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The goals of making deployment consistent, reliable and fast are very
laudable, and the conclusion involving creating Debian packages is just
great. But in the spirit of geek nitpicking, I can't help but think the
justifications given are misguided, and overlook a simpler solution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt;&amp;gt; pip does not offer a "revert deploy" strategy&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Yes it does. Throw away the virtualenv and create a new one using the
requirements.txt from an earlier release. This might be slow, but you
can both speed it up (see below), and you can keep old versioned
virtualenvs around, as a cache of the output of this slow step, so that
reverts (the time when you really want deploys to go quickly) require
only a couple of symlinks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Oooh, and I just had an idea. You could version virtualenvs
using a hash of the requirements, so that deploys which do not change
dependencies can share the same virtualenv. I've never tried that - it
might work?!?!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt;&amp;gt; Installing dependencies with pip can make deploys painfully
slow&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is true. But it's not the final word on the matter.&lt;/p&gt;
&lt;p&gt;Firstly, don't grab the dependencies you're installing from PyPI. Have a
local cache of them. That speeds up the install tremendously, not just
because no download takes place, but also because no trawling of PyPI
and author sites for possible alternate versions takes place. Some
people use a local PyPI instance, but I like simply using a directory
full of packages. Point pip at it using
'&lt;code&gt;pip install --no-index --find-links=packages -r requirements&lt;/code&gt;'. The
'packages' directory could be checked into your project's repo, so that
once you've checked a project out, you have everything you need to
deploy to local VMs, even with no network connection at all. I wrote
about this &lt;a href="https://www.tartley.com/posts/pip-install-lightspeed-and-bulletproof"&gt;a while ago&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt;&amp;gt; Building your code separately on each host will cause
consistency issues&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;So don't install dependencies using source packages, use wheels instead.
Then any slow or unreliable build step is done once, when you create the
packages directory, while deployment is now reliable and requires no
development tools on the production server such as compilers, headers,
etc.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; As a bonus, this will again substantially speed up the
creation of your virtualenv when deploying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt;&amp;gt; Deploys will fail if the PyPI or your git server are down&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;PyPI outages (or a package author deleting their package, which happens
routinely) are not a problem if you're deploying from a local source of
packages.&lt;/p&gt;
&lt;p&gt;I agree that application code deployment shouldn't be done using a 'git
pull'. Accessibility to GitHub shouldn't be a single point of failure,
and the fewer development tools needed on production servers the better.
So export the code from git into a tar file when you cut a release, and
then push it out using scp at deploy time.&lt;/p&gt;
&lt;p&gt;Having said all that, there are still advantages to having your whole
app and its dependencies handled by a single mechanism like Debian
packages, rather than more bug-prone ad-hoc scripts and Ansible config.
So I'm not at all against the final conclusions of the Nylas article.
(Hugs to you folks at Nylas!)&lt;/p&gt;
&lt;p&gt;Dashed this out in ten minutes between daycare pickup and dinner being
ready. Apologies for the inevitable shoddyness.&lt;/p&gt;</description><category>geek</category><category>python</category><category>software</category><guid>https://www.tartley.com/posts/thoughts-on-nylas-how-we-deploy-code/</guid><pubDate>Wed, 22 Jul 2015 00:06:02 GMT</pubDate></item><item><title>Chaining a sequence of generators</title><link>https://www.tartley.com/posts/chaining-a-sequence-of-generators/</link><dc:creator>Jonathan Hartley</dc:creator><description>&lt;p&gt;I often gravitate towards solutions using a series of chained
generators, in the style of David Beazley's '&lt;a href="http://www.dabeaz.com/generators-uk/"&gt;Generator Tricks for
Systems Programmers&lt;/a&gt;.'&lt;/p&gt;
&lt;p&gt;This results in the outer level of my code calling one generator after
another, terminating in something that consumes the rows, pulling data
one row at a time through each of the generators:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;inputRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;parsedRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;processedRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parsedRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;outputRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;format_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processedRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;where each called function except the last is actually a generator, e.g:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is great. But my itch is that the top level code above is a bit
wordy, given that what it does is so simple. The reader has to check
each temporary variable quite carefully to be sure it's doing the right
thing.&lt;/p&gt;
&lt;p&gt;Fowler's 'Refactoring' describes circumstances when &lt;a href="http://www.refactoring.com/catalog/replaceTempWithQuery.html"&gt;it's good to remove
intermediate
variables&lt;/a&gt;,
which results in:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;format_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is certainly less wordy, and expresses what's happening very
directly, but it annoys some of my colleagues that the called functions
are listed in reverse order from what one might intuitively expect.&lt;/p&gt;
&lt;p&gt;I've had this idea in my head to create a decorator for generators which
allows one to chain them in an intuitive order, possibly using some
unconventional notation such as:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;format_&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;where 'parse', et al, are now decorated with '@chainable' or somesuch,
which returns an instance of a class that stores the wrapped generator,
and overrides __or__ to do its magic. Maybe 'read' doesn't need to
be invoked manually there at the start of the chain. I haven't really
thought this through.&lt;/p&gt;
&lt;p&gt;Luckily, before embarking on that, I realised today I've been
over-complicating the whole thing. There's no need for decorators, nor
for the cute '|' syntax. I just need a plain old function:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;transforms&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;transforms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: This code has been improved thanks to suggestions in the
comments from Daniel Pope (eliminate the 'first' variable) and Xtian
(take an iterable rather than a callable for the source.)&lt;/p&gt;
&lt;p&gt;This assumes the first item passed to link is an iterable, and each
subsequent item is a generator that takes the result of the item before.&lt;/p&gt;
&lt;p&gt;If the final item in the sequence passed to 'link' is a generator, then
this returns a generator which is the composite of all the ones passed
in:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;format_&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Or if the final item passed to 'link' is a regular function, which
consumes the preceding generators, then calling 'link' will invoke the
generators, i.e. the following is the same as the above 'for' loop:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;format_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There's some rough edges, such as determining what to do if different
generators require other args. Presumably 'partial' could help here. But
in general, 'link' only needs to be written once, and I'm liking it.&lt;/p&gt;</description><category>geek</category><category>python</category><category>software</category><guid>https://www.tartley.com/posts/chaining-a-sequence-of-generators/</guid><pubDate>Tue, 15 Oct 2013 20:09:54 GMT</pubDate></item><item><title>pip install : Lightspeed and Bulletproof</title><link>https://www.tartley.com/posts/pip-install-lightspeed-and-bulletproof/</link><dc:creator>Jonathan Hartley</dc:creator><description>&lt;p&gt;I saw a post about &lt;a href="http://www.scottisheyes.com/how-to-fix-slow-pip-install"&gt;speeding up the Python packaging command "pip
install"&lt;/a&gt;, by
specifying more responsive mirrors for querying and downloading
packages. For my situation, a better tactic is this.&lt;/p&gt;
&lt;p&gt;Step one: Download all your project's dependencies into a local
'packages' dir, but don't install them yet:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;packages
pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--download&lt;span class="o"&gt;=&lt;/span&gt;packages&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;requirements.txt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Step two, install from the 'packages' dir:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--no-index&lt;span class="w"&gt; &lt;/span&gt;--find-links&lt;span class="o"&gt;=&lt;/span&gt;packages&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;requirements.txt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;(The above syntax works on pip 1.3, released yesterday. Docs for older
versions of pip claim to support this, but in practice, for pip 1.2,
I've had to use "&lt;code&gt;--find-links=file://$PWD/packages&lt;/code&gt;")&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Step 2 works even if PyPI is unreachable. It works even if some of your
dependencies are self-hosted by the authors, and that website is
unreachable. It works even if the version you have pinned of one of your
dependencies has been deleted by the author (some packages do this
routinely after security updates.) It works even if you have no network
connection at all. In short, it makes creation of your virtualenv
bulletproof.&lt;/p&gt;
&lt;p&gt;As a nice side effect, it runs really fast, because it isn't downloading
the packages across the internet, nor is it attempting to scan a remote
index to check for matching or newer versions of each package. This is
much quicker than just using a Pip download cache, especially for large
projects with many dependencies which only change occasionally.&lt;/p&gt;
&lt;p&gt;At &lt;a href="http://rangespan.com"&gt;Rangespan&lt;/a&gt;, we check the 'packages' directory
into source control, so that once you've checked out a project's repo,
you have everything you need to deploy locally and run, even if you have
no network. You might choose to treat 'packages' as ephemeral.&lt;/p&gt;
&lt;p&gt;It was pointed out to me recently by
&lt;a href="https://twitter.com/jezdez"&gt;@jezdez&lt;/a&gt;, Pip maintainer, this usage
pattern has now been &lt;a href="http://www.pip-installer.org/en/latest/cookbook.html#fast-local-installs"&gt;explicitly called out in the
documentation&lt;/a&gt;,
which was substantially reorganised and improved with the recent 1.3
release.&lt;/p&gt;</description><category>geek</category><category>python</category><category>software</category><category>terminal</category><guid>https://www.tartley.com/posts/pip-install-lightspeed-and-bulletproof/</guid><pubDate>Fri, 08 Mar 2013 15:29:09 GMT</pubDate></item></channel></rss>