<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5236867476487043111</id><updated>2026-02-27T19:07:34.467-05:00</updated><category term="python"/><category term="testing"/><category term="Canonical"/><category term="ubuntu"/><category term="selenium"/><category term="Linux"/><category term="audio"/><category term="unittest"/><category term="G6"/><category term="Gource"/><category term="LG"/><category term="NAS"/><category term="QC 3.0"/><category term="UDS"/><category term="backup"/><category term="bandwidth"/><category term="boot"/><category term="chrome"/><category term="conky"/><category term="java"/><category term="latency"/><category term="mobile"/><category term="monitoring"/><category term="music"/><category term="network"/><category term="perl"/><category term="phantomjs"/><category term="rsync"/><category term="splash"/><category term="squeezebox"/><category term="tools"/><category term="visualization"/><category term="web"/><category term="webdriver"/><title type='text'>Corey Goldberg</title><subtitle type='html'>Blog - Technology, Programming</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>174</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-6485344598637497715</id><published>2018-09-07T12:07:00.001-04:00</published><updated>2018-09-07T12:07:16.986-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="chrome"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="selenium"/><category scheme="http://www.blogger.com/atom/ns#" term="webdriver"/><title type='text'>Python - Using Chrome Extensions With Selenium WebDriver</title><content type='html'>&lt;div&gt;
When you launch a Chrome browser from Selenium, a fresh temporary profile is created for the browser session. This means that by default, Chrome extensions are not loaded. To use an extension during a Selenium test, you must install the extension each time you launch a new browser.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;&lt;u style=&quot;background-color: #f5f5f5;&quot;&gt;Example 1: Installing a packed extension&lt;/u&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
A packed extension comes in the form of a single .crx file.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
You can install a packed extension like this:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;script src=&quot;https://gist.github.com/cgoldberg/e2983e0d0db89c6d46d40547bc148415.js&quot;&gt;&lt;/script&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;&lt;u style=&quot;background-color: #f5f5f5;&quot;&gt;Example 2: Installing an unpacked extension&lt;/u&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
An unpacked extension is a directory containing extension code.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
You can install an unpacked extension like this:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;script src=&quot;https://gist.github.com/cgoldberg/e6e2a1ef3535f8d614462cc42c935f33.js&quot;&gt;&lt;/script&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;i&gt;examples tested with: chromium 68, chromedriver 2.41, selenium 3.14&lt;/i&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/6485344598637497715'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/6485344598637497715'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2018/09/python-using-chrome-extensions-with.html' title='Python - Using Chrome Extensions With Selenium WebDriver'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-490591654242800651</id><published>2017-07-06T14:36:00.000-04:00</published><updated>2017-07-06T14:36:28.848-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="G6"/><category scheme="http://www.blogger.com/atom/ns#" term="LG"/><category scheme="http://www.blogger.com/atom/ns#" term="mobile"/><category scheme="http://www.blogger.com/atom/ns#" term="QC 3.0"/><title type='text'>LG G6 - Quick Charge 3.0 Not Working?</title><content type='html'>I finally got my G6 to charge properly with Quick Charge 3.0! &amp;nbsp;Here is what I learned...&lt;br /&gt;
&lt;br /&gt;
The following conditions must be met, or else the G6 steps down power during charging and you will not get a proper QC 3.0 charge:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;battery must be below 80% capacity&lt;/li&gt;
&lt;li&gt;screen must be turned off&lt;/li&gt;
&lt;li&gt;screen saver mode must be disabled&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
The LG G6 has a Snapdragon 821 SoC that supports Qualcomm&#39;s Quick Charge 3.0 technology for rapid charging. However, I haven&#39;t been getting very fast charge times on my G6. &amp;nbsp;I tried the stock charger/cable and even several QC-compatible 3rd party accessories. &amp;nbsp;While the G6 showed a &quot;Fast Charging&quot; notification, it didn&#39;t seem very fast at all. &amp;nbsp;It also seemed to charge faster over wireless (Qi) than with a charger and cable attached!&lt;br /&gt;
&lt;br /&gt;
So I bought a QC-compatible USB multimeter to test the voltage and current the G6 was drawing during a charge. &amp;nbsp;Just as I suspected, I was *not* getting a Quick Charge at all. &amp;nbsp;The adapter that comes with the G6 is supposed to be able to charge at 9V/1.8A, but I couldn&#39;t see it draw anything beyond a 5V/0.5A. &amp;nbsp;After much frustration and troubleshooting, I realized I had my G6 configured to run Screen Saver mode during charging. &amp;nbsp;After disabling the Screen Saver, I finally saw it charge at the full 9V/1.8A!&lt;br /&gt;
&lt;br /&gt;
Also note:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;when the &quot;Fast Charging&quot; notification is displayed, it just means a QC compatible charger/cable was detected, not that it&#39;s actually charging at QC 3.0 speed.&lt;/li&gt;
&lt;li&gt;Screen Saver setting is located at: Settings &amp;gt; Display &amp;gt; Screen saver&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/490591654242800651/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/490591654242800651' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/490591654242800651'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/490591654242800651'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2017/07/lg-g6-quick-charge-tips.html' title='LG G6 - Quick Charge 3.0 Not Working?'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-4205867855184977536</id><published>2017-02-02T20:02:00.000-05:00</published><updated>2017-03-02T08:19:22.252-05:00</updated><title type='text'>Batch Convert Images from PNG to JPEG</title><content type='html'>This post briefly shows how to recursively scan a directory tree for PNG images and convert them to JPEG format. &amp;nbsp;To achieve this, we use the `find` shell command and ImageMagick&#39;s `convert` utility. &amp;nbsp;We use GNU Parallel to speed up processing time by executing conversions in parallel. &amp;nbsp;This should make efficient use of all CPU cores.&lt;br /&gt;
&lt;br /&gt;
First, install ImageMagick and GNU Parallel. &amp;nbsp;For example, on Debian/Ubuntu you would run:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;b style=&quot;background-color: #fff2cc;&quot;&gt;$ sudo apt-get install imagemagick&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;b style=&quot;background-color: #fff2cc;&quot;&gt;$ sudo apt-get install parallel&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Once these are &amp;nbsp;installed, `cd` to the root of the directory tree containing your images, and run:&lt;br /&gt;
&lt;span style=&quot;background-color: #fff2cc;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;background-color: orange;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;b style=&quot;background-color: #fff2cc;&quot;&gt;$ find . -iname &quot;*.png&quot; | parallel convert -quality 95% {} {.}.jpg&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;background-color: white;&quot;&gt;This shell pipeline will:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
* recursively search under the current directory&lt;br /&gt;
* match files that have the [case-insensitive] `.png` extension&lt;br /&gt;
* convert each PNG image to JPEG format at 95% quality&lt;br /&gt;
* save the new image with the file extension changed to `.jpg`&lt;br /&gt;
&lt;br /&gt;
Once converted, you can delete the original PNG images with:&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;b style=&quot;background-color: #ffe599;&quot;&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;b style=&quot;background-color: #fff2cc;&quot;&gt;$ find . -iname &quot;*.png&quot; | parallel rm {}&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/4205867855184977536/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/4205867855184977536' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/4205867855184977536'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/4205867855184977536'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2017/02/batch-convert-images-from-png-to-jpeg.html' title='Batch Convert Images from PNG to JPEG'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-1133442959623426206</id><published>2016-03-09T12:48:00.001-05:00</published><updated>2016-03-09T12:48:36.934-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="conky"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="monitoring"/><category scheme="http://www.blogger.com/atom/ns#" term="ubuntu"/><title type='text'>What The Heck Happened To Conky? (I&#39;m rolling back to 1.9)</title><content type='html'>Conky is a stalwart on my desktops. &amp;nbsp;All my system info is a glance away in that great little monitoring tool.&lt;br /&gt;
&lt;br /&gt;
However, Conky 1.10 is extremely buggy and has made incompatible changes to it&#39;s config syntax since the 1.9 release.&lt;br /&gt;
&lt;br /&gt;
The changes from conky 1.9-&amp;gt;1.10 made for an incompatible disaster and conky is now totally borked in the current Ubuntu 16.04 repos. Bugs have been reported... but, while maintainers and devs figure out issues, I need my old conky back!&lt;br /&gt;
&lt;br /&gt;
So I just reverted to the old `conky-std` 1.9.0 in Ubuntu 16.04, and I&#39;m back in business for now.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Here is how I did it:&lt;br /&gt;
&lt;br /&gt;
First, let&#39;s see which conky version is in installed on my 16.04 (Xenial) machine:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: red; font-family: Courier New, Courier, monospace;&quot;&gt;$ conky -v | head -n 1 | cut -d&quot; &quot; -f 2&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red; font-family: Courier New, Courier, monospace;&quot;&gt;1.10.1&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
so, I removed this borked conky and purged it&#39;s configurations and leftover dependencies:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: red; font-family: Courier New, Courier, monospace;&quot;&gt;$ sudo apt-get remove --purge conky-std &amp;amp;&amp;amp; sudo apt-get autoremove&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
I wanted conky version 1.9, which is not in the Ubuntu Xenial repos. &amp;nbsp;So I snagged the package that shipped with Trusty:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: red; font-family: Courier New, Courier, monospace;&quot;&gt;$ wget http://security.ubuntu.com/ubuntu/pool/universe/c/conky/conky-std_1.9.0-4_amd64.deb&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
once I had the .deb package downloaded, I installed it with `gdebi` rather `dpkg` so it would also install the necessary dependencies:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: red; font-family: Courier New, Courier, monospace;&quot;&gt;$ sudo gdebi conky-std_1.9.0-4_amd64.deb&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
we should now be back in business. &amp;nbsp;Let&#39;s check the version that is installed:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: red; font-family: Courier New, Courier, monospace;&quot;&gt;$ conky -v | head -n 1 | cut -d&quot; &quot; -f 2&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red; font-family: Courier New, Courier, monospace;&quot;&gt;1.9.0&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
score!&lt;br /&gt;
&lt;br /&gt;
One final task is pin the package at the 1.9.0 version so future package updates don&#39;t revert us back up to the bad version:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: red; font-family: Courier New, Courier, monospace;&quot;&gt;$ sudo apt-mark hold conky-std&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
done!&lt;br /&gt;
&lt;br /&gt;
I&#39;m back running conky 1.9 like a boss for now. &amp;nbsp;I hope the upstream situation gets sorted soon.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TLDR:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: red; font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;$ sudo apt-get remove --purge conky-std &amp;amp;&amp;amp; sudo apt-get autoremove&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red; font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;$ wget http://security.ubuntu.com/ubuntu/pool/universe/c/conky/conky-std_1.9.0-4_amd64.deb&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red; font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;$ sudo gdebi conky-std_1.9.0-4_amd64.deb&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red; font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;$ sudo apt-mark hold conky-std&lt;/span&gt;&lt;br /&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
happy hacking.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/1133442959623426206/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/1133442959623426206' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/1133442959623426206'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/1133442959623426206'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2016/03/what-heck-happened-to-conky-im-rolling-back.html' title='What The Heck Happened To Conky? (I&#39;m rolling back to 1.9)'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total><georss:featurename>Boston, MA, USA</georss:featurename><georss:point>42.3600825 -71.05888010000001</georss:point><georss:box>42.1722125 -71.3816036 42.547952499999994 -70.736156600000015</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-8340905772649758051</id><published>2015-11-29T16:08:00.000-05:00</published><updated>2015-11-29T16:08:29.990-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="boot"/><category scheme="http://www.blogger.com/atom/ns#" term="splash"/><category scheme="http://www.blogger.com/atom/ns#" term="ubuntu"/><title type='text'>Ubuntu - Changing the bootsplash screen</title><content type='html'>Plymouth is the default bootsplash app used in Ubuntu... It displays the Ubuntu logo while booting.&lt;br /&gt;
&lt;br /&gt;
For as long as I can remember, every time I install the proprietary NVidia display driver, my bootsplash gets borked. &amp;nbsp;It just displays a black screen, then a few flickers of the Ubuntu logo or NVidia logo, then the desktop is presented.&lt;br /&gt;
&lt;br /&gt;
It seems that simply resetting the default plymouth theme fixes this issue:&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; &amp;nbsp; $ sudo update-alternatives --config default.plymouth&lt;br /&gt;
&lt;br /&gt;
apply changes:&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; &amp;nbsp; $ sudo update-initramfs -u&lt;br /&gt;
&lt;br /&gt;
Here is how to switch the bootsplash to [my favorite] the solar theme:&lt;br /&gt;
&lt;br /&gt;
$ sudo apt-get install plymouth-theme-solar&lt;br /&gt;
$ sudo update-alternatives --config default.plymouth&lt;br /&gt;
$ sudo update-initramfs -u&lt;br /&gt;
&lt;br /&gt;
then reboot!&lt;br /&gt;
&lt;br /&gt;
more info:&lt;br /&gt;
&lt;a href=&quot;https://wiki.ubuntu.com/Plymouth&quot;&gt;https://wiki.ubuntu.com/Plymouth&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/8340905772649758051/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/8340905772649758051' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/8340905772649758051'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/8340905772649758051'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2015/11/ubuntu-changing-bootsplash-screen.html' title='Ubuntu - Changing the bootsplash screen'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-2770259297645587889</id><published>2015-04-27T11:55:00.000-04:00</published><updated>2015-04-27T11:55:07.442-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bandwidth"/><category scheme="http://www.blogger.com/atom/ns#" term="latency"/><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><category scheme="http://www.blogger.com/atom/ns#" term="network"/><category scheme="http://www.blogger.com/atom/ns#" term="testing"/><title type='text'>Linux - Simulating Degraded Network Conditions</title><content type='html'>When testing an application or service, it can be very useful to simulate degraded network conditions.  This allows you to see how they might perform for real users.  Testing on a controlled LAN is not always realistic, since the LAN has much different network properties than a real user would experience over a WAN.
&lt;br /&gt;&lt;br /&gt;
I will give examples using 2 tools that alter a network interface&#39;s properties.  The tools are: &lt;a href=&quot;http://www.linuxfoundation.org/collaborate/workgroups/networking/netem&quot;&gt;NetEm&lt;/a&gt; and &lt;a href=&quot;http://lartc.org/wondershaper/&quot;&gt;Wondershaper&lt;/a&gt;.
&lt;br /&gt;&lt;br /&gt;
&lt;b&gt;NetEm&lt;/b&gt; is an enhancement of the Linux traffic control facilities that allows you to add delay, packet loss, duplication, corruption, reordering, and more to outgoing packets from a specified network interface.  NetEm is controlled by the command line tool &lt;b&gt;tc&lt;/b&gt;, which is part of the iproute2 package and included in most Linux distros.
&lt;br /&gt;&lt;br /&gt;
&lt;b&gt;Wondershaper&lt;/b&gt; is a traffic shaping script that allows you to throttle network transfers on a specified network interface.  This is useful for testing latency when bandwidth is limited.
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;i&gt;Examples:&lt;/i&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;b&gt;tc/NetEm&lt;/b&gt;:&lt;br /&gt;
add fixed 250ms delay to all outgoing packets on Ethernet interface eth1:&lt;br /&gt;
&amp;nbsp;&amp;nbsp&lt;tt&gt;$ sudo tc qdisc add dev eth1 root netem delay 250ms&lt;/tt&gt;&lt;br /&gt;
turn it off:&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&lt;tt&gt;$ sudo tc qdisc del dev eth1 root netem&lt;/tt&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;b&gt;Wondershaper&lt;/b&gt;:&lt;br /&gt;
limit bandwidth of eth1 interface to 256kbps upload and 128kbps download:&lt;br /&gt;
&amp;nbsp;&amp;nbsp&lt;tt&gt;$ sudo wondershaper eth1 256 128&lt;/tt&gt;&lt;br /&gt;
turn it off:&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&lt;tt&gt;$ sudo wondershaper clear eth1&lt;/tt&gt;&lt;br /&gt;
&lt;br /&gt;&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/2770259297645587889/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/2770259297645587889' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/2770259297645587889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/2770259297645587889'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2015/04/linux-simulating-degraded-network.html' title='Linux - Simulating Degraded Network Conditions'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-4958613971121462244</id><published>2014-08-31T16:19:00.000-04:00</published><updated>2014-08-31T16:19:58.589-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="backup"/><category scheme="http://www.blogger.com/atom/ns#" term="NAS"/><category scheme="http://www.blogger.com/atom/ns#" term="rsync"/><title type='text'>rsync for file sync and backup - storing my peronal media</title><content type='html'>&lt;p&gt;
You do backup all of your important files, right?
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;TLDR: Dropbox + NAS + Local Storage + rsync = my backup strategy.&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
I have a lot of data that is important to me.  Mostly: documents, code, images, music, and videos.  I currently have about 1.2TB stored.  This is an overview of how I store and backup my data.
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
About &lt;a href=&quot;http://rsync.samba.org/&quot;&gt;rsync&lt;/a&gt;:
&lt;/p&gt;

&lt;p&gt;
rsync is a great file synchronization and file transfer program.  Since being announced in 1996, it has become a standard Linux utility, included in all popular Linux distributions (and other Unix-like systems).
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
I have a Dropbox with an 11GB quota.  This suffices for storing my documents, code, and images.  It nicely syncs them to to all my computers.  I can easily access this data from outside my LAN on any device I own.
&lt;/p&gt;

&lt;p&gt;
Then I have a NAS as my main storage within my LAN.  It is a 2-bay enclosure, with 2 2TB hard drives inside that are in a RAID configuration.  I can easily access all of my media from any computer or device on my home network.  I also have a secondary NAS attached to my network, purely for backup/disaster situations.
&lt;/p&gt;

&lt;p&gt;
To complete the storage picture... on my main workstation, I have a 2TB hard disk mounted as a secondary data drive.
&lt;/p&gt;

&lt;p&gt;
Storage Overview:
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dropbox&lt;/li&gt;
&lt;li&gt;2 NAS servers mounted as drives on my main workstation (using SMB)&lt;/li&gt;
&lt;li&gt;Main workstation with 2TB Hard disk mounted internally&lt;/li&gt;
&lt;li&gt;Gigabit Ethernet LAN&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
So, that&#39;s the given hardware setup.  I use a shell script to actually run my backup.  It is initiated from the main workstation (scheduled from cron).
&lt;/p&gt;

&lt;p&gt;
The script follows this workflow:
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local Dropbox gets rsync&#39;ed to my primary NAS&lt;/li&gt;
&lt;li&gt;primary NAS gets rsync&#39;ed to my local workstation&#39;s data drive&lt;/li&gt;
&lt;li&gt;primary NAS gets rsync&#39;ed to my backup NAS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
I have ~20k files (~1.2TB) in my archive.  The first time running the backup job was slow (several hours), but subsequent backups are extremely fast.  This is due to the rsync algorithm, delta encoding, and compression.  If not many files changed since my last backup/sync, differential backups take only a few seconds or minutes.
&lt;/p&gt;

&lt;p&gt;
Besides not having a offsite copy for disaster recovery, I like this system, and it makes me sleep well knowing my data is safe and recoverable.
&lt;/p&gt;

&lt;p&gt;
Any flaws?
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/4958613971121462244/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/4958613971121462244' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/4958613971121462244'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/4958613971121462244'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2014/08/rsync-for-file-sync-and-backup.html' title='rsync for file sync and backup - storing my peronal media'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-692610443445149338</id><published>2014-01-11T11:02:00.000-05:00</published><updated>2014-01-11T11:02:16.645-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><title type='text'>Python - Fixing My Photo Library Dates (Exif Metadata)</title><content type='html'>&lt;p&gt;
I have a large image library of photos I&#39;ve taken or downloaded over the years.  They are from various cameras and sources, many with missing or incomplete &lt;a href=&quot;http://en.wikipedia.org/wiki/Exchangeable_image_file_format&quot;&gt;Exif&lt;/a&gt; metadata.
&lt;/p&gt;

&lt;p&gt;
This is problematic because some image viewing programs and galleries use metadata to sort images into timelines.  For example, when I view my library in &lt;a href=&quot;https://www.dropbox.com/features/photos&quot;&gt;Dropbox Photos&lt;/a&gt; timeline, images with missing Exif date tags are not displayed.
&lt;/p&gt;

&lt;p&gt;
To remedy this, I wrote a Python script to fix the dates in my photo library.  It uses &lt;a href=&quot;https://wiki.gnome.org/Projects/gexiv2&quot;&gt;gexiv2&lt;/a&gt;, which is a wrapper around the &lt;a href=&quot;http://www.exiv2.org/&quot;&gt;Exiv2&lt;/a&gt; photo metadata library.
&lt;/p&gt;

&lt;p&gt;
The scipt will:
&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;recursively scan a directory tree for jpg and png files&lt;/li&gt;
  &lt;li&gt;get each file&#39;s creation time&lt;/li&gt;
  &lt;li&gt;convert it to a timestamp string&lt;/li&gt;
  &lt;li&gt;set Exif.Image.DateTime tag to timestamp&lt;/li&gt;
  &lt;li&gt;set Exif.Photo.DateTimeDigitized tag to timestamp&lt;/li&gt;
  &lt;li&gt;set Exif.Photo.DateTimeOriginal tag to timestamp&lt;/li&gt;
  &lt;li&gt;save file with modified metadata&lt;/li&gt;
  &lt;li&gt;set file access and modified times to file creation time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
* Note: it does modifications in-place.
&lt;/p&gt;

&lt;p&gt;
The &lt;a href=&quot;https://gist.github.com/cgoldberg/8372494&quot;&gt;Code&lt;/a&gt;:
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/cgoldberg/8372494.js&quot;&gt;&lt;/script&gt;



</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/692610443445149338/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/692610443445149338' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/692610443445149338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/692610443445149338'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2014/01/python-fixing-my-photo-library-dates.html' title='Python - Fixing My Photo Library Dates (Exif Metadata)'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total><georss:featurename>Boston, MA, USA</georss:featurename><georss:point>42.3584308 -71.0597732</georss:point><georss:box>42.3584308 -71.0597732 42.3584308 -71.0597732</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-2919863038363642082</id><published>2013-11-17T14:12:00.000-05:00</published><updated>2013-11-17T14:12:00.892-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Canonical"/><category scheme="http://www.blogger.com/atom/ns#" term="Gource"/><category scheme="http://www.blogger.com/atom/ns#" term="ubuntu"/><category scheme="http://www.blogger.com/atom/ns#" term="visualization"/><title type='text'>Gource Visualization of Ubuntu Touch Core Apps Development</title><content type='html'>&lt;p&gt;
TLDR: I made a cool version control visualization of all the &lt;a href=&quot;https://launchpad.net/ubuntu-phone-coreapps/&quot;&gt;Ubuntu Touch Core Apps&lt;/a&gt;. 
&lt;/p&gt;

&lt;p&gt;
The video: &lt;a href=&quot;https://www.youtube.com/watch?v=nAmKAgRS0tw&quot;&gt;https://www.youtube.com/watch?v=nAmKAgRS0tw&lt;/a&gt;
&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;//www.youtube.com/embed/nAmKAgRS0tw&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;
* Warning: abrasive techno music&lt;br /&gt;
* To be watched in HD, preferably at maximum volume
&lt;p&gt;

&lt;hr /&gt;

&lt;p&gt;
Making &lt;a href=&quot;https://code.google.com/p/gource/&quot;&gt;Gource&lt;/a&gt; visualizations of complex software projects is awesome.  I love seeing a VCS commit log come to life as blooming trees and swarming workers.  Normally, I do a visualization video of a single repository. But in this case, I used a bash script to create a visualization of multiple source code repositories.  I wanted to see the progress of the entire stack of Ubuntu Touch Core Apps (17 projects).  &lt;a href=&quot;https://launchpad.net/ubuntu-phone-coreapps/&quot;&gt;Ubuntu Touch Core Apps&lt;/a&gt; is an umbrella project for all [17] of the core apps that are available in Ubuntu on mobile devices
&lt;/p&gt;

&lt;p&gt;
The Ubuntu Touch Core Apps:
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dropping Letters&lt;/li&gt;
&lt;li&gt;Evernote Online Accounts plugin&lt;/li&gt;
&lt;li&gt;QtDeclarative bindings for the Grilo media scanner&lt;/li&gt;
&lt;li&gt;Stock Ticker App&lt;/li&gt;
&lt;li&gt;Sudoku App&lt;/li&gt;
&lt;li&gt;Ubuntu Calculator App&lt;/li&gt;
&lt;li&gt;Ubuntu Calendar App&lt;/li&gt;
&lt;li&gt;Ubuntu Clock App&lt;/li&gt;
&lt;li&gt;Ubuntu Document Viewer App&lt;/li&gt;
&lt;li&gt;Ubuntu E-mail App&lt;/li&gt;
&lt;li&gt;Ubuntu Facebook App&lt;/li&gt;
&lt;li&gt;Ubuntu File Manager App&lt;/li&gt;
&lt;li&gt;Ubuntu Music App&lt;/li&gt;
&lt;li&gt;Ubuntu Phone Commons&lt;/li&gt;
&lt;li&gt;Ubuntu RSS Feed Reader App&lt;/li&gt;
&lt;li&gt;Ubuntu Terminal App&lt;/li&gt;
&lt;li&gt;Ubuntu Weather App&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Making the visualization:
&lt;/p&gt;

&lt;p&gt;
Assuming you have a bunch of source code repositories already branched/cloned locally, here is a general version of the script to generate visualization videos of multiple projects/repositories: &lt;a href=&quot;https://gist.github.com/cgoldberg/7488521&quot;&gt;https://gist.github.com/cgoldberg/7488521&lt;/a&gt;
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/cgoldberg/7488521.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;
The script I used to create the Ubuntu Touch Core Apps video: &lt;a href=&quot;https://gist.github.com/cgoldberg/7516510&quot;&gt;https://gist.github.com/cgoldberg/7516510&lt;/a&gt;
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/cgoldberg/7516510.js&quot;&gt;&lt;/script&gt;

</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/2919863038363642082/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/2919863038363642082' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/2919863038363642082'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/2919863038363642082'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2013/11/gource-visualization-of-ubuntu-touch.html' title='Gource Visualization of Ubuntu Touch Core Apps Development'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-1423650560786645012</id><published>2013-10-22T10:38:00.000-04:00</published><updated>2013-10-22T10:38:00.827-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="ubuntu"/><title type='text'>deadsnakes - Using Old Versions of Python on Ubuntu</title><content type='html'>&lt;p&gt;How do you install an older version of Python on Ubuntu without building it yourself?&lt;/p&gt;
    
&lt;p&gt;The Python packages in the official Ubuntu archives generally don&#39;t go back all that far, but people might still need to develop and test against these old Python interpreters.  Felix Krull maintains a PPA (package archive) of older Python versions that are easy to install on Ubuntu.&lt;/p&gt;

&lt;p&gt;
see: &lt;a href=&quot;https://launchpad.net/~fkrull/+archive/deadsnakes&quot;&gt;https://launchpad.net/~fkrull/+archive/deadsnakes&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Currently supported Python releases: &lt;strong&gt;2.4, 2.5, 2.6, 2.7, 3.1, 3.2, 3.3&lt;/strong&gt;
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
&lt;strong&gt;Instructions:&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
Add the deadsnakes repository:
&lt;/p&gt;

&lt;pre style=&quot;font-size:13px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;$ sudo add-apt-repository ppa:fkrull/deadsnakes&lt;/pre&gt;

&lt;p&gt;
Run Update:
&lt;/p&gt;

&lt;pre style=&quot;font-size:13px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;$ sudo apt-get update&lt;/pre&gt;

&lt;p&gt;
Install an older version of Python:
&lt;/p&gt;

&lt;pre style=&quot;font-size:13px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;$ sudo apt-get install python2.6 python2.6-dev&lt;/pre&gt;

&lt;hr /&gt;
</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/1423650560786645012/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/1423650560786645012' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/1423650560786645012'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/1423650560786645012'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2013/10/deadsnakes-using-old-versions-of-python.html' title='deadsnakes - Using Old Versions of Python on Ubuntu'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-633607069233803984</id><published>2013-06-22T15:44:00.000-04:00</published><updated>2013-06-22T15:44:52.329-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="audio"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><title type='text'>Generating Audio Spectrograms in Python</title><content type='html'>&lt;p&gt;
A &lt;strong&gt;spectrogram&lt;/strong&gt; is a visual representation of the spectrum of frequencies in a sound sample.
&lt;/p&gt;

&lt;p&gt;
more info: &lt;a href=&quot;http://en.wikipedia.org/wiki/Spectrogram&quot;&gt;wikipedia spectrogram&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Spectrogram code in Python, using Matplotlib:&lt;/br&gt;
(source on &lt;a href=&quot;https://github.com/cgoldberg/audioscripts/blob/master/visualization/spectrogram_matplotlib.py&quot;&gt;GitHub&lt;/a&gt;)
&lt;/p&gt;

&lt;pre style=&quot;font-size:11px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
&quot;&quot;&quot;Generate a Spectrogram image for a given WAV audio sample.

A spectrogram, or sonogram, is a visual representation of the spectrum
of frequencies in a sound.  Horizontal axis represents time, Vertical axis
represents frequency, and color represents amplitude.
&quot;&quot;&quot;


import os
import wave

import pylab


def graph_spectrogram(wav_file):
    sound_info, frame_rate = get_wav_info(wav_file)
    pylab.figure(num=None, figsize=(19, 12))
    pylab.subplot(111)
    pylab.title(&#39;spectrogram of %r&#39; % wav_file)
    pylab.specgram(sound_info, Fs=frame_rate)
    pylab.savefig(&#39;spectrogram.png&#39;)


def get_wav_info(wav_file):
    wav = wave.open(wav_file, &#39;r&#39;)
    frames = wav.readframes(-1)
    sound_info = pylab.fromstring(frames, &#39;Int16&#39;)
    frame_rate = wav.getframerate()
    wav.close()
    return sound_info, frame_rate


if __name__ == &#39;__main__&#39;:
    wav_file = &#39;sample.wav&#39;
    graph_spectrogram(wav_file)
&lt;/pre&gt;

&lt;p&gt;
Spectrogram code in Python, using &lt;a href=&quot;https://code.google.com/p/timeside/&quot;&gt;timeside&lt;/a&gt;:&lt;br /&gt;
(source on &lt;a href=&quot;https://github.com/cgoldberg/audioscripts/blob/master/visualization/spectrogram_timeside.py&quot;&gt;GitHub&lt;/a&gt;)
&lt;/p&gt;

&lt;pre style=&quot;font-size:11px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
&quot;&quot;&quot;Generate a Spectrogram image for a given audio sample.

Compatible with several audio formats: wav, flac, mp3, etc.
Requires: https://code.google.com/p/timeside/

A spectrogram, or sonogram, is a visual representation of the spectrum
of frequencies in a sound.  Horizontal axis represents time, Vertical axis
represents frequency, and color represents amplitude.
&quot;&quot;&quot;


import timeside


audio_file = &#39;sample.wav&#39;

decoder = timeside.decoder.FileDecoder(audio_file)
grapher = timeside.grapher.Spectrogram(width=1920, height=1080)
(decoder | grapher).run()
grapher.render(&#39;spectrogram.png&#39;)
&lt;/pre&gt;

&lt;p&gt;
happy audio hacking.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/633607069233803984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/633607069233803984' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/633607069233803984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/633607069233803984'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2013/06/generating-audio-spectrograms-in-python.html' title='Generating Audio Spectrograms in Python'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total><georss:featurename>Boston, MA 02116, USA</georss:featurename><georss:point>42.353068 -71.076518800000031</georss:point><georss:box>42.306118 -71.157199800000029 42.400018 -70.995837800000032</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-5687740226732278197</id><published>2013-06-10T09:27:00.000-04:00</published><updated>2013-06-10T09:27:58.329-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="testing"/><title type='text'>Python - concurrencytest: Running Concurrent Tests</title><content type='html'>&lt;p&gt;
Add parallel testing to your unit test framework.
&lt;/p&gt;

&lt;p&gt;
In my &lt;a href=&quot;http://coreygoldberg.blogspot.com/2013/06/python-nose-running-concurrent-tests.html&quot;&gt;previous post&lt;/a&gt;, I described running concurrent tests using &lt;a href=&quot;http://nose.readthedocs.org/&quot;&gt;nose&lt;/a&gt; as a loader and runner.
&lt;/p&gt;

&lt;p&gt;
On a similar note, let&#39;s look at building concurrency into your own test framework built on Python&#39;s &lt;a href=&quot;http://docs.python.org/3/library/unittest.html&quot;&gt;unittest&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Have a look at this module: &lt;a href=&quot;https://github.com/cgoldberg/concurrencytest&quot;&gt;concurrencytest&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;i&gt;(Thanks to bits and concepts taken from &lt;a href=&quot;http://testtools.readthedocs.org&quot;&gt;testtools&lt;/a&gt; and &lt;a href=&quot;http://bazaar.canonical.com&quot;&gt;bzrlib&lt;/a&gt;)&lt;/i&gt;
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
&lt;b&gt;An Example:&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
Say you have a &#39;TestSuite&#39; of tests loaded.  You could run them with the standard &#39;TextTestRunner&#39; like this:
&lt;/p&gt;

&lt;pre style=&quot;font-size:12px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
runner = unittest.TextTestRunner()
runner.run(suite)
&lt;/pre&gt;

&lt;p&gt;
That would run the tests in your suite sequentially in a single process.
&lt;/p&gt;

&lt;p&gt;
By adding the &lt;a href=&quot;https://github.com/cgoldberg/concurrencytest&quot;&gt;concurrencytest&lt;/a&gt; module, you can use a &#39;ConcurrentTestSuite&#39; instead, by adding:
&lt;/p&gt;

&lt;pre style=&quot;font-size:12px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
from concurrencytest import ConcurrentTestSuite, fork_for_tests

concurrent_suite = ConcurrentTestSuite(suite, fork_for_tests(4))
runner.run(concurrent_suite)
&lt;/pre&gt;

&lt;p&gt;
That would run the same tests split across 4 processes (workers).
&lt;/p&gt;

&lt;p&gt;
Note: this relies on &#39;os.fork()&#39; which only works on Unix systems.
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
There&#39;s no way to understand this better than looking at some contrived examples!
&lt;/p&gt;

&lt;p&gt;
This first example is totally unrealistic, but shows off concurrency perfectly.  The test cases it loads each sleep for 0.5 seconds and then exit.
&lt;/p&gt;

&lt;p&gt;
The Code:
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/cgoldberg/5744573.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;
Output:
&lt;/p&gt;

&lt;pre style=&quot;font-size:12px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
Loaded 50 test cases...

Run tests sequentially:
..................................................
----------------------------------------------------------------------
Ran 50 tests in 25.031s

OK

Run same tests across 50 processes:
..................................................
----------------------------------------------------------------------
Ran 50 tests in 0.525s

OK
&lt;/pre&gt;

&lt;p&gt;
nice!
&lt;/p&gt;

&lt;p&gt;
Now another example that shows concurrency with CPU-bound test cases.  The test cases it loads each calculate fibonacci of 31 (recursively!) and then exit.  We can see how it performs on my 8-core machine (Core2 i7 quad, hyperthreaded).
&lt;/p&gt;

&lt;p&gt;
The Code:
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/cgoldberg/5744574.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;
Output:
&lt;/p&gt;

&lt;pre style=&quot;font-size:12px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
Loaded 50 test cases...

Run tests sequentially:
..................................................
----------------------------------------------------------------------
Ran 50 tests in 21.941s

OK

Run same tests with 2 processes:
..................................................
----------------------------------------------------------------------
Ran 50 tests in 11.081s

OK

Run same tests with 4 processes:
..................................................
----------------------------------------------------------------------
Ran 50 tests in 5.862s

OK

Run same tests with 8 processes:
..................................................
----------------------------------------------------------------------
Ran 50 tests in 4.743s

OK
&lt;/pre&gt;

&lt;hr /&gt;

&lt;p&gt;
happy hacking.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/5687740226732278197/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/5687740226732278197' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/5687740226732278197'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/5687740226732278197'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2013/06/python-concurrencytest-running.html' title='Python - concurrencytest: Running Concurrent Tests'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total><georss:featurename>Boston, MA 02116, USA</georss:featurename><georss:point>42.353068 -71.076518800000031</georss:point><georss:box>42.306118 -71.157199800000029 42.400018 -70.995837800000032</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-6513481021993200742</id><published>2013-06-09T19:17:00.000-04:00</published><updated>2013-06-09T19:17:01.138-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="testing"/><category scheme="http://www.blogger.com/atom/ns#" term="unittest"/><title type='text'>Python - Nose: Running Concurrent Tests</title><content type='html'>&lt;p&gt;
&lt;b&gt;TLDR&lt;/b&gt;:&lt;br /&gt;
To enable multiprocessing with &lt;i&gt;N&lt;/i&gt; workers,&lt;br /&gt;
run &lt;a href=&quot;https://nose.readthedocs.org&quot;&gt;nose&lt;/a&gt; with:
&lt;p&gt;

&lt;pre style=&quot;font-size:12px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
$ nosetests --processes=N
&lt;/pre&gt;

&lt;hr /&gt;

&lt;p&gt;
When writing tests in Python, I start with TestCase&#39;s derived from &lt;a href=&quot;http://docs.python.org/2/library/unittest.html#test-cases&quot;&gt;unittest.TestCase&lt;/a&gt;, and standard &lt;a href=&quot;http://docs.python.org/2/library/unittest.html#test-discovery&quot;&gt;test discovery&lt;/a&gt;.  When I need more complex test discovery/loading or output reports, I often use &lt;a href=&quot;https://nose.readthedocs.org&quot;&gt;nose&lt;/a&gt; and its assortment of plugins as my test loader/runner.
&lt;/p&gt;

&lt;p&gt;
One nice feature of nose is the &lt;a href=&quot;http://nose.readthedocs.org/en/latest/plugins/multiprocess.html&quot;&gt;multiprocess plugin&lt;/a&gt;.  It allows you to run your tests suites concurrently rather than sequentially, spread across a number of worker processes.  Running tests in parallel like this can potentially give you a large speedup in your test run times.
&lt;/p&gt;

&lt;p&gt;
from the nose multiprocess docs:
&lt;/p&gt;

&lt;blockquote&gt;
&lt;i&gt;&quot;You can parallelize a test run across a configurable number of worker processes. While this can speed up CPU-bound test runs, it is mainly useful for IO-bound tests that spend most of their time waiting for data to arrive from someplace else and can benefit from parallelization.&quot;&lt;/i&gt;
&lt;/blockquote&gt;

&lt;p&gt;
Normally, you run tests from nose with:
&lt;/p&gt;

&lt;pre style=&quot;font-size:12px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
$ nosetests
&lt;/pre&gt;

&lt;p&gt;
To run the same tests split across 4 processes (workers), you would just do:
&lt;/p&gt;

&lt;pre style=&quot;font-size:12px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
$ nosetests --processes=4
&lt;/pre&gt;

&lt;p&gt;
Assuming your tests are properly isolated, everything should run normally, and you can benefit from a speedup on a multiprocessor machine.
&lt;/p&gt;

&lt;p&gt;
However, &lt;b&gt;Beware&lt;/b&gt;.
&lt;/p&gt;

&lt;blockquote&gt;
&lt;i&gt;&quot;Not all test suites will benefit from, or even operate correctly using, this plugin. For example, CPU-bound tests will run more slowly if you don&#39;t have multiple processors.&quot;&lt;/i&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;i&gt;&quot;But the biggest issue you will face is probably concurrency. Unless you have kept your tests as religiously pure unit tests, with no side-effects, no ordering issues, and no external dependencies, chances are you will experience odd, intermittent and unexplainable failures and errors when using this plugin. This doesn&#39;t necessarily mean the plugin is broken; it may mean that your test suite is not safe for concurrency.&quot;&lt;/i&gt;
&lt;/blockquote&gt;
</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/6513481021993200742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/6513481021993200742' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/6513481021993200742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/6513481021993200742'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2013/06/python-nose-running-concurrent-tests.html' title='Python - Nose: Running Concurrent Tests'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total><georss:featurename>Boston, MA 02116, USA</georss:featurename><georss:point>42.353068 -71.076518800000031</georss:point><georss:box>42.306118 -71.157199800000029 42.400018 -70.995837800000032</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-5327255616854453425</id><published>2013-04-01T10:19:00.001-04:00</published><updated>2013-04-01T10:19:36.509-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="audio"/><category scheme="http://www.blogger.com/atom/ns#" term="music"/><category scheme="http://www.blogger.com/atom/ns#" term="squeezebox"/><title type='text'>Squeezelite - Headless Squeezebox Emulator</title><content type='html'>&lt;p&gt;
Use Squeezebox, without buying a Squeezebox...
&lt;/p&gt;

&lt;p&gt;
Recently, Logitech discontinued most &lt;a href=&quot;http://en.wikipedia.org/wiki/Squeezebox_(network_music_player)&quot;&gt;Squeezebox streaming music players&lt;/a&gt;.  However, the media server is Open Source, so it looks like some form of &lt;a href=&quot;http://en.wikipedia.org/wiki/Logitech_Media_Server&quot;&gt;Logitech Media Server&lt;/a&gt; (LMS) will live on, no matter what Logitech eventually does with it.
&lt;/p&gt;

&lt;p&gt;
I&#39;ve been a user of Squeezebox network music player since it was released by SlimDevices (SliMP3/SlimServer), and throughout the transfer to Logitech.  I&#39;ve owned 3 Squeezebox models over the years... currently enjoying the Squeezebox Touch, with music streamed from Logitech Media Server.
&lt;/p&gt;

&lt;p&gt;
It works flawlessly for streaming my own music collection (FLAC/MP3/etc), and streaming radio (Pandora/Slacker/Sirius/etc), to my HiFi.  I use the digital (S/PDIF) outputs, and sometimes the DAC/analog (RCA) outputs.  
&lt;/p&gt;

&lt;p&gt;
Now... with the release of &lt;a href=&quot;https://code.google.com/p/squeezelite/&quot;&gt;Squeezelite&lt;/a&gt;, you can build your own Squeezebox, or use an existing computer/laptop with digital output as a Squeezebox.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Squeezelite&lt;/strong&gt; is a cross-platform, headless, LMS client that supports playback synchronization, gapless playback, direct streaming, and playback at various sampling rates.  It runs on Linux using ALSA audio output and other platforms using PortAudio. It is aimed at supporting high quality audio.
&lt;/p&gt;

&lt;p&gt;
I gave Squeezelite 1.0 a try on Ubuntu 12.04, with S/PDIF optical output to my DAC.  It worked like a charm!
&lt;/p&gt;

&lt;p&gt;
Squeezelite info:&lt;br /&gt;
&lt;a href=&quot;https://code.google.com/p/squeezelite/&quot;&gt;https://code.google.com/p/squeezelite/&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Squeezelite download (precompiled binaries for x86/amd64/arm):&lt;br /&gt;
&lt;a href=&quot;https://code.google.com/p/squeezelite/downloads/list&quot;&gt;https://code.google.com/p/squeezelite/downloads/list&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Enjoy the music.
&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/5327255616854453425/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/5327255616854453425' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/5327255616854453425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/5327255616854453425'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2013/04/squeezelite-headless-squeezebox-emulator.html' title='Squeezelite - Headless Squeezebox Emulator'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-2367345880276503522</id><published>2013-03-28T18:35:00.000-04:00</published><updated>2013-04-02T12:16:44.224-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><title type='text'>Python - Re-tag FLAC Audio Files (Update Metadata)</title><content type='html'>&lt;p&gt;
I had a bunch of &lt;a href=&quot;http://en.wikipedia.org/wiki/FLAC&quot;&gt;FLAC&lt;/a&gt; (.flac) audio files together in a directory.  They are from various sources and their metadata (tags) were somewhat incomplete or incorrect.
&lt;/p&gt;

&lt;p&gt;
I managed to manually get all of the files standardized in &quot;%Artist% - %Title%.flac&quot; file name format.  However, What I really wanted was to clear their metadata and just save &quot;Artist&quot; and &quot;Title&quot; tags, pulled from file names.
&lt;/p&gt;

&lt;p&gt;
I looked at a few audio tagging tools in the Ubuntu repos, and came up short finding something simple that covered my needs.  (I use &lt;a href=&quot;http://pwp.netcabo.pt/paol/tagtool/&quot;&gt;Audio Tag Tool&lt;/a&gt; for MP3&#39;s, but it has no FLAC file support.)
&lt;/p&gt;

&lt;p&gt;
So, I figured the easiest way to get this done was a quick Python script.
&lt;/p&gt;

&lt;p&gt;
I grabbed &lt;a href=&quot;https://code.google.com/p/mutagen/&quot;&gt;Mutagen&lt;/a&gt;, a Python module to handle audio metadata with FLAC support.
&lt;/p&gt;

&lt;p&gt;
This is essentially the task I was looking to do:
&lt;/p&gt;

&lt;pre style=&quot;font-size:12px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
#!/usr/bin/env python

import glob
import os
from mutagen.flac import FLAC

for filename in glob.glob(&#39;*.flac&#39;):
    artist, title = os.path.splitext(filename)[0].split(&#39; - &#39;, 1)
    audio = FLAC(filename)
    audio.clear()
    audio[&#39;artist&#39;] = artist
    audio[&#39;title&#39;] = title
    audio.save()
&lt;/pre&gt;

&lt;p&gt;
It iterates over .flac files in the current directory, clearing the metadata and rewriting only the artist/title tags based on each file name.
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
I created a repository with a slightly more full-featured version, used to re-tag single FLAC files:&lt;br /&gt; &lt;a href=&quot;https://github.com/cgoldberg/audioscripts/blob/master/flac_retag.py&quot;&gt;https://github.com/cgoldberg/audioscripts/blob/master/flac_retag.py&lt;/a&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/2367345880276503522/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/2367345880276503522' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/2367345880276503522'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/2367345880276503522'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2013/03/python-re-tag-flac-audio-files-update.html' title='Python - Re-tag FLAC Audio Files (Update Metadata)'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-5651956091791965206</id><published>2013-01-28T17:09:00.000-05:00</published><updated>2013-01-28T17:09:31.867-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><title type='text'>Python - verify a PNG file and get image dimensions</title><content type='html'>&lt;p&gt;
useful snippet for getting .png image dimensions without using an external imaging library.
&lt;/p&gt;

&lt;pre style=&quot;font-size:12px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
#!/usr/bin/env python

import struct


def get_image_info(data):
    if is_png(data):
        w, h = struct.unpack(&#39;&gt;LL&#39;, data[16:24])
        width = int(w)
        height = int(h)
    else:
        raise Exception(&#39;not a png image&#39;)
    return width, height


def is_png(data):
    return (data[:8] == &#39;\211PNG\r\n\032\n&#39;and (data[12:16] == &#39;IHDR&#39;))


if __name__ == &#39;__main__&#39;:
    with open(&#39;foo.png&#39;, &#39;rb&#39;) as f:
        data = f.read()

    print is_png(data)
    print get_image_info(data)
&lt;/pre&gt;

&lt;p&gt;
/headnods:&lt;br /&gt;
&lt;a href=&quot;https://code.google.com/p/bfg-pages/source/browse/trunk/pages/getimageinfo.py&quot;&gt;getimageinfo.py source&lt;/a&gt;, &lt;a href=&quot;http://en.wikipedia.org/wiki/Portable_Network_Graphics&quot;&gt;Portable_Network_Graphics (Wikipedia)&lt;/a&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/5651956091791965206/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/5651956091791965206' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/5651956091791965206'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/5651956091791965206'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2013/01/python-verify-png-file-and-get-image.html' title='Python - verify a PNG file and get image dimensions'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-1827847815105061327</id><published>2013-01-23T18:06:00.000-05:00</published><updated>2013-01-23T18:06:49.946-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="testing"/><category scheme="http://www.blogger.com/atom/ns#" term="unittest"/><title type='text'>Python Unit Testing Tutorial (PyMOTW unittest update)</title><content type='html'>
&lt;p&gt;
&lt;b&gt;tl;dr&lt;/b&gt;: an update to PyMOTW for `unittest` in Python 3: &lt;strong&gt;&lt;a href=&quot;http://cgoldberg.github.com/python-unittest-tutorial/&quot;&gt;Python Unit Testing Tutorial&lt;/a&gt;&lt;/strong&gt;.
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
When I was learning programming in &lt;a href=&quot;http://www.python.org/&quot;&gt;Python&lt;/a&gt;, Doug Hellmann&#39;s &lt;a href=&quot;http://www.doughellmann.com/PyMOTW/&quot;&gt;&#39;PyMOTW&#39; (Python Module Of The Week)&lt;/a&gt; blog-series was one of the best resources to learn Python&#39;s &lt;a href=&quot;http://docs.python.org/3.3/library/&quot;&gt;standard library&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
His series later culminated in the book: &lt;a href=&quot;http://www.doughellmann.com/books/byexample/&quot;&gt;&#39;The Python Standard Library By Example&#39;&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
The &lt;a href=&quot;http://www.doughellmann.com/PyMOTW/unittest/&quot;&gt;PyMOTW entry on `unittest`&lt;/a&gt; was a great introduction to unit testing in Python.  Since the PyMOTW version is getting quite outdated, I updated the `unittest` module entry.
&lt;/p&gt;

&lt;p&gt;
This new version includes some edits and updates to the text, and all code and examples have been updated to reflect Python 3.3.
&lt;/p&gt;

&lt;p&gt;
Have a look at my updated Python 3.3 version:&lt;br /&gt;
&lt;strong&gt;&lt;a href=&quot;http://cgoldberg.github.com/python-unittest-tutorial/&quot;&gt;&#39;Python Unit Testing Tutorial&#39;&lt;/a&gt;&lt;/strong&gt;
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
&lt;i&gt;license:
&lt;a href=&quot;http://creativecommons.org/licenses/by-nc-sa/3.0/us/&quot;&gt;Creative Commons Attribution, Non-commercial, Share-alike 3.0&lt;/a&gt;&lt;/i&gt;
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
say no to bugs...
&lt;/p&gt;

&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu5lURtHahuaxBaiq2PofrId4AlkxXIVY_yxfVbMfvaCf-WuzD07uo2swY8eZ04h_qCGUU8DAz4EloV966RXwiEjkXit0tpX9AzygL-YlzBglk_fu2tEhWNZwG8gynznrp2YFA7GQFE3Y/s1600/ladybug_for_blogpost.jpg&quot; imageanchor=&quot;1&quot; style=&quot;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;300&quot; width=&quot;355&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu5lURtHahuaxBaiq2PofrId4AlkxXIVY_yxfVbMfvaCf-WuzD07uo2swY8eZ04h_qCGUU8DAz4EloV966RXwiEjkXit0tpX9AzygL-YlzBglk_fu2tEhWNZwG8gynznrp2YFA7GQFE3Y/s400/ladybug_for_blogpost.jpg&quot; /&gt;&lt;/a&gt;
</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/1827847815105061327/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/1827847815105061327' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/1827847815105061327'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/1827847815105061327'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2013/01/python-unit-testing-tutorial-pymotw.html' title='Python Unit Testing Tutorial (PyMOTW unittest update)'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu5lURtHahuaxBaiq2PofrId4AlkxXIVY_yxfVbMfvaCf-WuzD07uo2swY8eZ04h_qCGUU8DAz4EloV966RXwiEjkXit0tpX9AzygL-YlzBglk_fu2tEhWNZwG8gynznrp2YFA7GQFE3Y/s72-c/ladybug_for_blogpost.jpg" height="72" width="72"/><thr:total>2</thr:total><georss:featurename>Boston, MA 02116, USA</georss:featurename><georss:point>42.353068 -71.076518800000031</georss:point><georss:box>42.306118 -71.157199800000029 42.400018 -70.995837800000032</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-401558891799222288</id><published>2013-01-14T09:42:00.001-05:00</published><updated>2013-01-14T09:58:43.975-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><title type='text'>Python - &quot;The Matrix&quot; in your terminal</title><content type='html'>&lt;p&gt;
Linux console program in Python, that scrolls binary numbers vertically in your terminal. Inspired by the movie: The Matrix
&lt;/p&gt;

&lt;p&gt;
a screenshot:
&lt;/p&gt;

&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzeZ-Lt4tMZIC9oEUHghTQR_EUcTNqqgJBg97gvEhalE5_WnLRjSHiM2-iKTn54L86me7MvFjz4-xvJE-xo-XVJbUE1KQWRTdNzJnSfGMLPUvwX8dQHs_Hhl2IUIrQ5BFvTh0W2I0q30Q/s1600/matrix_code.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;329&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzeZ-Lt4tMZIC9oEUHghTQR_EUcTNqqgJBg97gvEhalE5_WnLRjSHiM2-iKTn54L86me7MvFjz4-xvJE-xo-XVJbUE1KQWRTdNzJnSfGMLPUvwX8dQHs_Hhl2IUIrQ5BFvTh0W2I0q30Q/s400/matrix_code.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;

&lt;p&gt;
in action:
&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;http://www.youtube.com/embed/96crI0NCbPg&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;
the Code:
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/4530348.js&quot;&gt;&lt;/script&gt;

&lt;a href=&quot;https://gist.github.com/4530348&quot;&gt;https://gist.github.com/4530348&lt;/a&gt;

&lt;hr /&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/401558891799222288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/401558891799222288' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/401558891799222288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/401558891799222288'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2013/01/python-matrix-in-your-terminal.html' title='Python - &quot;The Matrix&quot; in your terminal'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzeZ-Lt4tMZIC9oEUHghTQR_EUcTNqqgJBg97gvEhalE5_WnLRjSHiM2-iKTn54L86me7MvFjz4-xvJE-xo-XVJbUE1KQWRTdNzJnSfGMLPUvwX8dQHs_Hhl2IUIrQ5BFvTh0W2I0q30Q/s72-c/matrix_code.png" height="72" width="72"/><thr:total>3</thr:total><georss:featurename>Boston, MA, USA</georss:featurename><georss:point>42.3584308 -71.0597732</georss:point><georss:box>42.170560800000004 -71.38249669999999 42.5463008 -70.7370497</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-1511106015880966704</id><published>2013-01-06T16:35:00.000-05:00</published><updated>2013-01-06T16:35:56.432-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="phantomjs"/><category scheme="http://www.blogger.com/atom/ns#" term="python"/><category scheme="http://www.blogger.com/atom/ns#" term="selenium"/><title type='text'>Python Testing - PhantomJS with Selenium WebDriver</title><content type='html'>&lt;p&gt;
&lt;a href=&quot;http://phantomjs.org/&quot;&gt;PhantomJS&lt;/a&gt; is a headless &lt;a href=&quot;http://en.wikipedia.org/wiki/WebKit&quot;&gt;WebKit&lt;/a&gt; with JavaScript API.  It can be used for headless website testing.  
&lt;/p&gt;

&lt;p&gt;
PhantomJS has a lot of different uses.  The interesting bit for me is to use PhantomJS as a lighter-weight replacement for a browser when running web acceptance tests.  This enables faster testing, without a display or the overhead of full-browser startup/shutdown.
&lt;/p&gt;

&lt;p&gt;
I write my web automation using &lt;a href=&quot;http://seleniumhq.org/&quot;&gt;Selenium WebDriver&lt;/a&gt;, in &lt;a href=&quot;http://pypi.python.org/pypi/selenium&quot;&gt;Python&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
In future versions of PhantomJS, the &lt;a href=&quot;https://github.com/detro/ghostdriver&quot;&gt;GhostDriver&lt;/a&gt; component will be included.
&lt;/p&gt;

&lt;p&gt;
GhostDriver is a pure JavaScript implementation of the &lt;a href=&quot;http://code.google.com/p/selenium/wiki/JsonWireProtocol&quot;&gt;WebDriver Wire Protocol&lt;/a&gt; for PhantomJS. It&#39;s a Remote WebDriver that uses PhantomJS as back-end.
&lt;/p&gt;

&lt;p&gt;
So, Ghostdriver is the bridge we need to use Selenium WebDriver with Phantom.JS.
&lt;/p&gt;

&lt;p&gt;
Since it is not available in the current PhantomJS release, you can try it yourself by compiling a special version of PhantomJS:
&lt;/p&gt;

&lt;p&gt;
It wes pretty trvial to setup on Ubuntu (12.04):
&lt;/p&gt;

&lt;pre style=&quot;font-size:10px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
$ sudo apt-get install build-essential chrpath git-core libssl-dev libfontconfig1-dev
$ git clone git://github.com/ariya/phantomjs.git
$ cd phantomjs
$ git checkout 1.8
$ ./build.sh
$ git remote add detro https://github.com/detro/phantomjs.git
$ git fetch detro &amp;&amp; git checkout -b detro-ghostdriver-dev remotes/detro/ghostdriver-dev
$ ./build.sh
&lt;/pre&gt;

&lt;p&gt;
Then grab the `phantomjs` binary it produced (look inside `phantomjs/bin`).  This is a self-contained executable, it can be moved to a different directory or another machine.  Make sure it is located somewhere on your PATH, or declare it&#39;s location when creating your PhantomJS driver like the example below.
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;for these examples, `phantomjs` binary is located in same directory as test script.
&lt;p&gt;
&lt;b&gt;Example: Python Using PhantomJS and Selenium WebDriver&lt;/b&gt;.
&lt;/p&gt;

&lt;pre style=&quot;font-size:14px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
#!/usr/bin/env python

driver = webdriver.PhantomJS(&#39;./phantomjs&#39;)
# do webdriver stuff here
driver.quit()
&lt;/pre&gt;

&lt;p&gt;
&lt;b&gt;Example: Python Unit Test Using PhantomJS and Selenium WebDriver&lt;/b&gt;.
&lt;/p&gt; 

&lt;pre style=&quot;font-size:14px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
#!/usr/bin/env python

import unittest
from selenium import webdriver


class TestUbuntuHomepage(unittest.TestCase):
    
    def setUp(self):
        self.driver = webdriver.PhantomJS(&#39;./phantomjs&#39;)
        
    def testTitle(self):
        self.driver.get(&#39;http://www.ubuntu.com/&#39;)
        self.assertIn(&#39;Ubuntu&#39;, self.driver.title)
        
    def tearDown(self):
        self.driver.quit()


if __name__ == &#39;__main__&#39;:
    unittest.main(verbosity=2)
&lt;/pre&gt;

&lt;hr /&gt;

&lt;p&gt;
resources:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://phantomjs.org/build.html&quot;&gt;http://phantomjs.org/build.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/detro/ghostdriver&quot;&gt;https://github.com/detro/ghostdriver&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://goldb.org/sst/selenium2_api_docs/html/selenium.webdriver.phantomjs.webdriver.WebDriver-class.html&quot;&gt;Selenium WebDriver Python API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/1511106015880966704/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/1511106015880966704' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/1511106015880966704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/1511106015880966704'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2013/01/python-testing-phantomjs-with-selenium.html' title='Python Testing - PhantomJS with Selenium WebDriver'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-1214829955091587108</id><published>2012-10-13T13:59:00.001-04:00</published><updated>2012-10-13T13:59:49.024-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="selenium"/><category scheme="http://www.blogger.com/atom/ns#" term="testing"/><category scheme="http://www.blogger.com/atom/ns#" term="tools"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>What is the Best Way To Learn Selenium?</title><content type='html'>&lt;p&gt;
(a small rant on asking answerable questions)
&lt;/p&gt;

&lt;p&gt;
&lt;img src=&quot;http://imgs.xkcd.com/comics/duty_calls.png&quot; alt=&quot;xkcd comic 386&quot;&gt;&lt;/img&gt;
&lt;img src=&quot;http://seleniumhq.org/images/big-logo.png&quot;&gt;&lt;/img&gt;
&lt;/p&gt;

&lt;p&gt;
At a certain online QA/Testing Forum I regularly visit, the &lt;a href=&quot;http://www.sqaforums.com/postlist.php?Board=Selenium&quot;&gt;Selenium forum&lt;/a&gt;
is one of the most active.
&lt;/p&gt;

&lt;p&gt;
I continuously see newbie questions like: &quot;How I do I learn Selenium?&quot;, 
with little or no other context given.
&lt;/p&gt;

&lt;p&gt;
Then, helpful responders chime in with tangentially related answers.  That&#39;s about as good as you can do, based on the lack of information given in the original post.  This happens often and wastes significant brain activity in the responders, while generally giving no value to the OP or forum community.
&lt;/p&gt;

&lt;p&gt;
So first... a clarification:&lt;br /&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://seleniumhq.org/&quot;&gt;Selenium WebDriver&lt;/a&gt; is a system for automating browser interaction. It is accessed via programming library/API.  If you want to use it without touching code, it is not possible (record/replay/export-madness via IDE aside).  You can abstract away most of the programming aspects with higher level frameworks, but at that point you are no longer really dealing with Selenium anymore.
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
Here was my response to a &lt;a href=&quot;.http://www.sqaforums.com/showflat.php?Number=717978&quot;&gt;recent forum example&lt;/a&gt;:
&lt;/p&gt;

&lt;p&gt;
Question asked:&lt;br /&gt;
&lt;em&gt;&quot;Best way to start learning Selenium - I would like to know what is the best practice to start with Selenium for non programmers.  Is there any books or ebooks?&quot;&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
The responses compelled me to chime in with a rant:
&lt;/p&gt;

&lt;hr /&gt;

&lt;em&gt;
&lt;p&gt;
To anyone on this thread looking for the &quot;Best way to start learning Selenium&quot;:
&lt;/p&gt;

&lt;p&gt;
Think of it this way:
&lt;/p&gt;

&lt;p&gt;
Selenium (WebDriver) provides language bindings and a similar API for several programming languages. It gives you a programming library that can manipulate a browser and web elements/controls.
&lt;/p&gt;

&lt;p&gt;
You can&#39;t &quot;learn&quot; a library for a programming language if you don&#39;t know the language itself (syntax, idioms, etc).
&lt;/p&gt;

&lt;p&gt;
So the question shouldn&#39;t be: &quot;How do I learn Selenium?&quot;, with no other information or context given.
&lt;/p&gt;

&lt;p&gt;
Selenium should be used with either: the supported language that you are most comfortable with, or the language of the system under test you are working against.
&lt;/p&gt;

&lt;p&gt;
If you are looking for information, be specific about what you want to learn. General understanding of the components that make up Selenium is great, but really, using the API&#39;s from any language is the best way to learn. If you specify a programming language along with your question, at least answers can be directed towards frameworks, code samples, documentation, tutorials, etc, that are *relevant* to you.
&lt;/p&gt;

&lt;p&gt;
Diving into the main Selenium docs can be confusing at first, as it covers things from a general level... mixing in code samples from various languages.
&lt;/p&gt;

&lt;p&gt;
So, my question to you: Which programming language are you going to use Selenium with? If you define that, someone can surely help point you in the right direction.
&lt;/p&gt;

&lt;p&gt;
How are your programming skills? Which language do you want to learn in? If you know several, switching is trivial down the line, as the API&#39;s are similar between languages.
&lt;/p&gt;

&lt;p&gt;
If you don&#39;t have skills in any Selenium supported programming language (java, c#, python, ruby, php, etc), then learn one of those before you even touch Selenium. If you have a language in mind, please phrase your question using that in context.
&lt;/p&gt;

&lt;p&gt;
happy hacking,
&lt;/p&gt;

&lt;p&gt;
-Corey
&lt;/p&gt;

&lt;p&gt;
p.s. now go RTFM: &lt;a href=&quot;http://seleniumhq.org/docs/&quot;&gt;http://seleniumhq.org/docs/&lt;/a&gt;
&lt;/p&gt;
&lt;/em&gt;

&lt;hr /&gt;

rant over.
</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/1214829955091587108/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/1214829955091587108' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/1214829955091587108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/1214829955091587108'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2012/10/what-is-best-way-to-learn-selenium.html' title='What is the Best Way To Learn Selenium?'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-6586650934818400424</id><published>2012-06-17T10:39:00.000-04:00</published><updated>2012-06-17T10:39:29.618-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><title type='text'>Python Timer Class - Context Manager for Timing Code Blocks</title><content type='html'>&lt;p&gt;
Here is a handy &lt;strong&gt;Python Timer class&lt;/strong&gt;.  It creates a context manager object, used for timing a block of code.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFxHNSZm3WmQsAI4AB_-5rnyceZaQ9nrAWQB0293qlSuAHIJSsoMI7-S4MRyTs5lOhlUFX5Bhb0SHoH317IIQ92ICzZ92GGfXq-YbI1TtKu5GIr4p9RBDHW1c-Rg_Gs-h6CMoTglp9VeU/s1600/timer.jpg&quot; imageanchor=&quot;1&quot; style=&quot;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;140&quot; width=&quot;200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFxHNSZm3WmQsAI4AB_-5rnyceZaQ9nrAWQB0293qlSuAHIJSsoMI7-S4MRyTs5lOhlUFX5Bhb0SHoH317IIQ92ICzZ92GGfXq-YbI1TtKu5GIr4p9RBDHW1c-Rg_Gs-h6CMoTglp9VeU/s200/timer.jpg&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;pre style=&quot;font-size:13px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
from timeit import default_timer


class Timer(object):
    def __init__(self, verbose=False):
        self.verbose = verbose
        self.timer = default_timer
        
    def __enter__(self):
        self.start = self.timer()
        return self
        
    def __exit__(self, *args):
        end = self.timer()
        self.elapsed_secs = end - self.start
        self.elapsed = self.elapsed_secs * 1000  # millisecs
        if self.verbose:
            print &#39;elapsed time: %f ms&#39; % self.elapsed
&lt;/pre&gt;

&lt;p&gt;        
To use the Timer (&lt;a href=&quot;http://docs.python.org/reference/datamodel.html#context-managers&quot;&gt;context manager&lt;/a&gt; object), invoke it using Python&#39;s &lt;a href=&quot;http://docs.python.org/reference/compound_stmts.html#with&quot;&gt;`with`&lt;/a&gt; statement.  The duration of the context (code inside your `with` block) will be timed.  It uses the &lt;a href=&quot;http://docs.python.org/library/timeit.html#timeit.default_timer&quot;&gt;appropriate timer&lt;/a&gt; for your platform, via the `timeit` module.
&lt;/p&gt;

&lt;p&gt;
Timer is used like this:
&lt;/p&gt;

&lt;pre style=&quot;font-size:13px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
with Timer() as target:
    # block of code goes here.
    # result (elapsed time) is stored in `target` properties.
&lt;/pre&gt;

&lt;p&gt;
Example script:&lt;br /&gt;
timing a web request (HTTP GET), using the &lt;a href=&quot;http://docs.python-requests.org/&quot;&gt;`requests`&lt;/a&gt; module.
&lt;/p&gt;

&lt;pre style=&quot;font-size:13px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
#!/usr/bin/env python

import requests
from timer import Timer

url = &#39;https://github.com/timeline.json&#39;

with Timer() as t:
    r = requests.get(url)
    
print &#39;fetched %r in %.2f millisecs&#39; % (url, t.elapsed)
&lt;/pre&gt;

&lt;p&gt;
Output:
&lt;/p&gt;

&lt;pre style=&quot;font-size:13px;border:0.5px #999999 solid;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:4px;&quot;&gt;
fetched &#39;https://github.com/timeline.json&#39; in 458.76 millisecs
&lt;/pre&gt; 

&lt;hr /&gt;

&lt;p&gt;    
&lt;a href=&quot;https://gist.github.com/2942781&quot;&gt;`timer.py`&lt;/a&gt; in GitHub Gist form, with more examples:
&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/2942781.js?file=timer.py&quot;&gt;&lt;/script&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/6586650934818400424/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/6586650934818400424' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/6586650934818400424'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/6586650934818400424'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2012/06/python-timer-class-context-manager-for.html' title='Python Timer Class - Context Manager for Timing Code Blocks'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFxHNSZm3WmQsAI4AB_-5rnyceZaQ9nrAWQB0293qlSuAHIJSsoMI7-S4MRyTs5lOhlUFX5Bhb0SHoH317IIQ92ICzZ92GGfXq-YbI1TtKu5GIr4p9RBDHW1c-Rg_Gs-h6CMoTglp9VeU/s72-c/timer.jpg" height="72" width="72"/><thr:total>8</thr:total><georss:featurename>Boston, MA, USA</georss:featurename><georss:point>42.3584308 -71.0597732</georss:point><georss:box>42.2645643 -71.217701699999992 42.4522973 -70.9018447</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-3084867101215711230</id><published>2012-06-16T09:25:00.000-04:00</published><updated>2012-06-16T09:25:37.619-04:00</updated><title type='text'>My Evolving Droids</title><content type='html'>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggp9WjlXFO15X5kpqslId_hezfAB3LaXdguvZiXx-gyh_mhU5Yqo2tP5RV_emmwZwyorJeGJnhPt3pxfpvxJGA4nBPEYP1g0QR4cay4v6hPE-8SJKGgAwDLc2uvJNhhpKhH6O205LDBdI/s1600/Motorola-Verizon_Droid_DroidX_DroidRazr.jpg&quot; imageanchor=&quot;1&quot; style=&quot;&quot;&gt;&lt;img border=&quot;0&quot; width=&quot;500&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggp9WjlXFO15X5kpqslId_hezfAB3LaXdguvZiXx-gyh_mhU5Yqo2tP5RV_emmwZwyorJeGJnhPt3pxfpvxJGA4nBPEYP1g0QR4cay4v6hPE-8SJKGgAwDLc2uvJNhhpKhH6O205LDBdI/s400/Motorola-Verizon_Droid_DroidX_DroidRazr.jpg&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;em&gt;(Droid -&gt; Droid X -&gt; Droid Razr) [stock pics]&lt;/em&gt;

&lt;p&gt;
In the past few years, mobile devices have turned into amazing little pocket computers.  I&#39;m blown away by the hardware (ARM) and software (Android) evolution.
&lt;/p&gt;

&lt;p&gt;
For smartphone wireless service, I &lt;em&gt;was&lt;/em&gt; a Verizon customer (USA) for the past decade.  I&#39;ve owned the flagship Droid series devices by Motorola since their first launch (currently running on the 4G LTE network).  It started with a clunky original &lt;strong&gt;Droid&lt;/strong&gt;, followed by a &lt;strong&gt;Droid X&lt;/strong&gt;, and then the sleek &lt;strong&gt;Droid Razr&lt;/strong&gt;... every year another generation of better, faster devices.
&lt;/p&gt;


&lt;strong&gt;Device Specs:&lt;/strong&gt;

&lt;p&gt;
&lt;strong&gt;Motorola Droid&lt;/strong&gt; - Nov 2009
&lt;ul&gt;
  &lt;li&gt;OS: Android v2.0&lt;/li&gt;
  &lt;li&gt;CPU: 600 MHz ARM Cortex-A8 (TI OMAP3430, 65nm)&lt;/li&gt;
  &lt;li&gt;RAM: 256 MB&lt;/li&gt;
  &lt;li&gt;Internal Storage: 512 MB&lt;/li&gt;
  &lt;li&gt;Screen Size: 3.7 inch&lt;/li&gt;
  &lt;li&gt;Resolution: 480 x 854&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Motorola Droid X&lt;/strong&gt; - July 2010
&lt;ul&gt;
  &lt;li&gt;OS: Android v2.1&lt;/li&gt;
  &lt;li&gt;CPU: 1 GHz ARM Cortex-A8 (TI OMAP3630, 45nm)&lt;/li&gt;
  &lt;li&gt;RAM: 512 MB&lt;/li&gt;
  &lt;li&gt;Internal Storage: 8 GB&lt;/li&gt;
  &lt;li&gt;Screen Size: 4.3 inch&lt;/li&gt;
  &lt;li&gt;Resolution: 480 x 854&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Motorola Droid Razr&lt;/strong&gt; - Nov 2011
&lt;ul&gt;
  &lt;li&gt;OS: Android v2.3&lt;/li&gt;
  &lt;li&gt;CPU: 1.2 GHz dual-core ARM Cortex-A9 (TI OMAP4430, 45nm)&lt;/li&gt;
  &lt;li&gt;RAM: 1 GB&lt;/li&gt;
  &lt;li&gt;Internal Storage: 16 GB&lt;/li&gt;
  &lt;li&gt;Screen Size: 4.3 inch&lt;/li&gt;
  &lt;li&gt;Resolution: 540 x 960&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;
However, my life with the Moto Droids is coming to an end.  I just signed up with a new wireless carrier and pre-ordered a new Sammy GS3!:
&lt;/p&gt;

&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiozScUPEO_gWPq8LN5M8BrAuGpe4RI_AbqVsVUR9NJw7cw7S_wBC6AVVPqWTyt2UbJenPXs3iu9FSYCDdudMVwJUBAw_scU9_NQFnrfW_4TB8Txwshs45ogC2NZl7n4Wy_xH_EGRJuTB8/s1600/Samsung_Galaxy_S3_pebble_blue.jpg&quot; imageanchor=&quot;1&quot; style=&quot;&quot;&gt;&lt;img border=&quot;0&quot; width=&quot;380&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiozScUPEO_gWPq8LN5M8BrAuGpe4RI_AbqVsVUR9NJw7cw7S_wBC6AVVPqWTyt2UbJenPXs3iu9FSYCDdudMVwJUBAw_scU9_NQFnrfW_4TB8Txwshs45ogC2NZl7n4Wy_xH_EGRJuTB8/s400/Samsung_Galaxy_S3_pebble_blue.jpg&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;em&gt;(Samsung Galaxy S3) [stock pic]&lt;/em&gt;

&lt;p&gt;
&lt;strong&gt;Samsung Galaxy S3&lt;/strong&gt; - June 2012
&lt;ul&gt;
  &lt;li&gt;OS: Android v4.0&lt;/li&gt;
  &lt;li&gt;CPU: 1.5 GHz dual-core Krait (Qualcomm Snapdragon S4 MSM8960, 28nm)&lt;/li&gt;
  &lt;li&gt;RAM: 2 GB&lt;/li&gt;
  &lt;li&gt;Internal Storage: 16 GB&lt;/li&gt;
  &lt;li&gt;Screen Size: 4.8 inch&lt;/li&gt;
  &lt;li&gt;Resolution: 720 x 1280&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/3084867101215711230/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/3084867101215711230' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/3084867101215711230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/3084867101215711230'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2012/06/my-evolving-droids.html' title='My Evolving Droids'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggp9WjlXFO15X5kpqslId_hezfAB3LaXdguvZiXx-gyh_mhU5Yqo2tP5RV_emmwZwyorJeGJnhPt3pxfpvxJGA4nBPEYP1g0QR4cay4v6hPE-8SJKGgAwDLc2uvJNhhpKhH6O205LDBdI/s72-c/Motorola-Verizon_Droid_DroidX_DroidRazr.jpg" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Boston, MA, USA</georss:featurename><georss:point>42.3584308 -71.0597732</georss:point><georss:box>42.2645643 -71.217701699999992 42.4522973 -70.9018447</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-4956647243781888696</id><published>2012-06-14T12:40:00.000-04:00</published><updated>2012-06-14T12:40:48.035-04:00</updated><title type='text'>Home Audio Setup</title><content type='html'>&lt;p&gt;
I work from my apartment and listen to music or talk-radio nearly 24-hours a day (even at soft volume while I sleep).  My home audio setup is pretty important to me.
&lt;/p&gt;

&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglqhX1IU1svOhHMIGKEvgizmOOTa-_EAfS2pZ7OdlC23kYOqLdldZYShf1SN4E5sv6l4fBIsVJda3mmoVFScm62EpHUfsTkUCmvZUCVoNyFzsDxB2x3KzzJ_pOjh6K0eJ96itB4Mz0gT8/s1600/streaming_music.jpg&quot; imageanchor=&quot;1&quot; style=&quot;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;160&quot; width=&quot;196&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglqhX1IU1svOhHMIGKEvgizmOOTa-_EAfS2pZ7OdlC23kYOqLdldZYShf1SN4E5sv6l4fBIsVJda3mmoVFScm62EpHUfsTkUCmvZUCVoNyFzsDxB2x3KzzJ_pOjh6K0eJ96itB4Mz0gT8/s200/streaming_music.jpg&quot; /&gt;&lt;/a&gt;

&lt;p&gt;
Here is a description of the current rig for home listening:
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
&lt;strong&gt;The Gear:&lt;/strong&gt;
&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;Media Server (Ubuntu, Logitech Media Server)&lt;/li&gt;
    &lt;li&gt;Touchscreen UI (Android, Squeezebox Controller)&lt;/li&gt;
    &lt;li&gt;Wifi Music Player (Squeezebox Touch)&lt;/li&gt;
    &lt;li&gt;Amplifier (Harman Kardon)&lt;/li&gt;
    &lt;li&gt;5 Speakers (Polk Audio)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;
&lt;strong&gt;The Content:&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;Streaming Radio Networks:&lt;/em&gt;
&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;Slacker Radio Plus ($3.99/month) - &lt;a href=&quot;http://www.slacker.com/&quot;&gt;slacker.com&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;SiriusXM Internet Radio ($14.49/month) - &lt;a href=&quot;http://www.siriusxm.com/&quot;&gt;siriusxm.com&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;Pandora (Free account) - &lt;a href=&quot;http://www.pandora.com/&quot;&gt;pandora.com&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;SomaFM (Free) - &lt;a href=&quot;http://somafm.com/&quot;&gt;somafm.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;em&gt;MP3 Collection:&lt;/em&gt;
&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;7010 Files&lt;/li&gt;
    &lt;li&gt;60 GB&lt;/li&gt;
    &lt;li&gt;Track List: &lt;a href=&quot;http://goldb.org/mp3.html&quot;&gt;Corey&#39;s MP3s&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;
Tablet interface (Logitech Squeezebox Controller):
&lt;/p&gt;

&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6ojwkMRBcgLQWHfx9N8l1LjFJmjeN-YJ5jrv2dzwG0Nti17-NiVD2zZzkqhf-K82p3VMx5WAeuUYZYCyEYJ4z8l0AiVNmCZ57_Wz_Kao9Tbng6yMWukUbWHzHzmFQ-Wo6KynLc6t3idc/s1600/Squeezebox_Controller_Android_ICS.png&quot; imageanchor=&quot;1&quot; style=&quot;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;234&quot; width=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6ojwkMRBcgLQWHfx9N8l1LjFJmjeN-YJ5jrv2dzwG0Nti17-NiVD2zZzkqhf-K82p3VMx5WAeuUYZYCyEYJ4z8l0AiVNmCZ57_Wz_Kao9Tbng6yMWukUbWHzHzmFQ-Wo6KynLc6t3idc/s400/Squeezebox_Controller_Android_ICS.png&quot; /&gt;&lt;/a&gt;

&lt;p&gt;
Web interface (Logitech Media Server 7.7.2):
&lt;/p&gt;

&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZiijkWWckFKP2RzhspwuaIRpojFZMn8uB7Pwx_sLOrwIfojtZslMwAHmEbUBvA9pTbNMZ85PXpsz9SQihI5UdVyPcJuJLz2t3ijgYipk2Xy_FLdWM_RJ8A1XGK7GG2vURomQnixn6b84/s1600/squeezebox_web_interface_7.7.2.png&quot; imageanchor=&quot;1&quot; style=&quot;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; width=&quot;317&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZiijkWWckFKP2RzhspwuaIRpojFZMn8uB7Pwx_sLOrwIfojtZslMwAHmEbUBvA9pTbNMZ85PXpsz9SQihI5UdVyPcJuJLz2t3ijgYipk2Xy_FLdWM_RJ8A1XGK7GG2vURomQnixn6b84/s400/squeezebox_web_interface_7.7.2.png&quot; /&gt;&lt;/a&gt;

&lt;hr /&gt;

Rock on!</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/4956647243781888696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/4956647243781888696' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/4956647243781888696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/4956647243781888696'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2012/06/home-audio-setup.html' title='Home Audio Setup'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglqhX1IU1svOhHMIGKEvgizmOOTa-_EAfS2pZ7OdlC23kYOqLdldZYShf1SN4E5sv6l4fBIsVJda3mmoVFScm62EpHUfsTkUCmvZUCVoNyFzsDxB2x3KzzJ_pOjh6K0eJ96itB4Mz0gT8/s72-c/streaming_music.jpg" height="72" width="72"/><thr:total>2</thr:total><georss:featurename>Boston, MA, USA</georss:featurename><georss:point>42.3584308 -71.0597732</georss:point><georss:box>42.2645643 -71.217701699999992 42.4522973 -70.9018447</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-428208208086725158</id><published>2012-06-04T15:45:00.002-04:00</published><updated>2012-06-04T20:07:26.214-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><title type='text'>History of Python - Development Visualization - Gource</title><content type='html'>&lt;p&gt;
I made a new visualization.  Have a look!
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;http://www.youtube.com/watch?v=cNBtDstOTmA&amp;hd=1&quot;&gt;History of Python - Gource - development visualization (august 1990 - june 2012)&lt;/a&gt;&lt;br /&gt;
[HD video, encoded at 1080p. watch on YouTube in highest resolution possible.]
&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;http://www.youtube.com/embed/cNBtDstOTmA?rel=0&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;
&lt;strong&gt;What is it?&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
This is a visualization of Python core development.  It shows growth of the Python project&#39;s source code over time (August 1990 - June 2012).  Nearly 22 years!  The source code history and relations are displayed by Gource as an animated tree, tracking commits over time.  Directories appear as branches with files as leaves. Developers can be seen working on the tree at the times they contributed to the Python project.
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;Video:&lt;/em&gt;&lt;br /&gt;
Rendered with &lt;a href=&quot;http://code.google.com/p/gource/&quot;&gt;Gource&lt;/a&gt; v0.37 on Ubuntu 12.04
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;Music:&lt;/em&gt;&lt;br /&gt;
&lt;a href=&quot;http://freemusicarchive.org/music/Chris_Zabriskie&quot;&gt;Chris Zabriskie - The Life and Death of a Certain K Zabriskie Patriarch&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;Repository:&lt;/em&gt;&lt;br /&gt;
cpython 3.3.0 alpha, retrieved from mercurial on June 2 2012
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
for more visualizations and other videos, check out &lt;a href=&quot;http://www.youtube.com/c0reyg&quot;&gt;my YouTube channel&lt;/a&gt;.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/428208208086725158/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/428208208086725158' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/428208208086725158'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/428208208086725158'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2012/06/history-of-python-development.html' title='History of Python - Development Visualization - Gource'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/cNBtDstOTmA/default.jpg" height="72" width="72"/><thr:total>3</thr:total><georss:featurename>Boston, MA, USA</georss:featurename><georss:point>42.3584308 -71.0597732</georss:point><georss:box>42.2645643 -71.217701699999992 42.4522973 -70.9018447</georss:box></entry><entry><id>tag:blogger.com,1999:blog-5236867476487043111.post-5754354347068900419</id><published>2012-04-23T14:02:00.000-04:00</published><updated>2012-04-23T14:02:26.276-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="python"/><title type='text'>SST 0.2.1 Release Announcement (selenium-simple-test)</title><content type='html'>&lt;p&gt;
&lt;strong&gt;SST version 0.2.1 has been released.&lt;/strong&gt;
&lt;/p&gt;

&lt;img src=&quot;http://testutils.org/sst/_static/sst-logo_small.png&quot; /&gt;

&lt;p&gt;
&lt;a href=&quot;http://testutils.org/sst&quot;&gt;SST&lt;/a&gt; (selenium-simple-test) is a web test framework that uses Python to generate functional browser-based tests.
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
SST version 0.2.1 is on PyPI: &lt;a href=&quot;http://pypi.python.org/pypi/sst&quot;&gt;http://pypi.python.org/pypi/sst&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
install or upgrade with:
&lt;/p&gt;
&lt;pre style=&quot;font-size:15px;border:1px #999999 dashed;font-family:monospace;color:#990000;background-color:#EEEEEE;padding:8px;&quot;&gt;
pip install -U sst
&lt;/pre&gt;

&lt;p&gt;
Changelog: &lt;a href=&quot;http://testutils.org/sst/changelog.html&quot;&gt;http://testutils.org/sst/changelog.html&lt;/a&gt;&lt;br /&gt;
SST Docs: &lt;a href=&quot;http://testutils.org/sst&quot;&gt;http://testutils.org/sst&lt;/a&gt;&lt;br /&gt;
SST on Launchpad: &lt;a href=&quot;https://launchpad.net/selenium-simple-test&quot;&gt;https://launchpad.net/selenium-simple-test&lt;/a&gt;&lt;br /&gt;
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
SST downloads | (Jan 1 2012 - April 23 2012)
&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdoSdOQFwZG_ZJ25H3rwy-6RIyU1NyWyQtCOAYBuMMEUptLhantg1C_yY9t21P4FyqT7aJP3MsUfjZxIMZ-Y9L-WEjT23U5FQhReNQHdjY-BqjSHBIIVITJNyq9sQQNDB-_aemZYm5Dwk/s1600/SST_downloads_pypi_2012-04-23.png&quot; imageanchor=&quot;1&quot; style=&quot;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;276&quot; width=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdoSdOQFwZG_ZJ25H3rwy-6RIyU1NyWyQtCOAYBuMMEUptLhantg1C_yY9t21P4FyqT7aJP3MsUfjZxIMZ-Y9L-WEjT23U5FQhReNQHdjY-BqjSHBIIVITJNyq9sQQNDB-_aemZYm5Dwk/s400/SST_downloads_pypi_2012-04-23.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;
1600+ downloads from PyPI since initial release.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://coreygoldberg.blogspot.com/feeds/5754354347068900419/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/5236867476487043111/5754354347068900419' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/5754354347068900419'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5236867476487043111/posts/default/5754354347068900419'/><link rel='alternate' type='text/html' href='http://coreygoldberg.blogspot.com/2012/04/sst-021-release-announcement-selenium.html' title='SST 0.2.1 Release Announcement (selenium-simple-test)'/><author><name>Corey Goldberg</name><uri>http://www.blogger.com/profile/06219872951977664560</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdoSdOQFwZG_ZJ25H3rwy-6RIyU1NyWyQtCOAYBuMMEUptLhantg1C_yY9t21P4FyqT7aJP3MsUfjZxIMZ-Y9L-WEjT23U5FQhReNQHdjY-BqjSHBIIVITJNyq9sQQNDB-_aemZYm5Dwk/s72-c/SST_downloads_pypi_2012-04-23.png" height="72" width="72"/><thr:total>0</thr:total></entry></feed>