<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Terry Jones</title>
	<atom:link href="http://blogs.fluidinfo.com/terry/feed/" rel="self" type="application/rss+xml" />
	<link>http://blogs.fluidinfo.com/terry</link>
	<description>Random thoughts on tech, books, programming, etc.</description>
	<lastBuildDate>Thu, 07 Mar 2013 23:44:24 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Daylight robbery: Barclays skims €170 off a 5K EUR -&gt; GBP transfer</title>
		<link>http://blogs.fluidinfo.com/terry/2013/02/05/daylight-robbery-barclays-skims-e170-off-a-5k-eur-gbp-transfer/</link>
		<comments>http://blogs.fluidinfo.com/terry/2013/02/05/daylight-robbery-barclays-skims-e170-off-a-5k-eur-gbp-transfer/#comments</comments>
		<pubDate>Tue, 05 Feb 2013 12:23:24 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[companies]]></category>
		<category><![CDATA[me]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1126</guid>
		<description><![CDATA[Last month (on Jan 18, 2013) someone I&#8217;m doing some work for initiated a transfer of €5,000 into my UK bank account. According to xe.com the mid-market rate that day was 1.1937940679 euros per pound. So you might innocently expect to receive about 5,000 / 1.1937940679 = £4,188 minus any transfer fees. The transfer went [...]]]></description>
				<content:encoded><![CDATA[<p>Last month (on Jan 18, 2013) someone I&#8217;m doing some work for initiated a transfer of €5,000 into my UK bank account. <a href="http://www.xe.com/currencytables/?from=EUR&#038;date=2013-01-18">According to xe.com</a> the mid-market rate that day was 1.1937940679 euros per pound.</p>
<p>So you might innocently expect to receive about 5,000 / 1.1937940679 = £4,188 minus any transfer fees.</p>
<p>The transfer went through an intermediate bank, who charged €17. Barclays charged &#8220;our commission&#8221; of a mere £6.</p>
<p>But the amount that arrived in my bank was not roughly £4,188 &#8211; £20 = £4,168 as you might hope.</p>
<p>The amount that arrived was £4017.74.</p>
<p>The friendly banks decided that the appropriate exchange rate for me that day was 1.23840, which is a full 4.5% higher than the mid-market 1.19379 rate. That&#8217;s £143 (€170).</p>
<p>Sure, I know there&#8217;s a buy/ask spread in currency and the mid-market rate isn&#8217;t what you&#8217;d get in any transaction. But taking £143 <em>from your own customer</em> just because you can is pretty fucking nasty. And so, via today&#8217;s arbitrary setting of the greed parameter in a bank computer, the voracious banking industry gobbles up just a little bit more of the money made by regular people. People who actually worked to earn that money.</p>
<p>It&#8217;s no wonder people hate their banks and that the financial system in general is so despised.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2013/02/05/daylight-robbery-barclays-skims-e170-off-a-5k-eur-gbp-transfer/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Secure per-site passwords with no encrypted blob</title>
		<link>http://blogs.fluidinfo.com/terry/2013/02/03/secure-per-site-passwords-with-no-encrypted-blob/</link>
		<comments>http://blogs.fluidinfo.com/terry/2013/02/03/secure-per-site-passwords-with-no-encrypted-blob/#comments</comments>
		<pubDate>Sun, 03 Feb 2013 15:45:55 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[tech]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1101</guid>
		<description><![CDATA[Last night I read a Guardian article, Online passwords: keep it complicated. It&#8217;s a surprisingly good summary, given that it&#8217;s aimed at the general public. The author concludes by telling us he decided to adopt LastPass, and also mentions 1Password. The comments section on the page gives similar solutions, like KeePass. The security-conscious people I [...]]]></description>
				<content:encoded><![CDATA[<p>Last night I read a Guardian article, <a href="http://www.guardian.co.uk/technology/2012/oct/05/online-security-passwords-tricks-hacking" title="Online passwords: keep it complicated">Online passwords: keep it complicated</a>. It&#8217;s a surprisingly good summary, given that it&#8217;s aimed at the general public. The author concludes by telling us he decided to adopt <a href="https://lastpass.com/" title="LastPass">LastPass</a>, and also mentions <a href="https://agilebits.com/onepassword" title="1Password">1Password</a>. The comments section on the page gives similar solutions, like <a href="http://keepass.info/" title="KeePass">KeePass</a>. The security-conscious people I know have arrived at the same conclusion. There are plenty of articles on the web that summarize various similar products, e.g., <a href="http://lifehacker.com/5944969/which-password-manager-is-the-most-secure" title="Which Password Manager Is The Most Secure?">Which Password Manager Is The Most Secure?</a> at LifeHacker. A single encrypted blob offers good security and works well in practice. It also allows the storage of information like name, address, credit cards, etc., that can be used to auto-fill web forms.</p>
<p>But&#8230; I&#8217;ve never liked the idea of a single encrypted file with all my passwords in it. What if the storage is lost or corrupted? Could the file someday be decrypted by someone else? If my encrypted blob is online, what happens when I am offline? If the blob is to be stored locally, I need to think about where to put it, how to back it up, etc. If a company holds it for me, what happens if they go out of business or get hacked? What if they use proprietary encryption, a closed-source access app, or a proprietary underlying data format? Not all the above solutions have all these issues, but they all have some of them.</p>
<p>The crucial thing they all have in common is that they use a master password to encrypt all your passwords into a single blob, and the blob has to then be reliably stored and accessible forever.</p>
<h3>An approach that requires no storage</h3>
<p>I realized there&#8217;s a solution that doesn&#8217;t require any storage. It&#8217;s not perfect, but it has some attractive properties. I&#8217;ve already started using it for some sites.</p>
<p>[Edit: it has been pointed out in the comments that the following solution has been thought of before. See <a href="http://supergenpass.com/" title="SuperGenPass">SuperGenPass</a>.]</p>
<p>Here&#8217;s a simple first version of Python code to print my password for a given service:</p>
<p><code>
<div class="dean_ch" style="white-space: nowrap;">
<span class="kw1">import</span> <span class="kw3">base64</span>, <span class="kw3">getpass</span>, hashlib, <span class="kw3">sys</span></p>
<p>service = <span class="kw3">sys</span>.<span class="me1">argv</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><br />
secret = <span class="kw3">getpass</span>.<span class="kw3">getpass</span><span class="br0">&#40;</span><span class="st0">'Enter your master password: '</span><span class="br0">&#41;</span><br />
password = <span class="kw3">base64</span>.<span class="me1">b64encode</span><span class="br0">&#40;</span>hashlib.<span class="me1">sha512</span><span class="br0">&#40;</span>secret + service<span class="br0">&#41;</span>.<span class="me1">digest</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#91;</span>:<span class="nu0">32</span><span class="br0">&#93;</span></p>
<p><span class="kw1">print</span> <span class="st0">'Your password on %s is %s'</span> % <span class="br0">&#40;</span>service, password<span class="br0">&#41;</span></div>
<p></code></p>
<p>The service name is given on the command line. The code prints a 32-character password for use on that service. Here&#8217;s some sample output:</p>
<pre>
        $ mypass.py facebook
        Enter your master password: 
        Your password on facebook is Wza2l5Tqy0omgWP+5DDsXjQLO/Mc07N8

        $ mypass.py twitter
        Enter your master password: 
        Your password on twitter is eVhhpjJrmtSa8XnNMu6vLSDhPeO5nFOT
</pre>
<p>This has some nice advantages. It also places some small requirements on the user. Unfortunately, however, it is not generally applicable &#8211; at least not today.  These are discussed below.</p>
<h3>Advantages</h3>
<p>The obvious advantage is that <em>there is no external storage</em>. Your passwords are not stored anywhere. There&#8217;s no blob to store, protect, access, backup, worry about, etc.  The algorithm used to generate your password is dead-simple, it&#8217;s open and available in dozens of languages, and it&#8217;s secure.</p>
<p>You&#8217;re free to use more than one master password if you like. You can invent your own services names (more on which below).</p>
<h3>Requirements / User burden</h3>
<p>As with all one-key-to-unlock-them-all approaches, the user obviously needs to remember their master password.</p>
<p>With this approach though, the user also has to remember the name they used for the service they&#8217;re trying to access. If you create a password for a service called &#8220;gmail&#8221; you&#8217;ll need to use that exact service name in the future. For me that&#8217;s not much of a burden, but I guess it would be for others.</p>
<p>There&#8217;s no reason why the list of services you have passwords for couldn&#8217;t be stored locally. If the password generator were in a browser extension, it could possibly suggest a service name, like &#8220;facebook.com&#8221;, based on the domain of the page you were on.</p>
<p>With this approach, it&#8217;s even more important that one&#8217;s master password be hard to guess. Unlike the single-encrypted-blob approach, anyone who can guess your master password (and the names you use for services) can immediately obtain your passwords. They don&#8217;t also need access to the blob &#8211; it doesn&#8217;t exist.</p>
<p>Additional security can be easily had by, for example, using a convention of adding a constant character to your service names. So, e.g., you could use &#8220;facebook*&#8221; and &#8220;twitter*&#8221; as service names, and not tell anyone how you form service names.</p>
<h3>General applicability</h3>
<p>Unfortunately, there is a major problem with this approach. That&#8217;s because different sites have different requirements on passwords. Some of the difficulties can be avoided quite easily, but there&#8217;s an additional problem, caused when services <em>change</em> their password policy.</p>
<p>The above code generates a <a href="http://tools.ietf.org/html/rfc3548.html" title="Base64">Base64</a> password string. So, to give some examples, if the service you want a password for doesn&#8217;t allow a plus sign in your password, the above code might make something unacceptable to the service. Same thing if they insist that passwords must be at most 12 characters long.</p>
<p>Ironically, these services are insisting on policies that prevent the use of truly secure passwords. They&#8217;re usually in place to ensure that short passwords are chosen from a bigger space. It would be better, though more work, to impose restrictions only on short passwords.</p>
<p>In a perfect world, all sites could immediately switch to allowing Base64 passwords of length &ge; 16 (say). Then the above approach would work everywhere and we&#8217;d be done.</p>
<h3>Varying password length</h3>
<p>A general approach to adjusting the generated password is to take some of the Base64 information produced and use it to modify the password. For example, you might not comfortable with all your passwords being the same length, so we can compute a length like this:</p>
<p><code>
<div class="dean_ch" style="white-space: nowrap;">
<span class="kw1">import</span> <span class="kw3">base64</span>, <span class="kw3">getpass</span>, hashlib, <span class="kw3">string</span>, <span class="kw3">sys</span></p>
<p>b64letters = <span class="kw3">string</span>.<span class="me1">ascii_letters</span> + <span class="st0">'0123456789+/'</span><br />
secret = <span class="kw3">getpass</span>.<span class="kw3">getpass</span><span class="br0">&#40;</span><span class="st0">'Enter your master password: '</span><span class="br0">&#41;</span><br />
password = <span class="kw3">base64</span>.<span class="me1">b64encode</span><span class="br0">&#40;</span>hashlib.<span class="me1">sha512</span><span class="br0">&#40;</span>secret + service<span class="br0">&#41;</span>.<span class="me1">digest</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
lenAdjust = b64letters.<span class="me1">find</span><span class="br0">&#40;</span>password<span class="br0">&#91;</span><span class="nu0">-5</span><span class="br0">&#93;</span><span class="br0">&#41;</span> % <span class="nu0">16</span><br />
<span class="kw1">print</span> <span class="st0">'Your password on %s is %s'</span> % <span class="br0">&#40;</span>service, password<span class="br0">&#91;</span><span class="nu0">0</span>:<span class="nu0">16</span> + lenAdjust<span class="br0">&#93;</span><span class="br0">&#41;</span></div>
<p></code></p>
<p>This generates passwords that are between 16 and 31 characters in length:</p>
<pre>
        $ ./length-varying.py facebook
        Enter your master password: 
        Your password on facebook is 1nTlVGPhuWZf0l9Sk27
        $ ./length-varying.py twitter
        Enter your master password: 
        Your password on twitter is WE1DVZHAFBx2c3g63tR+Oi3Jxs4xMV
</pre>
<h3>Satisfying site requirements</h3>
<p>A possible approach to dealing with per-site password requirements is to have the code look up known services and adjust the initial password it generates to be acceptable. This can easily be done in a secure, random, repeatable way. For example:</p>
<ul>
<li>If a site doesn&#8217;t allow upper case, lowercase the password.</li>
<li>If a site doesn&#8217;t allow digits, replace them with random letters.</li>
<li>If a site requires punctuation, you can replace some initial letters in the password with randomly chosen punctuation and then <a href="http://en.wikipedia.org/wiki/Random_permutation" title="Random permutation">randomly permute</a> the result using the Knuth Shuffle.</li>
</ul>
<p>Some of these transformations use random numbers. These are easy to obtain: take an unused part of the Base64 string and use it to seed a RNG. For each transformation, you would need to call the RNG a fixed number of times, i.e., independent of the number of random numbers actually used to perform the transformation. That&#8217;s necessary in order to keep the RNG in a known state for subsequent transformations (if any).</p>
<p>For example, the following replaces digits 0-9 with a letter from A-J whose case is chosen randomly:</p>
<p><code></p>
<div class="dean_ch" style="white-space: nowrap;">
<span class="kw1">import</span> <span class="kw3">base64</span>, <span class="kw3">getpass</span>, hashlib, <span class="kw3">random</span>, <span class="kw3">string</span>, <span class="kw3">sys</span></p>
<p><span class="kw1">def</span> getSeed<span class="br0">&#40;</span>chars<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; seed = <span class="kw2">ord</span><span class="br0">&#40;</span>chars<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">for</span> letter <span class="kw1">in</span> chars<span class="br0">&#91;</span><span class="nu0">1</span>:<span class="br0">&#93;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; seed = <span class="br0">&#40;</span>seed &lt;&lt; <span class="nu0">8</span><span class="br0">&#41;</span> &amp; <span class="kw2">ord</span><span class="br0">&#40;</span>letter<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> seed</p>
<p>service = <span class="kw3">sys</span>.<span class="me1">argv</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><br />
b64letters = <span class="kw3">string</span>.<span class="me1">ascii_letters</span> + <span class="st0">'0123456789+/'</span><br />
secret = <span class="kw3">getpass</span>.<span class="kw3">getpass</span><span class="br0">&#40;</span><span class="st0">'Enter your master password: '</span><span class="br0">&#41;</span><br />
digest = <span class="kw3">base64</span>.<span class="me1">b64encode</span><span class="br0">&#40;</span>hashlib.<span class="me1">sha512</span><span class="br0">&#40;</span>secret + service<span class="br0">&#41;</span>.<span class="me1">digest</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
lenAdjust = b64letters.<span class="me1">find</span><span class="br0">&#40;</span>digest<span class="br0">&#91;</span><span class="nu0">-5</span><span class="br0">&#93;</span><span class="br0">&#41;</span> % <span class="nu0">16</span></p>
<p>passwordWithDigits = digest<span class="br0">&#91;</span><span class="nu0">0</span>:<span class="nu0">16</span> + lenAdjust<span class="br0">&#93;</span><br />
password = <span class="st0">''</span></p>
<p><span class="kw3">random</span>.<span class="me1">seed</span><span class="br0">&#40;</span>getSeed<span class="br0">&#40;</span>digest<span class="br0">&#91;</span><span class="nu0">32</span>:<span class="nu0">36</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
randoms = <span class="br0">&#91;</span><span class="kw3">random</span>.<span class="me1">randint</span><span class="br0">&#40;</span><span class="nu0">0</span>, <span class="nu0">1</span><span class="br0">&#41;</span> <span class="kw1">for</span> _ <span class="kw1">in</span> passwordWithDigits<span class="br0">&#93;</span></p>
<p><span class="kw1">for</span> index, letter <span class="kw1">in</span> <span class="kw2">enumerate</span><span class="br0">&#40;</span>passwordWithDigits<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; <span class="kw1">if</span> letter <span class="kw1">in</span> <span class="st0">'0123456789'</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; base = <span class="kw2">ord</span><span class="br0">&#40;</span><span class="st0">'a'</span> <span class="kw1">if</span> randoms<span class="br0">&#91;</span>index<span class="br0">&#93;</span> <span class="kw1">else</span> <span class="st0">'A'</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; replacement = <span class="kw2">chr</span><span class="br0">&#40;</span>base + <span class="kw2">ord</span><span class="br0">&#40;</span>letter<span class="br0">&#41;</span> - <span class="kw2">ord</span><span class="br0">&#40;</span><span class="st0">'0'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; password += replacement<br />
&nbsp; &nbsp; <span class="kw1">else</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; password += letter</p>
<p><span class="kw1">print</span> <span class="st0">'Your password on %s is %s'</span> % <span class="br0">&#40;</span>service, password<span class="br0">&#41;</span></div>
<p></code></p>
<p>Here&#8217;s the output for the above two services (using the same master password):</p>
<pre>
        $ ./no-digits.py facebook
        Enter your master password: 
        Your password on facebook is bnTlVGPhuWZfAljSkch
        $ ./no-digits.py twitter
        Enter your master password: 
        Your password on twitter is WEBDVZHAFBxccdgGdtR+OidJxsExMV
</pre>
<p>As you can see, the digits are replaced with letters (in a biased way, that we can ignore in an informal blog post). The RNG is in a known state because the number of times it has been called is independent of the number of digits in the pre-transformation text.</p>
<p>This approach can be used to transform the initial random sequence into one that satisfies a service&#8217;s password restrictions.</p>
<p>It is difficult to reliably associate service names with site policies. To do so might require keeping a file mapping the name a user used for a service to the policy of the site.  Although this doesn&#8217;t defeat the purpose of this approach (since that file would not need to be stored securely), it is an additional and unwanted pain for the user.  Part of the point was to try to entirely avoid additional storage, even if it doesn&#8217;t have to be encrypted.</p>
<h3>The major problem with per-site requirements</h3>
<p>The major problem however is that sites may <em>change</em> their password policy. Even if our program knew the rules for all sites, it would have a real problem if a site changed its policy. The code would need to be updated to generate passwords according to the new site policy. Existing users, supposing they upgraded, would then be shown incorrect passwords and would need to do password resets, which is obviously inconvenient.</p>
<h3>Conclusion</h3>
<p>I like the above approach a lot, but don&#8217;t see a way to solve the issue with changing site policies. I wouldn&#8217;t mind building in some rules for known popular sites, but any step in that direction has its problems &#8211; at least as far as I can see.</p>
<p>For now, I&#8217;m going to start using the above approach on sites that allow a long random password with characters from the Base64 set. That covers the majority of sites I use. Importantly, that includes Google, so if I ever need password resets I can have them sent there, knowing that I can always log in to recover them.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2013/02/03/secure-per-site-passwords-with-no-encrypted-blob/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>CEL, a Chrome Event Logger</title>
		<link>http://blogs.fluidinfo.com/terry/2013/01/27/cel-a-chrome-event-logger/</link>
		<comments>http://blogs.fluidinfo.com/terry/2013/01/27/cel-a-chrome-event-logger/#comments</comments>
		<pubDate>Sun, 27 Jan 2013 15:50:19 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[browser extensions]]></category>
		<category><![CDATA[tech]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1093</guid>
		<description><![CDATA[Last night I wrote CEL, a Chrome Event Logger, a Google Chrome extension that logs all known chrome.* API events to the Javascript console. Example use cases are: You wonder if there is a Chrome API event that&#8217;s triggered for some action you take in the browser. Rather than guessing what the event might be [...]]]></description>
				<content:encoded><![CDATA[<p><a href="http://bit.ly/chrome-event-logger"><img src="http://blogs.fluidinfo.com/terry/wp-content/uploads/2013/01/logo-128.png" alt="logo-128" width="128" height="128" class="alignright size-full wp-image-1094" /></a>Last night I wrote CEL, a Chrome Event Logger, a Google Chrome extension that logs all known chrome.* API events to the Javascript console.  Example use cases are:</p>
<ul>
<li>You wonder if there is a Chrome API event that&#8217;s triggered for some action you take in the browser. Rather than guessing what the event might be and trying to find it in the API docs, you can enable CEL, perform the action in Chrome, and see what CEL logs.</li>
<li>You&#8217;re writing an extension and are unsure about whether an event is being triggered or with what arguments. Instead of adding an event listener in your own code and reloading your extension, you can just look in the CEL log.</li>
</ul>
<p><a href="http://bit.ly/chrome-event-logger">Click here to install.</a></p>
<p><strong>Viewing the logging</strong></p>
<p>Once installed, you can examine the CEL logging by visiting <a href="chrome://extensions">chrome://extensions</a>, clicking to enable Developer mode, and then clicking the link next to the CEL icon where it says Inspect views: _generated_background_page.html</p>
<p><strong>Manually adjusting logging</strong></p>
<p>In the JS console for the extension&#8217;s background page, there are several commands you can run to adjust what is logged:</p>
<div class="dean_ch" style="white-space: nowrap;">
<span class="co1">// Return a list of the chrome.* API events being logged. </span><br />
CEL.<span class="me1">enabled</span><span class="br0">&#40;</span><span class="br0">&#41;</span></p>
<p><span class="co1">// Return a list of the chrome.* API events being ignored.</span><br />
CEL.<span class="me1">disabled</span><span class="br0">&#40;</span><span class="br0">&#41;</span></p>
<p><span class="co1">// Enable logging of some calls (see below).</span><br />
CEL.<span class="me1">enable</span><span class="br0">&#40;</span>name1, name2, &#8230;<span class="br0">&#41;</span></p>
<p><span class="co1">// Disable logging of some calls (see below).</span><br />
CEL.<span class="me1">disable</span><span class="br0">&#40;</span>name1, name2, &#8230;<span class="br0">&#41;</span><br />
&nbsp;</div>
<p>The names you pass to CEL.enable and CEL.disable can be individual API calls (without the leading &#8220;chrome.&#8221;), or can be higher-level categories.  Here are some examples:</p>
<div class="dean_ch" style="white-space: nowrap;">
<span class="co1">// Enable chrome.tabs.onCreated and all chrome.webRequest.* events:</span><br />
CEL.<span class="me1">enable</span><span class="br0">&#40;</span><span class="st0">&#8216;tabs.onCreated&#8217;</span>, <span class="st0">&#8216;webRequest&#8217;</span><span class="br0">&#41;</span></p>
<p><span class="co1">// Disable all chrome.tabs.* events and chrome.webNavigation.onCommitted</span><br />
CEL.<span class="me1">disable</span><span class="br0">&#40;</span><span class="st0">&#8216;tabs&#8217;</span>, <span class="st0">&#8216;webNavigation.onCommitted&#8217;</span><span class="br0">&#41;</span><br />
&nbsp;</div>
<p>Note that <code>CEL.enable</code> will enable all necessary higher level logging. So, for example, if you call <code>CEL.enable('omnibox.onInputEntered')</code> all <code>chrome.omnibox.*</code> events (that have not been explicitly disabled) will be logged.  If you don&#8217;t want to enable and disable groups of calls in this way, always pass explicit API calls.</p>
<p><code>CEL.disabled</code> will show you the names of individual calls that are disabled, as well as any disabled higher levels.</p>
<p><strong>Global enable / disable</strong></p>
<p>The extension provides a context menu item that lets you globally enable or disable logging.</p>
<p><strong>Installation from the Chrome web store</strong></p>
<p>Go to <a href="http://bit.ly/chrome-event-logger">http://bit.ly/chrome-event-logger</a> which will redirect you to the CEL page in the CWS.</p>
<p><strong>Tracking the development version</strong></p>
<p>If you want the development version, you can install the extension by visting <a href="https://fluiddb.fluidinfo.com/about/chrome-event-logger/fluidinfo.com/chrome.crx">https://fluiddb.fluidinfo.com/about/chrome-event-logger/fluidinfo.com/chrome.crx</a>. Chrome will warn you that extensions cannot be installed from non-Chrome Web Store URLs but will download the .crx file in any case.  Open <a href="chrome://extensions">chrome://extensions</a> and drag the .crx file you just downloaded onto that page.</p>
<p><strong>Installation from source</strong></p>
<p>The source to the extension <a href="http://github.com/terrycojones/chrome-event-logger">is available on Github</a>. Here&#8217;s how to clone and install it.</p>
<ul>
<li>Download the repo: git clone http://github.com/terrycojones/chrome-event-logger</li>
<li>In Chrome, go to <a href="chrome://extensions">chrome://extensions</a></li>
<li>Click Developer mode</li>
<li>Click Load Unpacked Extension&#8230;</li>
<li>Navigate to the directory where you cloned the repo and click Open</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2013/01/27/cel-a-chrome-event-logger/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A chrome extension for examining tab events and ids</title>
		<link>http://blogs.fluidinfo.com/terry/2012/12/19/a-chrome-extension-for-examining-tab-events-and-ids/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/12/19/a-chrome-extension-for-examining-tab-events-and-ids/#comments</comments>
		<pubDate>Wed, 19 Dec 2012 16:31:25 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[browser extensions]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1088</guid>
		<description><![CDATA[Yesterday I was on a call with a friend who told me that when he enters a URL into an existing Chrome tab, the tab id changes. He asked if I&#8217;d ever seen that happening, and I said no. I told him his code was probably to blame :-) Anyway, I wrote a quick Chrome [...]]]></description>
				<content:encoded><![CDATA[<p>Yesterday I was on a call with a friend who told me that when he enters a URL into an existing Chrome tab, the tab id changes. He asked if I&#8217;d ever seen that happening, and I said no. I told him his code was probably to blame :-)</p>
<p>Anyway, I wrote a quick Chrome extension, called <a href="https://github.com/terrycojones/tabsanity">Tabsanity</a>, to log <a href="https://developer.chrome.com/extensions/tabs.html">all 8 tab events</a> with the tab ids, as well as to run a simple sanity check on tab ids after every tab event.</p>
<p>All the action is in the Javascript console for the background page.</p>
<p>To see if you&#8217;ve got the issue my friend has, open a tab and go to <a href="http://en.wikipedia.org/wiki/Virtual_private_network">http://en.wikipedia.org/wiki/Virtual_private_network</a>. In the JS console you&#8217;ll see the tab id. Now go to the URL location bar, enter nytimes.com, and go to that URL. If Chrome is behaving properly for you, the tab id involved wont change. If you have the issue, the console log will show you that Chrome (quickly) removes the existing tab, creates a new one, and loads the nytimes page &#8211; resulting in a different tab id. We were both running Chrome 23.0.1271.101 on a MacBook Air. The same behavior happens in Incognito Mode with all other extensions disabled, and regular mode.</p>
<p>You can <a href="https://fluiddb.fluidinfo.com/about/tabsanity/fluidinfo.com/chrome.crx">install from this link</a> or <a href="https://github.com/terrycojones/tabsanity">get the source on Github</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/12/19/a-chrome-extension-for-examining-tab-events-and-ids/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Omit needless parens</title>
		<link>http://blogs.fluidinfo.com/terry/2012/12/18/omit-needless-parens/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/12/18/omit-needless-parens/#comments</comments>
		<pubDate>Tue, 18 Dec 2012 14:41:19 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1074</guid>
		<description><![CDATA[The famous 17th commandment in The Elements of Style is &#8220;Omit needless words&#8221;. There should be an equivalent in programming, but for parentheses. Every time I see needless parens in a program I want to rip them out (unless they&#8217;re obviously there for formatting/readability reasons). Community service message: Omit needless parens. When in doubt whether [...]]]></description>
				<content:encoded><![CDATA[<p>The famous 17th commandment in <a href="http://en.wikipedia.org/wiki/The_Elements_of_Style" title="The Elements of Style">The Elements of Style</a> is &#8220;Omit needless words&#8221;.</p>
<p>There should be an equivalent in programming, but for parentheses.  Every time I see needless parens in a program I want to rip them out (unless they&#8217;re obviously there for formatting/readability reasons).</p>
<p>Community service message: <strong>Omit needless parens.</strong> When in doubt whether parens are needed, look up the precedence rules for the operators involved and only use parens if the default isn&#8217;t what you want.</p>
<p>Here&#8217;s why you shouldn&#8217;t use needless parens:</p>
<ul>
<li>The #1 reason is that you&#8217;re making your code more difficult to read for people who know the language better than you do. A more experienced programmer will see a red flag and look at your code more carefully than necessary because they will be trying to figure out why you used the extra parens and if there&#8217;s something non-obvious going on. When I come across code like that, I usually conclude that whoever wrote the code doesn&#8217;t know the language that well. My opinion of the code goes down. My reading speed goes down too because the needless parens, in my estimation, indicate an increased likelihood that programmer has done other (worse) things elsewhere.</li>
<li>You don&#8217;t want to appear incompetent or lazy, or to slow down or put off people reading your code, right?  (See above.)</li>
<li>Putting in needless parens is heading down a slippery slope. How many levels of extra parens should you stop at? The only clear cut rule that makes sense is to stop at zero.</li>
<li>If you pause to look up the precedence rules, you&#8217;ll make yourself a better programmer in the language in question. You&#8217;ll be able to read other people&#8217;s needless-parenthesis-free code with no problem. You can pen lofty holier-than-thou blog posts like this one.</li>
</ul>
<p>Back in about 1985 I wrote a tiny shell script to print out operator precedence and associativity rules for C. When I started programming in Perl, I wrote one for it. Then one for Python and later one for Javascript. For your convenience, and as a reward for reading, I just stuck the 4 scripts up <a href="https://github.com/terrycojones/precedence">on Github</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/12/18/omit-needless-parens/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Destructive, invasive, and dangerous behavior by UK ISP TalkTalk (aka StalkStalk)</title>
		<link>http://blogs.fluidinfo.com/terry/2012/12/05/destructive-invasive-and-dangerous-behavior-by-uk-isp-talktalk-aka-stalkstalk/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/12/05/destructive-invasive-and-dangerous-behavior-by-uk-isp-talktalk-aka-stalkstalk/#comments</comments>
		<pubDate>Tue, 04 Dec 2012 23:45:39 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[companies]]></category>
		<category><![CDATA[tech]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1065</guid>
		<description><![CDATA[Today I spent several hours trying to figure out what was going wrong with a web service I&#8217;ve been building. The service uses websockets to let browsers and the server send messages to each other over a connection that is held open. I built and tested the service locally and it worked fine. But when [...]]]></description>
				<content:encoded><![CDATA[<p><a href="http://blogs.fluidinfo.com/terry/wp-content/uploads/2012/12/stalk.jpg"><img src="http://blogs.fluidinfo.com/terry/wp-content/uploads/2012/12/stalk-280x300.jpg" alt="" title="stalk stalk" width="280" height="300" class="alignright size-medium wp-image-1066" /></a>Today I spent several hours trying to figure out what was going wrong with a web service I&#8217;ve been building. The service uses <a href="http://en.wikipedia.org/wiki/WebSocket">websockets</a> to let browsers and the server send messages to each other over a connection that is held open.</p>
<p>I built and tested the service locally and it worked fine. But when I deployed it to a remote server, the websocket connections were mysteriously dropped 30-35 seconds after they were established. The error messages on the server were cryptic, as were those in the browser. Google came to the rescue, and led me to the <a href="https://github.com/LearnBoost/socket.io/issues/619">eye-opening explanation</a>&#8230;</p>
<p>It turns out <a href="http://www.talktalk.co.uk/">TalkTalk</a>, my ISP, <em>are also fetching the URLs my browser fetches, after a delay of 30-35 seconds!</em>  I guess they&#8217;re not doing it with all URLs, probably because they figure the sites are &#8220;safe&#8221; and not full of content that might be deemed objectionable. All the accesses come from a single IP address (62.24.252.133), and if you block that address or otherwise deny its connection attempts, the websocket problem goes away immediately.</p>
<p>Dear TalkTalk &#8211; <b>this is a very bad idea</b>.  Here are 3 strong reasons why:</p>
<ol>
<li>First of all, you&#8217;re breaking the web for your own customers, as seen above. When your customers try to use a new start-up service based on websockets, their experience will be severely degraded, perhaps to the point where the service is unusable. Time and money will be spent trying to figure out what&#8217;s going on, and people will not be happy to learn that their ISP is to blame.</li>
<li>Second, there&#8217;s a real privacy issue here. I don&#8217;t really want to go into it, but I don&#8217;t trust my ISP (any ISP) to securely look after data associated with my account, let alone all the web content I look at. I have Google web history disabled. I don&#8217;t want my ISP building up a profile of what the people in this house look at online.  There&#8217;s a big difference between recording the URLs I go to and actually retrieving their content.</li>
<li>Third, it&#8217;s downright dangerous. What if I were controlling a medical device via a web interface and TalkTalk were interfering by killing my connections or by replaying my requests? What if there was a security system or some other sensitive controller on the other end? <b>There&#8217;s no way on earth TalkTalk should be making requests with unknown effects to an unknown service that they have not been authorized to use.</b> The TalkTalk legal team should consider this an emergency. Something is going to break, perhaps with fatal consequences, and they are going to get sued.</li>
</ol>
<p>If you want to read more opinions on this issue, try Googling <a href="http://lmgtfy.com/?q=talktalk+62.24.252.133">TalkTalk 62.24.252.133</a>.  Lots of people have run into this problem and are upset for various reasons.</p>
<p>See also: <a href="http://en.wikipedia.org/wiki/Phorm">Phorm</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/12/05/destructive-invasive-and-dangerous-behavior-by-uk-isp-talktalk-aka-stalkstalk/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Max Tabs</title>
		<link>http://blogs.fluidinfo.com/terry/2012/12/04/max-tabs/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/12/04/max-tabs/#comments</comments>
		<pubDate>Tue, 04 Dec 2012 02:48:36 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[browser extensions]]></category>
		<category><![CDATA[tech]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1056</guid>
		<description><![CDATA[Here&#8217;s the second of several Chrome and Chromium browser extensions I&#8217;ve recently written. Earlier, I posted some of the background motivation in Alternate browsing realities. After installing Max Tabs, your browser will not allow you to have more than 15 tabs open at once. Any time you try to open more tabs, it will automatically [...]]]></description>
				<content:encoded><![CDATA[<p><a href="http://blogs.fluidinfo.com/terry/wp-content/uploads/2012/12/logo-1281.png"><img src="http://blogs.fluidinfo.com/terry/wp-content/uploads/2012/12/logo-1281.png" alt="" title="maxtabs" width="128" height="128" class="alignright size-full wp-image-1057" /></a>Here&#8217;s the second of several <a href="https://www.google.com/intl/en/chrome/browser/">Chrome</a> and <a href="http://www.chromium.org/Home">Chromium</a> browser extensions I&#8217;ve recently written.  Earlier, I posted some of the background motivation in <a href="http://blogs.fluidinfo.com/terry/2012/12/04/alternate-browsing-realities/">Alternate browsing realities</a>.</p>
<p>After installing <em>Max Tabs</em>, your browser will not allow you to have more than 15 tabs open at once.  Any time you try to open more tabs, it will automatically close existing tabs to keep you at the limit.  If you later close some tabs and go below the limit, tabs will be reopened to show URLs that were previously automatically closed. I.e., the URLs in tabs that are closed are not forgotten, they&#8217;re stored until you&#8217;re down to a reasonable number of tabs.  (The URLs are not stored across browser sessions, though they easily could be.)</p>
<p>The main idea here is to limit the amount of memory Chrome consumes in keeping tabs open for you.  I regularly have about 50 tabs open, sometimes for weeks or even months, on pages I&#8217;m planning to read.  My laptop gets bogged down as Chrome consumes more and more memory.  I&#8217;ve long wanted something to limit my tab habit. I didn&#8217;t really like any of the options I found in the Chrome Store, so I wrote my own.  In case you&#8217;re wondering, the extension closes your rightmost open tabs.</p>
<p><em>Max Tabs</em> installs a context menu item that shows you the number of URLs it has closed. If you click the context menu item, you can disable the extension, at which point it will immediately open tabs for all URLs it automatically closed.</p>
<p>Note that the extension starts out disabled. I set it up that way so it would be less alarming on installation (if you have over 15 tabs open when you install it, it will immediately close as many tabs as needed).  Enable it via the context menu.</p>
<p>The extension is not in the Chrome Web Store yet.  It&#8217;s still very easy to install: just <a href="https://fluiddb.fluidinfo.com/about/maxtabs/fluidinfo.com/chrome.crx">click here</a> to download the extension, then <a href="http://www.howtogeek.com/120743/how-to-install-extensions-from-outside-the-chrome-web-store/">follow these instructions</a>.</p>
<p>If you&#8217;re a programmer, or just curious about how to build Chrome extensions, the source code <a href="https://github.com/terrycojones/maxtabs">is available on Github</a>.  For info on what URLs the extension has closed tabs for, you can look in the console of its background page, accessible from <a href="chrome://extensions">chrome://extensions</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/12/04/max-tabs/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Open It Later</title>
		<link>http://blogs.fluidinfo.com/terry/2012/12/04/open-it-later/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/12/04/open-it-later/#comments</comments>
		<pubDate>Tue, 04 Dec 2012 02:07:38 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[browser extensions]]></category>
		<category><![CDATA[tech]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1043</guid>
		<description><![CDATA[Here&#8217;s the first of several Chrome and Chromium browser extensions I&#8217;ve recently written. Earlier, I posted some of the background motivation in Alternate browsing realities. After installing Open It Later, your browser will randomly delay following links you click on. That is, instead of following the link in your existing tab, it immediately closes the [...]]]></description>
				<content:encoded><![CDATA[<p><a href="http://blogs.fluidinfo.com/terry/wp-content/uploads/2012/12/links1.jpg"><img src="http://blogs.fluidinfo.com/terry/wp-content/uploads/2012/12/links1.jpg" alt="" title="I don&#039;t often click on links..." width="250" height="250" class="alignright size-full wp-image-1036" /></a>Here&#8217;s the first of several <a href="https://www.google.com/intl/en/chrome/browser/">Chrome</a> and <a href="http://www.chromium.org/Home">Chromium</a> browser extensions I&#8217;ve recently written.  Earlier, I posted some of the background motivation in <a href="http://blogs.fluidinfo.com/terry/2012/12/04/alternate-browsing-realities/">Alternate browsing realities</a>.</p>
<p>After installing <em>Open It Later</em>, your browser will randomly delay following links you click on. That is, instead of following the link in your existing tab, it immediately closes the tab! :-) If you open a new tab and try to go to a URL, that tab will immediately close too.  The URL you were trying to reach will be opened in a new tab at a random future time, between 15 seconds and 5 minutes later.</p>
<p>This is pretty silly, of course. It deliberately goes directly against the idea that there should be an immediate (useful) reaction from your browser when you click a link. Think of it as something that slows you down, that makes your browsing more considered, that gives you a pause during which you might forget about something that you didn&#8217;t really need to read anyway.</p>
<p><em>Open It Later</em> installs a context menu item that shows you the number of URLs that are pending opening. Click the context menu item to disable the extension. Not only will it ungrudgingly disable itself without pause, it will also immediately open all URLs that were scheduled to be opened in the future.</p>
<p>The extension is not in the Chrome Web Store yet.  It&#8217;s still very easy to install: just <a href="https://fluiddb.fluidinfo.com/about/open-it-later/fluidinfo.com/chrome.crx">click here</a> to download the extension, then <a href="http://www.howtogeek.com/120743/how-to-install-extensions-from-outside-the-chrome-web-store/">follow these instructions</a>.</p>
<p>If you&#8217;re a programmer, or just curious about how to build Chrome extensions, the source code <a href="https://github.com/terrycojones/open-it-later">is available on Github</a>.  For info on when the extension plans to open your URLs, you can look in the console of its background page, accessible from <a href="chrome://extensions">chrome://extensions</a>.</p>
<p>Special thanks to <a href="http://gapingvoid.com/">Hugh McLeod</a>, who (unknowingly) provided <em><b>O</b>pen <b>I</b>t <b>L</b>ater</em>&#8216;s Snake Oil icon: <div id="attachment_1047" class="wp-caption alignnone" style="width: 138px"><a href="http://gapingvoid.com/"><img src="http://blogs.fluidinfo.com/terry/wp-content/uploads/2012/12/logo-128.png" alt="" title="Snake Oil" width="128" height="128" class="size-full wp-image-1047" /></a><p class="wp-caption-text">Image: Hugh McLeod</p></div></p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/12/04/open-it-later/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Alternate browsing realities</title>
		<link>http://blogs.fluidinfo.com/terry/2012/12/04/alternate-browsing-realities/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/12/04/alternate-browsing-realities/#comments</comments>
		<pubDate>Tue, 04 Dec 2012 00:28:17 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[browser extensions]]></category>
		<category><![CDATA[tech]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1032</guid>
		<description><![CDATA[I find it interesting to look for things we take for granted, and to ask what the world would look like if the basic assumptions were outlandishly violated. Recently I&#8217;ve been thinking about browsing. What do we all take for granted when browsing? A biggie is that when we click on a link (or open [...]]]></description>
				<content:encoded><![CDATA[<p><a href="http://blogs.fluidinfo.com/terry/wp-content/uploads/2012/12/links1.jpg"><img src="http://blogs.fluidinfo.com/terry/wp-content/uploads/2012/12/links1.jpg" alt="" title="I don&#039;t often click on links..." width="250" height="250" class="alignright size-full wp-image-1036" /></a>I find it interesting to look for things we take for granted, and to ask what the world would look like if the basic assumptions were outlandishly violated.</p>
<p>Recently I&#8217;ve been thinking about browsing.  What do we all take for granted when browsing?  A biggie is that when we click on a link (or open a new tab) the browser should take us to what we asked for, and do it immediately.</p>
<p>Below are some fanciful ways in which that could be different. You can probably think of others. Imagine if:</p>
<ul>
<li>When you click a link, the new page is shown as usual, but only at some random point in the future. Clicking a link or opening a new tab on a URL, causes your current tab to immediately close!</li>
<li>Your browser restricts you to having (say) at most 10 tabs open. If you try to open more, the browser automatically picks another open tab and closes it. When you drop back under 10 tabs, a tab that was automatically closed pops into existence again.</li>
<li>When you click to follow a link or you open a new tab, the page appears in someone else&#8217;s browser, not in yours.</li>
<li>You and a group of friends are limited in the number of total tabs you can collectively have open. If you open a tab that takes you over the limit, a random tab is closed in the browser of a random group member. When the group drops under the limit, the tab is re-opened in the browser of a random group member.</li>
<li>You and a group of friends are limited so that only one of you can be looking at any given URL. I.e., if you go to a URL that one of your group already has open, their browser automatically closes their tab.</li>
<li>When you click on a link, your browser shows you the page and the page also appears in the browsers of a group of friends. If a friend then clicks on a link on that page, your tab follows along.</li>
<li>When reading an interesting page, with one click you can send the URL to a group of friends, whose browsers all load the page.</li>
</ul>
<p>The nice thing about this kind of blue-sky thinking is that it starts out as frivolous or even ridiculous, but can quickly lead to interesting new approaches. For example, the idea of opening tabs in the future comes directly from questioning the immediacy of link following. Hot on the heels of the ridiculous idea of never following links at all, we land right next to the idea of a Read-Later button that millions of people already find very useful.</p>
<p>Anyway&#8230;.. I decided to have some fun and implement several of the above for the <a href="https://www.google.com/intl/en/chrome/browser/">Chrome</a> and <a href="http://www.chromium.org/Home">Chromium</a>. I&#8217;ll write them up separately very soon.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/12/04/alternate-browsing-realities/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>A simple way to calculate the day of the week for any day of a given year</title>
		<link>http://blogs.fluidinfo.com/terry/2012/11/11/a-simple-way-to-calculate-the-day-of-the-week-for-any-day-of-a-given-year/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/11/11/a-simple-way-to-calculate-the-day-of-the-week-for-any-day-of-a-given-year/#comments</comments>
		<pubDate>Sun, 11 Nov 2012 20:39:09 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[me]]></category>
		<category><![CDATA[other]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1020</guid>
		<description><![CDATA[The other day I read a tweet about how someone was impressed that a friend had been able to tell them the day of the week given an arbitrary date. There are a bunch of general methods to do this listed on the Wikipedia page for Determination of the day of the week. Typically, there [...]]]></description>
				<content:encoded><![CDATA[<p><div id="attachment_1023" class="wp-caption alignright" style="width: 310px"><a href="http://designmoo.com/3993/calendar/"><img src="http://blogs.fluidinfo.com/terry/wp-content/uploads/2012/11/calendar-300x225.png" alt="March 29th" title="calendar" width="300" height="225" class="size-medium wp-image-1023" /></a><p class="wp-caption-text">Image: Jeremy Church</p></div>The other day I read a tweet about how someone was impressed that a friend had been able to tell them the day of the week given an arbitrary date.</p>
<p>There are a bunch of general methods to do this listed on the Wikipedia page for <a href="http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week">Determination of the day of the week</a>.  Typically, there are several steps involved, and you need to memorize some small tables of numbers.</p>
<p>I used to practice that mental calculation (and many others) when I was about 16.  Although all the steps are basic arithmetic, it&#8217;s not easy to do the calculation in your head in a couple of seconds. Given that most of these questions that you&#8217;re likely to face in day-to-day life will be about the current year, it seemed like it might be a poor tradeoff to learn the complicated method to calculate the day of the week for any date if there was a simpler way to do it for a specific year.</p>
<p>The method I came up with after that observation is really simple. It just requires you to memorize a single 12-digit sequence for the current year.  The 12 digits correspond to the first Monday for each month.</p>
<p>For example, the sequence for 2012 is 265-274-263-153.  Suppose you&#8217;ve memorized the sequence and you need to know the day of the week for March 29th. You can trivially calculate that it&#8217;s a Thursday. You take the 3rd digit of the sequence (because March is the 3rd month), which is 5. That tells you that the 5th of March was a Monday. Then you just go backward or forward as many weeks and days as you need. The 5th was a Monday, so the 12th, 19th, and 26th were too, which means the 29th was a Thursday.</p>
<p>It&#8217;s nice because the amount you need to memorize is small, and you can  memorize less digits if you only want to cover a shorter period.  The calculation is very simple and always the same in every case, and you never have to think about leap years.  At the start of each year you just memorize a single sequence, which is quickly reinforced once you use it a few times.</p>
<p>Here&#8217;s Python code to print the sequence for any year.</p>
<p><code>
<div class="dean_ch" style="white-space: nowrap;">
<span class="co1">#!/usr/bin/env python</span></p>
<p><span class="kw1">import</span> <span class="kw3">datetime</span>, <span class="kw3">sys</span></p>
<p><span class="kw1">try</span>:<br />
&nbsp; &nbsp; year = <span class="kw2">int</span><span class="br0">&#40;</span><span class="kw3">sys</span>.<span class="me1">argv</span><span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
<span class="kw1">except</span> <span class="kw2">IndexError</span>:<br />
&nbsp; &nbsp; year = <span class="kw3">datetime</span>.<span class="kw3">datetime</span>.<span class="me1">today</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">year</span></p>
<p>firstDayToFirstMonday = <span class="br0">&#91;</span><span class="st0">'1st'</span>, <span class="st0">'7th'</span>, <span class="st0">'6th'</span>, <span class="st0">'5th'</span>, <span class="st0">'4th'</span>, <span class="st0">'3rd'</span>, <span class="st0">'2nd'</span><span class="br0">&#93;</span><br />
months = <span class="br0">&#91;</span><span class="st0">'Jan'</span>, <span class="st0">'Feb'</span>, <span class="st0">'Mar'</span>, <span class="st0">'Apr'</span>, <span class="st0">'May'</span>, <span class="st0">'Jun'</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'Jul'</span>, <span class="st0">'Aug'</span>, <span class="st0">'Sep'</span>, <span class="st0">'Oct'</span>, <span class="st0">'Nov'</span>, <span class="st0">'Dec'</span><span class="br0">&#93;</span><br />
summary = <span class="st0">''</span></p>
<p><span class="kw1">for</span> month <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">&#40;</span><span class="nu0">12</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; firstOfMonth = <span class="kw3">datetime</span>.<span class="kw3">datetime</span><span class="br0">&#40;</span>year, month + <span class="nu0">1</span>, <span class="nu0">1</span><span class="br0">&#41;</span>.<span class="me1">weekday</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; firstMonday = firstDayToFirstMonday<span class="br0">&#91;</span>firstOfMonth<span class="br0">&#93;</span><br />
&nbsp; &nbsp; <span class="kw1">print</span> months<span class="br0">&#91;</span>month<span class="br0">&#93;</span>, firstMonday<br />
&nbsp; &nbsp; summary += firstMonday<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span></p>
<p><span class="kw1">print</span> <span class="st0">'Summary:'</span>, <span class="st0">'-'</span>.<span class="me1">join</span><span class="br0">&#40;</span>summary<span class="br0">&#91;</span>x:x<span class="nu0">+3</span><span class="br0">&#93;</span> <span class="kw1">for</span> x <span class="kw1">in</span> <span class="kw2">range</span><span class="br0">&#40;</span><span class="nu0">0</span>, <span class="nu0">12</span>, <span class="nu0">3</span><span class="br0">&#41;</span><span class="br0">&#41;</span></div>
<p></code></p>
<p>The output for 2012 looks like</p>
<pre>
Jan 2nd
Feb 6th
Mar 5th
Apr 2nd
May 7th
Jun 4th
Jul 2nd
Aug 6th
Sep 3rd
Oct 1st
Nov 5th
Dec 3rd
Summary: 265-274-263-153
</pre>
<p>The memory task is made simpler by the fact that there are only 14 different possible sequences.  Or, if you consider just the last 10 digits of the sequences (i.e., starting from March), there are only 7 possible sequences. There are only 14 different sequences, so if you use this method in the long term, you&#8217;ll find the effort of remembering a sequence will pay off when it re-appears. E.g., 2013 and 2019 both have sequence 744-163-152-742. There are other nice things you can learn that can also make the memorization and switching between years easier (see the <a href="http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week#Corresponding_months">Corresponding months</a> section on the above Wikipedia page).</p>
<p>Here are the sequences through 2032:</p>
<pre>
2012 265-274-263-153
2013 744-163-152-742
2014 633-752-741-631
2015 522-641-637-527
2016 417-426-415-375
2017 266-315-374-264
2018 155-274-263-153
2019 744-163-152-742
2020 632-641-637-527
2021 411-537-526-416
2022 377-426-415-375
2023 266-315-374-264
2024 154-163-152-742
2025 633-752-741-631
2026 522-641-637-527
2027 411-537-526-416
2028 376-315-374-264
2029 155-274-263-153
2030 744-163-152-742
2031 633-752-741-631
2032 521-537-526-416
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/11/11/a-simple-way-to-calculate-the-day-of-the-week-for-any-day-of-a-given-year/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Simpler Twisted deferred code via decorated callbacks</title>
		<link>http://blogs.fluidinfo.com/terry/2012/10/14/simpler-twisted-deferred-code-via-decorated-callbacks/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/10/14/simpler-twisted-deferred-code-via-decorated-callbacks/#comments</comments>
		<pubDate>Sun, 14 Oct 2012 22:52:58 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[tech]]></category>
		<category><![CDATA[twisted]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1007</guid>
		<description><![CDATA[This morning I was thinking about Twisted deferreds and how people find them difficult to grasp, but how they&#8217;re conceptually simple once you get it. I guess most of us tell people a deferred is something to hold a result that hasn&#8217;t arrived yet. Sometimes, though, deferreds do have a result in them immediately (e.g., [...]]]></description>
				<content:encoded><![CDATA[<p>This morning I was thinking about <a href="http://twistedmatrix.com">Twisted</a> <a href="http://twistedmatrix.com/documents/10.1.0/core/howto/deferredindepth.html">deferreds</a> and how people find them difficult to grasp, but how they&#8217;re conceptually simple once you get it.  I guess most of us tell people a deferred is something to hold a result that hasn&#8217;t arrived yet. Sometimes, though, deferreds do have a result in them immediately (e.g., using <a href="http://twistedmatrix.com/documents/12.2.0/api/twisted.internet.defer.html#succeed">succeed</a> or <a href="http://twistedmatrix.com/documents/12.2.0/api/twisted.internet.defer.html#fail">fail</a> to get an already-fired deferred).</p>
<p>I wondered if it might work to tell people to think of a deferred as really being the result. If that were literally true, then instead of writing:</p>
<p><code></p>
<div class="dean_ch" style="white-space: nowrap;">
d = getPage<span class="br0">&#40;</span>...<span class="br0">&#41;</span><br />
d.<span class="me1">addErrback</span><span class="br0">&#40;</span>errcheck, args<span class="br0">&#41;</span><br />
d.<span class="me1">addCallback</span><span class="br0">&#40;</span>cleanup, args<span class="br0">&#41;</span><br />
d.<span class="me1">addCallback</span><span class="br0">&#40;</span>reformat, args<span class="br0">&#41;</span><br />
<span class="kw1">return</span> d</div>
<p></code></p>
<p>We might write something like:</p>
<p><code></p>
<div class="dean_ch" style="white-space: nowrap;">
result1 = getPage<span class="br0">&#40;</span>...<span class="br0">&#41;</span><br />
result2 = errcheck<span class="br0">&#40;</span>result1, args<span class="br0">&#41;</span><br />
result3 = cleanup<span class="br0">&#40;</span>result2, args<span class="br0">&#41;</span><br />
<span class="kw1">return</span> reformat<span class="br0">&#40;</span>result3, args<span class="br0">&#41;</span></div>
<p></code></p>
<p>And if you could write that, you could obviously instead write:</p>
<p><code></p>
<div class="dean_ch" style="white-space: nowrap;">
<span class="kw1">return</span> reformat<span class="br0">&#40;</span>cleanup<span class="br0">&#40;</span>errcheck<span class="br0">&#40;</span>getPage<span class="br0">&#40;</span>...<span class="br0">&#41;</span>, args<span class="br0">&#41;</span>, args<span class="br0">&#41;</span>, args<span class="br0">&#41;</span></div>
<p></code></p>
<p>If we could write Twisted code that way, I think using deferreds would be simpler for people unfamiliar with them. We could show them Twisted code and not even have to mention deferreds (see below).</p>
<p>In the style we&#8217;re all used to, the programmer manually adds callbacks and errbacks.  That&#8217;s basically boilerplate. It gets worse when you then need to also use <a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.internet.defer.DeferredList.html">DeferredList</a>, etc. It&#8217;s a little confusing to read deferred code at first, because you need to know that the deferred result/failure is automatically passed as the first arg to callbacks/errbacks.  It seems to take a year or more for people to finally realize how the callback &#038; errback chains actually interact :-) Also, I wonder how comfortable programmers are with code ordered innermost function first, as in the normal <code>d.addCallback(inner).addCallback(outer)</code> Twisted style, versus <code>outer(inner())</code>, as in the line above.</p>
<p>Anyway&#8230; I realized we CAN let people use the succinct style above, by putting boilerplate into decorators.  I wrote two decorators, called (surprise!) callback and errback.  You can do this:</p>
<p><code></p>
<div class="dean_ch" style="white-space: nowrap;">
@errback<br />
<span class="kw1">def</span> errcheck<span class="br0">&#40;</span>failure, arg<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; ...</p>
<p>@callback<br />
<span class="kw1">def</span> cleanup<span class="br0">&#40;</span>page, arg<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; ...</p>
<p>@callback<br />
<span class="kw1">def</span> reformat<span class="br0">&#40;</span>page, arg<span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; ...</p>
<p><span class="me1">reformat</span><span class="br0">&#40;</span>cleanup<span class="br0">&#40;</span>errcheck<span class="br0">&#40;</span>getPage<span class="br0">&#40;</span>...<span class="br0">&#41;</span>, arg1<span class="br0">&#41;</span>, arg2<span class="br0">&#41;</span>, arg3<span class="br0">&#41;</span></div>
<p></code></p>
<p>The deferred callback and errback chains are hooked up automatically. You still get a regular deferred back as the return value.</p>
<p>And&#8230; the &#8220;deferred&#8221; aspect of the code (or at least the need to talk about or explain it) has conveniently vanished.</p>
<p>You can also do things like</p>
<p><code></p>
<div class="dean_ch" style="white-space: nowrap;">
func1<span class="br0">&#40;</span>getDeferred1<span class="br0">&#40;</span><span class="br0">&#41;</span>, errcheck<span class="br0">&#40;</span>func2<span class="br0">&#40;</span>getDeferred2<span class="br0">&#40;</span><span class="br0">&#41;</span>, getDeferred3<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></div>
<p></code></p>
<p>This gets the result of deferreds 2 &#038; 3 and (if neither fails) passes the result of calling func2 on both results through to func1, which is called along with the result of deferred 1. You don&#8217;t need to use DeferredLists, as the decorator makes them for you. The errcheck function wont be called at all unless there&#8217;s an error.</p>
<p>That&#8217;s nice compared to the verbose equivalent:</p>
<p><code></p>
<div class="dean_ch" style="white-space: nowrap;">
d1 = DeferredList<span class="br0">&#40;</span><span class="br0">&#91;</span>getDeferred2<span class="br0">&#40;</span><span class="br0">&#41;</span>, getDeferred3<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
d1.<span class="me1">addCallback</span><span class="br0">&#40;</span>func2<span class="br0">&#41;</span><br />
d1.<span class="me1">addErrback</span><span class="br0">&#40;</span>errcheck<span class="br0">&#41;</span><br />
d2 = DeferredList<span class="br0">&#40;</span><span class="br0">&#91;</span>getDeferred1<span class="br0">&#40;</span><span class="br0">&#41;</span>, d1<span class="br0">&#93;</span><span class="br0">&#41;</span><br />
d2.<span class="me1">addCallback</span><span class="br0">&#40;</span>func1<span class="br0">&#41;</span></div>
<p></code></p>
<p>Or the more compact but awkward:</p>
<p><code></p>
<div class="dean_ch" style="white-space: nowrap;">
DeferredList<span class="br0">&#40;</span><span class="br0">&#91;</span>getDeferred<span class="br0">&#40;</span><span class="br0">&#41;</span>, DeferredList<span class="br0">&#40;</span><span class="br0">&#91;</span>getDeferred<span class="br0">&#40;</span><span class="br0">&#41;</span>, getDeferred<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.<span class="me1">addCallback</span><span class="br0">&#40;</span><br />
&nbsp; &nbsp; func2<span class="br0">&#41;</span>.<span class="me1">addErrback</span><span class="br0">&#40;</span>errcheck<span class="br0">&#41;</span><span class="br0">&#93;</span><span class="br0">&#41;</span>.<span class="me1">addCallback</span><span class="br0">&#40;</span>func1<span class="br0">&#41;</span><br />
&nbsp;</div>
<p></code></p>
<p>There&#8217;s lots more that could be said about this, but that&#8217;s enough for now.  The code (surely not bulletproof) and some tests are <a href="https://github.com/terrycojones/twisted-callback-decorators">on Github</a>. I&#8217;ll add a README sometime soon.  This is still pretty much proof of concept, and some it could be done slightly differently. I&#8217;m happy to discuss in more detail if people are interested.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/10/14/simpler-twisted-deferred-code-via-decorated-callbacks/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>SOBGTR OCCC AILD FUNEX?</title>
		<link>http://blogs.fluidinfo.com/terry/2012/08/10/sobgtr-occc-aild-funex/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/08/10/sobgtr-occc-aild-funex/#comments</comments>
		<pubDate>Fri, 10 Aug 2012 07:10:03 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[me]]></category>
		<category><![CDATA[other]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=972</guid>
		<description><![CDATA[Suppose you had to pick a very small set of character strings that you, and only you, could identify without hesitation in a particular way. What would you choose? How small a set could you choose and still be unique? For example, SOBGTR OCCC AILD FUNEX? is a set of strings that I think would [...]]]></description>
				<content:encoded><![CDATA[<p>Suppose you had to pick a very small set of character strings that you, and only you, could identify without hesitation in a particular way. What would you choose? How small a set could you choose and still be unique? For example, <strong>SOBGTR OCCC AILD FUNEX?</strong> is a set of strings that I think would uniquely identify me. (My interpretation is below.) I&#8217;m pretty sure that almost any subset of 3 of them would suffice. Coming up with a set of two wouldn&#8217;t be hard, I don&#8217;t think &#8211; but it feels risky.</p>
<p>There are 7 billion people on the planet. So if you just pick 3 reasonably obscure acronyms, e.g., things that only 1 person in 2000 would recognize, you&#8217;re heading in the right direction (since 2000 cubed is 8 billion). But that&#8217;s only if the obscurity of the things you pick is independent. For example, it&#8217;s less good to pick 3 computer acronyms from the 1960s than to choose just one of them plus some things from very different areas of your knowledge.</p>
<p><strong>The rules</strong></p>
<ol>
<li>Each of your strings <em>with its meaning to you</em> must be findable on Google.</li>
<li>To match with you, another person must interpret <em>all</em> your strings the way you do.</li>
</ol>
<p>Rule 1 prevents you from choosing something like your bank PIN number, that only you could possibly know.  Without this rule, everyone could trivially choose a set of one string. The rule makes thinking up a uniquely identifying set for yourself like a game. Given that all your strings and their interpretations are on Google, each of your strings will likely be recognized by <em>someone</em> in the way you recognize it, so your set will probably have at least 2 strings. You need to choose a set of strings whose set of interpretations, taken as a whole, make you unique (Rule 2).</p>
<p><strong>Why is this interesting?</strong></p>
<p>I find this interesting for many reasons. It seems clear that uniquely identifying sets are fairly easy to construct for people and they&#8217;re very small. Certainly small enough to fit in a tweet.  Although it&#8217;s easy to make a set for yourself, it&#8217;s hard to make one for someone else &#8211; you might even argue that by definition it&#8217;s not possible. If someone else makes one, you can&#8217;t produce their set of interpretations without spending time on Google, and even then you&#8217;d probably have to know the person pretty well.</p>
<p>Is there a new authentication scheme here somewhere? It&#8217;s tempting to think yes, but there probably isn&#8217;t. This is less secure than asking people for a set of secrets that are not each findable in Google, so anything you come up with is almost certain to be less secure than the same thing based on a set of actual secrets.  It&#8217;s more of a fun thought exercise (or Twitter game). It&#8217;s not hard to imagine some form of authentication. For example, identify which of a set of symbols are special to you (avoiding others chosen randomly from, say, the set of all acronyms), and their correct interpretations for you, and do it rapidly.  Or if a clone shows up one day, claiming to be you, and you&#8217;ve thoughtfully put a sealed set of unique symbol strings in your safe, you should be able to convince people that you&#8217;re the real you :-)</p>
<p><strong>Answer</strong></p>
<p>Here&#8217;s my unhesitating interpretation of the set of 4 strings above:</p>
<ul>
<li>SOBGTR: Subtract one and branch if greater than zero. You probably have to have written <a href="http://en.wikipedia.org/wiki/VAX">VAX</a> assembly language code or a compiler targetting the VAX in the 1970s or 80s to get that one. <a href="http://jcsites.juniata.edu/faculty/rhodes/org/instr.htm">More info here</a>.
</li>
<li>OCCC: The <a href="http://www.oldcranbrookianscricketclub.com/">Old Cranbrookians Cricket Club</a>, of course.</li>
<li>AILD: A widely-used (in very small circles) acronym for William Faulkner&#8217;s book, <a href="http://en.wikipedia.org/wiki/As_I_Lay_Dying_(novel)">As I Lay Dying</a>.</li>
<li>FUNEX? &#8211; &#8220;Have you any eggs?&#8221; From a 1974 <a href="http://en.wikipedia.org/wiki/The_Two_Ronnies">Two Ronnies</a> episode <a href="http://www.youtube.com/watch?v=zkWMcRlE1mQ">Swedish Made Simple</a>.</li>
</ul>
<p>Remember, to be me you have to get them <em>all</em>. It&#8217;s not enough to get a couple, or even three of them.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/08/10/sobgtr-occc-aild-funex/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>describejson &#8211; a Python script for summarizing JSON structure</title>
		<link>http://blogs.fluidinfo.com/terry/2012/08/09/describejson-a-python-script-for-summarizing-json-structure/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/08/09/describejson-a-python-script-for-summarizing-json-structure/#comments</comments>
		<pubDate>Thu, 09 Aug 2012 19:32:20 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[tech]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=954</guid>
		<description><![CDATA[Yesterday I was sent a 24M JSON file and needed to look through it to give someone an opinion on its contents. I did what I normally do to look at JSON, piping it into python -m json.tool. The result looked pretty good, I scrolled through some screens with a single long list and jumped [...]]]></description>
				<content:encoded><![CDATA[<p>Yesterday I was sent a 24M JSON file and needed to look through it to give someone an opinion on its contents.  I did what I normally do to look at JSON, piping it into <tt>python -m json.tool</tt>. The result looked pretty good, I scrolled through some screens with a single long list and jumped to the bottom to see what looked like the end of the list. What I didn&#8217;t know at the time was that the output was 495,647 lines long!  And there was some important stuff in the middle of the output that I didn&#8217;t see at all.</p>
<p>So I decided to write a quick program to recursively summarize JSON. You can grab it from Github at <a href="https://github.com/terrycojones/describejson" title="describejson">https://github.com/terrycojones/describejson</a>.</p>
<p>Usage is simple, just send it JSON on stdin. Here&#8217;s an example input:</p>
<div class="dean_ch" style="white-space: nowrap;">
{<br />
&nbsp; &quot;cats&quot;: 3,<br />
&nbsp; &quot;dogs&quot;: 6,<br />
&nbsp; &quot;parrots&quot;: 1<br />
}</div>
<p>Which gets you this output:</p>
<div class="dean_ch" style="white-space: nowrap;">
$ python describejson.py &lt; input.json<br />
1 dict of length 3. Values:<br />
&nbsp; 3 ints</div>
<p>The output is a little cryptic, but you&#8217;ll get used to it (and I may improve it). In words, this is telling you that (after loading the JSON) the input contained 1 Python dictionary with 3 elements. The values of the 3 elements are all integers.  The indentation is meaningful, of course. You can see that the script is summarizing the fact that the 3 values in the dict were all of the same type.</p>
<p>Here&#8217;s another sample input:</p>
<div class="dean_ch" style="white-space: nowrap;">
[<br />
&nbsp; [&quot;fluffy&quot;, &quot;kitty&quot;, &quot;ginger&quot;],<br />
&nbsp; [&quot;fido&quot;, &quot;spot&quot;, &quot;rover&quot;],<br />
&nbsp; [&quot;squawk&quot;]<br />
]</div>
<p>Which gets you:</p>
<div class="dean_ch" style="white-space: nowrap;">
$ python describejson.py &lt; input.json<br />
1 list of length 3. Values:<br />
&nbsp; 2 lists of length 3. Values:<br />
&nbsp; &nbsp; 3 unicodes<br />
&nbsp; 1 list of length 1. Values:<br />
&nbsp; &nbsp; 1 unicode</div>
<p>In words, the input was a list of length 3. Its contents were 2 lists of length 3 that both contained 3 unicode strings, and a final list that contains just a single unicode string.</p>
<p><strong>Specifying equality strictness</strong></p>
<p>The script currently takes just one option, <tt>--strictness</tt> (or just <tt>-s</tt>) to indicate how strict it should be in deciding whether things are &#8220;the same&#8221; in order to summarize them.  In the above output, the default strictness <tt>length</tt> is used, so the script considers the first two inner lists to be collapsible in the summary, and prints a separate line for the last list since it&#8217;s of a different length.  Here&#8217;s the output from running with <tt>--strictness type</tt>:</p>
<div class="dean_ch" style="white-space: nowrap;">
$ python describejson.py &#8211;strictness type &lt; input.json<br />
1 list of length 3. Values:<br />
&nbsp; 3 lists of length 3. Values:<br />
&nbsp; &nbsp; 3 unicodes</div>
<p>The lists are all considered equal here. The output is a little misleading, since it tells us there are 3 lists of length 3, each containing 3 unicodes. I may fix that.</p>
<p>We can also be more strict. Here&#8217;s the output from <tt>--strictness keys</tt>:</p>
<div class="dean_ch" style="white-space: nowrap;">
$ python describejson.py &#8211;strictness keys &lt; input.json<br />
1 list of length 3. Values:<br />
&nbsp; 1 list of length 3. Values:<br />
&nbsp; &nbsp; 3 unicodes<br />
&nbsp; 1 list of length 3. Values:<br />
&nbsp; &nbsp; 3 unicodes<br />
&nbsp; 1 list of length 1. Values:<br />
&nbsp; &nbsp; 1 unicode</div>
<p>The 3 inner lists are each printed separately because their contents differ.  The <tt>keys</tt> argument is also a bit confusing for lists, it just means the list values. It&#8217;s clearer when you have dictionaries in the input.</p>
<p>This input:</p>
<div class="dean_ch" style="white-space: nowrap;">
[<br />
&nbsp; {<br />
&nbsp; &nbsp; &quot;a&quot;: 1,<br />
&nbsp; &nbsp; &quot;b&quot;: 2,<br />
&nbsp; &nbsp; &quot;c&quot;: 3<br />
&nbsp; },<br />
&nbsp; {<br />
&nbsp; &nbsp; &quot;d&quot;: 4,<br />
&nbsp; &nbsp; &quot;e&quot;: 5,<br />
&nbsp; &nbsp; &quot;f&quot;: 6<br />
&nbsp; }<br />
]</div>
<p>produces</p>
<div class="dean_ch" style="white-space: nowrap;">
$ python describejson.py &lt; input.json<br />
1 list of length 2. Values:<br />
&nbsp; 2 dicts of length 3. Values:<br />
&nbsp; &nbsp; 3 ints</div>
<p>I.e., one list, containing 2 dictionaries, each containing 3 int values. Note that this is using the default of <tt>--strictness length</tt> so the two dicts are considered the same. If we run that input with strictness of <tt>keys</tt>, we&#8217;ll instead get this:</p>
<div class="dean_ch" style="white-space: nowrap;">
$ python describejson.py &#8211;strictness keys &lt; input.json<br />
1 list of length 2. Values:<br />
&nbsp; 1 dict of length 3. Values:<br />
&nbsp; &nbsp; 3 ints<br />
&nbsp; 1 dict of length 3. Values:<br />
&nbsp; &nbsp; 3 ints</div>
<p>The dicts are considered different because their keys differ. If we change the input to make the keys the same:</p>
<div class="dean_ch" style="white-space: nowrap;">
[<br />
&nbsp; {<br />
&nbsp; &nbsp; &quot;a&quot;: 1,<br />
&nbsp; &nbsp; &quot;b&quot;: 2,<br />
&nbsp; &nbsp; &quot;c&quot;: 3<br />
&nbsp; },<br />
&nbsp; {<br />
&nbsp; &nbsp; &quot;a&quot;: 4,<br />
&nbsp; &nbsp; &quot;b&quot;: 5,<br />
&nbsp; &nbsp; &quot;c&quot;: 6<br />
&nbsp; }<br />
]</div>
<p>and run again with <tt>--strictness keys</tt>, the dicts are considered the same:</p>
<div class="dean_ch" style="white-space: nowrap;">
$ python describejson.py &#8211;strictness keys &lt; input.json<br />
1 list of length 2. Values:<br />
&nbsp; 2 dicts of length 3. Values:<br />
&nbsp; &nbsp; 3 ints</div>
<p>but if we use <tt>--strictness equal</tt>, the dicts will be considered different:</p>
<div class="dean_ch" style="white-space: nowrap;">
$ python describejson.py &#8211;strictness equal &lt; input.json<br />
1 list of length 2. Values:<br />
&nbsp; 1 dict of length 3. Values:<br />
&nbsp; &nbsp; 3 ints<br />
&nbsp; 1 dict of length 3. Values:<br />
&nbsp; &nbsp; 3 ints</div>
<p>Finally, making the dicts the same:</p>
<div class="dean_ch" style="white-space: nowrap;">
[<br />
&nbsp; {<br />
&nbsp; &nbsp; &quot;a&quot;: 1,<br />
&nbsp; &nbsp; &quot;b&quot;: 2,<br />
&nbsp; &nbsp; &quot;c&quot;: 3<br />
&nbsp; },<br />
&nbsp; {<br />
&nbsp; &nbsp; &quot;a&quot;: 1,<br />
&nbsp; &nbsp; &quot;b&quot;: 2,<br />
&nbsp; &nbsp; &quot;c&quot;: 3<br />
&nbsp; }<br />
]</div>
<p>and running with <tt>--strictness equal</tt> will collapse the summary as you&#8217;d expect:</p>
<div class="dean_ch" style="white-space: nowrap;">
$ python describejson.py &#8211;strictness equal &lt; input.json<br />
1 list of length 2. Values:<br />
&nbsp; 2 dicts of length 3. Values:<br />
&nbsp; &nbsp; 3 ints</div>
<p>Hopefully it&#8217;s clear that by being less strict on matching you&#8217;ll get more concise output in which things are casually considered &#8220;the same&#8221; and if you&#8217;re more strict you&#8217;ll get more verbose output, all the way to using strict equality for both lists and dicts.</p>
<p>Here&#8217;s the full set of <tt>--strictness</tt> options:</p>
<ul>
<li><tt>type</tt>: compare things by type only.</li>
<li><tt>length</tt>: compare lists and objects by length.</li>
<li><tt>keys</tt>: compare lists by equality, dicts by keys.</li>
<li><tt>equal</tt>: compare lists and dicts by equality.</li>
</ul>
<p><strong>Improvements</strong></p>
<p>The naming of the <tt>--strictness</tt> options could be improved. The <tt>keys</tt> option should probably be called <tt>values</tt> (but that is confusing, since dictionaries have values and it&#8217;s a comparison based on their keys!).  A <tt>values</tt> option should probably also compare the value of primitive things, like integers and strings.</p>
<p>There are quite a few other things I might do to this script, if I ever have time. It would be helpful to print out some keys and values when these are short and unchanging. It would be good to show an example representative value of something that repeats (modulo strictness) many times. It might be good to be able to limit the depth to go into a JSON structure.</p>
<p>Overall though, I already find the script useful and I&#8217;m not in a rush to &#8220;improve&#8221; it by adding features.  <a href="https://github.com/terrycojones/describejson">You can though</a> :-)</p>
<p>You might also find it helpful to take what you learn about a JSON object via describe JSON and use that to grep out specific pieces of the structure using <a href="http://blogs.fluidinfo.com/terry/2010/11/25/jsongrep-py-python-for-extracting-pieces-of-json-objects/">jsongrep.py</a>.</p>
<p>If you&#8217;re curious, here&#8217;s the 24-line output summary of the 24M JSON I received.  Much more concise than the nearly 1/2 a million lines from <tt>python -m json.tool</tt>:</p>
<div class="dean_ch" style="white-space: nowrap;">
1 dict of length 3. Values:<br />
&nbsp; 1 int<br />
&nbsp; 1 dict of length 4. Values:<br />
&nbsp; &nbsp; 1 list of length 17993. Values:<br />
&nbsp; &nbsp; &nbsp; 17993 dicts of length 5. Values:<br />
&nbsp; &nbsp; &nbsp; &nbsp; 1 unicode<br />
&nbsp; &nbsp; &nbsp; &nbsp; 1 int<br />
&nbsp; &nbsp; &nbsp; &nbsp; 1 list of length 0.<br />
&nbsp; &nbsp; &nbsp; &nbsp; 2 unicodes<br />
&nbsp; &nbsp; 1 list of length 0.<br />
&nbsp; &nbsp; 1 list of length 11907. Values:<br />
&nbsp; &nbsp; &nbsp; 11907 dicts of length 5. Values:<br />
&nbsp; &nbsp; &nbsp; &nbsp; 1 unicode<br />
&nbsp; &nbsp; &nbsp; &nbsp; 1 int<br />
&nbsp; &nbsp; &nbsp; &nbsp; 1 list of length 1. Values:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 1 unicode<br />
&nbsp; &nbsp; &nbsp; &nbsp; 2 unicodes<br />
&nbsp; &nbsp; 1 list of length 28068. Values:<br />
&nbsp; &nbsp; &nbsp; 28068 dicts of length 5. Values:<br />
&nbsp; &nbsp; &nbsp; &nbsp; 1 unicode<br />
&nbsp; &nbsp; &nbsp; &nbsp; 1 int<br />
&nbsp; &nbsp; &nbsp; &nbsp; 1 list of length 0.<br />
&nbsp; &nbsp; &nbsp; &nbsp; 2 unicodes<br />
&nbsp; 1 unicode</div>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/08/09/describejson-a-python-script-for-summarizing-json-structure/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Autovivification in Python: nested defaultdicts with a specific final type</title>
		<link>http://blogs.fluidinfo.com/terry/2012/05/26/autovivification-in-python-nested-defaultdicts-with-a-specific-final-type/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/05/26/autovivification-in-python-nested-defaultdicts-with-a-specific-final-type/#comments</comments>
		<pubDate>Fri, 25 May 2012 23:10:12 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[tech]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=929</guid>
		<description><![CDATA[I quite often miss the flexibility of autovivification in Python. That Wikipedia link shows a cute way to get what Perl has: from collections import defaultdict def tree&#40;&#41;: &#160; &#160; return defaultdict&#40;tree&#41; lupin = tree&#40;&#41; lupin&#91;&#34;express&#34;&#93;&#91;3&#93; = &#34;stand and deliver&#34; It&#8217;s interesting to think about what&#8217;s going on in the above code and why it [...]]]></description>
				<content:encoded><![CDATA[<p>I quite often miss the flexibility of <a href="http://en.wikipedia.org/wiki/Autovivification">autovivification</a> in <a href="http://python.org">Python</a>. That Wikipedia link shows a cute way to get what Perl has:</p>
<div class="dean_ch" style="white-space: nowrap;">
<span class="kw1">from</span> <span class="kw3">collections</span> <span class="kw1">import</span> defaultdict</p>
<p><span class="kw1">def</span> tree<span class="br0">&#40;</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; <span class="kw1">return</span> defaultdict<span class="br0">&#40;</span>tree<span class="br0">&#41;</span></p>
<p>lupin = tree<span class="br0">&#40;</span><span class="br0">&#41;</span><br />
lupin<span class="br0">&#91;</span><span class="st0">&quot;express&quot;</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="nu0">3</span><span class="br0">&#93;</span> = <span class="st0">&quot;stand and deliver&quot;</span></div>
<p>It&#8217;s interesting to think about what&#8217;s going on in the above code and why it works. I really like defaultdict.</p>
<p>What I more often want, though, is not infinitely deep nested dictionaries like the above, but a (known) finite number of nested defaultdicts with a specific type at the final level. Here&#8217;s a tiny function I wrote to get just that:</p>
<div class="dean_ch" style="white-space: nowrap;">
<span class="kw1">from</span> <span class="kw3">collections</span> <span class="kw1">import</span> defaultdict</p>
<p><span class="kw1">def</span> autovivify<span class="br0">&#40;</span>levels=<span class="nu0">1</span>, final=<span class="kw2">dict</span><span class="br0">&#41;</span>:<br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="br0">&#40;</span>defaultdict<span class="br0">&#40;</span>final<span class="br0">&#41;</span> <span class="kw1">if</span> levels &lt; <span class="nu0">2</span> <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; defaultdict<span class="br0">&#40;</span><span class="kw1">lambda</span>: autovivify<span class="br0">&#40;</span>levels &#8211; <span class="nu0">1</span>, final<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></div>
<p>So let&#8217;s say you were counting the number of occurrences of words said by people by year, month, and day in a chat room. You&#8217;d write:</p>
<div class="dean_ch" style="white-space: nowrap;">
words = autovivify<span class="br0">&#40;</span><span class="nu0">5</span>, <span class="kw2">int</span><span class="br0">&#41;</span></p>
<p>words<span class="br0">&#91;</span><span class="st0">&quot;sam&quot;</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="nu0">2012</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="nu0">5</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="nu0">25</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">&quot;hello&quot;</span><span class="br0">&#93;</span> += <span class="nu0">1</span><br />
words<span class="br0">&#91;</span><span class="st0">&quot;sue&quot;</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="nu0">2012</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="nu0">5</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="nu0">24</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">&quot;today&quot;</span><span class="br0">&#93;</span> += <span class="nu0">1</span></div>
<p>Etc.  It&#8217;s pretty trivial, but it was fun to think about how to do it with a function and to play with some alternatives. You could do it manually with nested lambdas:</p>
<div class="dean_ch" style="white-space: nowrap;">
words = defaultdict<span class="br0">&#40;</span><span class="kw1">lambda</span>: defaultdict<span class="br0">&#40;</span><span class="kw2">int</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
words<span class="br0">&#91;</span><span class="st0">&quot;sam&quot;</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">&quot;hello&quot;</span><span class="br0">&#93;</span> += <span class="nu0">1</span></div>
<p>But that gets messy quickly and isn&#8217;t nearly as much fun.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/05/26/autovivification-in-python-nested-defaultdicts-with-a-specific-final-type/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>The love of pleasure and the love of action</title>
		<link>http://blogs.fluidinfo.com/terry/2012/04/28/the-love-of-pleasure-and-the-love-of-action/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/04/28/the-love-of-pleasure-and-the-love-of-action/#comments</comments>
		<pubDate>Sat, 28 Apr 2012 12:03:31 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[books]]></category>
		<category><![CDATA[Gibbon]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1143</guid>
		<description><![CDATA[There are two very natural propensities which we may distinguish in the most virtuous and liberal dispositions, the love of pleasure and the love of action. If the former is refined by art and learning, improved by the charms of social intercourse, and corrected by a just regard to economy, to health, and to reputation, [...]]]></description>
				<content:encoded><![CDATA[<blockquote><p>There are two very natural propensities which we may distinguish in the most virtuous and liberal dispositions, the love of pleasure and the love of action. If the former is refined by art and learning, improved by the charms of social intercourse, and corrected by a just regard to economy, to health, and to reputation, it is productive of the greatest part of the happiness of private life. The love of action is a principle of a much stronger and more doubtful nature. It often leads to anger, to ambition, and to revenge; but when it is guided by the sense of propriety and benevolence, it becomes the parent of every virtue, and if those virtues are accompanied with equal abilities, a family, a state, or an empire, may be indebted for their safety and prosperity to the undaunted courage of a single man. To the love of pleasure we may therefore ascribe most of the agreeable, to the love of action we may attribute most of the useful and respectable, qualifications. The character in which both the one and the other should be united and harmonized, would seem to constitute the most perfect idea of human nature. The insensible and inactive disposition, which should be supposed alike destitute of both, would be rejected, by the common consent of mankind, as utterly incapable of procuring any happiness to the individual, or any public benefit to the world. But it was not in <em>this</em> world, that the primitive Christians were desirous of making themselves either agreeable or useful.</p></blockquote>
<p>Edward Gibbon <br />From Chapter XV: Progress Of The Christian Religion. <br /><a href="http://ancienthistory.about.com/library/bl/bl_text_gibbon_1_15_5.htm">http://ancienthistory.about.com/library/bl/bl_text_gibbon_1_15_5.htm</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/04/28/the-love-of-pleasure-and-the-love-of-action/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A more melancholy duty is imposed on the historian</title>
		<link>http://blogs.fluidinfo.com/terry/2012/04/08/a-more-melancholy-duty-is-imposed-on-the-historian/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/04/08/a-more-melancholy-duty-is-imposed-on-the-historian/#comments</comments>
		<pubDate>Sun, 08 Apr 2012 15:09:41 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[books]]></category>
		<category><![CDATA[Gibbon]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1141</guid>
		<description><![CDATA[The theologian may indulge the pleasing task of describing Religion as she descended from Heaven, arrayed in her native purity. A more melancholy duty is imposed on the historian. He must discover the inevitable mixture of error and corruption, which she contracted in a long residence upon earth, among a weak and degenerate race of [...]]]></description>
				<content:encoded><![CDATA[<blockquote><p>The theologian may indulge the pleasing task of describing Religion as she descended from Heaven, arrayed in her native purity. A more melancholy duty is imposed on the historian. He must discover the inevitable mixture of error and corruption, which she contracted in a long residence upon earth, among a weak and degenerate race of beings.</p></blockquote>
<p>From Gibbon <a href="http://ancienthistory.about.com/library/bl/bl_text_gibbon_1_15_1.htm">vol 1 ch 15</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/04/08/a-more-melancholy-duty-is-imposed-on-the-historian/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The ruling passion of his soul</title>
		<link>http://blogs.fluidinfo.com/terry/2012/02/28/the-ruling-passion-of-his-soul/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/02/28/the-ruling-passion-of-his-soul/#comments</comments>
		<pubDate>Tue, 28 Feb 2012 06:52:39 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[books]]></category>
		<category><![CDATA[Gibbon]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1139</guid>
		<description><![CDATA[Yet Commodus was not, as he has been represented, a tiger born with an insatiate thirst of human blood, and capable, from his infancy, of the most inhuman actions. Nature had formed him of a weak rather than a wicked disposition. His simplicity and timidity rendered him the slave of his attendants, who gradually corrupted [...]]]></description>
				<content:encoded><![CDATA[<blockquote><p>Yet Commodus was not, as he has been represented, a tiger born with an insatiate thirst of human blood, and capable, from his infancy, of the most inhuman actions. Nature had formed him of a weak rather than a wicked disposition. His simplicity and timidity rendered him the slave of his attendants, who gradually corrupted his mind. His cruelty, which at first obeyed the dictates of others, degenerated into habit, and at length became the ruling passion of his soul.</p></blockquote>
<p>Gibbon.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/02/28/the-ruling-passion-of-his-soul/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>It is almost superfluous to enumerate the unworthy successors of Augustus</title>
		<link>http://blogs.fluidinfo.com/terry/2012/02/07/it-is-almost-superfluous-to-enumerate-the-unworthy-successors-of-augustus/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/02/07/it-is-almost-superfluous-to-enumerate-the-unworthy-successors-of-augustus/#comments</comments>
		<pubDate>Tue, 07 Feb 2012 06:42:57 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[books]]></category>
		<category><![CDATA[Gibbon]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=1131</guid>
		<description><![CDATA[It is almost superfluous to enumerate the unworthy successors of Augustus. Their unparalleled vices, and the splendid theatre on which they were acted, have saved them from oblivion. The dark, unrelenting Tiberius, the furious Caligula, the feeble Claudius, the profligate and cruel Nero, the beastly Vitellius, and the timid, inhuman Domitian, are condemned to everlasting [...]]]></description>
				<content:encoded><![CDATA[<blockquote><p>It is almost superfluous to enumerate the unworthy successors of Augustus. Their unparalleled vices, and the splendid theatre on which they were acted, have saved them from oblivion. The dark, unrelenting Tiberius, the furious Caligula, the feeble Claudius, the profligate and cruel Nero, the beastly Vitellius, and the timid, inhuman Domitian, are condemned to everlasting infamy. During fourscore years (excepting only the short and doubtful respite of Vespasian&#8217;s reign) Rome groaned beneath an unremitting tyranny, which exterminated the ancient families of the republic, and was fatal to almost every virtue and every talent that arose in that unhappy period.</p></blockquote>
<p>From Gibbon, Decline &amp; Fall of the Roman Empire (vol 1).</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/02/07/it-is-almost-superfluous-to-enumerate-the-unworthy-successors-of-augustus/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Women&#8217;s guide to HTTP status codes for dealing with unwanted geek advances</title>
		<link>http://blogs.fluidinfo.com/terry/2012/01/21/womens-guide-to-http-status-codes-for-dealing-with-unwanted-geek-advances/</link>
		<comments>http://blogs.fluidinfo.com/terry/2012/01/21/womens-guide-to-http-status-codes-for-dealing-with-unwanted-geek-advances/#comments</comments>
		<pubDate>Sat, 21 Jan 2012 06:18:14 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[tech]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=920</guid>
		<description><![CDATA[Here&#8217;s a women&#8217;s guide to the most useful HTTP status codes for dealing with unwanted geek advances. Someone should make and sell a deck of these. 301 MOVED PERMANENTLY 303 SEE OTHER 305 USE PROXY 306 RESERVED 307 TEMPORARY REDIRECT 400 BAD REQUEST 401 UNAUTHORIZED 402 PAYMENT REQUIRED 403 FORBIDDEN 404 NOT FOUND 405 METHOD [...]]]></description>
				<content:encoded><![CDATA[<p>Here&#8217;s a women&#8217;s guide to the most useful HTTP status codes for dealing with unwanted geek advances. Someone should make and sell a deck of these.</p>
<p><code><br />
301    MOVED PERMANENTLY<br />
303    SEE OTHER<br />
305    USE PROXY<br />
306    RESERVED<br />
307    TEMPORARY REDIRECT<br />
400    BAD REQUEST<br />
401    UNAUTHORIZED<br />
402    PAYMENT REQUIRED<br />
403    FORBIDDEN<br />
404    NOT FOUND<br />
405    METHOD NOT ALLOWED<br />
406    NOT ACCEPTABLE<br />
407    PROXY AUTHENTICATION REQUIRED<br />
408    REQUEST TIMEOUT<br />
409    CONFLICT<br />
410    GONE<br />
411    LENGTH REQUIRED<br />
412    PRECONDITION FAILED<br />
413    REQUEST ENTITY TOO LARGE<br />
414    REQUEST-URI TOO LONG<br />
415    UNSUPPORTED MEDIA TYPE<br />
416    REQUESTED RANGE NOT SATISFIABLE<br />
417    EXPECTATION FAILED<br />
422    UNPROCESSABLE ENTITY<br />
423    LOCKED<br />
424    FAILED DEPENDENCY<br />
426    UPGRADE REQUIRED<br />
500    INTERNAL SERVER ERROR<br />
501    NOT IMPLEMENTED<br />
502    BAD GATEWAY<br />
503    SERVICE UNAVAILABLE<br />
504    GATEWAY TIMEOUT<br />
507    INSUFFICIENT STORAGE<br />
508    LOOP DETECTED<br />
510    NOT EXTENDED<br />
</code></p>
<p>Source: <a href="http://www.iana.org/assignments/http-status-codes">Hypertext Transfer Protocol (HTTP) Status Code Registry</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2012/01/21/womens-guide-to-http-status-codes-for-dealing-with-unwanted-geek-advances/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Emacs buffer mode histogram</title>
		<link>http://blogs.fluidinfo.com/terry/2011/11/10/emacs-buffer-mode-histogram/</link>
		<comments>http://blogs.fluidinfo.com/terry/2011/11/10/emacs-buffer-mode-histogram/#comments</comments>
		<pubDate>Thu, 10 Nov 2011 11:45:59 +0000</pubDate>
		<dc:creator>terry</dc:creator>
				<category><![CDATA[me]]></category>
		<category><![CDATA[other]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tech]]></category>
		<category><![CDATA[elisp]]></category>
		<category><![CDATA[emacs]]></category>
		<category><![CDATA[lisp]]></category>

		<guid isPermaLink="false">http://blogs.fluidinfo.com/terry/?p=914</guid>
		<description><![CDATA[Tonight I noticed that I had over 200 buffers open in emacs. I&#8217;ve been programming a lot in Python recently, so many of them are in Python mode. I wondered how many Python files I had open, and I counted them by hand. About 90. I then wondered how many were in Javascript mode, in [...]]]></description>
				<content:encoded><![CDATA[<p>Tonight I noticed that I had over 200 buffers open in <a href="http://directory.fsf.org/wiki/Emacs">emacs</a>. I&#8217;ve been programming a lot in <a href="http://www.python.org">Python</a> recently, so many of them are in Python mode. I wondered how many Python files I had open, and I counted them by hand. About 90. I then wondered how many were in Javascript mode, in RST mode, etc. I wondered what a histogram would look like, for me and for others, at times when I&#8217;m programming versus working on documentation, etc.</p>
<p>Because it&#8217;s emacs, it wasn&#8217;t hard to write a function to display a buffer mode histogram.  Here&#8217;s mine:</p>
<pre>
235 buffers open, in 23 distinct modes

91               python +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
47          fundamental +++++++++++++++++++++++++++++++++++++++++++++++
24                  js2 ++++++++++++++++++++++++
21                dired +++++++++++++++++++++
16                 html ++++++++++++++++
 7                 text +++++++
 4                 help ++++
 4           emacs-lisp ++++
 3                   sh +++
 3       makefile-gmake +++
 2          compilation ++
 2                  css ++
 1          Buffer-menu +
 1                 mail +
 1                 grep +
 1      completion-list +
 1                   vm +
 1                  org +
 1               comint +
 1              apropos +
 1                 Info +
 1           vm-summary +
 1      vm-presentation +
</pre>
<p>Tempting as it is, I&#8217;m not going to go on about the heady delights of having a fully programmable editor.  You either already know, or you can just drool in slack-jawed wonder.</p>
<p>Unfortunately I&#8217;m a terrible emacs lisp programmer. I can barely remember a thing each time I use it. But the interpreter is of course just emacs itself and the elisp documentation is in emacs, so it&#8217;s a really fun environment to develop in.  And because emacs lisp has a ton of support for doing things to itself, code that acts on emacs and your own editing session or buffers is often very succinct. See for example the <tt>save-excursion</tt> and <tt>with-output-to-temp-buffer</tt> functions below.</p>
<div class="dean_ch" style="white-space: nowrap;">
<span class="br0">&#40;</span><span class="kw1">defun</span> buffer-mode-histogram <span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="st0">&quot;Display a histogram of emacs buffer modes.&quot;</span><br />
&nbsp; <span class="br0">&#40;</span>interactive<span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#40;</span><span class="kw1">let</span>* <span class="br0">&#40;</span><span class="br0">&#40;</span>totals &#8216;<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#40;</span>buffers <span class="br0">&#40;</span>buffer-<span class="kw1">list</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#40;</span>total-buffers <span class="br0">&#40;</span><span class="kw1">length</span> buffers<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#40;</span>ht <span class="br0">&#40;</span>make-hash-table :<span class="me1">test</span> &#8216;<span class="kw1">equal</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#40;</span>save-excursion<br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="kw1">dolist</span> <span class="br0">&#40;</span>buffer buffers<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span>set-buffer buffer<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="kw1">let</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="br0">&#40;</span>mode-<span class="kw1">name</span> <span class="br0">&#40;</span>symbol-<span class="kw1">name</span> major-mode<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span>puthash mode-<span class="kw1">name</span> <span class="br0">&#40;</span><span class="nu0">1</span>+ <span class="br0">&#40;</span>gethash mode-<span class="kw1">name</span> ht <span class="nu0">0</span><span class="br0">&#41;</span><span class="br0">&#41;</span> ht<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#40;</span>maphash <span class="br0">&#40;</span><span class="kw1">lambda</span> <span class="br0">&#40;</span>key <span class="kw1">value</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#40;</span><span class="kw1">setq</span> totals <span class="br0">&#40;</span><span class="kw1">cons</span> <span class="br0">&#40;</span><span class="kw1">list</span> key <span class="kw1">value</span><span class="br0">&#41;</span> totals<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ht<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#40;</span><span class="kw1">setq</span> totals <span class="br0">&#40;</span>sort totals <span class="br0">&#40;</span><span class="kw1">lambda</span> <span class="br0">&#40;</span>x y<span class="br0">&#41;</span> <span class="br0">&#40;</span>&gt; <span class="br0">&#40;</span><span class="kw1">cadr</span> x<span class="br0">&#41;</span> <span class="br0">&#40;</span><span class="kw1">cadr</span> y<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#40;</span>with-output-to-temp-buffer <span class="st0">&quot;Buffer mode histogram&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="kw1">princ</span> <span class="br0">&#40;</span>format <span class="st0">&quot;%d buffers open, in %d distinct modes<span class="es0">\n</span><span class="es0">\n</span>&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; total-buffers <span class="br0">&#40;</span><span class="kw1">length</span> totals<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="kw1">dolist</span> <span class="br0">&#40;</span>item totals<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="kw1">let</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="br0">&#40;</span>key <span class="br0">&#40;</span><span class="kw1">car</span> item<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#40;</span>count <span class="br0">&#40;</span><span class="kw1">cadr</span> item<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw1">equal</span> <span class="br0">&#40;</span>substring key <span class="nu0">-5</span><span class="br0">&#41;</span> <span class="st0">&quot;-mode&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="kw1">setq</span> key <span class="br0">&#40;</span>substring key <span class="nu0">0</span> <span class="nu0">-5</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="kw1">princ</span> <span class="br0">&#40;</span>format <span class="st0">&quot;%2d %20s %s<span class="es0">\n</span>&quot;</span> count key<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#40;</span>make-string count ?+<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp;</div>
<p>Various things about the formatting could be improved. E.g., not use fixed-width fields for the count and the mode names, and make the + signs indicate more than one buffer mode when there are many.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.fluidinfo.com/terry/2011/11/10/emacs-buffer-mode-histogram/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>
