<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Ceci n'est pas une -EPIPE</title><link href="https://notes.pault.ag/" rel="alternate"></link><link href="https://notes.pault.ag/feeds/all-en.atom.xml" rel="self"></link><id>https://notes.pault.ag/</id><updated>2016-08-07T20:17:00-04:00</updated><entry><title>Using PKCS#11 on GNU/Linux</title><link href="https://notes.pault.ag/pkcs11" rel="alternate"></link><published>2016-08-07T20:17:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2016-08-07:pkcs11</id><summary type="html">&lt;p&gt;PKCS#11 is a standard API to interface with HSMs, Smart Cards, or other types
of random hardware backed crypto. On my travel laptop, I use a few Yubikeys in
PKCS#11 mode using OpenSC to handle system login. &lt;code&gt;libpam-pkcs11&lt;/code&gt; is a pretty
easy to use module that will let you log into your system locally using a
PKCS#11 token locally.&lt;/p&gt;
&lt;p&gt;One of the least documented things, though, was how to use an OpenSC PKCS#11
token in Chrome. First, close all web browsers you have open.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="x"&gt;sudo apt-get install libnss3-tools&lt;/span&gt;

&lt;span class="x"&gt;certutil -U -d sql:&lt;/span&gt;&lt;span class="p"&gt;$&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="x"&gt;/.pki/nssdb&lt;/span&gt;
&lt;span class="x"&gt;modutil -add &amp;quot;OpenSC&amp;quot; -libfile /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so -dbdir sql:&lt;/span&gt;&lt;span class="p"&gt;$&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="x"&gt;/.pki/nssdb&lt;/span&gt;
&lt;span class="x"&gt;modutil -list &amp;quot;OpenSC&amp;quot; -dbdir sql:&lt;/span&gt;&lt;span class="p"&gt;$&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="x"&gt;/.pki/nssdb &lt;/span&gt;
&lt;span class="x"&gt;modutil -enable &amp;quot;OpenSC&amp;quot; -dbdir sql:&lt;/span&gt;&lt;span class="p"&gt;$&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="x"&gt;/.pki/nssdb&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now, we'll have the PKCS#11 module ready for &lt;code&gt;nss&lt;/code&gt; to use, so let's double
check that the tokens are registered:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="x"&gt;certutil -U -d sql:&lt;/span&gt;&lt;span class="p"&gt;$&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="x"&gt;/.pki/nssdb&lt;/span&gt;
&lt;span class="x"&gt;certutil -L -h &amp;quot;OpenSC&amp;quot; -d sql:&lt;/span&gt;&lt;span class="p"&gt;$&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="x"&gt;/.pki/nssdb&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If this winds up causing issues, you can remove it using the following
command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="x"&gt;modutil -delete &amp;quot;OpenSC&amp;quot; -dbdir sql:&lt;/span&gt;&lt;span class="p"&gt;$&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="x"&gt;/.pki/nssdb&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;</summary><category term="pkcs11"></category><category term="debian"></category></entry><entry><title>Hacking a Projector in Hy</title><link href="https://notes.pault.ag/hacking-a-projector-in-hy" rel="alternate"></link><published>2016-07-31T12:02:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2016-07-31:hacking-a-projector-in-hy</id><summary type="html">&lt;p&gt;About a year ago, I bought a Projector after I finally admitted that I could
actually use a TV in my apartment. I settled on buying a
&lt;a href="http://ap.viewsonic.com/il/products/projectors/PJD5132.php"&gt;ViewSonic PJD5132&lt;/a&gt;.
It was a really great value, and has been nothing short of a delight to own.&lt;/p&gt;
&lt;p&gt;I was always a bit curious about the DB9 connector on the back of the unit,
so I dug into the user manual, and found some hex code strings in there. So,
last year, between my last gig at the
&lt;a href="https://sunlightfoundation.com/"&gt;Sunlight Foundtion&lt;/a&gt; and
&lt;a href="https://www.usds.gov/"&gt;USDS&lt;/a&gt;, I spent some time wandering around the US,
hitting up &lt;a href="https://debconf15.debconf.org/"&gt;DebConf&lt;/a&gt;, and exploring Washington
DC. Between trips, I set out to figure out exactly what was going on with my
Projector, and see if I could make it do anything fun.&lt;/p&gt;
&lt;p&gt;So, I started off with basics, and tried to work out how these command codes
were structured. I had a few working codes, but to write clean code, I'd be
better off understanding why the codes looked like they do. Let's look at
the "Power On" code.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;0x06 0x14 0x00 0x04 0x00 0x34 0x11 0x00 0x00 0x5D&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Some were 10 bytes, other were 11, and most started with similar looking
things. The first byte was usually a &lt;code&gt;0x06&lt;/code&gt; or &lt;code&gt;0x07&lt;/code&gt;, followed by two
bytes &lt;code&gt;0x14 0x00&lt;/code&gt;, and either a &lt;code&gt;0x04&lt;/code&gt; or &lt;code&gt;0x05&lt;/code&gt;. Since the first few bytes
were similarly structured, I assumed the first octet (either &lt;code&gt;0x06&lt;/code&gt; or &lt;code&gt;0x07&lt;/code&gt;)
was actually a length, since the first 4 octets seemed always present.&lt;/p&gt;
&lt;p&gt;So, my best guess is that we have a Length byte at index 0, followed by
two bytes for the Protocol, a flag for if you're Reading or Writing (best
guess on that one), and opaque data following that. Sometimes it's a const
of sorts, and sometimes an octet (either little or big endian, confusingly).&lt;/p&gt;
&lt;aside class="left"&gt;
  These are all just wild guesses, but thinking of it like this has actually
  helped a bit, so I'm just going to use this as my working understanding
  and adjust as needed.
&lt;/aside&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Length
 |         Read / Write
 |              |
 |   Protocol   |            Data
 |    |----|    |    |------------------------|
0x06 0x14 0x00 0x04 0x00 0x34 0x11 0x00 0x00 0x5D
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Right. OK. So, let's get to work. In the spirit of code is data, data is code,
I hacked up some of the projector codes into a s-expression we can use later.
The structure of this is boring, but it'll let us both store the command
code to issue, as well as define the handler to read the data back.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setv&lt;/span&gt; &lt;span class="nv"&gt;*commands*&lt;/span&gt;
  &lt;span class="c1"&gt;;  function                       type family         control&lt;/span&gt;
  &lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;power-on&lt;/span&gt;                         &lt;span class="nv"&gt;nil&lt;/span&gt; &lt;span class="nv"&gt;nil&lt;/span&gt;            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x06&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x14&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x04&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x34&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x11&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x5D&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;power-off&lt;/span&gt;                        &lt;span class="nv"&gt;nil&lt;/span&gt; &lt;span class="nv"&gt;nil&lt;/span&gt;            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x06&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x14&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x04&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x34&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x11&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x01&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x5E&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;power-status&lt;/span&gt;                   &lt;span class="nv"&gt;const&lt;/span&gt; &lt;span class="nv"&gt;power&lt;/span&gt;          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x07&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x14&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x05&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x34&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x11&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x5E&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;                            &lt;span class="nv"&gt;nil&lt;/span&gt; &lt;span class="nv"&gt;nil&lt;/span&gt;            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x06&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x14&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x04&lt;/span&gt;  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x34&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x11&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x02&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x5F&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nv"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As well as defining some of the const responses that come back from the
Projector itself. These are pretty boring, but it's helpful to put a
name to the int that falls out.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setv&lt;/span&gt; &lt;span class="nv"&gt;*consts*&lt;/span&gt;
  &lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;power&lt;/span&gt;        &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;           &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x01&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;       &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;           &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x01&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;x00&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
    &lt;span class="nv"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;After defining a few simple functions to write the byte arrays to the serial
port as well as reading and understanding responses from the projector, I could
start elaborating on some higher order functions that can talk projector. So
the first thing I wrote was to make a function that converts the command
entry into a native Hy function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;make-api-function&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;function&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt; &lt;span class="nv"&gt;family&lt;/span&gt; &lt;span class="nv"&gt;data&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="kd"&gt;defn &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="nv"&gt;function&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;serial&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;import &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PJD5132.dsl&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;interpret-response&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
              &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PJD5132.serial&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;read-response/raw&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;serial.write&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;bytearray&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;~@&lt;/span&gt;&lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;interpret-response&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str &lt;/span&gt;&lt;span class="nv"&gt;type&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="nb"&gt;str &lt;/span&gt;&lt;span class="nv"&gt;family&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;read-response/raw&lt;/span&gt; &lt;span class="nv"&gt;serial&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Fun. Fun! Now, we can invoke it to create a Hy &amp;amp; Python importable API wrapper
in a few lines!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;import &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PJD5132.commands&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;*commands*&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PJD5132.dsl&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;make-api-function&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;map &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn &lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;, &lt;span class="nv"&gt;function&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt; &lt;span class="nv"&gt;family&lt;/span&gt; &lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
               &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;make-api-function&lt;/span&gt; &lt;span class="nv"&gt;function&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt; &lt;span class="nv"&gt;family&lt;/span&gt; &lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nv"&gt;*commands*&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Cool. So, now we can import things like &lt;code&gt;power-on&lt;/code&gt; from &lt;code&gt;*commands*&lt;/code&gt; which
takes a single argument (&lt;code&gt;serial&lt;/code&gt;) for the serial port, and it'll send a
command, and return the response. The best part about all this is you only
have to define the data once in a list, and the rest comes for free.&lt;/p&gt;
&lt;p&gt;Finally, I do want to be able to turn my projector on and off over the network
so I went ahead and make a Flask "API" on top of this. First, let's define
a macro to define Flask routes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defmacro &lt;/span&gt;&lt;span class="nv"&gt;defroute&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name root &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;rest &lt;/span&gt;&lt;span class="nv"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;import &lt;/span&gt;&lt;span class="nv"&gt;os.path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;generate-method&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;path &lt;/span&gt;&lt;span class="nv"&gt;method&lt;/span&gt; &lt;span class="nv"&gt;status&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="nf"&gt;with-decorator&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;app.route&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;import &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;PJD5132.api&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="nv"&gt;method&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt; &lt;span class="nv"&gt;status&lt;/span&gt; &lt;span class="nv"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)]])&lt;/span&gt;

       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;try&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;do &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setv&lt;/span&gt; &lt;span class="nv"&gt;ret&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="nv"&gt;method&lt;/span&gt; &lt;span class="nv"&gt;serial-line&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;if &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setv&lt;/span&gt; &lt;span class="nv"&gt;ret&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt; &lt;span class="nv"&gt;serial-line&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;json.dumps&lt;/span&gt; &lt;span class="nv"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;except&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;e&lt;/span&gt; &lt;span class="nv"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setv&lt;/span&gt; &lt;span class="nv"&gt;response&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;make-response&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.format&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Fatal Error: ValueError: {}&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str &lt;/span&gt;&lt;span class="nv"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setv&lt;/span&gt; &lt;span class="nv"&gt;response.status-code&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="nv"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setv&lt;/span&gt; &lt;span class="nb"&gt;path &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.format&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/projector/{}&amp;quot;&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setv&lt;/span&gt; &lt;span class="nv"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dict&lt;/span&gt; &lt;span class="nv"&gt;methods&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;do &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generate-method&lt;/span&gt; &lt;span class="nb"&gt;path root &lt;/span&gt;&lt;span class="nv"&gt;nil&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="nf"&gt;list-comp&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generate-method&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;os.path.join&lt;/span&gt; &lt;span class="nb"&gt;path &lt;/span&gt;&lt;span class="nv"&gt;method-path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;method&lt;/span&gt; &lt;span class="nv"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;[(&lt;/span&gt;, &lt;span class="nv"&gt;method-path&lt;/span&gt; &lt;span class="nv"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;])))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now, we can define how we want our API to look, so let's define the &lt;code&gt;power&lt;/code&gt;
route, which will expand out into the Flask route code above.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defroute&lt;/span&gt; &lt;span class="nv"&gt;power&lt;/span&gt;
  &lt;span class="nv"&gt;power-status&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;on&amp;quot;&lt;/span&gt;  &lt;span class="nv"&gt;power-on&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;off&amp;quot;&lt;/span&gt; &lt;span class="nv"&gt;power-off&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And now, let's play with it!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ curl http://192.168.1.50/projector/power
&lt;span class="s2"&gt;&amp;quot;off&amp;quot;&lt;/span&gt;
$ curl http://192.168.1.50/projector/power/on
&lt;span class="s2"&gt;&amp;quot;on&amp;quot;&lt;/span&gt;
$ curl http://192.168.1.50/projector/power
&lt;span class="s2"&gt;&amp;quot;on&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Or, the volume!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ curl 192.168.1.50/projector/volume
10
$ curl 192.168.1.50/projector/volume/decrease
9
$ curl 192.168.1.50/projector/volume/decrease
8
$ curl 192.168.1.50/projector/volume/decrease
7
$ curl 192.168.1.50/projector/volume/increase
8
$ curl 192.168.1.50/projector/volume/increase
9
$ curl 192.168.1.50/projector/volume/increase
10
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Check out the full source over at &lt;a href="https://github.com/paultag/PJD5132/"&gt;github.com/paultag/PJD5132&lt;/a&gt;!&lt;/p&gt;</summary><category term="python"></category><category term="hy"></category><category term="pjd5132"></category></entry><entry><title>The Open Source License API</title><link href="https://notes.pault.ag/osi-license-api" rel="alternate"></link><published>2016-07-16T15:30:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2016-07-16:osi-license-api</id><summary type="html">&lt;p&gt;Around a year ago, I started hacking together a machine readable version
of the OSI approved licenses list, and casually picking parts up until it
was ready to launch. A few weeks ago, we officially announced
the &lt;a href="https://opensource.org/node/822"&gt;osi license api&lt;/a&gt;, which is now
live at &lt;a href="https://api.opensource.org/"&gt;api.opensource.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I also took a whack at writing a few API bindings, in
&lt;a href="https://github.com/opensourceorg/python-opensource"&gt;Python&lt;/a&gt;,
&lt;a href="https://github.com/opensourceorg/ruby-opensourceapi"&gt;Ruby&lt;/a&gt;,
and using the models from the API implementation itself in
&lt;a href="https://github.com/OpenSourceOrg/api/tree/master/client"&gt;Go&lt;/a&gt;. In the following
few weeks, &lt;a href="https://github.com/clinty"&gt;Clint&lt;/a&gt; wrote one in &lt;a href="https://github.com/OpenSourceOrg/haskell-opensource"&gt;Haskell&lt;/a&gt;,
&lt;a href="https://mornie.org/"&gt;Eriol&lt;/a&gt; wrote one in &lt;a href="https://github.com/opensourceorg/rust-opensource"&gt;Rust&lt;/a&gt;,
and &lt;a href="https://ironholds.org/"&gt;Oliver&lt;/a&gt; wrote one in &lt;a href="https://cran.r-project.org/web/packages/osi/"&gt;R&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The data is sourced from a &lt;a href="https://github.com/opensourceorg/licenses"&gt;repo on GitHub&lt;/a&gt;,
the &lt;code&gt;licenses&lt;/code&gt; repo under &lt;code&gt;OpenSourceOrg&lt;/code&gt;. Pull Requests against that repo are
wildly encouraged! Additional data ideas, cleanup or more hand collected data
would be wonderful!&lt;/p&gt;
&lt;p&gt;In the meantime, use-cases for using this API range from language package
managers pulling OSI approval of a licence programmatically to using a license
identifier as defined in one dataset (SPDX, for example), and using that
to find the identifier as it exists in another system (DEP5, Wikipedia,
TL;DR Legal).&lt;/p&gt;
&lt;p&gt;Patches are hugely welcome, as are bug reports or ideas! I'd also love more
API wrappers for other languages!&lt;/p&gt;</summary><category term="osi"></category><category term="opendata"></category><category term="licenses"></category></entry><entry><title>Hello, InfluxDB</title><link href="https://notes.pault.ag/hello-influxdb" rel="alternate"></link><published>2016-07-02T13:13:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2016-07-02:hello-influxdb</id><summary type="html">&lt;p&gt;Last week, I posted about &lt;a href="https://notes.pault.ag/hello-sense/"&gt;python-sense&lt;/a&gt;,
and API wrapper for the internal Sense API. I wrote this so that I could
pull data about myself into my own databases, allowing me to use that
information for myself.&lt;/p&gt;
&lt;p&gt;One way I'm doing this is by pulling my room data into an
&lt;a href="https://influxdata.com/"&gt;InfluxDB&lt;/a&gt; database, letting me run time series
queries against my environmental data.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;influxdb&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InfluxDBClient&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;dt&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sense.service&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sense&lt;/span&gt;

&lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sense&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="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_sensors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;flavor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;series&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&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;datum&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;series&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datum&lt;/span&gt;&lt;span class="p"&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&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="k"&gt;continue&lt;/span&gt;

            &lt;span class="n"&gt;timezone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datum&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;offset_millis&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;))&lt;/span&gt;

            &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dt&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;fromtimestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;datum&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;datetime&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&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="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tzinfo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timezone&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;flavor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&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;InfluxDBClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;url.to.host.here&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;sense&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&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;series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;flavor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;measurement&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;{}&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flavor&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;tags&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;user&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;paultag&amp;quot;&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;fields&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;value&amp;quot;&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="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&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;write_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I'm able to run this on a cron, automatically loading data from the Sense
API into my Influx database. I can then use that with something like
&lt;a href="http://grafana.org/"&gt;Grafana&lt;/a&gt;, to check out what my room looks like over
time.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://notes.pault.ag/static/posts/hello-influx/sense-influx-light.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="http://notes.pault.ag/static/posts/hello-influx/sense-influx-temp.png" /&gt;&lt;/p&gt;</summary><category term="python"></category><category term="influxdb"></category><category term="hello"></category><category term="sense"></category></entry><entry><title>Hello, Sense!</title><link href="https://notes.pault.ag/hello-sense" rel="alternate"></link><published>2016-06-26T21:42:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2016-06-26:hello-sense</id><summary type="html">&lt;p&gt;A while back, I saw a &lt;a href="https://www.kickstarter.com/projects/hello/sense-know-more-sleep-better"&gt;Kickstarter&lt;/a&gt;
for one of the most well designed and pretty sleep trackers on the market. I
fell in love with it, and it has stuck with me since.&lt;/p&gt;
&lt;p&gt;A few months ago, I finally got my hands on one and started to track my data.
Naturally, I now want to store this new data with the rest of the data I have
on myself in my own databases.&lt;/p&gt;
&lt;p&gt;I went in search of an API, but I found that the Sense API hasn't been published
yet, and is being worked on by the team. Here's hoping it'll land soon!&lt;/p&gt;
&lt;p&gt;After some subdomain guessing, I hit on &lt;a href="https://api.hello.is"&gt;api.hello.is&lt;/a&gt;.
So, naturally, I went to take a quick look at their Android app and network
traffic, lo and behold, there was a pretty nicely designed API.&lt;/p&gt;
&lt;p&gt;This API is clearly an internal API, and as such, it's something that
&lt;strong&gt;should not&lt;/strong&gt; be considered stable. However, I'm OK with a fragile API,
so &lt;a href="https://github.com/paultag/python-sense"&gt;I've published a quick and dirty API wrapper for the Sense API
to my GitHub.&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I've published it because I've found it useful, but I can't promise the world,
(since I'm not a member of the Sense team at Hello!), so here are a few ground
rules of this wrapper:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I make no claims to the stability or completeness.&lt;/li&gt;
&lt;li&gt;I have no documentation or assurances.&lt;/li&gt;
&lt;li&gt;I will not provide the client secret and ID. You'll have to find them on
   your own.&lt;/li&gt;
&lt;li&gt;This may stop working without any notice, and there may even be really nasty
   bugs that result in your alarm going off at 4 AM.&lt;/li&gt;
&lt;li&gt;Send PRs! This is a side-project for me.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This module is currently Python 3 only. If someone really needs Python 2
support, I'm open to minimally invasive patches to the codebase using
&lt;code&gt;six&lt;/code&gt; to support Python 2.7.&lt;/p&gt;
&lt;h2&gt;Working with the API:&lt;/h2&gt;
&lt;p&gt;First, let's go ahead and log in using &lt;code&gt;python -m sense&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ python -m sense
Sense OAuth Client ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Sense OAuth Client Secret: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Sense email: paultag@gmail.com
Sense password: 
Attempting to log into Sense&lt;span class="s1"&gt;&amp;#39;s API&lt;/span&gt;
&lt;span class="s1"&gt;Success!&lt;/span&gt;
&lt;span class="s1"&gt;Attempting to query the Sense API&lt;/span&gt;
&lt;span class="s1"&gt;The humidity is **just right**.&lt;/span&gt;
&lt;span class="s1"&gt;The air quality is **just right**.&lt;/span&gt;
&lt;span class="s1"&gt;The light level is **just right**.&lt;/span&gt;
&lt;span class="s1"&gt;It&amp;#39;&lt;/span&gt;s **pretty hot** in here.
The noise level is **just right**.
Success!
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now, let's see if we can pull up information on my Sense:&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;sense&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sense&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sense&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sense&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;sense&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devices&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;senses&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;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;xxxxxxxxxxxxxxxx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;firmware_version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;11a1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;last_updated&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1466991060000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;state&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;NORMAL&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;wifi_info&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;rssi&amp;#39;&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="s1"&gt;&amp;#39;ssid&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Pretty Fly for a WiFi (2.4 GhZ)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;condition&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;GOOD&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;last_updated&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1462927722000&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;BLACK&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;pills&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;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;xxxxxxxxxxxxxxxx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;firmware_version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;last_updated&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1466990339000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;battery_level&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;87&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;color&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;BLUE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;state&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;NORMAL&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Neat! Pretty cool. Look, you can even see my WiFi AP! Let's try some more
and pull some trends out.&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;values&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="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;value&amp;quot;&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;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sense&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_sensors&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;humidity&amp;quot;&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="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;45.73904&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mf"&gt;45.985928&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I plan to keep maintaining it as long as it's needed, so I welcome
co-maintainers, and I'd love to see what people build with it! So far, I'm
using it to dump my room data into InfluxDB, pulling information on my room
into Grafana. Hopefully more to come!&lt;/p&gt;
&lt;p&gt;Happy hacking!&lt;/p&gt;</summary><category term="python"></category><category term="sense"></category><category term="hello.is"></category></entry><entry><title>Go Debian!</title><link href="https://notes.pault.ag/go-debian" rel="alternate"></link><published>2016-06-19T12:30:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2016-06-19:go-debian</id><summary type="html">&lt;p&gt;As some of the world knows full well by now, I've been noodling with Go
for a few years, working through its pros, its cons, and thinking a lot
about how humans use code to express thoughts and ideas. Go's got a lot of
neat use cases, suited to particular problems, and used in the right place,
you can see some clear massive wins.&lt;/p&gt;
&lt;aside class="left"&gt;
Some of the things Go is great at: Writing a server. Dealing with asynchronous
communication. Backend and front-end in the same binary. Fast and memory safe.
&lt;/aside&gt;

&lt;aside class="right"&gt;
Things Go is bad at: Having to rebuild everything for a CVE. Having if
`err != nil` everywhere. "Better than C" being the excuse for bad semantics.
No generics, cgo (enough said)
&lt;/aside&gt;

&lt;p&gt;I've started writing Debian tooling in Go, because it's a pretty natural fit.
Go's fairly tight, and overhead shouldn't be taken up by your operating system.
After a while, I wound up hitting the usual blockers, and started to build up
abstractions. They became pretty darn useful, so, this blog post is announcing
(a still incomplete, year old and perhaps API changing) Debian package for Go.
The Go importable name is &lt;code&gt;pault.ag/go/debian&lt;/code&gt;. This contains a lot of utilities
for dealing with Debian packages, and will become an edited down "toolbelt"
for working with or on Debian packages.&lt;/p&gt;
&lt;h1&gt;Module Overview&lt;/h1&gt;
&lt;p&gt;Currently, the package contains 4 major sub packages. They're a &lt;code&gt;changelog&lt;/code&gt;
parser, a &lt;code&gt;control&lt;/code&gt; file parser, &lt;code&gt;deb&lt;/code&gt; file format parser, &lt;code&gt;dependency&lt;/code&gt; parser
and a &lt;code&gt;version&lt;/code&gt; parser. Together, these are a set of powerful building blocks
which can be used together to create higher order systems with reliable
understandings of the world.&lt;/p&gt;
&lt;h2&gt;changelog&lt;/h2&gt;
&lt;p&gt;The first (and perhaps most incomplete and least tested) is a &lt;a href="https://godoc.org/pault.ag/go/debian/changelog"&gt;changelog file
parser.&lt;/a&gt;. This provides the
programmer with the ability to pull out the suite being targeted in the
changelog, when each upload was, and the version for each. For example, let's
look at how we can pull when all the uploads of Docker to sid took place:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://metadata.ftp-master.debian.org/changelogs/main/d/docker.io/unstable_changelog&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;allEntries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;changelog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="nx"&gt;allEntries&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Version %s was uploaded on %s\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;When&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The output of which looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Version 1.8.3~ds1-2 was uploaded on 2015-11-04 00:09:02 -0800 -0800
Version 1.8.3~ds1-1 was uploaded on 2015-10-29 19:40:51 -0700 -0700
Version 1.8.2~ds1-2 was uploaded on 2015-10-29 07:23:10 -0700 -0700
Version 1.8.2~ds1-1 was uploaded on 2015-10-28 14:21:00 -0700 -0700
Version 1.7.1~dfsg1-1 was uploaded on 2015-08-26 10:13:48 -0700 -0700
Version 1.6.2~dfsg1-2 was uploaded on 2015-07-01 07:45:19 -0600 -0600
Version 1.6.2~dfsg1-1 was uploaded on 2015-05-21 00:47:43 -0600 -0600
Version 1.6.1+dfsg1-2 was uploaded on 2015-05-10 13:02:54 -0400 EDT
Version 1.6.1+dfsg1-1 was uploaded on 2015-05-08 17:57:10 -0600 -0600
Version 1.6.0+dfsg1-1 was uploaded on 2015-05-05 15:10:49 -0600 -0600
Version 1.6.0+dfsg1-1~exp1 was uploaded on 2015-04-16 18:00:21 -0600 -0600
Version 1.6.0~rc7~dfsg1-1~exp1 was uploaded on 2015-04-15 19:35:46 -0600 -0600
Version 1.6.0~rc4~dfsg1-1 was uploaded on 2015-04-06 17:11:33 -0600 -0600
Version 1.5.0~dfsg1-1 was uploaded on 2015-03-10 22:58:49 -0600 -0600
Version 1.3.3~dfsg1-2 was uploaded on 2015-01-03 00:11:47 -0700 -0700
Version 1.3.3~dfsg1-1 was uploaded on 2014-12-18 21:54:12 -0700 -0700
Version 1.3.2~dfsg1-1 was uploaded on 2014-11-24 19:14:28 -0500 EST
Version 1.3.1~dfsg1-2 was uploaded on 2014-11-07 13:11:34 -0700 -0700
Version 1.3.1~dfsg1-1 was uploaded on 2014-11-03 08:26:29 -0700 -0700
Version 1.3.0~dfsg1-1 was uploaded on 2014-10-17 00:56:07 -0600 -0600
Version 1.2.0~dfsg1-2 was uploaded on 2014-10-09 00:08:11 +0000 +0000
Version 1.2.0~dfsg1-1 was uploaded on 2014-09-13 11:43:17 -0600 -0600
Version 1.0.0~dfsg1-1 was uploaded on 2014-06-13 21:04:53 -0400 EDT
Version 0.11.1~dfsg1-1 was uploaded on 2014-05-09 17:30:45 -0400 EDT
Version 0.9.1~dfsg1-2 was uploaded on 2014-04-08 23:19:08 -0400 EDT
Version 0.9.1~dfsg1-1 was uploaded on 2014-04-03 21:38:30 -0400 EDT
Version 0.9.0+dfsg1-1 was uploaded on 2014-03-11 22:24:31 -0400 EDT
Version 0.8.1+dfsg1-1 was uploaded on 2014-02-25 20:56:31 -0500 EST
Version 0.8.0+dfsg1-2 was uploaded on 2014-02-15 17:51:58 -0500 EST
Version 0.8.0+dfsg1-1 was uploaded on 2014-02-10 20:41:10 -0500 EST
Version 0.7.6+dfsg1-1 was uploaded on 2014-01-22 22:50:47 -0500 EST
Version 0.7.1+dfsg1-1 was uploaded on 2014-01-15 20:22:34 -0500 EST
Version 0.6.7+dfsg1-3 was uploaded on 2014-01-09 20:10:20 -0500 EST
Version 0.6.7+dfsg1-2 was uploaded on 2014-01-08 19:14:02 -0500 EST
Version 0.6.7+dfsg1-1 was uploaded on 2014-01-07 21:06:10 -0500 EST
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;control&lt;/h2&gt;
&lt;p&gt;Next is one of the most complex, and one of the oldest parts of &lt;code&gt;go-debian&lt;/code&gt;,
which is the &lt;a href="https://godoc.org/pault.ag/go/debian/control"&gt;control file parser&lt;/a&gt;
(otherwise sometimes known as &lt;code&gt;deb822&lt;/code&gt;). This module was inspired by the way
that the &lt;code&gt;json&lt;/code&gt; module works in Go, allowing for files to be defined in code
with a &lt;code&gt;struct&lt;/code&gt;. This tends to be a bit more declarative, but also winds up
putting logic into struct tags, which can be a nasty anti-pattern if used too
much.&lt;/p&gt;
&lt;p&gt;The first primitive in this module is the concept of a &lt;code&gt;Paragraph&lt;/code&gt;, a struct
containing two values, the order of keys seen, and a map of &lt;code&gt;string&lt;/code&gt; to &lt;code&gt;string&lt;/code&gt;.
All higher order functions dealing with control files will go through this
type, which is a helpful interchange format to be aware of. All parsing of
meaning from the Control file happens when the Paragraph is unpacked into
a struct using reflection.&lt;/p&gt;
&lt;p&gt;The idea behind this strategy that you define your struct, and let the Control
parser handle unpacking the data from the IO into your container, letting you
maintain type safety, since you never have to read and cast, the conversion
will handle this, and return an Unmarshaling error in the event of failure.&lt;/p&gt;
&lt;aside class="right"&gt;
I'm starting to think parsing and defining the control structs are two different
tasks and should be split apart -- or the common structs ought to be removed
entirely. More on this later.
&lt;/aside&gt;

&lt;p&gt;Additionally, Structs that define an anonymous member of &lt;code&gt;control.Paragraph&lt;/code&gt;
will have the raw &lt;code&gt;Paragraph&lt;/code&gt; struct of the underlying file, allowing the
programmer to handle dynamic tags (such as &lt;code&gt;X-Foo&lt;/code&gt;), or at least, letting
them survive the round-trip through go.&lt;/p&gt;
&lt;p&gt;The default &lt;a href="https://godoc.org/pault.ag/go/debian/control#NewDecoder"&gt;decoder&lt;/a&gt;
contains an argument, the ability to verify the input control file using an
OpenPGP keyring, which is exposed to the programmer through the
&lt;code&gt;(*Decoder).Signer()&lt;/code&gt; function. If the passed argument is nil, it will not
check the input file signature (at all!), and if it has been passed, any
signed data must be found or an &lt;code&gt;error&lt;/code&gt; will fall out of the &lt;code&gt;NewDecoder&lt;/code&gt; call.
On the way out, the opposite happens, where the struct is introspected,
turned into a &lt;code&gt;control.Paragraph&lt;/code&gt;, and then written out to the &lt;code&gt;io.Writer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here's a quick (and VERY dirty) example showing the basics of reading and
writing Debian Control files with &lt;code&gt;go-debian&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;fmt&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;io&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;net/http&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;strings&amp;quot;&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;pault.ag/go/debian/control&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AllowedPackage&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Package&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;Fingerprint&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;AllowedPackage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;UnmarshalControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TrimSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SplitN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot; &amp;quot;&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="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;)&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Syntax sucks: &amp;#39;%s&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Package&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunks&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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fingerprint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunks&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="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&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="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="kc"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DMUA&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Fingerprint&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;Uid&lt;/span&gt;             &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;AllowedPackages&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;AllowedPackage&lt;/span&gt; &lt;span class="s"&gt;`control:&amp;quot;Allow&amp;quot; delim:&amp;quot;,&amp;quot;`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://metadata.ftp-master.debian.org/dm.txt&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;decoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;dmua&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;DMUA&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;decoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;dmua&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EOF&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;The DM %s is allowed to upload:\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dmua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Uid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;allowedPackage&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="nx"&gt;dmua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AllowedPackages&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;   %s [granted by %s]\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;allowedPackage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Package&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;allowedPackage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Fingerprint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Output (truncated!) looks a bit like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;...
The DM Allison Randal &amp;lt;allison@lohutok.net&amp;gt; is allowed to upload:
   parrot [granted by A4F455C3414B10563FCC9244AFA51BD6CDE573CB]
...
The DM Benjamin Barenblat &amp;lt;bbaren@mit.edu&amp;gt; is allowed to upload:
   boogie [granted by 3224C4469D7DF8F3D6F41A02BBC756DDBE595F6B]
   dafny [granted by 3224C4469D7DF8F3D6F41A02BBC756DDBE595F6B]
   transmission-remote-gtk [granted by 3224C4469D7DF8F3D6F41A02BBC756DDBE595F6B]
   urweb [granted by 3224C4469D7DF8F3D6F41A02BBC756DDBE595F6B]
...
The DM أحمد المحمودي &amp;lt;aelmahmoudy@sabily.org&amp;gt; is allowed to upload:
   covered [granted by 41352A3B4726ACC590940097F0A98A4C4CD6E3D2]
   dico [granted by 6ADD5093AC6D1072C9129000B1CCD97290267086]
   drawtiming [granted by 41352A3B4726ACC590940097F0A98A4C4CD6E3D2]
   fonts-hosny-amiri [granted by BD838A2BAAF9E3408BD9646833BE1A0A8C2ED8FF]
   ...
...
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;deb&lt;/h2&gt;
&lt;p&gt;Next up, we've got the &lt;code&gt;deb&lt;/code&gt; module. This contains code to handle reading
Debian 2.0 &lt;code&gt;.deb&lt;/code&gt; files. It contains a wrapper that will parse the control
member, and provide the data member through the
&lt;a href="https://godoc.org/archive/tar"&gt;archive/tar&lt;/a&gt; interface.&lt;/p&gt;
&lt;p&gt;Here's an example of how to read a &lt;code&gt;.deb&lt;/code&gt; file, access some metadata, and
iterate over the &lt;code&gt;tar&lt;/code&gt; archive, and print the filenames of each of the
entries.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/tmp/fluxbox_1.3.5-2+b1_amd64.deb&amp;quot;&lt;/span&gt;
    &lt;span class="nx"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nx"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nx"&gt;debFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;deb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;debFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Version&lt;/span&gt;
    &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;&amp;quot;Epoch: %d, Version: %s, Revision: %s\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Epoch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Revision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;debFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EOF&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;  -&amp;gt; %s\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hdr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Boringly, the output looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;Epoch&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="o"&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;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Revision&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="n"&gt;b1&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="sr"&gt;/menu-methods/&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="sr"&gt;/menu-methods/&lt;/span&gt;&lt;span class="n"&gt;fluxbox&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="sr"&gt;/X11/&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="sr"&gt;/X11/fluxbox/&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="sr"&gt;/X11/fluxbox/&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;menu&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="sr"&gt;/X11/fluxbox/&lt;/span&gt;&lt;span class="n"&gt;fluxbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;menu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="sr"&gt;/X11/fluxbox/&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="sr"&gt;/X11/fluxbox/i&lt;/span&gt;&lt;span class="n"&gt;nit&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="sr"&gt;/X11/fluxbox/s&lt;/span&gt;&lt;span class="n"&gt;ystem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fluxbox&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;menu&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="sr"&gt;/X11/fluxbox/&lt;/span&gt;&lt;span class="n"&gt;overlay&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="sr"&gt;/X11/fluxbox/&lt;/span&gt;&lt;span class="n"&gt;apps&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="sr"&gt;/share/&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="sr"&gt;/share/man/&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="sr"&gt;/share/man/man5/&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="sr"&gt;/share/man/man5/&lt;/span&gt;&lt;span class="n"&gt;fluxbox&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gz&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="sr"&gt;/share/man/man5/&lt;/span&gt;&lt;span class="n"&gt;fluxbox&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;menu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gz&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="sr"&gt;/share/man/man5/&lt;/span&gt;&lt;span class="n"&gt;fluxbox&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gz&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="sr"&gt;/share/man/man5/&lt;/span&gt;&lt;span class="n"&gt;fluxbox&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gz&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="sr"&gt;/share/man/man1/&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;./&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="sr"&gt;/share/man/man1/s&lt;/span&gt;&lt;span class="n"&gt;tartfluxbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gz&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;dependency&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;dependency&lt;/code&gt; package provides an interface to parse and compute
dependencies. This package is a bit odd in that, well, there's no other
library that does this. The issue is that there are actually two different
parsers that compute our Dependency lines, one in Perl (as part of &lt;code&gt;dpkg-dev&lt;/code&gt;)
and another in C (in &lt;code&gt;dpkg&lt;/code&gt;).&lt;/p&gt;
&lt;aside class="left"&gt;
I have yet to track it down, but it's shockingly likely that `apt` has another
in `C++`, and maybe another in `aptitude`. I don't know this for a fact, so
I'll assume nothing
&lt;/aside&gt;

&lt;p&gt;To date, this has resulted in me filing
&lt;a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816473"&gt;three&lt;/a&gt;
&lt;a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=784808"&gt;different&lt;/a&gt;
&lt;a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=784806"&gt;bugs&lt;/a&gt;.
I also found a broken package in the
&lt;a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816741"&gt;archive&lt;/a&gt;,
which actually resulted in another bug being (totally accidentally)
&lt;a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=815478"&gt;already fixed&lt;/a&gt;.
I hope to continue to run the archive through my parser in hopes of finding
more bugs! This package is a bit complex, but it basically just returns what
amounts to be an &lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree"&gt;AST&lt;/a&gt;
for our Dependency lines. I'm positive there are bugs, so file them!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;foo | bar, baz, foobar [amd64] | bazfoo [!sparc], fnord:armhf [gnu-linux-sparc]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;anySparc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParseArch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;sparc&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;possi&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GetPossibilities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;anySparc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%s (%s)\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;possi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;possi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Arch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Gives the output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;foo (&amp;lt;nil&amp;gt;)
baz (&amp;lt;nil&amp;gt;)
fnord (armhf)
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;version&lt;/h2&gt;
&lt;p&gt;Right off the bat, I'd like to thank
&lt;a href="https://twitter.com/zekjur"&gt;Michael Stapelberg&lt;/a&gt; for letting me graft this
out of &lt;a href="https://github.com/debian/dcs"&gt;dcs&lt;/a&gt; and into the &lt;code&gt;go-debian&lt;/code&gt; package.
This was nearly entirely his work (with a one or two line function I added
later), and was amazingly helpful to have. Thank you!&lt;/p&gt;
&lt;p&gt;This module implements Debian version comparisons and parsing, allowing for
sorting in lists, checking to see if it's native or not, and letting the
programmer to implement smart(er!) logic based on upstream (or Debian)
version numbers.&lt;/p&gt;
&lt;p&gt;This module is extremely easy to use and very straightforward, and not worth
writing an example for.&lt;/p&gt;
&lt;h1&gt;Final thoughts&lt;/h1&gt;
&lt;p&gt;This is more of a "Yeah, OK, this has been useful enough to me at this point
that I'm going to support this" rather than a "It's stable!" or even
"It's alive!" post. Hopefully folks can report bugs and help iterate on
this module until we have some really clean building blocks to build
solid higher level systems on top of. Being able to have multiple libraries
interoperate by relying on &lt;code&gt;go-debian&lt;/code&gt; will be a massive ease.
I'm in need of more documentation, and to finalize some parts of the older
sub package APIs, but I'm hoping to be at a "1.0" real soon now.&lt;/p&gt;</summary><category term="golang"></category><category term="debian"></category></entry><entry><title>It's all relative</title><link href="https://notes.pault.ag/its-all-relative" rel="alternate"></link><published>2016-06-10T23:45:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2016-06-10:its-all-relative</id><summary type="html">&lt;p&gt;As nearly anyone who's worked with me will attest to, I've long since
touted &lt;a href="http://nedbatchelder.com"&gt;nedbat's&lt;/a&gt; talk
&lt;a href="http://nedbatchelder.com/text/unipain.html"&gt;Pragmatic Unicode, or, How do I stop the pain?&lt;/a&gt;
as one of the most foundational talks, and required watching for all programmers.&lt;/p&gt;
&lt;p&gt;The reason is because netbat hits on something bigger - something more
fundamental than how to handle Unicode -- it's how to handle data which is
relative.&lt;/p&gt;
&lt;p&gt;For those who want the TL;DR, the argument is as follows:&lt;/p&gt;
&lt;p&gt;Facts of Life:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Computers work with Bytes. Bytes go in, Bytes go out.&lt;/li&gt;
&lt;li&gt;The world needs more than 256 symbols.&lt;/li&gt;
&lt;li&gt;You need both Bytes and Unicode&lt;/li&gt;
&lt;li&gt;You cannot infer the encoding of bytes.&lt;/li&gt;
&lt;li&gt;Declared encodings can be Wrong&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, to fix it, the following protips:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="http://nedbatchelder.com/text/unipain/unipain.html#35"&gt;Unicode sandwich&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Know what you have&lt;/li&gt;
&lt;li&gt;TEST&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Relative Data&lt;/h2&gt;
&lt;p&gt;I've started to think more about why we do the things we do when we write
code, and one thing that continues to be a source of morbid schadenfreude
is watching code break by failing to handle Unicode right. It's hard! However,
watching &lt;em&gt;what&lt;/em&gt; breaks lets you gain a bit of insight into how the author
thinks, and what assumptions they make.&lt;/p&gt;
&lt;p&gt;When you send someone Unicode, there are a lot of assumptions that have to be
made. Your computer has to trust what you (yes, you!) entered into your web
browser, your web browser has to pass that on over the network (most of the
time without encoding information), to a server which reads that bytestream,
and makes a wild guess at what it should be. That server might save it to a
database, and interpolate it into an HTML template in a different encoding
(called &lt;a href="https://simple.wikipedia.org/wiki/Mojibake"&gt;Mojibake&lt;/a&gt;), resulting
in a bad time for everyone involved.&lt;/p&gt;
&lt;p&gt;Everything's awful, and the fact our computers can continue to display
text to us is a goddamn miracle. Never forget that.&lt;/p&gt;
&lt;p&gt;When it comes down to it, when I see a byte sitting on a page, I don't know
(and can't know!) if it's &lt;code&gt;Windows-1252&lt;/code&gt;, &lt;code&gt;UTF-8&lt;/code&gt;, &lt;code&gt;Latin-1&lt;/code&gt;, or &lt;code&gt;EBCDIC&lt;/code&gt;. What's
a poem to me is terminal garbage to you.&lt;/p&gt;
&lt;p&gt;Over the years, hacks have evolved. We have
&lt;a href="https://en.wikipedia.org/wiki/Magic_number_(programming)"&gt;magic numbers&lt;/a&gt;,
and plain ole' hacks to just guess based on the content. Of course, like
all good computer programs, this has lead to its fair share of hilarious
&lt;a href="https://bugs.launchpad.net/ubuntu/+source/cupsys/+bug/255161/comments/28"&gt;bugs&lt;/a&gt;,
and there's nothing stopping files from (validly!) being multiple things at the
same time.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Like many things, it's all in the eye of the beholder&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Timezones&lt;/h2&gt;
&lt;p&gt;Just like Unicode, this is a word that can put your friendly neighborhood
programmer into a series of profanity laden tirades. Go find one in the wild,
and ask them about what they think about timezone handling bugs they've seen.
I'll wait. Go ahead.&lt;/p&gt;
&lt;p&gt;Rants are funny things. They're fun to watch. Hilarious to give. Sometimes
just getting it all out can help. They can tell you a lot about the true
nature of problems.&lt;/p&gt;
&lt;p&gt;It's funny to consider the isomorphic nature of Unicode rants and Timezone
rants.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I don't think this is an accident.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;U̶n̶i̶c̶o̶d̶e̶ timezone Sandwich&lt;/h2&gt;
&lt;p&gt;Ned's Unicode Sandwich applies -- As early as we can, in the lowest level
we can (reading from the database, filesystem, wherever!), all datetimes
must be timezone qualified with their correct timezone. Always. If you mean
UTC, say it's in UTC.&lt;/p&gt;
&lt;p&gt;Treat any unqualified datetimes as "bytes". They're not to be trusted.
&lt;a href="https://youtu.be/W7wpzKvNhfA?t=3m18s"&gt;Never, never, never trust 'em&lt;/a&gt;. Don't
process any datetimes until you're sure they're in the right timezone.&lt;/p&gt;
&lt;p&gt;This lets the delicious inside of your datetime sandwich handle timezones
with grace, and finally, as late as you can, turn it back into bytes
(if at all!). Treat locations as &lt;code&gt;tzdb&lt;/code&gt; entries, and qualify datetime
objects into their absolute timezone (&lt;code&gt;EST&lt;/code&gt;, &lt;code&gt;EDT&lt;/code&gt;, &lt;code&gt;PST&lt;/code&gt;, &lt;code&gt;PDT&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;It's not until you want to show the datetime to the user again should you
consider how to re-encode your datetime to bytes. You should think about
what flavor of bytes, what encoding -- what timezone -- should I be
encoding into?&lt;/p&gt;
&lt;h2&gt;TEST&lt;/h2&gt;
&lt;p&gt;Just like Unicode, testing that your code works with datetimes is important.
Every time I think about how to go about doing this, I think about that
one time that &lt;a href="http://mjg59.dreamwidth.org/"&gt;mjg59&lt;/a&gt; couldn't book a flight
starting Tuesday from AKL, landing in HNL on Monday night, because
United couldn't book the last leg to SFO. Do you ever assume dates only go
forward as time goes on? Remember timezones.&lt;/p&gt;
&lt;p&gt;Construct test data, make sure someone in New Zealand's
&lt;a href="https://en.wikipedia.org/wiki/UTC%2B13:45"&gt;+13:45&lt;/a&gt; can correctly talk with
their friends in
Baker Island's &lt;a href="https://en.wikipedia.org/wiki/UTC%E2%88%9212:00"&gt;-12:00&lt;/a&gt;,
and that the events sort right.&lt;/p&gt;
&lt;p&gt;Just because it's Noon on New Years Eve in England doesn't mean it's not
1 AM the next year in New Zealand. Places a few miles apart may go on Daylight
savings different days. Indian Standard Time is not even aligned on the hour
to GMT (&lt;code&gt;+05:30&lt;/code&gt;)!&lt;/p&gt;
&lt;p&gt;Test early, and test often. Memorize a few timezones, and challenge
your assumptions when writing code that has to do with time. Don't use
wall clocks to mean monotonic time. Remember there's a whole world out there,
and we only deal with part of it.&lt;/p&gt;
&lt;p&gt;It's also worth remembering, as &lt;a href="https://twitter.com/andrewindc"&gt;Andrew Pendleton&lt;/a&gt;
pointed out to me, that it's possible that a datetime isn't even &lt;em&gt;unique&lt;/em&gt; for a
place, since you can never know if &lt;code&gt;2016-11-06 01:00:00&lt;/code&gt; in &lt;code&gt;America/New_York&lt;/code&gt;
(in the &lt;code&gt;tzdb&lt;/code&gt;) is the first one, or second one. Storing &lt;code&gt;EST&lt;/code&gt; or &lt;code&gt;EDT&lt;/code&gt; along
with your datetime may help, though!&lt;/p&gt;
&lt;h2&gt;Pitfalls&lt;/h2&gt;
&lt;p&gt;Improper handling of timezones can lead to some interesting things, and failing
to be explicit (or at least, very rigid) in what you expect will lead to an
unholy class of bugs we've all come to hate. At best, you have confused
users doing math, at worst, someone misses a critical event, or our
security code fails.&lt;/p&gt;
&lt;p&gt;I recently found what I regard to be a pretty bad
&lt;a href="https://bugs.debian.org/819697"&gt;bug in apt&lt;/a&gt; (which David has prepared a
&lt;a href="https://anonscm.debian.org/cgit/apt/apt.git/diff/?id=9febc2b"&gt;fix&lt;/a&gt;
for and is pending upload, yay! Thank you!), which boiled down to documentation
and code expecting datetimes in a timezone, but &lt;em&gt;accepting any timezone&lt;/em&gt;, and
&lt;em&gt;silently&lt;/em&gt; treating it as &lt;code&gt;UTC&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The solution is to hard-fail, which is an interesting choice to me (as a vocal
fan of timezone aware code), but at the least it won't fail by
misunderstanding what the server is trying to communicate, and I do understand
and empathize with the situation the &lt;code&gt;apt&lt;/code&gt; maintainers are in.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Overall, my main point is although most modern developers know how to deal
with Unicode pain, I think there is a more general lesson to learn -- namely,
you should always know what data you have, and always remember what it is.
Understand assumptions as early as you can, and always store them with the data.&lt;/p&gt;</summary><category term="datetime"></category><category term="timezones"></category><category term="unicode"></category><category term="python"></category></entry><entry><title>Docker PostgreSQL Foreign Data Wrapper</title><link href="https://notes.pault.ag/dockerfdw" rel="alternate"></link><published>2014-09-18T21:49:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2014-09-18:dockerfdw</id><summary type="html">&lt;p&gt;For the tl;dr: &lt;a href="https://github.com/paultag/dockerfdw"&gt;Docker FDW&lt;/a&gt; is a thing.
Star it, hack it, try it out. File bugs, be happy. If you want to see what it's
like to read, there's some example SQL down below.&lt;/p&gt;
&lt;aside class="left"&gt;
    This post was edited on Sep 21st to add information about the
    &lt;code&gt;DELETE&lt;/code&gt; and &lt;code&gt;INSERT&lt;/code&gt; operators
&lt;/aside&gt;

&lt;p&gt;The question is first, what the heck is a PostgreSQL Foreign Data Wrapper?
PostgreSQL Foreign Data Wrappers are plugins that allow C libraries
to provide an adaptor for PostgreSQL to talk to an external database.&lt;/p&gt;
&lt;p&gt;Some folks have used this to wrap stuff like
&lt;a href="https://github.com/citusdata/mongo_fdw"&gt;MongoDB&lt;/a&gt;, which I always found
to be hilarous (and an epic hack).&lt;/p&gt;
&lt;h1&gt;Enter Multicorn&lt;/h1&gt;
&lt;p&gt;During my time at &lt;a href="http://pygotham.org/"&gt;PyGotham&lt;/a&gt;, I saw a talk from 
&lt;a href="https://twitter.com/weschow"&gt;Wes Chow&lt;/a&gt; about something called
&lt;a href="http://multicorn.org/"&gt;Multicorn&lt;/a&gt;. He was showing off some really neat
plugins, such as the git revision history of CPython, and parsed logfiles
from some stuff over at Chartbeat. This basically blew my mind.&lt;/p&gt;
&lt;aside class="right"&gt;
    If you're interested in some of these, there are a bunch in the
    Multicorn VCS repo, such as the
    &lt;a href="https://github.com/Kozea/Multicorn/blob/master/python/multicorn/gitfdw.py"&gt;gitfdw&lt;/a&gt;
    example.
&lt;/aside&gt;

&lt;p&gt;All throughout the talk I was coming up with all sorts of things that I wanted
to do -- this whole library is basically exactly what I've been dreaming
about for years. I've always wanted to provide a SQL-like interface
into querying API data, joining data cross-API using common crosswalks,
such as using &lt;a href="http://capitolwords.org/"&gt;Capitol Words&lt;/a&gt; to query for
Legislators, and use the
&lt;a href="http://bioguide.congress.gov/biosearch/biosearch.asp"&gt;bioguide ids&lt;/a&gt;
to &lt;code&gt;JOIN&lt;/code&gt; against the &lt;a href="https://sunlightlabs.github.io/congress/"&gt;congress api&lt;/a&gt;
to get their Twitter account names.&lt;/p&gt;
&lt;p&gt;My first shot was to Multicorn the new
&lt;a href="http://opencivicdata.org/"&gt;Open Civic Data&lt;/a&gt; API I was working on, chuckled
and put it aside as a really awesome hack.&lt;/p&gt;
&lt;h1&gt;Enter Docker&lt;/h1&gt;
&lt;p&gt;It wasn't until &lt;a href="https://github.com/tianon"&gt;tianon&lt;/a&gt; connected the dots for me
and suggested a &lt;a href="http://docker.io/"&gt;Docker&lt;/a&gt; FDW did I get really excited.
Cue a few hours of hacking, and I'm proud to say -- here's
&lt;a href="https://github.com/paultag/dockerfdw"&gt;Docker FDW&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This lets us ask all sorts of really interesting questions out of the API,
and might even help folks writing webapps avoid adding too much Docker-aware
logic. Abstractions can be fun!&lt;/p&gt;
&lt;h1&gt;Setting it up&lt;/h1&gt;
&lt;aside class="left"&gt;
    The only stumbling block you might find (at least on Debian and Ubuntu) is
    that you'll need a Multicorn `.deb`. It's currently undergoing an
    official Debianization from the Postgres team, but in the meantime I put
    the source and binary up on my
    &lt;a href="https://people.debian.org/~paultag/tmp/"&gt;people.debian.org&lt;/a&gt;.
    Feel free to use that while the Debian PostgreSQL team prepares the upload
    to unstable.
&lt;/aside&gt;

&lt;p&gt;I'm going to assume you have a working Multicorn, PostgreSQL and Docker setup
(including adding the &lt;code&gt;postgres&lt;/code&gt; user to the &lt;code&gt;docker&lt;/code&gt; group)&lt;/p&gt;
&lt;p&gt;So, now let's pop open a &lt;code&gt;psql&lt;/code&gt; session. Create a database (I called mine
&lt;code&gt;dockerfdw&lt;/code&gt;, but it can be anything), and let's create some tables.&lt;/p&gt;
&lt;p&gt;Before we create the tables, we need to let PostgreSQL know where our
objects are. This takes a name for the &lt;code&gt;server&lt;/code&gt;, and the &lt;code&gt;Python&lt;/code&gt; importable
path to our FDW.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;SERVER&lt;/span&gt; &lt;span class="n"&gt;docker_containers&lt;/span&gt; &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;WRAPPER&lt;/span&gt; &lt;span class="n"&gt;multicorn&lt;/span&gt; &lt;span class="k"&gt;options&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;wrapper&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;dockerfdw.wrappers.containers.ContainerFdw&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;SERVER&lt;/span&gt; &lt;span class="n"&gt;docker_image&lt;/span&gt; &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;WRAPPER&lt;/span&gt; &lt;span class="n"&gt;multicorn&lt;/span&gt; &lt;span class="k"&gt;options&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;wrapper&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;dockerfdw.wrappers.images.ImageFdw&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now that we have the server in place, we can tell PostgreSQL to create a table
backed by the FDW by creating a foreign table. I won't go too much into the
syntax here, but you might also note that we pass in some options - these are
passed to the constructor of the FDW, letting us set stuff like the Docker
host.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;foreign&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;docker_containers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;          &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;image&amp;quot;&lt;/span&gt;       &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;        &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;names&amp;quot;&lt;/span&gt;       &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;privileged&amp;quot;&lt;/span&gt;  &lt;span class="nb"&gt;BOOLEAN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;ip&amp;quot;&lt;/span&gt;          &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;bridge&amp;quot;&lt;/span&gt;      &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;running&amp;quot;&lt;/span&gt;     &lt;span class="nb"&gt;BOOLEAN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;pid&amp;quot;&lt;/span&gt;         &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;exit_code&amp;quot;&lt;/span&gt;   &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;command&amp;quot;&lt;/span&gt;     &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;docker_containers&lt;/span&gt; &lt;span class="k"&gt;options&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;host&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unix:///run/docker.sock&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;foreign&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;docker_images&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;              &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;architecture&amp;quot;&lt;/span&gt;    &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;author&amp;quot;&lt;/span&gt;          &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;comment&amp;quot;&lt;/span&gt;         &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;parent&amp;quot;&lt;/span&gt;          &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;&amp;quot;tags&amp;quot;&lt;/span&gt;            &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;docker_image&lt;/span&gt; &lt;span class="k"&gt;options&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;host&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unix:///run/docker.sock&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And, now that we have tables in place, we can try to learn something about the
Docker containers. Let's start with something fun - a join from containers
to images, showing all image tag names, the container names and the ip of the
container (if it has one!).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;docker_containers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;docker_containers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;docker_images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;docker_containers&lt;/span&gt;
  &lt;span class="k"&gt;RIGHT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;docker_images&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;docker_containers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;docker_images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;     ip      |            names            |                  tags                   
-------------+-----------------------------+-----------------------------------------
             |                             | {ruby:latest}
             &lt;span class="o"&gt;|&lt;/span&gt;                             | {paultag/vcs-mirror:latest}
             &lt;span class="o"&gt;|&lt;/span&gt; {/de-openstates-to-ocd}     | {sunlightlabs/scrapers-us-state:latest}
             &lt;span class="o"&gt;|&lt;/span&gt; {/ny-openstates-to-ocd}     | {sunlightlabs/scrapers-us-state:latest}
             &lt;span class="o"&gt;|&lt;/span&gt; {/ar-openstates-to-ocd}     | {sunlightlabs/scrapers-us-state:latest}
 172.17.0.47 | {/ms-openstates-to-ocd}     | {sunlightlabs/scrapers-us-state:latest}
 172.17.0.46 | {/nc-openstates-to-ocd}     | {sunlightlabs/scrapers-us-state:latest}
             &lt;span class="o"&gt;|&lt;/span&gt; {/ia-openstates-to-ocd}     | {sunlightlabs/scrapers-us-state:latest}
             &lt;span class="o"&gt;|&lt;/span&gt; {/az-openstates-to-ocd}     | {sunlightlabs/scrapers-us-state:latest}
             &lt;span class="o"&gt;|&lt;/span&gt; {/oh-openstates-to-ocd}     | {sunlightlabs/scrapers-us-state:latest}
             &lt;span class="o"&gt;|&lt;/span&gt; {/va-openstates-to-ocd}     | {sunlightlabs/scrapers-us-state:latest}
 172.17.0.41 | {/wa-openstates-to-ocd}     | {sunlightlabs/scrapers-us-state:latest}
             &lt;span class="o"&gt;|&lt;/span&gt; {/jovial_poincare}          | {&amp;lt;none&amp;gt;:&lt;span class="nt"&gt;&amp;lt;none&amp;gt;&lt;/span&gt;}
             &lt;span class="o"&gt;|&lt;/span&gt; {/jolly_goldstine}          | {&amp;lt;none&amp;gt;:&lt;span class="nt"&gt;&amp;lt;none&amp;gt;&lt;/span&gt;}
             &lt;span class="o"&gt;|&lt;/span&gt; {/cranky_torvalds}          | {&amp;lt;none&amp;gt;:&lt;span class="nt"&gt;&amp;lt;none&amp;gt;&lt;/span&gt;}
             &lt;span class="o"&gt;|&lt;/span&gt; {/backstabbing_wilson}      | {&amp;lt;none&amp;gt;:&lt;span class="nt"&gt;&amp;lt;none&amp;gt;&lt;/span&gt;}
             &lt;span class="o"&gt;|&lt;/span&gt; {/desperate_hoover}         | {&amp;lt;none&amp;gt;:&lt;span class="nt"&gt;&amp;lt;none&amp;gt;&lt;/span&gt;}
             &lt;span class="o"&gt;|&lt;/span&gt; {/backstabbing_ardinghelli} | {&amp;lt;none&amp;gt;:&lt;span class="nt"&gt;&amp;lt;none&amp;gt;&lt;/span&gt;}
             &lt;span class="o"&gt;|&lt;/span&gt; {/cocky_feynman}            | {&amp;lt;none&amp;gt;:&lt;span class="nt"&gt;&amp;lt;none&amp;gt;&lt;/span&gt;}
             &lt;span class="o"&gt;|&lt;/span&gt;                             | {paultag/postgres:latest}
             &lt;span class="o"&gt;|&lt;/span&gt;                             | {debian:testing}
             &lt;span class="o"&gt;|&lt;/span&gt;                             | {paultag/crank:latest}
             &lt;span class="o"&gt;|&lt;/span&gt;                             | {&amp;lt;none&amp;gt;:&lt;span class="nt"&gt;&amp;lt;none&amp;gt;&lt;/span&gt;}
             &lt;span class="o"&gt;|&lt;/span&gt;                             | {&amp;lt;none&amp;gt;:&lt;span class="nt"&gt;&amp;lt;none&amp;gt;&lt;/span&gt;}
             &lt;span class="o"&gt;|&lt;/span&gt; {/stupefied_fermat}         | {hackerschool/doorbot:latest}
             &lt;span class="o"&gt;|&lt;/span&gt; {/focused_euclid}           | {debian:unstable}
             &lt;span class="o"&gt;|&lt;/span&gt; {/focused_babbage}          | {debian:unstable}
             &lt;span class="o"&gt;|&lt;/span&gt; {/clever_torvalds}          | {debian:unstable}
             &lt;span class="o"&gt;|&lt;/span&gt; {/stoic_tesla}              | {debian:unstable}
             &lt;span class="o"&gt;|&lt;/span&gt; {/evil_torvalds}            | {debian:unstable}
             &lt;span class="o"&gt;|&lt;/span&gt; {/foo}                      | {debian:unstable}
(31 rows)
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;OK, let's see if we can bring this to the next level now. I finally got around
to implementing &lt;code&gt;INSERT&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt; operations, which turned out to be
pretty simple to do. Check this out:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;docker_containers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;DELETE 1
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This will do a &lt;code&gt;stop&lt;/code&gt; + &lt;code&gt;kill&lt;/code&gt; after a 10 second hang behind the scenes. It's
actually a lot of fun to spawn up a container and terminate it from
&lt;code&gt;PostgreSQL&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;docker_containers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;debian:unstable&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;RETURNING&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;                                id                                
------------------------------------------------------------------
 0a903dcf5ae10ee1923064e25ab0f46e0debd513f54860beb44b2a187643ff05

INSERT 0 1
(1 row)
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Spawning containers works too - this is still very immature and not super
practical, but I figure while I'm showing off, I might as well go all the way.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;docker_containers&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;0a903dcf5ae10ee1923064e25ab0f46e0debd513f54860beb44b2a187643ff05&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;     ip      
-------------
 172.17.0.12
(1 row)
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Success! This is just a taste of what's to come, so please feel free to hack on
&lt;a href="https://github.com/paultag/dockerfdw"&gt;Docker FDW&lt;/a&gt;,
tweet me &lt;a href="http://twitter.com/paultag"&gt;@paultag&lt;/a&gt;, file bugs / feature requests.
It's currently a bit of a hack, and it's something that I think has
long-term potential after some work goes into making sure that this is a rock
solid interface to the Docker API.&lt;/p&gt;</summary><category term="docker"></category><category term="postgresql"></category><category term="fdw"></category><category term="python"></category></entry><entry><title>Linode pv-grub chaining</title><link href="https://notes.pault.ag/linode-pv-grub-chainning" rel="alternate"></link><published>2014-06-14T21:40:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2014-06-14:linode-pv-grub-chainning</id><summary type="html">&lt;p&gt;I've been using &lt;a href="https://linode.com"&gt;Linode&lt;/a&gt; since 2010, and many of
my friends have heard me talk about how big a fan I am of linode. I've
used Debian unstable on all my Linodes, since I often use them as a remote
shell for general purpose Debian development. I've found my linodes to be
indispensable, and I really love Linode.&lt;/p&gt;
&lt;h1&gt;The Problem&lt;/h1&gt;
&lt;p&gt;Recently, because of my work on &lt;a href="http://docker.io/"&gt;Docker&lt;/a&gt;, I was forced
to stop using the Linode kernel in favor of the stock Debian kernel, since
the stock Linode kernel has no aufs support, and the default LVM-based
devicemapper backend can be quite a pain.&lt;/p&gt;
&lt;aside class="left"&gt;
    The btrfs errors are ones I fully expect to be gone soon, I can't wait
    to switch back to using it.
&lt;/aside&gt;

&lt;p&gt;I tried loading in &lt;a href="http://en.wikipedia.org/wiki/Btrfs"&gt;btrfs&lt;/a&gt; support, and
using that to host the Docker instance backed with btrfs, but it was throwing
errors as well. Stuck with unstable backends, I wanted to use the
&lt;a href="http://en.wikipedia.org/wiki/Aufs"&gt;aufs&lt;/a&gt; backend, which, despite problems in
aufs internally, is quite stable with Docker (and in general).&lt;/p&gt;
&lt;p&gt;I started to run through the &lt;a href="https://library.linode.com/custom-instances/pv-grub-howto"&gt;Linode Library's guide on PV-Grub&lt;/a&gt;,
but that resulted in a cryptic error with xen not understanding the compression
of the kernel. I checked for recent changes to the compression, and lo, the
Debian kernel has been switched to use xz compression in sid. Awesome news,
really. XZ compression is awesome, and I've been super impressed with how
universally we've adopted it in Debian. Keep it up!  However, it appears only
a newer pv-grub than the Linode hosts have installed will fix this.&lt;/p&gt;
&lt;p&gt;After contacting the (ever friendly) Linode support, they were unable to give
me a timeline on adding xz support, which would entail upgrading pv-grub. It
was quite disappointing news, to be honest. Workarounds were suggested,
but I'm not quite happy with them as proper solutions.&lt;/p&gt;
&lt;p&gt;After asking in &lt;code&gt;#debian-kernel&lt;/code&gt;, &lt;a href="http://bblank.thinkmo.de/blog"&gt;waldi&lt;/a&gt; was
able to give me a few pointers, and the following is very inspired by him,
the only thing that changed much was config tweaking, which was easy enough.
Thanks, Bastian!&lt;/p&gt;
&lt;h1&gt;The Constraints&lt;/h1&gt;
&lt;p&gt;I wanted to maintain a 100% stock configuration from the kernel up.
When I upgraded my kernel, I wanted to just work. I didn't want to
unpack and repack the kernel, and I didn't want to install software
outside main on my system. It had to be 100% Debian and unmodified.&lt;/p&gt;
&lt;h1&gt;The Solution&lt;/h1&gt;
&lt;aside class="right"&gt;
    It's pretty fun to attach to the lish console and watch bootup pass
    through GRUB 0.9, to GRUB 2.x to Linux. Free Software, Fuck Yeah.
&lt;/aside&gt;

&lt;p&gt;Left unable to run my own kernel directly in the Linode interface, the tact
here was to use Linode's old pv-grub to chain-load grub-xen, which loaded
a modern kernel. Turns out this works great.&lt;/p&gt;
&lt;p&gt;Let's start by creating a config for Linode's pv-grub to read
and use.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo mkdir -p /boot/grub/
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now, since pv-grub is legacy grub, we can write out the following
config to chain-load in &lt;code&gt;grub-xen&lt;/code&gt; (which is just Grub 2.0, as far as I can
tell) to &lt;code&gt;/boot/grub/menu.lst&lt;/code&gt;. And to think, I almost forgot all about
&lt;code&gt;menu.lst&lt;/code&gt;. Almost.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;default 1

timeout 3

title grub-xen shim
root (hd0)
kernel /boot/xen-shim
boot
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Just like riding a bike! Now, let's install and set up grub-xen to work for us.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo apt-get install grub-xen
sudo update-grub
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And, let's set the config for the GRUB image we'll create in the next step
in the &lt;code&gt;/boot/load.cf&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;configfile (xen/xvda)/boot/grub/grub.cfg
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now, lastly, let's generate the &lt;code&gt;/boot/xen-shim&lt;/code&gt; file that we need
to boot to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;grub-mkimage --prefix &lt;span class="s1"&gt;&amp;#39;(xen/xvda)/boot/grub&amp;#39;&lt;/span&gt; -c /boot/load.cf -O x86_64-xen /usr/lib/grub/x86_64-xen/*.mod &amp;gt; /boot/xen-shim
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Next, change your boot configuration to use &lt;code&gt;pv-grub&lt;/code&gt;, and give the machine
a kick. Should work great! If you run into issues, use the lish shell to
debug it, and let me know what else I should include in this post!&lt;/p&gt;
&lt;p&gt;Hack on!&lt;/p&gt;</summary><category term="linode"></category><category term="debian"></category></entry><entry><title>Hy at PyCon 2014</title><link href="https://notes.pault.ag/hy-pycon-2014" rel="alternate"></link><published>2014-04-18T20:13:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2014-04-18:hy-pycon-2014</id><summary type="html">&lt;p&gt;I gave a talk this year at &lt;a href="https://us.pycon.org/2014/"&gt;PyCon 2014&lt;/a&gt;, about one
of my favorite subjects: &lt;a href="http://hylang.org/"&gt;Hy&lt;/a&gt;. Many of my regular readers
will have no doubt explored Hy's thriving
&lt;a href="http://github.com/hylang"&gt;GitHub org&lt;/a&gt;, played with
&lt;a href="http://try-hy.appspot.com/"&gt;try-hy&lt;/a&gt;, or even installed it locally by
&lt;a href="https://pypi.python.org/pypi/hy"&gt;pip installing it&lt;/a&gt;. I was lucky enough to
be able to attend PyCon on behalf of &lt;a href="http://sunlightfoundation.com/"&gt;Sunlight&lt;/a&gt;,
with a solid contingent of my colleagues. We put together a writeup on the
&lt;a href="http://sunlightfoundation.com/blog/2014/04/18/sunlight-at-pycon-2014/"&gt;Sunlight blog&lt;/a&gt;
if anyone was interested in our favorite talks.&lt;/p&gt;
&lt;div class="video" style="width: 500px; height: 340px; margin: 0 auto;"&gt;
    &lt;iframe width="500" height="315" src="//www.youtube.com/embed/AmMaN1AokTI" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;Tons of really amazing questions, and such an amazingly warm reception from
so many of my peers throughout this year's PyCon. Thank you so much to
everyone that attended the talk. As always, you should
&lt;a href="https://github.com/hylang/hy"&gt;Fork Hy on GitHub&lt;/a&gt;,
follow &lt;a href="https://twitter.com/hylang"&gt;@hylang&lt;/a&gt; on the twitters, and
send in any bugs you find!&lt;/p&gt;
&lt;p&gt;Hopefully I'll be able to put my talk up in blog-post form soon, but until then
feel free to look over the &lt;a href="http://slides.pault.ag/hy.html"&gt;slides&lt;/a&gt; or just
&lt;a href="https://www.youtube.com/watch?v=AmMaN1AokTI"&gt;watch the talk&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;An extra shout-out to &lt;a href="https://twitter.com/akaptur"&gt;@akaptur&lt;/a&gt; for hacking on
Hy during the sprints, and giving the exception system
&lt;a href="https://github.com/hylang/hy/pull/556"&gt;quite the workthrough&lt;/a&gt;.
Thanks, Allison!&lt;/p&gt;</summary><category term="hy"></category><category term="lisp"></category><category term="pycon"></category></entry><entry><title>Musings about Debian and Python</title><link href="https://notes.pault.ag/debian-python" rel="alternate"></link><published>2013-09-21T22:49:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2013-09-21:debian-python</id><summary type="html">&lt;p&gt;On a regular basis, I find myself the odd-man-out when it comes to talking
about how to work with Python on Debian systems. I'm going to write this and
post it so that I might be able to point people at my thoughts without having
to write the same email in response to each thread that pops up.&lt;/p&gt;
&lt;p&gt;Turns out I don't fit in with the Debian hardliners (which is to say, the
mindset that &lt;code&gt;pip&lt;/code&gt; sucks and shouldn't exist), nor do I fit in with the Python
hardliners (which is to say &lt;code&gt;apt&lt;/code&gt; and &lt;code&gt;dpkg&lt;/code&gt; are out of date, and neither have
a place on a Development machine).&lt;/p&gt;
&lt;p&gt;I think our discourse on this topic has become &lt;em&gt;petty&lt;/em&gt; and &lt;em&gt;stupid&lt;/em&gt; in general.
Let's all try to step back and drop a bit of the attitude.&lt;/p&gt;
&lt;h1&gt;&lt;code&gt;pip&lt;/code&gt; doesn't suck, and neither does &lt;code&gt;apt&lt;/code&gt;.&lt;/h1&gt;
&lt;p&gt;The truth is, both sides are wrong. As with any subject, the real
answer here is much more nuanced than either side presents it. I'm going to
try and present my opinion on this, in the way that both my Pythonista self
and my Debianite self see the issue. Hopefully I can keep this short, to
the point, and caked with logic.&lt;/p&gt;
&lt;h2&gt;The case for &lt;code&gt;dpkg&lt;/code&gt; (the Debianite in me)&lt;/h2&gt;
&lt;p&gt;In defense of &lt;code&gt;dpkg&lt;/code&gt; and &lt;code&gt;apt&lt;/code&gt;, imagine having to install &lt;code&gt;python-gnome2&lt;/code&gt;
on all your systems when you install.  It'd be hell on earth.
Imagine having a &lt;strong&gt;user&lt;/strong&gt; try to do this. It's insane to assume that
end-users will be using &lt;code&gt;pip&lt;/code&gt; for this purpose.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pip&lt;/code&gt; is fun and all, but it's also installing 100% untrusted code to your
system (perhaps as root, if you're using &lt;code&gt;pip&lt;/code&gt; with &lt;code&gt;sudo&lt;/code&gt; for some reason),
and hasn't been reviewed for software freeness, which is something Debian
(and Debian users) take seriously. This isn't even to mention the hell that
&lt;code&gt;pip&lt;/code&gt; wreaks on &lt;code&gt;dpkg&lt;/code&gt; controlled files / packages.&lt;/p&gt;
&lt;aside class="left"&gt;
    Remember, Debian spends a lot of time and effort into ensuring software
    is &lt;a href="http://www.debian.org/social_contract#guidelines" &gt;DFSG&lt;/a&gt;
    free, and safe.
&lt;/aside&gt;

&lt;p&gt;Try to remember how much of your system running (yes, right now) is running
because of Python or Python modules. Try to imagine how much of a pain in
the ass it'd be if you couldn't boot into &lt;code&gt;GNOME&lt;/code&gt; to use &lt;code&gt;nm-applet&lt;/code&gt; to connect
to wifi to &lt;code&gt;pip&lt;/code&gt; install something. I'm sure even the most extreme pip'er
understands the need for Operating System level package management.&lt;/p&gt;
&lt;p&gt;Debian also has a bigger problem scope - we're not maintaining a library
in Debian for kicks, we're maintaining it so that &lt;em&gt;end user applications&lt;/em&gt; may
use the library. When we update something like &lt;code&gt;Django&lt;/code&gt;, we have to make sure
that we don't break anything using it (although, to be honest, the fact that we
package webapps is an entire rant for later) before we get to update it to the
newest release.&lt;/p&gt;
&lt;p&gt;Hell, with a few coffees, I could automate the process of releasing a &lt;code&gt;.deb&lt;/code&gt;
with a new upstream release, 100% unattended. I won't, however, since this is
an insane idea. Let's go over a brief list of things I do before uploading a
new package:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Review the &lt;em&gt;entire&lt;/em&gt; codebase for simple mistakes.&lt;/li&gt;
&lt;li&gt;Review the &lt;em&gt;entire&lt;/em&gt; codebase for license issues.&lt;/li&gt;
&lt;li&gt;Review the &lt;em&gt;entire&lt;/em&gt; codebase for files without source, and track down
    (and include source for) any sourceless files (such as &lt;code&gt;pickle&lt;/code&gt;
    files, etc).&lt;/li&gt;
&lt;li&gt;Get to know the upstream, get to know open bugs, write something using
    the lib, in case I need to debug later.&lt;/li&gt;
&lt;li&gt;Install the package.&lt;/li&gt;
&lt;li&gt;Test the package.&lt;/li&gt;
&lt;li&gt;Work out any Debian package issues (this is easy).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, a brief list of things I do before I update a package:&lt;/p&gt;
&lt;aside class="right"&gt;
    Some non-Debian people may call this anal. I disagree, since this is
    important to ensure we have &lt;i&gt;source&lt;/i&gt; for all files. In addition,
    it's trivial to take the next step and ensure things are &lt;i&gt;roughly&lt;/i&gt;
    safe.
&lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;Review the changes between the last uploaded version (in diff format, if
    it's sane, otherwise get the VCS and review), ensure all the above are still
    OK.&lt;/li&gt;
&lt;li&gt;Review for Debian-local issues (such as how it will upgrade, using
    &lt;code&gt;piuparts&lt;/code&gt;, and &lt;code&gt;adequate&lt;/code&gt;, etc).&lt;/li&gt;
&lt;li&gt;Check to make sure it won't break any reverse dependencies.&lt;/li&gt;
&lt;li&gt;Review for bugfixes that I might need to bring back to the &lt;code&gt;stable&lt;/code&gt; release.&lt;/li&gt;
&lt;li&gt;Figure out if I should (or even can) backport the package, if API is
    stable.&lt;/li&gt;
&lt;li&gt;Review for bugs (upstream or in Debian) that I need to mention in the
    debian/changelog.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Clearly, this isn't a quick-and-dirty task. It's not a matter of getting a
package updated (technically), it's a much more detailed process than that.
This is also why Debian is so highly regarded for its technical virtuosity,
and why the
&lt;a href="http://training.linuxfoundation.org/why-our-linux-training/training-reviews/linux-foundation-training-prepares-the-international-space-station-for-linux-migration"&gt;ISS decided to deploy Debian in space&lt;/a&gt;,
despite other commercial distros such as &lt;code&gt;Red Hat&lt;/code&gt;, or &lt;code&gt;Ubuntu&lt;/code&gt;, and
community distros, such as &lt;code&gt;Fedora&lt;/code&gt; or &lt;code&gt;Arch&lt;/code&gt;.&lt;/p&gt;
&lt;aside class="left"&gt;
    Cheap shot, I know.
&lt;/aside&gt;

&lt;p&gt;It's also not Debian's job to package the world in the archive. This is an
insane task, and it's not Debian's place to do it. We introduce libraries
as things need them, not because we wrote some new library that someone
may find slightly useful at some point in the future. maybe.&lt;/p&gt;
&lt;p&gt;Upstream developers and language communities (not only Python here) tend to
lose sight of why we're doing this in the first place, which
is our users. This isn't some sort of technical pissing contest to see who can
distribute the software in the best way. Debian-folk always keep end users
as our highest priority.&lt;/p&gt;
&lt;aside class="right"&gt;
    I'm sorry to any
    &lt;a href="http://lists.debian.org/20100106100055.GV3438@radis.liafa.jussieu.fr" &gt;kittens that may have been harmed by this statement&lt;/a&gt;.
&lt;/aside&gt;

&lt;p&gt;I quote the
&lt;a href="http://www.debian.org/social_contract"&gt;Debian Social Contract&lt;/a&gt;, when I say
that &lt;em&gt;Our priorities are our users and free software&lt;/em&gt;. No one's trying to
get &lt;em&gt;developers&lt;/em&gt; to use &lt;code&gt;dpkg&lt;/code&gt; to create software. In fact, as you'll see
below, I actively &lt;em&gt;discourage&lt;/em&gt; using system modules for development.&lt;/p&gt;
&lt;h2&gt;The case for &lt;code&gt;pip&lt;/code&gt; (the Pythonista in me)&lt;/h2&gt;
&lt;p&gt;In defense of &lt;code&gt;pip&lt;/code&gt;, the idea that Debian will keep the latest versions of
packages is insane. The idea that we can keep pace with upstream releases is
nuts, and the idea that every upstream release on &lt;code&gt;pypi&lt;/code&gt; is ready to ship is
bananas. &lt;a href="http://youtu.be/gZHjRQjbHrE?t=2m30s"&gt;b-a-n-a-n-a-n-a-s&lt;/a&gt;.
As a developer, I don't want to support every release, and I surely don't want
other people depending on some random snapshot.&lt;/p&gt;
&lt;aside class="right"&gt;
    In fact, I have a very hard time saying anything but &lt;i&gt;"try upgrading
    first"&lt;/i&gt; when I get a bug report on a side-project.
    It's tough to remember some edge-case from 2 years ago if this code is
    tightly coupled with another codebase.
&lt;/aside&gt;

&lt;p&gt;Often times, I'll put stuff up on &lt;code&gt;pypi&lt;/code&gt; as a preview, or to release often, and
solicit feedback without having to give out instructions on using a &lt;code&gt;git&lt;/code&gt;
checkout (it's also easier to have them try a version from &lt;code&gt;pypi&lt;/code&gt; so I can
cross-ref the git tag to reproduce issues when they file them)&lt;/p&gt;
&lt;aside class="left"&gt;
    Even Debian tools I write, like
    &lt;a href="https://pypi.python.org/pypi/schroot"&gt;python-schroot&lt;/a&gt;
    are released to &lt;code&gt;pypi&lt;/code&gt; first, and I treat that as the
    upstream location when packaging it in Debian.
&lt;/aside&gt;

&lt;p&gt;&lt;code&gt;pypi&lt;/code&gt; is easy, ubiquitous and works regardless of the platform, which means
less of my development time is spent packaging stuff up for platforms I don't
really care about (&lt;code&gt;Arch&lt;/code&gt;, &lt;code&gt;Fedora&lt;/code&gt;, &lt;code&gt;OSX&lt;/code&gt;, &lt;code&gt;Windows&lt;/code&gt;), even though I value
feedback from users on those systems. The effort it takes to release something
is limited to &lt;code&gt;python setup.py sdist upload&lt;/code&gt;, and it's in a place (and in a
shape) that anyone can use it without having 10 sets of platform-local
instructions.&lt;/p&gt;
&lt;p&gt;Even ignoring all the above, when &lt;em&gt;I'm&lt;/em&gt; writing a new app or bit of code,
I want to be sure I'm targeting the latest version of the code I depend on,
so that future changes to API won't hit me as hard. By following
along with my dependencies' development, I can be sure that my code breaks
early, and breaks in development, not production. Upstreams also tend to not
like bug reports against old branches, so ensuring I have the latest code from
&lt;code&gt;pypi&lt;/code&gt; means I can properly file bugs.&lt;/p&gt;
&lt;p&gt;Lastly, I prefer &lt;code&gt;virtualenv&lt;/code&gt; based setups for development, since I'm usually
working on many things at once. This often means version mismatches in
libraries, which brings in API changes (another whole rant here as well).
I &lt;em&gt;don't&lt;/em&gt; want to keep installing and uninstalling packages to switch between
the two projects, and using a &lt;code&gt;chroot(8)&lt;/code&gt; means a lot of overhead and that it's
disconnected from my development environment / filesystem, so I resort to
&lt;code&gt;virtualenv&lt;/code&gt; to isolate my Development environment.&lt;/p&gt;
&lt;h1&gt;Final notes&lt;/h1&gt;
&lt;aside class="right"&gt;
    I love apt, I love pip, why can't you?
&lt;/aside&gt;

&lt;p&gt;I don't want to keep arguing about this. Just accept that the world's a big
place and that there exist use-cases that both &lt;code&gt;apt&lt;/code&gt; and &lt;code&gt;pip&lt;/code&gt; need to exist
and work in the way they're working now. At the very least, try and understand
there exist smart people on both sides, and no one is trying to screw anyone
over or keep their own little private club to themselves. Hopefully, going
forward, we can make sure that the integration between these two tools gets
&lt;em&gt;better&lt;/em&gt;, not worse.&lt;/p&gt;
&lt;p&gt;Help make this dream a reality. Contribute to a productive tone, not a
destructive one. In short:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;pip&lt;/code&gt; without &lt;code&gt;sudo&lt;/code&gt; always. Don't tell people to use &lt;code&gt;sudo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;apt&lt;/code&gt; or &lt;code&gt;dpkg&lt;/code&gt; when deploying system-wide.&lt;/li&gt;
&lt;li&gt;Understand people are going to package, and they will be more concerned
    about software using your library then keeping your library up to date.&lt;/li&gt;
&lt;li&gt;Understand Debian Developers and package maintainers have to do a lot of
    work when updating or sponsoring an upload.&lt;/li&gt;
&lt;li&gt;Understand upstream developers can't be bothered to fix every issue
    with every release (release early, release often) with some snapshot
    you introduced into unstable.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;pip&lt;/code&gt; and &lt;code&gt;virtualenv&lt;/code&gt; in development setups, so we can upgrade your
    app when we upgrade the lib.&lt;/li&gt;
&lt;/ul&gt;</summary><category term="debian"></category><category term="python"></category></entry><entry><title>Hy: The survival guide</title><link href="https://notes.pault.ag/hy-survival-guide" rel="alternate"></link><published>2013-08-02T23:19:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2013-08-02:hy-survival-guide</id><summary type="html">&lt;p&gt;One of my new favorite languages is a peppy little
&lt;a href="http://en.wikipedia.org/wiki/Lisp"&gt;lisp&lt;/a&gt; called
&lt;a href="http://hylang.org"&gt;hy&lt;/a&gt;. I like it a lot since it's a result of a hilarious
idea I had while talking with some coworkers over Mexican food. Since I'm
the most experienced &lt;a href="https://github.com/hylang?tab=members"&gt;Hypster&lt;/a&gt; on the
planet, I figured I should write a survival guide. This will go a lot easier
if you already know Lisp, but you can get away with quite a bit of Python.&lt;/p&gt;
&lt;h1&gt;The Tao of Hy&lt;/h1&gt;
&lt;p&gt;We don't have many rules (yet), but we do have quite a bit of philosophy.
The collective Hyve Mind has spent quite a bit of time working out Hy's
internals, and we do spend a bit of time looking at how the language “feels”.
The following is a brief list of some of the design decisions we've
picked out.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Look like a lisp, &lt;code&gt;DTRT&lt;/code&gt; with it (e.g. dashes turn to underscores,
     earmuffs turn to all-caps.)&lt;/li&gt;
&lt;li&gt;We're still Python. Most of the internals translate 1:1 to Python
     internals.&lt;/li&gt;
&lt;li&gt;Use unicode &lt;em&gt;everywhere&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Tests or it doesn't exist.&lt;/li&gt;
&lt;li&gt;Fix the bad decisions in Python 2 when we can (see &lt;code&gt;true_division&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;When in doubt, defer to Python.&lt;/li&gt;
&lt;li&gt;If you're still unsure, defer to Clojure&lt;/li&gt;
&lt;li&gt;If you're even more unsure, defer to Common Lisp&lt;/li&gt;
&lt;li&gt;Keep in mind we're &lt;em&gt;not&lt;/em&gt; Clojure. We're &lt;em&gt;not&lt;/em&gt; Common Lisp. We're Homoiconic
     Python, with extra bits that make sense.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Naturally, this doesn't cover everything, but if you can drop into that mindset,
things start to make quite a bit of sense.&lt;/p&gt;
&lt;h1&gt;The Style of Hy&lt;/h1&gt;
&lt;p&gt;Although I am perhaps the least qualified person to do so (I still don't write
idiomatic Lisp all the time), I'm going to set up a few ground-rules when it
comes to idiomatic Hy code. We borrow quite a bit of syntax from Common Lisp
and Clojure, so again, feel free to defer to either if you're not working
on Hy internals. I prefer the
&lt;a href="https://github.com/bbatsov/clojure-style-guide"&gt;Clojure Style Guidelines&lt;/a&gt;
myself. As such, these are what we will defer to in the case that the Hy
style is undefined.&lt;/p&gt;
&lt;h2&gt;Clojure-isms&lt;/h2&gt;
&lt;p&gt;Hy has quite a few Clojure-isms that I rather prefer, such as the threading
macro, and dot-notation (for accessing methods on an Object), which I would
rather see used throughout the hylands.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;;; good:&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;fd&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/etc/passwd&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;print &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.readlines&lt;/span&gt; &lt;span class="nv"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="c1"&gt;;; bad:&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;fd&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/etc/passwd&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;print &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fd.readlines&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Some &lt;a href="http://dustycloud.org/"&gt;other hy devs&lt;/a&gt; very much disagree, and there's
nothing syntactically invalid about the latter, and it will continue to be
supported (in fact, it makes some things easier!), but it will not be
considered for Hy internal code.&lt;/p&gt;
&lt;p&gt;We also very much encourage use of the &lt;code&gt;threading macro&lt;/code&gt; throughout code
where it makes sense.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;;; good:&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;import &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;sh&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;cat&lt;/span&gt; &lt;span class="nv"&gt;grep&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cat&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/usr/share/dict/words&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;grep&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;-E&amp;quot;&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;tag$&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;;; bad:&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;import &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;sh&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;cat&lt;/span&gt; &lt;span class="nv"&gt;grep&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;grep&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cat&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/usr/share/dict/words&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;-E&amp;quot;&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;tag$&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;However, do use it when it helps aid in clarity, like all things, there are
cases where it makes a mess out of something that ought to not be futzed with.&lt;/p&gt;
&lt;h2&gt;Python-isms&lt;/h2&gt;
&lt;p&gt;In addition to stealing quite a bit of syntax from Clojure, I'm going to
take a few Python rules from PEP8 that apply to Hy as well. These are taken
because PEP8 is a really great set of rules, and Hy code ends up pretty,
well, Pythonic. The following are a collection of Pythonic rules that
explicitly apply to Hy code.&lt;/p&gt;
&lt;p&gt;Trailing spaces is a huge one. Never ever ever shall it be OK to have
trailing spaces on internal Hy code. For they suck.&lt;/p&gt;
&lt;p&gt;As with Python, you shall always double-space module-level definitions if
separated with a newline.&lt;/p&gt;
&lt;p&gt;All public functions must always contain docstrings.&lt;/p&gt;
&lt;p&gt;Inline comments shall be &lt;em&gt;two&lt;/em&gt; spaces from the end of the code, if they
are inline comments. They must always have a space between the comment
character and the start of the comment.&lt;/p&gt;
&lt;h2&gt;Hy-isms&lt;/h2&gt;
&lt;p&gt;Indentation shall be two spaces, except where matching the indentation
of the previous line.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;;; good (and preferred):&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;= &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nv"&gt;n&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+ &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;- &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;- &lt;/span&gt;&lt;span class="nv"&gt;n&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;;; still OK:&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;= &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+ &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;- &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;- &lt;/span&gt;&lt;span class="nv"&gt;n&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;;; still OK:&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;= &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;n&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+ &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;- &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;- &lt;/span&gt;&lt;span class="nv"&gt;n&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;;; Stupid as hell&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;= &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nv"&gt;n&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+ &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;- &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;- &lt;/span&gt;&lt;span class="nv"&gt;n&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;Parens must &lt;em&gt;never&lt;/em&gt; be alone, sad, all by their lonesome on their own line.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;;; good (and preferred):&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;= &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nv"&gt;n&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+ &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;- &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;- &lt;/span&gt;&lt;span class="nv"&gt;n&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;;; Stupid as hell&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;= &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nv"&gt;n&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+ &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;- &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;- &lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;; GAH, BURN IT WITH FIRE&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Don't use S-Expression syntax where vector syntax is really required. For
instance, the fact that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;;; bad (and evil)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;foo&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;print &lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;works is just because the compiler isn't overly strict. In reality, the
correct syntax in places such as this is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;;; good (and preferred):&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;foo&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;print &lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h1&gt;Notice&lt;/h1&gt;
&lt;p&gt;This guide is, above all, a &lt;em&gt;guide&lt;/em&gt;. This is also only truly binding
for working on Hy code internally. This post is also super subject to change
in the future, whenever I can be bothered to ensure that we have more of the
de facto rules written down.&lt;/p&gt;</summary><category term="hy"></category><category term="lisp"></category></entry><entry><title>Automatically lint your packages with debuild.me</title><link href="https://notes.pault.ag/debuild-me" rel="alternate"></link><published>2013-06-09T17:43:00-04:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2013-06-09:debuild-me</id><summary type="html">&lt;p&gt;Over my time working with Debian packages, I've always been concerned that
I have been missing catchable mistakes by not running all the static checking
tools I could run. As a result, I've been interested in writing some code that
automates this process, a place where I can push a package and come back a few
hours later to check on the results. This is great, since it provides a slightly
less scary interface to new packagers, and helps avoid thinking they've
just been “told off” by a Developer.&lt;/p&gt;
&lt;p&gt;I've spent the time to actually write this code, and I've called it
&lt;a href="http://debuild.me"&gt;debuild.me&lt;/a&gt;. The code itself is in its fourth
iteration, and is built up from a few core components. The client / server code
(&lt;a href="http://github.com/paultag/lucy"&gt;lucy&lt;/a&gt; and
&lt;a href="http://github.com/paultag/ethel"&gt;ethel&lt;/a&gt;) are quite interconnected, but
&lt;a href="https://github.com/fedora-static-analysis/firehose"&gt;firehose&lt;/a&gt; works great
on its own, and is a single, unified (and sane!) spec that is easy
to hack with (or even on!). Hopefully, this means that our wrappers will be
usable outside of debuild.me, which is a win for everyone.&lt;/p&gt;
&lt;h1&gt;Backend Design&lt;/h1&gt;
&lt;p&gt;The backend (&lt;a href="http://github.com/paultag/lucy"&gt;lucy&lt;/a&gt;) was the first part
I wanted to design. I made the decision (very early on) that everything was
going to be 100% Python 3.3+. This lets me use some of the (frankly, sweet)
tools in the stdlib. Since I've written this type of thing before
(I've tried to write this tool &lt;a href="https://github.com/paultag/monomoy-old"&gt;many&lt;/a&gt;,
&lt;a href="https://github.com/paultag/monomoy"&gt;many&lt;/a&gt;,
&lt;a href="https://github.com/paultag/chatham-old"&gt;many&lt;/a&gt;,
&lt;a href="https://github.com/paultag/chatham"&gt;many&lt;/a&gt; times before), so I had a rough
sense of how I wanted to design the backend. Past iterations had suffered from
an overly complex server half, so I decided to go ultra minimal with the
design of debuild.me.&lt;/p&gt;
&lt;aside class='left'&gt;
  You can find the code for the server (lucy) on
  &lt;a href="http://github.com/paultag/lucy"&gt;my GitHub&lt;/a&gt;
&lt;/aside&gt;

&lt;p&gt;The backend watches a directory (using a simple &lt;code&gt;inotify&lt;/code&gt; script) and processes
&lt;code&gt;.changes&lt;/code&gt; files as they come in. If the package is a source package, a set of
jobs are triggered (such as &lt;code&gt;lintian&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;desktop-file-validate&lt;/code&gt;),
as well as a different set for binary packages (such as &lt;code&gt;lintian&lt;/code&gt;, &lt;code&gt;piuparts&lt;/code&gt;
and &lt;code&gt;adequite&lt;/code&gt;).  Only people may upload source packages (without any debs) and
only builders can upload binary packages (without source).&lt;/p&gt;
&lt;p&gt;The client and server talk using
&lt;a href="http://docs.python.org/3/library/xmlrpc.server.html"&gt;XML-RPC&lt;/a&gt; with BASIC HTTP
auth. I'm going to (eventually) SSL secure the transport layer, but for now,
this will work as a proof of concept.&lt;/p&gt;
&lt;p&gt;Since I tend to like to keep my codebase simple and straightforward, I've used
&lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; as Lucy's DB. This lets me move between
documents in Mongo to Python objects without any trouble. In addition, I
evaluated some of the queue code out there (ZMQ, etc), and they all seemed
like overkill for my problem, and had a hard time keeping track of jobs that
(must never!) get lost. As a result, I wrote my own (very simple) job queue
in Mongo, which has no sense of scheduling (at all), but can do its job (and
do it well).&lt;/p&gt;
&lt;p&gt;Jobs describe what's to be built with a link to the &lt;code&gt;package&lt;/code&gt; document
that the job relates to, and its &lt;code&gt;arch&lt;/code&gt; and &lt;code&gt;suite&lt;/code&gt; (don't worry about the
rest just yet). Jobs get assigned via natural sort on its &lt;code&gt;UUID&lt;/code&gt; based &lt;code&gt;_id&lt;/code&gt;,
and assigned to the first builder that can process its &lt;code&gt;arch&lt;/code&gt; / &lt;code&gt;suite&lt;/code&gt;.
Source packages are considered &lt;code&gt;arch:all&lt;/code&gt; / &lt;code&gt;suite:unstable&lt;/code&gt; (so they always
get the most up-to-date linters on any arch that comes along).&lt;/p&gt;
&lt;p&gt;Lucy also allows for uploads to be given an &lt;code&gt;X-Lucy-Group&lt;/code&gt; tag to manage which
set of packages they're a part of. This comes in handy for doing partial
archive rebuilds, or eventually using it to manage what jobs should be run
on which uploads. This will allow me to run much more time-consuming tools
for packages I want to review versus rebuilding to ensure packages don't
FTBFS or aren't adequite.&lt;/p&gt;
&lt;h1&gt;Client Design&lt;/h1&gt;
&lt;p&gt;The buildd client (&lt;a href="http://github.com/paultag/ethel"&gt;ethel&lt;/a&gt;) talks with &lt;code&gt;lucy&lt;/code&gt;
via &lt;code&gt;XML-RPC&lt;/code&gt; to get assigned new jobs, release old jobs, close finished jobs,
and upload package report data. When the &lt;code&gt;etheld&lt;/code&gt; requests a new job, it also
passes along what &lt;code&gt;suites&lt;/code&gt; it knows of, which &lt;code&gt;arches&lt;/code&gt; it can build, as well
as what &lt;code&gt;types&lt;/code&gt; it can run (stuff like &lt;code&gt;lintian&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt; or &lt;code&gt;cppcheck&lt;/code&gt;.) Lucy
then assigns the builder to that job (so that we don't allocate the same job
twice), and what time it was assigned at.&lt;/p&gt;
&lt;aside class='right'&gt;
  You can find the code for the client (ethel) on
  &lt;a href="http://github.com/paultag/ethel"&gt;my GitHub&lt;/a&gt;
&lt;/aside&gt;

&lt;p&gt;Ethel then takes the result of the job (in the form of a &lt;code&gt;firehose.model&lt;/code&gt; tree)
and transmits it over the line back to the Lucy server as a &lt;code&gt;report&lt;/code&gt; (which also
contains information on if the build failed or not), at which point
lucy hands back a location (on the lucy host) that the daemon can write the log
to.&lt;/p&gt;
&lt;p&gt;If the job was a binary build, the &lt;code&gt;etheld&lt;/code&gt; process will &lt;code&gt;dput&lt;/code&gt; the package to
the server, with a special &lt;code&gt;X-Lucy-Job&lt;/code&gt; tag to signal which job that build
relates to, so that future lint runs can fetch the &lt;code&gt;deb&lt;/code&gt; files that the build
produced.&lt;/p&gt;
&lt;h1&gt;Tooling&lt;/h1&gt;
&lt;p&gt;Ethel runs a set of static checkers on the source code, which are basically
fancy wrappers around the tools we all know and love (like
&lt;a href="http://lintian.debian.org/"&gt;lintian&lt;/a&gt;,
&lt;a href="http://freedesktop.org/wiki/Software/desktop-file-utils/"&gt;desktop-file-validate&lt;/a&gt;,
or &lt;a href="http://piuparts.debian.org/"&gt;piuparts&lt;/a&gt;) which output Firehose in place of
home-grown stdout. This allows us to programmatically deal with the output
of these tools in a normal and consistent way.&lt;/p&gt;
&lt;aside class='left'&gt;
  You can read more about Firehose over in the Firehose
  &lt;a href="https://github.com/fedora-static-analysis/firehose/blob/master/README.rst"&gt;README.rst&lt;/a&gt;
&lt;/aside&gt;

&lt;p&gt;Some of the more complex runners are made of 3 parts - a &lt;code&gt;runner&lt;/code&gt;, &lt;code&gt;wrapper&lt;/code&gt;
and &lt;code&gt;command&lt;/code&gt;. The server invokes the &lt;code&gt;command&lt;/code&gt; routine, which invokes the
&lt;code&gt;runner&lt;/code&gt; (the command just provides a unified interface to all the runners),
who's output gets parsed by the &lt;code&gt;wrapper&lt;/code&gt; to turn it into a Firehose model
tree.&lt;/p&gt;
&lt;p&gt;The goal here is that tons of very quick-running tools get run over a
distributed network, and machine-readable reports get filed in a central
location to aid in reviewing packages.&lt;/p&gt;
&lt;h1&gt;Ricky&lt;/h1&gt;
&lt;p&gt;In addition to the actual code to run builds, I've worked on a few tools to
aid with using debuild.me for my DD related life. I have some uncommon
use-cases that are nice to support. One such use-case is the ability to rebuild
packages from the archive (unmodified) to check that they rebuild OK against
the target. This is handy for things like &lt;code&gt;arch:all&lt;/code&gt; packages that get
uploaded (since they never get rebuilt on the buildd machines, and FTBFSs are
sadly common) or packages that have had a &lt;code&gt;Build-Dependency&lt;/code&gt; change on them.&lt;/p&gt;
&lt;p&gt;Ricky is able to create a &lt;code&gt;.dsc&lt;/code&gt; url to your friendly local mirror, and fetch
that exact version of the package. Ricky can then also use the &lt;code&gt;.dsc&lt;/code&gt; (in a
monumental hack) to forge a &lt;code&gt;package_version_source.changes&lt;/code&gt; file, and sign
it with an autobuild key and upload it to the debuild.me instance. Since it
can also modify the &lt;code&gt;.changes&lt;/code&gt;'s target distribution, you can also use this to
test if a package will build on &lt;code&gt;stable&lt;/code&gt; or &lt;code&gt;testing&lt;/code&gt;, unmodified.&lt;/p&gt;
&lt;h1&gt;Fred&lt;/h1&gt;
&lt;p&gt;Fred is a wrapper around Ricky, to help with fetching packages that may not
exist yet. Fred also contains an email scraper to read off such lists as
&lt;a href="http://lists.debian.org/debian-devel-changes"&gt;debian-devel-changes&lt;/a&gt;, and
add an entry to fetch that upload when it becomes available on the local
mirror, pass it to &lt;code&gt;ricky&lt;/code&gt;, and allow debuild.me to rebuild new packages
that match a set of criteria.&lt;/p&gt;
&lt;p&gt;I'm currently playing around with the idea of rebuilding all incoming
Python packages to ensure they don't FTBFS in a clean chroot.&lt;/p&gt;
&lt;h1&gt;Loofah&lt;/h1&gt;
&lt;p&gt;Loofah is also another wrapper around Ricky, but for use manually. Loofah
is able to sync down the apt &lt;code&gt;Sources&lt;/code&gt; list, and place it in Mongo for fast
queries. This than allows me to manually run rebuilds on any Source package
that fits a set of criteria (written in the form of a Mongo query), which get
pulled and uploaded by &lt;code&gt;Ricky&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;An example script to rebuild any packages that &lt;code&gt;Build-Depend&lt;/code&gt; on
&lt;code&gt;python3-all-dev&lt;/code&gt; in Debian &lt;code&gt;unstable&lt;/code&gt; / &lt;code&gt;main&lt;/code&gt; would look like:&lt;/p&gt;
&lt;aside class='right'&gt;
    You can find more queries in the Loofah
    &lt;a href = 'https://github.com/paultag/loofah/tree/master/eg' &gt;examples&lt;/a&gt;
&lt;/aside&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;unstable&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;suite&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;main&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;quot;Build-Depends&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;python3-all-dev&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Or, a script to rebuild any package that depends on CDBS:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;$or&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Build-Depends&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;cdbs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
             &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;Build-Depends-Indep&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;cdbs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You can use anything that exists in the &lt;code&gt;Sources.gz&lt;/code&gt; file to query off of (
including &lt;code&gt;Maintainer&lt;/code&gt;!)&lt;/p&gt;
&lt;h1&gt;Future Work&lt;/h1&gt;
&lt;p&gt;The future work on debuild.me will be centered around making it easier for
buildd nodes to be added to the network, with more and more automation in that
process (likely in the form of debs). I also want to add better control over
the jobs, so that packages I upload only go to my personal servers.&lt;/p&gt;
&lt;p&gt;I'd also very much like to get better EC2 / Virtualization support integrated
into the network, so that the buildd count grows with the queue size. This
is a slightly hard problem that I'm keen to fix.&lt;/p&gt;
&lt;p&gt;I'm also considering moving the log parsing code &lt;em&gt;out&lt;/em&gt; of the workers, so that
the parsing code can be fixed without upgrading all the workers. This would also
drop the &lt;code&gt;Firehose&lt;/code&gt; dep on the client code, which would be nice.&lt;/p&gt;
&lt;p&gt;Migration from a debuild.me build into a local &lt;code&gt;reprepro&lt;/code&gt; repo is something
that would be fairly easy to do as well, likely to be done remotely via
the &lt;code&gt;XML-RPC&lt;/code&gt; interface, which calls a couple of &lt;code&gt;reprepro&lt;/code&gt; commands (such as
&lt;code&gt;includedsc&lt;/code&gt; and &lt;code&gt;includedeb&lt;/code&gt;) and publishes it to the user's repo. This is
a nice use of the debs that get built, and could also allow debuild.me to be
used like a PPA system, but this allows the user to &lt;em&gt;not&lt;/em&gt; migrate packages
that may contain &lt;code&gt;piuparts&lt;/code&gt; issues.&lt;/p&gt;</summary><category term="debuild.me"></category><category term="firehose"></category><category term="debian"></category></entry><entry><title>A primer on apt's mirror:// protocol</title><link href="https://notes.pault.ag/apt-mirror" rel="alternate"></link><published>2013-02-23T21:04:00-05:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2013-02-23:apt-mirror</id><summary type="html">&lt;p&gt;It's sometimes helpful to keep your machines using a list of apt archives
to use, rather then a single mirror, because redundancy is good. Rather then
using (the great) services like &lt;code&gt;http.debian.net&lt;/code&gt; or &lt;code&gt;ftp.us.debian.org&lt;/code&gt;,
you can set your own mirror lists using apt's &lt;code&gt;mirror://&lt;/code&gt; protocol.&lt;/p&gt;
&lt;aside class="right"&gt;
    While initially hacking this through, Micah ended up
    filing a bug on &lt;code&gt;mirror://&lt;/code&gt;, more information in
    &lt;a href="http://bugs.debian.org/699310"&gt;the bts&lt;/a&gt;. I've since been
    able to get it to work for me, but beware!
&lt;/aside&gt;

&lt;p&gt;All of this is ultra unstable, so be a bit careful when using this. I've been
using &lt;code&gt;mirror://&lt;/code&gt; for a few months now, and it seems fine (even have my servers
using it), but it was a bit of a pain to set up. It gets slightly confused if
you point it at something bad, and it's a mild pain to debug. Hopefully
more people will see the value in &lt;code&gt;mirror://&lt;/code&gt;, and contribute code to it's
development.&lt;/p&gt;
&lt;h1&gt;Why bother?&lt;/h1&gt;
&lt;p&gt;If you have a local network mirror, it's helpful to have your machines default
to a local mirror, if you're the sort to keep an archive mirror on the LAN,
and fall back to your nearest friendly mirror otherwise. In addition,
this lets you hand-define where apt searches for mirrors, which is great, since
you can control the subset of servers you ping a bit more closely.&lt;/p&gt;
&lt;h1&gt;Practical Bits / quickstart&lt;/h1&gt;
&lt;p&gt;The following block covers the quick and dirty details on how to set up
&lt;code&gt;mirror://&lt;/code&gt; for use on your machine (today!). This is very basic, and
details are very sparse, but hopefully there's enough here to help folks
use this on their local system. Basically, you've got three core things to do:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pick your mirrors (this one's a bit of a duh)&lt;/li&gt;
&lt;li&gt;Put them in a public place you can always get to, regardless of
     where you are in cyberspace (I use
     &lt;a href="http://static.pault.ag/debian/mirrors.txt"&gt;static.pault.ag&lt;/a&gt;) - remember,
     this is the one thing all your machines need to always get to, no matter
     where they are.&lt;/li&gt;
&lt;li&gt;Configure your &lt;code&gt;sources.list&lt;/code&gt; to use the mirror.txt file by pointing
     to the text file with the &lt;code&gt;mirror://&lt;/code&gt; protocol.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Turns out &lt;code&gt;mirror://&lt;/code&gt;'s protocol handler will segfault if you give it
something bad, so don't be afraid if you see &lt;code&gt;apt-get update&lt;/code&gt; segfault - it
just means you've likely not pointed it at a valid text file. The format of
the text file should be a simple text file of mirrors it can try, in
order of priority. Mine looks a bit like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;http://127.0.0.1:3142/debian.lcs.mit.edu/debian/
http://debian.lcs.mit.edu/debian/
# http://http.debian.net/debian/
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Finally, your &lt;code&gt;sources.list&lt;/code&gt; entry should look a bit like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;deb&lt;/span&gt; &lt;span class="s"&gt;mirror://static.pault.ag/debian/mirrors.txt&lt;/span&gt; &lt;span class="kp"&gt;unstable&lt;/span&gt; &lt;span class="kp"&gt;main&lt;/span&gt;
&lt;span class="k"&gt;deb&lt;/span&gt; &lt;span class="s"&gt;mirror://static.pault.ag/debian/mirrors.txt&lt;/span&gt; &lt;span class="kp"&gt;experimental&lt;/span&gt; &lt;span class="kp"&gt;main&lt;/span&gt;
&lt;span class="k"&gt;deb-src&lt;/span&gt; &lt;span class="s"&gt;mirror://static.pault.ag/debian/mirrors.txt&lt;/span&gt; &lt;span class="kp"&gt;unstable&lt;/span&gt; &lt;span class="kp"&gt;main&lt;/span&gt;
&lt;span class="k"&gt;deb-src&lt;/span&gt; &lt;span class="s"&gt;mirror://static.pault.ag/debian/mirrors.txt&lt;/span&gt; &lt;span class="kp"&gt;experimental&lt;/span&gt; &lt;span class="kp"&gt;main&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h1&gt;Problems&lt;/h1&gt;
&lt;p&gt;With the good comes the bad. Not everything fully supports this, and most
tools that parse &lt;code&gt;sources.list&lt;/code&gt; break in a really silly way.&lt;/p&gt;
&lt;h2&gt;command-not-found&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;update-command-not-found&lt;/code&gt; will blow up like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;W: Don&amp;#39;t know how to handle mirror
W: Don&amp;#39;t know how to handle mirror
W: Don&amp;#39;t know how to handle mirror
W: Don&amp;#39;t know how to handle mirror
W: Don&amp;#39;t know how to handle mirror
W: Don&amp;#39;t know how to handle mirror
&lt;/pre&gt;&lt;/div&gt;</summary><category term="debian"></category><category term="apt"></category></entry><entry><title>Using env(1) in the shebang</title><link href="https://notes.pault.ag/env-in-shebang" rel="alternate"></link><published>2013-01-15T20:02:00-05:00</published><author><name>Paul Tagliamonte</name></author><id>tag:notes.pault.ag,2013-01-15:env-in-shebang</id><summary type="html">&lt;p&gt;Some of you out there may have tried to pass flags to a script that was being
invoked via &lt;code&gt;/usr/bin/env&lt;/code&gt; in the shebang (&lt;code&gt;#!&lt;/code&gt;), such as &lt;code&gt;python&lt;/code&gt;. You might
recall an error such as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;/usr/bin/env: python -d: No such file or directory
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This error is super annoying, so I went about trying to figure out how
I can pass arguments to &lt;code&gt;python&lt;/code&gt; (or even things like &lt;code&gt;ipython&lt;/code&gt; or &lt;code&gt;bpython&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The idea is we can abuse the concept of a
&lt;a href="http://en.wikipedia.org/wiki/Polyglot_(computing)"&gt;polygot&lt;/a&gt; to shim in some
things we care about.&lt;/p&gt;
&lt;h1&gt;Implementation&lt;/h1&gt;
&lt;p&gt;Let's take a look at a quick script I hacked up to use bpython with a pre-made
script that drops into interactive work.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;#!/bin/sh
&amp;quot;&amp;quot;&amp;quot;&amp;quot;:
exec /usr/bin/env bpython -i $0 $@
&amp;quot;&amp;quot;&amp;quot;
import hy
print &amp;quot;Hython is now importable!&amp;quot;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Let's step through this slowly. First, the bits the &lt;code&gt;bash&lt;/code&gt; sees:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;:
&lt;span class="nb"&gt;exec&lt;/span&gt; /usr/bin/env bpython -i &lt;span class="nv"&gt;$0&lt;/span&gt; &lt;span class="nv"&gt;$@&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Which will cause &lt;code&gt;bpython&lt;/code&gt; to reload the file, which looks like the following
to Python:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&amp;quot;:&lt;/span&gt;
&lt;span class="sd"&gt;exec /usr/bin/env bpython -i $0 $@&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;hy&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Hython is now importable!&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Where Python can now ignore the docstring. Magic!&lt;/p&gt;</summary><category term="unix"></category><category term="posix"></category></entry></feed>