<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>James Cooke</title><link href="https://jamescooke.info/" rel="alternate"></link><link href="https://jamescooke.info/feeds/all.atom.xml" rel="self"></link><id>https://jamescooke.info/</id><updated>2024-03-28T00:00:00+00:00</updated><entry><title>Why isn’t the UK in DST yet?!</title><link href="https://jamescooke.info/why-isnt-the-uk-in-dst-yet.html" rel="alternate"></link><published>2024-03-28T00:00:00+00:00</published><updated>2024-03-28T00:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2024-03-28:/why-isnt-the-uk-in-dst-yet.html</id><summary type="html">&lt;p&gt;British Summer Time is due to start this weekend. However, for some
reason, it&amp;#8217;s three weeks after the usual &lt;span class="caps"&gt;USA&lt;/span&gt; switch over, rather than the
usual two. Why is this? And when will it happen&amp;nbsp;again?&lt;/p&gt;</summary><content type="html">&lt;blockquote&gt;
&lt;p&gt;Warning: I particularly hate Daylight Savings Time (&lt;span class="caps"&gt;DST&lt;/span&gt;), so this post is
tainted with negativity. However, I work hard to prepare myself and family
for the biannual time change, so this post explores how I set my mental model
up for the clocks going forwards in the &lt;span class="caps"&gt;UK&lt;/span&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&amp;#8217;s 28th March as I write this from the &lt;span class="caps"&gt;UK&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;span class="caps"&gt;UK&lt;/span&gt; &lt;em&gt;should&lt;/em&gt; have moved to &lt;span class="caps"&gt;DST&lt;/span&gt; according to my mental model. This should
have happened last&amp;nbsp;weekend.&lt;/p&gt;
&lt;p&gt;I even did some family sleep time prepping last weekend. I even &lt;a href="https://fosstodon.org/@underlap/112145880071901460"&gt;wrongly tooted
about it&lt;/a&gt; and &lt;a href="https://fosstodon.org/@jamescooke/112145934698147675"&gt;then
corrected myself&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All of this has happened because my mental model about &lt;span class="caps"&gt;DST&lt;/span&gt; in the &lt;span class="caps"&gt;UK&lt;/span&gt; is
&lt;strong&gt;&lt;span class="caps"&gt;WRONG&lt;/span&gt;&lt;/strong&gt;&amp;nbsp;🤦.&lt;/p&gt;
&lt;h2&gt;🇺🇸 &lt;span class="caps"&gt;USA&lt;/span&gt; and 🇨🇦&amp;nbsp;Canada&lt;/h2&gt;
&lt;p&gt;Canada is a country that I like - some of our friends and family live there and
I&amp;#8217;ve visited multiple times. I also like the &lt;span class="caps"&gt;USA&lt;/span&gt; - I&amp;#8217;ve worked for &lt;span class="caps"&gt;USA&lt;/span&gt;
companies and have visited multiple&amp;nbsp;times.&lt;/p&gt;
&lt;p&gt;All this means (I think) I&amp;#8217;m well connected to what timezone the &lt;span class="caps"&gt;USA&lt;/span&gt; and Canada
are on any particular day. So when they move to &lt;span class="caps"&gt;DST&lt;/span&gt; (which happens before the
&lt;span class="caps"&gt;UK&lt;/span&gt; and Europe&amp;#8217;s change), we get a brief couple of weeks&amp;nbsp;where: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We get more overlap with family and friends in Canada because their summer
  time is closer by 1 hour to &lt;span class="caps"&gt;GMT&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;As an &lt;span class="caps"&gt;NHL&lt;/span&gt; ice hockey follower, most games start at midnight &lt;span class="caps"&gt;UK&lt;/span&gt; time, but for
  this period, games start an hour&amp;nbsp;earlier.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;#8217;s call this period the &amp;#8220;1 hour closer&amp;nbsp;overlap&amp;#8221;.&lt;/p&gt;
&lt;h2&gt;🧠 Mental&amp;nbsp;model&lt;/h2&gt;
&lt;p&gt;My mental model is based off this &amp;#8220;1 hour closer overlap&amp;#8221;. In my head this
overlap lasts two weeks - I don&amp;#8217;t know why - I&amp;#8217;ve just programmed that as a&amp;nbsp;&amp;#8220;fact&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Therefore, my mental model&amp;nbsp;for:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When does the &lt;span class="caps"&gt;UK&lt;/span&gt; apply &lt;span class="caps"&gt;DST&lt;/span&gt; and switch from &lt;span class="caps"&gt;GMT&lt;/span&gt;?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Is&amp;#8230;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Two weeks after North America changes to &lt;span class="caps"&gt;DST&lt;/span&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;This can be &lt;span class="caps"&gt;WRONG&lt;/span&gt;&lt;/strong&gt;&amp;nbsp;(sometimes).&lt;/p&gt;
&lt;p&gt;This year, 2024, is one of those wrong&amp;nbsp;years.&lt;/p&gt;
&lt;h2&gt;⚠️ The&amp;nbsp;problem&lt;/h2&gt;
&lt;p&gt;I didn&amp;#8217;t realise how ingrained this two week duration of overlap was in me, so
let&amp;#8217;s check facts and find out if I need an&amp;nbsp;upgrade.&lt;/p&gt;
&lt;h3&gt;First&amp;nbsp;fact&lt;/h3&gt;
&lt;p&gt;&lt;span class="caps"&gt;UK&lt;/span&gt;&amp;#8217;s biannual switch to &lt;span class="caps"&gt;DST&lt;/span&gt; happens on &lt;a href="https://en.wikipedia.org/wiki/British_Summer_Time"&gt;the last Sunday in
March&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[In the &lt;span class="caps"&gt;UK&lt;/span&gt;] &lt;span class="caps"&gt;BST&lt;/span&gt; begins at 01:00 &lt;span class="caps"&gt;GMT&lt;/span&gt; every year on the last Sunday of&amp;nbsp;March&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Second&amp;nbsp;fact&lt;/h3&gt;
&lt;p&gt;North America&amp;#8217;s biannual switch to &lt;span class="caps"&gt;DST&lt;/span&gt; happens on &lt;a href="https://en.wikipedia.org/wiki/Daylight_saving_time_in_the_United_States"&gt;the second Sunday in&amp;nbsp;March&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In the U.S., daylight saving time starts on the second Sunday in&amp;nbsp;March&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;🚨 Calendar Siren!&amp;nbsp;🚨&lt;/h3&gt;
&lt;p&gt;For any months of March where the last Sunday happens in the fifth week of
March, the &amp;#8220;1 hour closer overlap&amp;#8221; will be three weeks long, not&amp;nbsp;two.&lt;/p&gt;
&lt;h2&gt;📅 Checking future&amp;nbsp;overlaps&lt;/h2&gt;
&lt;p&gt;In order to have a look at what the &amp;#8216;normal&amp;#8217; overlap looks like, I&amp;#8217;m going to
brute force the calculation of how many days the &amp;#8220;1 hour closer overlap&amp;#8221; lasts
for each year from 2014 to 2033&amp;nbsp;inclusive.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m going to use Python with the &lt;code&gt;rrule()&lt;/code&gt; from
&lt;a href="https://dateutil.readthedocs.io/en/stable/rrule.html"&gt;dateutil&lt;/a&gt; and stuff the
data inside a &lt;a href="https://pandas.pydata.org/pandas-docs/stable/index.html"&gt;Pandas
DataFrame&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;In&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="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;pd&lt;/span&gt;

&lt;span class="n"&gt;In&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="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;dateutil.rrule&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;rrule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;YEARLY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SU&lt;/span&gt;

&lt;span class="n"&gt;In&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="c1"&gt;# Build UK and USA start dates using rrule&lt;/span&gt;
   &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dst_df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&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="s1"&gt;&amp;#39;UK DST Start&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rrule&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="n"&gt;YEARLY&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="n"&gt;dtstart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2014&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;1&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="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&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="n"&gt;bymonth&lt;/span&gt;&lt;span class="o"&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;...&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;byweekday&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SU&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="c1"&gt;# Last Sunday of the month&lt;/span&gt;
   &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="s1"&gt;&amp;#39;USA DST Start&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rrule&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="n"&gt;YEARLY&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="n"&gt;dtstart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2014&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;1&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="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&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="n"&gt;bymonth&lt;/span&gt;&lt;span class="o"&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;...&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;byweekday&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SU&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  &lt;span class="c1"&gt;# Second Sunday of the month&lt;/span&gt;
   &lt;span class="o"&gt;...&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;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;dst_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Weeks overlap&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dst_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;UK DST Start&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;dst_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;USA DST Start&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;dst_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dst_df&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;👈 You are here&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;UK DST Start&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2024&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&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="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;columns&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;In&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="n"&gt;dst_df&lt;/span&gt;
&lt;span class="n"&gt;Out&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="n"&gt;UK&lt;/span&gt; &lt;span class="n"&gt;DST&lt;/span&gt; &lt;span class="n"&gt;Start&lt;/span&gt; &lt;span class="n"&gt;USA&lt;/span&gt; &lt;span class="n"&gt;DST&lt;/span&gt; &lt;span class="n"&gt;Start&lt;/span&gt; &lt;span class="n"&gt;Weeks&lt;/span&gt; &lt;span class="n"&gt;overlap&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;    &lt;span class="mi"&gt;2014&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;    &lt;span class="mi"&gt;2014&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;       &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;    &lt;span class="mi"&gt;2015&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;    &lt;span class="mi"&gt;2015&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;       &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;    &lt;span class="mi"&gt;2016&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;    &lt;span class="mi"&gt;2016&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;       &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;    &lt;span class="mi"&gt;2017&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;    &lt;span class="mi"&gt;2017&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;       &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;    &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;    &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;       &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;    &lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;    &lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;       &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;    &lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;    &lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;       &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;    &lt;span class="mi"&gt;2021&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;    &lt;span class="mi"&gt;2021&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;       &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;    &lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;    &lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;       &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;9&lt;/span&gt;    &lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;    &lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;       &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt;   &lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;    &lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;       &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;  &lt;span class="err"&gt;👈&lt;/span&gt; &lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt;   &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;    &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;       &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;12&lt;/span&gt;   &lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;    &lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;       &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;13&lt;/span&gt;   &lt;span class="mi"&gt;2027&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;    &lt;span class="mi"&gt;2027&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;       &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;14&lt;/span&gt;   &lt;span class="mi"&gt;2028&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;    &lt;span class="mi"&gt;2028&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;       &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;15&lt;/span&gt;   &lt;span class="mi"&gt;2029&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;    &lt;span class="mi"&gt;2029&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;       &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;16&lt;/span&gt;   &lt;span class="mi"&gt;2030&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;    &lt;span class="mi"&gt;2030&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;       &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;17&lt;/span&gt;   &lt;span class="mi"&gt;2031&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;    &lt;span class="mi"&gt;2031&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;       &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;18&lt;/span&gt;   &lt;span class="mi"&gt;2032&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;    &lt;span class="mi"&gt;2032&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;       &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="mi"&gt;19&lt;/span&gt;   &lt;span class="mi"&gt;2033&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;    &lt;span class="mi"&gt;2033&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;       &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Wow - so there are many more 3 week overlaps than I&amp;nbsp;thought!&lt;/p&gt;
&lt;h2&gt;👀&amp;nbsp;Review&lt;/h2&gt;
&lt;p&gt;Looking back, only two of the last eight years have had a three week &amp;#8220;1 hour
closer&amp;nbsp;overlap&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s be honest, I&amp;#8217;m not sure how I missed the three week overlap in&amp;nbsp;2019.&lt;/p&gt;
&lt;p&gt;But for the 2020 one, although I &lt;em&gt;was&lt;/em&gt; working for a &lt;span class="caps"&gt;US&lt;/span&gt; company, we were in
&lt;span class="caps"&gt;COVID&lt;/span&gt; lock-down &lt;em&gt;and&lt;/em&gt; &lt;code&gt;child[0]&lt;/code&gt; was only a few weeks old so I was on parental&amp;nbsp;leave.&lt;/p&gt;
&lt;p&gt;However, looking forward, 2024 marks the start of a 3 year run of three week&amp;nbsp;overlaps!&lt;/p&gt;
&lt;h2&gt;🧠 A new mental&amp;nbsp;model&lt;/h2&gt;
&lt;p&gt;So my conclusion is that my previous mental model was pretty poor. It&amp;#8217;s time
for a new one. Here we&amp;nbsp;go:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When March starts on a Friday, Saturday or Sunday then there will be 3 weeks
of &amp;#8220;1 hour closer overlap&amp;#8221; with North America, otherwise it&amp;#8217;s&amp;nbsp;2.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;#8217;s confirm that by averaging the twenty years in the DataFrame&amp;nbsp;above:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Add a &amp;#39;first&amp;#39; column which contains the first day of March for each year&lt;/span&gt;
&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;dst_df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;first&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dst_df&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apply&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="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&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="s1"&gt;&amp;#39;UK DST Start&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%a&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&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="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;columns&amp;#39;&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="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;dst_df&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;first&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Weeks overlap&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;span class="n"&gt;first&lt;/span&gt;
&lt;span class="n"&gt;Fri&lt;/span&gt;   &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="n"&gt;Mon&lt;/span&gt;   &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="n"&gt;Sat&lt;/span&gt;   &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="n"&gt;Sun&lt;/span&gt;   &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="n"&gt;Thu&lt;/span&gt;   &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="n"&gt;Tue&lt;/span&gt;   &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="n"&gt;Wed&lt;/span&gt;   &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;days&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;Weeks&lt;/span&gt; &lt;span class="n"&gt;overlap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;timedelta64&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That looks right - so that&amp;#8217;s my new mental model&amp;nbsp;sorted.&lt;/p&gt;
&lt;p&gt;Now I just need to remember to check on what &lt;span class="caps"&gt;NHL&lt;/span&gt; ice hockey games will be
happening between 9th and 30th March 2025 when the schedule is published -
that&amp;#8217;s going to be three weeks of &amp;#8220;easier to watch&amp;#8221; matches!&amp;nbsp;😊&lt;/p&gt;</content><category term="ZZZ Misc..."></category><category term="language:python"></category><category term="topic:dst"></category></entry><entry><title>Pipx’s upgrade is shallow, let’s go deeper</title><link href="https://jamescooke.info/pipxs-upgrade-is-shallow-lets-go-deeper.html" rel="alternate"></link><published>2024-03-07T00:00:00+00:00</published><updated>2024-03-07T00:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2024-03-07:/pipxs-upgrade-is-shallow-lets-go-deeper.html</id><summary type="html">&lt;p&gt;Software installed in pipx&amp;#8217;s managed virtual environments can get
stale. How can we update those packages &lt;em&gt;and&lt;/em&gt; their&amp;nbsp;dependencies?&lt;/p&gt;</summary><content type="html">&lt;p&gt;pipx has been managing my Python tools for almost a&amp;nbsp;year.&lt;/p&gt;
&lt;p&gt;But those tools are getting stale - new versions are out - I need to&amp;nbsp;upgrade.&lt;/p&gt;
&lt;h2&gt;💪 Let&amp;#8217;s upgrade&amp;nbsp;this&lt;/h2&gt;
&lt;p&gt;One of my favourite and most used Python tools installed in pipx is
&lt;a href="https://pypi.org/project/frogmouth/"&gt;Frogmouth&lt;/a&gt;. While working on some
documentation, I think I&amp;#8217;ve spotted a bug in some Markdown rendering. So before
I report the bug, let&amp;#8217;s ensure I&amp;#8217;ve got the latest&amp;nbsp;version.&lt;/p&gt;
&lt;p&gt;Upgrading &amp;#8220;Is Easy ™️&amp;#8221;. Just use &lt;code&gt;pipx upgrade&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pipx&lt;span class="w"&gt; &lt;/span&gt;upgrade&lt;span class="w"&gt; &lt;/span&gt;frogmouth
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We get a spinner, and&amp;nbsp;then:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;frogmouth is already at latest version 0.9.2 (location: /home/james/.local/pipx/venvs/frogmouth)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Success! Nothing to do, end of blog&amp;nbsp;post.&lt;/p&gt;
&lt;p&gt;&amp;#8230;&lt;/p&gt;
&lt;h2&gt;🔎 Let&amp;#8217;s&amp;nbsp;check&lt;/h2&gt;
&lt;p&gt;Frogmouth is using &lt;a href="https://pypi.org/project/textual/"&gt;Textual&lt;/a&gt; and
&lt;a href="https://pypi.org/project/rich/"&gt;rich&lt;/a&gt; under the hood - so if I want to make
sure I&amp;#8217;ve got the latest Markdown code, I need to ensure they&amp;#8217;ve been upgraded&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s ask &lt;code&gt;pip&lt;/code&gt; to tell us all versions of packages in the &lt;code&gt;frogmouth&lt;/code&gt; virtual&amp;nbsp;environment:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pipx&lt;span class="w"&gt; &lt;/span&gt;runpip&lt;span class="w"&gt; &lt;/span&gt;frogmouth&lt;span class="w"&gt; &lt;/span&gt;list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Package&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;
&lt;span class="o"&gt;------------------&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;---------&lt;/span&gt;
&lt;span class="n"&gt;anyio&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="mf"&gt;3.7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;certifi&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="mf"&gt;2023.7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;
&lt;span class="n"&gt;frogmouth&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;👈&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Here&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s Frogmouth at the latest version&lt;/span&gt;
&lt;span class="n"&gt;h11&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="mf"&gt;0.14&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;httpcore&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="mf"&gt;0.17&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="mf"&gt;0.24&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;idna&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="mf"&gt;3.4&lt;/span&gt;
&lt;span class="n"&gt;importlib&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;6.8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;linkify&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;markdown&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;mdit&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;mdurl&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="mf"&gt;24.0&lt;/span&gt;
&lt;span class="n"&gt;pkg_resources&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;Pygments&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="mf"&gt;2.16&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;rich&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="mf"&gt;13.5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;👈&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rich&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;13.7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PyPI&lt;/span&gt;
&lt;span class="n"&gt;setuptools&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mf"&gt;69.1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;sniffio&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="mf"&gt;1.3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;textual&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="mf"&gt;0.43&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;👈&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Textual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.52&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PyPI&lt;/span&gt;
&lt;span class="n"&gt;typing_extensions&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="mf"&gt;4.7&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;uc&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;micro&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;wheel&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="mf"&gt;0.42&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;xdg&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;zipp&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="mf"&gt;3.16&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Uho - rich and Textual didn&amp;#8217;t get updated by doing &lt;code&gt;pipx upgrade&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;🤔 This kinda makes&amp;nbsp;sense&lt;/h2&gt;
&lt;p&gt;When we have a virtual environment for a project and we run &lt;code&gt;pip upgrade&lt;/code&gt;, it
&lt;em&gt;just&lt;/em&gt; upgrades the package we request. It only upgrades dependencies if they
conflict with the newly upgraded package. This is called the &amp;#8220;only-if-needed&amp;#8221;
strategy and is &lt;a href="https://pip.pypa.io/en/stable/user_guide/#only-if-needed-recursive-upgrade"&gt;documented in the pip User
Guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But, given I&amp;#8217;m a &lt;a href="https://pypi.org/project/pip-tools/"&gt;pip-tools&lt;/a&gt; addict, I
rarely call &lt;code&gt;pip&lt;/code&gt; directly. Usually I blow away all of a project&amp;#8217;s
requirements, rebuild them with &lt;code&gt;pip-compile&lt;/code&gt; and then install all the new
freshness with &lt;code&gt;pip-sync&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;How can I get this &amp;#8220;everything new&amp;#8221; behaviour with &lt;code&gt;pipx&lt;/code&gt;? I think there are
two&amp;nbsp;options&amp;#8230;&lt;/p&gt;
&lt;h2&gt;Option 1: Tell pip to be&amp;nbsp;eager&lt;/h2&gt;
&lt;p&gt;Also listed in the pip User Guide is the &amp;#8220;eager&amp;#8221; option&amp;nbsp;which:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;upgrades all dependencies regardless of whether they still satisfy the new
parent&amp;nbsp;requirements.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This sounds like what I&amp;#8217;m looking&amp;nbsp;for.&lt;/p&gt;
&lt;p&gt;And, luckily, &lt;code&gt;pipx upgrade --help&lt;/code&gt; shows us just what we&amp;nbsp;need:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;--pip-args PIP_ARGS   Arbitrary pip arguments to pass directly to pip install/upgrade commands
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let&amp;#8217;s try it by passing &lt;code&gt;--upgrade-strategy=eager&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pipx&lt;span class="w"&gt; &lt;/span&gt;upgrade&lt;span class="w"&gt; &lt;/span&gt;--pip-args&lt;span class="o"&gt;=&lt;/span&gt;--upgrade-strategy&lt;span class="o"&gt;=&lt;/span&gt;eager&lt;span class="w"&gt; &lt;/span&gt;frogmouth
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This, unfortunately, gives very little output regarding the packages being
updated. So let&amp;#8217;s check them again with &lt;code&gt;pip list&lt;/code&gt; (this time just grepping for
&amp;#8216;rich&amp;#8217; and&amp;nbsp;&amp;#8216;textual&amp;#8217;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pipx&lt;span class="w"&gt; &lt;/span&gt;runpip&lt;span class="w"&gt; &lt;/span&gt;frogmouth&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;-E&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^rich|^textual&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;rich               13.7.1   🎉 Yay - upgraded to latest.
textual            0.43.2   😞 boo - not upgraded to latest.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;😬 Textual ain&amp;#8217;t gunna&amp;nbsp;upgrade&lt;/h3&gt;
&lt;p&gt;After &amp;#8220;some&amp;#8221; digging, it turns out that Textual isn&amp;#8217;t going to upgrade when
installing / upgrading Frogmouth. That&amp;#8217;s because Frogmouth has a &lt;a href="https://python-poetry.org/docs/dependency-specification/#caret-requirements"&gt;caret
requirement&lt;/a&gt;
in &lt;a href="https://github.com/Textualize/frogmouth/blob/main/pyproject.toml#L31"&gt;its &lt;code&gt;pyproject.toml&lt;/code&gt;
file&lt;/a&gt;
which restricts Textual from being upgraded beyond &lt;code&gt;0.43&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I only discovered this after pulling out &lt;code&gt;pip-tools&lt;/code&gt; and running a clean
compile of the current Frogmouth requirements and diffing them to the output of
&lt;code&gt;pipx runpip frogmouth list&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Personally, I think this kind of pinning is frustrating, especially in &lt;a href="https://0ver.org/"&gt;zero
versioned&lt;/a&gt; software. If something breaks I can apply any
pins required to get them to work - I don&amp;#8217;t need the upstream maintainer to do
it for me. That just creates slowness and unnecessary&amp;nbsp;confusion.&lt;/p&gt;
&lt;p&gt;Anyway - back to the&amp;nbsp;upgrades&amp;#8230;&lt;/p&gt;
&lt;h2&gt;Option 2: Hit it with a&amp;nbsp;reinstall&lt;/h2&gt;
&lt;p&gt;There is &lt;em&gt;another&lt;/em&gt; way. That&amp;#8217;s to ask pipx to do a reinstallation of the
software. As per &lt;code&gt;pipx reinstall --help&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Package is uninstalled, then installed with pipx install &lt;span class="caps"&gt;PACKAGE&lt;/span&gt; with the
same options used in the original install of &lt;span class="caps"&gt;PACKAGE&lt;/span&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Warning: this is a bit of a lie. The &lt;code&gt;--python&lt;/code&gt; option is not kept when doing
reinstall. But, this &lt;em&gt;does&lt;/em&gt; allow for new versions of Python to be used after&amp;nbsp;reinstalling.&lt;/p&gt;
&lt;p&gt;Given that I&amp;#8217;m not using the default Python version for pipx installs, I always
have to pass in my preferred&amp;nbsp;Python:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pipx&lt;span class="w"&gt; &lt;/span&gt;reinstall&lt;span class="w"&gt; &lt;/span&gt;frogmouth&lt;span class="w"&gt; &lt;/span&gt;--python&lt;span class="o"&gt;=&lt;/span&gt;python3.12
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;uninstalled&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;frogmouth&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;✨&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;🌟&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;✨&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;installed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;frogmouth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;0.9.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;installed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;3.12.2&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;These&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;apps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;globally&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;frogmouth&lt;/span&gt;
&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;✨&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;🌟&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;✨&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And rich and Textual got to the same versions as before with&amp;nbsp;&amp;#8220;eager&amp;#8221;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pipx&lt;span class="w"&gt; &lt;/span&gt;runpip&lt;span class="w"&gt; &lt;/span&gt;frogmouth&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;-E&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^rich|^textual&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;rich               13.7.1
textual            0.43.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Which is&amp;nbsp;best?&lt;/h2&gt;
&lt;p&gt;My guess is you should use what you think is best for your&amp;nbsp;workflow.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m aggressive with my upgrading, so I&amp;#8217;m happy with the &lt;code&gt;pipx reinstall&lt;/code&gt; route.
This also may give cleaner virtual environments since we shouldn&amp;#8217;t get any
hanging dependencies in the scenario that a package stops using a particular&amp;nbsp;dependency.&lt;/p&gt;
&lt;p&gt;Also, during my experimentation, I accidentally installed a package off PyPI
called &amp;#8220;eager&amp;#8221; 🤦. Luckily it didn&amp;#8217;t run and the source doesn&amp;#8217;t look malicious
to my trusting eye. But it&amp;#8217;s this kind of mistake that&amp;#8217;s nicely cleaned up
every time the virtual environment is recreated with &lt;code&gt;reinstall&lt;/code&gt;.&amp;nbsp;😅&lt;/p&gt;</content><category term="Python"></category><category term="language:python"></category></entry><entry><title>Missing tiny data breaks pipeline</title><link href="https://jamescooke.info/missing-tiny-data-breaks-pipeline.html" rel="alternate"></link><published>2024-02-18T00:00:00+00:00</published><updated>2024-02-18T00:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2024-02-18:/missing-tiny-data-breaks-pipeline.html</id><summary type="html">&lt;p&gt;At work, when our usage and revenue reporting pipelines fail, they
usually fail because of &lt;em&gt;tiny&lt;/em&gt;&amp;nbsp;data.&lt;/p&gt;</summary><content type="html">&lt;p&gt;This week, during our monthly reporting run, two major label licensing reports
failed validation. This is unexpected because usually all reports are generated
and validate just&amp;nbsp;fine.&lt;/p&gt;
&lt;p&gt;It turned out a row of advertising revenue was missed for the United States
Minor Outlying Islands (&lt;span class="caps"&gt;UMI&lt;/span&gt;).&lt;/p&gt;
&lt;p&gt;That missed row was worth just £ 0.0003.&amp;nbsp;🙀&lt;/p&gt;
&lt;h2&gt;👌 This is tiny tiny&amp;nbsp;data&lt;/h2&gt;
&lt;p&gt;At work (&lt;a href="https://www.mixcloud.com/"&gt;Mixcloud&lt;/a&gt;) we generate usage reports for
major labels on a monthly basis. The&amp;nbsp;pipeline:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;identifies, reports and pays royalties out on tens of millions of tracks,
played by millions of Mixcloud creators, and owned by hundreds of thousands
of different artists and songwriters.
&lt;a href="https://blog.mixcloud.com/2021/06/30/why-mixcloud-doesnt-offer-on-demand-video-vods/"&gt;Via Mixcloud&amp;nbsp;blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This missing row was &amp;#8220;tiny&amp;#8221; by many&amp;nbsp;definitions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It was a tiny territory that I have to &lt;a href="https://en.wikipedia.org/wiki/United_States_Minor_Outlying_Islands"&gt;look up on
  Wikipedia&lt;/a&gt;.
  Turns out the population is about 300&amp;nbsp;people.&lt;/li&gt;
&lt;li&gt;It was a tiny amount of revenue that would get rounded out of existence at
  payout time. It would literally make zero change to the total payout for the
  month to any&amp;nbsp;label.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We often use a 0.1 % sense check definition of edge cases when working out what
bugs and issues to put effort against, and by every definition, this missing
row was less than 0.1 % of all sorts of monthly&amp;nbsp;factors.&lt;/p&gt;
&lt;h2&gt;🔥 But the pipeline&amp;nbsp;failed&lt;/h2&gt;
&lt;p&gt;A long time ago, I realised that we needed to validate the reports generated
&lt;em&gt;before&lt;/em&gt; they were sent to partners. So we built a post-process validation
system. This checks the generated reports from the client perspective,
providing row-wise, file-wise and batch-wise&amp;nbsp;validation.&lt;/p&gt;
&lt;p&gt;One of these checks ensures that advertising revenue is reported in &lt;span class="caps"&gt;GBP&lt;/span&gt; £.
However, because we had a missing row for the United States Minor Outlying
Islands (&lt;span class="caps"&gt;UMI&lt;/span&gt;), the reported advertising-based usage row became &lt;span class="caps"&gt;USD&lt;/span&gt; $ and failed&amp;nbsp;validation.&lt;/p&gt;
&lt;p&gt;Under the hood, this happened because we have a &lt;code&gt;LEFT JOIN&lt;/code&gt; between revenue and
usage which wasn&amp;#8217;t populated on the revenue side because the &lt;span class="caps"&gt;UMI&lt;/span&gt; row was&amp;nbsp;missing.&lt;/p&gt;
&lt;h2&gt;🛑 When there&amp;#8217;s a validation failure, everything&amp;nbsp;stops&lt;/h2&gt;
&lt;p&gt;When the generated reports with $ 0 amounts of advertising revenue hit our
validators they fail for the partners whose reports contain enough detail to
see that revenue and currency information. Even though this was just two
partners, when we receive those validation errors in the pipeline, the monthly
production&amp;nbsp;stops.&lt;/p&gt;
&lt;p&gt;We keep the generated reports, but work to find out the cause of the error and
assess how many generated reports are&amp;nbsp;tainted.&lt;/p&gt;
&lt;h2&gt;🔧 Fix and&amp;nbsp;regenerate&lt;/h2&gt;
&lt;p&gt;This time the error was, as discussed, tiny. And the fix was pretty tiny too.
We generated an extra row of revenue for &lt;span class="caps"&gt;UMI&lt;/span&gt; worth £ 0.0001 and spliced it back
into our monthly source data&amp;nbsp;snapshots.&lt;/p&gt;
&lt;p&gt;Then we reran all partners that receive reports on Mixcloud&amp;#8217;s ad-funded usage
and our ops colleagues got our monthly production process back up to&amp;nbsp;speed.&lt;/p&gt;
&lt;h2&gt;🤔 Is this kind of behaviour a &amp;#8220;good&amp;#8221;&amp;nbsp;thing?&lt;/h2&gt;
&lt;p&gt;After this incident, I&amp;#8217;m left wondering if it&amp;#8217;s &lt;span class="caps"&gt;OK&lt;/span&gt; that our pipeline is halted
by a missing row worth less than a penny that wouldn&amp;#8217;t affect monthly&amp;nbsp;payouts.&lt;/p&gt;
&lt;h3&gt;This is&amp;nbsp;good&lt;/h3&gt;
&lt;p&gt;On the &amp;#8220;good&amp;#8221; side, we could&amp;nbsp;say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;All the main sources of error are stable, it&amp;#8217;s just the tiny edge cases that
are&amp;nbsp;failing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In addition, these failures are so rare that we often are surprised when things
fail. Plus, it&amp;#8217;s good that we have the validation in place that finds these
kind of errors and reports&amp;nbsp;them.&lt;/p&gt;
&lt;h3&gt;This is&amp;nbsp;bad&lt;/h3&gt;
&lt;p&gt;On the other hand, we could&amp;nbsp;say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The pipelines are so fragile that a tiny missing piece of revenue allocated
to a user in a territory can bring down a monthly reporting&amp;nbsp;run.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There also seems some truth in&amp;nbsp;this.&lt;/p&gt;
&lt;p&gt;Probably the &lt;code&gt;LEFT JOIN&lt;/code&gt; in our revenue pipeline that caused the &lt;span class="caps"&gt;USD&lt;/span&gt; row to
appear is not robust enough. And as we&amp;#8217;ve dug more into the error later in the
week, my colleague Tim might have found a scenario that we would never be able
to prevent without strengthening this revenue query&amp;#8217;s &lt;span class="caps"&gt;SQL&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;⭐ Turn the bad into&amp;nbsp;good&lt;/h2&gt;
&lt;p&gt;What I realised is that the failure is a gift in disguise - it&amp;#8217;s helped us to
see a flaw in the pipeline that&amp;#8217;s so often hidden by aggregation. Instead of
resting on our laurels, we have an opportunity to improve the robustness and
accuracy of our revenue pipeline, plus a new test case to add to our test&amp;nbsp;suite.&lt;/p&gt;
&lt;p&gt;As a result of this error, we&amp;#8217;re also planning to adjust the source of the
missing row. This is currently a manual monthly process, but we&amp;#8217;ve seen that it
might be better incorporated into our pipeline directly, which we think will
give more&amp;nbsp;stability.&lt;/p&gt;
&lt;p&gt;So, if you happen to be that Mixcloud user in the United States Minor Outlying
Islands who listened in January - thanks so much. Your unusual pattern of
listening really helped us out.&amp;nbsp;😊&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;🙏 Thanks to Duncan and Dan for proof reading and&amp;nbsp;suggestions.&lt;/p&gt;</content><category term="Code"></category></entry><entry><title>hledger failure messages are better than Ledger’s</title><link href="https://jamescooke.info/hledger-failure-messages-are-better-than-ledgers.html" rel="alternate"></link><published>2023-08-29T00:00:00+01:00</published><updated>2023-08-29T00:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2023-08-29:/hledger-failure-messages-are-better-than-ledgers.html</id><summary type="html">&lt;p&gt;About six months ago, I upgraded our family accounts from Ledger to
hledger. The &lt;span class="caps"&gt;CLI&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt; of hledger is better than that of Ledger and the
feedback received when a balance assertion fails is just one&amp;nbsp;example.&lt;/p&gt;</summary><content type="html">&lt;p&gt;For any new plain text accounting project I always recommend using
&lt;a href="https://hledger.org/"&gt;hledger&lt;/a&gt; over
&lt;a href="https://ledger-cli.org/index.html"&gt;Ledger&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The main reason is errors and failures are better reported and rendered with
hledger, so let&amp;#8217;s look at an example - failed balance&amp;nbsp;assertions.&lt;/p&gt;
&lt;h2&gt;An erroneous&amp;nbsp;assertion&lt;/h2&gt;
&lt;p&gt;Given a journal file with a single transaction, which contains an&amp;nbsp;error:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;2023&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;08&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;29&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Some&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Assets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;75.73&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Income&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The error is that the balance of the Current Account is asserted as &lt;code&gt;$ 75.73&lt;/code&gt;
after the transaction, when it&amp;#8217;s really &lt;code&gt;$ 100&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Ledger&amp;nbsp;output&lt;/h2&gt;
&lt;p&gt;Running Ledger, here&amp;#8217;s the&amp;nbsp;version:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ledger&lt;span class="w"&gt; &lt;/span&gt;--version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Ledger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20190331&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accounting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;tool&lt;/span&gt;

&lt;span class="n"&gt;Copyright&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2003&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;John&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Wiegley&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;All&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rights&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;made&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;under&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;terms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BSD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;License&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;See&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LICENSE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;included&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;distribution&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;disclaimer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, let&amp;#8217;s ask for a balance - this will check the transaction and complain
about the incorrect balance&amp;nbsp;assertion:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ledger&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;ledger.dat&lt;span class="w"&gt; &lt;/span&gt;bal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;While&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;parsing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/tmp/ledger.dat&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;:
&lt;span class="k"&gt;While&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;parsing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;posting&lt;/span&gt;:
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;Assets&lt;/span&gt;:&lt;span class="nv"&gt;Current&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;.&lt;span class="mi"&gt;73&lt;/span&gt;
&lt;span class="w"&gt;                                 &lt;/span&gt;&lt;span class="o"&gt;^^^^^^^&lt;/span&gt;
&lt;span class="nv"&gt;Error&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;assertion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;off&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;.&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;expected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;see&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I&amp;#8217;ve always found the &amp;#8220;off by&amp;#8221; amount confusing and find I don&amp;#8217;t know if the
asserted balance is too low or&amp;nbsp;high.&lt;/p&gt;
&lt;h2&gt;hledger&amp;#8217;s failed&amp;nbsp;assertion&lt;/h2&gt;
&lt;p&gt;Just confirming my &lt;code&gt;hledger&lt;/code&gt; version:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;hledger&lt;span class="w"&gt; &lt;/span&gt;--version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;hledger 1.28, linux-x86_64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now let&amp;#8217;s run the same balance report with&amp;nbsp;hledger:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;hledger&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;ledger.dat&lt;span class="w"&gt; &lt;/span&gt;bal
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;hledger&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sr"&gt;/tmp/&lt;/span&gt;&lt;span class="n"&gt;ledger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dat&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Some&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;Assets&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;75.73&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                                    &lt;/span&gt;&lt;span class="o"&gt;^^^^^^^^^&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;Income&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="n"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;In&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Assets&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Current&lt;/span&gt;
&lt;span class="n"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;commodity&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;$&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;was&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;asserted&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mf"&gt;75.73&lt;/span&gt;
&lt;span class="n"&gt;but&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;calculated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;difference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;24.27&lt;/span&gt;

&lt;span class="n"&gt;Consider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;viewing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s calculated balances to troubleshoot. Eg:&lt;/span&gt;

&lt;span class="s1"&gt;hledger reg &amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Assets&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Current$&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; cur:&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FILE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For me, this output&amp;nbsp;is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Much more clear. It helpfully shows the failing transaction in the error&amp;nbsp;message.&lt;/li&gt;
&lt;li&gt;Easier to understand: The asserted balance is compared to the computed&amp;nbsp;balance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Given that plain text accounting is hard enough to work with at the best of
times, I would always go for a tool that helps me out the most with the
complexity. Right now, that means I&amp;#8217;d take hledger over&amp;nbsp;Ledger.&lt;/p&gt;</content><category term="Accounting"></category></entry><entry><title>An Ode to pipx</title><link href="https://jamescooke.info/an-ode-to-pipx.html" rel="alternate"></link><published>2023-07-26T21:00:00+01:00</published><updated>2023-07-26T21:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2023-07-26:/an-ode-to-pipx.html</id><summary type="html">&lt;p&gt;Using pipx has improved my daily development experience&amp;nbsp;considerably.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Oh pipx, how I love thee&amp;#8230;&amp;nbsp;🎵&lt;/p&gt;
&lt;p&gt;Using pipx means I can have Python packages installed and executable on my path
much more easily than in the past. That&amp;#8217;s changed my personal &lt;em&gt;and&lt;/em&gt; work
development experience for the better. Here&amp;#8217;s&amp;nbsp;how&amp;#8230;&lt;/p&gt;
&lt;h2&gt;Before&amp;nbsp;pipx&lt;/h2&gt;
&lt;p&gt;When I wanted to make a Python package (like IPython) available on the command
line in my Linux environment, I would get hacky&amp;#8230; Using &lt;code&gt;virtualenv&lt;/code&gt; and
boilerplate &lt;code&gt;bash&lt;/code&gt; scripts I would manage package installs, and then wrap them
in a script to make them available on my &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As an example, to make IPython runnable on the command line I&amp;nbsp;would:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create an IPython directory in my user&amp;#8217;s &lt;code&gt;opt&lt;/code&gt; dir: &lt;code&gt;~/opt/ipython&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Build a virtual environment inside&amp;nbsp;it.&lt;/li&gt;
&lt;li&gt;Activate the virtual environment and install IPython there with &lt;code&gt;pip&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add a wrapper executable script called &lt;code&gt;ipython&lt;/code&gt; which was then callable on
  my shell&amp;#8217;s &lt;code&gt;PATH&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That script looked&amp;nbsp;like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-eo&lt;span class="w"&gt; &lt;/span&gt;pipefail

~/opt/ipython/venv/bin/ipython
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;A side note about Python environments:
The main reason for using virtual environments for these projects and tools is
to keep my Ubuntu global Python environment clean: Not all Python installed
packages can or should just be thrown in there. Separation is important, and
sometimes required, not least because each package may have conflicting package
requirements and may not be able to be installed&amp;nbsp;together.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Disadvantages of these&amp;nbsp;hacks&lt;/h3&gt;
&lt;p&gt;There were a growing number of issues with the hacky approach above - not least
the problems with managing the resulting stack of venv and wrappers as the
number of Python tools I wanted on my path&amp;nbsp;grew.&lt;/p&gt;
&lt;p&gt;Yes - these could be handled with Ansible (I like to build and manage my
machines with Ansible), but there always seems to be a lag between the time I
&amp;#8220;need&amp;#8221; a new thing on my command line, and when I manage to get it wired into
Ansible&amp;nbsp;correctly.&lt;/p&gt;
&lt;p&gt;Upgrades also became hard - where were all those manually managed tools? Which
ones should I&amp;nbsp;update?&lt;/p&gt;
&lt;p&gt;A small, but niggling, disadvantage for using the wrapper script to run local
private tools: I found is that it was hard to keep &amp;#8220;development&amp;#8221; and
&amp;#8220;production&amp;#8221; separate. I&amp;#8217;d rarely re-create the private code repository so I
could run a version on shell &lt;code&gt;PATH&lt;/code&gt; separate from the development directory.
No, instead, the wrapper script would call the development directory directly.
Often when I was trying to do small fixes or improvements, I would accidentally
break my tool, or make it unusable in some way. Annoying when you&amp;#8217;re trying to
update some accounts and the bank account parsing tool is crashing because
you&amp;#8217;re half way through updating&amp;nbsp;it.&lt;/p&gt;
&lt;h2&gt;Switching to&amp;nbsp;pipx&lt;/h2&gt;
&lt;p&gt;I installed pipx into the user virtual environment on my Ubuntu machine as
per &lt;a href="https://pypa.github.io/pipx/"&gt;the instructions&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python3&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--user&lt;span class="w"&gt; &lt;/span&gt;pipx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, installing IPython was as simple&amp;nbsp;as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pipx&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;ipython
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Everything just worked and IPython was installed successfully. pipx even warned
me that there was a previous executable on my path (my previous crappy wrapper&amp;nbsp;script).&lt;/p&gt;
&lt;h3&gt;A better dev&amp;nbsp;life&lt;/h3&gt;
&lt;p&gt;Now I use pipx to install, manage the virtual environment and expose packages&amp;#8217;
endpoints on my shell&amp;#8217;s &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🙅 Gone are the wrapper scripts and manually built virtual&amp;nbsp;environments.&lt;/li&gt;
&lt;li&gt;🙅 Gone are the multiple directories of Python apps, some in &lt;code&gt;~/opt&lt;/code&gt; some in
  &lt;code&gt;~/active&lt;/code&gt; (my usual working path). Along with their Make recipes for
  managing virtual environments and&amp;nbsp;upgrades.&lt;/li&gt;
&lt;li&gt;🙅 Gone is the need for orchestration scripts and Make recipes to &amp;#8220;know&amp;#8221; the
  particular directory and virtual environment a package is installed in. pipx
  can upgrade everything with &lt;code&gt;pipx upgrade-all&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✅ Public&amp;nbsp;packages&lt;/h2&gt;
&lt;p&gt;I now install all my favourite, regularly used, public packages with pipx so
they&amp;#8217;re available all the time on the command&amp;nbsp;line.&lt;/p&gt;
&lt;p&gt;My favourite public packages currently installed&amp;nbsp;are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/devpi/devpi"&gt;devpi-server&lt;/a&gt; to allow Tox to install
  packages without having Pip call&amp;nbsp;PyPI.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flit.pypa.io/en/stable/"&gt;flit&lt;/a&gt; for&amp;nbsp;packaging.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Textualize/frogmouth/"&gt;frogmouth&lt;/a&gt; - my new favourite
  Markdown&amp;nbsp;tool.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/hledger-utils/"&gt;hledger-utils&lt;/a&gt; for helping with our
  family&amp;nbsp;accounts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;✅ Personal private&amp;nbsp;packages&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;ve got baggage - and it lives in private repositories: A suite of personal
tools I&amp;#8217;ve built up over the years used for all sorts of tasks, from filing
downloads into correct directories, to managing my work time, to bookkeeping
our family&amp;nbsp;accounts.&lt;/p&gt;
&lt;p&gt;With pipx these are now executable from anywhere in my shell, with none of the
previous overhead and boilerplate mentioned&amp;nbsp;above.&lt;/p&gt;
&lt;p&gt;These personal private packages are a little harder for me to get into pipx,
but only because I&amp;#8217;m lazy - if you&amp;#8217;ve done your proper packaging, then you&amp;#8217;re
probably already&amp;nbsp;set.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&amp;#8217;ve got a follow-up post about making your private packages installable with
pipx which I&amp;#8217;ll publish&amp;nbsp;soon.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Next&amp;nbsp;steps&lt;/h2&gt;
&lt;p&gt;Some things I&amp;#8217;m not sure about&amp;nbsp;yet.&lt;/p&gt;
&lt;h3&gt;Private packages from private&amp;nbsp;repositories&lt;/h3&gt;
&lt;p&gt;My current pipx install workflow for private packages depends on having them
cloned to a local directory, and then calling &lt;code&gt;pipx install [path]&lt;/code&gt; to install
from&amp;nbsp;there.&lt;/p&gt;
&lt;p&gt;I would like it if I could install my private packages directly from their
private GitLab repository without manually cloning first - I&amp;#8217;m pretty sure pipx
&lt;em&gt;can&lt;/em&gt; do this, I&amp;#8217;ve just not hacked around enough with the&amp;nbsp;invocation.&lt;/p&gt;
&lt;p&gt;This improvement would mean that I would just use a pipx install of my private
packages, and that means more cleanliness in my development environment - no
need to keep directories around in order to provide runnable Python code any&amp;nbsp;more.&lt;/p&gt;
&lt;h3&gt;Managing all this with&amp;nbsp;Ansible&lt;/h3&gt;
&lt;p&gt;As I mentioned I usually build and manage my machines with Ansible. I need to
invest some time in catching my Ansible playbooks with my current machine
states and the &lt;a href="https://docs.ansible.com/ansible/latest/collections/community/general/pipx_module.html"&gt;Ansible &lt;code&gt;pipx&lt;/code&gt;
module&lt;/a&gt;
in Ansible galaxy looks particularly&amp;nbsp;helpful.&lt;/p&gt;
&lt;h2&gt;🙏&amp;nbsp;Thanks&lt;/h2&gt;
&lt;p&gt;Thanks for&amp;nbsp;reading.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href="https://pythonbytes.fm/episodes/show/342/dont-believe-those-old-blogging-myths"&gt;Brian and Michael&amp;#8217;s
coverage&lt;/a&gt;
of &lt;a href="https://jvns.ca/blog/2023/06/05/some-blogging-myths/"&gt;Julia Evans&amp;#8217;s &amp;#8220;Some blogging
myths&amp;#8221;&lt;/a&gt; post&amp;#8230; For
&amp;#8220;nagging&amp;#8221; bloggers that it doesn&amp;#8217;t have to be perfect - just write the thing
and put it out&amp;nbsp;there.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href="https://fosstodon.org/"&gt;Fosstodon folks&lt;/a&gt; for tooting the new and
interesting things, that, in turn, inspire me to try out these things and get
them working for&amp;nbsp;myself.&lt;/p&gt;</content><category term="Python"></category><category term="language:python"></category></entry><entry><title>Pytest’s cache and gitignore</title><link href="https://jamescooke.info/pytests-cache-and-gitignore.html" rel="alternate"></link><published>2022-12-19T15:00:00+00:00</published><updated>2022-12-19T15:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2022-12-19:/pytests-cache-and-gitignore.html</id><summary type="html">&lt;p class="first last"&gt;Sanity checking Pytest&amp;#8217;s &lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt; files.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This post is about sanity checking. It was written at the end of 2019, but not
published until the end of 2022. The underlying change to Pytest&amp;#8217;s cache
directories was made in &lt;tt class="docutils literal"&gt;3.8.1&lt;/tt&gt;, released at the end of&amp;nbsp;2018.&lt;/p&gt;
&lt;div class="section" id="tl-dr"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt;&amp;nbsp;🥱&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;You can check any path, real or imaginary, with &lt;tt class="docutils literal"&gt;git &lt;span class="pre"&gt;check-ignore&lt;/span&gt;&lt;/tt&gt; to see
if Git will ignore it or&amp;nbsp;not.&lt;/li&gt;
&lt;li&gt;Pytest prevents its cache directory &lt;tt class="docutils literal"&gt;.pytest_cache&lt;/tt&gt; from getting into Git
repositories by adding a &lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt; file inside&amp;nbsp;them.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="the-long-story"&gt;
&lt;h2&gt;The (long) story&amp;nbsp;📜&lt;/h2&gt;
&lt;p&gt;While working on a project using Pytest, &lt;tt class="docutils literal"&gt;pytest &lt;span class="pre"&gt;--lf&lt;/span&gt;&lt;/tt&gt; was not selecting all
possible&amp;nbsp;tests.&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--lf&lt;/span&gt;&lt;/tt&gt; flag tells Pytest to run &lt;a class="reference external" href="https://docs.pytest.org/en/latest/cache.html#rerunning-only-failures-or-failures-first"&gt;the tests that failed in the last run&lt;/a&gt;
and those test IDs are stored in Pytest&amp;#8217;s&amp;nbsp;cache.&lt;/p&gt;
&lt;p&gt;To ensure that I started from a clean place, I went to clean out the
&lt;tt class="docutils literal"&gt;.pytest_cache&lt;/tt&gt; directory. But while I was looking at that directory, I had a
mild panic - I had completely forgotten to add it to project&amp;#8217;s &lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt;
file!&lt;/p&gt;
&lt;p&gt;Had I accidentally committed the &lt;tt class="docutils literal"&gt;.pytest_cache&lt;/tt&gt; dir?!&lt;/p&gt;
&lt;p&gt;Was this why &lt;tt class="docutils literal"&gt;pytest &lt;span class="pre"&gt;--lf&lt;/span&gt;&lt;/tt&gt; was being&amp;nbsp;strange?!&lt;/p&gt;
&lt;div class="section" id="not-in-git"&gt;
&lt;h3&gt;Not in&amp;nbsp;Git&lt;/h3&gt;
&lt;p&gt;Firstly, I was able to reassure myself that I&amp;#8217;d &lt;em&gt;not&lt;/em&gt; accidentally committed
the cache directory: &lt;tt class="docutils literal"&gt;git log&lt;/tt&gt; can accept a path, so when &lt;tt class="docutils literal"&gt;git log &lt;span class="pre"&gt;--&lt;/span&gt;
.pytest_cache&lt;/tt&gt; came back empty, this was reassuring. It was not committed to
the&amp;nbsp;repo!&lt;/p&gt;
&lt;p&gt;However, there was no entry for &lt;tt class="docutils literal"&gt;.pytest_cache&lt;/tt&gt; in &lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;I usually populate the &lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt; for Python projects by lifting the lines
that I want from the &lt;a class="reference external" href="https://github.com/github/gitignore/blob/master/Python.gitignore"&gt;Github gitignore repo&lt;/a&gt;, but I&amp;#8217;d
forgotten to copy over the line for &lt;tt class="docutils literal"&gt;.pytest_cache&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Why is the &lt;tt class="docutils literal"&gt;.pytest_cache&lt;/tt&gt; directory being ignored by Git if I&amp;#8217;ve not written
a pattern for it into &lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt;?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="checking-ignored-files"&gt;
&lt;h3&gt;Checking ignored&amp;nbsp;files&lt;/h3&gt;
&lt;p&gt;My guess was one of the existing patterns in &lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt; might be matching
the &lt;tt class="docutils literal"&gt;.pytest_cache&lt;/tt&gt; path. To check this I went through deleting lines from
the file until it was empty. But even with an empty ignore file,
&lt;tt class="docutils literal"&gt;.pytest_cache&lt;/tt&gt; still did not get picked up by&amp;nbsp;Git!&lt;/p&gt;
&lt;p&gt;Then I went and found that there is a super-helpful &lt;tt class="docutils literal"&gt;git &lt;span class="pre"&gt;check-ignore&lt;/span&gt;&lt;/tt&gt;
command. You can read some of the background of this command on &lt;a class="reference external" href="https://stackoverflow.com/a/12168102/1286705"&gt;Stack Overflow&lt;/a&gt;. This can be used to check
what Git ignore thinks of a&amp;nbsp;path.&lt;/p&gt;
&lt;p&gt;So now I can&amp;nbsp;call:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
git check-ignore -v .pytest_cache/
&lt;/pre&gt;
&lt;p&gt;And get&amp;nbsp;back:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
.pytest_cache/.gitignore:2:*    .pytest_cache/
&lt;/pre&gt;
&lt;p&gt;This&amp;nbsp;means:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;There is a file &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;.pytest_cache/.gitignore&lt;/span&gt;&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;Line 2 of that file is &lt;tt class="docutils literal"&gt;*&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;This rule is being applied to &lt;tt class="docutils literal"&gt;.pytest_cache/&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So - Pytest creates its own &lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt; file in the cache to prevent it
being included! Phew, what a journey!&amp;nbsp;😪&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="a-bit-more-investigation"&gt;
&lt;h3&gt;A bit more&amp;nbsp;investigation&lt;/h3&gt;
&lt;p&gt;So now we have an opportunity to learn a little bit about&amp;nbsp;Pytest&amp;#8230;&lt;/p&gt;
&lt;p&gt;From some searching, I found that the inclusion of a &lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt; file in
Pytest&amp;#8217;s cache directories was a&amp;nbsp;feature:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Introduced in &lt;a class="reference external" href="https://github.com/pytest-dev/pytest/pull/3982"&gt;Pull #3982: Ignore pytest cache&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;To solve &lt;a class="reference external" href="https://github.com/pytest-dev/pytest/issues/3286"&gt;Issue #3286: .pytest_cache is showing up in projects git repos&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Previously, Pytest had renamed its cache directory from &lt;tt class="docutils literal"&gt;.cache&lt;/tt&gt; to
&lt;tt class="docutils literal"&gt;.pytest_cache&lt;/tt&gt;. As a result, on projects where maintainers hadn&amp;#8217;t updated
their ignore files, the new cache directories had been committed by&amp;nbsp;accident.&lt;/p&gt;
&lt;p&gt;In looking at the Pytest team&amp;#8217;s response, what&amp;#8217;s interesting to me is the
trade-off&amp;nbsp;between:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Pytest developers do nothing. Let Pytest users update their &lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt;
files or other &lt;span class="caps"&gt;SCM&lt;/span&gt; ignore methods,&amp;nbsp;or&amp;#8230;&lt;/li&gt;
&lt;li&gt;Pytest developers take some action. Prevent the folder being added to &lt;span class="caps"&gt;SCM&lt;/span&gt;
systems or some other&amp;nbsp;fix.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the discussion on the Issue, &lt;a class="reference external" href="https://github.com/pytest-dev/pytest/issues/3286#issuecomment-393142058"&gt;this comment&lt;/a&gt;
shows the idea of a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;.pytest_cache/.gitignore&lt;/span&gt;&lt;/tt&gt; file coming into&amp;nbsp;being:&lt;/p&gt;
&lt;blockquote&gt;
another devious idea - if we add a &lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt; with the content &lt;tt class="docutils literal"&gt;*&lt;/tt&gt;
then the folder is protected as well and people dont need to track manually&lt;/blockquote&gt;
&lt;p&gt;But all decisions have&amp;nbsp;consequences.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="less-might-be-more"&gt;
&lt;h3&gt;Less might be&amp;nbsp;more&lt;/h3&gt;
&lt;p&gt;For me I would prefer to follow &lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0020/#id3"&gt;the Zen of Python&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
Explicit is better than implicit.&lt;/blockquote&gt;
&lt;p&gt;I would vote for: Let Pytest users update their ignore&amp;nbsp;mechanisms.&lt;/p&gt;
&lt;p&gt;This would&amp;nbsp;mean:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Pytest &lt;span class="caps"&gt;SCM&lt;/span&gt; users learn that &lt;tt class="docutils literal"&gt;.pytest_cache&lt;/tt&gt; exists and add it to their
&lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt; or&amp;nbsp;similar.&lt;/li&gt;
&lt;li&gt;Confusion is avoided because no directories are unexpectedly ignored by Git.
(Confusion as you can see in my case above and also in &lt;a class="reference external" href="https://github.com/pytest-dev/pytest/issues/4886"&gt;this issue&lt;/a&gt;.)&lt;/li&gt;
&lt;li&gt;Other side effects do not occur, like this ones mentioned in the issue above
regarding Debian packaging or&amp;nbsp;search.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To the wider open source issue, I think that projects that do less will last
better than projects that do too much. I would generally take trade-offs where
less is done rather than&amp;nbsp;more.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="reflection-2022"&gt;
&lt;h2&gt;Reflection&amp;nbsp;2022&lt;/h2&gt;
&lt;p&gt;Much of this post was written in 2019, much has happened, my confusion has&amp;nbsp;lessened.&lt;/p&gt;
&lt;p&gt;If you ask &amp;#8220;did the Pytest team do the right thing by adding &lt;tt class="docutils literal"&gt;.gitignore&lt;/tt&gt; to
the newly named &lt;tt class="docutils literal"&gt;.pytest_cache&lt;/tt&gt; directories?&amp;#8221;, then my answer is &lt;strong&gt;yes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It seems to have been a successful strategy and is even &lt;a class="reference external" href="https://github.com/python/mypy/pull/8193"&gt;used by mypy&lt;/a&gt; with a hat-tip to Ronny
Pfannschmidt&amp;#8217;s original comment suggesting the&amp;nbsp;idea.&lt;/p&gt;
&lt;p&gt;While editing this post, I found &lt;a class="reference external" href="https://github.com/pytest-dev/pytest/issues/4886#issuecomment-470498105"&gt;two&lt;/a&gt;
&lt;a class="reference external" href="https://github.com/pytest-dev/pytest/issues/4886#issuecomment-469877128"&gt;quotes&lt;/a&gt;
from Ronny that I&amp;#8217;ll end&amp;nbsp;with:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;we would be more than happy to have a better way (like&amp;nbsp;xdg)&lt;/p&gt;
&lt;p&gt;but lets be realistic here - the added .gitignore protects beginner uses
from a very common mistake, that&amp;#8217;s why its&amp;nbsp;there&lt;/p&gt;
&lt;p&gt;its a practical solution to a practical problem and has a interference&amp;nbsp;component&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;#8230;&lt;/p&gt;
&lt;blockquote&gt;
from my pov its an absolutely acceptable tradeoff to prevent a lot of
developer pain by inflicting a extra step on package maintainers&lt;/blockquote&gt;
&lt;p&gt;Nice one Pytest team for looking after new developers!&amp;nbsp;🙌&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="language:python"></category><category term="git"></category></entry><entry><title>Migrating Open Source projects on Travis CI to fix GitHub API limit problems</title><link href="https://jamescooke.info/migrating-open-source-projects-on-travis-ci-to-fix-github-api-limit-problems.html" rel="alternate"></link><published>2020-04-23T23:00:00+01:00</published><updated>2020-04-23T23:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2020-04-23:/migrating-open-source-projects-on-travis-ci-to-fix-github-api-limit-problems.html</id><summary type="html">&lt;p class="first last"&gt;Open source maintainers can move their projects from travis-ci.org to
travis-ci.com to get more reliable GitHub&amp;nbsp;integration.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Previously I wrote that &lt;a class="reference external" href="/travis-hitting-githubs-api-limits-for-open-source-projects.html"&gt;Travis dot org has been exhausting its GitHub &lt;span class="caps"&gt;API&lt;/span&gt; rate
limit&lt;/a&gt;.
Test results for projects built on Travis dot org (travis-ci.org) have not
been reliably reported back to GitHub. This leaves commits on GitHub in a
pending yellow status and pull requests&amp;nbsp;blocked.&lt;/p&gt;
&lt;p&gt;The solution is for open source maintainers to migrate their projects from
Travis dot org to Travis dot com (travis-ci.com). This solves the &lt;span class="caps"&gt;API&lt;/span&gt; rate
limit problem because Travis dot com uses GitHub Apps, whereas Travis dot org
uses a GitHub&amp;nbsp;integration.&lt;/p&gt;
&lt;p&gt;With GitHub Apps &lt;a class="reference external" href="https://developer.github.com/apps/differences-between-apps/#token-based-identification"&gt;each install of the app gets its own &lt;span class="caps"&gt;API&lt;/span&gt; quota&lt;/a&gt;.
So with the Travis dot com GitHub app installed in your GitHub user or
organisation, the 5,000 requests per hour &lt;span class="caps"&gt;API&lt;/span&gt; limit applies to just your
install of the app, not globally for all Travis dot com calls to GitHub. As a
small-time open source developer, there are no realistic future scenarios where
my install of the app will reach 5k requests per&amp;nbsp;hour.&lt;/p&gt;
&lt;div class="section" id="key-migration-points"&gt;
&lt;h2&gt;Key migration&amp;nbsp;points&lt;/h2&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://docs.travis-ci.com/user/migrate/open-source-repository-migration/#migrating-a-repository"&gt;migration documentation on Travis&lt;/a&gt;
is pretty comprehensive, but watch out for these&amp;nbsp;gotchas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Make sure you &amp;#8220;Sign up for the beta&amp;#8221; of migration in &lt;a class="reference external" href="https://travis-ci.org/account/repositories"&gt;your Travis dot org
account&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="Travis &amp;quot;Sign up for beta&amp;quot; call to action" src="https://jamescooke.info/images/200424_travis_sign_up.png" /&gt;
&lt;p&gt;Without this your existing repositories will not appear in your new Travis
dot com&amp;nbsp;account.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;If you have required checks in the branch protection rules of your GitHub
project repository, these need to be switched&amp;nbsp;over.&lt;/p&gt;
&lt;img alt="GitHub branch status checks required" src="https://jamescooke.info/images/200424_branch_status_checks.png" /&gt;
&lt;p&gt;You will need to trigger a build on Travis dot com for these new checks to
appear as&amp;nbsp;options.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Remember to change any build badges on your &lt;span class="caps"&gt;README&lt;/span&gt; from dot org to dot&amp;nbsp;com.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="a-trade-off"&gt;
&lt;h2&gt;A trade&amp;nbsp;off&lt;/h2&gt;
&lt;p&gt;With GitHub apps, results of checks are kept in the Checks Framework. This
means that when you click &amp;#8220;details&amp;#8221; of a Travis dot com check, you will be
shown GitHub&amp;#8217;s page for this check (&lt;a class="reference external" href="https://github.com/jamescooke/flake8-aaa/pull/140/checks?check_run_id=582544560"&gt;here&amp;#8217;s an example&lt;/a&gt;).
Whereas with Travis dot org, clicking on the &amp;#8220;details&amp;#8221; link for a check took
you straight to Travis dot&amp;nbsp;org.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s how GitHub advertises this&amp;nbsp;benefit:&lt;/p&gt;
&lt;img alt="Integrations built with Checks API - Travis CI - Get a complete picture of a project’s health directly from GitHub by viewing your build's stages, jobs, and results, including the config associated with them. You can also re-run builds from within the GitHub interface." src="https://jamescooke.info/images/200424_travis_checks_integration.png" /&gt;
&lt;p&gt;Once you migrate your project, Travis will be one click further away. Therefore
you are more likely to stay on GitHub while nursing a pull request or checking
on a&amp;nbsp;build.&lt;/p&gt;
&lt;p&gt;While I&amp;#8217;m sure many people consider this an improvement, I&amp;#8217;m not a fan of the
GitHub checks system. I prefer the old system&amp;nbsp;because:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;It was easier and more reliable to visit the external build system&amp;#8217;s site. As
we&amp;#8217;ve seen with this whole issue, communication across GitHub&amp;#8217;s boundary can
be&amp;nbsp;unreliable.&lt;/li&gt;
&lt;li&gt;I prefer Travis&amp;#8217;s interface for showing build information, not GitHub&amp;#8217;s
static checks&amp;nbsp;page.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="finally"&gt;
&lt;h2&gt;Finally&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;span class="caps"&gt;MK&lt;/span&gt; at Travis for the help with&amp;nbsp;migration.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m glad that it was possible to find a way to continue to use Travis on my
open source&amp;nbsp;projects.&lt;/p&gt;
&lt;p&gt;Happy&amp;nbsp;building!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="topic:testing"></category></entry><entry><title>Travis hitting GitHub’s API limits for Open Source projects</title><link href="https://jamescooke.info/travis-hitting-githubs-api-limits-for-open-source-projects.html" rel="alternate"></link><published>2020-04-02T23:00:00+01:00</published><updated>2020-04-02T23:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2020-04-02:/travis-hitting-githubs-api-limits-for-open-source-projects.html</id><summary type="html">&lt;p class="first last"&gt;GitHub&amp;#8217;s &lt;span class="caps"&gt;API&lt;/span&gt; rate limits are hurting Travis &lt;span class="caps"&gt;CI&lt;/span&gt;&amp;#8217;s service quality.
What does this mean for the future of the GitHub&amp;nbsp;ecosystem?&lt;/p&gt;
</summary><content type="html">&lt;!--  --&gt;
&lt;blockquote&gt;
&lt;strong&gt;Note:&lt;/strong&gt; A newer post &lt;a class="reference external" href="/migrating-open-source-projects-on-travis-ci-to-fix-github-api-limit-problems.html"&gt;Migrating Open Source projects on Travis &lt;span class="caps"&gt;CI&lt;/span&gt; to fix
GitHub &lt;span class="caps"&gt;API&lt;/span&gt; limit problems&lt;/a&gt;
has information on how to fix the problems described below.&lt;/blockquote&gt;
&lt;p&gt;Last week, GitHub&amp;#8217;s Dependabot created &lt;a class="reference external" href="https://github.com/jamescooke/flake8-aaa/pull/138"&gt;a pull request&lt;/a&gt; with a fix to a
vulnerability found in the development dependencies of one of my &lt;span class="caps"&gt;FOSS&lt;/span&gt; projects.
This was a bump to Mozilla&amp;#8217;s &lt;a class="reference external" href="https://github.com/mozilla/bleach"&gt;bleach&lt;/a&gt;, a
project that GitHub states is used by more than 61,000 other&amp;nbsp;projects.&lt;/p&gt;
&lt;a class="reference external image-reference" href="https://github.com/jamescooke/flake8-aaa/pull/138"&gt;&lt;img alt="GitHub's Dependabot opened a PR to bump bleach in Flake8-AAA." src="https://jamescooke.info/images/200402_pr.png" /&gt;&lt;/a&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/jamescooke/flake8-aaa"&gt;Flake8-&lt;span class="caps"&gt;AAA&lt;/span&gt;&amp;#8217;s repository&lt;/a&gt; is wired
into Travis &lt;span class="caps"&gt;CI&lt;/span&gt; to provide automated execution of its test suites across all
supported versions of Python. Better still, because Flake8-&lt;span class="caps"&gt;AAA&lt;/span&gt; is an open
source public repository, Travis provides the computing power to run these
tests for free. I&amp;#8217;ve always found Travis reliable and stable, so it&amp;#8217;s a
requirement that pull requests have a &amp;#8220;green&amp;#8221; Travis build before merging into
Flake8-&lt;span class="caps"&gt;AAA&lt;/span&gt;&amp;#8217;s master&amp;nbsp;branch.&lt;/p&gt;
&lt;div class="section" id="unreported-build-status"&gt;
&lt;h2&gt;Unreported build&amp;nbsp;status&lt;/h2&gt;
&lt;p&gt;However, when I checked on the Dependabot Pull Request, GitHub was still
waiting for the status of its Travis build to be&amp;nbsp;reported.&lt;/p&gt;
&lt;img alt="GitHub's merge dialogue box showing that expected tests have not completed." src="https://jamescooke.info/images/200401_some_checks_havent_completed_yet.png" /&gt;
&lt;p&gt;You can see that the &amp;#8220;Merge pull request&amp;#8221; box is greyed out because the
required Travis build has not completed yet according to&amp;nbsp;GitHub.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt; here&amp;#8217;s the build at &lt;a class="reference external" href="https://travis-ci.org/github/jamescooke/flake8-aaa/builds/669024353"&gt;Travis&lt;/a&gt; - both
green &lt;em&gt;and&lt;/em&gt; done within 3 minutes of Dependabot opening the &lt;span class="caps"&gt;PR&lt;/span&gt; at GitHub, so
the call from Travis to GitHub to report the build status on the commit failed
for some&amp;nbsp;reason.&lt;/p&gt;
&lt;img alt="Travis build of the Dependabot PR is green." src="https://jamescooke.info/images/200402_green_build.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="debugging"&gt;
&lt;h2&gt;Debugging&lt;/h2&gt;
&lt;p&gt;Sometimes webhook and &lt;span class="caps"&gt;API&lt;/span&gt; calls to GitHub fail - I&amp;#8217;ve seen this with both
personal and work projects. Often the simplest solution is to retrigger the
build in some way. At first I tried to get a follow up build to work&amp;nbsp;by:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Creating a new commit on the branch with updated requirements and pushing
that to the&amp;nbsp;branch.&lt;/li&gt;
&lt;li&gt;Amending the existing commit and pushing with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--force&lt;/span&gt;&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;Creating and pushing a new branch with an update to all&amp;nbsp;requirements.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these strategies had the same effect - a new build was triggered on
Travis and that build was green, but it was not reported to GitHub. So it
looked like all &lt;span class="caps"&gt;API&lt;/span&gt; calls were failing from Travis to&amp;nbsp;GitHub.&lt;/p&gt;
&lt;p&gt;Next, while checking the &lt;a class="reference external" href="https://www.githubstatus.com/"&gt;GitHub status page&lt;/a&gt;
and &lt;a class="reference external" href="https://www.traviscistatus.com/"&gt;Travis status page&lt;/a&gt;, I found this
status update on the Travis&amp;nbsp;site:&lt;/p&gt;
&lt;a class="reference external image-reference" href="https://www.traviscistatus.com/incidents/rx6fhs3wqcln"&gt;&lt;img alt="Travis status page shows GitHub commit status issue: GitHub status may not be posted on commits occasionally from builds using the legacy Services integration." src="https://jamescooke.info/images/200402_travis_status.png" /&gt;&lt;/a&gt;
&lt;p&gt;In light of that status message, I tried installing the Travis app integration,
but had no success getting it to link to Flake8-&lt;span class="caps"&gt;AAA&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The message&amp;nbsp;says:&lt;/p&gt;
&lt;blockquote&gt;
Please write to &lt;a class="reference external" href="mailto:support&amp;#64;travis-ci.com"&gt;support&amp;#64;travis-ci.com&lt;/a&gt; if you encounter any similar
problems.&lt;/blockquote&gt;
&lt;p&gt;So I&amp;nbsp;emailed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="reply-from-travis-support"&gt;
&lt;h2&gt;Reply from Travis&amp;nbsp;Support&lt;/h2&gt;
&lt;p&gt;Here&amp;#8217;s the full text of the reply from Travis&amp;nbsp;support:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;span class="caps"&gt;MK&lt;/span&gt; (Travis &lt;span class="caps"&gt;CI&lt;/span&gt;)&lt;/p&gt;
&lt;p&gt;Mar 31, 15:38 &lt;span class="caps"&gt;EDT&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Hello&amp;nbsp;,&lt;/p&gt;
&lt;p&gt;Thanks for your patience on this&amp;nbsp;issue.&lt;/p&gt;
&lt;p&gt;We want to provide some visibility into the issues we are facing, the
effects on our infrastructure and efforts made so far to restore&amp;nbsp;normalcy.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;We recently started hitting &lt;span class="caps"&gt;API&lt;/span&gt; rate limits for Github calls and on
March 25, 2020, we contacted Github to ask for increases and are
awaiting their feedback in this&amp;nbsp;regard.&lt;/li&gt;
&lt;li&gt;On the Travis &lt;span class="caps"&gt;CI&lt;/span&gt; end, we have made improvements on how our code accesses
the Github &lt;span class="caps"&gt;API&lt;/span&gt;, which has led to improvements, albeit&amp;nbsp;minimal.&lt;/li&gt;
&lt;li&gt;While we occasionally hit &lt;span class="caps"&gt;API&lt;/span&gt; limits, it&amp;#8217;s important to note that we
haven&amp;#8217;t hit these kinds of limits before now. In the interim, the
best course of action would be to retry the action you wanted to&amp;nbsp;perform.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For next&amp;nbsp;steps,&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;We are following up with Github via various channels to get the
requested &lt;span class="caps"&gt;API&lt;/span&gt; rate limit&amp;nbsp;increased.&lt;/li&gt;
&lt;li&gt;In addition, we are looking for more avenues to remove
invalid/unnecessary Github &lt;span class="caps"&gt;API&lt;/span&gt; calls in our codebase to ensure we stay
under the limit and avoid disruptions like&amp;nbsp;this.&lt;/li&gt;
&lt;li&gt;We are coordinating internally to ensure customers are up-to-date on
progress made so&amp;nbsp;far.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We know how critical our platform is to your business and our goal is to
provide the best experience for our customers. In line with this, we extend
our sincere apologies for inconveniences this is&amp;nbsp;causing.&lt;/p&gt;
&lt;p&gt;Thank you and we will provide periodic updates as we have&amp;nbsp;more.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Firstly, thanks to Travis support for this helpful message - it&amp;#8217;s pretty
unusual for a service that offers a free tier to be open and responsive to
messages from freeloading users like&amp;nbsp;myself.&lt;/p&gt;
&lt;p&gt;Secondly, I assumed that Travis would not be opposed to publishing the text of
the email since it should help other developers in my&amp;nbsp;situation.&lt;/p&gt;
&lt;p&gt;In response to the mail&amp;nbsp;itself:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;My understanding is that this issue mainly affects open source projects on
Travis dot&amp;nbsp;org.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;This message makes no mention of migrating to the Travis dot com GitHub Apps
integration, so I assume that it wouldn&amp;#8217;t work for Flake8-&lt;span class="caps"&gt;AAA&lt;/span&gt; or other open
source&amp;nbsp;projects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The mail&amp;nbsp;states:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In the interim, the best course of action would be to retry the action
you wanted to&amp;nbsp;perform.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Unfortunately I&amp;#8217;ve had no success with this yet, but will continue to&amp;nbsp;try.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!--  --&gt;
&lt;blockquote&gt;
&lt;strong&gt;Update:&lt;/strong&gt; Since writing this post I have successfully migrated projects
to Travis dot com. My &lt;a class="reference external" href="/migrating-open-source-projects-on-travis-ci-to-fix-github-api-limit-problems.html"&gt;next post&lt;/a&gt;
has a list of items to remember when migrating.&lt;/blockquote&gt;
&lt;p&gt;Although I&amp;#8217;m happy with the Travis response so far, I&amp;#8217;m worried about what this
means about the future of&amp;nbsp;GitHub.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="thoughts-on-the-github-ecosystem"&gt;
&lt;h2&gt;Thoughts on the GitHub&amp;nbsp;ecosystem&lt;/h2&gt;
&lt;p&gt;I was not part of the &amp;#8220;mass exodus&amp;#8221; from GitHub in 2018 after &lt;a class="reference external" href="https://github.blog/2018-10-26-github-and-microsoft/"&gt;Microsoft
completed its purchase&lt;/a&gt; of the platform. At
the time I thought that this could only be good for the site, however, now I&amp;#8217;m
reconsidering, especially in the light of the situation above. Let me explain&amp;nbsp;why&amp;#8230;&lt;/p&gt;
&lt;div class="section" id="github-wants-actions-to-replace-travis"&gt;
&lt;h3&gt;GitHub wants Actions to replace&amp;nbsp;Travis&lt;/h3&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/features/actions"&gt;GitHub Actions&lt;/a&gt; is what GitHub calls
its &amp;#8220;world-class &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt;&amp;#8221; system. &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; has been supported by Actions since
August 2019 and is free for open source projects - GitHub has &amp;#8220;embraced&amp;#8221; &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Travis dot org is now a &lt;strong&gt;competitor&lt;/strong&gt; to GitHub rather than the helpful
addition to the ecosystem it was&amp;nbsp;before.&lt;/p&gt;
&lt;p&gt;Also the existence of &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; in Actions means that GitHub can allow the
degradation of other &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; integrations because it&amp;#8217;s able to offer a &amp;#8220;better&amp;#8221;
replacement - use Actions instead. My guess would be that GitHub intends
Actions to replace all &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; building on GitHub for open source&amp;nbsp;projects.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="github-wants-developers-to-stay-on-github"&gt;
&lt;h3&gt;GitHub wants developers to stay on&amp;nbsp;GitHub&lt;/h3&gt;
&lt;p&gt;In the final paragraph of the &lt;a class="reference external" href="https://github.blog/2018-10-26-github-and-microsoft/"&gt;GitHub blog post above&lt;/a&gt;, Nat Friedman&amp;nbsp;states:&lt;/p&gt;
&lt;blockquote&gt;
Our vision is to serve every developer on the planet, by being the best
place to build software.&lt;/blockquote&gt;
&lt;p&gt;Building software includes &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; and GitHub&amp;#8217;s vision means that every developer
that needs a &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; function would stay on GitHub while &amp;#8220;building software&amp;#8221;, not
traverse external systems like Travis, Circle &lt;span class="caps"&gt;CI&lt;/span&gt; or&amp;nbsp;Codeship.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="github-can-make-it-harder-for-ci-cd-integrations-to-keep-up"&gt;
&lt;h3&gt;GitHub can make it harder for &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; integrations to keep&amp;nbsp;up&lt;/h3&gt;
&lt;p&gt;Since GitHub (and therefore Microsoft) &lt;a class="reference external" href="https://dependabot.com/blog/hello-github/"&gt;acquired Dependabot in 2019&lt;/a&gt;, GitHub now has a tool which it
can use to generate a larger number of builds on &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; services integrated with
its platform like Travis. This will have the knock-on effect of making it
harder for those &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; services to keep within their &lt;span class="caps"&gt;API&lt;/span&gt; rate limits and more
expensive to run because they will need to buy more computing power from &lt;span class="caps"&gt;AWS&lt;/span&gt;
and/or Google to run&amp;nbsp;builds.&lt;/p&gt;
&lt;p&gt;Best of all for GitHub, they can put this pressure on others while maintaining
the guise of &lt;a class="reference external" href="https://github.blog/2019-05-23-introducing-new-ways-to-keep-your-code-secure/#automated-security-fixes-with-dependabot"&gt;making &amp;#8220;dependency upgrades easy&amp;#8221;&lt;/a&gt;.
Now GitHub automatically creates a pull request for any project owned by an
account with security alerts enabled when it finds a relevant security
vulnerability&amp;nbsp;alert.&lt;/p&gt;
&lt;p&gt;In the case of the pull request above that started this post, that was a
vulnerability in bleach. As I mentioned this is a project used by over 60k
projects on GitHub. So when a security advisory on bleach occurs, Dependabot
creates a pull request on GitHub, each pull request will then be built by a
&lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; system for those repositories that have one wired in. For an external
&lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; system like Travis, that flood of builds requires a large volume of
computing resources &lt;strong&gt;and&lt;/strong&gt; GitHub &lt;span class="caps"&gt;API&lt;/span&gt;&amp;nbsp;calls.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://developer.github.com/v3/#rate-limiting"&gt;GitHub rate limit documentation&lt;/a&gt; currently states a quota of
5,000 requests per hour. If each &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; build requires 2 &lt;span class="caps"&gt;API&lt;/span&gt; calls (one to say
&amp;#8220;in progress&amp;#8221; and one to post the result), then once 2,500 builds are completed
in an hour the quota will be exhausted. If  4% of all the repositories that
depend on bleach are using Travis for builds, then a single bump to the bleach
release would exhaust a 5,000 request quota immediately - and that&amp;#8217;s before any
&amp;#8220;normal&amp;#8221; human-driven regular build activity is taken into&amp;nbsp;consideration.&lt;/p&gt;
&lt;p&gt;Now I&amp;#8217;m pretty sure that Travis has an hourly quota that&amp;#8217;s greater than 5,000
requests per hour, probably granted to them when GitHub saw them as augmenting
the GitHub ecosystem, but when the Travis email above&amp;nbsp;stated:&lt;/p&gt;
&lt;blockquote&gt;
We are following up with Github via various channels to get the requested
&lt;span class="caps"&gt;API&lt;/span&gt; rate limit increased.&lt;/blockquote&gt;
&lt;p&gt;&amp;#8230; why would GitHub bump this&amp;nbsp;now?&lt;/p&gt;
&lt;p&gt;Instead, GitHub can leave Travis in an awkward situation: choose to throttle
builds and get reliable status calls back to the GitHub &lt;span class="caps"&gt;API&lt;/span&gt;, or make open
source projects have a less reliable and smooth experience when status update
&lt;span class="caps"&gt;API&lt;/span&gt; calls are dropped. Either option makes GitHub Actions look &amp;#8220;better&amp;#8221; as a
&lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; solution - a win for&amp;nbsp;GitHub.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="finally-hope"&gt;
&lt;h2&gt;Finally,&amp;nbsp;hope&lt;/h2&gt;
&lt;p&gt;I hope that my thoughts on the GitHub ecosystem above are overly negative and
that these issues with Travis are not the start of an &amp;#8220;extinguish&amp;#8221; strategy by
GitHub towards external &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; systems (see &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Embrace,_extend,_and_extinguish"&gt;Embrace, extend, extinguish&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I hope I&amp;#8217;m completely wrong and that GitHub open up their &lt;span class="caps"&gt;API&lt;/span&gt; limits to Travis
so that open source projects like Flake8-&lt;span class="caps"&gt;AAA&lt;/span&gt; can still use it for reliable
&lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt;. But if things don&amp;#8217;t go well then I&amp;#8217;m certainly more ready to join the
GitHub exodus, just 18 months behind the&amp;nbsp;curve.&lt;/p&gt;
&lt;p&gt;Thanks Travis &lt;span class="caps"&gt;CI&lt;/span&gt; for all the builds, I hope we have many more to&amp;nbsp;come!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="topic:testing"></category></entry><entry><title>It’s good to extract</title><link href="https://jamescooke.info/its-good-to-extract.html" rel="alternate"></link><published>2018-04-21T19:00:00+01:00</published><updated>2018-04-21T19:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2018-04-21:/its-good-to-extract.html</id><summary type="html">&lt;p class="first last"&gt;Thoughts on the benefits of extracting library code from Python
projects into their own&amp;nbsp;packages.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Last week we released version 1 of &lt;a class="reference external" href="https://pypi.org/project/pysyncgateway/"&gt;pysyncgateway&lt;/a&gt; - a Python package for
communicating with &lt;a class="reference external" href="https://github.com/couchbase/sync_gateway"&gt;Couchbase&amp;#8217;s Sync Gateway&lt;/a&gt; via its &lt;span class="caps"&gt;REST&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;But this &amp;#8220;new&amp;#8221; library was not created from scratch. It consists mainly of code
extracted from my employer&amp;#8217;s Django based &lt;span class="caps"&gt;API&lt;/span&gt; server repository. That &lt;span class="caps"&gt;API&lt;/span&gt; is
now around 4,000 lines of code and test smaller and installs &lt;tt class="docutils literal"&gt;pysyncgateway&lt;/tt&gt;
as a package during&amp;nbsp;deployment.&lt;/p&gt;
&lt;p&gt;Both the process of extraction and the final result have been been really
helpful - this post covers some of the benefits that we have found so&amp;nbsp;far.&lt;/p&gt;
&lt;div class="section" id="better-separation-of-concerns"&gt;
&lt;h2&gt;Better separation of&amp;nbsp;concerns&lt;/h2&gt;
&lt;p&gt;The boundary between the new library and the server code makes it much easier
to reason about where responsibilities start and&amp;nbsp;end.&lt;/p&gt;
&lt;p&gt;Originally the Sync Gateway communication code was tightly knitted with our
Django &lt;span class="caps"&gt;API&lt;/span&gt;&amp;nbsp;server:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;It used Django settings for establishing URLs of the Sync Gateway instance in
test and&amp;nbsp;production.&lt;/li&gt;
&lt;li&gt;It provided test cases to our server&amp;#8217;s old &lt;tt class="docutils literal"&gt;Unittest&lt;/tt&gt; test suite, Those
test cases created test Databases, Users and Documents on the Sync Gateway
for each test - tearing them down&amp;nbsp;afterwards.&lt;/li&gt;
&lt;li&gt;It manipulated the statistical data retrieved from Sync Gateway and posted it
to our &lt;tt class="docutils literal"&gt;statsd&lt;/tt&gt; instance. Again Django&amp;#8217;s settings were used for&amp;nbsp;configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In extracting the library, these responsibilities have been cleaned out and&amp;nbsp;clarified:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Communication with Sync Gateway&amp;#8217;s &lt;span class="caps"&gt;API&lt;/span&gt; from Python - Responsibility of
&lt;tt class="docutils literal"&gt;pysyncgateway&lt;/tt&gt; library. All calls made to Sync Gateway are the
responsibility of the&amp;nbsp;library.&lt;/li&gt;
&lt;li&gt;Testing and mitigating any strange behaviours of the Sync Gateway &lt;span class="caps"&gt;API&lt;/span&gt; -
Responsibility of &lt;tt class="docutils literal"&gt;pysyncgateway&lt;/tt&gt;. The library&amp;#8217;s code is the place to pin
and mitigate any strange behaviours that are&amp;nbsp;found.&lt;/li&gt;
&lt;li&gt;Integration of Sync Gateway&amp;#8217;s objects (User, Document, Database) into
the &lt;span class="caps"&gt;API&lt;/span&gt; server and Django - Responsibility of &lt;span class="caps"&gt;API&lt;/span&gt; server code. The server
code remains responsible for managing its own tests&amp;nbsp;conditions.&lt;/li&gt;
&lt;li&gt;Synchronisation of Django&amp;#8217;s User object with Sync Gateway&amp;#8217;s User objects -
Responsibility of &lt;span class="caps"&gt;API&lt;/span&gt; server. The library is oblivious to the application that is
using it - in the same way that the &lt;a class="reference external" href="http://docs.python-requests.org/en/master/"&gt;requests libary&lt;/a&gt; is oblivious to the fact that is
it being used by &lt;tt class="docutils literal"&gt;pysyncgateway&lt;/tt&gt; to communicate with Sync&amp;nbsp;Gateway.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="improved-efficiency-of-development-and-test"&gt;
&lt;h2&gt;Improved efficiency of development and&amp;nbsp;test&lt;/h2&gt;
&lt;p&gt;While working on the library code, I&amp;#8217;ve found that testing has been much more&amp;nbsp;efficient.&lt;/p&gt;
&lt;p&gt;In terms of time, a single test run as part of a build on &lt;a class="reference external" href="https://circleci.com/gh/constructpm/pysyncgateway/tree/master"&gt;Circle &lt;span class="caps"&gt;CI&lt;/span&gt;&lt;/a&gt; takes around
10s whereas in our &lt;span class="caps"&gt;API&lt;/span&gt; server test suite it was taking 40s and was mixed in
with a much longer (~20 minute) long test&amp;nbsp;suite.&lt;/p&gt;
&lt;p&gt;The dedicated library repository now means that when I&amp;#8217;ve had questions
about how Sync Gateway behaves in certain situations, then the library is
the place to explore that behaviour and ensure that the library code is
fulfilling its main responsibility - communicating as best it can with any Sync
Gateway&amp;nbsp;instance.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="document-all-the-things"&gt;
&lt;h2&gt;Document all the&amp;nbsp;things&lt;/h2&gt;
&lt;p&gt;The documentation built by &lt;a class="reference external" href="http://www.sphinx-doc.org/en/master/"&gt;sphinx&lt;/a&gt; and
hosted on &lt;a class="reference external" href="https://pysyncgateway.readthedocs.io/en/stable/"&gt;Read The Docs&lt;/a&gt; is
great. I&amp;#8217;ve found it much better than reading docs via a code editor or
&lt;tt class="docutils literal"&gt;ipython&lt;/tt&gt; and end up using the &lt;span class="caps"&gt;RTD&lt;/span&gt; site as the main point of&amp;nbsp;reference.&lt;/p&gt;
&lt;p&gt;Luckily many of the docstrings were in place in much of the code before the
extraction, but moving they were mixed in with &lt;span class="caps"&gt;API&lt;/span&gt; project specific information
that could not be published. Again, the clarity of responsibilities meant that
we could clean up much of the docs to make them ready to be&amp;nbsp;published.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="still-a-monolith-but-with-packaging-benefits"&gt;
&lt;h2&gt;Still a monolith, but with packaging&amp;nbsp;benefits&lt;/h2&gt;
&lt;p&gt;Our server code remains a single monolith - it&amp;#8217;s one installed blob of code on
one server. The Sync Gateway code was extracted into a library, not a&amp;nbsp;service.&lt;/p&gt;
&lt;p&gt;However, now that the Sync Gateway code is installed from PyPi via
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-sync&lt;/span&gt;&lt;/tt&gt;, this provides the additional abstraction that we can select the
version of the library that will be&amp;nbsp;installed.&lt;/p&gt;
&lt;p&gt;This means we will have more flexibility to improve the library to work with
the latest version of Sync Gateway 2 (it&amp;#8217;s currently only tested with 1.5) and
also Python 3. We can upgrade the library, make breaking changes if required
and bump versions without touching the server monolith at&amp;nbsp;all.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="finally"&gt;
&lt;h2&gt;Finally&lt;/h2&gt;
&lt;p&gt;The extraction of &lt;tt class="docutils literal"&gt;pysyncgateway&lt;/tt&gt; has worked out well for us and so I&amp;#8217;m
preparing to extract our next library - a simple object orientated layer that
we use to communicate with&amp;nbsp;Nextcloud.&lt;/p&gt;
&lt;p&gt;There will be quite a bit of time invested to extract the code, but my
expectation is that the test benefits will be great. Not only will we get to
remove library code that takes around 6 minutes to test, but also we will gain
the library&amp;#8217;s test suite as a dedicated area to test the nuanced edge cases of
Nextcloud&amp;#8217;s &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Happy code&amp;nbsp;extraction!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="language:python"></category></entry><entry><title>AAA Part 2: Extracting Arrange code to make fixtures</title><link href="https://jamescooke.info/aaa-part-2-extracting-arrange-code-to-make-fixtures.html" rel="alternate"></link><published>2017-08-07T00:00:00+01:00</published><updated>2017-08-07T00:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2017-08-07:/aaa-part-2-extracting-arrange-code-to-make-fixtures.html</id><summary type="html">&lt;p class="first last"&gt;This post explores how to extract arrangement code when working with
the Arrange Act Assert pattern so that it can be used with certainty
across the test&amp;nbsp;suite.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In this post I will describe how code in tests&amp;#8217; Arrange blocks can become
over-complicated, break the &lt;span class="caps"&gt;AAA&lt;/span&gt; pattern and benefit from&amp;nbsp;extraction.&lt;/p&gt;
&lt;div class="section" id="background"&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;This post is Part 2 of a series on the Arrange Act Assert pattern for Python
developers. See &lt;a class="reference external" href="/arrange-act-assert-pattern-for-python-developers.html"&gt;Part 1&lt;/a&gt; for an
introduction to the pattern and outline of its constituent&amp;nbsp;parts.&lt;/li&gt;
&lt;li&gt;When I mention &amp;#8220;code extraction&amp;#8221; I&amp;#8217;m primarily referring to the Extract
Method &lt;a class="footnote-reference" href="#em" id="footnote-reference-1"&gt;[1]&lt;/a&gt; of refactoring. &lt;a class="reference external" href="https://www.goodreads.com/book/show/387190.Test_Driven_Development"&gt;Kent Beck&amp;#8217;s book &amp;#8220;Test Driven Development: By
Example&amp;#8221;&lt;/a&gt; really
turned me on to the value in eliminating duplicated code between tests and
between tests and the &lt;span class="caps"&gt;SUT&lt;/span&gt; &lt;a class="footnote-reference" href="#sut" id="footnote-reference-2"&gt;[2]&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I&amp;#8217;m using &lt;a class="reference external" href="https://docs.pytest.org/en/latest/"&gt;pytest&lt;/a&gt; in this example
which means that fixtures are marked with the &lt;tt class="docutils literal"&gt;&amp;#64;pytest.fixture&lt;/tt&gt; decorator.
If you&amp;#8217;re using &lt;tt class="docutils literal"&gt;unittest&lt;/tt&gt; then you could extract the set up code into the
&lt;tt class="docutils literal"&gt;TestCase.setUp&lt;/tt&gt; method.&lt;/li&gt;
&lt;li&gt;If you can, perform Extract Method while your test suite is &lt;span class="caps"&gt;GREEN&lt;/span&gt; &lt;a class="footnote-reference" href="#green" id="footnote-reference-3"&gt;[3]&lt;/a&gt;.
This means that you can be more assured that your refactoring has worked
without&amp;nbsp;errors.&lt;/li&gt;
&lt;li&gt;During my work I often build permission systems that manage access to
resources such as files, accounts, projects, etc, based on the connection
between Users and those resources. The example test below is from one of
those projects. I often use Simpsons and Futurama characters in tests because
I think it makes it easier to visualise the test conditions when characters
are used that other programmers may be familiar with&amp;nbsp;already.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="the-problem"&gt;
&lt;h2&gt;The&amp;nbsp;problem&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;ve found that this problem, which I call &amp;#8220;Complicated Setup&amp;#8221;, occurs as a test suite grows and
the complexity of the tests on the outside of the code&amp;nbsp;increases.&lt;/p&gt;
&lt;p&gt;Tests will often need to combine a number of objects in increasingly complex
states to build the &lt;span class="caps"&gt;SUT&lt;/span&gt; &lt;a class="footnote-reference" href="#sut" id="footnote-reference-4"&gt;[2]&lt;/a&gt;. As a result, additional assertions are
required before the Act block to ensure that the test conditions are correctly
established. The problem with these additional assertions is that
they break the &lt;span class="caps"&gt;AAA&lt;/span&gt; pattern because there should be no assertions in the Arrange&amp;nbsp;block.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Warning - this test does *not* fit the AAA pattern because it has&lt;/span&gt;
&lt;span class="c1"&gt;# assertions in the Arrange block.&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_owner_invite_admin&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Leela can invite Bender to an additional Project, Fry is notified&lt;/span&gt;

&lt;span class="sd"&gt;    ----------------+---------------+-----------&lt;/span&gt;
&lt;span class="sd"&gt;     Account Role   | Project Role  | Name&lt;/span&gt;
&lt;span class="sd"&gt;    ----------------+---------------+-----------&lt;/span&gt;
&lt;span class="sd"&gt;     Owner          | -             | Leela&lt;/span&gt;
&lt;span class="sd"&gt;     Admin          | -             | Fry&lt;/span&gt;
&lt;span class="sd"&gt;     Viewer         | Admin         | Bender&lt;/span&gt;
&lt;span class="sd"&gt;    ----------------+---------------+-----------&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="c1"&gt;# LEELA (and account)&lt;/span&gt;
    &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AccountFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;owner__first_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Leela&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;account_document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AccountDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;account_document&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_or_create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;leela&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;
    &lt;span class="n"&gt;new_project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;leela&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;new_project&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# FRY&lt;/span&gt;
    &lt;span class="n"&gt;admin_membership&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AccountMembershipFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;permission&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;AA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;person__first_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Fry&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;admin_membership&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;
    &lt;span class="c1"&gt;# BENDER&lt;/span&gt;
    &lt;span class="n"&gt;project_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProjectMembershipFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;person__first_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Bender&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;admin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;project_couchbase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;project&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;bender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;person&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# Check&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;bender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accounts&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="c1"&gt;# &amp;lt;&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;bender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;leela&lt;/span&gt;    &lt;span class="c1"&gt;# &amp;lt; Assertions in Arrange&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;bender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;projects&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="c1"&gt;# &amp;lt;&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;bender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;new_project&lt;/span&gt;    &lt;span class="c1"&gt;# &amp;lt;&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;fry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;               &lt;span class="c1"&gt;# &amp;lt;&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;leela&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new_project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;fry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&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;/pre&gt;&lt;/div&gt;
&lt;p&gt;Tests on the arrangement of the &lt;span class="caps"&gt;SUT&lt;/span&gt; will often be informed by the tests that are about
to be carried out on it in the Act. Here I want to ensure that Fry is notified with a new
message so it is important that after Arrange Fry has no messages waiting.
But adding these assertions before the Act section means
breaking &lt;span class="caps"&gt;AAA&lt;/span&gt; and this is a smell the test has grown too complex and should be cut&amp;nbsp;down.&lt;/p&gt;
&lt;p&gt;It is possible to use Extract Method to create a fixture that solves this issue
and returns the test to pure &lt;span class="caps"&gt;AAA&lt;/span&gt; pattern. I&amp;#8217;ve used a simplified example to
illustrate how to solve this below. I&amp;#8217;ve imagined a &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;SUT&lt;/span&gt;&lt;/tt&gt; class that must be
called with some arrangement functions like &lt;tt class="docutils literal"&gt;arrange_a&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;arrange_b&lt;/tt&gt;,&amp;nbsp;etc.&lt;/p&gt;
&lt;script async class="speakerdeck-embed" data-id="da526efe5fb6445eadb71b7f4b66c2f5" data-ratio="1.82857142857143" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;&lt;p&gt;If the example does not load for you, you can &lt;a class="reference external" href="https://speakerdeck.com/jamescooke/extract-arrangement-code"&gt;view it on speakerdeck&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now applying this process to the Futurama account test above I get the
following fixture with its own dedicated test and a much simpler test for the
invite&amp;nbsp;behaviour.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;account_members&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Returns:&lt;/span&gt;
&lt;span class="sd"&gt;        tuple:&lt;/span&gt;
&lt;span class="sd"&gt;            User: Leela - Account owner.&lt;/span&gt;
&lt;span class="sd"&gt;            User: Fry - Admin.&lt;/span&gt;
&lt;span class="sd"&gt;            User: Bender - Project admin.&lt;/span&gt;

&lt;span class="sd"&gt;    ----------------+---------------+-----------&lt;/span&gt;
&lt;span class="sd"&gt;     Account Role   | Project Role  | Name&lt;/span&gt;
&lt;span class="sd"&gt;    ----------------+---------------+-----------&lt;/span&gt;
&lt;span class="sd"&gt;     Owner          | -             | Leela&lt;/span&gt;
&lt;span class="sd"&gt;     Admin          | -             | Fry&lt;/span&gt;
&lt;span class="sd"&gt;     Viewer         | Admin         | Bender&lt;/span&gt;
&lt;span class="sd"&gt;    ----------------+---------------+-----------&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="c1"&gt;# LEELA (and account)&lt;/span&gt;
    &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AccountFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;owner__first_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Leela&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;account_document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AccountDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;account_document&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_or_create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;leela&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;
    &lt;span class="n"&gt;new_project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;leela&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;new_project&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# FRY&lt;/span&gt;
    &lt;span class="n"&gt;admin_membership&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AccountMembershipFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;permission&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;AA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;person__first_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Fry&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;admin_membership&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;
    &lt;span class="c1"&gt;# BENDER&lt;/span&gt;
    &lt;span class="n"&gt;project_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProjectMembershipFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;person__first_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Bender&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;admin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;project_couchbase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;project&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;bender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;person&amp;#39;&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;leela&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bender&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_account_members&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account_members&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Fry has no pending messages and Bender is a member of the Account&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account_members&lt;/span&gt;

    &lt;span class="k"&gt;assert&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;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="n"&gt;leela&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;bender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accounts&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="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;bender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;leela&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;bender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;projects&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="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;bender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;new_project&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;fry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_owner_invite_admin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account_members&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Leela can invite Bender to an additional Project, Fry is notified&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;leela&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account_members&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;leela&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new_project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;fry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&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;/pre&gt;&lt;/div&gt;
&lt;p&gt;Even though this example is long winded, I hope you can see that the
extraction of the set up code into its own fixture has simplified the tests and
brought the code back into conformity with the &lt;span class="caps"&gt;AAA&lt;/span&gt;&amp;nbsp;pattern.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="benefits-of-extraction"&gt;
&lt;h2&gt;Benefits of&amp;nbsp;extraction&lt;/h2&gt;
&lt;p&gt;The result of the extraction process is a pair of tests with a single fixture. The tests
fit the &lt;span class="caps"&gt;AAA&lt;/span&gt; pattern that I advocated in Part 1 of this series and the resulting
code&amp;#8217;s structure has a number of advantages for the future of the test&amp;nbsp;suite:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Continued development on the fixture can happen using &lt;span class="caps"&gt;TDD&lt;/span&gt; &lt;a class="footnote-reference" href="#tdd" id="footnote-reference-5"&gt;[4]&lt;/a&gt; by adding
new requirements to &lt;tt class="docutils literal"&gt;test_fixture()&lt;/tt&gt; and then expanding the fixture to get
back to &lt;span class="caps"&gt;GREEN&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The resulting fixture can be reused really easily. Permutations of different
actions on a particular &lt;span class="caps"&gt;SUT&lt;/span&gt; can be easily tested without having to depend on
our power of copy and paste and without creating more duplicated&amp;nbsp;code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;If a situation arises in the future where the arrangement of the &lt;span class="caps"&gt;SUT&lt;/span&gt; needs to
change in the fixture all the tests that use it &lt;em&gt;might&lt;/em&gt; fail. However, the
payoff for the additional failure of the fixture&amp;#8217;s dedicated tests is that
there is the opportunity to fix the problem in one place - the extracted code
in the&amp;nbsp;fixture.&lt;/p&gt;
&lt;p&gt;On top of that, the fix can be performed using &lt;span class="caps"&gt;TDD&lt;/span&gt; because the fixture is
already extracted and under test - a potential double&amp;nbsp;win.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this way the test suite remains dynamic, clear and able to adapt with the
software it&amp;#8217;s&amp;nbsp;testing.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="should-all-fixtures-have-their-own-tests"&gt;
&lt;h2&gt;Should all fixtures have their own&amp;nbsp;tests?&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;m often asked whether I think test fixtures should be tested. My answer is:
&amp;#8220;It&amp;nbsp;depends&amp;#8221;.&lt;/p&gt;
&lt;p&gt;When the fixture was arrived at via &amp;#8220;Complicated setup&amp;#8221; then my answer is
&amp;#8220;yes&amp;#8221;. As we&amp;#8217;ve seen, the &lt;tt class="docutils literal"&gt;test_fixture()&lt;/tt&gt; test remains to pin the fixture&amp;#8217;s
behaviour and assert that the &lt;span class="caps"&gt;SUT&lt;/span&gt; is in the expected&amp;nbsp;state.&lt;/p&gt;
&lt;p&gt;When the fixture has been extracted because of &amp;#8220;Setup duplication&amp;#8221; &lt;a class="footnote-reference" href="#sd" id="footnote-reference-6"&gt;[5]&lt;/a&gt; there will
be a fixture created that does not have its own explicit test. Instead, the
fixture is tested implicitly by the two tests but does not have a dedicated
test of its&amp;nbsp;own.&lt;/p&gt;
&lt;p&gt;For me this is an &amp;#8220;&lt;span class="caps"&gt;OK&lt;/span&gt;&amp;#8221; situation and if it turns out that the fixture should be
adjusted then a fixture test can be created to facilitate that change under the
usual &lt;span class="caps"&gt;RED&lt;/span&gt;, &lt;span class="caps"&gt;GREEN&lt;/span&gt;, &lt;span class="caps"&gt;REFACTOR&lt;/span&gt;&amp;nbsp;cycle.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="flake8-aaa"&gt;
&lt;h2&gt;flake8-aaa&lt;/h2&gt;
&lt;p&gt;Check out &lt;a class="reference external" href="https://flake8-aaa.readthedocs.io/en/stable/"&gt;flake8-aaa&lt;/a&gt; - a
Flake8 plugin that makes it easier to write tests that follow the Arrange Act
Assert&amp;nbsp;pattern.&lt;/p&gt;
&lt;p&gt;Happy&amp;nbsp;testing!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tiny-glossary"&gt;
&lt;h2&gt;Tiny&amp;nbsp;glossary&lt;/h2&gt;
&lt;table class="docutils footnote" frame="void" id="em" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Extract Method is a refactoring step &lt;a class="reference external" href="https://refactoring.com/catalog/extractMethod.html"&gt;defined here&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="sut" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;[2]&lt;/td&gt;&lt;td&gt;&lt;em&gt;(&lt;a class="fn-backref" href="#footnote-reference-2"&gt;1&lt;/a&gt;, &lt;a class="fn-backref" href="#footnote-reference-4"&gt;2&lt;/a&gt;)&lt;/em&gt; &lt;a class="reference external" href="https://en.wikipedia.org/wiki/System_under_test"&gt;System Under Test&lt;/a&gt; I&amp;#8217;ve used this to mean the
Unit under test, there is no implication around the size of the &amp;#8220;system&amp;#8221; or
&amp;#8220;unit&amp;#8221;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="green" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;span class="caps"&gt;GREEN&lt;/span&gt; is the name for the state when all tests in your suite pass.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="tdd" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-5"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Test Driven Development.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="sd" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-6"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Setup duplication: My name for the situation where there are large
chunks of Arrange code duplicated between tests. This topic warrants a
follow-up post.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="language:python"></category><category term="topic:testing"></category></entry><entry><title>Arrange Act Assert pattern for Python developers</title><link href="https://jamescooke.info/arrange-act-assert-pattern-for-python-developers.html" rel="alternate"></link><published>2017-07-06T23:00:00+01:00</published><updated>2017-07-06T23:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2017-07-06:/arrange-act-assert-pattern-for-python-developers.html</id><summary type="html">&lt;p class="first last"&gt;This post introduces the Arrange Act Assert pattern of testing and
shows how it can be used in a Python context with&amp;nbsp;Pytest.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This is the first of two posts exploring the Arrange Act Assert pattern and
how to apply it to Python tests.
It presents a recognisable and reusable test template
following the Arrange Act Assert pattern of testing. In addition, I aim to
present strategies for test writing and refactoring which I&amp;#8217;ve developed over
the last couple of years, both on my own projects and within&amp;nbsp;teams.&lt;/p&gt;
&lt;p&gt;In this first part I will introduce the Arrange Act Assert pattern and discuss its
constituent&amp;nbsp;parts.&lt;/p&gt;
&lt;div class="section" id="what-is-arrange-act-assert"&gt;
&lt;h2&gt;What is Arrange Act&amp;nbsp;Assert?&lt;/h2&gt;
&lt;p&gt;The &amp;#8220;Arrange-Act-Assert&amp;#8221; (also &lt;span class="caps"&gt;AAA&lt;/span&gt; and 3A) pattern of testing was &lt;a class="reference external" href="https://xp123.com/articles/3a-arrange-act-assert/"&gt;observed and
named by Bill Wake in 2001&lt;/a&gt;. I first came across it in
&lt;a class="reference external" href="https://www.goodreads.com/book/show/387190.Test_Driven_Development"&gt;Kent Beck&amp;#8217;s book &amp;#8220;Test Driven Development: By Example&amp;#8221;&lt;/a&gt; and I
spoke about it at &lt;a class="reference external" href="https://jamescooke.info/cleaner-unit-testing-with-the-arrange-act-assert-pattern.html"&gt;PyConUK 2016&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The pattern focuses each test on a single action. The advantage of this focus
is that it clearly separates the arrangement of the System Under Test (&lt;span class="caps"&gt;SUT&lt;/span&gt;) and
the assertions that are made on it after the&amp;nbsp;action.&lt;/p&gt;
&lt;p&gt;On multiple projects I&amp;#8217;ve worked on I&amp;#8217;ve experienced organised and &amp;#8220;clean&amp;#8221; code
in the main codebase, but disorganisation and inconsistency in the
test suite. However when &lt;span class="caps"&gt;AAA&lt;/span&gt; is applied, I&amp;#8217;ve found it helps by unifying and
clarifying the structure of tests which helps make the test suite much more
understandable and&amp;nbsp;manageable.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tl-dr-the-shape-of-an-aaa-test"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt;: The shape of an &lt;span class="caps"&gt;AAA&lt;/span&gt;&amp;nbsp;test&lt;/h2&gt;
&lt;p&gt;Here is a test that I was working on recently that follows the &lt;span class="caps"&gt;AAA&lt;/span&gt; pattern.
I&amp;#8217;ve extracted it from Vim and blocked out the code with the colour that Vim&amp;nbsp;assigns.&lt;/p&gt;
&lt;img alt="The shape of a test in Python built with Arrange Act Assert." src="https://jamescooke.info/images/test_shape.png" /&gt;
&lt;p&gt;Hopefully in this rough image you will see three sections to the test separated
by an empty&amp;nbsp;line:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;First there is the test definition, docstring and&amp;nbsp;Arrangement.&lt;/li&gt;
&lt;li&gt;Empty&amp;nbsp;line.&lt;/li&gt;
&lt;li&gt;In the middle, there is a single line of code - this is the most important
part: The&amp;nbsp;Act.&lt;/li&gt;
&lt;li&gt;Empty&amp;nbsp;line.&lt;/li&gt;
&lt;li&gt;Finally there are the Assertions. You can see that the Assert block code
lines all start with the orange / brown colour - that is because the Python
keyword &lt;tt class="docutils literal"&gt;assert&lt;/tt&gt; is marked with this colour in Vim with my current&amp;nbsp;configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While working on test suites that employ this pattern, my experience has been
that I&amp;#8217;ve found it easier to understand each test. My eye has definitely got
used to the test &amp;#8220;shape&amp;#8221;. Want to know what is being tested? Just look at the
clear line above the assertion&amp;nbsp;block.&lt;/p&gt;
&lt;p&gt;Follow this pattern across your tests and your suite will be much&amp;nbsp;improved.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="background"&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;ll now go into detail on each of these parts using Pytest and a toy
test example - a simple happy-path test for Python&amp;#8217;s builtin
&lt;tt class="docutils literal"&gt;list.reverse&lt;/tt&gt; function.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve made the following&amp;nbsp;assumptions:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;We all love &lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0008/"&gt;&lt;span class="caps"&gt;PEP008&lt;/span&gt;&lt;/a&gt;, so we want
tests to pass &lt;tt class="docutils literal"&gt;flake8&lt;/tt&gt; linting.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0020/"&gt;&lt;span class="caps"&gt;PEP020&lt;/span&gt;, The Zen of Python&lt;/a&gt;, is
also something we work towards - I will use some of it&amp;#8217;s &amp;#8220;mantras&amp;#8221; when I
justify some of the suggestions in this&amp;nbsp;guide.&lt;/li&gt;
&lt;li&gt;Simplicity trumps performance. We want a test suite that is easy to maintain
and manage and can pay for that with some performance loss. I&amp;#8217;ve assumed this
is a reasonable trade off because the tests are run much less frequently than
the &lt;span class="caps"&gt;SUT&lt;/span&gt; in&amp;nbsp;production.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This post is only an introduction to the &lt;span class="caps"&gt;AAA&lt;/span&gt; pattern. Where certain topics will
be covered in more detail in future posts in this series, I have marked them
with a&amp;nbsp;footnote.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="definition"&gt;
&lt;h2&gt;Definition&lt;/h2&gt;
&lt;p&gt;The definition of the test&amp;nbsp;function.&lt;/p&gt;
&lt;div class="section" id="example"&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_reverse&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="guidelines"&gt;
&lt;h3&gt;Guidelines&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Name your function something descriptive because the function name will be
shown when the test fails in Pytest&amp;nbsp;output.&lt;/li&gt;
&lt;li&gt;Good test method names can make docstrings redundant in simple tests (&lt;a class="reference external" href="https://github.com/jamescooke/blog/pull/10#discussion_r125855056"&gt;thanks
Adam!&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="docstring"&gt;
&lt;h2&gt;Docstring&lt;/h2&gt;
&lt;p&gt;An optional short single line statement about the behaviour under&amp;nbsp;test.&lt;/p&gt;
&lt;div class="section" id="example-1"&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;list.reverse inverts the order of items in a list, in place&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="guidelines-1"&gt;
&lt;h3&gt;Guidelines&lt;/h3&gt;
&lt;p&gt;Docstrings are not part of the &lt;span class="caps"&gt;AAA&lt;/span&gt; pattern. Consider if your test needs one or
if you are best to omit it for&amp;nbsp;simplicity.&lt;/p&gt;
&lt;p&gt;If you do include a docstring, then I recommend that&amp;nbsp;you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Follow the existing Docstring style of your project so that the tests are
consistent with the code base you are&amp;nbsp;testing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Keep the language positive - state clearly what the expected behaviour is.
Positive docstrings read similar&amp;nbsp;to:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;X does Y when&amp;nbsp;Z&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Or&amp;#8230;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given Z, then X does&amp;nbsp;Y&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Be cautious when using any uncertain language in the docstring and follow the
mantra &amp;#8220;Explicit is better than implicit&amp;#8221; (&lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0020/"&gt;&lt;span class="caps"&gt;PEP20&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Words like &amp;#8220;should&amp;#8221; and &amp;#8220;if&amp;#8221; introduce uncertainty. For&amp;nbsp;example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;X should do Y if&amp;nbsp;Z&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this case the reader could be left with questions. Is X doing it right at
the moment? Is this a &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;TODO&lt;/span&gt;&lt;/tt&gt; note? Is this a test for an expected&amp;nbsp;failure?&lt;/p&gt;
&lt;p&gt;In a similar vein, avoid future&amp;nbsp;case.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;X will do Y when&amp;nbsp;Z&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Again, this reads like a &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;TODO&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="arrange"&gt;
&lt;h2&gt;Arrange&lt;/h2&gt;
&lt;p&gt;The block of code that sets up the conditions for the test&amp;nbsp;action.&lt;/p&gt;
&lt;div class="section" id="example-2"&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;p&gt;There&amp;#8217;s not much work to do in this example to build a list, so the arrangement
block is just one&amp;nbsp;line.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;greek&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alpha&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;beta&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;gamma&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;delta&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="guidelines-2"&gt;
&lt;h3&gt;Guidelines&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Use a single block of code with no empty&amp;nbsp;lines.&lt;/li&gt;
&lt;li&gt;Do not use &lt;tt class="docutils literal"&gt;assert&lt;/tt&gt; in the Arrange block. If you need to make an assertion
about your arrangement, then this is a smell that your arrangement is too
complicated and should be extracted to a fixture or setup function and tested
in its own&amp;nbsp;right.&lt;/li&gt;
&lt;li&gt;Only prepare non-deterministic results not available after&amp;nbsp;action.&lt;/li&gt;
&lt;li&gt;The arrange section should not require comments. If you have a large
arrangement in your tests which is complex enough to require detailed
comments then consider:&lt;ul&gt;
&lt;li&gt;Extracting the comments into a multi-line&amp;nbsp;docstring.&lt;/li&gt;
&lt;li&gt;Extracting the arrangement code into a fixture and testing that the fixture
is establishing the expected conditions as previously&amp;nbsp;mentioned.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="act"&gt;
&lt;h2&gt;Act&lt;/h2&gt;
&lt;p&gt;The line of code where the Action is taken on the &lt;span class="caps"&gt;SUT&lt;/span&gt;.&lt;/p&gt;
&lt;div class="section" id="example-3"&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;greek&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="guidelines-3"&gt;
&lt;h3&gt;Guidelines&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Start every Action line with &lt;tt class="docutils literal"&gt;result =&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;This makes it easier to distinguish test actions and means you can avoid the
hardest job in programming: naming. When every result is called &lt;tt class="docutils literal"&gt;result&lt;/tt&gt;,
then you do not need to waste brain power wondering if it should be &lt;tt class="docutils literal"&gt;item =&lt;/tt&gt;
or &lt;tt class="docutils literal"&gt;response =&lt;/tt&gt; etc. An added benefit is that you can find test actions
easily with a tool like &lt;tt class="docutils literal"&gt;grep&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Even when there is no result from the action, capture it with &lt;tt class="docutils literal"&gt;result =&lt;/tt&gt;
and then &lt;tt class="docutils literal"&gt;assert result is None&lt;/tt&gt;. In this way, the &lt;span class="caps"&gt;SUT&lt;/span&gt;&amp;#8217;s behaviour is&amp;nbsp;pinned.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;If you struggle to write a single line action, then consider extracting some
of that code into your&amp;nbsp;arrangement.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The action can be wrapped in &lt;tt class="docutils literal"&gt;with ... raises&lt;/tt&gt; for expected exceptions. In
this case your action will be two lines surrounded by empty&amp;nbsp;lines.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="assert"&gt;
&lt;h2&gt;Assert&lt;/h2&gt;
&lt;p&gt;The block of code that performs the assertions on the state of the &lt;span class="caps"&gt;SUT&lt;/span&gt; after
the&amp;nbsp;action.&lt;/p&gt;
&lt;div class="section" id="example-4"&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;greek&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delta&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;gamma&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;beta&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;alpha&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="guidelines-4"&gt;
&lt;h3&gt;Guidelines&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Use a single block of code with no empty&amp;nbsp;lines.&lt;/li&gt;
&lt;li&gt;First test &lt;tt class="docutils literal"&gt;result&lt;/tt&gt;, then side&amp;nbsp;effects.&lt;/li&gt;
&lt;li&gt;Limit the actions that you make in this block. Ideally, no actions should
happen, but that is not always&amp;nbsp;possible.&lt;/li&gt;
&lt;li&gt;Use simple blocks of assertions. If you find that you are repeatedly writing
the same code to extract information from the &lt;span class="caps"&gt;SUT&lt;/span&gt; and perform assertions on
it, then consider extracting an assertion&amp;nbsp;helper.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="the-final-test"&gt;
&lt;h2&gt;The final&amp;nbsp;test&lt;/h2&gt;
&lt;p&gt;Here&amp;#8217;s the example test in&amp;nbsp;full:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_reverse&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    list.reverse inverts the order of items in a list, in place&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;greek&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alpha&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;beta&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;gamma&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;delta&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;greek&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;greek&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;delta&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;gamma&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;beta&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;alpha&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="flake8-aaa"&gt;
&lt;h2&gt;flake8-aaa&lt;/h2&gt;
&lt;p&gt;Check out &lt;a class="reference external" href="https://flake8-aaa.readthedocs.io/en/stable/"&gt;flake8-aaa&lt;/a&gt; - a
Flake8 plugin that makes it easier to write tests that follow the Arrange Act
Assert pattern outlined&amp;nbsp;above.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="thanks"&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;p&gt;I hope that this introduction has been helpful and you will return for part 2:
&lt;a class="reference external" href="/aaa-part-2-extracting-arrange-code-to-make-fixtures.html"&gt;&lt;span class="caps"&gt;AAA&lt;/span&gt; Part 2: Extracting Arrange code to make fixtures&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a class="reference external" href="https://adamj.eu/"&gt;Adam&lt;/a&gt; for reviewing this post and his helpful&amp;nbsp;feedback.&lt;/p&gt;
&lt;p&gt;Thanks for reading and happy&amp;nbsp;testing!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="language:python"></category><category term="topic:testing"></category></entry><entry><title>Comparing Django Q Objects in Python 3 with pytest</title><link href="https://jamescooke.info/comparing-django-q-objects-in-python-3-with-pytest.html" rel="alternate"></link><published>2017-05-30T22:00:00+01:00</published><updated>2017-05-30T22:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2017-05-30:/comparing-django-q-objects-in-python-3-with-pytest.html</id><summary type="html">&lt;p class="first last"&gt;An updated simple assertion helper for comparing instances of
Django&amp;#8217;s Q objects using pytest in Python&amp;nbsp;3.&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="background"&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;In a &lt;a class="reference external" href="https://jamescooke.info/comparing-django-q-objects.html"&gt;previous post&lt;/a&gt; I wrote
about comparing Django&amp;#8217;s Q object instances. The original code was Python 2
with unittest and was &lt;a class="reference external" href="https://github.com/jamescooke/blog/issues/6"&gt;due for an update&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The previous issue with comparing Django&amp;#8217;s Q objects remains the&amp;nbsp;same:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Django&amp;#8217;s Q object does not implement &lt;tt class="docutils literal"&gt;__cmp__&lt;/tt&gt; and neither does
&lt;tt class="docutils literal"&gt;Node&lt;/tt&gt; which it extends (&lt;tt class="docutils literal"&gt;Node&lt;/tt&gt; is in the &lt;tt class="docutils literal"&gt;django.utils.tree&lt;/tt&gt; module).&lt;/p&gt;
&lt;p&gt;Unfortunately, that means that comparison of Q objects that are equal&amp;nbsp;fails.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="a-simple-python-3-solution"&gt;
&lt;h2&gt;A simple Python 3&amp;nbsp;solution&lt;/h2&gt;
&lt;p&gt;The following is a Python 3.6 assertion helper for use with pytest that uses
the original strategy of comparing the string versions of the Q&amp;nbsp;objects.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assert_q_equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Test two Q objects for equality. Does is not match commutative.&lt;/span&gt;

&lt;span class="sd"&gt;    Args:&lt;/span&gt;
&lt;span class="sd"&gt;        left (Q)&lt;/span&gt;
&lt;span class="sd"&gt;        right (Q)&lt;/span&gt;

&lt;span class="sd"&gt;    Raises:&lt;/span&gt;
&lt;span class="sd"&gt;        AssertionError: When -&lt;/span&gt;
&lt;span class="sd"&gt;            * `left` or `right` are not an instance of `Q`&lt;/span&gt;
&lt;span class="sd"&gt;            * `left` and `right` are not considered equal.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__class__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; is not subclass of Q&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__class__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; is not subclass of Q&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;assert&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;left&lt;/span&gt;&lt;span class="p"&gt;)&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;right&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Q&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; != Q&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This time the helper is just a function rather than a mixin for
&lt;tt class="docutils literal"&gt;unittest.TestCase&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;isinstance&lt;/tt&gt; is used for comparison so that any instance of a class derived
from &lt;tt class="docutils literal"&gt;Q&lt;/tt&gt; can also be matched. The assertions have secondary expressions in
the form of &lt;a class="reference external" href="https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep498"&gt;f-strings&lt;/a&gt; to give
helpful output without raising a custom&amp;nbsp;assertion.&lt;/p&gt;
&lt;p&gt;When two &lt;tt class="docutils literal"&gt;Q&lt;/tt&gt; instances do not match, pytest shows the following&amp;nbsp;output:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
______________________ test_neq_multi_not_commutative ______________________
test_assert_q_equal.py:83: in test_neq_multi_not_commutative
    assert_q_equal(q_a, q_b)
test_assert_q_equal.py:22: in assert_q_equal
    assert str(left) == str(right), f'Q{left} != Q{right}'
E   AssertionError: Q(AND: ('speed', 12), ('direction', 'north')) != Q(AND: ('direction', 'north'), ('speed', 12))
E   assert &amp;quot;(AND: ('spee...n', 'north'))&amp;quot; == &amp;quot;(AND: ('direc...'speed', 12))&amp;quot;
E     - (AND: ('speed', 12), ('direction', 'north'))
E     + (AND: ('direction', 'north'), ('speed', 12))
==================== 1 failed, 7 passed in 0.07 seconds ====================
&lt;/pre&gt;
&lt;p&gt;The important thing is to adjust your assertion helpers to best fit
the needs of your test suite and&amp;nbsp;team.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="final-testing-related-note"&gt;
&lt;h2&gt;Final testing related&amp;nbsp;note&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;a class="reference external" href="https://github.com/jamescooke/blog/pull/7#pullrequestreview-41177014"&gt;Adam&amp;#8217;s feedback on my initial post&lt;/a&gt;, I
improved the assertions to use &lt;tt class="docutils literal"&gt;isinstance&lt;/tt&gt; and secondary expressions to
provide helpful&amp;nbsp;output.&lt;/p&gt;
&lt;p&gt;Tests for &lt;tt class="docutils literal"&gt;assert_q_equal&lt;/tt&gt; and its original code are &lt;a class="reference external" href="https://gist.github.com/jamescooke/1bed3414fee7d5c72540e567bcd63887"&gt;in this gist&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy&amp;nbsp;testing!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="topic:testing"></category><category term="language:python"></category><category term="topic:django"></category></entry><entry><title>My Vim setup for Python development</title><link href="https://jamescooke.info/my-vim-setup-for-python-development.html" rel="alternate"></link><published>2017-01-04T14:30:00+00:00</published><updated>2017-01-04T14:30:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2017-01-04:/my-vim-setup-for-python-development.html</id><summary type="html">&lt;p class="first last"&gt;My current Vim setup for Python&amp;nbsp;development.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Below is a list of my Vim plug-ins and&amp;nbsp;configurations.&lt;/p&gt;
&lt;p&gt;My goal has been to make Vim more useful for (primarily) Python development.
This post refers to Vim 7, because I have not yet updated to Vim 8. I&amp;#8217;m using
&lt;a class="reference external" href="https://github.com/junegunn/vim-plug"&gt;vim-plug&lt;/a&gt; to manage my packages, so
mentions of packages below will use the &lt;tt class="docutils literal"&gt;Plug&lt;/tt&gt; command.&lt;/p&gt;
&lt;p&gt;All the commands and configuration below come from my &lt;a class="reference external" href="https://github.com/jamescooke/dotfiles/blob/master/store/.vimrc"&gt;vimrc file&lt;/a&gt;. You&amp;#8217;ll
find that I don&amp;#8217;t have a large number of plug-ins or configuration lines
compared to other more famous Vim users (cough) &lt;a class="reference external" href="https://github.com/nelstrom/dotfiles/blob/master/bundles.vim"&gt;Drew&lt;/a&gt; (cough). That
is a direct result of &lt;a class="reference external" href="https://vimeo.com/65250028"&gt;Kris Jenkins&amp;#8217;s Bare Bones Navigation Vim talk&lt;/a&gt; at Vim London. The main result of which has been
that I have always run a very simple Vim&amp;nbsp;setup.&lt;/p&gt;
&lt;p&gt;My relatively recent use of &lt;span class="caps"&gt;FZF&lt;/span&gt; and Ctags listed below are a direct result of
attending the most recent &lt;a class="reference external" href="https://www.meetup.com/Vim-London/"&gt;Vim London meetup&lt;/a&gt; and, if you&amp;#8217;re in the London area, I
fully recommend joining and attending. Every meetup I attend, my Vim-fu&amp;nbsp;improves.&lt;/p&gt;
&lt;div class="section" id="specific-python-config"&gt;
&lt;h2&gt;Specific Python&amp;nbsp;config&lt;/h2&gt;
&lt;p&gt;The following are my &lt;tt class="docutils literal"&gt;.vimrc&lt;/tt&gt; lines for handling&amp;nbsp;Python.&lt;/p&gt;
&lt;p&gt;When searching for files with Vim, only load Python&amp;nbsp;files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;suffixesadd&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;.&lt;span class="k"&gt;py&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ignore &lt;tt class="docutils literal"&gt;pyc&lt;/tt&gt; files when expanding&amp;nbsp;wildcards:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;wildignore&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;*.pyc
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Don&amp;#8217;t show &lt;tt class="docutils literal"&gt;pyc&lt;/tt&gt; in file&amp;nbsp;lists:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;:netrw_list_hide&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.*\.pyc$&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Keep &amp;#8220;Pythonic&amp;#8221; tabs using 4 white&amp;nbsp;spaces:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;autoindent&lt;/span&gt; &lt;span class="nb"&gt;nosmartindent&lt;/span&gt;    &lt;span class="c"&gt;&amp;quot; auto/smart indent&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;smarttab&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;expandtab&lt;/span&gt;                   &lt;span class="c"&gt;&amp;quot; expand tabs to spaces&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;shiftwidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;softtabstop&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I really get frustrated with tabs that look like white spaces, so I ensure
they are visible by telling Vim to show all tabs as little arrows &lt;tt class="docutils literal"&gt;▷&lt;/tt&gt;. This
line also ensures that end of lines are shown with a negation sign &lt;tt class="docutils literal"&gt;¬&lt;/tt&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;listchars&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;eol&lt;/span&gt;:¬&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;tab&lt;/span&gt;:▷\ &lt;span class="p"&gt;,&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A classic &amp;#8220;Python tell&amp;#8221; in Vim is the 79th or 80th character&amp;nbsp;highlight:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;colorcolumn&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;80&lt;/span&gt;              &lt;span class="c"&gt;&amp;quot; Show the 80th char column.&lt;/span&gt;
&lt;span class="nb"&gt;highlight&lt;/span&gt; ColorColumn ctermbg&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="fzf"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;FZF&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;My greatest recent revelation has been the integration of &lt;a class="reference external" href="https://github.com/junegunn/fzf"&gt;&lt;span class="caps"&gt;FZF&lt;/span&gt;&lt;/a&gt; to provide &amp;#8220;quick&amp;#8221; fuzzy searching. Most
frequently I search for files in the current git repository, open buffers and&amp;nbsp;tags.&lt;/p&gt;
&lt;p&gt;Install &lt;span class="caps"&gt;FZF&lt;/span&gt; and get it working on your machine, then add it to your Vim
setup using &lt;a class="reference external" href="https://github.com/junegunn/fzf.vim"&gt;fzf.vim&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Plug &lt;span class="s1"&gt;&amp;#39;junegunn/fzf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; { &lt;span class="s1"&gt;&amp;#39;dir&amp;#39;&lt;/span&gt;: &lt;span class="s1"&gt;&amp;#39;~/.fzf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;do&amp;#39;&lt;/span&gt;: &lt;span class="s1"&gt;&amp;#39;./install --all&amp;#39;&lt;/span&gt; }
Plug &lt;span class="s1"&gt;&amp;#39;junegunn/fzf.vim&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I&amp;#8217;ve mapped my most common &lt;span class="caps"&gt;FZF&lt;/span&gt; searches to &lt;a class="reference external" href="http://stevelosh.com/blog/2010/09/coming-home-to-vim/#using-the-leader"&gt;leader commands&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;imap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;x&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;o&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;plug&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;fzf&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;line&lt;span class="p"&gt;)&lt;/span&gt;
map &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; :Buffers&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;cr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
map &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;f&lt;/span&gt; :Files&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;cr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
map &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;g&lt;/span&gt; :GFiles&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;cr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
map &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;t&lt;/span&gt; :Tags&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;cr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Keeping &lt;span class="caps"&gt;FZF&lt;/span&gt;&amp;#8217;s line completion on &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&lt;span class="caps"&gt;CTRL&lt;/span&gt;-x&lt;/span&gt; &lt;span class="pre"&gt;&lt;span class="caps"&gt;CTRL&lt;/span&gt;-o&lt;/span&gt;&lt;/tt&gt; means that I can keep
access to Vim&amp;#8217;s line completion which is bound to &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&lt;span class="caps"&gt;CTRL&lt;/span&gt;-x&lt;/span&gt; &lt;span class="pre"&gt;&lt;span class="caps"&gt;CTRL&lt;/span&gt;-l&lt;/span&gt;&lt;/tt&gt; by&amp;nbsp;default.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/ggreer/the_silver_searcher"&gt;Ag&lt;/a&gt; results integration
with &lt;span class="caps"&gt;FZF&lt;/span&gt; is next on my list, I&amp;#8217;m still using &lt;tt class="docutils literal"&gt;Ag&lt;/tt&gt; results on the command&amp;nbsp;line.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="ctags"&gt;
&lt;h2&gt;Ctags&lt;/h2&gt;
&lt;p&gt;I was definitely slow to get on the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Ctags"&gt;Ctags&lt;/a&gt;
bandwagon, only adding them to my workflow in the last couple of months, but
along with &lt;span class="caps"&gt;FZF&lt;/span&gt;, they have been a revelation. I&amp;#8217;ve been using &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Ctags"&gt;Exhuberant Ctags&lt;/a&gt; as my index&amp;nbsp;generator.&lt;/p&gt;
&lt;p&gt;TPope has published a neat trick of stashing the &lt;tt class="docutils literal"&gt;ctags&lt;/tt&gt; script inside the
&lt;tt class="docutils literal"&gt;.git&lt;/tt&gt; folder, outlined in &lt;a class="reference external" href="https://tbaggery.com/2011/08/08/effortless-ctags-with-git.html"&gt;his blog post here&lt;/a&gt;. My version
of the script is inside my &lt;a class="reference external" href="https://github.com/jamescooke/dotfiles/blob/master/store/.git_template/hooks/ctags.sh"&gt;git hooks configuration&lt;/a&gt;
and works in combination with my &lt;a class="reference external" href="https://github.com/jamescooke/dotfiles/blob/master/store/.ctags"&gt;ctags config&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As mentioned above, I have used &lt;tt class="docutils literal"&gt;&amp;lt;leader&amp;gt;t&lt;/tt&gt; to trigger an &lt;span class="caps"&gt;FZF&lt;/span&gt;-powered search
of&amp;nbsp;tags:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;map &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;t&lt;/span&gt; :Tags&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;cr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The default &amp;#8220;jump to definition under cursor&amp;#8221; is still the default &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&lt;span class="caps"&gt;CTRL&lt;/span&gt;-]&lt;/span&gt;&lt;/tt&gt;
which, with &amp;#8220;previous tag&amp;#8221; &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&lt;span class="caps"&gt;CTRL&lt;/span&gt;-t&lt;/span&gt;&lt;/tt&gt; makes it really easy to traverse&amp;nbsp;code.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="visual-selection"&gt;
&lt;h2&gt;Visual&amp;nbsp;selection&lt;/h2&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://github.com/gorkunov/smartpairs.vim"&gt;smartpairs plugin&lt;/a&gt; is
fantastic for selecting text inside brackets, braces and parentheses and is
excellent for all languages I work with, not just&amp;nbsp;Python:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Plug &lt;span class="s1"&gt;&amp;#39;gorkunov/smartpairs.vim&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="linting"&gt;
&lt;h2&gt;Linting&lt;/h2&gt;
&lt;p&gt;In general, I&amp;#8217;ve used external programs to provide linting of my Python code
and so I run Vim with the current project&amp;#8217;s virtualenv&amp;nbsp;active.&lt;/p&gt;
&lt;p&gt;With &lt;a class="reference external" href="https://pypi.python.org/pypi/isort"&gt;Isort&lt;/a&gt; installed in the current
environment, sort the imports of the current file with &lt;tt class="docutils literal"&gt;&amp;lt;leader&amp;gt;i&lt;/tt&gt; or call it
with &lt;tt class="docutils literal"&gt;:Isort&lt;/tt&gt; command on a range of&amp;nbsp;lines:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;map &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;i&lt;/span&gt; :Isort&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;cr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
command&lt;span class="p"&gt;!&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;range&lt;span class="p"&gt;=&lt;/span&gt;% Isort :&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;line1&lt;span class="p"&gt;&amp;gt;,&amp;lt;&lt;/span&gt;line2&lt;span class="p"&gt;&amp;gt;!&lt;/span&gt; isort &lt;span class="p"&gt;-&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With &lt;a class="reference external" href="https://pypi.python.org/pypi/flake8/"&gt;flake8&lt;/a&gt; installed in the current
environment, lint the current file with &lt;tt class="docutils literal"&gt;F7&lt;/tt&gt; as provided by &lt;a class="reference external" href="https://github.com/nvie/vim-flake8"&gt;Vincent
Driessen&amp;#8217;s vim-flake8&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Plug &lt;span class="s1"&gt;&amp;#39;nvie/vim-flake8&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Happy&amp;nbsp;Vimming!&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;:xa&lt;/tt&gt;&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="topic:vim"></category></entry><entry><title>A successful pip-tools workflow for managing Python package requirements</title><link href="https://jamescooke.info/a-successful-pip-tools-workflow-for-managing-python-package-requirements.html" rel="alternate"></link><published>2016-11-13T23:00:00+00:00</published><updated>2016-11-13T23:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2016-11-13:/a-successful-pip-tools-workflow-for-managing-python-package-requirements.html</id><summary type="html">&lt;p class="first last"&gt;Using pip-tools with multiple requirements files can be difficult.
This post describes my current workflow that manages the complexity with a
&lt;tt class="docutils literal"&gt;Makefile&lt;/tt&gt;.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In this post I present the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-tools&lt;/span&gt;&lt;/tt&gt; workflow I&amp;#8217;ve been using over a number
of projects to manage multiple inherited requirements files. At its core is a
&lt;a class="reference external" href="https://www.gnu.org/software/make/manual/make.html"&gt;&lt;span class="caps"&gt;GNU&lt;/span&gt; Make&lt;/a&gt; Makefile to
provide recipes for managing requirements and specifying the dependencies
between the requirements&amp;nbsp;files.&lt;/p&gt;
&lt;p&gt;If you are not aware of the excellent &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-tools&lt;/span&gt;&lt;/tt&gt; &lt;a class="reference external" href="https://github.com/jazzband/pip-tools"&gt;package&lt;/a&gt; it provides two commands:
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-compile&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-sync&lt;/span&gt;&lt;/tt&gt;. In this post I will be focusing on using
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-compile&lt;/span&gt;&lt;/tt&gt; to compile &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; files consisting of top level&amp;nbsp;requirements.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-compile&lt;/span&gt;&lt;/tt&gt; consults the PyPI index for each top level package required,
looking up the package versions available, outputting a specific list of pinned
packages in a &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; file. This extra layer of abstraction (&lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; files
containing top level requirements rather than just outputting &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files
with &lt;tt class="docutils literal"&gt;pip freeze&lt;/tt&gt;) is very helpful for managing requirements, but does create
some complications which mean that a solid workflow is essential for stable
package&amp;nbsp;management.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Update&amp;nbsp;13/10/2019&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This post has been updated to use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-c&lt;/span&gt;&lt;/tt&gt; constraint files rather than
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-r&lt;/span&gt;&lt;/tt&gt; recursive inclusion. More info at the end of the&amp;nbsp;post.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="section" id="keep-requirements-files-in-their-own-folder"&gt;
&lt;h2&gt;Keep requirements files in their own&amp;nbsp;folder&lt;/h2&gt;
&lt;p&gt;In order to preserve sanity, I keep my project requirements in their own folder
directly inside the&amp;nbsp;project.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;project
$&lt;span class="w"&gt; &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;requirements/
base.in&lt;span class="w"&gt;  &lt;/span&gt;base.txt&lt;span class="w"&gt;  &lt;/span&gt;Makefile&lt;span class="w"&gt;  &lt;/span&gt;test.in&lt;span class="w"&gt;  &lt;/span&gt;test.txt
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;During this post, I&amp;#8217;ll use this simple example with one set of &amp;#8220;base&amp;#8221;
requirements and one set of &amp;#8220;test&amp;#8221;&amp;nbsp;requirements.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="store-in-and-txt-files-in-version-control"&gt;
&lt;h2&gt;Store &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files in version&amp;nbsp;control&lt;/h2&gt;
&lt;p&gt;Both &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files are tracked in the project&amp;#8217;s revision control
system, for example &lt;tt class="docutils literal"&gt;git&lt;/tt&gt;. This allows for shipping of the compiled &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt;
files for installation, but more importantly, it presents the opportunity to
check the diff of &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files when upgrading&amp;nbsp;packages.&lt;/p&gt;
&lt;p&gt;I also tend to keep &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; files sorted&amp;nbsp;alphabetically.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="set-in-files-to-depend-on-txt-files"&gt;
&lt;h2&gt;Set &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; files to depend on &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files&lt;/h2&gt;
&lt;p&gt;In the example project there are &lt;tt class="docutils literal"&gt;base.in&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;test.in&lt;/tt&gt; requirements&amp;nbsp;files:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;base.in&lt;/tt&gt; compiles to &lt;tt class="docutils literal"&gt;base.txt&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;test.in&lt;/tt&gt; compiles to &lt;tt class="docutils literal"&gt;test.txt&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I want the test requirements to be compiled to respect the versions of the base
packages so that they can be installed without disrupting those selected
versions. Therefore I set &lt;tt class="docutils literal"&gt;test.in&lt;/tt&gt; to be constrained by the &lt;tt class="docutils literal"&gt;base.txt&lt;/tt&gt;
compiled requirements with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-c&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;base.in&lt;/tt&gt; contains:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
project-packages
&lt;/pre&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;test.in&lt;/tt&gt; contains:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
-c base.txt

test-packages
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="use-a-makefile-for-common-tasks"&gt;
&lt;h2&gt;Use a Makefile for common&amp;nbsp;tasks&lt;/h2&gt;
&lt;p&gt;On each project that has multiple requirements files, I use a Makefile and
place it in the requirements&amp;nbsp;folder.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nf"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="n"&gt;clean&lt;/span&gt;

&lt;span class="nv"&gt;objects&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;wildcard&lt;span class="w"&gt; &lt;/span&gt;*.in&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;outputs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;objects:.in&lt;span class="o"&gt;=&lt;/span&gt;.txt&lt;span class="k"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;outputs&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;%.txt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;%.&lt;span class="n"&gt;in&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;pip-compile&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;--output-file&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$&amp;lt;

&lt;span class="nf"&gt;test.txt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;.&lt;span class="n"&gt;txt&lt;/span&gt;

&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@which&lt;span class="w"&gt; &lt;/span&gt;pip-compile&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/dev/null

&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;*.txt
&lt;/pre&gt;&lt;/div&gt;
&lt;!-- ** --&gt;
&lt;p&gt;Here is [a similar] file &lt;a class="reference external" href="https://github.com/jamescooke/flake8-aaa/blob/master/requirements/Makefile"&gt;in a current project&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note 1: This is an updated version of the Makefile that I have been using.
There are no &lt;tt class="docutils literal"&gt;clean&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;check&lt;/tt&gt; recipes.&lt;/p&gt;
&lt;p&gt;Note 2: &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; requires recipes to be indented by tabs, so if you
want to copy this file then it could be helpful to pull the &lt;a class="reference external" href="https://raw.githubusercontent.com/jamescooke/flake8-aaa/master/requirements/Makefile"&gt;raw file from
Github&lt;/a&gt;
rather than copying and pasting from this page which does not show tab&amp;nbsp;characters.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;#8217;s go over the key functionality provided by this&amp;nbsp;Makefile:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;First two&amp;nbsp;definitions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;objects&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;wildcard&lt;span class="w"&gt; &lt;/span&gt;*.in&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;!-- ** --&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;objects&lt;/tt&gt; is a list containing every &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; file in requirements&amp;nbsp;folder.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;outputs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;objects:.in&lt;span class="o"&gt;=&lt;/span&gt;.txt&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;outputs&lt;/tt&gt; is also a list made of one &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; filename for each &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; file
in the &lt;tt class="docutils literal"&gt;outputs&lt;/tt&gt; list. The &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files do not need to exist yet, this
list tells &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; what they should be&amp;nbsp;called.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;A recipe called &lt;tt class="docutils literal"&gt;all&lt;/tt&gt; to build all &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;outputs&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;all&lt;/tt&gt; recipe has no commands of its own - it solely depends on all the
&lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files in the &lt;tt class="docutils literal"&gt;outputs&lt;/tt&gt; list being built. In order to fulfil this
recipe, &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; will attempt to build  every &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; file in the &lt;tt class="docutils literal"&gt;objects&lt;/tt&gt;
list.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Up until now, &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; does not know how to build a &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; file, so here we
give it a&amp;nbsp;recipe:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nf"&gt;%.txt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;%.&lt;span class="n"&gt;in&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;pip-compile&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;--output-file&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$&amp;lt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first line tells &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; that any &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; file depends on the &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt;
file with the same name. &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; will check the date stamp on the two files
and compare them - if the &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; file is older than the &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; file or does
not exist, then &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; will build&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;The next line tells &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; the command to use to perform the build - it is
the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-compile&lt;/span&gt;&lt;/tt&gt; command with the following&amp;nbsp;flags:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-v&lt;/span&gt;&lt;/tt&gt; means &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-compile&lt;/span&gt;&lt;/tt&gt; will give verbose output. I find this helpful
for general watchfulness, but you may prefer to remove&amp;nbsp;it.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;output-file&lt;/span&gt; $&amp;#64;&lt;/tt&gt; means &amp;#8220;send the output to the target of the recipe&amp;#8221;, which
is the &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; file we&amp;#8217;ve asked to be made. For example when invoking &lt;tt class="docutils literal"&gt;make
base.txt&lt;/tt&gt;, then &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--output-file&lt;/span&gt; base.txt&lt;/tt&gt; will be&amp;nbsp;passed.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;$&amp;lt;&lt;/tt&gt; at the end is the corresponding &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; input file. Make matches the
names using the &lt;tt class="docutils literal"&gt;%&lt;/tt&gt; sign in the recipe, so it knows to build &lt;tt class="docutils literal"&gt;base.txt&lt;/tt&gt;
from &lt;tt class="docutils literal"&gt;base.in&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Now we tell &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; about the dependency between the requirements&amp;nbsp;files.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nf"&gt;test.txt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;.&lt;span class="n"&gt;txt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This creates a dependency chain. This is an additional recipe for
&lt;tt class="docutils literal"&gt;test.txt&lt;/tt&gt; which tells &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; that it depends on &lt;tt class="docutils literal"&gt;base.txt&lt;/tt&gt;. That means
that if &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; is asked to build &lt;tt class="docutils literal"&gt;test.txt&lt;/tt&gt;, then it should be updated if
&lt;tt class="docutils literal"&gt;test.in&lt;/tt&gt; &lt;em&gt;or&lt;/em&gt; &lt;tt class="docutils literal"&gt;base.txt&lt;/tt&gt; have been&amp;nbsp;updated.&lt;/p&gt;
&lt;p&gt;If &lt;tt class="docutils literal"&gt;base.in&lt;/tt&gt; is updated, then &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; knows that it will need to recompile
&lt;tt class="docutils literal"&gt;base.txt&lt;/tt&gt; in order to make &lt;tt class="docutils literal"&gt;test.txt&lt;/tt&gt;. We can see that&amp;nbsp;here:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;touch&lt;span class="w"&gt; &lt;/span&gt;base.in&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;# Update timestamp on base.in&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;test.txt&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# What commands will be run to build test.txt&lt;/span&gt;
pip-compile&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;--output-file&lt;span class="w"&gt; &lt;/span&gt;base.txt&lt;span class="w"&gt; &lt;/span&gt;base.in
pip-compile&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;--output-file&lt;span class="w"&gt; &lt;/span&gt;test.txt&lt;span class="w"&gt; &lt;/span&gt;test.in
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is exactly what we want for requirements constraining. If the
requirements in our base have changed, then we want our test file to be
recompiled too because of the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-c&lt;/span&gt; base.txt&lt;/tt&gt; line we added to the
&lt;tt class="docutils literal"&gt;test.in&lt;/tt&gt; file.&lt;/p&gt;
&lt;p&gt;Of course, this is a trivial example, but I have used
multiple lines of dependency in Makefiles to manage multiple levels of
inheritance in requirements&amp;nbsp;files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Finally, a recipe to help us update&amp;nbsp;requirements.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;@which&lt;span class="w"&gt; &lt;/span&gt;pip-compile&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/dev/null

&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;check&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;*.txt
&lt;/pre&gt;&lt;/div&gt;
&lt;!-- ** --&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;check&lt;/tt&gt; recipe will fail if &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-tools&lt;/span&gt;&lt;/tt&gt; is not&amp;nbsp;installed.&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;clean&lt;/tt&gt; recipe will remove all the &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files if the &lt;tt class="docutils literal"&gt;check&lt;/tt&gt;
recipe is successful. This makes it harder to accidentally delete your
requirements files without &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-tools&lt;/span&gt;&lt;/tt&gt; already installed to be able to
build them&amp;nbsp;again.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;#8217;ve explained what the Makefile above does, but not how or when you would use
it. So let&amp;#8217;s continue with some common workflow&amp;nbsp;actions.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="build-one-or-more-requirements-files"&gt;
&lt;h2&gt;Build one or more requirements&amp;nbsp;files&lt;/h2&gt;
&lt;p&gt;To update all requirements use the default &lt;tt class="docutils literal"&gt;all&lt;/tt&gt; recipe.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;all
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To update a particular file, ask for it by&amp;nbsp;name:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;test.txt
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If &lt;tt class="docutils literal"&gt;make&lt;/tt&gt; tells you that a file is up-to-date but you want to force it to
be rebuilt you should &lt;tt class="docutils literal"&gt;touch&lt;/tt&gt; the &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;base.txt
make:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;base.txt&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;up&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;date.
$&lt;span class="w"&gt; &lt;/span&gt;touch&lt;span class="w"&gt; &lt;/span&gt;base.in
$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;base.txt
pip-compile&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;--output-file&lt;span class="w"&gt; &lt;/span&gt;base.txt&lt;span class="w"&gt; &lt;/span&gt;base.in
...
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="add-a-dependency"&gt;
&lt;h2&gt;Add a&amp;nbsp;dependency&lt;/h2&gt;
&lt;p&gt;To add a dependency, locate the appropriate &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; file and add the new
package name there. The version number is only required if a particular version
of the library is required. The latest version will be chosen by default when&amp;nbsp;compiling.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;base.in
ipython
$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;all
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="update-a-package"&gt;
&lt;h2&gt;Update a&amp;nbsp;package&lt;/h2&gt;
&lt;p&gt;In order to update a single top level package version, remove its lines from
the compiled corresponding &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files. I tend to be quite &amp;#8220;aggressive&amp;#8221; with
this and remove every package that the top level package depended on using
&lt;tt class="docutils literal"&gt;sed&lt;/tt&gt; with a pattern&amp;nbsp;match.&lt;/p&gt;
&lt;p&gt;Given that I want to update &lt;tt class="docutils literal"&gt;ipython&lt;/tt&gt; and it is not pinned in my &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt;
file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/ipython/d&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;*.txt
$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;all
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There is no command for this removal built into the Makefile, but potentially
it could be. Ideally, it could be provided as extra functionality by
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-tools&lt;/span&gt;&lt;/tt&gt;. Beware that packages often contain each other&amp;#8217;s names as
substrings so could lead to bad matching. If in doubt review your diff and
potentially remove lines from your &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files&amp;nbsp;manually.&lt;/p&gt;
&lt;p&gt;The call to &lt;tt class="docutils literal"&gt;make all&lt;/tt&gt; will reevaluate the latest version for packages that
do not have corresponding lines in the &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; file and they will be updated
as&amp;nbsp;required.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="update-all-requirements"&gt;
&lt;h2&gt;Update all&amp;nbsp;requirements&lt;/h2&gt;
&lt;p&gt;A full update of all requirements to the latest version (including updating all
packages that are not pinned in the &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; file with a particular version
number) can be achieved&amp;nbsp;with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;clean&lt;span class="w"&gt; &lt;/span&gt;all
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;clean&lt;/tt&gt; recipe will clean out all &lt;tt class="docutils literal"&gt;*.txt&lt;/tt&gt; files if you have
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-tools&lt;/span&gt;&lt;/tt&gt; installed. Then the &lt;tt class="docutils literal"&gt;all&lt;/tt&gt; recipe will rebuild them all in
dependency&amp;nbsp;order.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="finally"&gt;
&lt;h2&gt;Finally&lt;/h2&gt;
&lt;p&gt;A tip for working with Makefiles. If you want to see what commands will be run
by a recipe, you can use the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-n&lt;/span&gt;&lt;/tt&gt; flag and inspect the commands that were&amp;nbsp;planned:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;all
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Happy requirements&amp;nbsp;packing!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="update-21-11-2016"&gt;
&lt;h2&gt;Update&amp;nbsp;21/11/2016&lt;/h2&gt;
&lt;p&gt;For more information on the advantages and disadvantages of setting recursive
requirements to point at &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; files or &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files please see &lt;a class="reference external" href="https://github.com/nvie/pip-tools/issues/398"&gt;this Issue&lt;/a&gt; on the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-tools&lt;/span&gt;&lt;/tt&gt;
repository.&lt;/p&gt;
&lt;p&gt;In particular, &lt;a class="reference external" href="https://github.com/nvie/pip-tools/issues/398#issuecomment-261313647"&gt;my comment&lt;/a&gt;
illustrates how development requirements can become out of sync with base
requirements when &lt;tt class="docutils literal"&gt;.in&lt;/tt&gt; files are used in recursion which does not happen
when &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files are used. It&amp;#8217;s for this reason, that I continue to
recommend pointing at &lt;tt class="docutils literal"&gt;.txt&lt;/tt&gt; files with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-r&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="update-30-06-2017"&gt;
&lt;h2&gt;Update&amp;nbsp;30/06/2017&lt;/h2&gt;
&lt;p&gt;See also &lt;a class="reference external" href="https://github.com/jamescooke/blog/issues/9"&gt;this comment on GitHub&lt;/a&gt; from Devin Fee for a
&lt;tt class="docutils literal"&gt;Makefile&lt;/tt&gt; which:&lt;/p&gt;
&lt;blockquote&gt;
&amp;#8230; corrects the annoyance &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-e&lt;/span&gt; &lt;span class="pre"&gt;file:///Users/dfee/code/zebra&lt;/span&gt; &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;-e&lt;/span&gt; .&lt;/tt&gt;,
making the file useful for users who don&amp;#8217;t develop / deploy from your
directory.&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="update-13-10-2019"&gt;
&lt;h2&gt;Update&amp;nbsp;13/10/2019&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/jazzband/pip-tools/pull/905"&gt;This pull request&lt;/a&gt; updated
the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-tools&lt;/span&gt;&lt;/tt&gt; &lt;span class="caps"&gt;README&lt;/span&gt; to include &lt;a class="reference external" href="https://github.com/jazzband/pip-tools#workflow-for-layered-requirements"&gt;info on creating layered requirements
files&lt;/a&gt;
using &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-c&lt;/span&gt;&lt;/tt&gt; constraints. The use of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-c&lt;/span&gt;&lt;/tt&gt; came up in &lt;a class="reference external" href="https://github.com/jazzband/pip-tools/issues/398"&gt;this long running issue&lt;/a&gt; discussing layered
requirements and how to ensure that each layer is compatible with the&amp;nbsp;others.&lt;/p&gt;
&lt;p&gt;I think that &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-c&lt;/span&gt;&lt;/tt&gt; constraints are much better than &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-r&lt;/span&gt;&lt;/tt&gt; inclusion and have
updated the post to reflect&amp;nbsp;that.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="language:python"></category></entry><entry><title>Django Factory Audit</title><link href="https://jamescooke.info/django-factory-audit.html" rel="alternate"></link><published>2016-10-12T10:00:00+01:00</published><updated>2016-10-12T10:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2016-10-12:/django-factory-audit.html</id><summary type="html">&lt;p class="first last"&gt;Exploring the various model instance factories available for Django.
Each factory is tested based on the quality of the instances it
creates. Bonus: talk version and slides also&amp;nbsp;available.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Factories build instances of models, primarily for testing, but can be used
anywhere. In this post I present my attempts at grading different factory
libraries available for Django based on the validity of the instances that they&amp;nbsp;create.&lt;/p&gt;
&lt;div class="section" id="the-problem"&gt;
&lt;h2&gt;The&amp;nbsp;Problem&lt;/h2&gt;
&lt;p&gt;You&amp;#8217;re a much better programmer than me - I&amp;#8217;m a bad programmer. I make
mistakes, lots of them. I often forget to validate my instances before saving.
In order for my tests to be more dependable and solid, I&amp;#8217;d like whatever object
factory I use to look after me by validating the generated instance before it
reaches the&amp;nbsp;database.&lt;/p&gt;
&lt;p&gt;This is the way that I&amp;#8217;ve been looking at Django model instances - for a single
model we can imagine sets of instances which might look&amp;nbsp;like:&lt;/p&gt;
&lt;img alt="Venn diagram showing sets of Django model instances grouped by validity." src="https://jamescooke.info/images/venn.png" style="width: 500px;" /&gt;
&lt;p&gt;Where:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;ε&lt;/tt&gt;: The universal set of all possible instances of this&amp;nbsp;model.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;D&lt;/tt&gt;: The set of instances that the database would consider valid. It&amp;#8217;s
interesting to note that this set might change as code is developed, tested
and run on machines that have different database versions or use different
databases&amp;nbsp;altogether.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;V&lt;/tt&gt;: The set of valid instances which pass &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt;. This is a subset
of &lt;tt class="docutils literal"&gt;D&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main issue is the set &lt;tt class="docutils literal"&gt;D/V&lt;/tt&gt; of instances. These are all the instances
which can be saved into the database but are considered invalid by&amp;nbsp;Django.&lt;/p&gt;
&lt;p&gt;When factories create instances that reside in this &lt;tt class="docutils literal"&gt;D/V&lt;/tt&gt; set, they create
instability in the&amp;nbsp;system:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;If a user attempts to edit that instance through the Django Admin system,
then they may not be able to save their changes without fixing a list of
invalid&amp;nbsp;fields.&lt;/li&gt;
&lt;li&gt;Test suites will be executed using model instances that &lt;em&gt;should&lt;/em&gt; not be
created during the &amp;#8220;normal&amp;#8221; lifetime of the&amp;nbsp;application.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="possible-solutions"&gt;
&lt;h2&gt;Possible&amp;nbsp;solutions&lt;/h2&gt;
&lt;p&gt;We could argue that way in which Django does not call &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; before it
writes instances to the database is the root of the problem - I&amp;#8217;ve previously
&lt;a class="reference external" href="https://jamescooke.info/djangos-model-save-vs-full_clean.html"&gt;written and spoken about this&lt;/a&gt;. However, this is more a
&amp;#8220;condition of the environment&amp;#8221; and therefore something that we need to manage,
rather than&amp;nbsp;fix.&lt;/p&gt;
&lt;p&gt;Also, look at it another way. Any factory that integrates with Django can
inspect the target model and immediately find the constraints on each field.
Therefore with Django, factory libraries have all the information they need to
build a strategy for creating valid data. On top of that, Django provides the
&lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; function so any generated data can also be checked for validity
before it&amp;#8217;s sent to the database. Why should we have to re-code the constraints
already created for our models into our factories? This looks like duplication
of&amp;nbsp;work.&lt;/p&gt;
&lt;p&gt;So let&amp;#8217;s explore how different factory libraries deal with this problem of
instances in the &lt;tt class="docutils literal"&gt;D/V&lt;/tt&gt; set - the white of the fried egg in the&amp;nbsp;diagram.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="factory-libraries"&gt;
&lt;h2&gt;Factory&amp;nbsp;libraries&lt;/h2&gt;
&lt;p&gt;The following factory libraries have been&amp;nbsp;explored:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/fcurella/django-fakery"&gt;Django&amp;nbsp;Fakery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/FactoryBoy/factory_boy"&gt;Factory Boy&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/jamescooke/factory_djoy"&gt;Factory&amp;nbsp;Djoy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://hypothesis.readthedocs.io/en/latest/django.html"&gt;Hypothesis[django]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/klen/mixer"&gt;Mixer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/vandersonmota/model_mommy"&gt;Model&amp;nbsp;Mommy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Factory Djoy is my factory library. It&amp;#8217;s a thin wrapper around Factory Boy
which does the hard work. I&amp;#8217;ve indented it because it&amp;#8217;s really a version of
Factory Boy than a standalone factory&amp;nbsp;library.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="test-conditions"&gt;
&lt;h2&gt;Test&amp;nbsp;conditions&lt;/h2&gt;
&lt;p&gt;The code used to test the factory libraries is available in the &lt;a class="reference external" href="https://github.com/jamescooke/factory_audit"&gt;Factory Audit
repository&lt;/a&gt;. For each factory
library, two factories have been created in a default state, one targeting each
of the test&amp;nbsp;Models:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;ItemFactory&lt;/tt&gt;: to create and save instances of &lt;tt class="docutils literal"&gt;plant.models.Item&lt;/tt&gt;, a
test model defined &lt;a class="reference external" href="https://github.com/jamescooke/factory_audit/blob/master/factory_audit/plant/models.py"&gt;in the &amp;#8216;plant&amp;#8217; app&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Single Item with one required field &amp;#39;name&amp;#39;&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&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;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This example has been taken from the &lt;a class="reference external" href="https://github.com/jamescooke/factory_audit"&gt;Factory_Djoy &lt;span class="caps"&gt;README&lt;/span&gt;&lt;/a&gt; but with a reduced length of
name down to one character to more easily force name&amp;nbsp;collisions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;UserFactory&lt;/tt&gt;: to create and save instances of the default
&lt;tt class="docutils literal"&gt;django.contrib.auth&lt;/tt&gt; User&amp;nbsp;Model.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is that each factory should reliably generate 10 valid instances of
each&amp;nbsp;model.&lt;/p&gt;
&lt;p&gt;Wherever possible I&amp;#8217;ve tried to be as explicit as possible and import the
target model, rather than refer to it by name as some factories&amp;nbsp;allow.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="gradings"&gt;
&lt;h2&gt;Gradings&lt;/h2&gt;
&lt;p&gt;Each factory library has been graded based on how its default configuration
behaves when used with the &lt;tt class="docutils literal"&gt;Item&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;User&lt;/tt&gt; models.&lt;/p&gt;
&lt;p&gt;The gradings are based on the definition of &amp;#8220;valid&amp;#8221;. Valid instances are ones
which will pass Django&amp;#8217;s &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; and not raise a &lt;tt class="docutils literal"&gt;ValidationError&lt;/tt&gt;.
For example, using the &lt;tt class="docutils literal"&gt;ItemFactory&lt;/tt&gt; a generated item passes validation&amp;nbsp;with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ItemFactory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full_clean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The gradings&amp;nbsp;are:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt; - Factory creates &lt;strong&gt;invalid&lt;/strong&gt; instances of the model and
saves them to the database. These are instances in the &lt;tt class="docutils literal"&gt;D/V&lt;/tt&gt; set.&lt;/li&gt;
&lt;li&gt;&lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt; - Factory raises an exception and does not save any
invalid instances. Preferably this would be a &lt;tt class="docutils literal"&gt;ValidationError&lt;/tt&gt;, but I&amp;#8217;ve
also allowed &lt;tt class="docutils literal"&gt;IntegrityError&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;RunTimeError&lt;/tt&gt; here.&lt;/li&gt;
&lt;li&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt; - Factory creates multiple &lt;strong&gt;valid&lt;/strong&gt; instances with no
invalid instances created or skipped. Running factory &lt;tt class="docutils literal"&gt;n&lt;/tt&gt; times generates
&lt;tt class="docutils literal"&gt;n&lt;/tt&gt; valid&amp;nbsp;instances.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tests on each of the factories have been written to pass when the factory
behaves to the expected grade. For example, the test on Factory Djoy&amp;#8217;s
&lt;tt class="docutils literal"&gt;ItemFactory&lt;/tt&gt; expect that it raises &lt;tt class="docutils literal"&gt;ValidationError&lt;/tt&gt; each time it&amp;#8217;s used
and &lt;a class="reference external" href="https://github.com/jamescooke/factory_audit/blob/master/factory_audit/plant/tests/test_factory_djoy_factories.py#L12-L20"&gt;is therefore &lt;span class="caps"&gt;YELLOW&lt;/span&gt; grade&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="results"&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;div class="section" id="original-results"&gt;
&lt;h3&gt;Original&amp;nbsp;results&lt;/h3&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="33%" /&gt;
&lt;col width="33%" /&gt;
&lt;col width="33%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Library&lt;/th&gt;
&lt;th class="head"&gt;ItemFactory&lt;/th&gt;
&lt;th class="head"&gt;UserFactory&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Django Fakery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Factory Boy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Factory Djoy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Hypothesis[django]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Mixer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Model Mommy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="section" id="update"&gt;
&lt;h3&gt;Update&lt;/h3&gt;
&lt;p&gt;Thanks to Piotr and Adam who pointed out some issues with my grading&amp;nbsp;system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adam&lt;/strong&gt; pointed out that collisions are still collisions, even if they are
unlikely. Therefore, even if factories are employing fantastic strategies for
generating valid data, their generated instances should still be run through
&lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; before&amp;nbsp;save.&lt;/p&gt;
&lt;p&gt;I agree with this opinion and think that calling &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; on every
instance creates the opportunity for two benefits, over and above asserting
that every instance is&amp;nbsp;valid:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;If a factory raises &lt;tt class="docutils literal"&gt;ValidationError&lt;/tt&gt; with information on what failed it
will always be helpful to the developer who is fixing the broken test&amp;nbsp;run.&lt;/li&gt;
&lt;li&gt;If invalid data is found, this would create an opportunity for a factory to
adjust failing fields so that valid data can be saved and the test run will
not be&amp;nbsp;interrupted.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;#8217;ve added a &amp;#8220;Uses &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt;&amp;#8221; field to evaluate each factory and capture
this&amp;nbsp;information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Piotr&lt;/strong&gt; pointed out that the results of the grading are inconclusive since I
don&amp;#8217;t agree with the results. For example, in the original results Mixer is the
only library that has &lt;span class="caps"&gt;GREEN&lt;/span&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt; and therefore we would assume that it is the
best of the factories tested. However, that&amp;#8217;s not the case, since I found it
hard to use and its exception bubbling was also&amp;nbsp;intrusive.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve added the &amp;#8220;Ease of use&amp;#8221; grading to capture this information based on my
experience working with each&amp;nbsp;factory.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="new-gradings"&gt;
&lt;h3&gt;New&amp;nbsp;gradings&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Uses &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt;:&lt;ul&gt;
&lt;li&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt; - Not instance of &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; in the factory code&amp;nbsp;base.&lt;/li&gt;
&lt;li&gt;&lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt; - Factory code base includes &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; in the
test suite&amp;nbsp;only.&lt;/li&gt;
&lt;li&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt; - Factory tests every generated instance with
&lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ease of use:&lt;ul&gt;
&lt;li&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt; - Do not bother trying. Too difficult to&amp;nbsp;use.&lt;/li&gt;
&lt;li&gt;&lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt; - Some pain may be experienced. You might struggle to
install, need to adjust your workflow, packages,&amp;nbsp;etc.&lt;/li&gt;
&lt;li&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt; - Easy to install. Clean &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="updated-results"&gt;
&lt;h3&gt;Updated&amp;nbsp;results&lt;/h3&gt;
&lt;p&gt;Results with additional &amp;#8220;Uses &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; and &amp;#8220;Ease of use&amp;#8221;&amp;nbsp;information:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="20%" /&gt;
&lt;col width="20%" /&gt;
&lt;col width="20%" /&gt;
&lt;col width="19%" /&gt;
&lt;col width="20%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Library&lt;/th&gt;
&lt;th class="head"&gt;ItemFactory&lt;/th&gt;
&lt;th class="head"&gt;UserFactory&lt;/th&gt;
&lt;th class="head"&gt;Uses &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt;&lt;/th&gt;
&lt;th class="head"&gt;Ease of use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Django Fakery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Factory Boy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Factory Djoy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Hypothesis[django]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Mixer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Model Mommy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="notes-about-each-library"&gt;
&lt;h2&gt;Notes about each&amp;nbsp;library&lt;/h2&gt;
&lt;p&gt;Grading each library was often harder than I thought it would be because many
don&amp;#8217;t fall into one grading or another. Where that has happened I&amp;#8217;ve noted it&amp;nbsp;below.&lt;/p&gt;
&lt;div class="section" id="django-fakery-1"&gt;
&lt;h3&gt;Django&amp;nbsp;Fakery&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;ItemFactory&lt;/strong&gt; &lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Unfortunately, Django Fakery does not recognise that only one character is
allowed for the Item model&amp;#8217;s &lt;tt class="docutils literal"&gt;name&lt;/tt&gt; field. It uses Latin words from a
generator which are saved by the default SQLite database and are invalid
because they are too&amp;nbsp;long.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;UserFactory&lt;/strong&gt; &lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In order to create &lt;tt class="docutils literal"&gt;User&lt;/tt&gt; instances Django Fakery also uses the Latin
generator which collides often. This means that &lt;tt class="docutils literal"&gt;IntegrityError&lt;/tt&gt; is raised
when collisions occur, but any Users created are&amp;nbsp;valid.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="factory-boy-1"&gt;
&lt;h3&gt;Factory&amp;nbsp;Boy&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;ItemFactory&lt;/strong&gt; &lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Creates invalid instance of &lt;tt class="docutils literal"&gt;Item&lt;/tt&gt; which has no name and saves&amp;nbsp;it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;UserFactory&lt;/strong&gt; &lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Creates &lt;tt class="docutils literal"&gt;User&lt;/tt&gt; with invalid &lt;tt class="docutils literal"&gt;username&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;password&lt;/tt&gt; fields and
saves&amp;nbsp;it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Factory Boy has no automatic strategies used for default factories and so it
fails this test hard. If the library was extended to call &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; for
generated instances before saving then it could be upgraded to &lt;span class="caps"&gt;YELLOW&lt;/span&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="factory-djoy-1"&gt;
&lt;h3&gt;Factory&amp;nbsp;Djoy&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;ItemFactory&lt;/strong&gt; &lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Calls &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; on the &lt;tt class="docutils literal"&gt;Item&lt;/tt&gt; instance created by Factory Boy which it
wraps. This raises &lt;tt class="docutils literal"&gt;ValidationError&lt;/tt&gt; and the &lt;tt class="docutils literal"&gt;Item&lt;/tt&gt; is not&amp;nbsp;saved.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;UserFactory&lt;/strong&gt; &lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Creates valid instances using a &lt;a class="reference external" href="https://factory-djoy.readthedocs.io/en/latest/userfactory.html#unique-usernames"&gt;simple strategy&lt;/a&gt;
Unique &lt;tt class="docutils literal"&gt;usernames&lt;/tt&gt; are generated via Faker Factory which is already a
requirement of Factory Boy. &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; is called on the generated
instance to catch any collisions in the strategy and on collision, a new name
is generated and&amp;nbsp;retried.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Factory Djoy contains only one simple strategy for creating &lt;tt class="docutils literal"&gt;Users&lt;/tt&gt;. It has
no inspection ability to create strategies of its own based on&amp;nbsp;Models.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="hypothesis-django-1"&gt;
&lt;h3&gt;Hypothesis[django]&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;ItemFactory&lt;/strong&gt; &lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;UserFactory&lt;/strong&gt; &lt;img alt="red_circle" src="https://jamescooke.info/images/red_circle.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;RED&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Hypothesis&amp;#8217;s Django extra does not reliably create instances of either model
because it&amp;#8217;s &lt;tt class="docutils literal"&gt;example&lt;/tt&gt; function does &lt;a class="reference external" href="https://github.com/jamescooke/factory_audit/pull/4"&gt;not reliably generate valid data&lt;/a&gt;. In the case that an
invalid example is generated it is skipped and the previous example is&amp;nbsp;used.&lt;/p&gt;
&lt;p&gt;Interestingly, Hypothesis creates &lt;tt class="docutils literal"&gt;User&lt;/tt&gt; instances that Django considers to
have invalid email&amp;nbsp;addresses.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;Uses &amp;#8220;full_clean&amp;#8220;&lt;/strong&gt; &lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Hypothesis&amp;#8217;s code base currently includes a &lt;a class="reference external" href="https://github.com/HypothesisWorks/hypothesis-python/blob/f6230a6f72ea8c89543e8c56a44d0510fb662f5d/tests/django/toystore/test_given_models.py#L112"&gt;single instance&lt;/a&gt; of &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt;.
This is in its test suite to assert that instances built are valid. However,
it doesn&amp;#8217;t call &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; on generated instances during its normal&amp;nbsp;operation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="mixer-1"&gt;
&lt;h3&gt;Mixer&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;ItemFactory&lt;/strong&gt; &lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Mixer appears to inspect the &lt;tt class="docutils literal"&gt;Item&lt;/tt&gt; model and generates a very limited
strategy for generating names. Unfortunately it runs out of instances around
23, even though there are hundreds of characters&amp;nbsp;available.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;UserFactory&lt;/strong&gt; &lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;Ease of use&lt;/strong&gt; &lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Mixer helpfully raises &lt;tt class="docutils literal"&gt;Runtime&lt;/tt&gt; error if a strategy can&amp;#8217;t generate a valid
instance. However, it echoes this to the standard out, which is annoying and
really confused me when I was first using it because exceptions appear on the
terminal even though all tests have&amp;nbsp;passed.&lt;/p&gt;
&lt;p&gt;It uses an old version of Fake Factory which meant that its tests had to be
extracted into a second test run that occurs after a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pip-sync&lt;/span&gt;&lt;/tt&gt; has taken
place. I found this the hardest factory library to work&amp;nbsp;with.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="model-mommy-1"&gt;
&lt;h3&gt;Model&amp;nbsp;Mommy&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;ItemFactory&lt;/strong&gt; &lt;img alt="yellow_heart" src="https://jamescooke.info/images/yellow_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;YELLOW&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There is no method used to create unique values so there are collisions
when there are a small number of possible values. Items that are
created are&amp;nbsp;valid.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;strong&gt;UserFactory&lt;/strong&gt; &lt;img alt="green_heart" src="https://jamescooke.info/images/green_heart.png" style="width: 25px;" /&gt; &lt;span class="caps"&gt;GREEN&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Model Mommy&amp;#8217;s random text strategy works here for &lt;tt class="docutils literal"&gt;username&lt;/tt&gt; and the random
strings are unlikely to&amp;nbsp;collide.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Model Mommy depends on its strategies to create valid data and does not call
&lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; meaning that &lt;tt class="docutils literal"&gt;IntegrityError&lt;/tt&gt; can be raises when collisions
occur. It could be argued that it should be downgraded to &lt;span class="caps"&gt;YELLOW&lt;/span&gt; because
&lt;tt class="docutils literal"&gt;IntegrityError&lt;/tt&gt; is&amp;nbsp;raised.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="and-the-winner-is"&gt;
&lt;h2&gt;And the winner&amp;nbsp;is?&lt;/h2&gt;
&lt;p&gt;What is the best factory to use? This is a really hard&amp;nbsp;question.&lt;/p&gt;
&lt;p&gt;These factory libraries generally consist of two parts and different libraries
do each part&amp;nbsp;well.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Control / &lt;span class="caps"&gt;API&lt;/span&gt;: Personally I really like the Factory Boy &lt;span class="caps"&gt;API&lt;/span&gt; and how it
interfaces with Django. I&amp;#8217;m happy with the Factory Djoy library because it
provides the certainly of calling &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; for every created instance
on top of the Factory Boy &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Data strategy: I&amp;#8217;m excited by Hypothesis and its ability to generate test&amp;nbsp;data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My current advice is to use Factory Djoy, or wrap your favourite factory in a
call to &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Yes, there is a performance overhead to calling &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; but my opinion
is that eliminating the &lt;tt class="docutils literal"&gt;D/V&lt;/tt&gt; set of invalid instances is worth the effort
and makes the test suite &amp;#8220;fundamentally simpler&amp;#8221; &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My future thinking is that if Hypothesis can improve its interface to Django it
could be the&amp;nbsp;winner.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="resources"&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/jamescooke/factory_audit"&gt;Factory audit repository&lt;/a&gt;:
Contains the research work - factories and tests for each factory library.
Pull requests very welcome - especially if they add a new factory library or
fix a&amp;nbsp;test.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://speakerdeck.com/jamescooke/full-clean-factories"&gt;Slides&lt;/a&gt;: From my
presentation of these results at the London Django October&amp;nbsp;meetup.&lt;/li&gt;
&lt;li&gt;Video: Available via the &lt;a class="reference external" href="https://skillsmatter.com/skillscasts/9137-full-clean-factories"&gt;Skills Matter website&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Thanks to Adam for pointing out the collisions issue which you can hear in
the video around 20 minutes in. Even if collisions are unlikely, they can
still be a problem. &lt;a class="reference external" href="https://adamj.eu/tech/2014/09/03/factory-boy-fun/"&gt;Check out his Factory Boy post&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a class="reference external" href="#update"&gt;15th October update&lt;/a&gt; to the post is visible as a &lt;a class="reference external" href="https://github.com/jamescooke/blog/pull/4"&gt;Pull
Request on the blog&amp;#8217;s repository&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Happy&amp;nbsp;fabricating!&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;!--  --&gt;
&lt;blockquote&gt;
Taking a few percent hit, going a little slower, in order to do
something that&amp;#8217;s just fundamentally simpler&amp;#8221;&lt;/blockquote&gt;
&lt;p&gt;&lt;a class="reference external" href="https://youtu.be/zFMdhXYlFfY?t=20m1s"&gt;Pycon &lt;span class="caps"&gt;UK&lt;/span&gt; 2016: Python and the Glories of the &lt;span class="caps"&gt;UNIX&lt;/span&gt;&amp;nbsp;Tradition&lt;/a&gt;&lt;/p&gt;
&lt;p class="last"&gt;Brandon Rhodes, Pycon &lt;span class="caps"&gt;UK&lt;/span&gt;&amp;nbsp;2016&lt;/p&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="language:python"></category><category term="topic:testing"></category><category term="topic:django"></category></entry><entry><title>Cleaner unit testing with the Arrange Act Assert pattern</title><link href="https://jamescooke.info/cleaner-unit-testing-with-the-arrange-act-assert-pattern.html" rel="alternate"></link><published>2016-09-18T20:00:00+01:00</published><updated>2016-09-18T20:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2016-09-18:/cleaner-unit-testing-with-the-arrange-act-assert-pattern.html</id><summary type="html">&lt;p class="first last"&gt;My PyConUK 2016 talk about the &lt;span class="caps"&gt;AAA&lt;/span&gt; pattern for unit tests and how
using it can help us all make our tests cleaner, easier to read
and as Pythonic as&amp;nbsp;possible.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;At &lt;a class="reference external" href="https://2016.pyconuk.org/talks/cleaner-unit-testing-with-the-arrange-act-assert-pattern/"&gt;PyConUK 2016&lt;/a&gt;
I spoke about the Arrange Act Assert pattern and how it can help clean up unit&amp;nbsp;tests.&lt;/p&gt;
&lt;blockquote&gt;
&lt;strong&gt;Note:&lt;/strong&gt; A newer post &lt;a class="reference external" href="https://jamescooke.info/arrange-act-assert-pattern-for-python-developers.html"&gt;Arrange Act Assert pattern for Python developers&lt;/a&gt; is available. It has
much clearer examples and guidelines for using &lt;span class="caps"&gt;AAA&lt;/span&gt; than the video and
slides below.&lt;/blockquote&gt;
&lt;div class="section" id="original-proposal"&gt;
&lt;h2&gt;Original&amp;nbsp;proposal&lt;/h2&gt;
&lt;p&gt;PyConUK ask that we provide an explanation of why we think that attendees
will be interested in our talk. This was my original proposal&amp;#8217;s&amp;nbsp;reasoning.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This talk focuses on developers that practise &lt;span class="caps"&gt;TDD&lt;/span&gt;, or want to use it
more in their&amp;nbsp;coding.&lt;/p&gt;
&lt;p&gt;My assumption is that our community feels a lot of pain from testing.
I&amp;#8217;ve heard fellow developers talk about the difficulty with managing
complicated test suites; issues with reading and understanding others&amp;#8217;
tests; and struggles when updating others&amp;#8217; tests. I hope that the
PyConUK attendees will have felt some of this pain be interested in a
talk that demonstrates the use of a pattern that can (hopefully)
mitigate some of it and help us all to be &amp;#8220;cleaner&amp;#8221;&amp;nbsp;testers.&lt;/p&gt;
&lt;p&gt;Although I&amp;#8217;ve marked &amp;#8220;moderately experienced&amp;#8221; I think that my talk
would have a broad appeal: Those who are new to testing and would like
a &amp;#8220;template&amp;#8221; to follow. And those who are expert because of the
discussion about when to &lt;span class="caps"&gt;DRY&lt;/span&gt; out tests and how to assert that our test
refactors are&amp;nbsp;safe.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="slides-and-video"&gt;
&lt;h2&gt;Slides and&amp;nbsp;video&lt;/h2&gt;
&lt;p&gt;The video of the talk does not capture much of the screen, so the slides are
posted here&amp;nbsp;too.&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/GGw5T1mw9vU" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;script async class="speakerdeck-embed" data-id="d25e0e15acef4ccc8fe70abba5adce03" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;&lt;/div&gt;
&lt;div class="section" id="resources"&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0008/"&gt;&lt;span class="caps"&gt;PEP08&lt;/span&gt;&lt;/a&gt; and &lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0020/"&gt;&lt;span class="caps"&gt;PEP20&lt;/span&gt;, The Zen of Python&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://www.goodreads.com/book/show/387190.Test_Driven_Development"&gt;Kent Beck: Test Driven Development: By Example&lt;/a&gt; - a
great book which references the &lt;span class="caps"&gt;AAA&lt;/span&gt; pattern (page&amp;nbsp;97).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html"&gt;Google-style docstrings&lt;/a&gt;:
In addition to using this style in my &lt;span class="caps"&gt;AAA&lt;/span&gt; tests, I&amp;#8217;ve started to add a
&lt;tt class="docutils literal"&gt;Trusts&lt;/tt&gt; section to indicate which other tests are trusted by any
particular test and&amp;nbsp;why.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://xp123.com/articles/3a-arrange-act-assert/"&gt;Bill Wake&amp;#8217;s post about &lt;span class="caps"&gt;AAA&lt;/span&gt;&lt;/a&gt;: Bill Wake is cited by
Kent Beck as having coined the term &lt;tt class="docutils literal"&gt;3A&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://refactoring.com/catalog/extractMethod.html"&gt;Extract Method&lt;/a&gt;: I&amp;#8217;ve
used extract method as defined by Martin Fowler. See also &lt;a class="reference external" href="https://refactoring.com/catalog/extractVariable.html"&gt;Extract Variable&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update August 2018:&lt;/strong&gt; Check out &lt;a class="reference external" href="https://flake8-aaa.readthedocs.io/en/stable/"&gt;flake8-aaa&lt;/a&gt; - a Flake8 plugin that
makes it easier to write tests that follow the Arrange Act Assert&amp;nbsp;pattern.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="finally"&gt;
&lt;h2&gt;Finally&lt;/h2&gt;
&lt;p&gt;Thanks again to &lt;a class="reference external" href="https://github.com/txels"&gt;Carles&lt;/a&gt; for introducing me to the
&lt;span class="caps"&gt;AAA&lt;/span&gt;&amp;nbsp;pattern.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Talk"></category><category term="language:python"></category><category term="topic:testing"></category></entry><entry><title>Python unittest: assertTrue is truthy, assertFalse is falsy</title><link href="https://jamescooke.info/python-unittest-asserttrue-is-truthy-assertfalse-is-falsy.html" rel="alternate"></link><published>2016-05-12T14:00:00+01:00</published><updated>2016-05-12T14:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2016-05-12:/python-unittest-asserttrue-is-truthy-assertfalse-is-falsy.html</id><summary type="html">&lt;p class="first last"&gt;Exploring Python&amp;#8217;s &lt;tt class="docutils literal"&gt;unittest&lt;/tt&gt; module boolean assert methods and how
they can be misleading in certain situations, but not if you &lt;span class="caps"&gt;RTFM&lt;/span&gt; of&amp;nbsp;course.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In this post, I explore the differences between the unittest boolean assert
methods &lt;tt class="docutils literal"&gt;assertTrue&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;assertFalse&lt;/tt&gt; and the &lt;tt class="docutils literal"&gt;assertIs&lt;/tt&gt; identity&amp;nbsp;assertion.&lt;/p&gt;
&lt;div class="section" id="definitions"&gt;
&lt;h2&gt;Definitions&lt;/h2&gt;
&lt;p&gt;Here&amp;#8217;s what the &lt;a class="reference external" href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertTrue"&gt;unittest module documentation&lt;/a&gt;
currently notes about &lt;tt class="docutils literal"&gt;assertTrue&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;assertFalse&lt;/tt&gt;, with the appropriate
code&amp;nbsp;highlighted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;assertTrue(expr, msg=None)&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;assertFalse(expr, msg=None)&lt;/tt&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Test that &lt;em&gt;expr&lt;/em&gt; is true (or&amp;nbsp;false).&lt;/p&gt;
&lt;p&gt;Note that this is equivalent&amp;nbsp;to&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and not&amp;nbsp;to&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(use &lt;tt class="docutils literal"&gt;assertIs(expr, True)&lt;/tt&gt; for the&amp;nbsp;latter).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a class="reference external" href="https://developer.mozilla.org/en-US/docs/Glossary/Truthy"&gt;Mozilla Developer Network defines truthy&lt;/a&gt;&amp;nbsp;as:&lt;/p&gt;
&lt;blockquote&gt;
A value that translates to true when evaluated in a Boolean context.&lt;/blockquote&gt;
&lt;p&gt;In Python this is equivalent&amp;nbsp;to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Which exactly matches what &lt;tt class="docutils literal"&gt;assertTrue&lt;/tt&gt; is testing&amp;nbsp;for.&lt;/p&gt;
&lt;p&gt;Therefore the documentation already indicates &lt;tt class="docutils literal"&gt;assertTrue&lt;/tt&gt; is truthy and
&lt;tt class="docutils literal"&gt;assertFalse&lt;/tt&gt; is falsy. These assertion methods are creating a &lt;tt class="docutils literal"&gt;bool&lt;/tt&gt; from
the received value and then evaluating it. It also suggests that we really
shouldn&amp;#8217;t use &lt;tt class="docutils literal"&gt;assertTrue&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;assertFalse&lt;/tt&gt; for very much at&amp;nbsp;all.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="what-does-this-mean-in-practice"&gt;
&lt;h2&gt;What does this mean in&amp;nbsp;practice?&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s use a very simple example - a function called &lt;tt class="docutils literal"&gt;always_true&lt;/tt&gt; that
returns &lt;tt class="docutils literal"&gt;True&lt;/tt&gt;. We&amp;#8217;ll write the tests for it and then make changes to the
code and see how the tests&amp;nbsp;perform.&lt;/p&gt;
&lt;p&gt;Starting with the tests, we&amp;#8217;ll have two tests. One is &amp;#8220;loose&amp;#8221;, using
&lt;tt class="docutils literal"&gt;assertTrue&lt;/tt&gt; to test for a truthy value. The other is &amp;#8220;strict&amp;#8221; using
&lt;tt class="docutils literal"&gt;assertIs&lt;/tt&gt; as recommended by the&amp;nbsp;documentation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;unittest&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;func&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;always_true&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestAlwaysTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_assertTrue&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="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        always_true returns a truthy value&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;always_true&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;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_assertIs&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="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        always_true returns True&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;always_true&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;assertIs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&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;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here&amp;#8217;s the code for our simple function in &lt;tt class="docutils literal"&gt;func.py&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;always_true&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    I&amp;#39;m always True.&lt;/span&gt;

&lt;span class="sd"&gt;    Returns:&lt;/span&gt;
&lt;span class="sd"&gt;        bool: True&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When run, everything&amp;nbsp;passes:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
always_true returns True ... ok
always_true returns a truthy value ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.004s

OK
&lt;/pre&gt;
&lt;p&gt;Happy&amp;nbsp;days!&lt;/p&gt;
&lt;p&gt;Now, &amp;#8220;someone&amp;#8221; changes &lt;tt class="docutils literal"&gt;always_true&lt;/tt&gt; to the&amp;nbsp;following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;always_true&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    I&amp;#39;m always True.&lt;/span&gt;

&lt;span class="sd"&gt;    Returns:&lt;/span&gt;
&lt;span class="sd"&gt;        bool: True&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;True&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Instead of returning &lt;tt class="docutils literal"&gt;True&lt;/tt&gt; (boolean), it&amp;#8217;s now returning string &lt;tt class="docutils literal"&gt;'True'&lt;/tt&gt;.
(Of course this &amp;#8220;someone&amp;#8221; hasn&amp;#8217;t updated the docstring - we&amp;#8217;ll raise a ticket&amp;nbsp;later.)&lt;/p&gt;
&lt;p&gt;This time the result is not so&amp;nbsp;happy:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
always_true returns True ... FAIL
always_true returns a truthy value ... ok

======================================================================
FAIL: always_true returns True
----------------------------------------------------------------------
Traceback (most recent call last):
  File &amp;quot;/tmp/assertttt/test.py&amp;quot;, line 22, in test_is_true
    self.assertIs(result, True)
AssertionError: 'True' is not True

----------------------------------------------------------------------
Ran 2 tests in 0.004s

FAILED (failures=1)
&lt;/pre&gt;
&lt;p&gt;Only &lt;em&gt;one&lt;/em&gt; test failed! This means &lt;tt class="docutils literal"&gt;assertTrue&lt;/tt&gt; gave us a false-positive. It
passed when it shouldn&amp;#8217;t have. It&amp;#8217;s lucky we wrote the second test with
&lt;tt class="docutils literal"&gt;assertIs&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Therefore, just as we learned from the manual, to keep the functionality of
&lt;tt class="docutils literal"&gt;always_true&lt;/tt&gt; pinned tightly the stricter &lt;tt class="docutils literal"&gt;assertIs&lt;/tt&gt; should be used rather
than &lt;tt class="docutils literal"&gt;assertTrue&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="use-assertion-helpers"&gt;
&lt;h2&gt;Use assertion&amp;nbsp;helpers&lt;/h2&gt;
&lt;p&gt;Writing out &lt;tt class="docutils literal"&gt;assertIs&lt;/tt&gt; to test for &lt;tt class="docutils literal"&gt;True&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;False&lt;/tt&gt; values is not too
lengthy. However, if you have a project in which you often need to check that
values are exactly &lt;tt class="docutils literal"&gt;True&lt;/tt&gt; or exactly &lt;tt class="docutils literal"&gt;False&lt;/tt&gt;, then you can make yourself
the &lt;tt class="docutils literal"&gt;assertIsTrue&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;assertIsFalse&lt;/tt&gt; assertion&amp;nbsp;helpers.&lt;/p&gt;
&lt;p&gt;This doesn&amp;#8217;t save a particularly large amount of code, but it does improve
readability in my&amp;nbsp;opinion.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assertIsTrue&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;value&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;assertIs&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="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assertIsFalse&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;value&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;assertIs&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="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="summary"&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;In general, my recommendation is to keep tests as tight as possible. If you
mean to test for the exact value &lt;tt class="docutils literal"&gt;True&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;False&lt;/tt&gt;, then follow the
&lt;a class="reference external" href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertTrue"&gt;documentation&lt;/a&gt;
and use &lt;tt class="docutils literal"&gt;assertIs&lt;/tt&gt;. Do not use &lt;tt class="docutils literal"&gt;assertTrue&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;assertFalse&lt;/tt&gt; unless you
really have&amp;nbsp;to.&lt;/p&gt;
&lt;p&gt;If you are looking at a function that can return various types, for example,
sometimes &lt;tt class="docutils literal"&gt;bool&lt;/tt&gt; sometimes &lt;tt class="docutils literal"&gt;int&lt;/tt&gt;, then consider refactoring. This is a code
smell and in Python, that &lt;tt class="docutils literal"&gt;False&lt;/tt&gt; value for an error would probably be better
raised as an&amp;nbsp;exception.&lt;/p&gt;
&lt;p&gt;In addition, if you really need to assert the return value from a function
under test is truthy, there might be a second code smell - is your code
correctly encapsulated? If &lt;tt class="docutils literal"&gt;assertTrue&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;assertFalse&lt;/tt&gt; are asserting
that function return values will trigger &lt;tt class="docutils literal"&gt;if&lt;/tt&gt; statements correctly, then it
might be worth sense-checking you&amp;#8217;ve encapsulated everything you intended in
the appropriate place. Maybe those &lt;tt class="docutils literal"&gt;if&lt;/tt&gt; statements should be encapsulated
within the function under&amp;nbsp;test.&lt;/p&gt;
&lt;p&gt;Happy&amp;nbsp;testing!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="language:python"></category><category term="topic:testing"></category></entry><entry><title>API Documentation and the Communication Illusion</title><link href="https://jamescooke.info/api-documentation-and-the-communication-illusion.html" rel="alternate"></link><published>2016-05-04T12:00:00+01:00</published><updated>2016-05-04T12:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2016-05-04:/api-documentation-and-the-communication-illusion.html</id><summary type="html">&lt;p class="first last"&gt;How can documenting an &lt;span class="caps"&gt;API&lt;/span&gt; help us to communicate with our team and
&lt;span class="caps"&gt;API&lt;/span&gt; consumer developers&amp;nbsp;efficiently?&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In this post I talk quite a lot about the &amp;#8220;Communication Illusion&amp;#8221; - a quote
often &lt;a class="reference external" href="https://en.wikiquote.org/wiki/George_Bernard_Shaw#Misattributed"&gt;misattributed to George Bernard Shaw&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
The single biggest problem in communication is the illusion that it has
taken place.&lt;/blockquote&gt;
&lt;p&gt;Now let&amp;#8217;s talk about documenting&amp;nbsp;APIs&amp;#8230;&lt;/p&gt;
&lt;div class="section" id="api-documentation-and-my-current-workflow"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;API&lt;/span&gt; documentation and my current&amp;nbsp;workflow&lt;/h2&gt;
&lt;p&gt;When I&amp;#8217;m building an &lt;span class="caps"&gt;API&lt;/span&gt;, I usually have a client in mind that will be
consuming its resources. That means a developer or team of developers will be
writing code against the &lt;span class="caps"&gt;API&lt;/span&gt; that I build and so my current workflow for
updating or creating new &lt;span class="caps"&gt;API&lt;/span&gt; services focuses on providing &lt;span class="caps"&gt;API&lt;/span&gt;&amp;nbsp;documentation.&lt;/p&gt;
&lt;p&gt;It works like&amp;nbsp;this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Manually document the endpoint using &lt;a class="reference external" href="https://github.com/jamescooke/restapidocs"&gt;&lt;span class="caps"&gt;REST&lt;/span&gt; &lt;span class="caps"&gt;API&lt;/span&gt; documentation templates&lt;/a&gt;. Highlight any side effects,
requirements and illustrate required data and&amp;nbsp;responses.&lt;/p&gt;
&lt;p&gt;Alternatively, if there is a change to be made to the service, then the
documentation edit can be provided as an &lt;span class="caps"&gt;SCM&lt;/span&gt; diff which can be easily&amp;nbsp;reviewed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Ask for sign off on the &lt;span class="caps"&gt;API&lt;/span&gt; documentation from the &lt;span class="caps"&gt;API&lt;/span&gt; consumer development
team. Incorporate any feedback and repeat until team are&amp;nbsp;happy.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Build out new endpoint using test cases written from the&amp;nbsp;documentation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Once complete, export server responses back from the test suite into the
documentation. Note any changes that were required to the signed off document
and flag them to the consumer developers during the release process, if not&amp;nbsp;beforehand.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="past-experience-of-communications-illusions"&gt;
&lt;h2&gt;Past experience of Communications&amp;nbsp;Illusions&lt;/h2&gt;
&lt;p&gt;In conversations with other developers about my process it&amp;#8217;s been called
&amp;#8220;tedious&amp;#8221;. I can agree that it seems like extra effort at first, however, I
believe that it pays off in the long run and that belief is based on my past
experience at my old web development business,&amp;nbsp;Fublo.&lt;/p&gt;
&lt;p&gt;At Fublo, we often produced &lt;a class="reference external" href="https://en.wiktionary.org/wiki/brochureware"&gt;brochureware&lt;/a&gt; sites on top of a simple &lt;span class="caps"&gt;CMS&lt;/span&gt;
system. When starting a new project, conversation within our team would often
happen like&amp;nbsp;this:&lt;/p&gt;
&lt;blockquote&gt;
We know the solution to the problem, so instead of drawing out the design,
let&amp;#8217;s just jump straight into &lt;span class="caps"&gt;HTML&lt;/span&gt; and code it up. This will save time.&lt;/blockquote&gt;
&lt;p&gt;Inevitably this didn&amp;#8217;t save time at all. We would often have to burn much of
our initial work because we couldn&amp;#8217;t fit the final client requirements into it,
or rework major structural elements because they turned out to be&amp;nbsp;unsuitable.&lt;/p&gt;
&lt;p&gt;This inefficiency is the result of three&amp;nbsp;illusions:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;The illusion with ourselves:&lt;/strong&gt; When we thought that we knew the solution
and that it would be suitable and workable, this was just an illusion. Our
minds tricked us into thinking that we knew the answer but we&amp;nbsp;didn&amp;#8217;t.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Communications Illusion within the team:&lt;/strong&gt; This was in full effect
within our small team. We found that what we described as the problem to each
other would fit with our own distorted view and we would only find out that
our opinions were not the same some significant time after starting the
build - sometimes too late to make&amp;nbsp;changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Communications Illusion with the client:&lt;/strong&gt; We also didn&amp;#8217;t often fully
understand the client need, even though we thought we did at the outset.
Sometimes there would be omissions in the brief or misunderstandings about
how content would be provided, presented or&amp;nbsp;managed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those three illusions sum up to one big Communications Illusion - we believe
that we, our team and our clients all have tallying opinions and ideas, when
there is no proof that that is the&amp;nbsp;case.&lt;/p&gt;
&lt;p&gt;Pretty quickly, but probably not as quickly as we would have liked, we realised
that the &amp;#8220;tedious&amp;#8221; way of producing flat designs before starting coding was
often the most efficient, even though it felt like it wasn&amp;#8217;t. It provided the
quickest route to a communication feedback loop, within ourselves, our team and
with our clients and that destroys the Communication Illusion. On top of that
it meant that we were not making any accidental, hard to refactor, architectural
decisions on the fly without all the required&amp;nbsp;information.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="applying-this-to-api-design"&gt;
&lt;h2&gt;Applying this to &lt;span class="caps"&gt;API&lt;/span&gt;&amp;nbsp;design&lt;/h2&gt;
&lt;p&gt;So it&amp;#8217;s my opinion that producing a flat &lt;span class="caps"&gt;API&lt;/span&gt; document without touching a line
of code or writing a single test is the most efficient way of destroying the
potential Communications Illusions when creating&amp;nbsp;APIs.&lt;/p&gt;
&lt;p&gt;This means that providing static &lt;span class="caps"&gt;API&lt;/span&gt; documentation first is the most efficient
path to a place where &lt;span class="caps"&gt;API&lt;/span&gt; producers and consumers have a joint shared opinion
about how the &lt;span class="caps"&gt;API&lt;/span&gt; will operate and&amp;nbsp;perform.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tooling-wishlist"&gt;
&lt;h2&gt;Tooling&amp;nbsp;wishlist&lt;/h2&gt;
&lt;p&gt;On the flip side of those benefits in the long term, there is still a part of
me that finds some of my manual process a little slow. My inner need for
efficiency feels unfulfilled every time I copy and paste an &lt;span class="caps"&gt;API&lt;/span&gt; response into a&amp;nbsp;document.&lt;/p&gt;
&lt;p&gt;In an ideal world I would have tools that&amp;nbsp;would:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Extract from the &lt;span class="caps"&gt;API&lt;/span&gt; documentation a set of tests that could be run against
the built &lt;span class="caps"&gt;API&lt;/span&gt; to assert that the documentation features were all adhered&amp;nbsp;to.&lt;/li&gt;
&lt;li&gt;A tool that would extract from the server responses made to these tests the
response payloads so that they could be added back in or matched with the &lt;span class="caps"&gt;API&lt;/span&gt;
document that they came&amp;nbsp;from.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, even if those tools did exist, the starting point would still be some
form of static documentation that describes what the &lt;span class="caps"&gt;API&lt;/span&gt;&amp;nbsp;does.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve seen that there are tools like &lt;a class="reference external" href="https://apiary.io/"&gt;Apiary&lt;/a&gt; which might
make this process easier, but I would want to hear some positive experience and
read a case study before I committed to using such a service for client&amp;nbsp;work.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="final-thoughts"&gt;
&lt;h2&gt;Final&amp;nbsp;thoughts&lt;/h2&gt;
&lt;p&gt;So, I hope in this post I&amp;#8217;ve been able to convince you that it&amp;#8217;s most
efficient to document your &lt;span class="caps"&gt;API&lt;/span&gt; before you build or change it. I hope that, like
me, you find that that efficiency comes from the increase in communication that
the documentation creates as a side-effect, destroying the Communication
Illusion that can ruin a project&amp;nbsp;build.&lt;/p&gt;
&lt;p&gt;Unfortunately, I&amp;#8217;ve not found a way to ensure that the consumer programmers of
my APIs read and understand what&amp;#8217;s in the documentation before coding starts.
That&amp;#8217;s a second layer of Communication Illusion that I&amp;#8217;ll maybe get to tackle
another&amp;nbsp;day.&lt;/p&gt;
&lt;p&gt;Happily, I still agree with this (old, now deleted) Tweet that I posted more
than 18 months&amp;nbsp;ago:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Building an &lt;span class="caps"&gt;API&lt;/span&gt;&amp;#8230; All that matters is the&amp;nbsp;docs.&lt;/p&gt;
&lt;p&gt;October 3,&amp;nbsp;2014&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;#8230;and in fact, after working on more &lt;span class="caps"&gt;API&lt;/span&gt; builds and writing this post, I
believe it&amp;#8217;s even more true than&amp;nbsp;before.&lt;/p&gt;
&lt;p&gt;Happy &lt;span class="caps"&gt;API&lt;/span&gt;&amp;nbsp;building!&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://news.ycombinator.com/item?id=11666301"&gt;Read comments on Hacker&amp;nbsp;News&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="topic:api"></category><category term="topic:documentation"></category></entry><entry><title>Comparing Django Q Objects</title><link href="https://jamescooke.info/comparing-django-q-objects.html" rel="alternate"></link><published>2016-03-28T12:00:00+01:00</published><updated>2016-03-28T12:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2016-03-28:/comparing-django-q-objects.html</id><summary type="html">&lt;p class="first last"&gt;A super simple assertion helper for comparing instances of Django&amp;#8217;s Q&amp;nbsp;objects.&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="background"&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;blockquote&gt;
&lt;strong&gt;Note:&lt;/strong&gt; A newer version of this post exists with an assertion helper for
&lt;a class="reference external" href="https://jamescooke.info/comparing-django-q-objects-in-python-3-with-pytest.html"&gt;Python 3 and pytest&lt;/a&gt;. Read on for
Python 2 and unittest and general background on Q objects&amp;#8230;&lt;/blockquote&gt;
&lt;p&gt;When programmatically building complex queries in Django &lt;span class="caps"&gt;ORM&lt;/span&gt;, it&amp;#8217;s helpful
to be able to test the resulting &lt;a class="reference external" href="https://docs.djangoproject.com/en/1.8/topics/db/queries/#complex-lookups-with-q"&gt;Q object instances&lt;/a&gt;
against each&amp;nbsp;other.&lt;/p&gt;
&lt;p&gt;However, Django&amp;#8217;s Q object does not implement &lt;tt class="docutils literal"&gt;__cmp__&lt;/tt&gt; and neither does
&lt;tt class="docutils literal"&gt;Node&lt;/tt&gt; which it extends (&lt;tt class="docutils literal"&gt;Node&lt;/tt&gt; is in the &lt;tt class="docutils literal"&gt;django.utils.tree&lt;/tt&gt; module).&lt;/p&gt;
&lt;p&gt;Unfortunately, that means that comparison of Q objects that are equal&amp;nbsp;fails.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;value&amp;#39;&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;value&amp;#39;&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="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;Assertion&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This means that writing unit tests that assert that correct Q objects have been
created is&amp;nbsp;hard.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="a-simple-solution"&gt;
&lt;h2&gt;A simple&amp;nbsp;solution&lt;/h2&gt;
&lt;p&gt;Q objects generate great Unicode representations of&amp;nbsp;themselves:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;place&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Residential&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;people__gt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&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;unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;(AND: (&amp;#39;place&amp;#39;, &amp;#39;Residential&amp;#39;), (&amp;#39;people__gt&amp;#39;, 5))&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In addition, it is &amp;#8220;good&amp;#8221; testing practice to write assertion helpers whenever
a test suite has complicated assertions to make frequently. This provides an
opportunity to &lt;span class="caps"&gt;DRY&lt;/span&gt; out test code and expand on any error messages that are
raised on&amp;nbsp;failure.&lt;/p&gt;
&lt;p&gt;Therefore a really simple solution is an assertion helper that would compare Q
objects&amp;nbsp;by:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Asserting that left and right sides are both instances of &lt;tt class="docutils literal"&gt;Q&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;Asserting that the Unicode for the left and right sides are&amp;nbsp;identical.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So here&amp;#8217;s a mixin containing the assertion helper. It can be added to any class
that extends &lt;tt class="docutils literal"&gt;unittest.TestCase&lt;/tt&gt; (such as Django&amp;#8217;s default &lt;tt class="docutils literal"&gt;TestCase&lt;/tt&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QTestMixin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assertQEqual&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;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Assert `Q` objects are equal by ensuring that their&lt;/span&gt;
&lt;span class="sd"&gt;        unicode outputs are equal (crappy but good enough)&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&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;assertIsInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Q&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;assertIsInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;left_u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;right_u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left_u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right_u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Disadvantage of this method is that it is simplistic and doesn&amp;#8217;t find all the Q
objects that are identical (see below). However, the advantage is that it
provides rich diffs on&amp;nbsp;failure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QTestMixin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_unhappy&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="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Two Q objects are not the same&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;place&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Residential&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;place&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Palace&amp;#39;&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;assertQEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Gives&amp;nbsp;output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;AssertionError:&lt;span class="w"&gt; &lt;/span&gt;u&lt;span class="s2"&gt;&amp;quot;(AND: (&amp;#39;place&amp;#39;, &amp;#39;Residential&amp;#39;))&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;u&lt;span class="s2"&gt;&amp;quot;(AND: (&amp;#39;place&amp;#39;, &amp;#39;Palace&amp;#39;))&amp;quot;&lt;/span&gt;
-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;AND:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;place&amp;#39;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Residential&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
?&lt;span class="w"&gt;                  &lt;/span&gt;^^^^^^^^^
+&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;AND:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;place&amp;#39;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Palace&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
?&lt;span class="w"&gt;                  &lt;/span&gt;^&lt;span class="w"&gt;  &lt;/span&gt;+++
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Which can be very helpful when trying to track down&amp;nbsp;errors.&lt;/p&gt;
&lt;blockquote&gt;
See this &lt;a class="reference external" href="https://jamescooke.info/comparing-django-q-objects-in-python-3-with-pytest.html"&gt;updated post&lt;/a&gt; for a version of
this assertion helper for Python 3 with pytest.&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="the-perfect-world-predicate-logic"&gt;
&lt;h2&gt;The perfect world: Predicate&amp;nbsp;Logic&lt;/h2&gt;
&lt;p&gt;Since Q objects represent the logic of &lt;span class="caps"&gt;SQL&lt;/span&gt; &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;WHERE&lt;/span&gt;&lt;/tt&gt; clauses they are therefore
Python representations of predicates. In an ideal world the predicate logic
rules of equality could be used to compare Q objects and this would be built
directly into &lt;tt class="docutils literal"&gt;Q.__cmp__&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;This would mean&amp;nbsp;that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# WARNING MAGIC IMAGINARY CODE!&lt;/span&gt;

&lt;span class="c1"&gt;# Commutative would work&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Q&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Q&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="mi"&gt;2&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Q&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Q&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="mi"&gt;1&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;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="kc"&gt;True&lt;/span&gt;

&lt;span class="c1"&gt;# Double negation would work&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Q&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="mi"&gt;1&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;~~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Q&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="kc"&gt;True&lt;/span&gt;

&lt;span class="c1"&gt;# Negation on expression would work&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Q&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Q&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="mi"&gt;2&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;Q&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;Q&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="mi"&gt;2&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;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="kc"&gt;True&lt;/span&gt;

&lt;span class="c1"&gt;# END IMAGINATION SECTION&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is probably never going to be implemented in Django, because it would be
functionality only used (as far as I can see) for testing. In addition, without
a special implementation for rendering Q objects diffs, it would be hard to
understand the source of errors when mismatches&amp;nbsp;occur.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="final-testing-related-notes"&gt;
&lt;h2&gt;Final testing related&amp;nbsp;notes&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;When a suite has complicated assertions to test regularly, create an
assertion helper. Write tests to show that your helper works correctly under
various&amp;nbsp;conditions.&lt;/li&gt;
&lt;li&gt;Tests for &lt;tt class="docutils literal"&gt;assertQEqual&lt;/tt&gt; are &lt;a class="reference external" href="https://gist.github.com/jamescooke/b9bd5afba3a7253d53bd"&gt;in this gist&lt;/a&gt;. (If you spot
something missing, please let me&amp;nbsp;know!)&lt;/li&gt;
&lt;li&gt;Always consider the output of failing tests - the complexity of managing a
test suite for a software project can be greatly influenced by how
informative assertion errors are when they&amp;nbsp;occur.&lt;/li&gt;
&lt;li&gt;A secondary assertion helper could be created to check for inequality
&lt;tt class="docutils literal"&gt;assertQNotEquals&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="topic:testing"></category><category term="language:python"></category><category term="topic:django"></category></entry><entry><title>Django’s model save vs full_clean</title><link href="https://jamescooke.info/djangos-model-save-vs-full_clean.html" rel="alternate"></link><published>2015-11-15T17:00:00+00:00</published><updated>2015-11-15T17:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2015-11-15:/djangos-model-save-vs-full_clean.html</id><summary type="html">&lt;p class="first last"&gt;Confirming that Django can screw up your data when saving, and
exploring why this is still the&amp;nbsp;case.&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="screwing-up-data"&gt;
&lt;h2&gt;Screwing up&amp;nbsp;data&lt;/h2&gt;
&lt;p&gt;At a previous talk I gave on &lt;a class="reference external" href="/things-i-wish-id-known-about-django.html"&gt;&amp;#8220;Things I wish I&amp;#8217;d known about Django&amp;#8221;&lt;/a&gt; there was this&amp;nbsp;slide:&lt;/p&gt;
&lt;script async class="speakerdeck-embed" data-slide="6" data-id="6f327318d302437f8ccafb8da4c2cd32" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;&lt;/div&gt;
&lt;div class="section" id="what"&gt;
&lt;h2&gt;What?&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;ve made some experimental code in a &lt;a class="reference external" href="https://github.com/jamescooke/django-clean-vs-save"&gt;small Django clean vs save project&lt;/a&gt;.  It has a few models
and a &lt;a class="reference external" href="https://github.com/jamescooke/django-clean-vs-save/blob/master/clean_vs_save/clean_vs_save/tests.py"&gt;single test file&lt;/a&gt;
which is readable and&amp;nbsp;passes.&lt;/p&gt;
&lt;p&gt;The main take-aways&amp;nbsp;are:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Creating an instance of a Model and calling &lt;tt class="docutils literal"&gt;save&lt;/tt&gt; on that instance does
not call &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt;. Therefore it&amp;#8217;s possible for invalid data to enter
your database if you don&amp;#8217;t manually call the &lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; function before&amp;nbsp;saving.&lt;/li&gt;
&lt;li&gt;Object managers&amp;#8217; default &lt;tt class="docutils literal"&gt;create&lt;/tt&gt; function also doesn&amp;#8217;t call
&lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Personally I find this&amp;nbsp;jarring.&lt;/p&gt;
&lt;p&gt;Given that the developer is the customer of Django I think it conflicts with
the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Principle_of_least_astonishment"&gt;principle of least astonishment&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="why-is-it-like-this"&gt;
&lt;h2&gt;Why is it like&amp;nbsp;this?&lt;/h2&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/ref/models/instances/#validating-objects"&gt;Django documentation of Validating Objects&lt;/a&gt;
is quoted in &lt;a class="reference external" href="https://code.djangoproject.com/ticket/13100"&gt;Django ticket #13100&lt;/a&gt; as&amp;nbsp;saying:&lt;/p&gt;
&lt;blockquote&gt;
Note that full_clean() will &lt;span class="caps"&gt;NOT&lt;/span&gt; be called automatically when you call your
model&amp;#8217;s save() method. You&amp;#8217;ll need to call it manually if you want to run
model validation outside of a ModelForm. (This is for backwards
compatibility.)&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ahh &amp;#8220;backwards&amp;nbsp;compatibility&amp;#8221;?!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It appears that phrase only lived for four months back in&amp;nbsp;2010.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/django/django/commit/4d6c66d4d8fa63005f8ca2d3fbae195922969d13"&gt;Created 5th Jan&amp;nbsp;2010&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/django/django/commit/4a15dc450996b62596d74f8d98388c9e2f4a10c7#diff-5b33a9c46f488003c1846ef677f861d3L56"&gt;Removed 9th May&amp;nbsp;2010&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I haven&amp;#8217;t been able to find any more specific reasons that it was added or&amp;nbsp;removed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="what-next"&gt;
&lt;h2&gt;What&amp;nbsp;next?&lt;/h2&gt;
&lt;p&gt;More warnings I&amp;nbsp;guess:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Consider if you ever want to be able to call &lt;tt class="docutils literal"&gt;save&lt;/tt&gt; &lt;strong&gt;without&lt;/strong&gt;
&lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt;. If the answer is &amp;#8216;no&amp;#8217;, then explore how you&amp;#8217;ll wrap your
models in business logic or extend them in some way that implements this
(with tests of course). A quick search of the internet will show you some
Django plugins that adjust this&amp;nbsp;behaviour.&lt;/li&gt;
&lt;li&gt;Remember that you can ruin your database when migrating if you don&amp;#8217;t call
&lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; after the migration has changed a model but before saving. All
migrations should be tested to ensure that they can roll-back if
&lt;tt class="docutils literal"&gt;full_clean&lt;/tt&gt; raises a &lt;tt class="docutils literal"&gt;ValidationError&lt;/tt&gt; during&amp;nbsp;migration.&lt;/li&gt;
&lt;li&gt;Check out posts like &lt;a class="reference external" href="http://dev.nando.audio/2014/04/04/why_i_sort_of_dislike_django.html"&gt;Why I sort of dislike Django&lt;/a&gt;. It
mentions things like backwards compatibility and the &lt;tt class="docutils literal"&gt;save&lt;/tt&gt; function.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="finally"&gt;
&lt;h2&gt;Finally&lt;/h2&gt;
&lt;p&gt;Thanks to &lt;a class="reference external" href="https://twitter.com/petexgraham"&gt;&lt;span class="caps"&gt;PXG&lt;/span&gt;&lt;/a&gt; for sharing the &amp;#8220;Why I sort
of dislike Django&amp;#8221; post and discussing Django project structures with&amp;nbsp;me.&lt;/p&gt;
&lt;p&gt;Thanks for&amp;nbsp;reading!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="topic:django"></category></entry><entry><title>Irregular Vim</title><link href="https://jamescooke.info/irregular-vim.html" rel="alternate"></link><published>2015-08-16T21:00:00+01:00</published><updated>2015-08-16T21:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2015-08-16:/irregular-vim.html</id><summary type="html">&lt;p class="first last"&gt;At July&amp;#8217;s Vim London I gave a talk about some of Vim&amp;#8217;s irregular
behaviours. Using bare-bones Vim to present and demonstrate from is a
risky&amp;nbsp;business!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;My frustrations with Vim arise when it makes actions that are unexpected. At
&lt;a class="reference external" href="https://www.meetup.com/Vim-London/events/223784891/"&gt;Vim London&lt;/a&gt; I presented
some of my &amp;#8220;pet misbehaviours&amp;#8221; - these are the ones that affect my regular use
of&amp;nbsp;Vim.&lt;/p&gt;
&lt;div class="section" id="background"&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;If you&amp;#8217;re new to Vim then one of the key features of Vim is that it&amp;#8217;s a modal
editor. As a result, to quote a quote from a &lt;a class="reference external" href="/vi-nature-everywhere-lightning-talk.html"&gt;previous talk&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
The &amp;#8220;Zen&amp;#8221; of vi is that you&amp;#8217;re speaking a language.&lt;/blockquote&gt;
&lt;p&gt;So what happens when a language has many irregularities and frequently broken
rules? They become hard to learn. For example the &lt;a class="reference external" href="https://www.oxford-royale.co.uk/articles/learning-english-hard.html"&gt;English language is hard&lt;/a&gt;&amp;nbsp;because:&lt;/p&gt;
&lt;blockquote&gt;
&amp;#8230; although there are rules, there are lots of exceptions to those rules.&lt;/blockquote&gt;
&lt;p&gt;My fear is that if Vim is hard to learn it will be overlooked by new users and
it will cease to exist in the future. I think we should all be working on the
maxim that Drew has put on the &lt;a class="reference external" href="https://www.meetup.com/Vim-London/"&gt;Vim London meetup page&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
Use Vim better, make Vim better.&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="pet-misbehaviours"&gt;
&lt;h2&gt;Pet&amp;nbsp;misbehaviours&lt;/h2&gt;
&lt;p&gt;Here are the five behaviours looked at in this talk, each one linked to its
section in the slides on&amp;nbsp;Github.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://github.com/jamescooke/irregular-vim-slides/blob/master/slides/10a_motion_exceptions.rst"&gt;Linewise motions always include the start and end&amp;nbsp;position&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Except&lt;/strong&gt; when the end of the motion is in column&amp;nbsp;1.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://github.com/jamescooke/irregular-vim-slides/blob/master/slides/15a_change_is_delete_insert.rst"&gt;Change is equivalent to Delete&amp;nbsp;Insert&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Except&lt;/strong&gt; when motion is &lt;tt class="docutils literal"&gt;w&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://github.com/jamescooke/irregular-vim-slides/blob/master/slides/20a_pasting_and_registers.rst"&gt;Pasting from registers is easily&amp;nbsp;repeatable&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Except&lt;/strong&gt; when in visual&amp;nbsp;modes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://github.com/jamescooke/irregular-vim-slides/blob/master/slides/25a_add_number.rst"&gt;Incrementing number after cursor is&amp;nbsp;predictable&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Except&lt;/strong&gt; when the number starts with a &lt;tt class="docutils literal"&gt;0&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="https://github.com/jamescooke/irregular-vim-slides/blob/master/slides/30a_ctrl_o_goes_jump_older.rst"&gt;&lt;span class="caps"&gt;CTRL&lt;/span&gt;-O goes back to old cursor&amp;nbsp;position&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Except&lt;/strong&gt; when in visual&amp;nbsp;modes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="testing-process"&gt;
&lt;h2&gt;Testing&amp;nbsp;process&lt;/h2&gt;
&lt;p&gt;Automated and predictable testing is an important part of how I work and so I
attempted to use a repeatable process for testing each of the&amp;nbsp;behaviours.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Outline the &lt;strong&gt;assumption&lt;/strong&gt; about Vim. Highlight the docs (where available)
that make the statement or&amp;nbsp;assertion.&lt;/li&gt;
&lt;li&gt;Do some small &lt;strong&gt;tests&lt;/strong&gt; of this assertion. Does it work as expected? How do
we feel about the&amp;nbsp;behaviour?&lt;/li&gt;
&lt;li&gt;Update the assertion with any &lt;strong&gt;exceptions&lt;/strong&gt; and look at any reasons for
those&amp;nbsp;exceptions.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="content"&gt;
&lt;h2&gt;Content&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://vimeo.com/135055397"&gt;Video on vimeo&lt;/a&gt; (thanks &lt;a class="reference external" href="https://twitter.com/nelstrom"&gt;Drew&lt;/a&gt;).&lt;/p&gt;
&lt;iframe src="//player.vimeo.com/video/135055397" width="500" height="281" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen&gt;&lt;/iframe&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/jamescooke/irregular-vim-slides"&gt;Slides are on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="learnings"&gt;
&lt;h2&gt;Learnings&lt;/h2&gt;
&lt;p&gt;One of my annoyances that started this journey arose when attempting to delete
everything up to but not including a character. As you&amp;#8217;ll see at the end of the
talk, I learned a new movement command &lt;tt class="docutils literal"&gt;t&lt;/tt&gt; (thanks&amp;nbsp;Audience!).&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;t&lt;/tt&gt; is like &lt;tt class="docutils literal"&gt;f&lt;/tt&gt; but not inclusive. From the help &lt;tt class="docutils literal"&gt;:help t&lt;/tt&gt; file:&lt;/p&gt;
&lt;blockquote&gt;
Till before [count]&amp;#8217;th occurrence of {char} to the right. The cursor is
placed on the character left of {char} inclusive. {char} can be entered
like with the f command.&lt;/blockquote&gt;
&lt;p&gt;This exactly solves the problem that started my exploration of Vim&amp;#8217;s
irregularities. It&amp;#8217;s a humbling experience when you talk for 20 minutes about
Vim commands and still learn a &amp;#8216;basic&amp;#8217; one at the end of the talk. I think that
this is a reminder to me that Vim is&amp;nbsp;deep.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="future"&gt;
&lt;h2&gt;Future&lt;/h2&gt;
&lt;p&gt;I would like to improve these misbehaviours and make them more regular. My hope
is that, if this could be achieved, it would make Vim&amp;#8217;s interface even more
great and also easier to&amp;nbsp;learn.&lt;/p&gt;
&lt;p&gt;The main thing for me going forwards is to use &lt;a class="reference external" href="https://neovim.io/"&gt;Neovim&lt;/a&gt;. A
project that is open to improving how Vim works. Here&amp;#8217;s a great post about &lt;a class="reference external" href="https://geoff.greer.fm/2015/01/15/why-neovim-is-better-than-vim/"&gt;why
Neovim is better than Vim&lt;/a&gt; - thanks
&lt;a class="reference external" href="https://twitter.com/ggreer"&gt;Geoff&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;From there I will check out how many of these irregularities can be improved
with code changes because having to have a &lt;tt class="docutils literal"&gt;vimrc&lt;/tt&gt; file that resets Vim to
&amp;#8216;regular&amp;#8217; behaviour by turning off things like octal numerical increments seems
horrible and repellent to new&amp;nbsp;users.&lt;/p&gt;
&lt;p&gt;We can do&amp;nbsp;better.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="thanks"&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://twitter.com/nelstrom"&gt;Drew&lt;/a&gt; for asking me to talk and providing
the &lt;tt class="docutils literal"&gt;cw&lt;/tt&gt; example.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://twitter.com/krisajenkins"&gt;Kris&lt;/a&gt; for inspiring me at my first Vim
London meetup with &lt;a class="reference external" href="https://vimeo.com/65250028"&gt;Barebones Vim navigation&lt;/a&gt;.
This showed me so much about Vim that I didn&amp;#8217;t know and also that you &lt;strong&gt;can&lt;/strong&gt;
do a high quality presentation from Vim. (I hope that one day I&amp;#8217;ll be able to
meet your standard&amp;nbsp;Kris).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And thanks to you for&amp;nbsp;reading!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Talk"></category><category term="topic:vim"></category></entry><entry><title>Things I wish I’d known about Django</title><link href="https://jamescooke.info/things-i-wish-id-known-about-django.html" rel="alternate"></link><published>2015-07-18T20:00:00+01:00</published><updated>2015-07-18T20:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2015-07-18:/things-i-wish-id-known-about-django.html</id><summary type="html">&lt;p class="first last"&gt;Django has been my go-to web framework for about three years. But I
wish I&amp;#8217;d started with it sooner. This became a talk which I gave at
the London Django meetup in July this&amp;nbsp;year.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This month at &lt;a class="reference external" href="https://www.meetup.com/The-London-Django-Meetup-Group/events/223297765/"&gt;The London Django meetup&lt;/a&gt; I
gave a talk about some of the things I wish I&amp;#8217;d known about Django (before I&amp;nbsp;started).&lt;/p&gt;
&lt;br&gt;
&lt;script async class="speakerdeck-embed" data-id="6f327318d302437f8ccafb8da4c2cd32" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;
&lt;br&gt;&lt;div class="section" id="thanks"&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;p&gt;My thanks to &lt;a class="reference external" href="https://github.com/zalun"&gt;Piotr&lt;/a&gt;, who I mentioned in the talk.
For years he told me to check out &lt;a class="reference external" href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt;
because &amp;#8220;it does things right&amp;#8221;. Only when I&amp;#8217;d truly broken myself with &lt;span class="caps"&gt;PHP&lt;/span&gt; did
I take his advice and I haven&amp;#8217;t looked&amp;nbsp;back.&lt;/p&gt;
&lt;p&gt;Django opened the door for me&amp;nbsp;to:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt; which has become my backend language of&amp;nbsp;choice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test Driven Development&lt;/strong&gt; which has become a way of&amp;nbsp;life.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="more-power"&gt;
&lt;h2&gt;More&amp;nbsp;Power&lt;/h2&gt;
&lt;p&gt;When I&amp;#8217;m developing for web and using Django, I think it&amp;#8217;s possible for me to
keep more features live with fewer errors than was previously possible for me
with &lt;span class="caps"&gt;PHP&lt;/span&gt; based tools. I put this down to two main&amp;nbsp;things:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The application of tests in my Django projects. I&amp;#8217;m particularly happy with
the test suite provided by Django. Where I&amp;#8217;ve needed to rewire it or patch
it, it&amp;#8217;s responded&amp;nbsp;well.&lt;/li&gt;
&lt;li&gt;The structure that Django provides, without enforcing how projects are&amp;nbsp;built.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="summary"&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;I wish I&amp;#8217;d jumped into Django sooner. If you&amp;#8217;re thinking about transitioning to
it, see what you can do today. I think it pays back in the long&amp;nbsp;run.&lt;/p&gt;
&lt;p&gt;Thanks for&amp;nbsp;reading.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Talk"></category><category term="django"></category></entry><entry><title>A water pouring problem sketched in Python</title><link href="https://jamescooke.info/a-water-pouring-problem-sketched-in-python.html" rel="alternate"></link><published>2015-01-09T10:00:00+00:00</published><updated>2015-01-09T10:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2015-01-09:/a-water-pouring-problem-sketched-in-python.html</id><summary type="html">&lt;p class="first last"&gt;A small Python 3 sketch of a solution to a water pouring&amp;nbsp;problem.&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="the-problem"&gt;
&lt;h2&gt;The&amp;nbsp;problem&lt;/h2&gt;
&lt;p&gt;At the end of last year, I came across the following water pouring problem
because something similar had come up in a friend&amp;#8217;s functional programming&amp;nbsp;interview:&lt;/p&gt;
&lt;blockquote&gt;
&lt;strong&gt;There are three glasses on the table - 3, 5, and 8 oz. The first two are
empty, the last contains 8 oz of water. By pouring water from one glass to
another make at least one of them contain exactly 4 oz of water.&lt;/strong&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Source: A. Bogomolny,&lt;/em&gt; &lt;a class="reference external" href="https://www.cut-the-knot.org/water.shtml"&gt;3 Glasses Puzzle from Interactive Mathematics
Miscellany and Puzzles&lt;/a&gt;, &lt;em&gt;Accessed
09 January&amp;nbsp;2015&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="a-solution-using-search-not-algebra"&gt;
&lt;h2&gt;A solution using search, not&amp;nbsp;algebra&lt;/h2&gt;
&lt;p&gt;At first I started to explore the problem by looking at it algebraically. What
are the differences between each cup? How can those differences be summed
together to give the required remainder of&amp;nbsp;4?&lt;/p&gt;
&lt;p&gt;However this didn&amp;#8217;t yield anything helpful. Instead, I started looking at the
solution states. What do the cups have to look like for the puzzle to be&amp;nbsp;solved?&lt;/p&gt;
&lt;div class="section" id="two-success-states"&gt;
&lt;h3&gt;Two success&amp;nbsp;states&lt;/h3&gt;
&lt;p&gt;There are at least two success states. One where the 5 oz cup contains 4 oz
of water and the other where the 8 oz cup contains 4 oz of water. The other two
cups must contain the remaining 4 oz of water. This is the notation I&amp;#8217;ve used
for these two states, where &lt;tt class="docutils literal"&gt;x + y = 4&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
[&amp;lt;Cup x/3&amp;gt;, &amp;lt;Cup 4/5&amp;gt;, &amp;lt;Cup y/8&amp;gt;]
&lt;/pre&gt;
&lt;p&gt;And:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
[&amp;lt;Cup x/3&amp;gt;, &amp;lt;Cup y/5&amp;gt;, &amp;lt;Cup 4/8&amp;gt;]
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="the-search-problem"&gt;
&lt;h3&gt;The search&amp;nbsp;problem&lt;/h3&gt;
&lt;p&gt;So taking the second state, and assuming that the first cup is full, then the
question&amp;nbsp;becomes:&lt;/p&gt;
&lt;blockquote&gt;
How do we get from the start state to the end state?&lt;/blockquote&gt;
&lt;pre class="literal-block"&gt;
[&amp;lt;Cup 0/3&amp;gt;, &amp;lt;Cup 0/5&amp;gt;, &amp;lt;Cup 8/8&amp;gt;]
...
TODO: Search in here for a path
...
[&amp;lt;Cup 3/3&amp;gt;, &amp;lt;Cup 1/5&amp;gt;, &amp;lt;Cup 4/8&amp;gt;]
&lt;/pre&gt;
&lt;p&gt;This is really helpful. It turns an algebra problem into a search problem.
Computers are good at doing search. We can write code for&amp;nbsp;this.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="a-python-3-sketch"&gt;
&lt;h2&gt;A Python 3&amp;nbsp;sketch&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;ve written some Python to solve this problems using a type of depth first
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Tree_traversal"&gt;Tree Traversal&lt;/a&gt; and tree
generation&amp;nbsp;strategy.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://github.com/jamescooke/water-pouring-python"&gt;code repository is available on GitHub&lt;/a&gt;. The &lt;span class="caps"&gt;README&lt;/span&gt; contains the
installation and operating&amp;nbsp;instructions.&lt;/p&gt;
&lt;p&gt;These are some of the features of the&amp;nbsp;code:&lt;/p&gt;
&lt;div class="section" id="cup-and-game-classes"&gt;
&lt;h3&gt;Cup and Game&amp;nbsp;classes&lt;/h3&gt;
&lt;p&gt;In this code, the &lt;a class="reference external" href="https://github.com/jamescooke/water-pouring-python/blob/master/water/cup.py"&gt;Cup
class&lt;/a&gt;
represents a cup in the problem. Each Cup has a certain &lt;tt class="docutils literal"&gt;capacity&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;contents&lt;/tt&gt;. The benefit of using a Cup class as a data type is that it can
perform checks that is not holding more than its capacity of water or that it&amp;#8217;s
holding a negative capacity of water&amp;nbsp;either.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://github.com/jamescooke/water-pouring-python/blob/master/water/game.py"&gt;Game
class&lt;/a&gt;
is more complex as it represents a single state in the tree. Each Game state
has three main&amp;nbsp;properties:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;cups&lt;/tt&gt; - The three Cups that make up this Game&amp;nbsp;state.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;parent&lt;/tt&gt; - The Game that came before this one in the search. The starting
state will have this as &lt;tt class="docutils literal"&gt;None&lt;/tt&gt;, but all the rest will have a&amp;nbsp;parent.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;children&lt;/tt&gt; - Each Game will have some or no child Game states stored in a
list. These are the valid states that can be made by pouring some or all of
one Cup&amp;#8217;s contents into another Cup in the Game that haven&amp;#8217;t already been
seen during the&amp;nbsp;search.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Game&amp;#8217;s &lt;tt class="docutils literal"&gt;children&lt;/tt&gt; property makes the Game class a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Recursive_data_type"&gt;recursive data
structure&lt;/a&gt; because it can
contain other instances of Games. This opens the door to the recursive search
described&amp;nbsp;below.&lt;/p&gt;
&lt;p&gt;Again using this Game type is really helpful because I&amp;#8217;ve been able to write
tested functions for supporting data type functions like &lt;tt class="docutils literal"&gt;__eq__&lt;/tt&gt; (which
tests if two Game stats are logically the same). The most important function in
Game is &lt;tt class="docutils literal"&gt;is_solvable&lt;/tt&gt; which implements the search&amp;nbsp;function.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="recursive-search"&gt;
&lt;h3&gt;Recursive&amp;nbsp;search&lt;/h3&gt;
&lt;p&gt;As mentioned above, the &lt;tt class="docutils literal"&gt;Game.is_solvable&lt;/tt&gt; function implements the tree
search, so here it is in full, comments&amp;nbsp;removed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_solvable&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="k"&gt;if&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;is_goal&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;print_trace&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="k"&gt;if&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;make_children&lt;/span&gt;&lt;span class="p"&gt;()&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;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;return&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;solvable_child&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are two base cases to this &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Recursion_(computer_science)#Recursive_functions_and_algorithms"&gt;recursive function&lt;/a&gt;.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;self.is_goal()&lt;/tt&gt; : Goal has been reached. This Game contains a Cup that
has 4 oz of water, success, a goal state has been found! Return &lt;tt class="docutils literal"&gt;True&lt;/tt&gt;
and print a trace of how the algorithm got&amp;nbsp;here.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;self.make_children() == 0&lt;/tt&gt; : There are no child states. This Game can not
generate any new states that don&amp;#8217;t exist in the tree already, so this state
is a fail, return &lt;tt class="docutils literal"&gt;False&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When neither of those two base cases are found, then this state is on a
&amp;#8220;success path&amp;#8221; if one of its children &amp;#8220;is solvable&amp;#8221;. The recursive case is that
the &lt;tt class="docutils literal"&gt;Game.solvable_child&lt;/tt&gt; helper function is then used to call
&lt;tt class="docutils literal"&gt;Game.is_solvable&lt;/tt&gt; on each of the child&amp;nbsp;Games.&lt;/p&gt;
&lt;p&gt;Here is the helper function without&amp;nbsp;comments:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;solvable_child&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="ow"&gt;in&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;children&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;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_solvable&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are two &amp;#8220;interesting&amp;#8221; features of this&amp;nbsp;function:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;It operates like a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Short-circuit_evaluation"&gt;short circuited &lt;span class="caps"&gt;OR&lt;/span&gt;&lt;/a&gt; reduction. This
means that as soon as a solvable child is found, it stops searching and
returns &lt;tt class="docutils literal"&gt;True&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;It has been split out from &lt;tt class="docutils literal"&gt;Game.is_solvable&lt;/tt&gt; to assist with unit&amp;nbsp;testing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This short circuiting feature is important. I wasn&amp;#8217;t able to get it to work in
a &lt;tt class="docutils literal"&gt;reduce&lt;/tt&gt; statement on the &lt;tt class="docutils literal"&gt;Game.children&lt;/tt&gt;, so instead I wrote it out
explicitly as a&amp;nbsp;for-loop.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="duplicate-search"&gt;
&lt;h3&gt;Duplicate&amp;nbsp;search&lt;/h3&gt;
&lt;p&gt;When generating new Games by pouring water from Cup to Cup, only new Game
states are added as children of any particular Game. This prevents duplication
of Games and ensures that the search will terminate once all different possible
states have been generated at the very&amp;nbsp;latest.&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;Game.has_game&lt;/tt&gt; function implements this duplicate search using a
recursive depth first tree&amp;nbsp;search.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="as-much-functional-style-as-possible"&gt;
&lt;h3&gt;As much functional style as&amp;nbsp;possible&lt;/h3&gt;
&lt;p&gt;Originally I intended to write this sketch with as much &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Functional_programming"&gt;functional style code&lt;/a&gt; as possible. However,
there were certainly some functions that we not possible to achieve this
without some serious hacking, and so I chose to keep those functions as simple
and testable as&amp;nbsp;possible.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;d love to have the time to come back and construct a similar sketch for this
problem in&amp;nbsp;Haskell.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="possible-improvements-and-follow-up-ideas"&gt;
&lt;h2&gt;Possible improvements and follow up&amp;nbsp;ideas&lt;/h2&gt;
&lt;p&gt;Apart from a fully functional rewrite, there are a couple of ways that I could
see to improve the sketch. Even though it doesn&amp;#8217;t run slowly, there are
certainly some optimisations that could be made, plus some follow up&amp;nbsp;ideas.&lt;/p&gt;
&lt;div class="section" id="save-time-by-checking-cups-contents-when-pouring"&gt;
&lt;h3&gt;Save time by checking Cups contents when&amp;nbsp;pouring&lt;/h3&gt;
&lt;p&gt;When generating child Game states by pouring from one cup to another, the
system does not care if a Cup has water to give or if the recipient is full. It
does the pour and then eliminates the new state because it&amp;#8217;s a duplicate of its&amp;nbsp;parent.&lt;/p&gt;
&lt;p&gt;Instead, time could be saved by improving the pouring function so that pours
only generate new Game states when there is water to give and the destination
cup has space for that&amp;nbsp;water.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="improve-the-network-anti-duplication-search"&gt;
&lt;h3&gt;Improve the network anti-duplication&amp;nbsp;search&lt;/h3&gt;
&lt;p&gt;Searching the existing Game states to ensure that the same state hasn&amp;#8217;t already
been created first runs to the top of the Game tree, then searches&amp;nbsp;downwards.&lt;/p&gt;
&lt;p&gt;Most Game states will be duplicates of a Game that&amp;#8217;s either their parent or one
Game state away from them. This means there&amp;#8217;s an advantage, especially when
running bigger problem searches, to search nearest Games&amp;nbsp;first.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="create-a-goal-variable"&gt;
&lt;h3&gt;Create a &lt;tt class="docutils literal"&gt;goal&lt;/tt&gt; variable&lt;/h3&gt;
&lt;p&gt;The code could be improved to accept a &lt;tt class="docutils literal"&gt;goal&lt;/tt&gt; value for the amount of water
that should be in a Cup for success to be&amp;nbsp;achieved.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="search-for-bigger-solvable-problems"&gt;
&lt;h3&gt;Search for bigger solvable&amp;nbsp;problems&lt;/h3&gt;
&lt;p&gt;Going meta, it would be interesting now to use this code to search for a nice
big complicated water pouring problem. What&amp;#8217;s the largest number of Cups and
steps to success that can be&amp;nbsp;found?&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="related-stuff"&gt;
&lt;h2&gt;Related&amp;nbsp;stuff&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;ve always been fascinated by the power of graph searching as a replacement
for intelligence. In this example, the code has searched all possible Game
states for one that meets the success&amp;nbsp;criteria.&lt;/p&gt;
&lt;p&gt;My first introduction to this idea was via &lt;a class="reference external" href="https://www.theguardian.com/science/2007/jul/10/uk.obituaries1"&gt;Donald Michie&lt;/a&gt;&amp;#8216;s  &lt;span class="caps"&gt;MENACE&lt;/span&gt;
machine. This was a noughts-and-crosses playing machine made from matchboxes.
It used a very simple algorithm, which is effectively a weighted graph, to
&amp;#8220;learn&amp;#8221; to play the game. &lt;a class="reference external" href="https://www.it.uu.se/edu/course/homepage/ai/menace"&gt;Uppsala University has an interesting project
outline for building a code version&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for&amp;nbsp;reading.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="python"></category></entry><entry><title>Django Contexts and get</title><link href="https://jamescooke.info/django-contexts-and-get.html" rel="alternate"></link><published>2014-11-17T20:00:00+00:00</published><updated>2014-11-17T20:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2014-11-17:/django-contexts-and-get.html</id><summary type="html">&lt;p class="first last"&gt;Found that tests on a recent project started breaking for no clear
reason, and it turned out it was because I&amp;#8217;d used &lt;tt class="docutils literal"&gt;get&lt;/tt&gt; to retrieve
values from&amp;nbsp;Contexts.&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="background"&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;If you know me, then you know that I&amp;#8217;m an avid tester. It could even be argued
that I test too extensively as part of my day-to-day development, but that&amp;#8217;s a
post for another&amp;nbsp;day.&lt;/p&gt;
&lt;p&gt;On a recent project, a particular view started failing with the&amp;nbsp;error:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
AttributeError: 'ContextList' object has no attribute 'get'
&lt;/pre&gt;
&lt;p&gt;I wasn&amp;#8217;t happy with just changing the tests to work again, so I dug down into
why they started&amp;nbsp;failing.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tl-dr"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;To get a value from a Context object returned  by the Django Test Client, then
it&amp;#8217;s better to use the &lt;tt class="docutils literal"&gt;[]&lt;/tt&gt; operator than the &lt;tt class="docutils literal"&gt;get&lt;/tt&gt; method.&lt;/p&gt;
&lt;p&gt;For&amp;nbsp;example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# In a test, after doing&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&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;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;home&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# ... then it&amp;#39;s better to use [] to test the context&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Homer&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ...than to use get&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Homer&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="reason"&gt;
&lt;h2&gt;Reason&lt;/h2&gt;
&lt;p&gt;It turns out that the problem was to do with a developer on the project
changing how the template for the view was generated. They changed a view that
was using a single template, to a couple of templates using &lt;a class="reference external" href="https://docs.djangoproject.com/en/1.7/topics/templates/#template-inheritance"&gt;Django&amp;#8217;s template
inheritance&lt;/a&gt;
and the &lt;tt class="docutils literal"&gt;extends&lt;/tt&gt; template&amp;nbsp;tag.&lt;/p&gt;
&lt;p&gt;This then effects how Django&amp;#8217;s test client returns the Context object for&amp;nbsp;inspection.&lt;/p&gt;
&lt;p&gt;To test this I prepared the following&amp;nbsp;test:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.urlresolvers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCase&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get&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;response&lt;/span&gt; &lt;span class="o"&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;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;home&amp;#39;&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Homer&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_operator&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;response&lt;/span&gt; &lt;span class="o"&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;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;home&amp;#39;&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Homer&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The home view was just a simple template&amp;nbsp;renderer:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.shortcuts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&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;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;home.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Homer&amp;#39;&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;div class="section" id="simple-works"&gt;
&lt;h3&gt;Simple&amp;nbsp;works&lt;/h3&gt;
&lt;p&gt;When the &amp;#8216;home.html&amp;#8217; template is a simple template with no inheritance (it can
be completely empty), then both tests&amp;nbsp;pass.&lt;/p&gt;
&lt;p&gt;&lt;span class="quo"&gt;&amp;#8216;&lt;/span&gt;home.html&amp;#8217; template&amp;nbsp;code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello World&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Test&amp;nbsp;run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;./manage.py&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;
Creating&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;database&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;...
..
----------------------------------------------------------------------
Ran&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tests&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.027s

OK
Destroying&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;database&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;...
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="template-inheritance-fails-with-get"&gt;
&lt;h3&gt;Template inheritance fails with &lt;cite&gt;get&lt;/cite&gt;&lt;/h3&gt;
&lt;p&gt;Now adjust &amp;#8216;home.html&amp;#8217; to extend another template &amp;#8216;base.html&amp;#8217; which has
arbitrary&amp;nbsp;contents.&lt;/p&gt;
&lt;p&gt;New &amp;#8216;home.html&amp;#8217; template&amp;nbsp;code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;{% extends &amp;#39;base.html&amp;#39; %}
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello World&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Test&amp;nbsp;run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;./manage.py&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;
Creating&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;database&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;...
E.
&lt;span class="o"&gt;======================================================================&lt;/span&gt;
ERROR:&lt;span class="w"&gt; &lt;/span&gt;test_get&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;mini.tests.Tests&lt;span class="o"&gt;)&lt;/span&gt;
----------------------------------------------------------------------
Traceback&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;most&lt;span class="w"&gt; &lt;/span&gt;recent&lt;span class="w"&gt; &lt;/span&gt;call&lt;span class="w"&gt; &lt;/span&gt;last&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;span class="w"&gt;  &lt;/span&gt;File&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/home/james/active/mini/mmm/mini/tests.py&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;test_get
&lt;span class="w"&gt;      &lt;/span&gt;self.assertEqual&lt;span class="o"&gt;(&lt;/span&gt;response.context.get&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Homer&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
AttributeError:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ContextList&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;object&lt;span class="w"&gt; &lt;/span&gt;has&lt;span class="w"&gt; &lt;/span&gt;no&lt;span class="w"&gt; &lt;/span&gt;attribute&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;get&amp;#39;&lt;/span&gt;

----------------------------------------------------------------------
Ran&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tests&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.029s

FAILED&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
Destroying&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;database&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;...
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So the &lt;tt class="docutils literal"&gt;test_get&lt;/tt&gt; case, which was using &lt;tt class="docutils literal"&gt;get&lt;/tt&gt; failed.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It&amp;#8217;s definitely more robust to be using list access &lt;tt class="docutils literal"&gt;[]&lt;/tt&gt; on Context objects
returned by the Django Test Client where possible when checking values passed
through to&amp;nbsp;templating.&lt;/p&gt;
&lt;p&gt;Thanks for&amp;nbsp;reading.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="python"></category><category term="django"></category></entry><entry><title>Current state of Codeship</title><link href="https://jamescooke.info/current-state-of-codeship.html" rel="alternate"></link><published>2014-10-12T18:00:00+01:00</published><updated>2014-10-12T18:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2014-10-12:/current-state-of-codeship.html</id><summary type="html">&lt;p class="first last"&gt;For the last month I&amp;#8217;ve been using Codeship for Continuous
Integration on my current client project. These are my current
thoughts on this hosted service that works with Github and BitBucket&amp;nbsp;repositories.&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="background"&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;In September I was introduced to &lt;a class="reference external" href="https://www.codeship.io/"&gt;Codeship&lt;/a&gt; at a
presentation by &lt;a class="reference external" href="http://anglepoised.com/"&gt;Paul Love&lt;/a&gt;.  These are some of the
learnings I have from using their hosted service primarily for &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Continuous_integration"&gt;Continuous
Integration&lt;/a&gt; for the
last couple of weeks on a client&amp;nbsp;project.&lt;/p&gt;
&lt;p&gt;This post isn&amp;#8217;t about advantages and disadvantages of &lt;span class="caps"&gt;CI&lt;/span&gt; or &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Continuous_delivery"&gt;Continuous
Delivery&lt;/a&gt;, but more focused
on Codeship&amp;#8217;s offering currently, compared to other &lt;span class="caps"&gt;CI&lt;/span&gt; services I&amp;#8217;ve used over
the last 6 months or&amp;nbsp;so.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tl-dr"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Codeship is relatively new and working hard to stabilise the system while
providing documentation and&amp;nbsp;service.&lt;/p&gt;
&lt;p&gt;The 100 free builds they provide per month, along with the ability to integrate
with &lt;a class="reference external" href="https://bitbucket.org/"&gt;BitBucket&lt;/a&gt; mean that you can run private
projects on &lt;span class="caps"&gt;CI&lt;/span&gt; for free, but watch out for their lack of&amp;nbsp;stability.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="positives"&gt;
&lt;h2&gt;Positives&lt;/h2&gt;
&lt;div class="section" id="good-value"&gt;
&lt;h3&gt;Good&amp;nbsp;value&lt;/h3&gt;
&lt;p&gt;Codeship currently offers &lt;a class="reference external" href="https://www.codeship.io/pricing"&gt;100 builds a month for free&lt;/a&gt; on private repos that include BitBucket and
&lt;a class="reference external" href="https://github.com/"&gt;Github&lt;/a&gt; - this is &lt;strong&gt;&lt;span class="caps"&gt;GOOD&lt;/span&gt;&lt;/strong&gt;. It&amp;#8217;s hard to find hosted &lt;span class="caps"&gt;CI&lt;/span&gt;
systems that will work with non-Github repository&amp;nbsp;hosting.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="good-support"&gt;
&lt;h3&gt;Good&amp;nbsp;support&lt;/h3&gt;
&lt;p&gt;Codeship&amp;#8217;s support team know their&amp;nbsp;stuff.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve had multiple discussions with Codeship&amp;#8217;s support via email and Twitter and
I&amp;#8217;ve found that they get back to you with timely email replies and suggestions
about why things are breaking. Great for a free&amp;nbsp;service!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="good-speed"&gt;
&lt;h3&gt;Good&amp;nbsp;speed&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Builds are &lt;span class="caps"&gt;QUICK&lt;/span&gt;!&lt;/strong&gt; So quick that I thought they weren&amp;#8217;t using a real &lt;span class="caps"&gt;DB&lt;/span&gt; and
wrote a test to prove that they were listening to the settings I&amp;#8217;d pushed
specifically for&amp;nbsp;Codeship.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="negatives"&gt;
&lt;h2&gt;Negatives&lt;/h2&gt;
&lt;p&gt;Unfortunately the good freeness above comes with some&amp;nbsp;disadvantages.&lt;/p&gt;
&lt;div class="section" id="false-negatives"&gt;
&lt;h3&gt;False&amp;nbsp;negatives&lt;/h3&gt;
&lt;p&gt;Some of my builds have received false negatives meaning they have failed when
they shouldn&amp;#8217;t have. This is annoying but manageable and can often be solved by
re-running the&amp;nbsp;build.&lt;/p&gt;
&lt;p&gt;For example, I&amp;#8217;ve had a couple of builds happen on an instance where the
database wasn&amp;#8217;t available when the tests started running, so everything went
&lt;span class="caps"&gt;RED&lt;/span&gt;. Slack then went &lt;span class="caps"&gt;RED&lt;/span&gt;. Client team start&amp;nbsp;worrying.&lt;/p&gt;
&lt;p&gt;Codeship&amp;#8217;s solution for this was &amp;#8220;Add &lt;cite&gt;sleep 3&lt;/cite&gt; to make the tests wait 3
seconds before running&amp;#8221;. This works but why isn&amp;#8217;t Codeship&amp;#8217;s instance build
process checking this before starting the&amp;nbsp;run?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="false-positives"&gt;
&lt;h3&gt;False&amp;nbsp;positives&lt;/h3&gt;
&lt;p&gt;Builds have received false positives. They have passed when they shouldn&amp;#8217;t
have - and this is &lt;strong&gt;far worse&lt;/strong&gt; than a false&amp;nbsp;negative.&lt;/p&gt;
&lt;p&gt;This week a build on Codeship had &lt;cite&gt;git submodule init&lt;/cite&gt; fail, but the build
didn&amp;#8217;t go red. The same condition happened last week and the build &lt;em&gt;did&lt;/em&gt; fail.
While I&amp;#8217;m writing this, Codeship are looking into the&amp;nbsp;problem.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="scarce-documentation"&gt;
&lt;h3&gt;Scarce&amp;nbsp;documentation&lt;/h3&gt;
&lt;p&gt;It&amp;#8217;s not very clear how to work with teams, amongst other topics. Team members
can&amp;#8217;t see how many remaining free builds are available each month so they can&amp;#8217;t
see if they&amp;#8217;re using up the allowance of free builds quickly or&amp;nbsp;not.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="artefacts-need-special-attention"&gt;
&lt;h3&gt;Artefacts need special&amp;nbsp;attention&lt;/h3&gt;
&lt;p&gt;It&amp;#8217;s hard to get build artefacts off Codeship&amp;#8217;s build instances since the
system burns the instance once the build is complete regardless of result. This
means if you don&amp;#8217;t push artefacts off the server yourself with a script,
they&amp;#8217;re&amp;nbsp;gone.&lt;/p&gt;
&lt;p&gt;Compare with &lt;a class="reference external" href="https://circleci.com/"&gt;CircleCI&lt;/a&gt; - they &lt;a class="reference external" href="https://circleci.com/docs/build-artifacts"&gt;keep build artefacts
in a special folder&lt;/a&gt; available for
further processing, downloading, or access after the build is&amp;nbsp;complete.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="test-commands-are-in-codeship-not-repo"&gt;
&lt;h3&gt;Test commands are in Codeship not&amp;nbsp;repo&lt;/h3&gt;
&lt;p&gt;A minor gripe I have with the Codeship system is that the test setup and run
commands are stored in the configuration for the project on their site. With
Travis for example, the server runs the commands it finds in &lt;tt class="docutils literal"&gt;.travis.yml&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;The benefit of putting the test commands in the repo is that they can be easily
coordinated with git commits - if you want to change how something is run, you
can do it all in the code, commit and&amp;nbsp;push.&lt;/p&gt;
&lt;p&gt;The Codeship way means copy-and-pasting run commands up onto their website and
then pushing new code to be tested with those settings. It makes it hard to
prove which branch was run with which settings, or if things were changed to
make builds&amp;nbsp;pass.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="summary"&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Most concerning are the false negatives and positives on build. It&amp;#8217;s very
important that a dev team can trust their &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; service 100%. These are the
results for the current Django project I&amp;#8217;m building on&amp;nbsp;Codeship:&lt;/p&gt;
&lt;script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"&gt;&lt;/script&gt;&lt;div class="math"&gt;
\begin{equation*}
\frac {1\ False\ Negative + 1\ False\ Positive}
      {37\ Total\ Builds} = 5\%\ Incorrect\ Results
\end{equation*}
&lt;/div&gt;
&lt;p&gt;These are the false builds that I&amp;#8217;m aware of - there might be some that have
gone unnoticed. For me, a figure of 95% success makes Codeship &amp;#8216;just&amp;#8217; stable
enough for work, but it&amp;#8217;s &lt;strong&gt;free and works with Bitbucket&lt;/strong&gt; and that&amp;#8217;s a
massive positive. I would be disappointed if we were on a paid account and
receiving the same&amp;nbsp;instability.&lt;/p&gt;
&lt;p&gt;For the future, if they can stabilise the builds and document the system then
they could become the go-to &lt;span class="caps"&gt;CI&lt;/span&gt; service for teams on&amp;nbsp;Bitbucket.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="project-background"&gt;
&lt;h2&gt;Project&amp;nbsp;Background&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;m running 125 tests in around 15s on a Python (2.7) Django (1.7) project that
makes integrated &lt;span class="caps"&gt;API&lt;/span&gt; calls to Dropbox, sits on top of MySQL, runs coverage and&amp;nbsp;flake8.&lt;/p&gt;
&lt;p&gt;Thanks for&amp;nbsp;reading.&lt;/p&gt;
&lt;/div&gt;
&lt;script type='text/javascript'&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (false) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Code"></category><category term="topic:testing"></category></entry><entry><title>Flat designs to website specification - a checklist</title><link href="https://jamescooke.info/flat-designs-to-website-specification-a-checklist.html" rel="alternate"></link><published>2014-03-29T16:00:00+00:00</published><updated>2014-03-29T16:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2014-03-29:/flat-designs-to-website-specification-a-checklist.html</id><summary type="html">&lt;p class="first last"&gt;Agencies often provide flat designs to web developers as a
specification, but these only scratch the surface of a true website
functional specification. This is a check-list of features which can
be fleshed out to start the journey towards a full functional&amp;nbsp;spec.&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="isolated-design-has-problems"&gt;
&lt;h2&gt;Isolated design has&amp;nbsp;problems&lt;/h2&gt;
&lt;p&gt;Since many web projects are approached from the visual aspect, often the &lt;em&gt;seen&lt;/em&gt;
elements are designed first. This can be fine if it&amp;#8217;s integrated with well
thought out feedback from developers, but can create more work for the project
if it&amp;#8217;s completed and signed off in&amp;nbsp;isolation.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;Results in more development work&lt;/strong&gt; when a design might have some serious
development implications when compared to a slightly different solution that
could also have been&amp;nbsp;acceptable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Work estimates will be less accurate&lt;/strong&gt; since the flat designs just start to
scratch the surface of the development required - they are not a full&amp;nbsp;specification.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Can lead to frustration within the design team&lt;/strong&gt; as they are asked to
redesign elements during the production process that they thought were
already signed&amp;nbsp;off.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Potentially leads to uncertainty with the client&lt;/strong&gt; if &amp;#8216;signed off&amp;#8217; designs
are presented again for sign off with changes that were not previously&amp;nbsp;foreseen.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="how-to-use-this-checklist"&gt;
&lt;h2&gt;How to use this&amp;nbsp;checklist&lt;/h2&gt;
&lt;p&gt;Next time you see a software project being discussed just via flat designs, let
your alarm bells ring. Open up conversation about the features on this list to
break down the isolation that the design team is operating&amp;nbsp;in.&lt;/p&gt;
&lt;div class="section" id="for-developers"&gt;
&lt;h3&gt;For&amp;nbsp;Developers&lt;/h3&gt;
&lt;p&gt;This basic list can start a journey of specification exploration. Start to ask
questions about all these features before you agree on a specification or
timeline since some of these items can become heavy or project&amp;nbsp;effecting.&lt;/p&gt;
&lt;p&gt;This list is very back-end focused, but hopefully can be helpful for
front-enders&amp;nbsp;too.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="for-website-owners"&gt;
&lt;h3&gt;For Website&amp;nbsp;owners&lt;/h3&gt;
&lt;p&gt;If you&amp;#8217;re being asked to sign off a project on flat designs alone, then it
might be beneficial to check that the team you&amp;#8217;re working with have these
aspects of the development on their radar. They might not have the answer to
them yet, but should have a plan to find&amp;nbsp;them.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="for-designers"&gt;
&lt;h3&gt;For&amp;nbsp;Designers&lt;/h3&gt;
&lt;p&gt;You are often stuck in the middle of the process. Continue to involve your
development team, they will be able to point out things that will require more
effort to build before your client signs off the visuals, saving the project
work in the long&amp;nbsp;term.&lt;/p&gt;
&lt;p&gt;You can help to ensure you get good value feedback by asking them questions
about items on this checklist - ensure they&amp;#8217;re not lulled into a false sense of
&amp;#8220;it&amp;#8217;ll be easy&amp;#8221; by your fantastic design&amp;nbsp;work!&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="the-checklist"&gt;
&lt;h2&gt;The&amp;nbsp;Checklist&lt;/h2&gt;
&lt;p&gt;The following details are often missing from flat web designs, but should be
provided in a full&amp;nbsp;specification.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;Page titles&lt;/strong&gt; - Standard and often missed by designers that use a generic
image of a web browser frame to wrap their designs. Loved by content managers
and search engines. Do they have a format and can be auto-generated? Does the
content database need an extra&amp;nbsp;field?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hidden &lt;span class="caps"&gt;HTML&lt;/span&gt; data&lt;/strong&gt; - What about all the data in the &lt;span class="caps"&gt;HTML&lt;/span&gt; &lt;cite&gt;&amp;lt;head&amp;gt;&lt;/cite&gt;? Meta
description, icon, Facebook data? Also for each image what will be the &lt;cite&gt;&amp;lt;img&amp;gt;
alt&lt;/cite&gt; tag?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;URLs&lt;/strong&gt; - Also missed by designers when using generic browser frames, what
are the URLs of each page being shown? Remember to check the URLs of pages
that have&amp;nbsp;pagination.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data field requirements&lt;/strong&gt; - Designs often show the &amp;#8216;best case&amp;#8217; for content,
but ensuring good data is entering the database is essential for a successful
web project. What are the limits - shortest names?  Longest ones?  Are spaces
allowed? Should content be trimmed? Emails should be validated, but on what
level? Semantically, or with a request to a &lt;span class="caps"&gt;DNS&lt;/span&gt; or mail&amp;nbsp;server?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Form fields, validation and error messages&lt;/strong&gt; - If you&amp;#8217;re looking at a
design that shows a web form, are there are error messages in the design?
Expanding on the requirements for the data above, what will happen to the
form when invalid data is entered? How will fields be flagged for errors?
Which data elements will be sent back to the form and which will be cleaned
out? Are there any fields (like address) that need to be&amp;nbsp;localised?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User sign up requirements&lt;/strong&gt; - If the site will be accepting registrations,
what will users need to provide to register? Email address? User name (how
long)? Are there any blocked words in user names (like &amp;#8216;admin&amp;#8217;, the project
brand name or profanities)? Password (how long)? Are there any password
strength requirements? How will users reset their&amp;nbsp;passwords?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transactional emails&lt;/strong&gt; - What emails will need to be sent by the system?
What is their content and design? Can users manage these&amp;nbsp;notifications?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt; - Will the site have any functions that will protect the data of
its users? For example, will the user login page throttle access on multiple
incorrect passwords? Will there be &amp;#8216;https&amp;#8217;&amp;nbsp;required?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Private data&lt;/strong&gt; - Since flat designs will show the public end user view of a
project, what data is hidden from the user but essential for the project?
Latest login dates? Number of logins? &lt;span class="caps"&gt;IP&lt;/span&gt; address of last visit or
registration? Banned, active, subscriber flags? Active or dormant flags on&amp;nbsp;content?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Click and hover behaviours&lt;/strong&gt; - What will happen when elements like links
are hovered? Are there any menus functions that are hidden behind clicks? Are
there any titles to be shown when the user hovers an&amp;nbsp;item?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Error pages&lt;/strong&gt; - What is the design for the 404 page? What about 503 and any
other error pages? Will there need to be a holding page when the site is
being updated and is&amp;nbsp;offline?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Analytics configuration&lt;/strong&gt; - How should the analytics be configured to track
behaviours on the site? Is it required? Will a simple configuration suffice
or will there need to be funnels and or events configured? Analytics can be
complex enough to require as much work as the original build out of a
project, so ensuring that the specification is defined and covers the
business needs early is a&amp;nbsp;benefit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Translation requirements&lt;/strong&gt; - Will any of the content in the designs require
translation? This also effects the items mentioned above in the checklist.
Remember that any image elements that have been prepared that contain text
will need to be generated in each target language - will each of those
translated texts fit within the&amp;nbsp;design?&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="feedback-and-thoughts"&gt;
&lt;h2&gt;Feedback and&amp;nbsp;thoughts&lt;/h2&gt;
&lt;p&gt;I hope that the list above helps someone who&amp;#8217;s working through the design of a
site. Any time that I&amp;#8217;ve worked on a project where developers and stakeholders
have been involved in the design stages early on have always been&amp;nbsp;successful.&lt;/p&gt;
&lt;p&gt;If there are items you think should be added you can contribute on &lt;a class="reference external" href="https://github.com/jamescooke/blog/blob/master/content/1403-spec-checklist.rst"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for&amp;nbsp;reading.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="work"></category></entry><entry><title>Seinfeld method and coding</title><link href="https://jamescooke.info/seinfeld-method-and-coding.html" rel="alternate"></link><published>2014-01-31T16:00:00+00:00</published><updated>2014-01-31T16:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2014-01-31:/seinfeld-method-and-coding.html</id><summary type="html">&lt;p class="first last"&gt;A presentation I made at ustwo&amp;#8217;s Tech Thursday about Seinfeld
Technique and how it can help coders to get their personal projects&amp;nbsp;moving.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This presentation focuses on using the Seinfeld method &amp;#8220;Don&amp;#8217;t break the chain&amp;#8221;
to get going with personal projects. Those could be all types of things from
learning the piano, a new programming language or achieving a personal&amp;nbsp;goal.&lt;/p&gt;
&lt;p&gt;I believe that the Seinfeld method can help break down some of the blockers
that we experience when procrastinating, by forcing us to refactor large,
unmeasurable and daunting tasks into mini-tasks which are the opposite -
achievable, simple and regular. It also helps us to refocus on continual small
steps rather than the big&amp;nbsp;picture.&lt;/p&gt;
&lt;br&gt;
&lt;script async class="speakerdeck-embed" data-id="bdabe0106c8c013162a91ed72f379050" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;
&lt;br&gt;&lt;p&gt;I&amp;#8217;m especially keen on how the regular measurement of time spent on a project
can give insight, and so I&amp;#8217;ve started combining Seinfeld with Pomodoro&amp;nbsp;Technique.&lt;/p&gt;
&lt;p&gt;The most important thing is to &amp;#8220;make it work for you&amp;#8221; - there are all sorts of
ways that these techniques can be used to push a project forward or help you to
achieve your&amp;nbsp;goal.&lt;/p&gt;
&lt;p&gt;Hope that&amp;#8217;s&amp;nbsp;helpful!&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a class="reference external" href="https://www.ustwo.com/"&gt;Victor at ustwo London&lt;/a&gt; for asking me to talk
at their Tech&amp;nbsp;Thursday.&lt;/p&gt;
&lt;p&gt;Read more on &lt;a class="reference external" href="https://lifehacker.com/281626/jerry-seinfelds-productivity-secret"&gt;Seinfeld Technique&lt;/a&gt; and
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Pomodoro_Technique"&gt;Pomodoro Technique&lt;/a&gt;. I&amp;#8217;m
currently using &lt;a class="reference external" href="http://tomatoi.st/"&gt;tomatoist&lt;/a&gt; as my online pomodoro&amp;nbsp;timer.&lt;/p&gt;
&lt;p&gt;Update 11/05/2018: I&amp;#8217;ve been using a local install of this &lt;a class="reference external" href="https://github.com/bastibe/pomodoro-timer"&gt;&lt;span class="caps"&gt;HTML&lt;/span&gt; Pomodoro timer&lt;/a&gt; for the last couple of years now.
Output the &lt;span class="caps"&gt;JSON&lt;/span&gt; report that it creates into a couple of processors that count
my hours and measure my efficiency. This means that I can ensure that I&amp;#8217;m
working the right number of hours per week, but also taking a good number of&amp;nbsp;breaks.&lt;/p&gt;
</content><category term="Talk"></category><category term="work"></category></entry><entry><title>Python generators and yield</title><link href="https://jamescooke.info/python-generators-and-yield.html" rel="alternate"></link><published>2013-12-14T16:00:00+00:00</published><updated>2013-12-14T16:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-12-14:/python-generators-and-yield.html</id><summary type="html">&lt;p class="first last"&gt;Notes to myself on generators and how to create them with generator
expressions and the yield&amp;nbsp;statement.&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="it-started-with-an-interview"&gt;
&lt;h2&gt;It started with an&amp;nbsp;interview&lt;/h2&gt;
&lt;p&gt;Last week in an interview for a Django developer job, I was&amp;nbsp;asked:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt; &lt;span class="o"&gt;=&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="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;xrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;!--  --&gt;
&lt;blockquote&gt;
What is the type of thing?&lt;/blockquote&gt;
&lt;p&gt;Although I was able to identify that the type is dependent on the &lt;tt class="docutils literal"&gt;()&lt;/tt&gt; around
the list-comprehension-like-construction, I didn&amp;#8217;t know the exact type that
&lt;tt class="docutils literal"&gt;thing&lt;/tt&gt; would&amp;nbsp;be.&lt;/p&gt;
&lt;p&gt;The answer is a &lt;strong&gt;generator&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This post shows some of the functionalities of generators and how they can
be used in Python control&amp;nbsp;flow.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="generator-expressions"&gt;
&lt;h2&gt;Generator&amp;nbsp;expressions&lt;/h2&gt;
&lt;p&gt;Generators can be created with generator expressions. A generator expression is
a bit like a list comprehension. List Comprehension uses square brackets
&lt;tt class="docutils literal"&gt;[]&lt;/tt&gt;. In&amp;nbsp;Python&amp;#8230;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&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="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&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="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;pre class="literal-block"&gt;
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
&lt;/pre&gt;
&lt;p&gt;A generator expression is a shortcut that allows generators to be created
easily with a similar syntax - this time it&amp;#8217;s using parentheses &lt;tt class="docutils literal"&gt;()&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&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="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&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="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;generator object &amp;lt;genexpr&amp;gt; at 0x2fa5eb0&amp;gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="generators-are-iterators"&gt;
&lt;h2&gt;Generators are&amp;nbsp;iterators&lt;/h2&gt;
&lt;p&gt;Generators &amp;#8220;provide a convenient way to implement the iterator&amp;nbsp;protocol&amp;#8221;.&lt;/p&gt;
&lt;p&gt;In Python, an &lt;a class="reference external" href="https://docs.python.org/2.7/library/stdtypes.html#typeiter"&gt;iterator&lt;/a&gt; provides two key
functions, &lt;cite&gt;__iter__&lt;/cite&gt; and &lt;cite&gt;next&lt;/cite&gt;, so a generator itself must provide these two&amp;nbsp;functions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_gen&lt;/span&gt; &lt;span class="o"&gt;=&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="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&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="mi"&gt;10&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;my_gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__iter__&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;genexpr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x293c3c0&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;__iter__&lt;/tt&gt; is there and returns the generator, now for &lt;tt class="docutils literal"&gt;next&lt;/tt&gt;&amp;#8230;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Therefore &lt;tt class="docutils literal"&gt;next&lt;/tt&gt; works. We can keep hitting&amp;nbsp;until&amp;#8230;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;81&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;---------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="ne"&gt;StopIteration&lt;/span&gt;                             &lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ipython&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;b28d59f370d8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;----&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;zzz&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="ne"&gt;StopIteration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A &lt;tt class="docutils literal"&gt;StopIteration&lt;/tt&gt; is raised - so the generator does everything we&amp;#8217;d expect it
to by the iterator&amp;nbsp;protocol.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="building-a-generator-with-yield"&gt;
&lt;h2&gt;Building a generator with&amp;nbsp;yield&lt;/h2&gt;
&lt;p&gt;Although it&amp;#8217;s not clear from the example above, a generator is able to
relinquish control and return a value - while saving its state. It then allows
the control to pass back to the structure that called it, until it&amp;#8217;s called
again, picking up where it left&amp;nbsp;off.&lt;/p&gt;
&lt;p&gt;This allows for loops over sets of values to be programmed, without the full
list of values being calculated first. A generator can be used so that &lt;cite&gt;next&lt;/cite&gt;
is called before each iteration&amp;nbsp;required.&lt;/p&gt;
&lt;p&gt;In this way, only the values required for each iteration need to be&amp;nbsp;computed.&lt;/p&gt;
&lt;div class="section" id="the-yield-keyword-simple-example"&gt;
&lt;h3&gt;The yield keyword - simple&amp;nbsp;example&lt;/h3&gt;
&lt;p&gt;Adding &lt;tt class="docutils literal"&gt;yield&lt;/tt&gt; to a function allows for generators to be constructed&amp;nbsp;&amp;#8216;manually&amp;#8217;.&lt;/p&gt;
&lt;p&gt;At its very simplest, a function could be written just to generate a single
value. However, to show that a generator can return to its previous state when
called again, let&amp;#8217;s make one with two values. For&amp;nbsp;example&amp;#8230;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;two_things&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;hi&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we can make an instance of the&amp;nbsp;generator.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_things&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;two_things&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;my_things&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;two_things&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x31d0960&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And we can ask for next&amp;nbsp;value.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_things&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now when we call &lt;tt class="docutils literal"&gt;next&lt;/tt&gt; again, our generator continues from the state of the
last&amp;nbsp;yield.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_things&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;hi&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So you see how different values can be returned, one after the&amp;nbsp;other.&lt;/p&gt;
&lt;p&gt;And after that second thing, the generator now raises a &lt;tt class="docutils literal"&gt;StopIteration&lt;/tt&gt;, since
it has no further values to&amp;nbsp;return.&lt;/p&gt;
&lt;p&gt;Since a generator implements the iterator protocol, it can be used in a &lt;cite&gt;for&lt;/cite&gt;
statement and therefore in a list comprehension. This makes for a convenient
way to check the values of a limited generator like this&amp;nbsp;one.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;two_things&lt;/span&gt;&lt;span class="p"&gt;()]&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="s1"&gt;&amp;#39;hi&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="more-complex-example-with-yield"&gt;
&lt;h3&gt;More complex example with&amp;nbsp;yield&lt;/h3&gt;
&lt;p&gt;So let&amp;#8217;s write Fibonacci as a generator. I&amp;#8217;m going to start with doctests to
create the definition of the function, then put the code at the&amp;nbsp;end.&lt;/p&gt;
&lt;p&gt;What I like about the doctests in this example is that in 3 &lt;tt class="docutils literal"&gt;fib&lt;/tt&gt; is tested
with &lt;tt class="docutils literal"&gt;next&lt;/tt&gt;, but in 4 it&amp;#8217;s tested using a list&amp;nbsp;comprehension.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="sd"&gt;    1.  Creates a generator&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;gt;&amp;gt;&amp;gt; type(fib(0))&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;lt;type &amp;#39;generator&amp;#39;&amp;gt;&lt;/span&gt;

&lt;span class="sd"&gt;    2.  fib(0) just generates 0th value (1)&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;gt;&amp;gt;&amp;gt; zero_fib = fib(0)&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;gt;&amp;gt;&amp;gt; zero_fib.next()&lt;/span&gt;
&lt;span class="sd"&gt;    1&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;gt;&amp;gt;&amp;gt; zero_fib.next()&lt;/span&gt;
&lt;span class="sd"&gt;    Traceback (most recent call last):&lt;/span&gt;
&lt;span class="sd"&gt;    ...&lt;/span&gt;
&lt;span class="sd"&gt;    StopIteration&lt;/span&gt;

&lt;span class="sd"&gt;    3.  fib(1) creates a generator that creates 0th (1) and 1st (1) values of&lt;/span&gt;
&lt;span class="sd"&gt;        fib seq&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;gt;&amp;gt;&amp;gt; one_fib = fib(1)&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;gt;&amp;gt;&amp;gt; one_fib.next()&lt;/span&gt;
&lt;span class="sd"&gt;    1&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;gt;&amp;gt;&amp;gt; one_fib.next()&lt;/span&gt;
&lt;span class="sd"&gt;    1&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;gt;&amp;gt;&amp;gt; one_fib.next()&lt;/span&gt;
&lt;span class="sd"&gt;    Traceback (most recent call last):&lt;/span&gt;
&lt;span class="sd"&gt;    ...&lt;/span&gt;
&lt;span class="sd"&gt;    StopIteration&lt;/span&gt;

&lt;span class="sd"&gt;    4.  fib(10) generates the first 10 fibonacci numbers&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;gt;&amp;gt;&amp;gt; [x for x in fib(10)]&lt;/span&gt;
&lt;span class="sd"&gt;    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&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;0&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&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;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That&amp;#8217;s all - have fun with&amp;nbsp;generators!&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="language:python"></category></entry><entry><title>Git: To squash or not to squash?</title><link href="https://jamescooke.info/git-to-squash-or-not-to-squash.html" rel="alternate"></link><published>2013-11-19T11:00:00+00:00</published><updated>2013-11-19T11:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-11-19:/git-to-squash-or-not-to-squash.html</id><summary type="html">&lt;p class="first last"&gt;Should detailed history be kept for development features, if so,&amp;nbsp;where?&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="it-started-with-a-tweet"&gt;
&lt;h2&gt;It started with a&amp;nbsp;Tweet&lt;/h2&gt;
&lt;p&gt;Over the weekend I spotted a tweet from &lt;a class="reference external" href="https://oli.me.uk/"&gt;Oliver&lt;/a&gt;&amp;#8230;&lt;/p&gt;
&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p&gt;To squash features into develop, or not to squash features into&amp;nbsp;develop?&lt;/p&gt;&amp;mdash; Oliver Caldwell (@OliverCaldwell) &lt;a href="https://twitter.com/OliverCaldwell/statuses/401299558887485440"&gt;November 15, 2013&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;&lt;p&gt;And I jumped straight in&amp;nbsp;with&amp;#8230;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a class="reference external" href="https://twitter.com/OliverCaldwell"&gt;&amp;#64;OliverCaldwell&lt;/a&gt; Squash, but keep
detailed commit messages. Unless you have a particular use-case / reason
not&amp;nbsp;to.&lt;/p&gt;
&lt;p&gt;November 17,&amp;nbsp;2013&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then, as part of our following conversation, I drew a&amp;nbsp;picture:&lt;/p&gt;
&lt;img alt="Git squashing, feature versus history" src="https://jamescooke.info/images/git.jpg" /&gt;
&lt;!--  --&gt;
&lt;blockquote&gt;
&lt;p&gt;This is how I see it. Better to keep the direct route rather than the &amp;#8220;how we got&amp;nbsp;here&amp;#8221;.&lt;/p&gt;
&lt;p&gt;November 18,&amp;nbsp;2013&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But&amp;#8230;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="it-s-about-more-than-just-squashing"&gt;
&lt;h2&gt;It&amp;#8217;s about more than just&amp;nbsp;squashing&lt;/h2&gt;
&lt;p&gt;What I realised while writing this post and experimenting with &lt;cite&gt;git&lt;/cite&gt; is that
the issue is not as simple as &amp;#8220;Squash? Yes /&amp;nbsp;No?&amp;#8221;&lt;/p&gt;
&lt;p&gt;Variables to consider&amp;nbsp;include:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;How you record your commit messages on your squashed commit.&lt;/strong&gt; This effects
the impact of history loss - good commit messages and or external ticketing /
dev tracking mean it&amp;#8217;s less&amp;nbsp;important.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Whether you push your feature branches for other developers, or between your
dev boxes, to share.&lt;/strong&gt; Do you need to keep the shared history between&amp;nbsp;machines?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The velocity of your project.&lt;/strong&gt; How long do you need to keep history for?
Do bugs show up&amp;nbsp;regularly?&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="tl-dr-simple-project-squash-yes"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt; Simple project. Squash =&amp;nbsp;Yes&lt;/h2&gt;
&lt;p&gt;For a simple project with no sharing between devs required and regular
releases, then squashing features seems like a good idea if&amp;nbsp;you:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Keep detailed commit messages when you&amp;nbsp;squash.&lt;/li&gt;
&lt;li&gt;Use &lt;cite&gt;git rebase&lt;/cite&gt; to squash your features&amp;#8217; commits into a candidate branch and
merge that in to &lt;cite&gt;dev&lt;/cite&gt; or &lt;cite&gt;master&lt;/cite&gt; depending on your &lt;span class="caps"&gt;SCM&lt;/span&gt;&amp;nbsp;strategy.&lt;/li&gt;
&lt;li&gt;Only push your squashed features to keep &lt;cite&gt;origin&lt;/cite&gt; clean and easy to&amp;nbsp;understand.&lt;/li&gt;
&lt;li&gt;Keep your feature branches if you want. But, if you delete them &lt;cite&gt;git&lt;/cite&gt; will
keep your commits in the reflog for 30 days by&amp;nbsp;default.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="keeping-a-detailed-history"&gt;
&lt;h2&gt;Keeping a detailed&amp;nbsp;history&lt;/h2&gt;
&lt;p&gt;One of the issues that Oliver raised was about losing&amp;nbsp;history.&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-conversation="none" lang="en"&gt;&lt;p&gt;&lt;a href="https://twitter.com/jamesfublo"&gt;@jamesfublo&lt;/a&gt; I suppose so. Squashing just feels like you&amp;#39;re killing off that fine grained history, like when was that two line change&amp;nbsp;made.&lt;/p&gt;&amp;mdash; Oliver Caldwell (@OliverCaldwell) &lt;a href="https://twitter.com/OliverCaldwell/statuses/402394094111977472"&gt;November 18, 2013&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;&lt;p&gt;So, since I advocate squashing and branch deletion, I&amp;#8217;m therefore suggesting
that the &lt;strong&gt;reflog is used to recover detailed history in the local repository&lt;/strong&gt;
if&amp;nbsp;required.&lt;/p&gt;
&lt;p&gt;So let&amp;#8217;s explore how much history is actually&amp;nbsp;kept&amp;#8230;&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://git-scm.com/docs/git-reflog"&gt;From the docs&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
Reflog is a mechanism to record when the tip of branches are updated.&lt;/blockquote&gt;
&lt;p&gt;This means&amp;nbsp;that&amp;#8230;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Every commit that every branch in your local repostitory has ever pointed to
is kept in the&amp;nbsp;reflog.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And this even includes branch&amp;nbsp;switching&amp;#8230;&lt;/p&gt;
&lt;blockquote&gt;
&lt;span class="caps"&gt;HEAD&lt;/span&gt; reflog records branch switching as well.&lt;/blockquote&gt;
&lt;p&gt;Sounds very warm and cozy, &lt;strong&gt;&lt;span class="caps"&gt;BUT&lt;/span&gt;&lt;/strong&gt; there are conditions, so let&amp;#8217;s do a
practical experiment with a test&amp;nbsp;repository.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="experiment-squashing-with-rebase-and-keeping-history"&gt;
&lt;h2&gt;Experiment: Squashing with rebase and keeping&amp;nbsp;history&lt;/h2&gt;
&lt;p&gt;Make a repository with an initial&amp;nbsp;commit.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;init
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create a &lt;cite&gt;&lt;span class="caps"&gt;README&lt;/span&gt;.md&lt;/cite&gt; file and put a line of text into it and commit - this
commit is called&amp;nbsp;A.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;README.md
First&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;readme&lt;span class="w"&gt; &lt;/span&gt;file
^C
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;README.md
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;commit
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Current &lt;cite&gt;git&lt;/cite&gt; tree&amp;nbsp;status:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
A   &amp;lt;-master
&lt;/pre&gt;
&lt;div class="section" id="work-on-feature"&gt;
&lt;h3&gt;Work on&amp;nbsp;feature&lt;/h3&gt;
&lt;p&gt;In a new branch, we create a &lt;em&gt;feature&lt;/em&gt; to update the &lt;span class="caps"&gt;README&lt;/span&gt; with two new lines
and to delete the first&amp;nbsp;line.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;-b&lt;span class="w"&gt; &lt;/span&gt;feature-a

&lt;span class="c1"&gt;# First feature commit (B)&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;README.md
Add&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;second&lt;span class="w"&gt; &lt;/span&gt;line
^C
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;README.md
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;commit

&lt;span class="c1"&gt;# Second feature commit (C)&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;README.md
Add&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;third&lt;span class="w"&gt; &lt;/span&gt;line
^C
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;README.md
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;commit

&lt;span class="c1"&gt;# Third feature commit (D)&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;vim&lt;span class="w"&gt; &lt;/span&gt;README.md
&lt;span class="c1"&gt;# Remove first line and save&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;README.md
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;commit
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Current &lt;cite&gt;git&lt;/cite&gt; tree&amp;nbsp;status:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
A   &amp;lt;-master
 \
  B--C--D   &amp;lt;-feature-a
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="check-progress-in-reflog"&gt;
&lt;h3&gt;Check progress in&amp;nbsp;reflog&lt;/h3&gt;
&lt;p&gt;Checkout &lt;cite&gt;master&lt;/cite&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;master
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let&amp;#8217;s check the&amp;nbsp;reflog.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;reflog
&lt;/pre&gt;&lt;/div&gt;
&lt;pre class="literal-block"&gt;
8e48d1d HEAD&amp;#64;{0}: checkout: moving from feature-a to master
262057a HEAD&amp;#64;{1}: commit: D: Remove first line
9efbf73 HEAD&amp;#64;{2}: commit: C: Add a third line
f2503d5 HEAD&amp;#64;{3}: commit: B: Add a second line
8e48d1d HEAD&amp;#64;{4}: checkout: moving from master to feature-a
8e48d1d HEAD&amp;#64;{5}: commit (initial): Make readme
&lt;/pre&gt;
&lt;p&gt;Newest stuff pops out&amp;nbsp;first:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;cite&gt;&lt;span class="caps"&gt;HEAD&lt;/span&gt;&amp;#64;{0}&lt;/cite&gt; - Checkout from &lt;cite&gt;feature-a&lt;/cite&gt; to &lt;cite&gt;master&lt;/cite&gt; is&amp;nbsp;recorded.&lt;/li&gt;
&lt;li&gt;&lt;cite&gt;&lt;span class="caps"&gt;HEAD&lt;/span&gt;&amp;#64;{1}&lt;/cite&gt; to &lt;cite&gt;&lt;span class="caps"&gt;HEAD&lt;/span&gt;&amp;#64;{3}&lt;/cite&gt; - our &lt;cite&gt;feature-a&lt;/cite&gt; commits (D, C and&amp;nbsp;B).&lt;/li&gt;
&lt;li&gt;&lt;cite&gt;&lt;span class="caps"&gt;HEAD&lt;/span&gt;&amp;#64;{4}&lt;/cite&gt; - Checkout of the &lt;cite&gt;feature-a&lt;/cite&gt; branch.&lt;/li&gt;
&lt;li&gt;&lt;cite&gt;&lt;span class="caps"&gt;HEAD&lt;/span&gt;&amp;#64;{5}&lt;/cite&gt; - Initial&amp;nbsp;commit.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="squash-commits-into-candidate-branch"&gt;
&lt;h3&gt;Squash commits into candidate&amp;nbsp;branch&lt;/h3&gt;
&lt;p&gt;&lt;cite&gt;feature-a&lt;/cite&gt; is ready to bring into &lt;cite&gt;master&lt;/cite&gt;. Let&amp;#8217;s first cleanup our history by
doing an interactive rebase. We use a candidate branch for this work because
it&amp;#8217;s a nice safety net which can help with&amp;nbsp;testing.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;feature-a
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;-b&lt;span class="w"&gt; &lt;/span&gt;feature-a-candidate
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Current &lt;cite&gt;git&lt;/cite&gt; tree&amp;nbsp;status:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
A   &amp;lt;-master
 \
  B--C--D   &amp;lt;-feature-a &amp;lt;-feature-a-candidate
&lt;/pre&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;rebase&lt;span class="w"&gt; &lt;/span&gt;--interactive&lt;span class="w"&gt; &lt;/span&gt;master
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let&amp;#8217;s squash our three commits into&amp;nbsp;one.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
pick f2503d5 B: Add a second line
squash 9efbf73 C: Add a third line
squash 262057a D: Remove first line
&lt;/pre&gt;
&lt;p&gt;And now we merge together the three commits, describing the activity that took
place. We keep the messages so that history is clean, but informative. We also
include a reference to the ticket we are working&amp;nbsp;against:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Updating README.md as per #ticket

* Add a second line
* Add a third line
* Remove first line
&lt;/pre&gt;
&lt;p&gt;Check reflog&amp;nbsp;again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;reflog
&lt;/pre&gt;&lt;/div&gt;
&lt;pre class="literal-block"&gt;
d0445b2 HEAD&amp;#64;{0}: rebase -i (finish): returning to refs/heads/feature-a-candidat
d0445b2 HEAD&amp;#64;{1}: rebase -i (squash): Updating README.md as per #ticket
362b6ef HEAD&amp;#64;{2}: rebase -i (squash): # This is a combination of 2 commits.
f2503d5 HEAD&amp;#64;{3}: checkout: moving from feature-a-candidate to f2503d5
262057a HEAD&amp;#64;{4}: checkout: moving from feature-a to feature-a-candidate
&lt;/pre&gt;
&lt;p&gt;The reflog shows us that there is a new commit &lt;cite&gt;d0445b2&lt;/cite&gt;, we&amp;#8217;ll call this &lt;cite&gt;E&lt;/cite&gt;.
This is the commit that results from the rebase and leaves the tree looking&amp;nbsp;like:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
A   &amp;lt;-master
|\
| B--C--D   &amp;lt;-feature-a
\
 \
  E   &amp;lt;-feature-a-candidate
&lt;/pre&gt;
&lt;p&gt;This is a good stage to test everything &lt;strong&gt;and&lt;/strong&gt; to check that your tests are
what you expect them to be, ensure that no information has been&amp;nbsp;lost.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="merge-onto-master"&gt;
&lt;h3&gt;Merge onto&amp;nbsp;master&lt;/h3&gt;
&lt;p&gt;The new commit &lt;cite&gt;E&lt;/cite&gt; is the patch for our &lt;em&gt;feature&lt;/em&gt; which we now merge onto
&lt;cite&gt;master&lt;/cite&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;master
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;merge&lt;span class="w"&gt; &lt;/span&gt;feature-a-candidate&lt;span class="w"&gt; &lt;/span&gt;master
&lt;/pre&gt;&lt;/div&gt;
&lt;pre class="literal-block"&gt;
Updating 8e48d1d..d0445b2
Fast-forward
 README.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
&lt;/pre&gt;
&lt;p&gt;The&amp;nbsp;tree:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
A--E   &amp;lt;-master &amp;lt;-feature-a-candidate
 \
  B--C--D   &amp;lt;-feature-a
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="push"&gt;
&lt;h3&gt;Push&lt;/h3&gt;
&lt;p&gt;At this stage the &lt;em&gt;feature&lt;/em&gt; would usually be pushed to a branch on &lt;cite&gt;origin&lt;/cite&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;push&lt;span class="w"&gt; &lt;/span&gt;origin&lt;span class="w"&gt; &lt;/span&gt;master
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that we&amp;#8217;ve only shared the squashed &lt;cite&gt;E&lt;/cite&gt; commit, not &lt;cite&gt;B&lt;/cite&gt;, &lt;cite&gt;C&lt;/cite&gt; or &lt;cite&gt;D&lt;/cite&gt; in the
&lt;cite&gt;feature-a&lt;/cite&gt; branch.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="cleanup"&gt;
&lt;h3&gt;Cleanup&lt;/h3&gt;
&lt;p&gt;We can then cleanup our working branches. First the&amp;nbsp;candidate.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;branch&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;feature-a-candidate
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This leaves us with a tree&amp;nbsp;like:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
A--E   &amp;lt;-master
 \
  B--C--D   &amp;lt;-feature-a
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="keeping-history"&gt;
&lt;h2&gt;Keeping&amp;nbsp;history&lt;/h2&gt;
&lt;p&gt;As Oliver noted, the &lt;cite&gt;feature-a&lt;/cite&gt; branch can just be kept by the developer in
their local repository to preserve the full history - that is certainly an&amp;nbsp;option.&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-conversation="none" lang="en"&gt;&lt;p&gt;&lt;a href="https://twitter.com/jamesfublo"&gt;@jamesfublo&lt;/a&gt; I suppose you can still keep the unsquashed branches in the repository. I never used to squash, but I might&amp;nbsp;start.&lt;/p&gt;&amp;mdash; Oliver Caldwell (@OliverCaldwell) &lt;a href="https://twitter.com/OliverCaldwell/statuses/402401798738018304"&gt;November 18, 2013&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;&lt;p&gt;However, I prefer a clean working repository so I like to delete the
&lt;cite&gt;feature-a&lt;/cite&gt; branch.&lt;/p&gt;
&lt;div class="section" id="clean-up-the-feature-branch"&gt;
&lt;h3&gt;Clean up the feature&amp;nbsp;branch&lt;/h3&gt;
&lt;p&gt;When deleting the &lt;cite&gt;feature-a&lt;/cite&gt; branch &lt;cite&gt;git&lt;/cite&gt; requires the &lt;cite&gt;-D&lt;/cite&gt; flag to force the
deletion. &lt;cite&gt;git&lt;/cite&gt; does not &lt;em&gt;work out&lt;/em&gt; that &lt;cite&gt;E&lt;/cite&gt; is &lt;em&gt;equal&lt;/em&gt; to &lt;cite&gt;B&lt;/cite&gt;, &lt;cite&gt;C&lt;/cite&gt; and &lt;cite&gt;D&lt;/cite&gt;
combined, so thinks that history could be&amp;nbsp;lost.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;branch&lt;span class="w"&gt; &lt;/span&gt;-D&lt;span class="w"&gt; &lt;/span&gt;feature-a
&lt;/pre&gt;&lt;/div&gt;
&lt;pre class="literal-block"&gt;
Deleted branch feature-a (was 262057a)
&lt;/pre&gt;
&lt;p&gt;This leaves a tree&amp;nbsp;like:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
A--E   &amp;lt;-master
 \
  B--C--D
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="b-c-and-d-are-now-hanging-commits"&gt;
&lt;h3&gt;B, C and D are now hanging&amp;nbsp;commits&lt;/h3&gt;
&lt;p&gt;Check&amp;nbsp;reflog.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;reflog
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is a part of&amp;nbsp;it:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
...
262057a HEAD&amp;#64;{12}: commit: D: Remove first line
9efbf73 HEAD&amp;#64;{13}: commit: C: Add a third line
f2503d5 HEAD&amp;#64;{14}: commit: B: Add a second line
...
&lt;/pre&gt;
&lt;p&gt;The development commits from the &lt;em&gt;feature&lt;/em&gt; development are still available and
could be checked out into &lt;em&gt;detached &lt;span class="caps"&gt;HEAD&lt;/span&gt;&lt;/em&gt; state and inspected, played with,
rebranched. Let&amp;#8217;s try&amp;nbsp;that.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;262057a
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now play and explore as much as you&amp;nbsp;want.&lt;/p&gt;
&lt;p&gt;When you&amp;#8217;re ready, move back to &lt;cite&gt;master&lt;/cite&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;master
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And &lt;cite&gt;git&lt;/cite&gt; warns us that we&amp;#8217;ve left behind our hanging&amp;nbsp;commits:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Warning: you are leaving 3 commits behind, not connected to
any of your branches:

  262057a D: Remove first line
  9efbf73 C: Add a third line
  f2503d5 B: Add a second line

If you want to keep them by creating a new branch, this may be a good time
to do so with:

 git branch new_branch_name 262057a
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="how-long-are-hanging-commits-kept"&gt;
&lt;h2&gt;How long are hanging commits&amp;nbsp;kept?&lt;/h2&gt;
&lt;p&gt;But how long will these unreachable commits &lt;em&gt;hang&lt;/em&gt; around&amp;nbsp;for?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;We can&amp;nbsp;decide!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Hanging commits are removed from the local repository by garbage collection,
known as &lt;cite&gt;gc&lt;/cite&gt;, or by manual&amp;nbsp;removal.&lt;/p&gt;
&lt;p&gt;There are various settings which &lt;cite&gt;gc&lt;/cite&gt; will use to determine which commits
should be cleaned before the repository is&amp;nbsp;repacked.&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;gc.reflogExpireUnreachable&lt;/cite&gt; tells &lt;cite&gt;gc&lt;/cite&gt; how long hanging commits should be left
in the repository. Default value is 30 days. Adjust this to a value that you
feel comfortable with. You can make that setting on any of the normal levels -
global, system or&amp;nbsp;local.&lt;/p&gt;
&lt;p&gt;Hey - you want to keep all history in the reflog for ever? Here&amp;#8217;s a&amp;nbsp;setting:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
[gc]
    reflogExpire = never
    reflogExpireUnreachable = never
&lt;/pre&gt;
&lt;p&gt;I&amp;#8217;m happy with the 30 day default&amp;nbsp;myself!&lt;/p&gt;
&lt;p&gt;For more detailed explanation, checkout the Configuration section of the
&lt;cite&gt;git-gc&lt;/cite&gt; man&amp;nbsp;page.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="a-manual-clean"&gt;
&lt;h2&gt;A manual&amp;nbsp;clean&lt;/h2&gt;
&lt;p&gt;Just for experimention, I tried to clean the repository of the &lt;cite&gt;B&lt;/cite&gt;, &lt;cite&gt;C&lt;/cite&gt; and &lt;cite&gt;D&lt;/cite&gt;
hanging commits. This was challenging because my default settings prevented
reflog and &lt;cite&gt;gc&lt;/cite&gt; from performing the clean, however I found &lt;a class="reference external" href="https://stackoverflow.com/a/14995269/1286705"&gt;this &lt;span class="caps"&gt;SO&lt;/span&gt; answer
helpful&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;reflog&lt;span class="w"&gt; &lt;/span&gt;expire&lt;span class="w"&gt; &lt;/span&gt;--all&lt;span class="w"&gt; &lt;/span&gt;--expire-unreachable&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;repack&lt;span class="w"&gt; &lt;/span&gt;-A&lt;span class="w"&gt; &lt;/span&gt;-d
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Repacking occurred. Now check&amp;nbsp;reflog.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;reflog
&lt;/pre&gt;&lt;/div&gt;
&lt;pre class="literal-block"&gt;
d0445b2 HEAD&amp;#64;{0}: merge feature-a-candidate: Fast-forward
8e48d1d HEAD&amp;#64;{1}: checkout: moving from feature-a-candidate to master
d0445b2 HEAD&amp;#64;{2}: rebase -i (finish): returning to refs/heads/feature-a-candidat
d0445b2 HEAD&amp;#64;{3}: checkout: moving from master to feature-a
8e48d1d HEAD&amp;#64;{4}: commit (initial): Make readme
&lt;/pre&gt;
&lt;p&gt;There are now only two commits in the&amp;nbsp;repository:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;cite&gt;8e48d1d&lt;/cite&gt; - Initial commit &lt;cite&gt;A&lt;/cite&gt; &amp;#64; 1 and&amp;nbsp;4.&lt;/li&gt;
&lt;li&gt;&lt;cite&gt;d0445b2&lt;/cite&gt; - Feature commit &lt;cite&gt;E&lt;/cite&gt; made by the rebase &amp;#64; 0, 2 and&amp;nbsp;3&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The cleaned repository now looks&amp;nbsp;like:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
A--E   &amp;lt;-master
&lt;/pre&gt;
&lt;p&gt;So fresh and so&amp;nbsp;clean!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="summary"&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;At the end of the day, the dev team (even if that&amp;#8217;s just you on a weekend
project) decides how best to keep history and share&amp;nbsp;features.&lt;/p&gt;
&lt;p&gt;My general solution is&amp;nbsp;for:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Squashed single-commit&amp;nbsp;features.&lt;/li&gt;
&lt;li&gt;Detailed commit messages created at &lt;em&gt;squash-time&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Devs keep more history locally, either with branches or in a long-life&amp;nbsp;reflog.&lt;/li&gt;
&lt;li&gt;Devs backup their repositories and don&amp;#8217;t rely on &lt;cite&gt;origin&lt;/cite&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Remember there can be a full 30 day history (or longer depending on the
&lt;cite&gt;gc.reflogExpireUnreachable&lt;/cite&gt; setting) in the local repo which hasn&amp;#8217;t been
pushed to &lt;cite&gt;origin&lt;/cite&gt;. It&amp;#8217;s this history that could save your bacon one day - so
consider backing it&amp;nbsp;up!&lt;/p&gt;
&lt;p&gt;Happy source code&amp;nbsp;management!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="update-23-08-2018"&gt;
&lt;h2&gt;Update&amp;nbsp;23/08/2018&lt;/h2&gt;
&lt;p&gt;See also &lt;a class="reference external" href="https://github.com/jamescooke/blog/issues/17"&gt;this comment on GitHub&lt;/a&gt; from Curt J. Sampson with some
great points about when not to squash. One helpful&amp;nbsp;excerpt:&lt;/p&gt;
&lt;blockquote&gt;
I think of a set of commits I&amp;#8217;m proposing for master branch as a story I&amp;#8217;m
telling to the other developers. Make the story as clear as possible,
divided up into reasonably small chunks where you can do so. This will make
other developers love, rather than hate, reviewing your code.&lt;/blockquote&gt;
&lt;p&gt;Thanks Curt - spread the&amp;nbsp;love!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="update-06-01-2019"&gt;
&lt;h2&gt;Update&amp;nbsp;06/01/2019&lt;/h2&gt;
&lt;p&gt;The Twitter account that I used in my conversations with Oliver above has been
deleted. I&amp;#8217;ve replaced the links to tweets with the original&amp;nbsp;content.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="git"></category></entry><entry><title>vi-nature everywhere - lightning talk</title><link href="https://jamescooke.info/vi-nature-everywhere-lightning-talk.html" rel="alternate"></link><published>2013-11-01T20:00:00+00:00</published><updated>2013-11-01T20:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-11-01:/vi-nature-everywhere-lightning-talk.html</id><summary type="html">&lt;p class="first last"&gt;This week I presented at Vim-London about vi nature and the benefits
for using it in more than&amp;nbsp;vim.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;vi-nature, the &amp;#8216;language&amp;#8217; of &lt;cite&gt;vim&lt;/cite&gt;. It&amp;#8217;s the reason that &lt;cite&gt;vim&lt;/cite&gt; works so well
for me. However, it does take some learning, and even after many months of use,
I&amp;#8217;d say I&amp;#8217;ve only just scratched the&amp;nbsp;surface.&lt;/p&gt;
&lt;p&gt;So if we&amp;#8217;re investing so much time and energy in learning this language, then
why not apply it to more tasks than just editing&amp;nbsp;files?&lt;/p&gt;
&lt;p&gt;In this five minute lighting talk I gave at &lt;a class="reference external" href="https://www.meetup.com/Vim-London/"&gt;Vim London&lt;/a&gt; this week, I delved into some of the benefits
and issues with using vi-nature for more than just&amp;nbsp;editing.&lt;/p&gt;
&lt;iframe src="//player.vimeo.com/video/78173248" width="500" height="281" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen&gt;&lt;/iframe&gt;&lt;p&gt;The feedback after the talk was great - here are my&amp;nbsp;take-aways:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Check out &lt;a class="reference external" href="https://www.uzbl.org/"&gt;uzbl&lt;/a&gt; - it provides an interface layer
that can be programmed to different keybindings. Thanks&amp;nbsp;Nestor.&lt;/li&gt;
&lt;li&gt;Check out &lt;a class="reference external" href="https://awesomewm.org/"&gt;Awesome Window Manager&lt;/a&gt; because it&amp;#8217;s completely operational without mouse. Thanks&amp;nbsp;Nestor.&lt;/li&gt;
&lt;li&gt;Write a blog post about &amp;#8216;vi-nature&amp;#8217; because there&amp;#8217;s not much about it on the web - Yes I will do this, thanks for the suggestion&amp;nbsp;Max.&lt;/li&gt;
&lt;li&gt;Check out Mac &lt;span class="caps"&gt;OSX&lt;/span&gt;&amp;#8217;s &lt;a class="reference external" href="https://github.com/jigish/slate"&gt;slate&lt;/a&gt; because it creates a programmable keyboard interface for window management. Thanks&amp;nbsp;David.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lots to follow up on and hopefully some ways to take vi-nature to more&amp;nbsp;places.&lt;/p&gt;
&lt;p&gt;Thanks for&amp;nbsp;reading!&lt;/p&gt;
</content><category term="Talk"></category><category term="topic:vim"></category></entry><entry><title>Things to remember about decorators</title><link href="https://jamescooke.info/things-to-remember-about-decorators.html" rel="alternate"></link><published>2013-10-22T20:00:00+01:00</published><updated>2013-10-22T20:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-10-22:/things-to-remember-about-decorators.html</id><summary type="html">&lt;p class="first last"&gt;Notes to myself about Python decorators with a focus on making them&amp;nbsp;testable.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;After an interview question about Python decorators which I stumbled over, I
promised myself that I would improve my knowledge of this metaprogramming&amp;nbsp;technique.&lt;/p&gt;
&lt;p&gt;These are my notes to myself on decorators - maybe they&amp;#8217;ll be helpful to
someone else who&amp;#8217;s improving their knowledge of decorators&amp;nbsp;too.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;A decorator is pure Pythonic syntatic&amp;nbsp;sugar.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;A decorator is a Python callable that receives the decorated function and
returns a new function in its&amp;nbsp;place.&lt;/p&gt;
&lt;p&gt;For example, if there is a decorator called &lt;cite&gt;my_decorator&lt;/cite&gt; and we want to
decorate &lt;cite&gt;my_func&lt;/cite&gt; then&amp;#8230;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@my_decorator&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_func&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;some stuff&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Is equivalent to&amp;nbsp;writing.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_func&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;some stuff&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="n"&gt;my_func&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The decorator callable is executed at load time, not at execution time. Here
is an example of a silly decorator that prints &amp;#8220;Hello World&amp;#8221; when the Python
file is loaded - there is nothing else in the&amp;nbsp;file.&lt;/p&gt;
&lt;p&gt;&lt;cite&gt;hello.py&lt;/cite&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say_hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;

&lt;span class="nd"&gt;@say_hello&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;nothing&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Do nothing just return&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Run it on the command line, and &amp;#8220;Hello World&amp;#8221; appears when the &lt;cite&gt;nothing&lt;/cite&gt;
function is&amp;nbsp;decorated.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;hello.py
Hello&lt;span class="w"&gt; &lt;/span&gt;World
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;When writing a decorator, remember to patch over the docstring of the wrapped
function. This can be done by accessing the passed function&amp;#8217;s &lt;cite&gt;__doc__&lt;/cite&gt;
attribute. Failing to do so will prevent doctest from testing the decorated&amp;nbsp;function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&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;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Pass through the doc string&lt;/span&gt;
    &lt;span class="n"&gt;wrapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__doc__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__doc__&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; This is actually far better done with the &lt;cite&gt;wraps&lt;/cite&gt; decorator from
the &lt;cite&gt;functools&lt;/cite&gt; modules, which fixes the &lt;cite&gt;__name__&lt;/cite&gt; and &lt;cite&gt;__doc__&lt;/cite&gt; attributes
to what we&amp;#8217;d expect them to&amp;nbsp;be.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wraps&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&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;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&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;wrapper&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Found on &lt;a class="reference external" href="https://jeffknupp.com/blog/2013/11/29/improve-your-python-decorators-explained/"&gt;Improve your Python&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;When unit testing decorators, one strategy can be to manually call the
decorator on a mocked object and inspect how it interacts with&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a caching function which is used to speed up the Fibonacci&amp;nbsp;series.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Keep a dict of values returned already&lt;/span&gt;
    &lt;span class="n"&gt;vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&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="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_key&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;vals&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;vals&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;wrapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__doc__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__doc__&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now use the cache function as a&amp;nbsp;decorator.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@cache&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&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="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Fibonacci series&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;gt;&amp;gt;&amp;gt; fib(1)&lt;/span&gt;
&lt;span class="sd"&gt;    1&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;gt;&amp;gt;&amp;gt; fib(2)&lt;/span&gt;
&lt;span class="sd"&gt;    2&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Must be greater than 0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&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;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;elif&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;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;else&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;fib&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fib&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And here&amp;#8217;s a unittest that asserts that the cache function only allows calls
through when there is no value saved in the &lt;cite&gt;vals&lt;/cite&gt; dict.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;unittest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mock&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestCashDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_cache&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;my_fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_fn&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;my_fn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;hi&amp;#39;&lt;/span&gt;

        &lt;span class="n"&gt;wrapped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# First call gives a call count of 1&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapped&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="s1"&gt;&amp;#39;hi&amp;#39;&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_fn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_count&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="c1"&gt;# Second call keeps the call count at 1 - the cached value is used&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapped&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="s1"&gt;&amp;#39;hi&amp;#39;&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_fn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_count&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="c1"&gt;# Subsequent call with a new value increased the call count&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapped&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="s1"&gt;&amp;#39;hi&amp;#39;&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_fn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Make sure the scope of variables used in the decorators is correct, this is
&lt;a class="reference external" href="http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/"&gt;an interesting article by Simeon Franklin about decorators and scope&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If in doubt, extend any tests to test a second decorated function and ensure
that the two functions do not effect each&amp;nbsp;other.&lt;/p&gt;
&lt;p&gt;Below is a test that aims to check that cache dictionaries are not shared
between instances of the &lt;cite&gt;cache&lt;/cite&gt; decorator, it is appended to the
&lt;cite&gt;test_cache&lt;/cite&gt; test&amp;nbsp;above.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Check that the vals dict isn&amp;#39;t shared between other decor&lt;/span&gt;
&lt;span class="n"&gt;my_other_fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;other fn&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;my_other_fn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;other hi&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# Create other wrapped function&lt;/span&gt;
&lt;span class="n"&gt;other_wrapped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_other_fn&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other_wrapped&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="s1"&gt;&amp;#39;other hi&amp;#39;&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_other_fn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_count&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="c1"&gt;# The original function has not have been additionally called, its&lt;/span&gt;
&lt;span class="c1"&gt;# call count remains 2&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;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_fn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All suggested tips on decorators very welcome - thanks for&amp;nbsp;reading!&lt;/p&gt;
</content><category term="Code"></category><category term="language:python"></category></entry><entry><title>Calculating your day rate for spare time freelance work</title><link href="https://jamescooke.info/calculating-your-day-rate-for-spare-time-freelance-work.html" rel="alternate"></link><published>2013-07-14T20:00:00+01:00</published><updated>2013-07-14T20:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-07-14:/calculating-your-day-rate-for-spare-time-freelance-work.html</id><summary type="html">&lt;p class="first last"&gt;So you want to earn some extra cash on your weekends, what should you&amp;nbsp;charge?&lt;/p&gt;
</summary><content type="html">&lt;p&gt;So you want to do some freelance work and you&amp;#8217;re not sure how much to charge
your new client. The most important thing is to not underestimate your value -
it frustrates me so much when I hear of a talented coder selling themselves&amp;nbsp;short.&lt;/p&gt;
&lt;div class="section" id="the-calculation"&gt;
&lt;h2&gt;The&amp;nbsp;calculation&lt;/h2&gt;
&lt;p&gt;When you work for yourself, it&amp;#8217;ll be like your day job, except you keep the
profit and take the additional time and cost&amp;nbsp;overheads.&lt;/p&gt;
&lt;p&gt;This calculation has worked well for me in the past, so I&amp;#8217;m sharing it here.
It&amp;#8217;s so simple. I hope it can work for&amp;nbsp;you.&lt;/p&gt;
&lt;div class="math"&gt;
\begin{equation*}
Your Day Rate = \frac {2\times Your Annual Salary} {252 - Number Of Days Holiday}
\end{equation*}
&lt;/div&gt;
&lt;p&gt;Where:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;span class="quo"&gt;&amp;#8216;&lt;/span&gt;2&amp;#8217; is my freelance&amp;nbsp;multiplier.&lt;/li&gt;
&lt;li&gt;&lt;span class="quo"&gt;&amp;#8216;&lt;/span&gt;252&amp;#8217; is the number of working days in a year - an&amp;nbsp;estimate.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="why-is-this-based-on-my-current-salary"&gt;
&lt;h2&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;Why is this based on my current&amp;nbsp;salary?&amp;#8221;&lt;/h2&gt;
&lt;p&gt;I assume you will be doing some work similar to your day job. This means that
you can use your usual salary as a base unit for calculating your day&amp;nbsp;rate.&lt;/p&gt;
&lt;p&gt;If you don&amp;#8217;t have a day job and all your income will be from self-employment,
then I would guess that you will have an idea what your employed market value
would be in the kind of business you&amp;#8217;ll be selling your services to&amp;nbsp;is.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="why-is-the-multiplier-2"&gt;
&lt;h2&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;Why is the multiplier&amp;nbsp;2?&amp;#8221;&lt;/h2&gt;
&lt;p&gt;Remember, the value of the work that you provide a company is greater than the
amount that you are&amp;nbsp;paid:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;In the &lt;span class="caps"&gt;UK&lt;/span&gt;, your employer pays an employment tax - &lt;a class="reference external" href="https://www.gov.uk/national-insurance-rates-letters#2"&gt;Employers&amp;#8217; National
Insurance Contributions&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Your employer pays overheads as part of your employment which you might not
be exposed to. The cost of your equipment, heating and lighting your work
space, insuring you at work, payroll costs&amp;#8230; All these overheads mount up
and you will be taking these on when you&amp;#8217;re working&amp;nbsp;freelance.&lt;/li&gt;
&lt;li&gt;Any successful business must sell the goods or services at a profit.
Therefore, if you&amp;#8217;re contributing code to a project, then the future or
immediate value of your contribution should be greater than your input for a
business to be making a profit from&amp;nbsp;you.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can play with this number of course, but a factor of 2 has worked for me in
the&amp;nbsp;past.&lt;/p&gt;
&lt;p&gt;One assumption is that you&amp;#8217;re going to do this new project in your free time,
probably on the weekends and evenings. Usually this would earn an overtime rate
for someone on an hourly wage - usually double time or time and a&amp;nbsp;half.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re in the &lt;span class="caps"&gt;UK&lt;/span&gt;, more free time will be taken up managing a tax-return,
paying &lt;a class="reference external" href="https://www.gov.uk/set-up-sole-trader"&gt;&lt;span class="caps"&gt;HMRC&lt;/span&gt; for additional National Insurance Contributions&lt;/a&gt;, invoicing and
keeping records. You need to ensure that this time is covered in some way by
the income from your freelance&amp;nbsp;work.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="what-if-my-new-client-is-too-poor-too-rich-to-afford-x"&gt;
&lt;h2&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;What if my new client is too poor / too rich to afford&amp;nbsp;£X?&amp;#8221;&lt;/h2&gt;
&lt;p&gt;Of course, you&amp;#8217;re perfectly allowed to adjust this if you want to give away
some of your work at less than the market rate. Remember, your employer is
already paying something along the lines of what you&amp;#8217;ve just calculated for
your time. Carefully consider how much you should adjust that for someone&amp;nbsp;else.&lt;/p&gt;
&lt;p&gt;In my previous businesses I&amp;#8217;ve charged all clients the same basic rate for the
simple reason that it&amp;#8217;s easier on the books and my&amp;nbsp;brain.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="in-the-end"&gt;
&lt;h2&gt;In the&amp;nbsp;end&lt;/h2&gt;
&lt;p&gt;As you work more freelance jobs you&amp;#8217;ll get a feel for what&amp;#8217;s suitable and
what&amp;#8217;s&amp;nbsp;not.&lt;/p&gt;
&lt;p&gt;I hope this has been&amp;nbsp;helpful.&lt;/p&gt;
&lt;p&gt;Good&amp;nbsp;luck!&lt;/p&gt;
&lt;/div&gt;
&lt;script type='text/javascript'&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (false) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="ZZZ Misc..."></category><category term="work"></category></entry><entry><title>Pyramid London talk - A testing strategy for Pyramid Applications</title><link href="https://jamescooke.info/pyramid-london-talk-a-testing-strategy-for-pyramid-applications.html" rel="alternate"></link><published>2013-06-16T21:00:00+01:00</published><updated>2013-06-16T21:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-06-16:/pyramid-london-talk-a-testing-strategy-for-pyramid-applications.html</id><summary type="html">&lt;p class="first last"&gt;Talk at Pyramid London meetup about testing strategies for Pyramid&amp;nbsp;applications.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Pyramid London meetup returned in June to &lt;a class="reference external" href="https://skillsmatter.com/"&gt;Skills Matter&lt;/a&gt;. This time I spoke about testing strategies for Pyramid&amp;nbsp;applications.&lt;/p&gt;
&lt;p&gt;As outlined in the slides below, my current testing framework builds up with doctests, through unit and integration tests to functional / behaviour driven testing on the outside of the application. Hopefully my very basic &amp;#8220;drawn on Google Docs&amp;#8221; diagram of the Pyramid Framework illustrates how each of the testing methods fits within the&amp;nbsp;framework.&lt;/p&gt;
&lt;p&gt;I would like to have been able to talk more about Behaviour Driven Development and &lt;a class="reference external" href="https://behave.readthedocs.io/en/latest/"&gt;testing with Behave&lt;/a&gt;, which I&amp;#8217;m enjoying at the moment, but maybe that&amp;#8217;s for another presentation. Again, putting together this presentation was really helpful - it helped me to reflect on the methods we&amp;#8217;re using at the moment, and how I might be able to improve and progress the level of test driven development in my daily&amp;nbsp;work.&lt;/p&gt;
&lt;br&gt;
&lt;script async class="speakerdeck-embed" data-id="57b235d0b8f1013000d27aa19dd2a8cb" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;
&lt;br&gt;&lt;p&gt;&lt;a class="reference external" href="https://skillsmatter.com/skillscasts/4265-pyramid-sqlalchemy-testing-and-auth-policy"&gt;Video is available via the SkillsMatter site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Many thanks to &lt;a class="reference external" href="http://lucumr.pocoo.org/"&gt;Armin Ronacher&lt;/a&gt; for his talk on &lt;a class="reference external" href="https://docs.sqlalchemy.org/en/latest/"&gt;SQLAlchemy&lt;/a&gt; at the same Pyramid meetup - the &lt;a class="reference external" href="https://skillsmatter.com/skillscasts/4266-pyramid-sqlalchemy-testing-and-auth-policy-4266"&gt;video is also online at SkillsMatter&lt;/a&gt;. As well as the technical details and some hints for things to check out with &lt;span class="caps"&gt;SQLA&lt;/span&gt;, I found Armin&amp;#8217;s thoughts on how the Pyramid community might improve on how we introduce new developers to Pyramid and SQLAlchemy very helpful. I hope I might be able to contribute to that some time in the future.
Hopefully we&amp;#8217;ll see more people at the next Pyramid Meetup which may include a talk on using &lt;a class="reference external" href="http://www.celeryproject.org/"&gt;Celery&lt;/a&gt; with&amp;nbsp;Pyramid.&lt;/p&gt;
</content><category term="Code"></category><category term="topic:pyramid"></category><category term="language:python"></category><category term="topic:testing"></category></entry><entry><title>Pyramid London talk - Pyramid Router</title><link href="https://jamescooke.info/pyramid-london-talk-pyramid-router.html" rel="alternate"></link><published>2013-05-08T23:00:00+01:00</published><updated>2013-05-08T23:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-05-08:/pyramid-london-talk-pyramid-router.html</id><summary type="html">&lt;p class="first last"&gt;Talk at the first Pyramid London meetup about the Pyramid Router, &lt;span class="caps"&gt;URL&lt;/span&gt; Traversal and&amp;nbsp;Dispatch.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Our first Pyramid London meetup was kindly hosted at &lt;a class="reference external" href="https://skillsmatter.com/"&gt;Skills Matter&lt;/a&gt;, who have &lt;a class="reference external" href="https://skillsmatter.com/skillscasts/4189-routing-traversal-and-url-dispatch"&gt;posted the video of my talk on their page for the meetup&lt;/a&gt;.&lt;/p&gt;
&lt;br&gt;
&lt;script async class="speakerdeck-embed" data-id="76686b40b8ed01307b196e084453428f" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;
&lt;br&gt;&lt;p&gt;All the code I demonstrated is on &lt;a class="reference external" href="https://github.com/jamescooke/pyramid-london-talk"&gt;GitHub in the pyramid-london-talk repository&lt;/a&gt; - Please note that the traversal code is in the traversal branch, not in a separate&amp;nbsp;project.&lt;/p&gt;
&lt;p&gt;I learned loads from preparing the demonstration code and chatting to everyone that attended - so thanks and hope to see you at the next meetup in&amp;nbsp;June!&lt;/p&gt;
</content><category term="Code"></category><category term="topic:pyramid"></category></entry><entry><title>Reincublog Django app</title><link href="https://jamescooke.info/reincublog-django-app.html" rel="alternate"></link><published>2013-04-28T13:00:00+01:00</published><updated>2013-04-28T13:00:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-04-28:/reincublog-django-app.html</id><summary type="html">&lt;p class="first last"&gt;Reincublog is a super mini Django blog weekend hack, coded as part of a recruitment&amp;nbsp;process.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Reincublog is a Django app that I was asked to code as part of the recruitment process at Reincubate. It&amp;#8217;s a weekend of glue code which I was set to see if I am a competent Django programmer. I&amp;#8217;m not sure that it&amp;#8217;s the kind of test that I show my best at - I&amp;#8217;m more of an algorithm&amp;nbsp;guy.&lt;/p&gt;
&lt;p&gt;However, after living on GitHub for a few months, the repo has picked up a couple of stars, and because I like to keep a super clean GitHub account, I&amp;#8217;ve decided to clean it out of my&amp;nbsp;account.&lt;/p&gt;
&lt;p&gt;So, from today the &lt;a class="reference external" href="https://github.com/shonenada/reincublog"&gt;Reincublog code&lt;/a&gt; will live on &lt;a class="reference external" href="https://github.com/shonenada"&gt;shonenada&amp;#8217;s GitHub&lt;/a&gt; hopefully it can grow and be&amp;nbsp;useful.&lt;/p&gt;
</content><category term="GitHub Contributions"></category><category term="django"></category></entry><entry><title>Migrating from Django 1.4 to 1.5 - Lessons learned</title><link href="https://jamescooke.info/migrating-from-django-14-to-15-lessons-learned.html" rel="alternate"></link><published>2013-03-29T19:00:00+00:00</published><updated>2013-03-29T19:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-03-29:/migrating-from-django-14-to-15-lessons-learned.html</id><summary type="html">&lt;p class="first last"&gt;Migrating a project from Django 1.4 to Django 1.5 had a couple of gotchas that cost me dev&amp;nbsp;time.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;From &lt;a class="reference external" href="https://twitter.com/ryankask/"&gt;Ryan Kaskel&lt;/a&gt;&amp;#8216;s talk at &lt;a class="reference external" href="https://www.meetup.com/The-London-Django-Meetup-Group/"&gt;Django London&lt;/a&gt; in November last
year, I guessed that upgrading the &lt;a class="reference external" href="https://github.com/jamescooke/actionguide"&gt;Action Guide code&lt;/a&gt; from Django 1.4 to 1.5 might have
created some issues with users (&lt;a class="reference external" href="https://docs.djangoproject.com/en/dev/releases/1.5/#configurable-user-model"&gt;user models have changed in Django 1.5 to
allow more customisation&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;However, as it turns out, the main problems were with settings and urls, the
users were fine. My main take-aways&amp;nbsp;were:&lt;/p&gt;
&lt;div class="section" id="url-formats-have-changed-now-need-quotes"&gt;
&lt;h2&gt;Url formats have changed - now need&amp;nbsp;quotes&lt;/h2&gt;
&lt;p&gt;The Django team had already updated the &lt;cite&gt;url&lt;/cite&gt; tag to accept the path parameter
as a string, but the old syntax was still allowed. 1.4 allowed both types of
syntax, the team having provided &lt;cite&gt;{% load url from future %}&lt;/cite&gt; for those that
wanted to update their templates to the new&amp;nbsp;syntax.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the warning from the &lt;a class="reference external" href="https://docs.djangoproject.com/en/1.5/ref/templates/builtins/#std:templatetag-url"&gt;&lt;span class="caps"&gt;URL&lt;/span&gt; tag documentation&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="Screenshot of warning from Django documentation. Warning reads: &amp;quot;Don't forget to put quotes around the function path or pattern name!" src="https://jamescooke.info/images/url-warning.png" /&gt;
&lt;p&gt;This was a reasonably easy change to implement - some search and replace and
all &lt;cite&gt;url&lt;/cite&gt; tags can be easily hunted down and&amp;nbsp;changed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="read-up-on-the-settings-no-allowed-hosts-makes-500s"&gt;
&lt;h2&gt;Read up on the settings - no ALLOWED_HOSTS makes&amp;nbsp;500s&lt;/h2&gt;
&lt;p&gt;This was the real&amp;nbsp;killer.&lt;/p&gt;
&lt;p&gt;There is a &lt;a class="reference external" href="https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts"&gt;new ALLOWED_HOSTS settings in 1.5&lt;/a&gt; required
to get Django and running in non-debug&amp;nbsp;mode.&lt;/p&gt;
&lt;p&gt;Worst thing about the implementation of this new setting is that I couldn&amp;#8217;t get
a single bit of debugging output it through &lt;cite&gt;wsgi&lt;/cite&gt; on WebFaction - just a 500
error on every page load when I took the site out of debug&amp;nbsp;mode.&lt;/p&gt;
&lt;p&gt;I was so confused that I posted &lt;a class="reference external" href="https://stackoverflow.com/questions/15605185/django-1-5-url-deprecation-warning-causes-500-error-in-webfaction-apache-wsgi/15626247"&gt;this question on StackOverflow&lt;/a&gt;,
thinking the problem was &lt;cite&gt;url&lt;/cite&gt; warnings being shown as errors and halting the
&lt;cite&gt;wsgi&lt;/cite&gt;. In the end, just adding &lt;cite&gt;ALLOWED_HOSTS&lt;/cite&gt; fixed everything up&amp;nbsp;great.&lt;/p&gt;
&lt;p&gt;My main problem was that I scanned the docs, tested the migration on localhost
in dev mode, and just expected everything to deploy. With Captain Hindsight,
I&amp;#8217;d have RTFMed much harder before deploying - a lesson for the&amp;nbsp;future.&lt;/p&gt;
&lt;p&gt;Apart from that, everything works really well. &lt;strong&gt;Have&amp;nbsp;fun!&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
</content><category term="Code"></category><category term="django"></category></entry><entry><title>Pelican Svbtle theme tweaks</title><link href="https://jamescooke.info/pelican-svbtle-theme-tweaks.html" rel="alternate"></link><published>2013-02-21T19:40:00+00:00</published><updated>2013-02-21T19:40:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-02-21:/pelican-svbtle-theme-tweaks.html</id><summary type="html">&lt;p class="first last"&gt;A tweaked fork of the Pelican Svbtle theme with some cleaned &lt;span class="caps"&gt;CSS&lt;/span&gt; and &lt;span class="caps"&gt;HTML&lt;/span&gt;.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;My first experiments with &lt;a class="reference external" href="https://blog.getpelican.com/"&gt;Pelican&lt;/a&gt; to run this blog have been good - it&amp;#8217;s a great way to publish static pages quickly and I find it much easier to manage than&amp;nbsp;Octopress.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s built on a version of the &lt;a class="reference external" href="https://github.com/wting/pelican-svbtle"&gt;Pelican-svbtle theme&lt;/a&gt;. There were some problems with the theme in its current form, so I&amp;#8217;ve forked &lt;a class="reference external" href="https://github.com/CNBorn/pelican-svbtle"&gt;CNBorn&amp;#8217;s already adjusted version&lt;/a&gt; and cleaned out some of the &lt;span class="caps"&gt;LESS&lt;/span&gt; and templates - &lt;a class="reference external" href="https://github.com/jamescooke/pelican-svbtle"&gt;my fork is on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, this theme isn&amp;#8217;t going to stay. &lt;a class="reference external" href="https://github.com/thesocialspaces"&gt;Paul&lt;/a&gt; has been working on some flat &lt;span class="caps"&gt;HTML&lt;/span&gt; based on Bootstrap to make a new clean theme. Once that&amp;#8217;s stable, I&amp;#8217;ll plug in some Pelican / Jinja2 tags and hopefully this site will have a new clean theme&amp;nbsp;soon.&lt;/p&gt;
</content><category term="Code"></category><category term="pelican"></category></entry><entry><title>jsFiddle documentation update</title><link href="https://jamescooke.info/jsfiddle-documentation-update.html" rel="alternate"></link><published>2013-01-25T19:40:00+00:00</published><updated>2013-01-25T19:40:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-01-25:/jsfiddle-documentation-update.html</id><summary type="html">&lt;p class="first last"&gt;A newly structured documentation site and tutorial for the online&amp;nbsp;editor.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="http://doc.jsfiddle.net/"&gt;Updated documentation&lt;/a&gt; for &lt;a class="reference external" href="https://jsfiddle.net/"&gt;jsFiddle&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/jsfiddle/jsfiddle-docs-alpha/commit/ef0f234e44e5a6d6791c09e672364fdf9518a31a"&gt;merged by Piotr&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Includes a new &lt;a class="reference external" href="http://doc.jsfiddle.net/tutorial.html"&gt;tutorial&lt;/a&gt; - but images are already out of&amp;nbsp;date!&lt;/p&gt;
&lt;p&gt;jsFiddle is such a great tool and my goal for the tutorial was to create a simple introduction which first time students would be able to understand and&amp;nbsp;execute.&lt;/p&gt;
</content><category term="GitHub Contributions"></category><category term="documentation"></category></entry><entry><title>Setting up this homepage with Pelican</title><link href="https://jamescooke.info/setting-up-this-homepage-with-pelican.html" rel="alternate"></link><published>2013-01-20T17:25:00+00:00</published><updated>2013-01-20T17:25:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-01-20:/setting-up-this-homepage-with-pelican.html</id><summary type="html">&lt;p class="first last"&gt;Using Pelican to generate this blog, a move away from Jekyll and to
some pure&amp;nbsp;Python.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This page has been through a lot in the last ten&amp;nbsp;years.&lt;/p&gt;
&lt;p&gt;Since starting work at Quibly, I&amp;#8217;ve had a lot more time to code and it&amp;#8217;s exactly what I wanted, hopefully it&amp;#8217;ll continue. The result of that is that I&amp;#8217;ve got more to write about&amp;#8230; The code that I develop at work, fixes I make to open source libraries and general things I learn, primarily about Python and web - hopefully all valuable and worth&amp;nbsp;sharing.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m experimenting with &lt;a class="reference external" href="https://github.com/getpelican/pelican"&gt;Pelican&lt;/a&gt; - a static blog generator written in Python. It&amp;#8217;s excellent and noticeably easier than Jekyll - probably because I&amp;#8217;m much more clued up in Python than Ruby. I&amp;#8217;m lazy, so I&amp;#8217;m hosting the outputted static files in the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gh-pages&lt;/span&gt;&lt;/tt&gt; branch of the &lt;a class="reference external" href="https://github.com/jamescooke/blog/"&gt;blog&amp;#8217;s repository&lt;/a&gt; to take advantage of &lt;a class="reference external" href="https://pages.github.com/"&gt;GitHub Pages&amp;#8217; free hosting features&lt;/a&gt; - thanks&amp;nbsp;GitHub!&lt;/p&gt;
&lt;p&gt;In addition, I found &lt;a class="reference external" href="https://www.davidfischer.name/2012/12/quick-note-pelican-github/"&gt;this article by David Fischer&lt;/a&gt; very helpful. Particularly the suggestion of adding the &lt;tt class="docutils literal"&gt;&lt;span class="caps"&gt;CNAME&lt;/span&gt;&lt;/tt&gt; copy command to the &lt;tt class="docutils literal"&gt;Makefile&lt;/tt&gt; to get GitHub Pages one configuration requirement and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gph-import&lt;/span&gt;&lt;/tt&gt; working nicely together. Plus David pointed out that Pelican already has a &lt;tt class="docutils literal"&gt;github&lt;/tt&gt; target in the &lt;tt class="docutils literal"&gt;Makefile&lt;/tt&gt; which I hadn&amp;#8217;t noticed and is now what I use to push articles&amp;nbsp;live.&lt;/p&gt;
&lt;p&gt;All in all - great and&amp;nbsp;simple.&lt;/p&gt;
</content><category term="ZZZ Misc..."></category><category term="pelican"></category></entry><entry><title>Got the Stack Overflow tumbleweed badge for Mako filters question</title><link href="https://jamescooke.info/got-the-stack-overflow-tumbleweed-badge-for-mako-filters-question.html" rel="alternate"></link><published>2013-01-20T16:40:00+00:00</published><updated>2013-01-20T16:40:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2013-01-20:/got-the-stack-overflow-tumbleweed-badge-for-mako-filters-question.html</id><summary type="html">&lt;p class="first last"&gt;My Mako question got no views and no answers - should we be using Mako if things are so&amp;nbsp;quiet?&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Last week I posted a question on Stack Overflow - &amp;#8220;&lt;a class="reference external" href="https://stackoverflow.com/questions/14215591/mako-template-filter-ordering"&gt;Mako template filter
ordering&lt;/a&gt;&amp;#8221;
- this week it earned the &lt;a class="reference external" href="https://stackoverflow.com/help/badges/63/tumbleweed"&gt;Tumbleweed badge&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s always a little concerning when libraries and toolkits you&amp;#8217;re using in a
project have forums and message boards that are a little too quiet - is there a
bad smell? Is there something bad I don&amp;#8217;t know about this tech? Even worse is
when you look around those quiet forums (or tags in Stack Overflow) you find
comments like this about the library you&amp;#8217;re being asked to&amp;nbsp;use:&lt;/p&gt;
&lt;img alt="Screenshot of comment on StackOverflow. Comment reads: &amp;quot;Suggestion: Don't use Mako. It's horrible. [...]&amp;quot;" src="https://jamescooke.info/images/mako.png" /&gt;
&lt;p&gt;You should listen to a &lt;a class="reference external" href="https://stackoverflow.com/questions/10870379/is-there-an-equivalent-to-django-template-filters-in-mako"&gt;Stack Overflow moderator who has 93K points&lt;/a&gt;
at time of writing&amp;nbsp;right?&lt;/p&gt;
&lt;p&gt;Meanwhile&amp;#8230; I haven&amp;#8217;t found the reason for the template filter ordering being
strange - and I still think that the &lt;tt class="docutils literal"&gt;h&lt;/tt&gt; filter is putting itself last in the
mako render order, but now I&amp;#8217;ve got a work around, I&amp;#8217;m going back to post&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;Maybe 10 more people will see it before Easter - it might even help&amp;nbsp;someone.&lt;/p&gt;
</content><category term="Code"></category><category term="mako"></category><category term="python"></category></entry><entry><title>Password cases and test fixes on pyramid_simpleauth</title><link href="https://jamescooke.info/password-cases-and-test-fixes-on-pyramid_simpleauth.html" rel="alternate"></link><published>2012-11-30T12:00:00+00:00</published><updated>2012-11-30T12:00:00+00:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2012-11-30:/password-cases-and-test-fixes-on-pyramid_simpleauth.html</id><summary type="html">&lt;p class="first last"&gt;Updates to pyramid_simpleauth to allow for uppercase passwords and some bug&amp;nbsp;fixes.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;At Quibly we&amp;#8217;re using Pyramid at the centre of a Python framework. Providing user functionality is the &lt;a class="reference external" href="https://github.com/thruflo/pyramid_simpleauth"&gt;pyramid_simpleauth&lt;/a&gt;&amp;nbsp;library.&lt;/p&gt;
&lt;p&gt;While writing integration tests before we put the site live, I found that my test users we not able to authenticate with their testing passwords (usually just a simple string like &amp;#8216;Password&amp;#8217;). Digging inside the simpleauth library, I found some fixes necessary to how cases are handled by the lib - plus also fixed some doctests while I was at&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;These &lt;a class="reference external" href="https://github.com/thruflo/pyramid_simpleauth/pull/7"&gt;changes&lt;/a&gt; all
been merged now and the library rolled up a&amp;nbsp;version.&lt;/p&gt;
</content><category term="GitHub Contributions"></category><category term="topic:pyramid"></category></entry><entry><title>Django-mailchimp compatability with v1.3 API</title><link href="https://jamescooke.info/django-mailchimp-compatability-with-v13-api.html" rel="alternate"></link><published>2012-09-25T07:14:00+01:00</published><updated>2012-09-25T07:14:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2012-09-25:/django-mailchimp-compatability-with-v13-api.html</id><summary type="html">&lt;p class="first last"&gt;Some small updates to the django-mailchimp library to upgrade to the latest Mailchimp &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;For a &lt;a class="reference external" href="/pages/fublo-ltd.html"&gt;Fublo&lt;/a&gt; project with &lt;a class="reference external" href="https://www.neuxpower.com/"&gt;Neuxpower&lt;/a&gt;, we had to communicate with &lt;a class="reference external" href="https://developer.mailchimp.com/"&gt;Mailchimp via their
&lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt;. On Django one of the best libraries for
this is &lt;a class="reference external" href="https://github.com/piquadrat/django-mailchimp"&gt;django-mailchimp&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, in its previous state django-mailchimp wasn&amp;#8217;t able to specify a &lt;tt class="docutils literal"&gt;send_welcome&lt;/tt&gt; parameter which lets Mailchimp know whether it should send out a list welcome message when a new user subscribes. For the project, we were managing the signup explicitly with Neuxpower&amp;#8217;s code, so no welcome message was required and the default for Mailchimp was &lt;tt class="docutils literal"&gt;True&lt;/tt&gt; for sending meaning that Neuxpower&amp;#8217;s new customers would get hit with a double welcome message&amp;#8230; Not&amp;nbsp;desirable.&lt;/p&gt;
&lt;p&gt;This &lt;a class="reference external" href="https://github.com/piquadrat/django-mailchimp/pull/6"&gt;small change&lt;/a&gt; is now merged in with the library, which has rolled up to a &amp;#8216;v1.3&amp;#8217; status as there is no backward&amp;nbsp;compatibility.&lt;/p&gt;
</content><category term="GitHub Contributions"></category><category term="topic:django"></category></entry><entry><title>Fixing exception in django-menu</title><link href="https://jamescooke.info/fixing-exception-in-django-menu.html" rel="alternate"></link><published>2012-05-05T19:40:00+01:00</published><updated>2012-05-05T19:40:00+01:00</updated><author><name>James</name></author><id>tag:jamescooke.info,2012-05-05:/fixing-exception-in-django-menu.html</id><summary type="html">&lt;p class="first last"&gt;A tiny pull request to stop django-menu from throwing exceptions on
new unconfigured&amp;nbsp;sites.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="https://github.com/rossp/django-menu/"&gt;django-menu&lt;/a&gt; is a nice simple library for building very simple menus. However, when a site is loaded for the first time, the menu structure was not configured and so it was throwing a &lt;tt class="docutils literal"&gt;DoesNotExist&lt;/tt&gt; Exception.&lt;/p&gt;
&lt;p&gt;This tiny &lt;a class="reference external" href="https://github.com/rossp/django-menu/pull/5"&gt;pull request&lt;/a&gt; simply
wrapped the call to the menu in a &lt;tt class="docutils literal"&gt;try&lt;/tt&gt;/&lt;tt class="docutils literal"&gt;except&lt;/tt&gt; so that new sites using
django-menu won&amp;#8217;t fall over on first&amp;nbsp;load.&lt;/p&gt;
</content><category term="GitHub Contributions"></category><category term="topic:django"></category></entry></feed>