<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Biotimylated</title>
    <atom:link href="http://blog.tim-smith.us/feed.xml" rel="self" type="application/rss+xml"/>
    <link>http://blog.tim-smith.us/</link>
    <description>Tim D. Smith's blog</description>
    <pubDate>Thu, 04 Jan 2024 05:19:07 +0000</pubDate>
    
      <item>
        <title>Pickle, slots, attrs, and inheritance</title>
        <link>http://blog.tim-smith.us/2024/01/pickle-slots/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2024/01/pickle-slots/</guid>
        <description>&lt;p&gt;When slotted &lt;a href=&quot;https://www.attrs.org/en/stable/&quot;&gt;attrs&lt;/a&gt; classes are derived from a non-attrs base class, the attributes of the base class do not survive pickling.&lt;/p&gt;

&lt;p&gt;What does this mean? Why would one do such a thing? What can one do about it?&lt;/p&gt;

&lt;p&gt;For a contrived example, consider these two classes:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyMixin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mixed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;define&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyMixin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;eggs&quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__attrs_post_init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyMixin&lt;/code&gt; is just some class that doesn’t depend on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attrs&lt;/code&gt;.&lt;sup&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;This generally works the way you probably expect; instances of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt;
all get decorated with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mixed&lt;/code&gt; attribute.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spam&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'eggs'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mixed&lt;/span&gt;
&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Wonderful.&lt;/p&gt;

&lt;p&gt;The problems don’t begin until later, when one discovers that &lt;a href=&quot;https://blog.nelhage.com/post/pickles-and-ml/&quot;&gt;one has cause&lt;/a&gt; to use
Python’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pickle&lt;/code&gt; module to serialize an instance of the class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt;.&lt;sup&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;It &lt;em&gt;seems&lt;/em&gt; to work:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a_prime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pickle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pickle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dumps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Round-trip `a` through pickle
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a_prime&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spam&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'eggs'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But the integration tests break violently!
Other code depends on the mixin’s attributes surviving serialization, but the mixin attributes &lt;em&gt;don’t&lt;/em&gt; survive serialization:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a_prime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mixed&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;AttributeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'A'&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;has&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;no&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attribute&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'mixed'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s funny. Where’d our attribute go?&lt;/p&gt;

&lt;h3 id=&quot;the-case-of-the-missing-member&quot;&gt;The case of the missing member&lt;/h3&gt;

&lt;p&gt;If we start experimenting, we may notice a couple of things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mixed&lt;/code&gt; gets dropped if we decorate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@attr.define&lt;/code&gt;.
But in code where we used the old-style &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@attr.s&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@attr.define&lt;/code&gt;,
instances round-trip through pickle just fine, preserving their &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mixed&lt;/code&gt; attribute. 🧐&lt;/li&gt;
  &lt;li&gt;If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyMixin&lt;/code&gt; is &lt;strong&gt;itself&lt;/strong&gt; an attrs class, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mixed&lt;/code&gt; attribute always survives a round-trip, whether we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@attr.define&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@attr.s&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’ve been following attrs development, you might have recalled that one of the &lt;a href=&quot;https://github.com/python-attrs/attrs/issues/487&quot;&gt;key differences&lt;/a&gt; between the “cute” &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attr.s&lt;/code&gt; API and the “next generation” &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attr.define&lt;/code&gt; API is that the next-generation API defaults to creating &lt;strong&gt;slotted classes&lt;/strong&gt;. The &lt;a href=&quot;https://docs.python.org/3/reference/datamodel.html#slots&quot;&gt;slotted data model&lt;/a&gt; for classes comes with some performance advantages and better hygiene: it prevents users from creating undeclared attributes on an instance at runtime. With attrs, it also comes with a long list of edge cases, &lt;a href=&quot;https://www.attrs.org/en/stable/glossary.html#term-slotted-classes&quot;&gt;mercifully summarized&lt;/a&gt; in the attrs docs. Could slots be to blame?&lt;/p&gt;

&lt;p&gt;Indeed, redefining &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@attr.define(slots=False)&lt;/code&gt; restores our ability to round-trip the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mixed&lt;/code&gt; attribute through pickling.&lt;/p&gt;

&lt;p&gt;One of the caveats in the attrs docs might stand out on review:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Slotted classes must implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__getstate__&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__setstate__&lt;/code&gt; to be serializable with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pickle&lt;/code&gt; protocol 0 and 1. Therefore, &lt;em&gt;attrs&lt;/em&gt; creates these methods automatically for slotted classes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Peeking at the &lt;a href=&quot;https://github.com/python-attrs/attrs/blob/23.2.0/src/attr/_make.py#L1031&quot;&gt;implementation&lt;/a&gt; of these methods explains our observations: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__getstate__&lt;/code&gt; method that attrs writes for slotted classes works from a list of &lt;em&gt;attrs properties&lt;/em&gt; belonging to the type to be serialized and its superclasses. It doesn’t try to include any properties of an instance that weren’t defined as attrs attribs.&lt;/p&gt;

&lt;p&gt;How does this interact with inheritance? Mostly, it doesn’t. There is no established mechanism to invoke any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__getstate__ &lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__setstate__&lt;/code&gt; methods associated with a class’s superclasses, and the attrs-generated methods do not try.&lt;/p&gt;

&lt;h3 id=&quot;these-united-states&quot;&gt;These united states&lt;/h3&gt;

&lt;p&gt;So, what can we do to help pickle find our missing properties?&lt;/p&gt;

&lt;p&gt;A few options are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Make the base type (i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyMixin&lt;/code&gt;) an attrs class. This may not be appropriate if the other children of the base type don’t want to use attrs, but it’s very easy and only requires a single line-of-code change.&lt;/li&gt;
  &lt;li&gt;Somehow customize &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__setstate__&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__getstate__&lt;/code&gt; on every leaf class we want to pickle. It’s not sufficient to define these on the base type; even though attrs will avoid overwriting these methods if they are already defined on a class, it ignores inherited implementations. You could imagine implementing this with a class decorator or something similarly cute, but that would still require changes at each point of use of the base class, which would make the example mixin much more annoying to use.&lt;/li&gt;
  &lt;li&gt;Types can define a &lt;a href=&quot;https://docs.python.org/3/library/pickle.html#object.__reduce__&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__reduce__&lt;/code&gt; method&lt;/a&gt; which (empirically!) takes precedence over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__getstate__&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__setstate__&lt;/code&gt;. We can implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__reduce__&lt;/code&gt; on the base type and make it responsible for collecting and setting the right state about itself and any subtypes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For both options 2 and 3, it’s awkward that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyMixin&lt;/code&gt; acquires responsibility for serializing subtypes that it doesn’t know or care about. One possibility is to delegate to any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__(get|set)state__&lt;/code&gt; methods that we find on the instance it receives, which must belong to subclasses, and can take responsibility for their own properties.&lt;/p&gt;

&lt;h3 id=&quot;writing-a-reduce-method-for-the-base-type&quot;&gt;Writing a reduce method for the base type&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__reduce__&lt;/code&gt; is expected to return a weird tuple, of length between 2 and 6. Quoting the pickle docs for each member, with annotations:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;1 . A callable object that will be called to create the initial version of the object.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is usually &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object.__new__&lt;/code&gt;. That’ll work for us.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;2 . A tuple of arguments for the callable object. An empty tuple must be given if the callable does not accept any argument.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object.__new__&lt;/code&gt; receives the type of the object to create. We can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(type(self),)&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;3 . Optionally, the object’s state, which will be passed to the object’s &lt;a href=&quot;https://docs.python.org/3/library/pickle.html#object.__setstate__&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__setstate__()&lt;/code&gt;&lt;/a&gt; method as previously described.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;This is effectively where we override &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__getstate__&lt;/code&gt;.&lt;/strong&gt; The object’s state is held in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self.__dict__&lt;/code&gt;, and in any slots on either this type or its children. If we assume that all slotted children are attrs classes, we’ll know that know that any subtypes that use slots will have a customized &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__getstate__()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;What we can do is:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__getstate__&lt;/code&gt; exists, call it, and assume that it handles all of the subtype’s responsibilities.&lt;/li&gt;
  &lt;li&gt;Otherwise, grab the instance &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__dict__&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Add any state specific to the slots of the base class.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;4 . Optionally, an iterator (and not a sequence) yielding successive items. These items will be appended to the object either using obj.append(item) or, in batch, using obj.extend(list_of_items).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We don’t care about this; we can set it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;5 . Optionally, an iterator (not a sequence) yielding successive key-value pairs. These items will be stored to the object using obj[key] = value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We don’t care about this; we can set it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;6 . Optionally, a callable with a (obj, state) signature. This callable allows the user to programmatically control the state-updating behavior of a specific object, instead of using obj’s static &lt;a href=&quot;https://docs.python.org/3/library/pickle.html#object.__setstate__&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__setstate__()&lt;/code&gt;&lt;/a&gt; method. If not None, this callable will have priority over obj’s &lt;a href=&quot;https://docs.python.org/3/library/pickle.html#object.__setstate__&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__setstate__()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;This is where we override &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__setstate__&lt;/code&gt;.&lt;/strong&gt; We get to provide a callable that accepts a brand new instance of the class, and feeds it the state that the instance needs to contain.&lt;/p&gt;

&lt;p&gt;If the instance has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__setstate__&lt;/code&gt; method, we can accomplish most of our work by calling it, and then slide in our base-class state on top of it.&lt;/p&gt;

&lt;p&gt;Otherwise, we’re responsible for the whole kit and caboodle. This is a little bit subtle in the usual attrs ways; we need to circumvent any property setters and frozen class behaviours. We can do this by using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object.__setattr__&lt;/code&gt; to assign each of the properties.&lt;/p&gt;

&lt;h3 id=&quot;denouement&quot;&gt;Denouement&lt;/h3&gt;

&lt;p&gt;A concrete implementation that rescues pickling round-trips for our MyMixin example is:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyMixin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mixed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__reduce__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;tuple&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hasattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__getstate__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# Subtypes with slots should have customized their pickling behaviour.
&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;# Rely on the subtype to handle the things it's responsible for.
&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__getstate__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# The subtype must not have slots. Grab its instance dictionary.
&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__dict__&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Sprinkle our own properties on top:
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hasattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;mixed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mixed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mixed&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__new__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_setstate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_setstate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
    Inspired by https://github.com/python-attrs/attrs/blob/336ea8b691f148b81db62e24bb55c7ca11ddbc6c/src/attr/_make.py#L931
    &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Use object.__setattr__ to actually assign in state, to avoid any property setters
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# or frozen class behaviours.
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# I think the precise form of this (instead of repeatedly calling
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# object.__setattr__(obj, key, value)) is a performance microoptimization.
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;_bound_setattr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__setattr__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__get__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hasattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__setstate__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# If the instance has a setstate method, rely on it to handle almost everything.
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__setstate__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# type: ignore
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# Restore our MyMixin state to the object
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;mixed&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_bound_setattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mixed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mixed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# The object doesn't know how to restore its state, so we're responsible for it all.
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__weakref__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_bound_setattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;define&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MyMixin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;eggs&quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__attrs_post_init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, we can write:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a_prime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pickle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pickle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dumps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a_prime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mixed&lt;/span&gt;
&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;endnotes&quot;&gt;Endnotes&lt;/h3&gt;

&lt;p&gt;1: A “mixin” class &lt;a href=&quot;https://stackoverflow.com/questions/533631/what-is-a-mixin-and-why-is-it-useful&quot;&gt;provides some functionality&lt;/a&gt; to child types, to which the mixin class may not be tightly coupled. It’s not important that the base class here is a mixin, instead of some other kind of base class, but it was the motivating example in the codebase where I encountered this strange circumstance.&lt;/p&gt;

&lt;p&gt;2: To avoid slandering pickle, running into problems serializing this data layout isn’t a pickle-specific problem. A glance at the default &lt;a href=&quot;https://catt.rs/en/stable/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cattrs&lt;/code&gt;&lt;/a&gt; representation of an instance of A is revealing:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unstructure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'spam'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'eggs'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There’s no attempt to represent the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mixed&lt;/code&gt; attribute here, so it follows that you would have to similarly customize how cattrs structures and unstructures this type to preserve any interesting properties belonging to the mixin class.&lt;/p&gt;
</description>
        <pubDate>Tue, 02 Jan 2024 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Vancouver COVID wastewater plot bot</title>
        <link>http://blog.tim-smith.us/2022/04/vancouver-wastewater-covid-bot/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2022/04/vancouver-wastewater-covid-bot/</guid>
        <description>&lt;p&gt;Wastewater sampling is one of the few available unbiased indicators of
community prevalence of COVID-19 in Vancouver. &lt;a href=&quot;https://twitter.com/YVRCovidPlots&quot;&gt;@YVRCovidPlots&lt;/a&gt; is a
community service that shares plots made from Metro Vancouver data to
help present recent wastewater sampling in historical context.&lt;/p&gt;

&lt;p&gt;Early in the pandemic, I was inspired by BC Public Health Officer Dr.
Bonnie Henry’s observation that if you explain to people what you need
them to do and &lt;strong&gt;give them the resources they need to do it&lt;/strong&gt;, people
will overwhelmingly do the right thing.&lt;/p&gt;

&lt;p&gt;Lately, provincial officials have been striking a very different tone.
In their last regular COVID-19 press conferences, Dr. Henry, health
minister Adrian Dix, and BC premier John Horgan bandied about phrases
like “personal responsibility.” Horgan, never known to keep his foot far
from his mouth, suggested that people troubled by the withdrawal of mask
mandates on public transit could simply &lt;a href=&quot;https://dailyhive.com/vancouver/horgan-sanitizer-covid-heat&quot;&gt;use hand sanitizer&lt;/a&gt;. I’m not sure
where he was going with that one.&lt;/p&gt;

&lt;p&gt;I think a valuable way to assess government response to a crisis
requiring collective action is to ask what government did to enable
people to act. Telling the people of BC that they’re responsible for
their own decisions, without using the resources of government to help
people access and understand the data that could usefully inform those
decisions, is hard to understand except as an abdication of
responsibility brought on by an exhaustion of political will. I’m tired,
too. COVID isn’t.&lt;/p&gt;

&lt;p&gt;This Twitter bot is my small contribution to our collective
self-defense. I hope it will help my neighbour Vancouverites make
decisions about when it feels right to get out and reconnect, and when
it’s time to pull out the good masks before a Skytrain trip. In my
wildest dreams, this bot will help demonstrate to our leaders that
there’s a demand for improving the quality of this data. If you like it,
let them know!&lt;/p&gt;

&lt;h2 id=&quot;how-many-people-have-covid&quot;&gt;How many people have COVID?&lt;/h2&gt;

&lt;p&gt;Right now, it’s very hard to understand how many people in BC currently
have COVID and how quickly that’s changing.&lt;/p&gt;

&lt;p&gt;That’s a problem because any given activity can be more or less safe
depending on how many people around you have COVID. Getting a sense of
whether COVID rates are high or low, and rising or falling, can help you
make better decisions and safer plans.&lt;/p&gt;

&lt;p&gt;Case counts are not reliable right now because people in low-risk groups
in BC are not eligible for PCR tests, which is the only kind of test the
government reports. That means the number of positive tests each day
isn’t comparable to the numbers that have been reported in the past.&lt;/p&gt;

&lt;p&gt;(If you’re in BC, you think you might have COVID, and you’re not
absolutely certain you’re in a low-risk group, you should 
&lt;a href=&quot;http://www.bccdc.ca/health-info/diseases-conditions/covid-19/testing/when-to-get-a-covid-19-test&quot;&gt;check the guidelines&lt;/a&gt;
to find out if you’re eligible for PCR testing and treatment.)&lt;/p&gt;

&lt;p&gt;In truth, getting a good estimate of COVID prevalence—the fraction of
people in a population who have COVID at any given moment—is always
hard. Counting confirmed positive tests always undercounts actual cases
because not everybody with COVID gets a test, and sometimes tests give
false negative results. Test positivity rates (number of positive tests
divided by the total number of tests delivered) can help you understand
whether a testing program is missing an unusually high number of cases,
but you can’t interpret it as a population prevalence because it’s not a
random sample of the population—people who have a reason to get a test
are more likely to have COVID than people who don’t.&lt;/p&gt;

&lt;p&gt;If you could test &lt;em&gt;everyone&lt;/em&gt;, &lt;em&gt;every day&lt;/em&gt;, you’d be able to measure
prevalence more or less exactly. Of course, doing that with nasal swabs
would be economically and politically untenable. You could try to
assemble representative samples, using either a panel of volunteers you
test regularly or recruiting people going about their daily lives for a
quick swab, but most governments don’t seem to feel this is an effective
use of public health staffing and laboratory resources.&lt;/p&gt;

&lt;h2 id=&quot;wastewater-sampling&quot;&gt;Wastewater sampling&lt;/h2&gt;

&lt;p&gt;Another approach that comes close to our ideal for assessing infectious
disease prevalence is wastewater sampling, which avoids sampling bias
among the sewer-connected population because &lt;strong&gt;everyone poops&lt;/strong&gt;. Taking
samples from wastewater processing plant inflows requires relatively
little dedicated labor, and running just a handful of laboratory tests
gives you information about an entire community.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/covidbot/image1.jpg&quot; style=&quot;width:3.33498in;height:3.7601in&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Wastewater sampling has its own challenges. For example, not all
effluent passing through a treatment station came from a toilet.
Industrial discharges and flows from stormwater, sinks, and showers can
ebb and flow irregularly over the course of a day, which can
unpredictably dilute waste in the sampling stream. There are more and
less sophisticated ways of correcting for waste flows from other
sources. A relatively fancy way is to use other viruses found in human
waste as a control: &lt;a href=&quot;https://www.nature.com/articles/s41545-018-0019-5&quot;&gt;pepper mild mottle virus&lt;/a&gt;
is a plant RNA virus that passes right through us when we eat peppers.
Expressing COVID concentration relative to PMMoV concentration lets you
factor out other sources of effluent. A simpler method, used in
Vancouver, is to assume that most variation comes from industrial and
environmental sources and that treatment plants process about the same
amount of human waste each day. Multiplying the measured concentration
by the total volume of flows passing through the plant each day yields a
daily COVID copy count, which removes the effect of dilution from other
sources.&lt;/p&gt;

&lt;h2 id=&quot;metro-vancouver&quot;&gt;Metro Vancouver&lt;/h2&gt;

&lt;p&gt;Metro Vancouver is a regional district of BC which delivers services to
the Lower Mainland, including water and sewerage. Metro Vancouver has been
&lt;a href=&quot;http://www.metrovancouver.org/services/liquid-waste/environmental-management/covid-19-wastewater/Pages/default.aspx&quot;&gt;measuring and reporting&lt;/a&gt;
COVID-19 levels in wastewater since summer 2020. I think Metro Vancouver
and BCCDC deserve praise for providing this public service.&lt;/p&gt;

&lt;p&gt;But there are a couple of problems:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Testing frequency is low. Wastewater is only sampled three days a week,
on Monday, Wednesday, and Friday.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Testing latency is high. Wednesday, Friday, and Monday results are posted each Friday—
so two of the new data points we get each week are already more than a week old.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;It’s hard to use the plots on the Metro Vancouver website.
Only a few data points from a single plant are visible at a time
and the bar plots don’t make it easy to understand changes over time.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Slow and infrequent testing severely limits the usefulness of this
data as a way to help people make better decisions.&lt;/strong&gt; There are plenty
of examples of governments doing better. For instance, Massachusetts
Water Resources Authority, which serves greater Boston,
&lt;a href=&quot;https://www.mwra.com/biobot/biobotdata.htm&quot;&gt;takes samples daily&lt;/a&gt;
and has a one-day turnaround. That’s news you can use!&lt;/p&gt;

&lt;p&gt;There’s not much I can do about the first two points (contact your city
government, Metro Vancouver, and your MLA to let them know you want
better wastewater data from Metro Vancouver and BCCDC!) but I do know
how to make a plot.
Let’s see if we can make the data we &lt;em&gt;do&lt;/em&gt; have easier to interpret.&lt;/p&gt;

&lt;h2 id=&quot;finding-the-plot&quot;&gt;Finding the plot&lt;/h2&gt;

&lt;p&gt;The first challenge: can we get the data in order to plot it?
Pleasantly, Metro Vancouver’s plots page uses dynamic plots, which
retrieve the data in the background from a public Sharepoint API. We can
access the same API from a script to get machine-readable, readily
plottable data.&lt;/p&gt;

&lt;p&gt;The next challenge: what’s the right data to plot? Metro Vancouver
provides both measured COVID concentration (copies/L) and an estimate of
total daily COVID copies (copies/day). The latter is computed from the
former by multiplying it by the volume of wastewater that passes through
each water treatment plant each day. As discussed above, this is
intended to help reduce the impact of dilution from environmental flows
on the data. Whether it normalizes the data effectively is unclear; you
can imagine a number of problems that could arise depending on the
uncertainty of the volume data, the details of the sampling procedure,
and the timing of the samples versus the timing of irregular flows.
Metro Vancouver doesn’t share details on their sampling process.&lt;/p&gt;

&lt;p&gt;In practice, they yield pretty similar looking curves if you put them on
the same scales:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/covidbot/image3.png&quot; style=&quot;width:7.22025in;height:6.39057in&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Normalization seems unnecessary here, and seems to suppress the signal
from the 2nd and 3rd waves seen at Iona Island. I’ll prefer the rawer
concentration values instead of the normalized copy values.&lt;/p&gt;

&lt;p&gt;The next challenge: what should the plots look like? After playing with
a few options, I settled on something similar to MWRA’s approach of
presenting both data since the beginning of the pandemic, as well as a
subset of recent data, to help give a sense of both global and local
context for the latest numbers. I thought using
&lt;a href=&quot;https://en.wikipedia.org/wiki/Small_multiple&quot;&gt;small multiples&lt;/a&gt; to present
each plant made it easier to appreciate the differences in the regional
evolution of the pandemic than overplotting them in different colors.&lt;/p&gt;

&lt;p&gt;An annoying problem is that the Omicron peak in January 2022 swamps the
earlier waves; I tried applying a log transformation to the y axis but I
found the result even harder to interpret.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/covidbot/image2.png&quot; style=&quot;width:7.19792in;height:7.875in&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is what I ended up with:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/covidbot/image4.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The vertical lines indicate the current date, to show the gap since the
last measurements.&lt;/p&gt;

&lt;p&gt;Finally, how should I share these? I think one shouldn’t have to be a
data scientist to be an informed citizen, so I want to get these out in the commons.
 I’ve been inspired by Vancouver CBC reporter and man-about-town
&lt;a href=&quot;https://twitter.com/j_mcelroy&quot;&gt;Justin McElroy&lt;/a&gt;, who helpfully informed the
COVID discussion in BC by making and posting accessible plots. I’m a
sucker for a &lt;a href=&quot;https://twitter.com/breezybrightbot&quot;&gt;Twitter bot&lt;/a&gt;,
so I decided to give that a try.&lt;/p&gt;

&lt;p&gt;The bot tweets as &lt;a href=&quot;https://twitter.com/YVRCovidPlots&quot;&gt;@YVRCovidPlots&lt;/a&gt;, and the script lives &lt;a href=&quot;https://github.com/tdsmith/yvrcovidplots&quot;&gt;on Github&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Sat, 16 Apr 2022 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>I declare! Personal infrastructure!</title>
        <link>http://blog.tim-smith.us/2021/12/vps-puppet-docker/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2021/12/vps-puppet-docker/</guid>
        <description>&lt;p&gt;The time had come to rebuild my (circa 2014) VPS,
which was running Ubuntu 18.04 and couldn’t be upgraded further.
Inspired by glyph’s &lt;a href=&quot;https://glyph.twistedmatrix.com/2021/06/trash-panda-devops.html&quot;&gt;A Tired Raccoon’s Containerization Manifesto&lt;/a&gt;,
I decided to try to push as much as I can into containers.
Here are a few notes about things I learned on the way.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/bankruptcy.webp&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;declarative-configuration-management&quot;&gt;Declarative configuration management&lt;/h2&gt;

&lt;p&gt;Part of the ordeal of a migration is re-discovering all of the things
I had to learn in order to configure the system the first time.
This time, I’m capturing everything I’m changing
by using a configuration management tool
to set up the services on the host.
&lt;em&gt;Vim ist verboten!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I wanted a solution where I’d be able to edit the configuration on my laptop
and just mash a button locally to synchronize the state of my VPS with the configuration.&lt;/p&gt;

&lt;p&gt;I liked the idea of writing declarative configurations
and I don’t like YAML,
so between Ansible, Chef, Puppet, and Salt,
I chose Puppet.&lt;/p&gt;

&lt;p&gt;The onboarding documentation for these tools
tends to focus on setting up the infrastructure you need
to manage and monitor a whole fleet of servers.&lt;/p&gt;

&lt;p&gt;Happily, Puppet’s Bolt tool lets you
&lt;a href=&quot;https://puppet.com/docs/bolt/latest/applying_manifest_blocks.html&quot;&gt;apply a Puppet manifest&lt;/a&gt;
to any node you can ssh to,
without any persistent management or agent services,
which was exactly what I wanted.&lt;/p&gt;

&lt;p&gt;Getting Docker services set up and working with Google Artifact Registry
required installing the Google Cloud SDK
and provisioning it with credentials from a service account.
Once that’s in place, running containerized services works out of the box
with the docker module.&lt;/p&gt;

&lt;p&gt;This is what the manifest ends up looking like: &lt;a href=&quot;https://gist.github.com/tdsmith/478f6346ed227e19132c1cb413821cb4&quot;&gt;Gist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most of the arguments to the Docker resources end up being passed through
to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker&lt;/code&gt; command line one way or another.&lt;/p&gt;

&lt;p&gt;Applying the manifest is as simple as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bolt apply manifest.pp --targets myhosts&lt;/code&gt;
after writing a Bolt &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inventory.yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;h2 id=&quot;certificate-management&quot;&gt;Certificate management&lt;/h2&gt;

&lt;p&gt;Among the services I run on my server are static Web hosting for my partner and me,
and a znc bouncer for libera.chat.
I’d like to secure connections to these services with TLS.&lt;/p&gt;

&lt;p&gt;The old server served HTTP with Apache
and I managed TLS certificates using the Let’s Encrypt client,
running a short shell script periodically to sync the refreshed certificates to znc.
We can do better!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://caddyserver.com/&quot;&gt;Caddy&lt;/a&gt; is an open-source,
memory-safe web server with robust reverse proxy support
and simple automatic TLS configuration
that knows how to obtain certificates with ACME out of the box.&lt;/p&gt;

&lt;p&gt;Using the &lt;a href=&quot;https://github.com/mholt/caddy-l4&quot;&gt;level4 extension&lt;/a&gt;,
it can happily provision certificates,
terminate TLS,
and reverse-proxy for arbitrary streams,
including IRC.&lt;/p&gt;

&lt;p&gt;Caddy ends up being the only exposed service,
forwarding cleartext traffic to znc over the Docker bridge network.&lt;/p&gt;

&lt;p&gt;A downside of using level4 is that you can’t use it with
the friendly Caddyfile configuration DSL,
so I had to figure out how
to write configuration in Caddy’s JSON configuration language.
This is partly mitigated by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;caddy adapt&lt;/code&gt; utility,
which emits the equivalent JSON for a Caddyfile,
which is straightforward to graft into the proper JSON config,
so I can continue to describe my web hosting in Caddyfile.&lt;/p&gt;

&lt;p&gt;A couple of tricks are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;For the ZNC web interface to work,
you have to keep Caddy from advertising http/2 availability
in ALPN during the TLS handshake.&lt;/li&gt;
  &lt;li&gt;Not all IRC clients send SNI, so you need to define a default SNI
so that caddy can choose a certificate to send.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The configuration for layer4 ends up looking like:&lt;/p&gt;

&lt;div class=&quot;language-jsonc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;apps&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;layer4&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;servers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;znc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;listen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;:1337&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;routes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;handle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// Terminate TLS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;handler&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tls&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;connection_policies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;alpn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http/1.1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http/1.0&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;default_sni&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;kvm.tds.xyz&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// Forward cleartext to ZNC&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;handler&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;proxy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;upstreams&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dial&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;znc:2337&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// Network address to proxy to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;what-have-we-learned&quot;&gt;What have we learned?&lt;/h2&gt;

&lt;p&gt;It’s possible to rub some declarative configuration onto a VPS
to make administering personal infrastructure a little more predictable.&lt;/p&gt;

&lt;p&gt;Throwing some services into a container and putting Caddy in front
is a convenient way to get automatic certificate renewal
without too many hijinks.&lt;/p&gt;

&lt;p&gt;Death still comes for us all,
but at least migrating to a new VPS provider won’t suck as much.&lt;/p&gt;

</description>
        <pubDate>Wed, 22 Dec 2021 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>A minimal Gmail API example</title>
        <link>http://blog.tim-smith.us/2018/12/gmail-api-example/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2018/12/gmail-api-example/</guid>
        <description>&lt;p&gt;Once upon a time, I created a whole lot of gmail labels that I don’t use anymore.
I wanted to clean those up in anticipation of Inbox going away.
There are a lot of outdated examples for creating a Google API client application,
so I wanted to capture the form of the “modern” solution I found.&lt;/p&gt;

&lt;p&gt;The solution is captured &lt;a href=&quot;https://gist.github.com/tdsmith/ca5766468827280481dcb7ae4e62f876&quot;&gt;in a notebook&lt;/a&gt;.
It uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google_auth_oauthlib&lt;/code&gt; library to create an authorized &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests&lt;/code&gt; session, and then just does REST-y things
instead of using a client library wrapper.&lt;/p&gt;

&lt;p&gt;To use it, run the first cell, visit the link, copy-paste
the authorization code into the text widget,
and then run the rest of the notebook.&lt;/p&gt;
</description>
        <pubDate>Thu, 27 Dec 2018 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Certificate spelunking for fun and profit</title>
        <link>http://blog.tim-smith.us/2018/03/moressl-spelunking/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2018/03/moressl-spelunking/</guid>
        <description>&lt;p&gt;Google and others contribute TLS certificates
to Certificate Transparency logs
as they crawl the web,
but there is less systematic effort
devoted to discovering certificates
from secure, non-HTTPS protocols.
I searched the
&lt;a href=&quot;https://github.com/rapid7/sonar/wiki/More-SSL-Certificates&quot;&gt;Rapid7 “More SSL” scan log&lt;/a&gt;
for novel certificates and contributed them to CT logs.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.certificate-transparency.org/&quot;&gt;Certificate transparency&lt;/a&gt;
is an effort to publicly log
all publicly trusted TLS certificates.
Publishing certificates has
&lt;a href=&quot;https://www.certificate-transparency.org/what-is-ct&quot;&gt;a number of benefits&lt;/a&gt;,
including allowing site owners to detect
fraudulent certificates issued in their name,
and allowing more comprehensive oversight
of CA operations.&lt;/p&gt;

&lt;p&gt;Google Chrome will begin
&lt;a href=&quot;https://groups.google.com/a/chromium.org/d/msg/ct-policy/sz_3W_xKBNY/6jq2ghJXBAAJ&quot;&gt;requiring CT records&lt;/a&gt;
to trust newly-issued certificates, beginning in the next month,
but historical certificates may not have been logged,
and site operators may prefer not to log certificates
which are not intended for web browsers.&lt;/p&gt;

&lt;p&gt;TLS certificates protecting HTTPS connections
are collected by Google’s spider
and added to CT logs.
I hypothesized that TLS-protected, non-HTTPS services
might be an additional source of novel certificates for logging,
and could reveal unidentified misissuance.&lt;/p&gt;

&lt;p&gt;Rapid7’s &lt;a href=&quot;https://github.com/rapid7/sonar/wiki&quot;&gt;Project Sonar&lt;/a&gt;
scans selected services on the entire IPv4 internet at regular intervals
and produces reports containing,
among other things,
any TLS certificates provided by the scanned hosts.
This work is based on
&lt;a href=&quot;https://github.com/rapid7/sonar/wiki/More-SSL-Certificates&quot;&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;moressl&lt;/code&gt; dataset&lt;/a&gt;,
which contains certificates discovered
while crawling services including SMTP, IMAP, and FTP
in both TLS-wrapped and STARTSSL modes.&lt;/p&gt;

&lt;h2 id=&quot;discovered-certificates&quot;&gt;Discovered certificates&lt;/h2&gt;

&lt;p&gt;This data set did indeed reveal many certificates
which had not been previously logged.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;moressl&lt;/code&gt; dataset contains 10,341,552 certificates as of Mar 22, 2018&lt;/li&gt;
  &lt;li&gt;2,172,408 certificates (21%) had valid signatures
and chained to roots included in the Mozilla trust store&lt;/li&gt;
  &lt;li&gt;56,249 trusted certificates (3.6% of trusted certificates) were novel to
&lt;a href=&quot;https://crt.sh/&quot;&gt;crt.sh&lt;/a&gt;, which has views of many public CT logs&lt;/li&gt;
  &lt;li&gt;22,893 trusted certificates (40% of novel certificates) were unexpired&lt;/li&gt;
  &lt;li&gt;2,279 unexpired, trusted certificates were novel but contained a SCT proof,
meaning that a precertificate had already been disclosed to CT,
but the final, issued certificate had not been logged&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This validates the idea that non-HTTPS TLS services
are an important source of undisclosed certificates,
and confirms the novelty of this analysis. :)&lt;/p&gt;

&lt;p&gt;Although CT signature embedding will become more common,
which requires that CAs submit precertificates to logs
before certificate issuance,
some CAs do not have a practice of subsequently logging
the final issued certificate
containing the embedded CT signature proofs.
Although the precertificates are sufficient
to identify most cases of misissuance,
understanding which proofs
are actually embedded in certificates
helps assess the impact of decertifying logs,
so collecting these issued certificates is still worthwhile
even in the presence of a logged precertificate.&lt;/p&gt;

&lt;h2 id=&quot;cablint-results&quot;&gt;cablint results&lt;/h2&gt;

&lt;p&gt;Running &lt;a href=&quot;https://github.com/awslabs/certlint&quot;&gt;cablint&lt;/a&gt;,
which audits certificates to the CA/Browser Forum Baseline Requirements,
over the unexpired certificates
reveals a variety of badly encoded or otherwise non-compliant certificates,
presented here in approximate order of interest.&lt;/p&gt;

&lt;h3 id=&quot;noncompliance-possibly-revealed-by-this-work&quot;&gt;Noncompliance possibly revealed by this work&lt;/h3&gt;

&lt;p&gt;These certificates were unrevoked at the time of discovery.
I believe these issues with these issuers have not been previously discussed,
or were considered resolved,
or minimally that they were the only results I saw for these CAs on
the &lt;a href=&quot;https://misissued.com/cablint/&quot;&gt;misissued.com cablint page&lt;/a&gt;.
Please let me know if you think I shouldn’t take credit for them. :)&lt;/p&gt;

&lt;p&gt;I have &lt;em&gt;not&lt;/em&gt; applied cablint to all certificates in CT;
it is completely possible that other certificates with these problems,
from these issuers or others, have already been publicly disclosed.
But these were the ones that you couldn’t have seen before yesterday!&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Issuer&lt;/th&gt;
      &lt;th&gt;Last issued&lt;/th&gt;
      &lt;th&gt;Certificate&lt;/th&gt;
      &lt;th&gt;Details&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;GoDaddy&lt;/td&gt;
      &lt;td&gt;2013&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://crt.sh/?id=370273130&amp;amp;opt=cablint,ocsp&quot;&gt;1&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;≥ 60 month validity period&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Digicert&lt;/td&gt;
      &lt;td&gt;2016&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://crt.sh/?q=a44620f1c703393ddd6c90db12a47e7b2074fc17aa34ca10c864363011986ff3&amp;amp;opt=ocsp,cablint&quot;&gt;1&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;IP address encoded as a DNS SAN&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ABB (Digicert)&lt;/td&gt;
      &lt;td&gt;2016&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://crt.sh/?id=370240502&amp;amp;opt=ocsp,cablint&quot;&gt;1&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;CN not in SAN *&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;NetLock&lt;/td&gt;
      &lt;td&gt;2017&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://crt.sh/?id=370179087&amp;amp;opt=ocsp,cablint&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://crt.sh/?id=370239913&quot;&gt;2&lt;/a&gt;, &lt;a href=&quot;https://crt.sh/?id=370210461&amp;amp;opt=ocsp,cablint&quot;&gt;3&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;Invalid EKU (KeyAgreement for RSA key)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;KPN (Logius/PKIoverheid)&lt;/td&gt;
      &lt;td&gt;2016&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://crt.sh/?id=370246293&amp;amp;opt=ocsp,cablint&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://crt.sh/?id=370245612&amp;amp;opt=ocsp,cablint&quot;&gt;2&lt;/a&gt;, &lt;a href=&quot;https://crt.sh/?id=370179725&amp;amp;opt=ocsp,cablint&quot;&gt;3&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;Invalid EKU (KeyAgreement for RSA key)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ECCE 001 (Digicert)&lt;/td&gt;
      &lt;td&gt;2018&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://crt.sh/?id=370256565&amp;amp;opt=ocsp,cablint&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://crt.sh/?id=370210890&amp;amp;opt=ocsp,cablint&quot;&gt;2&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;Invalid string encoding&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Tenera (Digicert)&lt;/td&gt;
      &lt;td&gt;2017&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://crt.sh/?id=370246110&amp;amp;opt=ocsp,cablint&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://crt.sh/?id=370184296&amp;amp;opt=cablint&amp;amp;opt=ocsp,cablint&quot;&gt;2&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;Invalid string lengths&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Entrust&lt;/td&gt;
      &lt;td&gt;2015&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://crt.sh/?id=370213436&amp;amp;opt=ocsp,cablint&quot;&gt;1&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;Invalid string encoding&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;* A &lt;a href=&quot;https://crt.sh/?id=16963475&amp;amp;opt=ocsp,cablint&quot;&gt;certificate&lt;/a&gt; with the same problem from the same issuer was revoked after it was disclosed &lt;a href=&quot;https://groups.google.com/forum/#!msg/mozilla.dev.security.policy/K3sk5ZMv2DE/4oVzlN1xBgAJ&quot;&gt;last August&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;noncompliance-already-visible-from-previously-logged-certificates&quot;&gt;Noncompliance already visible from previously logged certificates&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;del&gt;The &lt;a href=&quot;https://crt.sh/?caid=1483&amp;amp;opt=cablint,zlint&quot;&gt;HydrantID SSL ICA G2&lt;/a&gt;
CA is trusted by Mozilla (via QuoVadis) for TLS authentication, but issues
certs intended for IPSEC and which lack serverAuth and clientAuth EKU values,
which are not BR-compliant (7.1.2.3.f).&lt;/del&gt; These certificates are compliant;
zlint was &lt;a href=&quot;https://github.com/zmap/zlint/pull/218&quot;&gt;reporting them incorrectly&lt;/a&gt;.
I regret the error.&lt;/li&gt;
  &lt;li&gt;Digicert has issued certificates with &lt;a href=&quot;https://groups.google.com/d/msg/mozilla.dev.security.policy/Cyyyjdf_t2Q/rBRBrLYLEQAJ&quot;&gt;underscores in DNS names&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Microsoft’s CA software inserts trailing NULs in the CPSuri field
and fails to flag the KeyUsage extension as critical.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;otherwise-notable&quot;&gt;Otherwise notable&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Comodo issued certificates in &lt;a href=&quot;https://crt.sh/?id=370265846&quot;&gt;2017&lt;/a&gt;
and &lt;a href=&quot;https://crt.sh/?id=370172550&amp;amp;opt=ocsp,cablint&quot;&gt;2014&lt;/a&gt;
where the CN is a U-label and only the matching A-label is present as a SAN;
this situation, but not this certificate, was
&lt;a href=&quot;https://groups.google.com/d/msg/mozilla.dev.security.policy/K3sk5ZMv2DE/4oVzlN1xBgAJ&quot;&gt;discussed&lt;/a&gt;
on m.d.s.p&lt;/li&gt;
  &lt;li&gt;Thawte very recently revoked two certificates containing only metadata in the
subject OU identifier; &lt;a href=&quot;https://crt.sh/?id=370208210&amp;amp;opt=ocsp,cablint&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://crt.sh/?id=370235442&amp;amp;opt=ocsp,cablint&quot;&gt;2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;discussion&quot;&gt;Discussion&lt;/h2&gt;

&lt;p&gt;This work shows that non-HTTPS servers present a meaningful volume of
undisclosed browser-trusted TLS certificates, and that these certificates
contain interesting examples of BR violations.&lt;/p&gt;

&lt;p&gt;It is not obvious to me that the rate or distribution of problems
in this data set is different
than they would be
from a random sample
of certificates already disclosed to CT,
which describes an interesting follow-on experiment.
The &lt;a href=&quot;https://misissued.com/cablint/&quot;&gt;misissued.com cablint page&lt;/a&gt;
monitors recent, but not historical, disclosures, and unknown horrors
may lurk in CT’s merkled depths. &lt;em&gt;Cthulhu fhtagn&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;Happily, systematic evaluations of the quality of historical logged certificates have been undertaken recently, including work presented &lt;a href=&quot;https://www.net.in.tum.de/fileadmin/bibtex/publications/papers/pam18ctlog-slides.pdf&quot;&gt;a few days ago&lt;/a&gt; by Oliver Gasser at TU Munich, and an upcoming &lt;a href=&quot;https://kumarde.com/papers/misissuance.pdf&quot;&gt;conference paper&lt;/a&gt; by Deepak Kumar at UIUC and colleagues. Their work on logged certificates provides a useful baseline for future comparisons to discovered certificates.&lt;/p&gt;

&lt;p&gt;The source code for retrieving and processing scan data is available at
&lt;a href=&quot;https://github.com/tdsmith/tattle&quot;&gt;https://github.com/tdsmith/tattle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Other outcomes of this work included
&lt;a href=&quot;https://github.com/pyca/cryptography/issues/4175&quot;&gt;a bug report against pyca/cryptography&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;acknowledgements&quot;&gt;Acknowledgements&lt;/h2&gt;

&lt;p&gt;Thanks to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://alexgaynor.net/&quot;&gt;Alex Gaynor&lt;/a&gt; for helpful discussion and advice.
I also used Alex’s &lt;a href=&quot;https://github.com/alex/ct-tools&quot;&gt;ct-tools&lt;/a&gt; to submit
certificates to CT logs, and his &lt;a href=&quot;https://github.com/alex/x509-validator&quot;&gt;x509-validator&lt;/a&gt;
informed my chain-building strategy&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://alexgaynor.net/&quot;&gt;Alex Gaynor&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/reaperhulk&quot;&gt;Paul Kehrer&lt;/a&gt;
for their work maintaining &lt;a href=&quot;https://cryptography.io/en/latest/&quot;&gt;pyca/cryptography&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Comodo and &lt;a href=&quot;https://twitter.com/_robstr&quot;&gt;Rob Stradling&lt;/a&gt; for operating &lt;a href=&quot;crt.sh&quot;&gt;crt.sh&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Amazon and Peter Bowen for &lt;a href=&quot;https://github.com/awslabs/certlint&quot;&gt;cablint&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Rapid7 and their collaborators for publishing scan data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any errors are, of course, mine alone.&lt;/p&gt;
</description>
        <pubDate>Sat, 31 Mar 2018 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Vegan Key Lime Pie-lets</title>
        <link>http://blog.tim-smith.us/2018/03/vegan-key-lime-pie/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2018/03/vegan-key-lime-pie/</guid>
        <description>&lt;p&gt;&lt;img src=&quot;/img/key_lime_pielets.jpeg&quot; alt=&quot;Key lime pielets&quot; width=&quot;800px&quot; title=&quot;Key Lime Pielets&quot; /&gt;&lt;br /&gt;
I participate in a
&lt;a href=&quot;https://vegancookoff.blogspot.ca/&quot;&gt;vegan cook-off&lt;/a&gt;
with some friends once a month and I was happy
with how my recipe came together for this month’s Pie-Off,
so I thought I’d share!&lt;/p&gt;

&lt;p&gt;I made key lime pie-lets,
consisting of individual firm custards
each served on their own graham cracker.
There are three components:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Graham crackers,
adapted from Nancy Silverton
by way of
&lt;a href=&quot;https://smittenkitchen.com/2009/05/graham-crackers/&quot;&gt;Smitten Kitchen&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A simple but delicious raspberry sauce,
from &lt;a href=&quot;https://www.tastesoflizzyt.com/easy-raspberry-sauce-recipe/&quot;&gt;Lizzy T&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A coconut-milk agar “custard,” by way of my own experimentation :)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;The
&lt;a href=&quot;https://smittenkitchen.com/2009/05/graham-crackers/&quot;&gt;graham cracker&lt;/a&gt;
adaptations were straightforward:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Earth Balance replaced butter,&lt;/li&gt;
  &lt;li&gt;salt was omitted, since Earth Balance is quite salty,&lt;/li&gt;
  &lt;li&gt;coconut milk replaced cow’s milk,&lt;/li&gt;
  &lt;li&gt;and golden syrup replaced honey.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I took the option to sub in some whole wheat flour
and I thought it worked quite well.
The dough was quite easy to work,
despite Deb’s warnings,
particularly as it warmed up.
I might skip the chilling step if I made it again.
Perhaps I improved the recipe?&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;The
&lt;a href=&quot;https://www.tastesoflizzyt.com/easy-raspberry-sauce-recipe/&quot;&gt;raspberry sauce&lt;/a&gt;
was made as-is,
except that I strained the hot raspberry-sugar mixture
through a wire mesh sieve
before blending in the cornstarch,
to remove most of the seeds.
Crushing the raspberries against the sieve
with the back of a spoon helps liberate the most
raspberry goodness.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;The custard was my own invention!
Instead of using acid to thicken sweetened condensed milk,
like in a traditional key lime pie,
the thickener in this recipe is
&lt;a href=&quot;https://en.wikipedia.org/wiki/Agar&quot;&gt;agar&lt;/a&gt;.
Agar is derived from seaweed, colorless, flavorless,
and sets to a firm gel.&lt;/p&gt;

&lt;p&gt;At &lt;a href=&quot;https://www.tnt-supermarket.com/&quot;&gt;my local Asian-food supermarket&lt;/a&gt;,
I noticed they stocked both pure agar-agar (Telephone brand),
and an agar-agar/sugar mix (Golden Coin brand).
The Golden Coin brand is about 10% agar-agar by weight,
and is easier to work with when making small quantities
of the custard.&lt;/p&gt;

&lt;p&gt;Arrowroot is decidedly non-traditional in agar desserts;
I added some to see it would help give the custards
a creamier texture. I think it helped; see what you think!&lt;/p&gt;

&lt;p&gt;I made the custard in individual ramekins.
You could also make a large batch
by setting the agar in a pan
and using a cookie cutter to cut out individual custards.&lt;/p&gt;

&lt;p&gt;For each 50 ml custard, you’ll want:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;EITHER 6 g Golden Coin agar-agar mix (10% agar/sugar),&lt;br /&gt;
OR (600 mg Telephone-brand agar-agar and 5.4 g table sugar)&lt;/li&gt;
  &lt;li&gt;an additional 4 g sugar&lt;/li&gt;
  &lt;li&gt;1 g arrowroot powder&lt;/li&gt;
  &lt;li&gt;25 ml coconut milk&lt;/li&gt;
  &lt;li&gt;15 ml water&lt;/li&gt;
  &lt;li&gt;10 ml lime juice&lt;/li&gt;
  &lt;li&gt;a pinch of lime zest&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can make several at once by scaling up each ingredient,
as long as you have enough ramekins.
The custard will solidify at room temperature
if you leave it for more than a few minutes.
(If this happens, you can just reheat it,
but heated lime juice may lose some of its pep.)&lt;/p&gt;

&lt;p&gt;Combine all ingredients except the lime juice and zest
in a microwave-safe measuring cup,
and microwave until just boiling and the agar is completely dissolved.
Watch it carefully; it will want to boil over!&lt;/p&gt;

&lt;p&gt;Stir the lime juice and zest into the hot agar
and quickly pour 50 ml
into each 2” ramekin.
Refrigerate for 10-15 minutes or until solid.&lt;/p&gt;

&lt;p&gt;Unmold the agar
by carefully running a paring knife
around the inner circumference of the ramekin,
and then firmly tapping the open end of the ramekin against your palm.&lt;/p&gt;

&lt;p&gt;If you like, use a fluted cookie cutter
to give each custard a cute edge.&lt;/p&gt;

&lt;p&gt;You can store the custards in a closed container in the fridge
for a few days; separate them with layers of wax paper.&lt;/p&gt;

&lt;p&gt;To serve, just pop a custard onto a graham cracker,
and top with the raspberry sauce!&lt;/p&gt;
</description>
        <pubDate>Wed, 14 Mar 2018 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>On Vancouver business license data</title>
        <link>http://blog.tim-smith.us/2018/03/vancouver-business-license-data/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2018/03/vancouver-business-license-data/</guid>
        <description>&lt;p&gt;Yesterday was &lt;a href=&quot;https://www.opendatabc.ca/&quot;&gt;Open Data BC&lt;/a&gt;’s
&lt;a href=&quot;https://www.opendatabc.ca/pages/vodday-2018-vancouver-open-data-day-hackathon&quot;&gt;Open Data Day Hackathon&lt;/a&gt;,
which was a great chance to learn about civic data sets
and spend some hacking on them
with data nerds and subject matter experts from the city and province.&lt;/p&gt;

&lt;p&gt;One interesting data set is the city’s
&lt;a href=&quot;http://data.vancouver.ca/datacatalogue/businessLicence.htm&quot;&gt;business license registry&lt;/a&gt;,
which contains data all the way back to 1997.&lt;/p&gt;

&lt;p&gt;Vancouver residents are technically required to hold a business license
to pursue almost any sort of non-employment income.
That includes renting property.&lt;/p&gt;

&lt;p&gt;I didn’t quite find a story I wanted to tell with the data,
but one surprising trend I noticed was that
the number of licenses associated with single-family housing rentals
(teal line)
has been declining year-over-year.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/housing_licenses_by_year_overall.png&quot; alt=&quot;Housing business licenses by year since 1997&quot; width=&quot;800px&quot; title=&quot;Housing business licenses by year since 1997&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The trend is repeated in
&lt;a href=&quot;/img/housing_business_licenses.pdf&quot;&gt;almost every neighborhood&lt;/a&gt;
except for very built-up neighborhoods
with very few single-family structures
like the CBD, West End, and Fairview.&lt;/p&gt;

&lt;p&gt;Some explanations for this trend could include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Poor awareness of the responsibility to hold a permit:
the number of licenses issued appears much smaller than the number of
housing units, so maybe all we’re measuring is decreasing compliance.
(I think this is probably the most likely interpretation.)&lt;/li&gt;
  &lt;li&gt;Densification or multiple-unit conversions reducing the number
of single-family structures available to rent.
I think this is a factor in some neighborhoods,
but I wouldn’t expect the effect to be so uniform across the city.&lt;/li&gt;
  &lt;li&gt;Increased occupancy of single-family structures by owners, or
an increasing number of empty structures, as the expected investment
return on the property swamps the expected profit on a rental.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some more reliable data sets about housing in Vancouver include
&lt;a href=&quot;http://data.vancouver.ca/datacatalogue/issuedBuildingPermits.htm&quot;&gt;city building permits&lt;/a&gt;,
which is only available for CY 2017 forward
but promises to be a valuable resource,
and &lt;a href=&quot;https://www03.cmhc-schl.gc.ca/hmiportal/en/#Profile/1/1/Canada&quot;&gt;CMHC’s information portal&lt;/a&gt;,
best accessed through
&lt;a href=&quot;https://twitter.com/vb_jens&quot;&gt;Jens von Bergmann’s&lt;/a&gt;
&lt;a href=&quot;https://github.com/mountainmath/cmhc&quot;&gt;R package&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Do you have another explanation?
What kind of stories would you like to be able to tell
about how and where businesses open and close in the city?&lt;/p&gt;
</description>
        <pubDate>Sun, 04 Mar 2018 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Implementing a small language in RPython</title>
        <link>http://blog.tim-smith.us/2016/10/small-language-in-rpython/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2016/10/small-language-in-rpython/</guid>
        <description>&lt;p&gt;Inspired by Alex Gaynor’s &lt;a href=&quot;https://www.youtube.com/watch?v=LCslqgM48D4&quot;&gt;“So You Want To Write An Interpreter?”&lt;/a&gt; PyCon talk, I decided I wanted to try to write an interpreter! In this post I’ll talk about writing Baby’s First Interpreter, porting my interpreter to RPython, and performing the translation.&lt;/p&gt;

&lt;p&gt;My final code lives &lt;a href=&quot;https://github.com/tdsmith/dummylang&quot;&gt;in a Github repository&lt;/a&gt; if you want to follow along.&lt;/p&gt;

&lt;p&gt;Like Alex in his talk, I’ll use &lt;a href=&quot;https://github.com/alex/rply&quot;&gt;rply&lt;/a&gt; to generate the lexer and parser for my interpreter. Alex’s &lt;a href=&quot;https://rply.readthedocs.io/en/latest/&quot;&gt;documentation for rply&lt;/a&gt; gives a walkthrough for building a simple calculator parser that generates an &lt;a href=&quot;https://en.wikipedia.org/wiki/Abstract_syntax_tree&quot;&gt;abstract syntax tree&lt;/a&gt; which can be evaluated by the interpreter.&lt;/p&gt;

&lt;p&gt;Before continuing with this blog post, I recommend implementing and playing with the calculator demo to get a sense of how the lexer and parser interact and to get a sense for the feel of an AST class heirarchy.&lt;/p&gt;

&lt;p&gt;Looking for a slightly taller hill to climb, I found &lt;a href=&quot;/img/eeniemeeny.pdf&quot;&gt;an old paper&lt;/a&gt; &lt;a href=&quot;http://dl.acm.org/citation.cfm?id=234880&quot;&gt;[source]&lt;/a&gt; by Lawrence A. Coon with iteratively more complex sample languages designed for a compiler design lab class. This post will be about my experience with his &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eenie&lt;/code&gt; language.&lt;/p&gt;

&lt;p&gt;NB: Josh Sharp wrote &lt;a href=&quot;http://joshsharp.com.au/blog/view/rpython-rply-interpreter-1&quot;&gt;a very helpful introduction&lt;/a&gt; that covers some of the same ground!&lt;/p&gt;

&lt;h2 id=&quot;lexing-eenie&quot;&gt;Lexing eenie&lt;/h2&gt;

&lt;p&gt;I started writing the front-end for eenie by building the lexer. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eenie&lt;/code&gt; lexer is morally similar to the calculator lexer with a couple of key differences:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eenie&lt;/code&gt; has identifiers: we need the lexer to generate tokens for the names of variables&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eenie&lt;/code&gt; has reserved words we need to identify, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;program&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Handling reserved words is a little tricky with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rply&lt;/code&gt;’s lexer generator because you need to be able to distinguish a reserved word like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has&lt;/code&gt; from a legal identifier name like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hasty&lt;/code&gt;. If you create a rule like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lg.add(&quot;HAS&quot;, &quot;has&quot;)&lt;/code&gt;, the regex &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has&lt;/code&gt; will match &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hasty&lt;/code&gt;, which is not what you want. The pattern &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;has$&quot;&lt;/code&gt; doesn’t ever match.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rply&lt;/code&gt; is based on &lt;a href=&quot;http://www.dabeaz.com/ply/&quot;&gt;PLY&lt;/a&gt;, so I went looking at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PLY&lt;/code&gt;’s documentation; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PLY&lt;/code&gt; essentially lets you define a callback function that’s invoked each time a certain type of token is identified that lets you modify the lexer output. You can solve this problem with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PLY&lt;/code&gt; by creating an identifier rule and then, in the callback, identify the identifiers that are actually reserved words, and then emit a new token type for them.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rply&lt;/code&gt; doesn’t let us define callbacks but we can do something morally similar by intercepting the lexer output after it’s produced and operating on the output a token at a time before feeding it to the parser:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;reserved&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;program&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;has&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;decls&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;lg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ID&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[a-zA-Z][a-zA-Z0-9]*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;lexer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;corpus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lexer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;corpus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ID&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reserved&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that it’s possible to test the lexer without implementing a parser!&lt;/p&gt;

&lt;h2 id=&quot;parsing-eenie-to-an-ast&quot;&gt;Parsing eenie to an AST&lt;/h2&gt;

&lt;p&gt;The grammar in Appendix A of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eenie&lt;/code&gt; paper contains the production rules that we can feed the parser generator to produce our parser. The calculator parser is, again, a good example to begin with.&lt;/p&gt;

&lt;h3 id=&quot;bnf-notation&quot;&gt;BNF notation&lt;/h3&gt;

&lt;p&gt;One notation note is that the BNF for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eenie&lt;/code&gt; contains rules like:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a ::= a, b | b&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;which produces an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; from either a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt;, or an existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; plus a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt;. While translating to rply, in order to generate a product from more than one rule, define an additional generator rule instead of using an or-pipe, like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;a : a COMMA b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;a_from_ab&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;a : b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;a_from_b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also apply more than one production rule to a single method, like in Alex’s calculator example:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'expression : expression PLUS expression'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'expression : expression MINUS expression'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'expression : expression MUL expression'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'expression : expression DIV expression'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;expression_binop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that the canonical &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eenie&lt;/code&gt; grammar specifies separate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;term&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;factor&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exp&lt;/code&gt; products in order to handle operator precedence. You can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exp&lt;/code&gt; for all of these if you add &lt;a href=&quot;https://rply.readthedocs.io/en/latest/api/rply.html#rply.ParserGenerator&quot;&gt;predence rules&lt;/a&gt; for the arithmetic operators to the ParserGenerator constructor.&lt;/p&gt;

&lt;h3 id=&quot;building-up-a-parser&quot;&gt;Building up a parser&lt;/h3&gt;

&lt;p&gt;I found it helpful to begin by implementing the parser in stages.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Declare the production rules and methods but just return the input &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt; from all of them. Now, you can make sure that a) parser generation (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parser = pg.build()&lt;/code&gt;) works, and b) the parser runs on your example code.&lt;/li&gt;
  &lt;li&gt;Implement the AST classes and modify the production methods to return instances of those classes.&lt;/li&gt;
  &lt;li&gt;Implement .eval() on the AST classes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;evaluating-the-ast&quot;&gt;Evaluating the AST&lt;/h3&gt;

&lt;p&gt;One feature &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eenie&lt;/code&gt; has, which the calculator language is missing, is variable assignment and reference. A simple way to keep track of these variables is to pass a Python dictionary around as you evaluate the AST which contains the current values of all defined variables.&lt;/p&gt;

&lt;p&gt;Therefore, the signature of the eval function is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ASTNode.eval(self, context)&lt;/code&gt; where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;context&lt;/code&gt; is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dict&lt;/code&gt; mapping strings (identifier names) to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Number&lt;/code&gt;s (variable values). Variable assignment stores a value in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;context&lt;/code&gt; and identifier references evaluate to the matching value in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;context&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;high-five&quot;&gt;High five!&lt;/h3&gt;

&lt;p&gt;My “finished” Python interpreter is here: &lt;a href=&quot;https://github.com/tdsmith/dummylang/tree/master/eenie&quot;&gt;https://github.com/tdsmith/dummylang/tree/master/eenie&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;porting-eenie-to-rpython&quot;&gt;Porting eenie to RPython&lt;/h2&gt;

&lt;p&gt;I quickly discovered a number of incompatibilities between RPython and my Python coding style. :) RPython is a &lt;a href=&quot;http://rpython.readthedocs.io/en/latest/rpython.html&quot;&gt;subset of Python&lt;/a&gt; upon which type inference is possible. The advantage of writing an interpreter in RPython is that the RPython translation toolchain can compile your interpreter and make it Very Fast. The disadvantage is that RPython is much stricter than standard Python. Some of the restrictions are:&lt;/p&gt;

&lt;h3 id=&quot;type-stability&quot;&gt;Type stability&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Functions should always be called with arguments of the same type (except that functions that accept complex types may also accept None)&lt;/p&gt;

    &lt;p&gt;This means, for example, that you can’t lazily cast something to an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt; after it’s been passed into a constructor. You always need to give the constructor an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt;!&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Functions should always return a value of the same type, except that a) functions that return complex types may also return None, and b) instances of different classes that inherit from the same superclass are allowed.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Names must hold variables of a single type in any given scope; i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a = &quot;foo&quot;; a = Bar()&lt;/code&gt; is not allowed.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the purpose of this paragraph, a “complex type” is any type that is not an integer, float, or boolean.&lt;/p&gt;

&lt;p&gt;The things I needed to change about my naïve Python implementation were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Stop using &lt;a href=&quot;https://attrs.readthedocs.io/en/stable/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attrs&lt;/code&gt;&lt;/a&gt; for my AST classes. :(&lt;/li&gt;
  &lt;li&gt;Make sure that all production functions returned a descendent of the same superclass; mine was called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ASTNode&lt;/code&gt;. In particular, they cannot return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list&lt;/code&gt;s; I had to jury-rig a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ASTList&lt;/code&gt; class. (While you implement this, note that RPython doesn’t support the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__add__&lt;/code&gt; dunder on user-defined classes.)&lt;/li&gt;
  &lt;li&gt;Make sure that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ASTNode.eval()&lt;/code&gt; always returns an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ASTNode&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;None&lt;/code&gt;, and never a primitive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Adding and checking static type annotations with &lt;a href=&quot;http://mypy-lang.org/&quot;&gt;mypy&lt;/a&gt; is a great intermediate step before trying to translate your interpreter with RPython; the mypy error messages are considerably more cromulent.&lt;/p&gt;

&lt;h3 id=&quot;standard-library-support&quot;&gt;Standard library support&lt;/h3&gt;

&lt;p&gt;The only stdlib modules that have RPython support are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;math&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;os&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;time&lt;/code&gt;. This means that operations on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sys.stdin&lt;/code&gt; are right out; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raw_input&lt;/code&gt; is also unimplemented. Instead, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;os.read(0, ...)&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;os.write(1, ...)&lt;/code&gt; to interact with stdin and stdout.&lt;/p&gt;

&lt;h3 id=&quot;defining-a-translation-target&quot;&gt;Defining a translation target&lt;/h3&gt;

&lt;p&gt;The RPython translation framework needs some hints about how to invoke your interpreter. &lt;a href=&quot;https://morepypy.blogspot.com/2011/04/tutorial-writing-interpreter-with-pypy.html#pypy-translation&quot;&gt;This blog post&lt;/a&gt; has a useful sample that I stole wholesale.&lt;/p&gt;

&lt;h3 id=&quot;perform-the-translation&quot;&gt;Perform the translation&lt;/h3&gt;

&lt;p&gt;Download a pypy source tarball or a hg checkout somewhere and run:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python ~/pypy/rpython/bin/rpython target.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If all goes well, you will have a fancy compiled interpreter in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./target-c&lt;/code&gt; in about 30 seconds!&lt;/p&gt;

&lt;p&gt;All did not go well for me. I began getting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UnionError&lt;/code&gt;s which reflected that I had called methods using different type signatures. Tracking all of these down took longer than I want to admit in such a small codebase. I think earlier use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mypy&lt;/code&gt; would have saved me a lot of angst. You can also just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python target.py prog1.eenie&lt;/code&gt; to make sure you should be expecting sane behavior.&lt;/p&gt;

&lt;h3 id=&quot;profit&quot;&gt;Profit???&lt;/h3&gt;

&lt;p&gt;My RPython eenie interpreter &lt;a href=&quot;https://github.com/tdsmith/dummylang/tree/master/eenie_rpython&quot;&gt;lives here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope this story about my experience was helpful! The RPython toolchain is a powerful tool for language implementors and I look forward to playing with it in greater detail.&lt;/p&gt;
</description>
        <pubDate>Tue, 25 Oct 2016 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Voting in the 2016 General Election: Candidates</title>
        <link>http://blog.tim-smith.us/2016/10/how-tims-voting/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2016/10/how-tims-voting/</guid>
        <description>&lt;p&gt;As usual, some attempt to share my efforts making an even slightly informed decision about voting. More blather about propositions later.&lt;/p&gt;

&lt;p&gt;President: You probably don’t need my help here but um definitely Hillary?&lt;/p&gt;

&lt;p&gt;Senate: Kamala Harris; she has almost literally everyone’s endorsement and seems less &lt;a href=&quot;http://www.ocweekly.com/news/loretta-sanchez-dabs-during-us-senate-debate-world-wonders-wtf-7571385&quot;&gt;erratic&lt;/a&gt; than Loretta Sanchez, which is good, because this is a Senate seat in California, which means the incumbent’s going to be there until they feel like leaving.&lt;/p&gt;

&lt;p&gt;US House, 45th District: Ron Varaseth; Mimi Walters is a terrifying right-winger.&lt;/p&gt;

&lt;p&gt;CA Senate, 37th District: I’m voting for Ari Grayson because he’s a Democrat with good things to say about climate change etc and has the left’s endorsements, but I’m doing it with relatively warm feelings about Moorlach; he spends a lot of time beating the fiscal discipline drum and seems genuinely passionate about the work he does. I don’t always agree with Moorlach but I think he’s serving us well!&lt;/p&gt;

&lt;p&gt;CA Assembly, 74th District: I’m voting for Democrat and perennial candidate Karina Onofre because I’ve never heard anything good about Matt Harper.&lt;/p&gt;

&lt;h2 id=&quot;south-orange-county-community-college-district-governing-board-area-3&quot;&gt;South Orange County Community College District, Governing Board, Area 3&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://www.danapointtimes.com/candidates-vie-south-oc-community-college-district-socccd-seats/&quot;&gt;Dana Point Times roundup&lt;/a&gt; of the SOCCCD trustee races&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/dr-peter-espinosa-2476aa1a&quot;&gt;Peter J. Espinosa&lt;/a&gt;; impressive CV. Emeritus professor at Saddleback College and adjunct at CSU Fullerton. Seems to have lots of experience in university and school administration. Has a campaign website and everything. &lt;a href=&quot;http://www.ratemyprofessors.com/ShowRatings.jsp?tid=234704&quot;&gt;Decent ratings&lt;/a&gt; at RateMyProfessors.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.votekimberlyclark.com/about&quot;&gt;Kimberly Clark&lt;/a&gt;; not the paper product conglomerate. “I have served as a school-site administrator and counselor, coordinated programs, and volunteered as a court appointed CASA advocate, education foundation board member, and community volunteer. Currently, I am the director of a 501 C(3) educational non-profit organization.”&lt;/li&gt;
  &lt;li&gt;Gary V. Miller; retired teacher? &lt;a href=&quot;http://www.joincalifornia.com/candidate/3218&quot;&gt;affiliated&lt;/a&gt; with the nutso American Independent Party. Hard pass.&lt;/li&gt;
  &lt;li&gt;Barbara “Bobbe” Jay; appointed incumbent, took her deceased husband’s seat. The &lt;a href=&quot;http://lariatnews.com/news/district-board-of-trustees-look-for-new-member/&quot;&gt;Saddleback College Lariat&lt;/a&gt; has an article about the appointment. &lt;a href=&quot;https://www.socccd.edu/about/about_board_jay.html&quot;&gt;Official bio&lt;/a&gt;. Allegedly &lt;a href=&quot;https://dissenttheblog.blogspot.com/2015_04_26_archive.html&quot;&gt;promised not to run&lt;/a&gt; for re-election.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Between Clark and Espinosa I’m leaning towards Espinosa based on his extensive experience with the community college system.&lt;/p&gt;

&lt;h2 id=&quot;south-orange-county-community-college-district-governing-board-area-6&quot;&gt;South Orange County Community College District, Governing Board, Area 6&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;James “Jim” R. Wright; incumbent. &lt;a href=&quot;https://www.socccd.edu/about/about_board_wright.html&quot;&gt;Official bio&lt;/a&gt;; long-experienced professor, dean, and administrator.&lt;/li&gt;
  &lt;li&gt;Mike Dalati; perpetual candidate, Karina Onofre’s husband. Also running for Irvine Ranch Water District board. No relevant experience?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What the hell is Mike Dalati doing? I’m happy to vote for Wright.&lt;/p&gt;

&lt;h2 id=&quot;south-orange-county-community-college-district-governing-board-area-4&quot;&gt;South Orange County Community College District, Governing Board, Area 4&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Terri Whitt; appointed incumbent. &lt;a href=&quot;https://www.socccd.edu/about/about_board_padberg.html&quot;&gt;Official bio&lt;/a&gt;; promised not to run in 2016. &lt;a href=&quot;http://www.ocregister.com/articles/nursing-164463-site-videos.html&quot;&gt;Award-winning nursing faculty&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;John Alpay; &lt;a href=&quot;https://www.linkedin.com/in/johnalpay&quot;&gt;corporate lawyer&lt;/a&gt; and Capistrano USD board member; OC Political &lt;a href=&quot;https://ocpolitical.com/2013/03/22/republican-democrat-independent-the-partisan-affiliations-of-everyone-holding-office-in-orange-county/#more-7844&quot;&gt;says he’s a Republican&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Jim Leach; &lt;a href=&quot;https://www.linkedin.com/in/jamesmleach&quot;&gt;government relations exec&lt;/a&gt; currently with the Rancho San Margarita water board. Served on the Saddleback College Foundation Board. Apparently &lt;a href=&quot;http://www.ocgop.org/event/event/you-are-invited-to-attend-a-reception-for-jim-leach-for-socccd-trustee/&quot;&gt;associated with OC GOP&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It seems like Whitt and Leach both have relevant experience. I’ll probably vote for Whitt since I think inside experience is good; I’d rather see faculty here than lawyers.&lt;/p&gt;

&lt;h2 id=&quot;irvine-usd-board&quot;&gt;Irvine USD Board&lt;/h2&gt;

&lt;p&gt;Vote for no more than 3 of:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Mark Newgent; had a fun &lt;a href=&quot;https://twitter.com/kumokasumi/status/772217241731411968&quot;&gt;Twitter beef&lt;/a&gt; with him about his awful ISIS-comin’-for-your-kids-gotta-elect-a-warrior messaging. Anyway, don’t vote for him.&lt;/li&gt;
  &lt;li&gt;Naz Hamid; &lt;a href=&quot;https://votefornaz.nationbuilder.com/&quot;&gt;website&lt;/a&gt;, endorsements from Irvine Teachers, OC Democrats, Planned Parenthood, Tom Torlakson, incumbent Bokota&lt;/li&gt;
  &lt;li&gt;Geri Zollinger; &lt;a href=&quot;http://www.gerizollinger.com/&quot;&gt;website&lt;/a&gt;, Harvey Mudd grad, Duke BME MS, lawyer. Weirdly &lt;a href=&quot;https://www.change.org/p/iusd-board-of-education-iusd-let-us-have-a-voice&quot;&gt;mad about curriculum&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Betty Carroll; &lt;a href=&quot;http://www.bettycarrollforschoolboard.com/&quot;&gt;website&lt;/a&gt;, Irvine Public Schools Foundation chair, endorsed by incumbent Brooks, California School Employees Association&lt;/li&gt;
  &lt;li&gt;Paul Bokota; incumbent, Irvine Teachers endorsement&lt;/li&gt;
  &lt;li&gt;Lauren Brooks; incumbent, Irvine Teachers endorsement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The OC Register very briefly &lt;a href=&quot;http://www.ocregister.com/articles/irvine-731680-board-district.html&quot;&gt;covered a candidate’s forum&lt;/a&gt; but it’s pretty redundant with the candidate statements.&lt;/p&gt;

&lt;p&gt;Nobody seems to have a beef with the incumbents. Has controversy ever visited this board? Hamid and Carroll both have convincing stories about classroom participation; Zollinger and Newgent don’t. Hamid seems aligned with Democrats and Carroll seems aligned with Republicans. I’ll vote for Bokota, Brooks, and Hamid.&lt;/p&gt;

&lt;h2 id=&quot;irvine-mayor&quot;&gt;Irvine Mayor&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Don Wagner; former Republican assemblyman. Has a committee open to &lt;a href=&quot;http://www.orangejuiceblog.com/2016/10/irvine-mayoral-candidate-and-developers-darling-don-wagner-has-seriously-filed-to-run-for-attorney-general-in-2018/comment-page-1/&quot;&gt;explore an Attorney General run&lt;/a&gt; in 2018. Doesn’t even have a website? &lt;a href=&quot;http://www.ocregister.com/articles/assemblyman-732223-steven-jobs.html&quot;&gt;OC Register endorsement&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Katherine Daigle; perennial candidate, no website, LinkedIn still says she’s running for Assembly. (Daigle was running for 74th AD as a Republican until she lost the primary.)&lt;/li&gt;
  &lt;li&gt;Gang Chen; some &lt;a href=&quot;http://www.gangchenusa.com/&quot;&gt;skinny nerd&lt;/a&gt;, no government experience, has lots of yard signs. &lt;a href=&quot;http://www.theliberaloc.com/2016/08/25/irvines-gang-chen-files-fppc-complaint-against-theliberaloc-and-irvine-community-news-views/&quot;&gt;Questionable 1st Amendment credentials&lt;/a&gt;. Doing lots of outreach to the Chinese community. Running as a center-right candidate. Wildly attacking Gaido as “too radical” and Wagner as “a career politician”. Did a bunch of &lt;a href=&quot;http://www.ocregister.com/articles/recall-712641-park-great.html&quot;&gt;stupid saber-rattling&lt;/a&gt; about a recall effort in the spring.&lt;/li&gt;
  &lt;li&gt;David Chey; &lt;a href=&quot;http://www.latimes.com/socal/coastline-pilot/opinion/tn-cpt-me-panhandler-hansen-20160714-16-story.html&quot;&gt;oh my god&lt;/a&gt;, how did he even get on the ballot&lt;/li&gt;
  &lt;li&gt;Mary Ann Gaido; &lt;a href=&quot;https://maryanngaido.com/&quot;&gt;website&lt;/a&gt;, council member 1976 - 1984, Democrat. Lost narrowly to Steven Choi in 2014. Really awful logo. Sierra Club endorsement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: The &lt;a href=&quot;https://www.youtube.com/watch?v=F_C_w9yyp_s&quot;&gt;Irvine candidate’s forum&lt;/a&gt; was posted to YouTube.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: Check out &lt;a href=&quot;http://askaleader.com/?page_id=25&quot;&gt;these interviews at Ask A Leader&lt;/a&gt; from KUCI!&lt;/p&gt;

&lt;p&gt;I’m skeptical about all of these candidates. I do not understand the hysteria about growth and traffic that seems to be sweeping Irvine. The traffic’s fine. Growth can be healthy.&lt;/p&gt;

&lt;p&gt;Understand while evaluating candidates that “Irvine Community News &amp;amp; Views” is a fake newspaper run by Larry Agran lieutenants. “Irvine City News” is likewise a threadbare Republican organ. You might, like me, find this deception upsetting.&lt;/p&gt;

&lt;p&gt;The OC Register &lt;a href=&quot;http://www.ocregister.com/articles/city-731904-irvine-wagner.html&quot;&gt;covered a candidates forum at UCI&lt;/a&gt; late last month. I like Daigle’s talk about affordable housing but I don’t think she’s a credible candidate.&lt;/p&gt;

&lt;p&gt;Gaido is the only Democrat. Ironically, lots of dark money is being spent to attack Gaido, accusing her of taking too much money. ¯\_(ツ)_/¯ She touts an “endorsement” from Larry Agran’s fake-ass newspaper on her website, which is unacceptably deceptive. I disagree with Gaido’s stance that voters should approve development projects, but it may not matter with an opposed Council.&lt;/p&gt;

&lt;p&gt;I’ve historically disagreed with Wagner as a conservative in the Assembly. Wagner doesn’t have a website, which is insulting.&lt;/p&gt;

&lt;p&gt;I think Chen, who is campaigning amateurishly but vigorously, is probably drawing votes from both Gaido and Wagner. I expect Daigle and Chey to be non-factors. The OC Reg noted that Gaido has $72k in fundraising, compared to $29k for Chen and $12k for Wagner. Wagner has also been the benefactor of $110k of supporting money and $243k of anti-Gaido money from PACs. Daigle has not solicited funds.&lt;/p&gt;

&lt;p&gt;I’d like to see Chen, Gaido, and Wagner at a forum and I’d like to see them answer questions about Irvine’s role in climate change, transit-oriented development with respect to the Master Plan, and what Irvine police should learn from Black Lives Matter. Municipal power is really important and here we are, awash in banalities about traffic and who’s in whose pockets. So far, I’ve found out about two forums after they happened, with no recordings available. I don’t think that should be happening.&lt;/p&gt;

&lt;p&gt;I’ll probably vote for Gaido unless I learn something surprising.&lt;/p&gt;

&lt;h2 id=&quot;city-of-irvine-council&quot;&gt;City of Irvine Council&lt;/h2&gt;

&lt;p&gt;Vote for no more than 2 of:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Farrah N. Khan; Democratic Foundation endorsement.&lt;/li&gt;
  &lt;li&gt;Anthony Kuo; worked for Christina Shea.&lt;/li&gt;
  &lt;li&gt;Chistina L. Shea; incumbent, Republican endorsement.&lt;/li&gt;
  &lt;li&gt;Courtney Santos&lt;/li&gt;
  &lt;li&gt;Ian Daelucian; on the Gang Chen “slate”&lt;/li&gt;
  &lt;li&gt;Matthew Ehorn; literally some UCI undergrad.&lt;/li&gt;
  &lt;li&gt;Hyunjoung “Genii” Ahn&lt;/li&gt;
  &lt;li&gt;Anila Ali&lt;/li&gt;
  &lt;li&gt;Dale Cheema&lt;/li&gt;
  &lt;li&gt;Melissa Fox; &lt;a href=&quot;https://votemelissafox.com/&quot;&gt;website&lt;/a&gt;, Democratic Foundation endorsement. I’ve spoken to her when she hosted an event in Uni Hills a couple years ago and I was impressed by her knowledge of Irvine’s history and planning. I’m enthusiastic about her opinions about bikes and development. She narrowly missed a seat in the last Council race by just a couple hundred votes. I think she’ll do a great job on the Council.&lt;/li&gt;
  &lt;li&gt;Shiva Farivar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: The &lt;a href=&quot;https://www.youtube.com/watch?v=F_C_w9yyp_s&quot;&gt;Irvine candidate’s forum&lt;/a&gt; was posted to YouTube.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: Check out &lt;a href=&quot;http://askaleader.com/?page_id=25&quot;&gt;these interviews at Ask A Leader&lt;/a&gt; from KUCI!&lt;/p&gt;

&lt;p&gt;I really want to see Melissa Fox on the Council. I think Christina Shea is likely to be re-elected. Therefore, I’m planning to vote only for Fox, to avoid helping any of the other candidates edge her out. I haven’t done any other research for this race. Khan was recently the target of some &lt;a href=&quot;http://www.orangejuiceblog.com/2016/10/illegal-or-worse-and-racist-signs-attacking-two-council-candidates-appear-on-irvine-streets/&quot;&gt;unreasonable, illegal, and anonymous yard signs&lt;/a&gt; and I’d support her on that basis alone.&lt;/p&gt;

&lt;h2 id=&quot;irvine-ranch-water-district-directors&quot;&gt;Irvine Ranch Water District Directors&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://www.irwd.com/about-us/board-of-directors&quot;&gt;Incumbent bios&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vote for no more than 2 of:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;John B. Withers; incumbent. Has an empty campaign Facebook page.&lt;/li&gt;
  &lt;li&gt;Mike Dalati; perennial candidate, also running for South Orange County Community College District governing board, no relevant experience&lt;/li&gt;
  &lt;li&gt;Mary Aileen Matheis; incumbent President. No Internet presence.&lt;/li&gt;
  &lt;li&gt;Margaret Brown; &lt;a href=&quot;http://www.brownforirwd.com/why-im-running.html&quot;&gt;official site&lt;/a&gt; and &lt;a href=&quot;https://www.facebook.com/brownforIRWD&quot;&gt;Facebook page&lt;/a&gt;, currently Director of District Facilities at Garden Grove USD. Running on highlighting board compensation practices. Targeting Matheis. Observing that non-incumbents haven’t been elected to the board since the 70s; typically when directors leave, they’re replaced by an appointee who’s then re-elected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think IRWD is doing a good job of managing our water resources. I’ll vote for Brown and Withers, on the theory that a little bit of qualified new blood is rarely a bad idea, especially when incumbents aren’t putting any effort into their campaigns. Don’t vote for Dalati.&lt;/p&gt;
</description>
        <pubDate>Sat, 15 Oct 2016 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Integrating Google Forms and Slack</title>
        <link>http://blog.tim-smith.us/2016/03/google-forms-slack/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2016/03/google-forms-slack/</guid>
        <description>&lt;p&gt;Getting submissions to a Google Form to appear in a Slack chat is straightforward. You can use services like &lt;a href=&quot;https://zapier.com/zapbook/google-sheets/slack/&quot;&gt;Zapier&lt;/a&gt; to do this, but it turns out to be easy to use Google’s scripting interface to get instant message-pushing without a third-party tool.&lt;/p&gt;

&lt;p&gt;This approach is lifted directly from an &lt;a href=&quot;http://www.obakelabs.com/push-to-slack-channel-on-google-form-submission/&quot;&gt;Obake Labs&lt;/a&gt; blog post, but the code didn’t quite work off the shelf for me.&lt;/p&gt;

&lt;p&gt;To implement:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Set up a Google Form and link it to a spreadsheet.&lt;/li&gt;
  &lt;li&gt;Set up a &lt;a href=&quot;https://slack.com/apps/A0F7YS25R-bots&quot;&gt;Bot User&lt;/a&gt; in Slack and /invite it to the channel you want it to speak in. Make a note of the key.&lt;/li&gt;
  &lt;li&gt;Go to the response spreadsheet and open the Script Editor (Tools -&amp;gt; Script editor).&lt;/li&gt;
  &lt;li&gt;Paste in the following code, replacing the values as necessary:&lt;/li&gt;
&lt;/ol&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SendSlackMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Only include form values that are not blank&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;user_info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;namedValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;namedValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;user_info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;https://slack.com/api/chat.postMessage&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;BOT_TOKEN_HERE&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;as_user&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;New response received:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;user_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#general&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// or whatever&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;followRedirects&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;muteHttpExceptions&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
   
  &lt;span class=&quot;c1&quot;&gt;//Hit the Slack API with the request&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UrlFetchApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
   
  &lt;span class=&quot;c1&quot;&gt;//Check the request went through and log errors if necessary&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getResponseCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getContentText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
   &lt;span class=&quot;nx&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Finally, go to Resources -&amp;gt; Current project’s triggers, and add a trigger to run SendSlackMessage on a form submit response from a spreadsheet. Save your project and you should be in business!&lt;/p&gt;

&lt;p&gt;I’m looking for ways to get responses &lt;em&gt;in&lt;/em&gt; to Google Forms from Slack in a reasonable manner. &lt;a href=&quot;https://github.com/howdyai/botkit&quot;&gt;Botkit&lt;/a&gt;’s approach to conversations is the only thing I’ve come across so far; let me know if you have a better way.&lt;/p&gt;
</description>
        <pubDate>Wed, 09 Mar 2016 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Installing botbot.me on Scaleway</title>
        <link>http://blog.tim-smith.us/2016/03/botbot-scaleway/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2016/03/botbot-scaleway/</guid>
        <description>&lt;p&gt;&lt;a href=&quot;http://botbot.me&quot;&gt;BotBot.me&lt;/a&gt; is a logging IRC bot. &lt;a href=&quot;https://scaleway.com&quot;&gt;Scaleway&lt;/a&gt; provides bare-metal ARM servers for €3/mo. Can we combine them? We can!&lt;/p&gt;

&lt;p&gt;Cribbed from the &lt;a href=&quot;https://botbot.readthedocs.org/en/latest/install.html&quot;&gt;real install instructions&lt;/a&gt;, with extensions as necessary.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Instantiate a new server based on the Ubuntu Wily (15.10) image&lt;/li&gt;
  &lt;li&gt;Install botbot.me dependencies: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get install postgresql-contrib-9.4 postgresql-server-dev-9.4 python-dev virtualenv golang-go redis-server git build-essential&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Install &lt;a href=&quot;https://github.com/nodesource/distributions&quot;&gt;node.js&lt;/a&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl -sL https://deb.nodesource.com/setup_5.x | bash - &amp;amp;&amp;amp; apt-get install -y nodejs&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Create a botbot user with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;adduser botbot --disabled-password&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Create a botbot postgres user with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo -iu postgres createuser -dP botbot&lt;/code&gt; and assign a secure password (consider &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pwgen 24 1&lt;/code&gt;). You &lt;em&gt;do&lt;/em&gt; need to configure password authentication because of how botbot interacts with lib/pg.&lt;/li&gt;
  &lt;li&gt;Switch to the botbot user with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;su botbot&lt;/code&gt;, change to the home directory with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;virtualenv botbot &amp;amp;&amp;amp; source botbot/bin/activate&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install -e git+https://github.com/BotBotMe/botbot-web.git#egg=botbot&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd $VIRTUAL_ENV/src/botbot&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make -j4 dependencies NPM_BIN=/usr/bin/npm&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt;; set SECRET_KEY and WEB_SECRET_KEY to good random strings (consider &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pwgen 24 2&lt;/code&gt;). Set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STORAGE_URL&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STORAGE_URL='postgres://botbot:YOUR_PSQL_PASSWORD@localhost/botbot'&lt;/code&gt;. Set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WEB_PORT='0.0.0.0:8000'&lt;/code&gt; or set up appropriate SSH forwarding. Comment out &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PUSH_STREAM_URL&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Export .env with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export $(cat .env | grep -v ^# | xargs)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;createdb botbot&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;As root: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo &quot;create extension hstore&quot; | sudo -iu postgres psql botbot&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;As botbot again: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manage.py migrate&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manage.py createsuperuser&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;honcho start&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It’s alive! Proceed to the &lt;a href=&quot;https://botbot.readthedocs.org/en/latest/getting_started.html&quot;&gt;setup instructions&lt;/a&gt;. Note that your bot will not connect to Freenode until you reload the configuration in “add a channel” step 7.&lt;/p&gt;

&lt;p&gt;To use a real web server, set up nginx and uwsgi. Install uwsgi into your virtualenv and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get install nginx&lt;/code&gt;. It will fail; this is fine. Edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/nginx/sites-available/default&lt;/code&gt; and comment out &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;listen [::]:80 default_server;&lt;/code&gt;. Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get install -f&lt;/code&gt; to make dpkg happy.&lt;/p&gt;

&lt;p&gt;Add to sites-enabled:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-nginx&quot; data-lang=&quot;nginx&quot;&gt;&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;botbot&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;localhost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;your_website.com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;kn&quot;&gt;charset&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;utf-8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	
	&lt;span class=&quot;kn&quot;&gt;client_max_body_size&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100M&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	
	&lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/static&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;kn&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/home/botbot/botbot/var/static&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	
	&lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;kn&quot;&gt;uwsgi_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;botbot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;kn&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/etc/nginx/uwsgi_params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Edit the Procfile to launch the web job like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uwsgi --socket localhost:8000 --module botbot.wsgi&lt;/code&gt;. Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manage.py collectstatic&lt;/code&gt; to collect the static files. Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;honcho start&lt;/code&gt; again and you should be in business!&lt;/p&gt;

&lt;p&gt;Ideally, this is where you write systemd configuration files to launch the bot at startup… or you could run it in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmux&lt;/code&gt; like me. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#devops&lt;/code&gt; Someone tell me how to do this and I’ll thank you.&lt;/p&gt;
</description>
        <pubDate>Mon, 07 Mar 2016 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Liberating Irvine campaign finance data</title>
        <link>http://blog.tim-smith.us/2015/09/irvine-campaign-finance-data/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2015/09/irvine-campaign-finance-data/</guid>
        <description>&lt;p&gt;Comrades of the Data Liberation Front rejoice! I come to report a successful action against the &lt;a href=&quot;http://nf4.netfile.com/pub2/Default.aspx?aid=COI&quot;&gt;campaign finance records of the City of Irvine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There is a shocking amount of money in Irvine politics. I received, without exaggeration, several pounds of direct mail in the run up to the November 2014 election, and I have very few guesses about who paid for it all. Irvine politicians are required to report donations, loans, and campaign expenditures. Unfortunately and unaccountably, they have not been required to file electronically. Several candidates have, in the past, chosen to file on paper; those filings are &lt;em&gt;not&lt;/em&gt; data-entered by the &lt;a href=&quot;http://www.cityofirvine.org/city-clerk&quot;&gt;City Clerk’s office&lt;/a&gt;, and are posted online &lt;a href=&quot;https://dl.dropboxusercontent.com/u/3720/temp/this_is_barely_disclosure.pdf&quot;&gt;as useless PDF page images&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since the City Clerk didn’t do their job, it’s up to us. I contracted a freelancer to enter the data from the November 2014 election. It took her about 30 hours. &lt;a href=&quot;https://github.com/tdsmith/irvinecampaignfinance/tree/data&quot;&gt;I have posted those records on Github&lt;/a&gt;. I assert that copyright cannot be held on this data and that these records exist in the public domain. I haven’t done any systematic quality control but the spot checks I’ve done look reasonable. Counterintuitively, donor and vendor mailing addresses are redacted (presumably: by hand, laboriously) from the scanned PDFs, but not from the e-file data.&lt;/p&gt;

&lt;p&gt;If you’re passionate about public access and campaign transparency, I’d love to have your help cleaning up and analyzing these records; please get in touch! There’s a lot of work left to do: these should be merged with the e-file data into a single coherent data set, and then it’ll be time to try and generate some analyses of the data. It would be exciting to come up with some newsworthy pitches for one of the woefully under-resourced Orange County news organizations.&lt;/p&gt;
</description>
        <pubDate>Sat, 26 Sep 2015 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Building Python extension modules on OS X with cmake or autotools</title>
        <link>http://blog.tim-smith.us/2015/09/python-extension-modules-os-x/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2015/09/python-extension-modules-os-x/</guid>
        <description>&lt;p&gt;tl;dr: Please use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-undefined dynamic_lookup&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-lpython&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-framework Python&lt;/code&gt; to build Python extension modules on OS X, no matter what &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python-config&lt;/code&gt; says. Only use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-lpython&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-framework Python&lt;/code&gt; if you intend to embed an interpreter.&lt;/p&gt;

&lt;p&gt;When a project with a cmake or autotools-based build system decides to start shipping Python bindings, developers often notice that the default behavior of the Apple and Linux linkers is different. On Linux, building with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-shared&lt;/code&gt; tells the linker to ignore undefined symbols, assuming that the dynamic linker will be able to resolve them at runtime. A consequence is that it’s unnecessary to pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-lpython&lt;/code&gt; to the linker when building extension modules.&lt;/p&gt;

&lt;p&gt;On OS X, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-shared&lt;/code&gt; (or, more correctly, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-bundle&lt;/code&gt;) on its own will still result in the linker demanding that all symbols are resolved at link time. Some developers work around this by adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-lpython&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-framework Python&lt;/code&gt; to their linker invocations. (Sometimes, this is done implicitly by trusting the output of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python-config --ldflags&lt;/code&gt;.) This appears to work but it is not the best solution. The best solution is to provide &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-bundle -undefined dynamic_lookup&lt;/code&gt;, which has the same behavior as the Linux linker.&lt;/p&gt;

&lt;h2 id=&quot;why--lpython-is-wrong&quot;&gt;Why -lpython is wrong&lt;/h2&gt;

&lt;p&gt;The effect of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-lpython&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-framework Python&lt;/code&gt; is to unnecessarily force the extension module to be used with the Python interpreter against which it was built. Building a module against one Python interpreter and then attempting to import it from a Python interpreter installed to a different location will cause the interpreter to segfault even if the build and import interpreters are ABI compatible. This often appears in practice if someone builds bindings with system Python and then later installs a compatible version of Python from another source (Homebrew, Python.org, pyenv) and tries to import the already-built bindings using their new python. It may also manifest if the first python in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$PATH&lt;/code&gt; is not what a user thinks it should be during their build. It is a common source of &lt;a href=&quot;https://github.com/Homebrew/homebrew/search?utf8=%E2%9C%93&amp;amp;q=PyThreadState_Get+OR+%28python+AND+%22Segmentation+fault%22%29&amp;amp;type=Issues&quot;&gt;crash reports&lt;/a&gt; to projects and to Homebrew, often leading to user and developer frustration and hairy workarounds.&lt;/p&gt;

&lt;p&gt;Another consequence is that the compiled module cannot be redistributed in a wheel or bdist – or in Homebrew’s bottles – because those must be portable to different installation locations.&lt;/p&gt;

&lt;p&gt;You can detect if a module is explicitly linked to a Python library by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;otool -L my_module.so&lt;/code&gt;, which will reveal any links to a python library or framework binary. For example, you may see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/System/Library/Frameworks/Python.framework/Versions/2.7/Python&lt;/code&gt; for a module linked explicitly to system Python. If your module is built correctly, you should see no reference to Python at all.&lt;/p&gt;

&lt;h2 id=&quot;what-to-do-instead&quot;&gt;What to do instead&lt;/h2&gt;

&lt;p&gt;If you are building an extension module, pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-undefined dynamic_lookup&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-lpython&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-framework Python&lt;/code&gt;. If you are worried that the linker will stop complaining about other symbols which may be missing, you can optionally use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-bundle_loader&lt;/code&gt; flag, which asks the linker to check that the specified library provides any missing symbols, but does not link to it.&lt;/p&gt;

&lt;p&gt;Note that you &lt;em&gt;do&lt;/em&gt; need to pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-lpython&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-framework Python&lt;/code&gt; when linking something that embeds the Python interpreter, but those things are probably not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import&lt;/code&gt;able.&lt;/p&gt;

&lt;p&gt;You should not trust the output of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python-config --ldflags&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--libs&lt;/code&gt;, which give the right answer for embedding an interpreter, but not for building extension modules. (In fact, the respective intentions of python-config and python’s pkgconfig files &lt;a href=&quot;https://bugs.python.org/issue15590&quot;&gt;are not well-defined&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;Indeed, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-undefined dynamic_lookup&lt;/code&gt; is how Python’s distutils builds extensions (run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install -vU --force-reinstall --no-use-wheel markupsafe&lt;/code&gt; for proof). I’ve helped several projects, including Boost.Python, opencv, pycairo, openimageio, and ledger, change their build systems to match this behavior. Afterwards, these projects and Homebrew see many fewer crash reports from users on OS X.&lt;/p&gt;

&lt;p&gt;If you have any questions about this easy way to safely improve the portability of your bindings on OS X, please reach out. I’m tdsmith in #machomebrew and #pypa on Freenode.&lt;/p&gt;
</description>
        <pubDate>Tue, 01 Sep 2015 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>Homebrew Python 3.5 transition</title>
        <link>http://blog.tim-smith.us/2015/08/python-35-transition/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2015/08/python-35-transition/</guid>
        <description>&lt;p&gt;Python 3.5 is &lt;a href=&quot;https://www.python.org/dev/peps/pep-0478/&quot;&gt;scheduled to be released&lt;/a&gt; on September 13, 2015. Since Homebrew carries the newest stable version of its packages, the Homebrew &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python3&lt;/code&gt; formula will adopt Python 3.5 very quickly upon its release. This post explains how migrating to Python 3.5 will affect Homebrew users who have set up a Python 3 development environment using Homebrew packages.&lt;/p&gt;

&lt;h2 id=&quot;packages-installed-with-pip-will-not-be-found-and-scripts-will-break&quot;&gt;Packages installed with pip will not be found and scripts will break&lt;/h2&gt;

&lt;p&gt;Python packages which were installed against Python 3.4 with pip or easy_install will need to be reinstalled for Python 3.5. Packages installed against Python 3.4 will not be visible from Python 3.5 because Python uses versioned site-packages directories. Scripts (i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ipython3&lt;/code&gt;) which were installed with pip or easy_install using Python 3.4 will stop working until they are reinstalled.&lt;/p&gt;

&lt;p&gt;To reconstitute your Python environment, consider running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip3 freeze &amp;gt; site-requirements.txt&lt;/code&gt; before you upgrade to save a list of your installed packages. You should edit this list by hand to remove any packages which are managed by Homebrew. Then, you can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip3 install -r site-requirements.txt&lt;/code&gt; after you upgrade to reinstall your environment.&lt;/p&gt;

&lt;p&gt;(If you forget to do this before you upgrade, you can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(brew --cellar)/python3/3.4.3_2/bin/python3 -m pip freeze&lt;/code&gt; to generate the list of packages 3.4 knows about, even after 3.5 is installed.)&lt;/p&gt;

&lt;h2 id=&quot;homebrew-packages-built-with---with-python3-will-break&quot;&gt;Homebrew packages built with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--with-python3&lt;/code&gt; will break&lt;/h2&gt;

&lt;p&gt;Homebrew will take care of rebuilding the relatively few Homebrew packages which have mandatory or recommended dependencies on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;python3&quot;&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:python3&lt;/code&gt;. If you are only using these packages, you won’t need to do anything. (The complete list of those packages is: circlator, gubbins, iva, keepassc, lensfun, libsigrokdecode, mypy, pastebinit, ponysay, pulseview, py3cairo, pymummer, pyqt5, retext, and xonsh.)&lt;/p&gt;

&lt;p&gt;Packages which may be &lt;em&gt;optionally&lt;/em&gt; built against python3 by passing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--with-python3&lt;/code&gt; will break and will need to be reinstalled by you. You can get a list of packages which were installed using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--with-python3&lt;/code&gt; using brew and jq like:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew info --json=v1 --installed | jq -r '.[] | if (.installed | map(.used_options | any(contains(&quot;with-python3&quot;))) | any) then .full_name else empty end'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew reinstall&lt;/code&gt; for each of those packages will bring your system up to date after the transition. (You can equivalently pipe the output into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xargs brew reinstall&lt;/code&gt;.)&lt;/p&gt;

&lt;h2 id=&quot;continuing-to-use-python-34&quot;&gt;Continuing to use Python 3.4&lt;/h2&gt;

&lt;p&gt;Life on the bleeding edge isn’t for everyone. If you would prefer to continue using Python 3.4, please consider uninstalling the python3 formula and using &lt;a href=&quot;https://github.com/yyuu/pyenv&quot;&gt;pyenv&lt;/a&gt; instead. You can install pyenv with Homebrew with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install pyenv&lt;/code&gt;. Pyenv is a great tool for managing multiple parallel Python installations.&lt;/p&gt;

&lt;p&gt;I prefer not to accept Python versions into homebrew-versions because of the relatively high maintenance load, but &lt;a href=&quot;https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/How-to-Create-and-Maintain-a-Tap.md&quot;&gt;starting a tap&lt;/a&gt; (Homebrew’s term for an alternative formula repository) is easy to do if you’d like to maintain a python34 formula yourself (or a suite of similar formulas, like Felix Krull’s &lt;a href=&quot;https://launchpad.net/~fkrull/+archive/ubuntu/deadsnakes&quot;&gt;deadsnakes&lt;/a&gt; PPA for Ubuntu). I would owe you a beer. :)&lt;/p&gt;

&lt;h2 id=&quot;migrating-early&quot;&gt;Migrating early&lt;/h2&gt;

&lt;p&gt;If you would like to transition to Python 3.5 before its official release, you can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install python3 --devel&lt;/code&gt; to install the release candidate. After 3.5 final is released, a normal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew update &amp;amp;&amp;amp; brew upgrade&lt;/code&gt; will upgrade you to the release version. Anything that works with a pre-release version of 3.5 should continue working with a release version of 3.5.&lt;/p&gt;

&lt;h2 id=&quot;questions&quot;&gt;Questions&lt;/h2&gt;

&lt;p&gt;If you need help, please feel free to reach out on Homebrew’s Github issue tracker or the mailing list (homebrew@librelist.com). Thanks for your patience!&lt;/p&gt;
</description>
        <pubDate>Mon, 31 Aug 2015 00:00:00 +0000</pubDate>
      </item>
    
      <item>
        <title>SDSC Summer Institute Wrapup</title>
        <link>http://blog.tim-smith.us/2015/08/sdsc-summer-institute-wrapup/</link>
        <guid isPermaLink="true">http://blog.tim-smith.us/2015/08/sdsc-summer-institute-wrapup/</guid>
        <description>&lt;p&gt;&lt;img src=&quot;/img/supercomputer_selfie.jpeg&quot; alt=&quot;Tim and Gordon&quot; style=&quot;float: right&quot; /&gt; What would you do with 2 petaflops of processing power? It’s not an idle question: NSF’s &lt;a href=&quot;https://www.xsede.org/&quot;&gt;XSEDE infrastructure&lt;/a&gt; offers researchers access to large-scale computing resources. I spent the last week at the &lt;a href=&quot;https://www.sdsc.edu&quot;&gt;San Diego Supercomputer Center&lt;/a&gt;, which hosts an annual &lt;a href=&quot;http://www.sdsc.edu/events/summerinstitute/&quot;&gt;Summer Institute&lt;/a&gt; to introduce scientists from many disciplines to high-performance computing technologies. Tissue engineering doesn’t have a lot of petascale problems but I did learn a lot about tools that can help me write more performant code on single and multi-processor systems—even in Python, my favorite programming language. ~And~ I got to take a selfie with a supercomputer.&lt;/p&gt;

&lt;h2 id=&quot;faster-python-code&quot;&gt;Faster Python code&lt;/h2&gt;

&lt;p&gt;We touched on a couple of technologies that can make code faster even on a single core on my laptop. &lt;a href=&quot;http://numba.pydata.org/&quot;&gt;Numba&lt;/a&gt; is a neat library that provides just-in-time compilation of regular ol’ numpy-using Python code using LLVM; all you have to do is label your function with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@jit&lt;/code&gt; decorator. It’s a great way to speed up code that can’t take advantage of numpy’s fast vectorization.&lt;/p&gt;

&lt;p&gt;Here’s a quick demo – note that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slow&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jitted&lt;/code&gt; implementations are exactly the same, except for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@jit&lt;/code&gt; decorator:&lt;/p&gt;

&lt;blockquote&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env ipython
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numba&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jit&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;slow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;xrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;vectorized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jit&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;jitted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;xrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vectorized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jitted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;/blockquote&gt;

&lt;p&gt;Here are the results:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Implementation&lt;/th&gt;
      &lt;th&gt;Time per loop&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;slow&lt;/td&gt;
      &lt;td&gt;1.44 ms&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;vectorized&lt;/td&gt;
      &lt;td&gt;7.79 µs&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;jitted&lt;/td&gt;
      &lt;td&gt;4.83 µs&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The vectorized numpy implementation is 180x faster than the naïve slow implementation, but the jitted version of the naïve implementation is even slightly faster than the numpy version, with no extra work!&lt;/p&gt;

&lt;p&gt;To use numba you need to have llvm and llvmlite installed; on Homebrew, all you have to do is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install homebrew/python/numba&lt;/code&gt;. Continuum Analytics also provides a conda package.&lt;/p&gt;

&lt;h2 id=&quot;gpu-processing&quot;&gt;GPU processing&lt;/h2&gt;

&lt;p&gt;GPUs offer massive parallelism. The AMD Radeon M370X card in my laptop provides 10 compute units with 64 stream processors each and GPU architectures can efficiently handle multiple threads. Cluster GPU nodes are even more powerful. While tools and interfaces like OpenCL and Nvidia’s CUDA have eliminated the need to write GPU code in VHDL, leveraging GPUs fully requires understanding their architecture. In the &lt;a href=&quot;https://github.com/sdsc/sdsc-summer-institute-2015/tree/master/hpc0_gpu_programming&quot;&gt;examples we saw&lt;/a&gt;, GPU kernels each perform a small calculation on a single location in memory; the outer layer of a for loop is essentially parallelized over all of the GPU cores, each pushing a result into a single cell of GPU memory. CUDA exposes parameters for allocating jobs to GPU cores and getting them right can have important effects on performance. Main memory management on the device is also handled by the user.&lt;/p&gt;

&lt;p&gt;Higher-level APIs like OpenACC, which uses OpenMP-like #pragma syntax, promise to make GPU computing more straightforward but vendor and toolchain support are still works in progress. I’m optimistic about &lt;a href=&quot;https://arrayfire.com&quot;&gt;ArrayFire&lt;/a&gt;, which is a high-level API with Python bindings that abstracts over the GPU’s API, so it works with my AMD card on OS X over an OpenCL interface as well as with Nvidia cluster compute nodes.&lt;/p&gt;

&lt;p&gt;A straightforward Arrayfire translation of the same problem looks like:&lt;/p&gt;

&lt;blockquote&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;arrayfire&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;af&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# use my discrete AMD GPU, not the integrated Isis GPU
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;af&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;af_vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;af&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;af&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;500000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;af_vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;/blockquote&gt;

&lt;p&gt;Running on the GPU is slower than the numba version for n &amp;lt; about 500,000, but is faster after that. I’ll look forward to learning which problems can be solved faster on a GPU and how to implement them optimally.&lt;/p&gt;

&lt;h2 id=&quot;scaling-up-scaling-out&quot;&gt;Scaling up, scaling out&lt;/h2&gt;

&lt;p&gt;I attended a session that touched on OpenMP and MPI, which I’ve always gotten confused before. OpenMP is a classic solution for multi-threaded &lt;strong&gt;shared-memory&lt;/strong&gt; computation on a single node. MPI lets you run multiple &lt;strong&gt;independent processes&lt;/strong&gt; with independent memory spaces on one to &lt;em&gt;n&lt;/em&gt; nodes, which can communicate and synchronize with each other through the MPI API. They are often used together. (For the record: OpenMPI is a particular MPI implementation, and has nothing to do with OpenMP!) I don’t think either is right for my purposes right now: they’re powerful and widely deployed but they work most naturally in lower-level languages like C and Fortran, and it’s easy to get burned without a deep appreciation of the challenges and idioms of multiprocessor programming.&lt;/p&gt;

&lt;p&gt;While IPython is maybe best known for its notebook view, it also delivers &lt;a href=&quot;http://ipython.org/ipython-doc/dev/parallel/parallel_intro.html&quot;&gt;a powerful set of tools for parallel processing&lt;/a&gt;, expanding well beyond the functionality of the python &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multiprocessing&lt;/code&gt; module. IPython has built-in support for spinning up cluster jobs on queue managers like Grid Engine as well as over AWS EC2. I think using IPython’s parallel tools will be my first foray into real cluster computing since it provides the infrastructure for moving the single-node multiprocessing I’ve been doing across a HPC cluster in a straightforward way.&lt;/p&gt;

&lt;p&gt;Apache Spark is an interesting tool for friendly parallel computing over large data sets. I was worried about Spark because it’s part of the Hadoop ecosystem and my experiences with Hadoop weren’t very pleasant but Spark is much easier to use. Spark leverages the Hadoop distributed file system to enable fast, multi-node access to big data and implements a terrifyingly smart abstraction layer over Hadoop’s map/reduce architecture to make problem-solving more straightforward and performant, including a suite of ML algorithms. I’ll definitely turn to Spark next time I want to deal with machine learning at scale. &lt;a href=&quot;https://twitter.com/andreazonca&quot;&gt;Andrea Zonca&lt;/a&gt; gave a great talk; his &lt;a href=&quot;https://github.com/sdsc/sdsc-summer-institute-2015/raw/master/bigdata1_spark/SDSC-SI-Spark.pdf&quot;&gt;slide deck&lt;/a&gt; is worth a perusal if you’re even slightly curious.&lt;/p&gt;

&lt;p&gt;I should also plug &lt;a href=&quot;https://www.gnu.org/software/parallel/&quot;&gt;GNU Parallel&lt;/a&gt;, which is basically a multiprocessing xargs; it’s the easiest way to spin up a job queue of embarrassingly parallel tasks on a single node.&lt;/p&gt;

&lt;h2 id=&quot;resources-at-sdsc-and-uci&quot;&gt;Resources at SDSC and UCI&lt;/h2&gt;

&lt;p&gt;SDSC hosts &lt;a href=&quot;https://www.sdsc.edu/services/hpc/hpc_systems.html&quot;&gt;two XSEDE facilities&lt;/a&gt;, both of which are architecturally interesting. Comet embodies the “computing for the 99%” strategy; while it provides 2 Pflop/s over nearly 48,000 computing cores, Comet is optimized for jobs that run on 72 or fewer nodes (1,728 or fewer cores) simultaneously. While some models can only be executed by scaling out to a huge number of nodes, most jobs are much smaller; &lt;a href=&quot;https://en.wikipedia.org/wiki/Amdahl%27s_law&quot;&gt;Amdahl’s law&lt;/a&gt; suggests that many practical problems encounter sharply decreasing returns beyond a certain degree of parallelism. Scheduling an ensemble of these “mid-sized” jobs can be a very effective way to compute. Comet also hosts 36 GPU nodes, each of which boasts 4 Nvidia compute GPUs.&lt;/p&gt;

&lt;p&gt;Gordon, the other facility, provides 341 Tflop/s and vast amounts of on-node fast flash storage for data-intensive processing.&lt;/p&gt;

&lt;p&gt;Comet, Gordon, and other XSEDE facilities are available through a (somewhat) competitive allocation process, but small grants for exploration and collecting scalability data for competitive grants are available very quickly.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://hpc.oit.uci.edu/&quot;&gt;UC Irvine’s facilities&lt;/a&gt;, where I will actually be working, are modest in comparison but still very powerful. Irvine has a small dedicated HPC cluster available to the campus, with 576 cores in 9 nodes available for general use, and another 5,952 cores in 93 condo nodes which are, generously, &lt;a href=&quot;http://hpc.oit.uci.edu/free-queue&quot;&gt;available to the community for free&lt;/a&gt; when they aren’t being used by their owners.&lt;/p&gt;

&lt;p&gt;The Summer Institute was a great opportunity to learn from world-class HPC experts and get my hands dirty with some multiprocessing code. If you’re curious about anything I touched on, the &lt;a href=&quot;https://github.com/sdsc/sdsc-summer-institute-2015&quot;&gt;slide decks and example code&lt;/a&gt; from this year’s session are available on Github.&lt;/p&gt;

&lt;iframe width=&quot;400&quot; height=&quot;80&quot; src=&quot;https://rd.io/i/QVlL5DdBwXM/&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
</description>
        <pubDate>Sat, 15 Aug 2015 00:00:00 +0000</pubDate>
      </item>
    
  </channel>
</rss>
