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

<channel>
	<title>Einar Egilsson &#187; Python</title>
	<atom:link href="http://einaregilsson.com/category/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://einaregilsson.com</link>
	<description>A site for my programming pet projects</description>
	<lastBuildDate>Fri, 03 Feb 2012 20:08:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Write to a file in the Google App Engine Development server</title>
		<link>http://einaregilsson.com/write-to-a-file-in-the-googleapp-engine-development-server/</link>
		<comments>http://einaregilsson.com/write-to-a-file-in-the-googleapp-engine-development-server/#comments</comments>
		<pubDate>Thu, 16 Dec 2010 14:21:23 +0000</pubDate>
		<dc:creator>einar</dc:creator>
				<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Tips & Tricks]]></category>

		<guid isPermaLink="false">http://tech.einaregilsson.com/?p=421</guid>
		<description><![CDATA[The App Engine development server tries to mimic the real environment by making it forbidden to write to the filesystem. However, for certain things you might want to do that anyway, for example to log to a file. In that case you can use this code snippet to enable writing to the filesystem:]]></description>
			<content:encoded><![CDATA[<p>The App Engine development server tries to mimic the real environment by making it forbidden to write to the filesystem. However, for certain things you might want to do that anyway, for example to log to a file. In that case you can use this code snippet to enable writing to the filesystem:</p>
<pre class="brush: python; title: ; notranslate">
if os.environ.get('SERVER_SOFTWARE','').startswith('Dev'):
    from google.appengine.tools.dev_appserver import FakeFile
    FakeFile.ALLOWED_MODES = frozenset(['a','r', 'w', 'rb', 'U', 'rU'])
</pre>
]]></content:encoded>
			<wfw:commentRss>http://einaregilsson.com/write-to-a-file-in-the-googleapp-engine-development-server/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Unit testing model classes in Google App Engine</title>
		<link>http://einaregilsson.com/unit-testing-model-classes-in-google-app-engine/</link>
		<comments>http://einaregilsson.com/unit-testing-model-classes-in-google-app-engine/#comments</comments>
		<pubDate>Mon, 11 Oct 2010 18:25:56 +0000</pubDate>
		<dc:creator>einar</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Tips & Tricks]]></category>

		<guid isPermaLink="false">http://tech.einaregilsson.com/?p=416</guid>
		<description><![CDATA[I&#8217;m currently writing my first Google App Engine app. I&#8217;ve created a few model classes which inherit from db.Model and I wanted to unit test them. That works fine until you try to save them to the datastore or retrieve them. Then you might get this error: BadArgumentError: app must not be empty. The problem [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m currently writing my first <a href="http://appengine.google.com">Google App Engine</a> app. I&#8217;ve created a few model classes which inherit from db.Model and I wanted to unit test them. That works fine until you try to save them to the datastore or retrieve them. Then you might get this error:</p>
<div style="background-color:black; color:white; padding:2px;">
BadArgumentError: app must not be empty.
</div>
<p>The problem here is that you need an environment variable with your app id, which you can set with the following code:<br />
<span id="more-416"></span></p>
<pre class="brush: python; title: ; notranslate">
os.environ['APPLICATION_ID'] = 'myapp'
</pre>
<p>After you do that you&#8217;ll get this error:</p>
<div style="background-color:black; color:white; padding:2px;">
AssertionError: No api proxy found for service &#8220;datastore_v3&#8243;
</div>
<p>To fix that one you need the following lines in your test setup:</p>
<pre class="brush: python; title: ; notranslate">
datastore_file = '/dev/null'
from google.appengine.api import apiproxy_stub_map,datastore_file_stub
apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()
stub = datastore_file_stub.DatastoreFileStub('myapp', datastore_file, '/')
apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', stub)
</pre>
<p>This temp datastore is persisted just while you run your tests. But you can also give the datastore_file variable a real filename and then it will save the datastore between tests, or you could create different datastore files for different scenarios.</p>
<p>Finally, to put it all together, here is the setUp method for my TestCase classes:</p>
<pre class="brush: python; title: ; notranslate">
class SomeTestCase(unittest.TestCase):
	def setUp(self):
		app_id = 'myapp'
		os.environ['APPLICATION_ID'] = app_id
		datastore_file = '/dev/null'
		from google.appengine.api import apiproxy_stub_map,datastore_file_stub
		apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()
		stub = datastore_file_stub.DatastoreFileStub(app_id, datastore_file, '/')
		apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', stub)
</pre>
]]></content:encoded>
			<wfw:commentRss>http://einaregilsson.com/unit-testing-model-classes-in-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Subtitle Fixer</title>
		<link>http://einaregilsson.com/subtitle-fixer/</link>
		<comments>http://einaregilsson.com/subtitle-fixer/#comments</comments>
		<pubDate>Thu, 20 Mar 2008 13:04:13 +0000</pubDate>
		<dc:creator>einar</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Utilities]]></category>

		<guid isPermaLink="false">http://tech.einaregilsson.com/2008/03/20/subtitle-fixer/</guid>
		<description><![CDATA[I was watching a movie on my computer the other day and I had gotten the subtitles for it off the internet, I think from http://opensubtitles.com or something like that. The only problem was that they were a bit out of sync with the picture, about 2 seconds too late. Using a good media player, [...]]]></description>
			<content:encoded><![CDATA[<p>I was watching a movie on my computer the other day and I had gotten the subtitles for it off the internet, I think from <a href="http://opensubtitles.com">http://opensubtitles.com</a> or something like that. The only problem was that they were a bit out of sync with the picture, about 2 seconds too late. Using a good media player, such as <a href="http://videolan.org">VLC</a> you can add an offset to the subtitles every time you watch the movie but I figured I could probably whip up a small script to do it for me so I could just do it once and then have the subtitles correct every time I watched the movie. <span id="more-58"></span> The subtitles were in the .srt format, which is basically just a very simple text file with time information and the text, a typical screen is something like</p>
<pre>
00:20:24,345 --> 00:20:25,200
Hello there.
How are you?
</pre>
<p>So I created a little script, suboffset.py, which takes in the name of a subtitle file and the offset to add to the file in milliseconds, modifies the file contents and writes it back out to the same file. Alternately you can specify &#8211; as the filename and then the script will read the file from stdin and write the output to stdout so you can pipe it together to write to another file. The original script was about 10 lines, but just to make it a little more useful I made it a module so you could get access to the script from other scripts and added the stdin option and some comments. You can view the script below or <a href="http://einaregilsson.com/download/suboffset.py">download the actual script here</a>.</p>
<pre class="brush: python; title: ; notranslate">
#!/usr/bin/python
&quot;&quot;&quot;
   Script to offset the time in subtitle files in the .srt format. Script
   takes in the filename and the offset (in milliseconds) to add or
   subtract from the subtitles. It then writes the new subtitles to the
   same file. Alternately you can specify the filename as '-' and then
   the script will read input from stdin and write output to stdout.

&quot;&quot;&quot;

__version__   = '1.0'
__author__    = 'Einar Egilsson'
__date__      = 'March 20th 2008'
__url__       = 'http://einaregilsson.com/2008/03/20/subtitle-fixer/' 

import sys, re, datetime

MILLISECOND = 1
SECOND = 1000 * MILLISECOND
MINUTE = 60 * SECOND
HOUR = 60 * MINUTE

def offset_time(time, offset):
    &quot;&quot;&quot; Takes in list with [hour, minute, second, millisecond] and returns
        it with offset milliseconds added and normalized
    &quot;&quot;&quot;
    ms = sum(map(int.__mul__,  time, [HOUR, MINUTE, SECOND, MILLISECOND]))
    ms += offset
    return [ms / HOUR, ms % HOUR / MINUTE, ms % MINUTE / SECOND, ms % SECOND]

def fix_subtitles(lines, offset, output):
    &quot;&quot;&quot; Takes in list (lines) with all the lines from the subtitle file, adds
        offset milliseconds to it and writes the file to output.
    &quot;&quot;&quot;
    for line in lines:
        pattern = r'(\d\d):(\d\d):(\d\d),(\d\d\d) --&gt; (\d\d):(\d\d):(\d\d),(\d\d\d)'
        match = re.match(pattern, line)
        if match:
            nrs = [int(nr) for nr in match.groups(0)]
            start = offset_time(nrs[:4], offset)
            end = offset_time(nrs[4:], offset)
            output.write('%02d:%02d:%02d,%03d' % tuple(start))
            output.write(' --&gt; ')
            output.write('%02d:%02d:%02d,%03d\n' % tuple(end))
        else:
            output.write(line)

def print_header():
    print 'Subtitle Fixer v%s' % __version__
    print 'Author: %s' % __author__
    print __url__
    print ''

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print_header()
        print 'Usage: suboffset.py &lt;filename&gt; &lt;offset-in-milliseconds&gt;'
        print 'Use - for filename to read from stdin and print to stdout'
        sys.exit(1)

    offset = int(sys.argv[2])
    file = None
    if sys.argv[1] == '-': #Read from stdin and print to stdout
        fix_subtitles(sys.stdin.readlines(), offset, sys.stdout)
    else: #read from file and write to same file
        file = open(sys.argv[1], 'r')
        lines = file.readlines()
        file.close()
        file = open(sys.argv[1], 'w')
        fix_subtitles(lines, offset, file)
        file.close()
        print 'Finished adding %s milliseconds to %s' % (offset, sys.argv[1])
</pre>
]]></content:encoded>
			<wfw:commentRss>http://einaregilsson.com/subtitle-fixer/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Export Haloscan comments</title>
		<link>http://einaregilsson.com/export-haloscan-comments/</link>
		<comments>http://einaregilsson.com/export-haloscan-comments/#comments</comments>
		<pubDate>Mon, 13 Aug 2007 09:51:52 +0000</pubDate>
		<dc:creator>einar</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Haloscan]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://tech.einaregilsson.com/2007/08/13/export-haloscan-comments/</guid>
		<description><![CDATA[A few weeks ago I was helping my sister change her blog. She has a Blogger account but uses Haloscan for comments, since when she started blogging Blogger didn&#8217;t offer comments as a part of their service. That has changed now, so I thought it would be much more convenient to have the comments and [...]]]></description>
			<content:encoded><![CDATA[<p>A few weeks ago I was helping my sister change her blog. She has a Blogger account but uses Haloscan for comments, since when she started blogging Blogger didn&#8217;t offer comments as a part of their service. That has changed now, so I thought it would be much more convenient to have the comments and blog all at the same place. I just needed a way to export her Haloscan comments and import them into Blogger. So I wrote a small Python script to do the exporting for me. <span id="more-47"></span></p>
<p>The script logs into your Haloscan account, makes a http request for each comment and writes them to an .xml file. If the script fails halfway through you can just start it again and it will continue where it left off. A xml file with the name yourusername.xml will be created in your working folder where you start the script. It contains all information about every comment, date, author, url, email, ip, threadid, commentid and text. You can then parse the xml file to import them into other comment systems. Before you download the script, be sure to read the following disclaimer:</p>
<p><strong>DISCLAIMER: This is provided &#8220;AS-IS&#8221;, I make no guarantees that this works, it&#8217;s based on screen scraping so could stop working whenever Haloscan change their pages. </p>
<p>According to the Haloscan Terms of Service (http://www.haloscan.com/privacy/) it&#8217;s not explicitly forbidden to screen scrape their site. HOWEVER, they say:</p>
<p>&#8220;We reserve the right to suspend, delete, or cancel any account/service at any time for any reason.&#8221;</p>
<p>This script makes a new http request for every single comment so if you have thousands of comments and Haloscan doesn&#8217;t approve of you pounding their server and suspends your account and you lose all your comments, I CANNOT BE HELD RESPONSIBLE. USE AT YOUR OWN RISK! You&#8217;ve been warned!</strong></p>
<p>Now that you&#8217;ve read that, you can <a href="/download/haloscan.py">download the script here</a> or <a href="/download/haloscan.py.html">view it syntax highlighted here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://einaregilsson.com/export-haloscan-comments/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Mozilla Extension Generator</title>
		<link>http://einaregilsson.com/mozilla-extension-generator/</link>
		<comments>http://einaregilsson.com/mozilla-extension-generator/#comments</comments>
		<pubDate>Wed, 01 Aug 2007 01:00:17 +0000</pubDate>
		<dc:creator>einar</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Extensions]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Mozilla]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://tech.einaregilsson.com/2007/08/01/mozilla-extension-generator/</guid>
		<description><![CDATA[Since I started creating Mozilla extensions I spent a lot of time writing boilerplate code, and copying from one extension from the next. Then I found <a href="http://ted.mielczarek.org/code/mozilla/extensionwiz/">Ted Mielczarek's Extension Generator</a> which is a great page that takes some parameters and creates an extension ready for you to use. I used that for some time but in the end I still kept modifying each extension to fit my own style and include my library functions. So I decided to make my own Mozilla Extension Generator in Python]]></description>
			<content:encoded><![CDATA[<p>Since I started creating Mozilla extensions I spent a lot of time writing boilerplate code, and copying from one extension from the next. Then I found <a href="http://ted.mielczarek.org/code/mozilla/extensionwiz/">Ted Mielczarek&#8217;s Extension Generator</a> which is a great page that takes some parameters and creates an extension ready for you to use. I used that for some time but in the end I still kept modifying each extension to fit my own style and include my library functions. So I decided to make my own Mozilla Extension Generator in Python<span id="more-44"></span>, that would spit out extensions exactly the way I want them. I mostly liked the output of Ted&#8217;s generator so I took an extension from it, modified to fit my style and then created the generator to create it. So if you see similarities between extension my by generator and Ted&#8217;s, that&#8217;s the reason, mine is partially based on his output. I&#8217;ve decided to put the generator on my page for others to download, and they can then tweak it to fit their ideas of what should be in all extensions. </p>
<p>When the script is run the output will look something like this:</p>
<pre style="background-color:black; color:white; font-family:monospace; font-size:11pt;">
Mozilla Extension Generator
Copyright (c) 2007 Einar Egilsson (http://einaregilsson.com)

Extension name: My New Extension
Extension code name: MyNewExtension
Extension description: Does this and that
Firefox or Thunderbird (f/t) : f
Include Menu item ? (y/n): y
Include Context menu ? (y/n): y
Author name: Einar Egilsson
Guid: mynewextension@einaregilsson.com
Menu item label: My New MenuItem
Menu item access key: m
Contextmenu item label: My New ContextItem
Contextmenu item access key: c

Creating files...

mynewextension/install.rdf
mynewextension/chrome.manifest
mynewextension/defaults/preferences/mynewextension.js
mynewextension/chrome/content/mynewextension.js
mynewextension/chrome/content/overlay.xul
mynewextension/chrome/skin/overlay.css
mynewextension/chrome/content/mynewextension.png
mynewextension/chrome/content/mynewextensionlib.js
mynewextension/chrome/locale/en-US/mynewextension.dtd
mynewextension/chrome/locale/en-US/mynewextension.properties

Extension complete
</pre>
<p>This will generate all the necessary files for the extension, you can simply go into the folder created, zip it up, name as .xpi and install the extension. The questions are pretty straight forward, just about the only thing you need to know is that Extension code name must be a single word, because it will become a javascript object that all your other code will be wrapped in, to avoid polluting the global namespace. </p>
<p>Now, some features of the generated extension:</p>
<ul>
<li>Localization ready, strings are in .dtd and .properties files</li>
<li>Includes a library file with common function like accessing your prefs, writing to a file and more.</li>
<li>Includes debug functions that output to the javascript console and can be turned off in about:config</li>
<li>Can include menu item and context menu commands</li>
<li>&#8230;and more, just look at the generated files</li>
</ul>
<p>The generator is a single python file that includes all the other files. It should be easy to modify for anyone with a working knowledge of python. There are a few constants at the top that you can tweak, they are:</p>
<pre class="brush: python; title: ; notranslate">
#Constants
#Fill these out so you won't get prompted for them everytime:
YOUR_NAME       = ''
YOUR_DOMAIN     = '' # if filled out the extension guid will be of
                     #the form extensionname@yourdomain.com
FIREFOX_MIN     = '2.0'
FIREFOX_MAX     = '2.0.0.*'
THUNDERBIRD_MIN = '2.0'
THUNDERBIRD_MAX = '2.0.0.*'
INITIAL_VERSION = '0.9'
</pre>
<p>The icon file is also embedded in the file as a base64 encoded string. To replace it with your own file, create a .png image file (34&#215;34 pixels) and run the script below (<a href="/download/base64.py">download</a>) to create the base64 string, then copy it over the existing base64 string in the extension.py file.</p>
<pre class="brush: python; title: ; notranslate">
import sys, base64

if len(sys.argv) == 1:
    print 'Usage: base64 &amp;lt;filename&amp;gt;'
    sys.exit(0)

b64 = base64.b64encode(open(sys.argv[1], 'rb').read())
i = 0

while i &lt; len(b64):
    print b64[i:i+80]
    i += 80
</pre>
<p>And finally, you can <a href="/download/extension.py">download the python script here</a>. You can also <a href="/download/extension.py.html">view it syntax highlighted here</a> but don&#8217;t copy it from there as the syntax highlighting might screw something up. Enjoy <img src='http://einaregilsson.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://einaregilsson.com/mozilla-extension-generator/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

