<?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: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-27964390</id><updated>2011-12-15T14:25:17.475Z</updated><category term='gobject'/><category term='pexpect'/><category term='couchdb YUI datatable'/><category term='javascript'/><category term='pida'/><category term='twisted'/><category term='apis'/><category term='instantclient'/><category term='newforms'/><category term='development'/><category term='pygtk'/><category term='subprocess'/><category term='spame deleting'/><category term='interfaces'/><category term='rat'/><category term='date'/><category term='openoffice'/><category term='presentation'/><category term='dart'/><category term='python'/><category term='twisted.web2'/><category term='wsgi'/><category term='storm'/><category term='spam'/><category term='zope'/><category term='gdata'/><category term='video'/><category term='windows'/><category term='launchpad'/><category term='file'/><category term='blogger.com'/><category term='multiadapters'/><category term='atom api'/><category term='powerpoint'/><category term='cx_oracle'/><category term='threads'/><category term='py2exe'/><category term='programming'/><category term='sqlite'/><category term='gtk'/><category term='adapters'/><category term='ffmpeg'/><category term='dialogs'/><category term='readline'/><category term='django'/><category term='concurrency'/><category term='oracle'/><category term='options'/><category term='time'/><category term='pylons'/><category term='jinja2'/><category term='inno setup'/><category term='blogger'/><category term='appengine'/><category term='desktop'/><category term='google code'/><category term='oauth2.0'/><category term='orm'/><category term='werkzeug'/><category term='kamaelia'/><category term='atom'/><category term='unit testing'/><category term='desklet'/><category term='glade3 custom widget'/><category term='kiwi'/><category term='command line'/><category term='testing'/><category term='datetime'/><category term='glade3'/><category term='ide'/><category term='glashammer'/><title type='text'>Toward a Secret Sky</title><subtitle type='html'>a weblog about programming and open source, mostly.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://unpythonic.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>46</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-27964390.post-2938939385141025862</id><published>2011-12-10T23:14:00.001Z</published><updated>2011-12-10T23:25:59.991Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dart'/><category scheme='http://www.blogger.com/atom/ns#' term='options'/><category scheme='http://www.blogger.com/atom/ns#' term='command line'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Getting the command line arguments in Dart (vm)</title><content type='html'>Another hidden gem in the Dart library today, can you tell I am trying to write a command line thingy? Reading the command line options is really easy.&lt;br /&gt;&lt;pre class="prettyprint"&gt;void main() {&lt;br /&gt;  Options opts = new Options();&lt;br /&gt;  for (String arg in opts.arguments) {&lt;br /&gt;    print(arg);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Let us play:&lt;br /&gt;&lt;pre&gt;$ dart opts.dart a&lt;br /&gt;a&lt;br /&gt;&lt;/pre&gt;&lt;pre&gt;$ dart opts.dart a b c d&lt;br /&gt;a&lt;br /&gt;b&lt;br /&gt;c&lt;br /&gt;d&lt;br /&gt;&lt;/pre&gt;Nothing to add, I think, except check &lt;a href="http://code.google.com/p/dart/source/browse/branches/bleeding_edge/dart/runtime/lib/options.dart?r=1176"&gt;the source&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Comments on &lt;a href="https://plus.google.com/u/1/118327176775959145936/posts/dgTZCSmt6eD"&gt;Google+&lt;/a&gt;.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-2938939385141025862?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2938939385141025862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2938939385141025862'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2011/12/getting-command-line-options-in-dart.html' title='Getting the command line arguments in Dart (vm)'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-716627809418513261</id><published>2011-12-09T11:36:00.000Z</published><updated>2011-12-09T11:36:08.325Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dart'/><category scheme='http://www.blogger.com/atom/ns#' term='oauth2.0'/><category scheme='http://www.blogger.com/atom/ns#' term='gdata'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='apis'/><title type='text'>OAuth2.0 with Dart in a web browser</title><content type='html'>I was trying to access a Google GData API using Dart in a browser. More for fun than profit, but it posed a few challenges, so I thought I should document them.&lt;br /&gt;&lt;br /&gt;The first step was to Authorize using &lt;a href="http://code.google.com/apis/accounts/docs/OAuth2.html"&gt;OAuth2.0&lt;/a&gt; which is the easiest way of Authorizing an application to use Google APIs. This is pretty trivial using server-side Dart, but I wanted to achieve the entire thing inside a web browser, and so this involves a different OAuth2.0 flow to the web-server flow that I am used to using, in this case the &lt;a href="http://code.google.com/apis/accounts/docs/OAuth2UserAgent.html"&gt;OAuth2.0 Client-side Application Flow&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The main difference with the web-server flow is that the entire OAuth2.0 dance occurs in a single step, i.e., there is no step to exchange a code for a token.&amp;nbsp;This single step is performed by asking for a token in the initial redirect step with a &lt;tt&gt;response_type&lt;/tt&gt; parameter of &lt;tt&gt;token&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;Additionally, the client secret is not passed at any time, because this would not be safe information for the client application to give up to a web browser (since anyone could just read it).&lt;br /&gt;&lt;br /&gt;The response is also different in that the token is returned in the URI fragment of the redirect, rather than as a URI parameter as would be returned in a web-server flow, so that after authorization the browser is redirected back to something like:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;http://localhost:7890/DartOAuthTest.html#access_token=AHES6ZS2-t1ntxFsGsvfKbEFosG6nokTOrn9qwVysp_&amp;amp;amp;token_type=Bearer&amp;amp;amp;expires_in=3600&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;So, this is what we are aiming at:&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://code.google.com/apis/accounts/images/tokenflow.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="287" src="http://code.google.com/apis/accounts/images/tokenflow.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;OAuth2.0 Client Flow (via &lt;a href="http://code.google.com/apis/accounts/docs/OAuth2.html"&gt;http://code.google.com/apis/accounts/docs/OAuth2.html&lt;/a&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Step 1: Generate the Authorization URI&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;String authEndpoint = 'https://accounts.google.com/o/oauth2/auth?';&lt;br /&gt;&lt;br /&gt;String generateAuthUri(String client_id, String redirect_uri, String scope) {&amp;nbsp; &amp;nbsp; &lt;br /&gt;&amp;nbsp; List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; params = [&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ['client_id', client_id],&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ['redirect_uri', redirect_uri],&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ['scope', scope],&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ['response_type', 'token'],&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ['approval_prompt', 'force']&lt;br /&gt;&amp;nbsp; &amp;nbsp; ];&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;return authEndpoint + generateQueryString(params);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Hopefully, this will one day be handled in a library&lt;br /&gt;&lt;br /&gt;String generateQueryString(List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; params) {&lt;br /&gt;&amp;nbsp; List&amp;lt;String&amp;gt; parts = new List&amp;lt;String&amp;gt;();&lt;br /&gt;&amp;nbsp; for (List&amp;lt;String&amp;gt; part in params) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; String k = encodeComponent(part[0]);&lt;br /&gt;&amp;nbsp; &amp;nbsp; String v = encodeComponent(part[1]);&lt;br /&gt;&amp;nbsp; &amp;nbsp; parts.add(Strings.join([k, v], '='));&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; return Strings.join(parts, '&amp;amp;');&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Taken from Uri.dart&lt;br /&gt;&lt;br /&gt;String encodeComponent(String component) {&lt;br /&gt;&amp;nbsp; &amp;nbsp;if (component == null) return component;&lt;br /&gt;&amp;nbsp; &amp;nbsp;return component.replaceAll(':', '%3A')&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .replaceAll('/', '%2F')&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .replaceAll('?', '%3F')&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .replaceAll('=', '%3D')&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .replaceAll('&amp;amp;', '%26')&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .replaceAll(' ', '%20');&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ok, so I'm sorry about that as it's mostly doing things that a decent URI library will handle, volunteers anyone? But we can see the parameters we will pass, and the important ones are the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;client_id&lt;/span&gt;, the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;scope&lt;/span&gt;, and the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;redirect_uri&lt;/span&gt;. The &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;client_id&lt;/span&gt; and the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;redirect_uri&lt;/span&gt; should be configured in the &lt;a href="https://code.google.com/apis/console"&gt;Google API Console&lt;/a&gt;, and the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;scope&lt;/span&gt; is specific to whichever API(s) you will be using. In our case, we are using the &lt;a href="http://code.google.com/apis/documents/"&gt;Google Documents List API&lt;/a&gt;, with a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;scope&lt;/span&gt; of&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;b&gt;https://docs.google.com/feeds/&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Step 2: Open a popup window and redirect to the Authorization URI&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We are now firmly in the realm of the DOM, and so once we have our redirect_uri, we open a window and send the client to it:&lt;br /&gt;&lt;pre class="prettyprint"&gt;void redirectAuth(String uri) {&lt;br /&gt;&amp;nbsp; window.open(uri, 'authwin', 'width=600,height=400');&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Very simple, but the key will be how we handle the redirect back to us in the next stage.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Step 3: Receive the redirect and extract the OAuth2.0 Authorization token&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Once we have been redirected back to our application, we should use window.location to extract the Authorization token. Remember that this is in the fragment, and once again we would hope that Dart eventually has libraries to handle this extraction, but in our case, I did something nasty like:&lt;br /&gt;&lt;pre class="prettyprint"&gt;String extractToken() {&lt;br /&gt;&amp;nbsp; // eww&lt;br /&gt;&amp;nbsp; return window.location.hash.split('&amp;amp;')[0].split('=')[1];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Which probably won't work for 1000 edge cases, but you get the idea.&lt;br /&gt;&lt;br /&gt;Once we have this token we need to somehow message it back to the parent window, which I was slightly puzzled how to do. I got some advice on &lt;a href="https://plus.google.com/118327176775959145936/posts/THaYy2baw6h"&gt;Google+&lt;/a&gt; and that was to use &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;a href="https://developer.mozilla.org/en/DOM/window.postMessage"&gt;window.postMessage&lt;/a&gt;&lt;/span&gt;. It is a pretty nifty way of handling it, as the child window can access the parent as &lt;a href="https://developer.mozilla.org/en/DOM/window.opener"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;window.opener&lt;/span&gt;&lt;/a&gt;, and so the call simply becomes:&lt;br /&gt;&lt;pre class="prettyprint"&gt;void sendTokenAndClose(String token) {&lt;br /&gt;&amp;nbsp; window.opener.postMessage(token, '*');&lt;br /&gt;&amp;nbsp; &amp;nbsp;window.close();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Which needs to be caught by the parent window by registering an event handler for the message event:&lt;br /&gt;&lt;pre class="prettyprint"&gt;window.on.message.add(tokenReceived, false);&lt;br /&gt;&lt;br /&gt;void tokenReceived(MessageEvent e) {&lt;br /&gt;&amp;nbsp; // the token is in e.data&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;We are now ready to use our token to make API calls, probably using JSONP which is the topic of another blog post, but you can see an idea on how to do it &lt;a href="https://groups.google.com/a/dartlang.org/group/misc/msg/a3e555a0a35524d4"&gt;here in the Dart mailing list&lt;/a&gt;.&lt;br /&gt;&lt;pre&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-716627809418513261?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/716627809418513261'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/716627809418513261'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2011/11/oauth20-and-jsonp-with-dartin-web.html' title='OAuth2.0 with Dart in a web browser'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-7934315676581485848</id><published>2011-12-08T22:54:00.001Z</published><updated>2011-12-08T23:02:52.527Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dart'/><category scheme='http://www.blogger.com/atom/ns#' term='subprocess'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Running a subprocess with Dart and reading its output</title><content type='html'>Starting a foreign program, and reading its standard output stream is useful, and here is an example of listing the root directory.&lt;br /&gt;&lt;pre class="prettyprint"&gt;void main() {&lt;br /&gt;  Process p = new Process('/bin/ls', ['/']);&lt;br /&gt;  p.start();&lt;br /&gt;  StringInputStream stdoutStream = new StringInputStream(p.stdout);&lt;br /&gt;  print(stdoutStream.read());&lt;br /&gt;  p.close();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Notes:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Argument list does not include the executable name like it usually does&lt;/li&gt;&lt;li&gt;Use a StringInputStream to read an InputStream as a String&lt;/li&gt;&lt;li&gt;Close the process or the script will hang forever&lt;/li&gt;&lt;/ul&gt;Comments on &lt;a href="https://plus.google.com/u/1/118327176775959145936/posts/6ffQc6ZLR54"&gt;Google+&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-7934315676581485848?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/7934315676581485848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/7934315676581485848'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2011/12/running-subprocess-with-dart-and.html' title='Running a subprocess with Dart and reading its output'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-8039313972500599980</id><published>2011-12-08T14:41:00.001Z</published><updated>2011-12-08T21:46:32.694Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dart'/><category scheme='http://www.blogger.com/atom/ns#' term='file'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Reading a file as a String in Dart</title><content type='html'>It took me a minute to work out how to read a file in Dart (vm), so here it is for posterity:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;void main() {&lt;br /&gt;  File f = new File('my_filename.txt');&lt;br /&gt;  FileInputStream fileStream = f.openInputStream();&lt;br /&gt;  StringInputStream stringStream = new StringInputStream(fileStream);&lt;br /&gt;  print(stringStream.read());&lt;br /&gt;  fileStream.close();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A StringInputStream reads strings from an InputStream&lt;/li&gt;&lt;li&gt;A FileInputStream is an implementor of InputStream that is opened from files&lt;/li&gt;&lt;li&gt;Remember to close the FileInputStream&lt;/li&gt;&lt;/ul&gt;Comments on &lt;a href="https://plus.google.com/u/1/118327176775959145936/posts/bju36vsyo5W"&gt;Google+&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-8039313972500599980?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/8039313972500599980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/8039313972500599980'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2011/12/reading-file-as-string-in-dart.html' title='Reading a file as a String in Dart'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-3308413987181693912</id><published>2011-11-24T16:10:00.001Z</published><updated>2011-11-24T23:02:44.283Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='dart'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Things I love about Dart (so far)</title><content type='html'>I've only been using &lt;a href="http://dartlang.org/"&gt;Dart&lt;/a&gt;&amp;nbsp;for a few hours in total, but already there are things to love about it. Forget the obvious "It's not JavaScript", because I don't hate JS, and if I did hate JS it would be because of the lack of decent structure (who decided prototypes were nice to use?). Certainly, I don't hate dynamically typed languages, and in fact, I quite like them, and have built my career on them. This article is about small things, I'll leave that huge stuff for the clever people.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;1. I already know Dart without having learned a single thing:&lt;/h3&gt;&lt;br /&gt;Ever used Java or C#? Dart is &lt;a href="http://knowyourmeme.com/memes/megyn-kelly-essentially"&gt;essentially&lt;/a&gt;&amp;nbsp;the same as those familiar languages that we learned at school. A class is defined like:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;class Banana {&lt;br /&gt;&amp;nbsp; String getColor() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; return "yellow";&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;See, this could be any language you already know, and to illustrate that nicely the syntax highlighter treats it as Java, and no one really cares.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;2. Class members are public by default:&lt;/h3&gt;&lt;br /&gt;If you define an instance variable like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;class Banana {&lt;br /&gt;  int age;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;then anyone using an instance of the Banana class is free to read and write to that variable. &lt;em&gt;"OMG you just broke encapsulation!".&lt;/em&gt; No, I didn't, you are free to define private variables if you like by prefixing them with the &lt;tt&gt;_&lt;/tt&gt; (underscore) character (not sure how much I love that syntax, but still). Who in the Java world decided that exposing an attribute is a bad thing to do? It's just part of the external interface of a class, use it as such, and move on with life having escaped 1. writing &lt;tt&gt;setAge&lt;/tt&gt; and &lt;tt&gt;getAge&lt;/tt&gt;, and 2. feeling dirty about using &lt;tt&gt;public&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;3. Initializing and defining instance variables:&lt;/h3&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;class Banana {&lt;br /&gt;  int age, length, width, ends = 2;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Look how I have defined the types of all those variables and actually initialized the value of one of them to &lt;tt&gt;2&lt;/tt&gt; — a banana has two ends, usually. These can be assigned to something called &lt;em&gt;"constant expressions"&lt;/em&gt;, which I am a bit fuzzy about.&lt;br /&gt;&lt;br /&gt;There is more; how annoying is the constructor pattern where you have a constructor that takes a number of variables, only to assign them to instance attributes: &lt;tt&gt;this.age = age&lt;/tt&gt; or &lt;tt&gt;self.age = age&lt;/tt&gt; etc.? It must account for something like 5% of all my code. Construct and assign, construct and assign. Dart does this nicely, look here:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;class Banana {&lt;br /&gt;  int age;&lt;br /&gt;&lt;br /&gt;  Banana(this.age);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The above class will assign the passed age to the instance variable on construction.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;4. Top-level attributes and multiple classes per file:&lt;/h3&gt;&lt;br /&gt;I love these in Dart, but mostly because I hate the lack of them in Java. C# somehow gets away with having &lt;a href="http://msdn.microsoft.com/en-us/library/0d941h9d.aspx"&gt;namespaces&lt;/a&gt;, but Python does fine, and Dart follows the Python model. Want 27,000 classes in a single file? be my guest. Want to have some constants defined at the top level of a module instead of in a useless static class, also be my guest.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;5. Factory constructors:&lt;/h3&gt;&lt;br /&gt;These are constructors that actually return new instances. Something like Python's &lt;tt&gt;__new__&lt;/tt&gt; although that is slightly painful to use in Python, and not something you would wish on someone you like. Dart does it properly with the use of the &lt;tt&gt;factory&lt;/tt&gt; keyword to indicate that a constructor will return a new instance. It is still a constructor so when creating an instance, the &lt;tt&gt;new&lt;/tt&gt; keyword is used; which differs from using a static method to achieve the same thing, as you would in Java, look how:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;class Banana {&lt;br /&gt;  Banana._internal();&lt;br /&gt;&lt;br /&gt;  factory Banana() {&lt;br /&gt;    return new Banana._internal();&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;The _internal constructor here is a private named constructor which the Banana constructor calls. This is great for doing nasty things like caching instances transparently from the caller of the API.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;6. It's not JavaScript:&lt;/h3&gt;&lt;br /&gt;Just kidding, you know I can't resist trolling! But anyway, try Dart, it's fun, refreshing, safe, and as I said, you already know it. Read more about &lt;a href="http://www.dartlang.org/articles/idiomatic-dart/"&gt;idiomatic dart&lt;/a&gt; if you are interested in the advisable ways of doing things.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://plus.google.com/u/1/118327176775959145936/posts/YLpsVTcwNCq"&gt;Comments on Google+&lt;/a&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-3308413987181693912?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/3308413987181693912'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/3308413987181693912'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2011/11/things-i-love-about-dart-so-far.html' title='Things I love about Dart (so far)'/><author><name>Ali Afshar</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-8171718993760928111</id><published>2009-05-23T20:26:00.009Z</published><updated>2009-05-23T20:56:02.193Z</updated><title type='text'>How to install IronPython2 with Mono on Ubuntu</title><content type='html'>This is just an instructional post with the hope that it might help others in the future.&lt;br /&gt;&lt;br /&gt;I spent a long time trying to get IronPython2 running on Mono. Now IronPython1 ships with Ubuntu, so &lt;pre&gt;apt-get install ironpython&lt;/pre&gt;, if that's all that you want. Ironically, I don't really know the difference between IronPython1 and IronPython2, but "2 has got to be better than 1", right?&lt;br /&gt;&lt;br /&gt;The good news is that its very possible, but you will have to step out of your comfort zones.&lt;br /&gt;&lt;br /&gt;You will need:&lt;br /&gt;&lt;a href="http://ftp.novell.com/pub/mono/sources/mono/mono-2.4.tar.bz2"&gt;Mono 2.4&lt;/a&gt; (sources) and &lt;a href="http://ironpython.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=12481#DownloadId=58404"&gt;IronPython 2.0.1&lt;/a&gt; (binary)&lt;br /&gt;&lt;br /&gt;First build Mono (note that you need Ubuntu's Mono to bootstrap, so &lt;pre&gt;apt-get install mono&lt;/pre&gt;) but make sure you put it somewhere special so it doesn't mess with the system-installed Mono. This advice is taken from an &lt;a href="http://www.mono-project.com/Parallel_Mono_Environments"&gt;article on the Mono wiki&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ tar xvjf mono-2.4.tar.bz2&lt;br /&gt;$ cd mono&lt;br /&gt;$ mkdir -p ~/opt/mono&lt;br /&gt;$ ./configure --prefix=~/opt/mono&lt;br /&gt;$ make &amp;&amp; make install&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So this is putting Mono in opt/mono in my home directory (which is where I like to put things, but you might like them somewhere else, like /opt).&lt;br /&gt;&lt;br /&gt;Then you will need to write a script. Again modified from the link above.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;MONO_PREFIX=~/opt/mono&lt;br /&gt;export DYLD_LIBRARY_PATH=$MONO_PREFIX/lib:$DYLD_LIBRARY_PATH&lt;br /&gt;export LD_LIBRARY_PATH=$MONO_PREFIX/lib:$LD_LIBRARY_PATH&lt;br /&gt;export C_INCLUDE_PATH=$MONO_PREFIX/include&lt;br /&gt;export ACLOCAL_PATH=$MONO_PREFIX/share/aclocal&lt;br /&gt;export PKG_CONFIG_PATH=$MONO_PREFIX/lib/pkgconfig&lt;br /&gt;PATH=$MONO_PREFIX/bin:$PATH&lt;br /&gt;PS1="[mono] \w @ "&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You will source this script to mutate your environment so you can use your nice new Mono, so say you saved it as ~/mono-2.4-env-activate.sh:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;source ~/mono-2.4-env-activate.sh&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And it will do its busines and change your prompt so you know it:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[mono] ~ @ &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And once you have that you can test it:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[mono] ~ @ which mono&lt;br /&gt;/home/ali/opt/mono/bin/mono&lt;br /&gt;[mono] ~ @ mono --version&lt;br /&gt;Mono JIT compiler version 2.4 (tarball Sat May 23 01:13:11 BST 2009)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And once you are happy it works, you just need to execute the IronPython2 executable.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[mono] ~/tmp @ unzip IronPython-2.0.1-Bin.zip &lt;br /&gt;[mono] ~/tmp @ cd IronPython-2.0.1/&lt;br /&gt;[mono] ~/tmp/IronPython-2.0.1 @ mono ipy.exe &lt;br /&gt;IronPython 2.0 (2.0.0.0) on .NET 2.0.50727.1433&lt;br /&gt;Type "help", "copyright", "credits" or "license" for more information.&lt;br /&gt;&gt;&gt;&gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Looking good! And before you kill yourself trying to exit (it wants &lt;pre&gt;Control-z&lt;return&gt;&lt;/pre&gt; which is going to be fairly impossible):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; import sys&lt;br /&gt;&gt;&gt;&gt; sys.exit()&lt;br /&gt;[mono] ~/tmp/IronPython-2.0.1 @ &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And there you have it. I am not sure exactly how I feel about IronPython on Mono, but it can't be a bad thing that it works. I will be investigating it more in the near future, so I may document what I am learning.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-8171718993760928111?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/8171718993760928111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/8171718993760928111'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2009/05/ironpython2-with-mono-on-ubuntu.html' title='How to install IronPython2 with Mono on Ubuntu'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-3954980623868394298</id><published>2009-02-25T20:34:00.006Z</published><updated>2009-02-25T23:27:01.825Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='unit testing'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Quis experiet ipsas experientiae?</title><content type='html'>Who will test those tests?&lt;br /&gt;&lt;br /&gt;A while ago I was invited by the lynch-mob in #twisted to write a post about what I meant when I said that "Unit tests are not real tests", and here it is.&lt;br /&gt;&lt;br /&gt;Now, recently there has been much said about Unit-testing, and &lt;a href="http://ivory.idyll.org/blog/feb-09/people-who-dont-use-code-coverage-are-idiots"&gt;code coverage&lt;/a&gt;, and even &lt;a href="http://nedbatchelder.com/blog/200902/titus_wouldnt_know_a_sensational_title_if_it_bit_him_in_the.html"&gt;sensationalist blog post titles&lt;/a&gt;, which in retrospect "Unit tests are not real tests" would have been. I won't talk about code test coverage here, because its not relevant, we should strive to get as much coverage as we can. And of course I believe that unit tests are a fundamentally important piece of development. Especially since I tend to use &lt;a href="http://python.org/"&gt;Python&lt;/a&gt;, which is prone to disaster if not tested correctly.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;So, if unit tests are real tests, then why did I say they aren't?&lt;/span&gt; I deal mostly with application development. The user of the application is a human being. Now quite simply, the only true test of my application is the user interacting with it. I understand now that this is not really the case for library development, where the user is a piece of code, and I guess you can have true tests for it that are unit tests.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;So how do we do it?&lt;/span&gt; Well, I am not sure what kind of testing this is, but I call it "Acceptance Testing". It is evidence that the application behaves as advertised. I guess it would come under headings like "Verification". We have a list of written tests that go something like:&lt;br /&gt;&lt;table style="width: 550px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt;&lt;th&gt;Test&lt;/th&gt;&lt;th&gt;Expected Outcome&lt;/th&gt;&lt;th&gt;Actual Outcome&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;User double-clicks the icon&lt;/td&gt;&lt;td&gt;&lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_0"&gt;log in&lt;/span&gt; screen displayed&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;User enters date in the future&lt;/td&gt;&lt;td&gt;text entry goes red and user is informed&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;And they go on and on, we have hundreds of pages of these. As I was in a &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;start-up&lt;/span&gt; consisting of one person, I used to do these myself. Now the test department does it.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;But these tests are just another type of unit test!&lt;/span&gt; Yes they are, and probably purists would disagree, but look at them:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;You test every single unit of an application (but from the user perspective)&lt;/li&gt;&lt;li&gt;When you find a bug, you write a new test to test for it, then fix it&lt;/li&gt;&lt;li&gt;When you add a new feature, you write the tests first (TDD!)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Anyone can run the tests&lt;/li&gt;&lt;/ul&gt;And then as an added bonus you can give them to the client to run, which some of our clients insist on. They differ from traditional acceptance tests as they are run on single acceptance from the client.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Can't you automate these?&lt;/span&gt; Yes you can, but that would again slip them into the realms of "non-real tests". As I said, a human user has to test the application in order to reflect the actual user of the software. I really love automation. I have hacked on &lt;a href="http://www.async.com.br/projects/kiwi/"&gt;kiwi&lt;/a&gt;'s automated test-runner, played with various other tools. Again, like traditional code-based unit tests, they are great tools.&lt;br /&gt;&lt;br /&gt;So, I should really &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_2"&gt;amend&lt;/span&gt; my statement to the #twisted guys to something more like:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;"Code-based unit tests are not the true tests for an application that interacts with a human user."&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-3954980623868394298?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/3954980623868394298'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/3954980623868394298'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2008/10/quis-experiet-ipsas-experientiae.html' title='Quis experiet ipsas experientiae?'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-8269134971001987487</id><published>2008-12-22T10:39:00.011Z</published><updated>2011-11-23T13:28:33.824Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='jinja2'/><category scheme='http://www.blogger.com/atom/ns#' term='glashammer'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='werkzeug'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='appengine'/><title type='text'>Glashammer, an alternative framework on Google AppEngine</title><content type='html'>&lt;a href="http://spyced.blogspot.com/2008/08/app-engine-conclusions.html"&gt;In his blog, on Friday, August 29, 2008&lt;/a&gt;, Johnathan  talked about Google &lt;a href="http://code.google.com/appengine/"&gt;AppEngine&lt;/a&gt;, and said&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;the "you can use any web framework you like, as long as it's django" attitude&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;That may have been true then, it probably isn't true now. &lt;a href="http://glashammer.org/"&gt;Glashammer&lt;/a&gt; have been working for the last few weeks on getting the Glashammer framework of Werkzeug and Jinja2 running and easy on Appengine.&lt;br /&gt;&lt;br /&gt;Firstly this wasn't hard. Glashammer is very free about what kind of data storage you use, so using Appengine's DataStore was straightforward. Some utility functions for running easily, add a few decorators for controlling what happens for authentication form redirection to limit views for certain users and we are pretty much done.&lt;br /&gt;&lt;br /&gt;They have additionally tried to make it easier to install with a little script to get us started. So (since it seems a good start):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;gh-admin quickstart_gae&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This will generate a starter AppEngine + Glashammer application that is ready to go. It will also fetch all the dependencies for you and build them in the right places in your application for uploading to AppEngine. Now you can deal with that bit yourself with:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;gh-admin quickstart_gae nodeps&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You will be asked the name of the project directory, and this directory will be created. Within this directory, Glashammer will create components necessary for the application to be both a Glashammer application, and an AppEngine application.&lt;br /&gt;&lt;br /&gt;These include an &lt;tt&gt;app.yaml&lt;/tt&gt; with the necessary rules for static files, in the &lt;tt&gt;shared&lt;/tt&gt; directory (handled by AppEngine) and the remaining paths to the WSGI Application built in &lt;tt&gt;main.py&lt;/tt&gt; and the &lt;tt&gt;templates&lt;/tt&gt; directory (handled by Glashammer).&lt;br /&gt;&lt;br /&gt;At this point, I should probably just refer you to the respective documentations of &lt;a href="http://glashammer.org/documentation.html"&gt;Glashammer&lt;/a&gt; and &lt;a href="http://code.google.com/appengine/docs/"&gt;AppEngine&lt;/a&gt;, but I can't resist a little teaser.&lt;br /&gt;&lt;br /&gt;Let's have a look at the generated main.py module:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;from glashammer import make_app&lt;br /&gt;from glashammer.bundles import gae&lt;br /&gt;&lt;br /&gt;TEMPLATES_DIRECTORY = 'templates'&lt;br /&gt;&lt;br /&gt;# Main application setup&lt;br /&gt;def setup(app):&lt;br /&gt;    # add the gae init function&lt;br /&gt;    app.add_setup(gae.setup_gae)&lt;br /&gt;&lt;br /&gt;    # setup templates&lt;br /&gt;    app.add_template_searchpath(TEMPLATES_DIRECTORY)&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;    gae.make_and_run_gae_app(setup)&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That application doesn't actually do anything, because there are no rules for views, but we could add one:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;from glashammer import Response&lt;br /&gt;&lt;br /&gt;def do_home(request):&lt;br /&gt;    return Response('Hello World')&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and then add it in our application setup function:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;def setup(app):&lt;br /&gt;    ...&lt;br /&gt;    app.add_url('/', 'main/index', do_home)&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And we have a simple &lt;span style="font-style: italic;"&gt;Hello World&lt;/span&gt; application.&lt;br /&gt;&lt;br /&gt;You can use different components from Glashammer and Appengine, and sometimes you can even choose which bits you want from each. For example, I like using Glashammer's Sessions and Memcached interface, but AppEngine's Django forms.&lt;br /&gt;&lt;br /&gt;The real advantages are in having Jinja2 along with hooks to add template filters and globals is ready to go, and Werkzeug with its lovely API. For me, this feels much nicer than the provided API in AppEngine.&lt;br /&gt;&lt;br /&gt;There are no monkey-patches or other trickery and all dependencies run out of the box. And since it was released today, roll on over and grab it at &lt;a href="http://glashammer.org/downloads.html"&gt;http://glashammer.org/downloads.html&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-8269134971001987487?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/8269134971001987487'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/8269134971001987487'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2008/12/glashammer-alternative-framework-on.html' title='Glashammer, an alternative framework on Google AppEngine'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-4752086850162215098</id><published>2008-10-04T13:17:00.004Z</published><updated>2008-10-04T13:38:59.902Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='kamaelia'/><category scheme='http://www.blogger.com/atom/ns#' term='twisted'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>What Twisted could learn from Kamaelia.</title><content type='html'>Two awesome things: &lt;a href="http://twistedmatrix.com/"&gt;Twisted&lt;/a&gt;, and &lt;a href="http://www.kamaelia.org/Home"&gt;Kamaelia&lt;/a&gt;. I will not compare them. They are different, and have different purposes, and as I said. Both are great.&lt;br /&gt;&lt;br /&gt;I have recently been playing with Kamaelia for the first time. If you don't know what Kamaelia is check out &lt;a href="http://www.kamaelia.org/Home"&gt;http://www.kamaelia.org/&lt;/a&gt; but I admit it does take some getting my head around. (Just like Twisted did, back in the day when I first came across it.) But persevere.&lt;br /&gt;&lt;br /&gt;Kamaelia is a library for creating highly concurrent applications. And along the way it has reminded me of: Erlang, Tasklets and Twisted. It has a highly componentized approach where components communicate with eachother using inboxes and message passing. That's all I'll say about it here. I am sure I will blog more as I use it more.&lt;br /&gt;&lt;br /&gt;Now the first thing I want to do with Kamaelia is be able to hook it up with GTK, and I have managed to do that, but it was incredibly easy. Why? Because Kamaelia is happy to run its scheduler in the background in a non-main thread.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from Axon.background import background&lt;br /&gt;background().start()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So the remainder of the gtk application can just run:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;gtk.main() # yay!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now Twisted has a solution for GTK, using the GTK reactor, and it works perfectly well, but look at the difference. Between the two:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Kamaelia is a library, Twisted is a framework.&lt;/span&gt;&lt;br /&gt;&lt;br/&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-4752086850162215098?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/4752086850162215098'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/4752086850162215098'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2008/10/what-twisted-could-learn-from-kamaelia.html' title='What Twisted could learn from Kamaelia.'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-3350914671492654792</id><published>2008-07-24T15:55:00.005Z</published><updated>2008-07-24T16:13:43.379Z</updated><title type='text'>bitbucket.org Dream Project Hosting?</title><content type='html'>Well, I am always in search of new ways to host and serve code. Recently I have been using &lt;a href="http://bitbucket.org/"&gt;bitbucket.org's free service&lt;/a&gt; more and more.&lt;br /&gt;&lt;br /&gt;The benefits as I see them are:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Mercurial hosting&lt;/li&gt;&lt;li&gt;Ability to branch and publish any number of branches (a la launchpad)&lt;/li&gt;&lt;li&gt;Plain web space for hosting generated documentation etc, with built-in wiki&lt;/li&gt;&lt;li&gt;User interface entirely geared around source code&lt;/li&gt;&lt;li&gt;I don't have to host it myself&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Responsive and generous developers&lt;/li&gt;&lt;/ol&gt;Now if we were to compare this to some other things, we might have: (and please correct me if I am wrong).&lt;br /&gt;&lt;br /&gt;&lt;table cellpadding="3" celspacing="6"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;th&gt;Mercurial&lt;/th&gt;&lt;th&gt;Publish many  branches&lt;/th&gt;&lt;th&gt;Web space&lt;/th&gt;&lt;th&gt;Wiki&lt;/th&gt;&lt;th&gt;Hosted by someone else for free&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Bitbucket.org&lt;/th&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Launchpad.net&lt;/th&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Google  code&lt;/th&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;Self-hosted Trac&lt;/th&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;There are features that I would still really like, and the great developers are implementing them as we speak. The most exciting is the idea of an API where third-party applications could be written to do just about anything.&lt;br /&gt;&lt;br /&gt;So &lt;a href="http://bitbucket.org/"&gt;Bitbucket.org&lt;/a&gt;, it's perfect for me, maybe it is perfect for you too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-3350914671492654792?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/3350914671492654792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/3350914671492654792'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2008/07/bitbucketorg-dream-project-hosting.html' title='bitbucket.org Dream Project Hosting?'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-857938055119101185</id><published>2008-07-06T00:56:00.000Z</published><updated>2008-07-06T01:04:13.481Z</updated><title type='text'>The problem with jQuery</title><content type='html'>Ok, I'll start by saying that I love jQuery as much as the next developer, and every contributor has my eternal thanks, but there is a problem.&lt;br /&gt;&lt;br /&gt;Plugin development goes crazy, in that many similar plugins are released, and often these are just modifications including other sets of modifications. There must be a better way, like isn't this what version control is made for?&lt;br /&gt;&lt;br /&gt;Here is an excerpt of http://docs.jquery.com/Plugins#Forms&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    *  Autocomplete by Dylan Verheul&lt;br /&gt;      Autocomplete with caching to limit server requests and other options.&lt;br /&gt;&lt;br /&gt;    * jQuery Autocomplete Mod by Dan G. Switzer&lt;br /&gt;      Modification of Autocomplete plugin with enhancements and bug fixes.&lt;br /&gt;&lt;br /&gt;    * Modified Auto-complete by Anjesh Tuladhar&lt;br /&gt;      Auto-complete plugin extended for auto-completing multiple words in the same text input&lt;br /&gt;&lt;br /&gt;    * Autocomplete by Jörn Zaefferer&lt;br /&gt;      Heavily improved Dylan Verheul’s initial plugin, integrating modifications by Dan G. Switzer and Anjesh Tuladhar.&lt;br /&gt;&lt;br /&gt;    * jQuery Autocomplete by Saurabh Periwal&lt;br /&gt;      Modification of Autocomplete plugin with enhancements of onItemSelect and Multiselect.&lt;br /&gt;&lt;br /&gt;    * Jeditable + Autocomplete by Ritesh Agrawal&lt;br /&gt;      Extended jeditable to include option for autocomplete. Also extended Dylan Verheul's autocomplete javascript to include option for having input token separator. Check the demo.&lt;br /&gt;&lt;br /&gt;End of snippet.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-857938055119101185?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/857938055119101185'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/857938055119101185'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2008/07/problem-with-jquery.html' title='The problem with jQuery'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-1045301696721551549</id><published>2008-05-17T08:21:00.004Z</published><updated>2008-05-17T09:01:41.088Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='google code'/><category scheme='http://www.blogger.com/atom/ns#' term='blogger'/><category scheme='http://www.blogger.com/atom/ns#' term='spame deleting'/><category scheme='http://www.blogger.com/atom/ns#' term='gdata'/><category scheme='http://www.blogger.com/atom/ns#' term='blogger.com'/><category scheme='http://www.blogger.com/atom/ns#' term='spam'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Blogger Comment Spam - Deleting it</title><content type='html'>It seems over recent months that my blog gets comment spam. I imagine any bloggers out there experience the same thing and it is a bit of a pain.&lt;br /&gt;&lt;br /&gt;I have three immediate problems with this and blogger.com.&lt;br /&gt;&lt;br /&gt;1. Blogger doesn't notify me of all comments at the time they are posted. It notifies me of some, and I have of course configured it to notify me of all comments, but it seems to miss off about 70%. So not only do I not notice the spam, I also miss a bunch of legitimate comments. Please get it together Blogger! Ajax panel configuration is nice, but only if the core functions work.&lt;br /&gt;&lt;br /&gt;2. Blogger should/could/might try to stop this spam before it happens. I am not guessing how, but then the company that runs Blogger.com are much brighter than me, and I am sure they have a solution.&lt;br /&gt;&lt;br /&gt;3. The interface for browsing comments and deleting many at a time simply does not exist. This would make the task of sifting through, identifying, and delting spam much easier.&lt;br /&gt;&lt;br /&gt;Now that I have had my grumble about it, I will offer my small solution. In praise of Google, they do provide a nice API and Python bindings to access all of their services and blogger is one of them. So I wrote a small script to go through all the comments, do a little bit of flagging on dodgy looking ones and offer you a chance of deleting them.&lt;br /&gt;&lt;br /&gt;The script is uncommented, has no tests, and I don't plan in any way to maintain it or release it, but for those people suffering the same problems, I provide it here.&lt;br /&gt;&lt;br /&gt;It is worth noting that the spam detection is really pathetic, and it could be vastly improved. I targetted it at my particular spam.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://paste.pocoo.org/show/50987/"&gt;&lt;br /&gt;Full script available here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;"""&lt;br /&gt;(c) Ali Afshar 2008&lt;br /&gt;MIT License&lt;br /&gt;"""&lt;br /&gt;&lt;br /&gt;import sys, getpass&lt;br /&gt;&lt;br /&gt;from gdata import service&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def get_details():&lt;br /&gt;    email = raw_input('email: ').strip()&lt;br /&gt;    password = getpass.getpass()&lt;br /&gt;    return email, password&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def create_service(email, password):&lt;br /&gt;    blogger_service = service.GDataService(email, password)&lt;br /&gt;    blogger_service.source = 'blogger_spam_killer'&lt;br /&gt;    blogger_service.service = 'blogger'&lt;br /&gt;    blogger_service.server = 'www.blogger.com'&lt;br /&gt;    blogger_service.ProgrammaticLogin()&lt;br /&gt;    return blogger_service&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def get_all_blog_ids(svc):&lt;br /&gt;    query = service.Query()&lt;br /&gt;    query.feed = '/feeds/default/blogs'&lt;br /&gt;    feed = svc.Get(query.ToUri())&lt;br /&gt;    for entry in feed.entry:&lt;br /&gt;        blog_id = entry.GetSelfLink().href.split("/")[-1]&lt;br /&gt;        yield blog_id&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def get_blog_comments(svc, blog_id):&lt;br /&gt;    query = service.Query()&lt;br /&gt;    query.feed = '/feeds/%s/comments/default' % blog_id&lt;br /&gt;    query.max_results = sys.maxint&lt;br /&gt;    feed = svc.Get(query.ToUri())&lt;br /&gt;    for entry in feed.entry:&lt;br /&gt;        yield entry&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def get_all_comments(svc):&lt;br /&gt;    for blog_id in get_all_blog_ids(svc):&lt;br /&gt;        for comment in get_blog_comments(svc, blog_id):&lt;br /&gt;            yield comment&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def rank_comment(comment):&lt;br /&gt;    words = 0&lt;br /&gt;    for word in spamwords:&lt;br /&gt;        words += comment.content.text.count(word)&lt;br /&gt;&lt;br /&gt;    author = comment.author[0]&lt;br /&gt;    has_uri = (author.uri is not None and&lt;br /&gt;                # I figure no one who puts a URI would link to a blogger&lt;br /&gt;                # profile. They would link to whatever they are spamming.&lt;br /&gt;                'http://www.blogger.com/profile/' not in author.uri.text)&lt;br /&gt;    print 'Spam words: %s' % words&lt;br /&gt;    print 'Dodgy author uri: %s' % has_uri&lt;br /&gt;    return bool(words) or has_uri&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def delete_comment(svc, comment):&lt;br /&gt;    svc.Delete(comment.GetEditLink().href)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def filter_all_comments(svc):&lt;br /&gt;    for comment in get_all_comments(svc):&lt;br /&gt;        print '--'&lt;br /&gt;        t = comment.content.text&lt;br /&gt;        print t[:70] + '...'&lt;br /&gt;        print '...' + t[-70:]&lt;br /&gt;        a = comment.author[0]&lt;br /&gt;        print 'Author Info: ', a.name.text&lt;br /&gt;        if rank_comment(comment):&lt;br /&gt;            print '**** LOOKS DODGY'&lt;br /&gt;        else:&lt;br /&gt;            print '==== OK'&lt;br /&gt;        s = raw_input('Delete? (y/N) ').strip()&lt;br /&gt;        if s == 'y':&lt;br /&gt;            print 'Deleting.'&lt;br /&gt;            delete_comment(svc, comment)&lt;br /&gt;        else:&lt;br /&gt;            print 'Not deleting.'&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# http://codex.wordpress.org/Spam_Words&lt;br /&gt;spamwords = """&lt;br /&gt;4u&lt;br /&gt;adipex&lt;br /&gt;advicer&lt;br /&gt;...&lt;br /&gt;""".strip().splitlines()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    em, pw = get_details()&lt;br /&gt;    svc = create_service(em, pw)&lt;br /&gt;    filter_all_comments(svc)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-1045301696721551549?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/1045301696721551549'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/1045301696721551549'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2008/05/blogger-spam-deleting-it.html' title='Blogger Comment Spam - Deleting it'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-1673009451075799258</id><published>2007-12-17T22:42:00.001Z</published><updated>2007-12-18T02:26:46.203Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='couchdb YUI datatable'/><title type='text'>CouchDB with YUI Datatable</title><content type='html'>I am not kidding, this must be the easiest way to get a ajaxy table of results from a database. For those who haven't heard the buzz, CouchDB is a document-based database. I am not an expert in databases so I won't really comment about CouchDB itself. One incredible feature is that the database server is queryable over HTTP (with REST) and responds with JSON, which seems perfect for writing pure Javascript applications.&lt;br /&gt;&lt;br /&gt;YUI probably needs no introduction. I think its the best JavaScript library for maintainability, and documentation. It sometimes seems a bit verbose, but I like verbose.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Note: You will have to get around the fact that you cannot make XHR requests to a server other than the server that the page lives on, either with a reverse proxy setup, or by hosting the files themselves in CouchDB. (I won't explain that here).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So, a basic request to a database to return all the documents goes something like this:&lt;br /&gt;&lt;br /&gt;a GET to:&lt;br /&gt;&lt;br /&gt;http://myserver/dbname/_all_docs&lt;br /&gt;&lt;br /&gt;And that will return a JSON data structure. In our case, each document has a "title" attribute and a "description" attribute, and the "rows" member of the return JSON contains a list of these.&lt;br /&gt;&lt;br /&gt;Our database is called "tickets" and you could imagine it is a ticket tracking application.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function list_tickets() {&lt;br /&gt;        var myDataSource = new YAHOO.util.DataSource("/tickets/_all_docs");&lt;br /&gt;        myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;&lt;br /&gt;   &lt;br /&gt;        // Define the data schema&lt;br /&gt;        myDataSource.responseSchema = {&lt;br /&gt;            resultsList: "rows", // Dot notation to results array&lt;br /&gt;            fields: ["id", "title","description"] // Field names&lt;br /&gt;        };&lt;br /&gt;&lt;br /&gt;         // Define how it will appear in the table&lt;br /&gt;        var myColumnDefs = [&lt;br /&gt;            {key:"id", label:"Ticket ID"},&lt;br /&gt;            {key:"title", label:"Ticket Title"},&lt;br /&gt;            {key:"description", label:"Ticket Description"}&lt;br /&gt;        ];&lt;br /&gt;&lt;br /&gt;        var myDataTable = new YAHOO.widget.DataTable("table_holder", myColumnDefs, myDataSource);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Just make sure you have a &amp;lt;div id="table_holder"&amp;gt; somewhere in your document, because this is what will be replaced by the data table.&lt;br /&gt;&lt;br /&gt;That's really it. No web server application, no PHP (or Python or Ruby or Perl). Just javascript talking to your database. Incredible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-1673009451075799258?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/1673009451075799258'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/1673009451075799258'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/12/couchdb-with-yui-datatable.html' title='CouchDB with YUI Datatable'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-3620851977927415178</id><published>2007-11-27T19:45:00.000Z</published><updated>2011-11-27T21:35:49.542Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='werkzeug'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='threads'/><category scheme='http://www.blogger.com/atom/ns#' term='sqlite'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='storm'/><title type='text'>Using Storm and SQLite in Multithreaded Web Applications</title><content type='html'>&lt;a href="http://initd.org/tracker/pysqlite"&gt;Pysqlite&lt;/a&gt; doesn't allow you to access the same connection from different threads. &lt;a href="http://initd.org/pub/software/pysqlite/doc/usage-guide.html"&gt;The pysqlite manual&lt;/a&gt; says: "SQLite connections/cursors can only safely be used in the same thread they were created in."&lt;br /&gt;&lt;br /&gt;When using &lt;a href="https://storm.canonical.com/"&gt;Storm&lt;/a&gt; (the ORM) with &lt;a href="http://werkzeug.pocoo.org/"&gt;Werkzeug&lt;/a&gt; (the WSGI utility lib) we suffer from the problem that the Werkzeug reloader runs code in a thread. Ok this feature is not exactly important in a production environment, but I can't guarantee that whatever platform I will be deploying the application on will not be threaded, so database access should be proofed against this.&lt;br /&gt;&lt;br /&gt;The solution? &lt;a href="https://storm.canonical.com/Manual#head-0d8dc34be678b52645db38887d42a4bcaa0cdd58"&gt;The Storm manual&lt;/a&gt; mentions that you should use a Store/connection per thread. Someone has already done this with the &lt;a href="http://pypi.python.org/pypi/middlestorm/0.4"&gt;Middlestorm&lt;/a&gt; application, which provides a threadsafe store in the WSGI environ. Rightly or wrongly (since I really don't want to have to wait to have a WSGI environ to get the store instance), and I am not exactly sure this kind of thing should be middleware, but that is a debate for another day.&lt;br /&gt;&lt;br /&gt;Looking at the code, it uses threading.local(), which is a thread-local attribute store. In other words, each thread will have its own values for the local object's attributes.&lt;br /&gt;&lt;br /&gt;So a very simple implementation:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;from threading import local&lt;br /&gt;&lt;br /&gt;from storm.locals import create_database, store&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ThreadSafeStorePool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;     &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;     &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_local&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;     &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_local&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;br /&gt;     &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_local&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_local&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt; &lt;/pre&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Using an instance of this class, and calling get() when you require the thread-local store (which can be easily hidden behind a property descriptor ensures that each thread has its own Store, and pysqlite stops complaining.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;uri = 'sqlite:test.sqlite'&lt;br /&gt;&lt;br /&gt;store_pool = ThreadSafeStorePool(uri)&lt;br /&gt;&lt;br /&gt;# From one thread&lt;br /&gt;store = store_pool.get()&lt;br /&gt;# Will always return the same store in that thread&lt;br /&gt;&lt;br /&gt;# From another thread&lt;br /&gt;store = store_pool.get()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Et cetera.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-3620851977927415178?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/3620851977927415178'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/3620851977927415178'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/11/using-storm-and-sqlite-in-multithreaded.html' title='Using Storm and SQLite in Multithreaded Web Applications'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-941667115320593464</id><published>2007-11-05T13:10:00.000Z</published><updated>2007-11-05T13:27:29.876Z</updated><title type='text'>Leopard Spaces Is Unusable</title><content type='html'>&lt;p&gt;With Leopard, OS X finally has a bundled virtual desktop implementation. Pre-leopard I used a (now unmaintained) 3rd-party app called &lt;a href="http://virtuedesktops.info/"&gt;VirtueDesktops&lt;/a&gt; to get this feature, and while not perfect, it worked fairly well. It certainly didn't annoy me. Spaces, on the other hand, does annoy me, to the extent that I find it unusable.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Spaces SWITCHES MY DAMN DESKTOP WITHOUT ME ASKING IT TO. When I command-tab between applications, Spaces changes to the desktop with the active window. Now, at first when I encountered this feature, I thought "oh, that's pretty nice," and indeed it would be ok if it worked consistently. It doesn't, though. Usually Spaces switches correctly, but occasionally (say every 5 or 10 application-switches) it switches to the wrong desktop!&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;If you don't believe me, try this out: Open two terminals, and put them on different desktops. Now open a browser on one of these desktops. Command-tab between the browser and the terminal a few times, and see what happens.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This makes Spaces unusable for me. When I'm working on code, command-tabbing between the browser, the terminal and textmate, having my concentration broken by this frequent and frustrating bug is something I just can't put up with. So I've turned it off, and I'm either going to install virtuedesktops again or just learn to live without the luxury of virtual desktops.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Also, it doesn't play nicely with firefox (disappearing windows).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-941667115320593464?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/941667115320593464'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/941667115320593464'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/11/leopard-spaces-is-unusable.html' title='Leopard Spaces Is Unusable'/><author><name>Steven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-1074830715609355113</id><published>2007-11-05T02:53:00.001Z</published><updated>2007-11-05T03:03:22.394Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='desklet'/><category scheme='http://www.blogger.com/atom/ns#' term='launchpad'/><category scheme='http://www.blogger.com/atom/ns#' term='gtk'/><category scheme='http://www.blogger.com/atom/ns#' term='desktop'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pygtk'/><title type='text'>Desktop Widgets with PyGTK, Launchpad Remote Control</title><content type='html'>"""&lt;br /&gt;Desktop Widgets with PyGTK, Launchpad Remote Control&lt;br /&gt;&lt;br /&gt;We shall today be looking at a dumb hack I glanced upon while foolishly&lt;br /&gt;reading the list of GDK Constants in the PyGTK documentation. This hack sets&lt;br /&gt;the type hint of a GTK Window instance (the thing that tells the window&lt;br /&gt;manager how to treat it) as a desktop window, and hence embeds the thing in&lt;br /&gt;the desktop.&lt;br /&gt;&lt;br /&gt;It even works quite well:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;The window and widgets appear in the desktop&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The widgets are raised when the "Show desktop" command happens&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The widgets appear on all desktops (in Gnome and XFCE4, but not in KDE)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The widgets appear fully functional&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;So I got all excited and tried to think of an awesome use-case as the first&lt;br /&gt;plugin for a new kind of desktop widget framework. One which couldn't care&lt;br /&gt;less about eye-candy, and focussed on truly functional and useful&lt;br /&gt;desktop-based applets. As a friend commented while I was discussing the idea:&lt;br /&gt;"I am often hacking away in emacs when I decide that I want to know what the&lt;br /&gt;weather is like, but I am too lazy to look out the window, so I use my weather&lt;br /&gt;desklet."&lt;br /&gt;&lt;br /&gt;And that is the point, these desklet things are absolutely useless in my&lt;br /&gt;opinion. I thought and thought and I came up with a handful of barely useful&lt;br /&gt;things, which are too dumb to even mention. I would be grateful if anyone has&lt;br /&gt;any ideas.&lt;br /&gt;&lt;br /&gt;Maybe I am the problem. I don't use a desktop at all. I turn the feature off&lt;br /&gt;in XFCE4 (my preferred desktop environment) and don't have to suffer icons and&lt;br /&gt;right click and things like this. (Incidentally the hack works on noremal&lt;br /&gt;desktops, even preserving the original desktop functionality - so you can&lt;br /&gt;click on icons and see the weather at the same time.&lt;br /&gt;&lt;br /&gt;So my lack of real-world use-case discouraged me from attempting the&lt;br /&gt;framework. But I would feel bad not to mention it here at least.&lt;br /&gt;&lt;br /&gt;A nice simple thought was to use Malone's XMLRPC to read any numbered bug from&lt;br /&gt;the launchpad and display it on the desktop. As I say this isn't a real world&lt;br /&gt;application, since you would just fire up a browser. And then I realised that&lt;br /&gt;you can only use the Malone XMLRPC for filing bugs.&lt;br /&gt;&lt;br /&gt;So I guess it will be a less pleasant thing that just opens your browser at&lt;br /&gt;the bug page. Hopefully Launchpad will add simple things like this to the API&lt;br /&gt;sooner rather than later.&lt;br /&gt;&lt;br /&gt;So, on to the process:&lt;br /&gt;&lt;br /&gt;First let's get the Launchpad bit out of the way&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;"""&lt;br /&gt;&lt;br /&gt;# Python standard library module that does portable web-browser opening&lt;br /&gt;import webbrowser&lt;br /&gt;# You could make the product optional&lt;br /&gt;BASE_URL = 'http://bugs.launchpad.net/pida/+bug/%s'&lt;br /&gt;&lt;br /&gt;def navigate_bug(number):&lt;br /&gt;  """&lt;br /&gt;  Open the web browser at the required URL&lt;br /&gt;  """&lt;br /&gt;  url = BASE_URL % number&lt;br /&gt;  webbrowser.open(url)&lt;br /&gt;&lt;/pre&gt;"""&lt;br /&gt;Now we can concentrate on the more interesting desktop embedding&lt;br /&gt;"""&lt;br /&gt;&lt;br /&gt;import gtk&lt;br /&gt;&lt;pre&gt;# Create the user interface for the application&lt;br /&gt;class BugEntry(object):&lt;br /&gt;"""&lt;br /&gt;A miniature user interface for entering a bug number.&lt;br /&gt;"""&lt;br /&gt;def __init__(self):&lt;br /&gt;   # an entry, a button, and a box to put them in&lt;br /&gt;   hb = gtk.HBox()&lt;br /&gt;   self.entry = gtk.Entry()&lt;br /&gt;   hb.pack_start(self.entry)&lt;br /&gt;   button = gtk.Button(stock=gtk.STOCK_OK)&lt;br /&gt;   hb.pack_start(button, expand=False)&lt;br /&gt;&lt;br /&gt;   # Connect a signal callback for the button&lt;br /&gt;   button.connect('clicked', self.on_button_clicked)&lt;br /&gt;&lt;br /&gt;   # A window to hold it all&lt;br /&gt;   self.window = gtk.Window()&lt;br /&gt;&lt;br /&gt;   # Set the type hint on the window to make the window manager think it&lt;br /&gt;   # is a desktop&lt;br /&gt;   self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DESKTOP)&lt;br /&gt;   self.window.add(hb)&lt;br /&gt;&lt;br /&gt;   # Don't forget to size and move your window where you want it&lt;br /&gt;   self.window.move(100, 100)&lt;br /&gt;   # If you wan to resize it: self.window.resize(x, y)&lt;br /&gt;   self.window.show_all()&lt;br /&gt;&lt;br /&gt;def on_button_clicked(self, button):&lt;br /&gt;   """&lt;br /&gt;   Called when the button is clicked&lt;br /&gt;   """&lt;br /&gt;   # A sane implementation would catch errors in the conversion,&lt;br /&gt;   # But we just call the web browser launcher function:&lt;br /&gt;   navigate_bug(self.entry.get_text())&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;   # Instantiate the interface, and run the gtk main loop&lt;br /&gt;   be = BugEntry()&lt;br /&gt;   gtk.main()&lt;br /&gt;&lt;/pre&gt;"""&lt;br /&gt;Ok it looks really ugly, but you can see the promise there, The main thing is&lt;br /&gt;that it really really works.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_z4CWv2DsbGU/Ry6GhjLEDEI/AAAAAAAAATg/VQBBdcliLso/s1600-h/Screenshot.jpg"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_z4CWv2DsbGU/Ry6GhjLEDEI/AAAAAAAAATg/VQBBdcliLso/s200/Screenshot.jpg" alt="" id="BLOGGER_PHOTO_ID_5129184936557022274" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;If anyone can think of a decent thing to do with this, please let me know!&lt;br /&gt;"""&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-1074830715609355113?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/1074830715609355113'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/1074830715609355113'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/11/desktop-widgets-with-pygtk-launchpad.html' title='Desktop Widgets with PyGTK, Launchpad Remote Control'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_z4CWv2DsbGU/Ry6GhjLEDEI/AAAAAAAAATg/VQBBdcliLso/s72-c/Screenshot.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-6440112021023411329</id><published>2007-10-25T18:24:00.000Z</published><updated>2007-10-25T18:31:53.928Z</updated><title type='text'>You are accessing this page from a forbidden country.</title><content type='html'>A friend from Iran recently made me aware of something at Google code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;http://pida.googlecode.com/files/PIDA-0.5.1.tar.gz&lt;br /&gt;You are accessing this page from a forbidden country.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It's not for me to judge right from wrong, and of course I am not as clever as the policy makers at Google so I just can't comment. I just want to make people aware of this so that they can make informed decisions about where they host their projects.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-6440112021023411329?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/6440112021023411329'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/6440112021023411329'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/10/you-are-accessing-this-page-from.html' title='You are accessing this page from a forbidden country.'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-7765382302082616413</id><published>2007-09-10T10:10:00.000Z</published><updated>2007-09-10T10:37:58.748Z</updated><title type='text'>Pycon UK</title><content type='html'>Just a quick note to say that Pycon UK was great fun, and hopefully a great success. A big "thank-you" to the organisers and speakers.&lt;br /&gt;&lt;br /&gt;The talk I enjoyed the most was about Fluidinfo, mostly because I am a sucker for someone with a real vision. http://fluidinfo.com/&lt;br /&gt;&lt;br /&gt;Hopefully, I managed to interest 1 or 2 people in PIDA with a little lightning talk. We'll see.&lt;br /&gt;&lt;br /&gt;Ali&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-7765382302082616413?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/7765382302082616413'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/7765382302082616413'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/09/pycon-uk.html' title='Pycon UK'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-6539863778672317436</id><published>2007-08-09T17:41:00.000Z</published><updated>2007-08-09T17:51:02.401Z</updated><title type='text'>Using threads in PyGTK</title><content type='html'>Well, all this thought of asynchronicity is getting to me. But since we&lt;br /&gt;mentioned how to use subprocesses and Twisted, I think it is only fair to talk a&lt;br /&gt;bit about threads.&lt;br /&gt;&lt;br /&gt;So, what will we be doing?&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create an abstraction for running threads in PyGTK&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Use it&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Now the thing which we will be wanting to run is a generator, call it&lt;br /&gt;"take_really_long". And it will surely block, and might be a bit intensive, and&lt;br /&gt;well we obviously need something asynchronous. A generator is a bit nicer to use&lt;br /&gt;than a single plain long-running function, because we can update the user&lt;br /&gt;interface every time it returns, making it much much easier to do things like&lt;br /&gt;Progress Bars.&lt;br /&gt;&lt;br /&gt;You must remember two things when using threads with PyGTK:&lt;br /&gt;&lt;br /&gt;1. GTK Threads must be initialised with gtk.gdk.threads_init:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import gtk&lt;br /&gt;gtk.gdk.threads_init()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;2. Any code that modifies the UI (basically any code) that is called from&lt;br /&gt;outside the main thread must be pushed into the main thread and called&lt;br /&gt;asynchronously in the main loop, with gobject.idle_add:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import gobject, gtk&lt;br /&gt;&lt;br /&gt;def set_progress_bar_fraction(fraction):&lt;br /&gt;   progress_bar.set_fraction(0.2)&lt;br /&gt;&lt;br /&gt;gobject.idle_add(set_progress_bar_fraction, 0.2)&lt;br /&gt;gtk.main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This Gobject/GTK "idle_time" is something like "as soon as the main loop can&lt;br /&gt;reasonably get around to it".&lt;br /&gt;&lt;br /&gt;So, to reiterate, when writing threaded code, I expect to see lots of calls to&lt;br /&gt;gobject.idle_add.&lt;br /&gt;&lt;br /&gt;Here is a silly example, that I urge you not to use, as we will concentrate on&lt;br /&gt;abstracting out the boilerplate later.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from threading import Thread&lt;br /&gt;import time&lt;br /&gt;import gtk, gobject&lt;br /&gt;gtk.gdk.threads_init()&lt;br /&gt;&lt;br /&gt;class MainWindow(gtk.Window):&lt;br /&gt;   def __init__(self):&lt;br /&gt;       super(MainWindow, self).__init__()&lt;br /&gt;       vb = gtk.VBox()&lt;br /&gt;       self.add(vb)&lt;br /&gt;       self.progress_bar = gtk.ProgressBar()&lt;br /&gt;       vb.pack_start(self.progress_bar)&lt;br /&gt;       b = gtk.Button(stock=gtk.STOCK_OK)&lt;br /&gt;       vb.pack_start(b)&lt;br /&gt;       b.connect('clicked', self.on_button_clicked)&lt;br /&gt;       self.show_all()&lt;br /&gt;&lt;br /&gt;   def on_button_clicked(self, button):&lt;br /&gt;       self.count_in_thread(5)&lt;br /&gt;&lt;br /&gt;   def count_in_thread(self, maximum):&lt;br /&gt;       Thread(target=self.count_up, args=(maximum,)).start()&lt;br /&gt;&lt;br /&gt;   def count_up(self, maximum):&lt;br /&gt;       for i in xrange(maximum):&lt;br /&gt;           fraction = (i + 1) / float(maximum)&lt;br /&gt;           time.sleep(1)&lt;br /&gt;           gobject.idle_add(self.set_progress_bar_fraction, fraction)&lt;br /&gt;&lt;br /&gt;   def set_progress_bar_fraction(self, fraction):&lt;br /&gt;       self.progress_bar.set_fraction(fraction)&lt;br /&gt;&lt;br /&gt;w = MainWindow()&lt;br /&gt;gtk.main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I say this is silly, but it is not really, it's a nearly a real-world example,&lt;br /&gt;and its probably how you would do it. Now imagine an application where you had&lt;br /&gt;to do this a lot, it would eventually wear you down.&lt;br /&gt;&lt;br /&gt;So we can move on to making an abstraction that allows us to forget about this&lt;br /&gt;boilerplate and concentrate on integrating it smoothly with your code. This will&lt;br /&gt;take the form of a class that is responsible for:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;    starting a generator in a thread&lt;br /&gt;&lt;/li&gt;&lt;li&gt;    calling a callback with every result (yield) of the generator&lt;br /&gt;&lt;/li&gt;&lt;li&gt;     possibly calling a callback on completion&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;And here it is (modified from PIDA work  by Tiago Cogumbrerio: Thanks Tiago!):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import threading, thread&lt;br /&gt;import gobject, gtk&lt;br /&gt;&lt;br /&gt;gtk.gdk.threads_init()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class GeneratorTask(object):&lt;br /&gt;&lt;br /&gt;   def __init__(self, generator, loop_callback, complete_callback=None):&lt;br /&gt;       self.generator = generator&lt;br /&gt;       self.loop_callback = loop_callback&lt;br /&gt;       self.complete_callback = complete_callback&lt;br /&gt;&lt;br /&gt;   def _start(self, *args, **kwargs):&lt;br /&gt;       self._stopped = False&lt;br /&gt;       for ret in self.generator(*args, **kwargs):&lt;br /&gt;           if self._stopped:&lt;br /&gt;               thread.exit()&lt;br /&gt;           gobject.idle_add(self._loop, ret)&lt;br /&gt;       if self.complete_callback is not None:&lt;br /&gt;           gobject.idle_add(self.complete_callback)&lt;br /&gt;&lt;br /&gt;   def _loop(self, ret):&lt;br /&gt;       if ret is None:&lt;br /&gt;           ret = ()&lt;br /&gt;       if not isinstance(ret, tuple):&lt;br /&gt;           ret = (ret,)&lt;br /&gt;       self.loop_callback(*ret)&lt;br /&gt;&lt;br /&gt;   def start(self, *args, **kwargs):&lt;br /&gt;       threading.Thread(target=self._start, args=args, kwargs=kwargs).start()&lt;br /&gt;&lt;br /&gt;   def stop(self):&lt;br /&gt;       self._stopped = True&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So this does all of those things, you pass the generator, the callback for each&lt;br /&gt;result and the callback for completion to the constructor, and the arguments to&lt;br /&gt;call the generator with to the start method. This way you can call the same&lt;br /&gt;GeneratorTask instance many times.&lt;br /&gt;&lt;br /&gt;For an example of its use, lets modify our version above:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;class MainWindow(gtk.Window):&lt;br /&gt;   def __init__(self):&lt;br /&gt;       super(MainWindow, self).__init__()&lt;br /&gt;       vb = gtk.VBox()&lt;br /&gt;       self.add(vb)&lt;br /&gt;       self.progress_bar = gtk.ProgressBar()&lt;br /&gt;       vb.pack_start(self.progress_bar)&lt;br /&gt;       b = gtk.Button(stock=gtk.STOCK_OK)&lt;br /&gt;       vb.pack_start(b)&lt;br /&gt;       b.connect('clicked', self.on_button_clicked)&lt;br /&gt;       self.show_all()&lt;br /&gt;&lt;br /&gt;   def on_button_clicked(self, button):&lt;br /&gt;       GeneratorTask(self.count_up,&lt;br /&gt;                     self.set_progress_bar_fraction).start(5)&lt;br /&gt;&lt;br /&gt;   def count_up(self, maximum):&lt;br /&gt;       for i in xrange(maximum):&lt;br /&gt;           fraction = (i + 1) / float(maximum)&lt;br /&gt;           time.sleep(1)&lt;br /&gt;           yield fraction&lt;br /&gt;&lt;br /&gt;   def set_progress_bar_fraction(self, fraction):&lt;br /&gt;       self.progress_bar.set_fraction(fraction)&lt;br /&gt;&lt;br /&gt;w = MainWindow()&lt;br /&gt;gtk.main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;One thing we have done is convert the count_up method to a generator so that we&lt;br /&gt;can use it. You will notice that there is no real impact on the code that would make you&lt;br /&gt;think "oh nasty, threads". The idle calls are taken care of for you, and the&lt;br /&gt;generator task takes care of calling your UI function for each result.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-6539863778672317436?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/6539863778672317436'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/6539863778672317436'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/08/using-threads-in-pygtk.html' title='Using threads in PyGTK'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-8776579421179359916</id><published>2007-08-08T15:33:00.000Z</published><updated>2007-08-09T15:36:45.283Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='gtk'/><category scheme='http://www.blogger.com/atom/ns#' term='twisted'/><category scheme='http://www.blogger.com/atom/ns#' term='gobject'/><category scheme='http://www.blogger.com/atom/ns#' term='subprocess'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pygtk'/><title type='text'>Spawning subprocess with PyGTK using Twisted</title><content type='html'>Well, it is an age-old problem: How to schedule long-running tasks withing a GUI main loop (in our case PyGTK). There are a few ways:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Use Python's subprocess module and select on the pipe with gobject's io_add_watch&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Use GTK's built in subprocess spawning abilities&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Use Twisted&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;1 &amp; 2 are reasonable approaches, and they both work. Of course 1 won't work on Win32. The only problem with both 1 &amp;amp; 2 is that they use gobject's polling functions to achieve asynchronicity. This is nice when we are forced in a PyGTK main loop, but really not nice when the application wants to run in command line mode, and we really want to be able to share the execution code between different UIs, including perhaps other toolkits.&lt;br /&gt;&lt;br /&gt;Enter Twisted. We need to do two things with Twisted:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Make sure Twisted knows we are running with PyGTK&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Launch the process&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;Making sure Twisted knows that we are running inside PyGTK is quite easy (though I imagine the implementation was painful). To do this, you must &lt;i&gt;install&lt;/i&gt; the gtk2reactor before importing any other reactors like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from twisted.internet import gtk2reactor&lt;br /&gt;gtk2reactor.install()&lt;br /&gt;from twisted.internet import reactor&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ok, I did say it was pretty easy. Now all that you need to remember with this is that you should now run your main loop with reactor.run, and not gtk.main.&lt;br /&gt;&lt;br /&gt;Now we should think about spawning our subprocess. We will do this by using reactor.spawnProcess, which looks like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;twisted.internet.reactor.spawnProcess = spawnProcess(self,&lt;br /&gt;    processProtocol,&lt;br /&gt;    executable,&lt;br /&gt;    args=(),&lt;br /&gt;    env={},&lt;br /&gt;    path=None,&lt;br /&gt;    uid=None, gid=None, usePTY=0, childFDs=None&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The only non-normal thing here is the processProtocol paramterer. All the other paramteres are standard things for things like subprocess.Popen. The processProtocol instance should be an instance of twisted.internet.protocol.ProcessProtocol, and defines how data is read from the pipes constructed to spawn the subprocess.&lt;br /&gt;&lt;br /&gt;You can just use ProcessProtocol without overriding, but that will do nothing useful, not even print the results, so here is an example with our own ProcessProtocol class.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import os&lt;br /&gt;# Have you remembered to install the gtk2reactor?&lt;br /&gt;from twisted.internet import reactor&lt;br /&gt;from twisted.internet.protocol import ProcessProtocol&lt;br /&gt;&lt;br /&gt;class EchoingProcessProtocol(ProcessProtocol):&lt;br /&gt;&lt;br /&gt;    # Will get called when the subprocess has data on stdout&lt;br /&gt;    def outReceived(self, data):&lt;br /&gt;        print 'STDOUT:', data&lt;br /&gt;    &lt;br /&gt;    # Will get called when the subprocess has data on stderr&lt;br /&gt;    def errReceived(self, data):&lt;br /&gt;        print 'STDERR:', data&lt;br /&gt;&lt;br /&gt;    # Will get called when the subprocess starts&lt;br /&gt;    def connectionMade(self):&lt;br /&gt;        print 'Started running subprocess'&lt;br /&gt;&lt;br /&gt;    # Will get called when the subprocess ends&lt;br /&gt;    def processEnded(self, reason):&lt;br /&gt;        print 'Completed running subprocess'&lt;br /&gt;&lt;br /&gt;# Spawn the process and copy across the environment&lt;br /&gt;reactor.spawnProcess(EchoingProcessProtocol(), 'ls', ['ls', '-al'], env=os.environ)&lt;br /&gt;reactor.run()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now the subprocess will execute, and anything written to the child's stdout will be printed to the screen.&lt;br /&gt;&lt;br /&gt;This may seem entirely unremarkable, but this is now ready to plug into a GUI. Since the callback outReceived is called inside the gtk main loop, it won't block and it will be called when necessary, so it may as well do something like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def outReceived(self, data):&lt;br /&gt;    self.text_view.get_buffer().insert(&lt;br /&gt;        self.text_view.get_buffer().get_end_iter(), data&lt;br /&gt;    )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Which would add the line to a textview control (called self.text_view).&lt;br /&gt;&lt;br /&gt;Links:&lt;br /&gt;&lt;a href="http://twistedmatrix.com/projects/core/documentation/howto/process.html"&gt;Twisted How-to Processes&lt;/a&gt;&lt;br /&gt;&lt;a href="http://twistedmatrix.com/projects/core/documentation/howto/choosing-reactor.html#auto10"&gt;Twisted How-to PYGTK&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-8776579421179359916?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/8776579421179359916'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/8776579421179359916'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/08/spawning-subprocess-with-pygtk-using.html' title='Spawning subprocess with PyGTK using Twisted'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-3019052055807498193</id><published>2007-08-06T19:00:00.000Z</published><updated>2007-08-08T09:45:37.933Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='video'/><category scheme='http://www.blogger.com/atom/ns#' term='powerpoint'/><category scheme='http://www.blogger.com/atom/ns#' term='ffmpeg'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='openoffice'/><category scheme='http://www.blogger.com/atom/ns#' term='presentation'/><title type='text'>Side-by-side presentation and video</title><content type='html'>Well, it was a nice idea I believe, to place a video side-by-side with a Powerpoint (or  one of it's inferior Open Source clones) presentation. The idea came when my boss showed me Microsoft Producer, which does exactly this (it's nice to have people who at least pay some attention to non-Open Source, especially when he pays you.).&lt;br /&gt;&lt;br /&gt;Producer is a plugin for Powerpoint, with a simple User Interface, which involves importing a presentation, and a video. Setting the timings for the slides, placing them adjacently in an output it video.&lt;br /&gt;&lt;br /&gt;It sounds simple enough, and I thought this functionality would be possible in some of the Open Source video editing equipment, but either: I knew for a fact that the software didn't have the capability, or: the software was impossibly difficult to use.&lt;br /&gt;&lt;br /&gt;So, I had a think, and a play, and another play, and I came up with the plan:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Split the presentation into images: 1 slide per file&lt;/li&gt;&lt;li&gt;Split the video into frames of png files, and save the sound for later&lt;/li&gt;&lt;li&gt;And for each frame:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;    Create a new image with the video frame, and the slide frame placed next to eachother.&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Join all the frames to make a new video, with the sound.&lt;/li&gt;&lt;/ol&gt;Ok, that shouldn't be too easy! So I started to investigate tools. I knew of MPlayer, and Mencoder, (and actually they were capable of performing this task), but I had a few problems, and eventually I got hold of Ffmpeg, which is clearly the best tool for this job.&lt;br /&gt;&lt;br /&gt;FFmpeg is a universal encoder-toolbox, and although it has most of the good functionality disabled in standard distributions, you can rebuild it to be able to deal with most files. When running Ffmpeg, you choose an input file, set some options for it, and choose an output file, and set options on it, and it converts between the two. Pretty amazing.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Stage 1: Converting a video into frames:&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;This is an example command:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ffmpeg -r 25 -i myvid.avi -r 25 -s 320x240 frames/%09d.png audio.wav&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This splits myvid.avi into its frames and its audio track. The frame names are specified as the frames/%09d.png formatting string. In our case, we place the frames in a directory called frames.&lt;br /&gt;&lt;br /&gt;Amazingly, the filenames is all Ffmpeg needs to work out what is what. It understands that if it is going to output to a filename like the format string for frames, that it should use the png encoder, and place the files there. It knows that because I also gave a wav filename, that the audio output should go there.&lt;br /&gt;&lt;br /&gt;The -r option sets the frame rate. Setting -r before and after the input file is a bit weird, but the first -r applies to the input file, and the second -r applies to the output file (in this case the png frames). This ensures that the frames come out at the correct frame rate, so that we can be vaguely sensible about recombining them later. Essentially, you should decide at this point what you want your final frame rate to be, since from this point onwards you will be stuck with whatever you use.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Stage 2: Convert a presentation to a set of frames.&lt;/h2&gt;&lt;br /&gt;Well, using open office across the UNO bridge with Python was a fairly painful task, considering how simple it could be. Openoffice will export a slide to a Jpeg, and it does it quite reasonably, however, there seems no way in which you can tell it to export more than one slide, or even which slide to export. I hope I am wrong about this, and so can refactor this hideous hack:&lt;br /&gt;&lt;br /&gt;Some pseudo code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;open the presentation and count pages&lt;br /&gt;for each page:&lt;br /&gt;open the presentation&lt;br /&gt;delete all pages except the page we want&lt;br /&gt;export the page&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It's pretty awful, I know, and very slow, and when you add on to that the fact that you have to somehow spawn a headless Open Office server instance to communicate with, it can get a bit muddled. But stick to your guns.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Stage 3: Creating a new image from the frames&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;So you have the frames from the video in a directory, and you have one frame for each slide in a presentation. To create the new image, we will need to know which slide applies to which particular frame of the video. We need a list of timings, those are the times when the slides change.&lt;br /&gt;&lt;br /&gt;Now, since we know our frame rate is 25 frames per second, we can calculate exactly which slide should be placed against which video frame, and we should do it. I used PIL (Python Imaging Library) for this task because it was the first library I thought of. I actually did a similar thing using ImageMagik, but not spawning a new process for each operation was  better.&lt;br /&gt;&lt;br /&gt;So, using PIL, and say we want a final video that is 640x320, with the presentation and video each occupying half of the picture, we should create a frame for each of the frames in the original video.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import os&lt;br /&gt;import Image&lt;br /&gt;&lt;br /&gt;for i, file_name in enumerate(os.listdir('frames')):&lt;br /&gt;    frame_path = os.path.join('frames', file_name)&lt;br /&gt;    new_image = Image.new('RGBA', (640, 320)) # Make a new image 640x320&lt;br /&gt;    frame_time = i * 25 # Where are we in the video&lt;br /&gt;    slide_path = get_slide_for_time(frame_time) # Get the frame for the time&lt;br /&gt;    slide_image = Image.open(slide_path) # Open the slide file&lt;br /&gt;    video_image = Image.open(frame_path) # Open the video frame file&lt;br /&gt;    slide_image.resize((320, 240), Image.ANTIALIAS) # Resize to the target size&lt;br /&gt;    video_image.resize((320, 240), Image.ANTIALIAS)&lt;br /&gt;    new_image.paste(slide_image, (0, 0)) # Put each iamge on the new target image&lt;br /&gt;    new_image.paste(video_image, (0, 320))&lt;br /&gt;    new_image.save(os.path.join('output_frames', file_name)) # Save the file&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, we left get_slide_for_time up to your imagination, but you can imagine. We open each file, and "paste" them onto the target file. Then we save the file with the same file name in a different directory. This will really help us when we are reencoding the whole thing, since it is the syntax ffmpeg already knows.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Stage 4: Reencoding the whole thing&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;So we have a directory of files which are the final frames, we have a framerate that we have stuck to, we have an audio track, and we want an output file.&lt;br /&gt;&lt;br /&gt;So again using Ffmpeg:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ffmpeg -r 25 -i output_frames/%09d.png audio.wav -ar 44100 myvideo.flv&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This time we specify two input files, one of which is actually a file mask, and it encodes them into the resulting video. Don't forget to tell it the frame rate of the frame images (that's the first parameter), and to set an audio rate. The output file we selected is a Flash Video, which is easy to play online.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Then what?&lt;/h2&gt;&lt;br /&gt;Then we write a script that does this for us. Which I have done, and it does everything I personally need it to. And you can see it at &lt;a href="http://code.google.com/p/lecture-maker/"&gt;The Google Code Project Page&lt;/a&gt;. There is also a brief summary of how to use it there.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Thoughts&lt;/h2&gt;&lt;br /&gt;This could be a useful educational tool. The problem of recording lectures with presentations when you have to zoom the camera in on the screen is just nasty.&lt;br /&gt;&lt;br /&gt;&lt;span&gt;Edit:&lt;/span&gt; the world needs more screenshots, a variation on what we can do:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_z4CWv2DsbGU/RriA2dtu-aI/AAAAAAAAAJI/47jDS6qixSY/s1600-h/mm1.jpg"&gt;&lt;img src="http://2.bp.blogspot.com/_z4CWv2DsbGU/RriA2dtu-aI/AAAAAAAAAJI/47jDS6qixSY/s200/mm1.jpg" alt="" id="BLOGGER_PHOTO_ID_5095964651547457954" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-3019052055807498193?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/3019052055807498193'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/3019052055807498193'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/08/side-by-side-presentation-and-video.html' title='Side-by-side presentation and video'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_z4CWv2DsbGU/RriA2dtu-aI/AAAAAAAAAJI/47jDS6qixSY/s72-c/mm1.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-2500246742018256216</id><published>2007-07-26T09:41:00.000Z</published><updated>2007-08-07T12:45:23.729Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='wsgi'/><category scheme='http://www.blogger.com/atom/ns#' term='werkzeug'/><category scheme='http://www.blogger.com/atom/ns#' term='twisted'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='twisted.web2'/><title type='text'>Running a WSGI Application inside Twisted</title><content type='html'>There are a few ways to run a WSGI application, and today I will comment on the Twisted approach. The magic happens from the twisted.web2.wsgi module, and is fortunately simple.&lt;br /&gt;&lt;br /&gt;The main advantages I can think of when running with Twisted/Twistd is that:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;All Python solution (if that really is an advantage)&lt;/li&gt;&lt;li&gt;Ability to run other twisted services. Did you want RPC, Email, FTP, remote shell etc for free?&lt;/li&gt;&lt;li&gt;Twisted authorization framework&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;Since the application in question has a very simple web part, and is predominately a remote GUI application, this seems the perfect solution.&lt;br /&gt;&lt;br /&gt;First make a tac file, which you will be running with the old favourite:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;twistd -ny mytac.tac&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;On to some code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# This is my process for creating my WSGI application. You may have other methods.&lt;br /&gt;from graelsite.www.application import make_app&lt;br /&gt;from graelsite.www import config&lt;br /&gt;wsgi_app = make_app(config)&lt;br /&gt;&lt;br /&gt;# Now on to the real stuff&lt;br /&gt;from twisted.application import service, strports&lt;br /&gt;from twisted.web2 import server, channel, wsgi&lt;br /&gt;&lt;br /&gt;application = service.Application('web2-wsgi') # call this anything you like&lt;br /&gt;site = server.Site(wsgi.WSGIResource(wsgi_app))&lt;br /&gt;s = strports.service('tcp:9876', channel.HTTPFactory(site))&lt;br /&gt;s.setServiceParent(application)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And I honestly think that is the easiest way to deploy a WSGI application (outside the safety of Cherry, Paster, Werkzeug, etc.).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Note: &lt;/span&gt;&lt;a style="font-weight: bold; font-style: italic;" href="http://werkzeug.pocoo.org/"&gt;Werkzeug&lt;/a&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt; is excellent, use it!&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-2500246742018256216?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2500246742018256216'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2500246742018256216'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/07/running-wsgi-application-inside-twisted.html' title='Running a WSGI Application inside Twisted'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-108372460955666705</id><published>2007-07-16T02:20:00.000Z</published><updated>2007-07-17T22:08:19.042Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='inno setup'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pygtk'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='py2exe'/><title type='text'>PyGTK, Py2exe, and Inno setup for single-file Windows installers</title><content type='html'>I am lucky, as part of my job, I get to code PyGTK. The downside is of course&lt;br /&gt;that I have to deploy these applications on Windows. Now these Windows users&lt;br /&gt;(especially at the price they are paying for this stuff) don't install&lt;br /&gt;dependencies, don't understand what a scripting language or interpreter is,&lt;br /&gt;and frankly they should not have to.&lt;br /&gt;&lt;br /&gt;Unfortunately, the list of dependencies for a PyGTK application (at the very&lt;br /&gt;minimum) is Python, GTK, PyGTK, PyGObject, PyCairo. 5 installers! So very&lt;br /&gt;early on in this project I found the way to give them exactly what they want&lt;br /&gt;and deserve, a single-file executable installer. There are a few guides online&lt;br /&gt;about how to achieve this, but none seem to work, and none are particularly&lt;br /&gt;new.&lt;br /&gt;&lt;br /&gt;How do we achieve this? The toolchain involved consists of two elements:&lt;br /&gt;&lt;br /&gt;1. Py2exe &lt;a href="http://www.py2exe.org/"&gt;website&lt;/a&gt;&lt;br /&gt;2. Inno Setup &lt;a href="http://www.jrsoftware.org/isinfo.php"&gt;website&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;NOTE: The documentation for all the tools used is extensive, I will not repeat everything in there.&lt;/b&gt;&lt;br /&gt;&lt;h2&gt;Py2exe&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Py2exe is a distutils extension that searches for Python modules that your&lt;br /&gt;application uses, and other dependent libraries, and copies them all into your&lt;br /&gt;dist/ directory. The pure Python stuff gets zipped up, and the DLLs (Windows&lt;br /&gt;remember) get copied into the directory.&lt;br /&gt;&lt;br /&gt;The dist directory would be runnable from say a CDRom at this stage, but of&lt;br /&gt;course this is not enough, we need a single file installer (more later).&lt;br /&gt;Making py2exe work is a bit of an art. There is lots of documentation at the&lt;br /&gt;website linked above, so I don't need to repeat that. What I will do is paste&lt;br /&gt;my setup.py file so that it can be copied if required (just don't tell my&lt;br /&gt;employer).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;setup(&lt;br /&gt;name='myapp',&lt;br /&gt;version='1.3',&lt;br /&gt;packages=['myapplib'],&lt;br /&gt;scripts=['myapp.py'],&lt;br /&gt;windows=[&lt;br /&gt;{&lt;br /&gt;  'script': 'myapp.py',&lt;br /&gt;  'icon_resources': [(1, 'pixmaps/cdm.ico')],&lt;br /&gt;}&lt;br /&gt;],&lt;br /&gt;data_files=[&lt;br /&gt;('data', [&lt;br /&gt;  'data/cd_db.csv'&lt;br /&gt;  ]&lt;br /&gt;),&lt;br /&gt;('glade', [&lt;br /&gt;  'glade/main.glade',&lt;br /&gt;  'glade/patient_editor.glade',&lt;br /&gt;  'glade/prescriber_editor.glade',&lt;br /&gt;  'glade/report_chooser.glade',&lt;br /&gt;  'glade/supplier_editor.glade',&lt;br /&gt;  'glade/user_editor.glade',&lt;br /&gt;  'glade/pharmacy_editor.glade',&lt;br /&gt;  ]&lt;br /&gt;),&lt;br /&gt;('pixmaps', [&lt;br /&gt;  'tools/kiwi_requirements/validation-error-16.png',&lt;br /&gt;  'tools/kiwi_requirements/plus.png',&lt;br /&gt;  'pixmaps/s2icon16.png',&lt;br /&gt;  'pixmaps/s2icon24.png',&lt;br /&gt;  'pixmaps/s2icon32.png',&lt;br /&gt;  'pixmaps/s2icon48.png',&lt;br /&gt;  ]&lt;br /&gt;),&lt;br /&gt;('catalogs', ['tools/kiwi_requirements/dummy.txt']),&lt;br /&gt;('plugins', ['tools/kiwi_requirements/dummy.txt']),&lt;br /&gt;('resources', ['tools/kiwi_requirements/dummy.txt']),&lt;br /&gt;('pixmaps/kiwi', ['tools/kiwi_requirements/dummy.txt']),&lt;br /&gt;('backups', ['tools/kiwi_requirements/backups_go_here.txt']),&lt;br /&gt;],&lt;br /&gt;options = {&lt;br /&gt;'py2exe' : {&lt;br /&gt;  'packages': 'encodings, sqlalchemy',&lt;br /&gt;  'includes': 'cairo, pango, pangocairo, atk, gobject',&lt;br /&gt;},&lt;br /&gt;'sdist': {&lt;br /&gt;  'formats': 'zip',&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ok, I changed the name of the application. "myapplib" is the name of the&lt;br /&gt;package for the application, and the top-level script that runs is called&lt;br /&gt;"myapp.py".&lt;br /&gt;&lt;br /&gt;Note: there are some specific things in there for packaging Kiwi. That is&lt;br /&gt;probably the hardest task (but is not relevant here).&lt;br /&gt;&lt;br /&gt;Now there are two tricky things about using py2exe, and they are dependencies,&lt;br /&gt;and data files (the rest is just basic setup.py stuff). Dependencies can be&lt;br /&gt;explicitly set in options['py2exe'], and this is the magical bit. It *should*&lt;br /&gt;find these automatically I think, but sometimes it just doesn't. Sometimes&lt;br /&gt;putting something in "packages" works, but putting it in "includes" doesn't,&lt;br /&gt;and I have to admit that I am not well enough versed with py2exe and the&lt;br /&gt;documentation to know the difference. So, if you want it to work, I would just&lt;br /&gt;copy what I have above, and save yourself the time and pain of making it work.&lt;br /&gt;&lt;br /&gt;Data files are the next problem. Eventually you will want this data file&lt;br /&gt;copied firstly to your dist directory, and then secondly when building the&lt;br /&gt;installer into the installer file, and then from there somewhere into Program&lt;br /&gt;Files, where your application will find it. So the above data_files list&lt;br /&gt;demonstrates how I do this.&lt;br /&gt;&lt;br /&gt;These data files will be copied into the dist/ directory, and then will be available (once installed) to your application in the Program Files\MY_APP_NAME directory, which should be easy enough to find. Examples are available on request for interested people.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Inno Setup&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Inno setup is an application that packages any sort of distribution of files&lt;br /&gt;into a single installer and unpacks it wherever you want. There are oodles of&lt;br /&gt;options, and it does a pretty good job, and it also has pretty good&lt;br /&gt;documentation that will get you on your way.&lt;br /&gt;&lt;br /&gt;The important line from my Inno Setup config file is this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[Files]&lt;br /&gt;Source: "..\dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This basically just dumps the entire dist directory into the package, and will&lt;br /&gt;dump it back out in a similar structure on the other side, which is exactly&lt;br /&gt;what we want.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;GTK Data files&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;It is not sufficient to just make py2exe pull in the GTK DLLs for packaging&lt;br /&gt;(which it does pretty successfully). GTK also requires a number of data files&lt;br /&gt;which include themes, translations etc. These will need to be manually copied&lt;br /&gt;into the dist directory so that the application can find them when being run.&lt;br /&gt;&lt;br /&gt;If you look inside your GTK runtime directory (usually something like c:\GTK\)&lt;br /&gt;you will find the directories: share, etc, lib. You will need to copy all of&lt;br /&gt;these into the dist directory after running py2exe.&lt;br /&gt;&lt;br /&gt;You can prune some of these directories. My main pruning action comes by&lt;br /&gt;removing all the non-English translations (which saves a few megabytes in the&lt;br /&gt;final bundle).&lt;br /&gt;&lt;br /&gt;Since I am in to automation (as we will discuss later) I have zipped this lot&lt;br /&gt;up and unzip it from a location after running py2exe.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Putting it all together&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Now that we have discussed the tools used, and some specific details about&lt;br /&gt;them, I shall present the specific process involved in the build on a clean Windows system:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Install GTK runtime&lt;/li&gt;&lt;li&gt;Install Python&lt;/li&gt;&lt;li&gt;Install all the dependent Python packages&lt;/li&gt;&lt;li&gt;Run py2exe&lt;/li&gt;&lt;li&gt;Copy GTK data files&lt;/li&gt;&lt;li&gt;Run Inno Setup&lt;/li&gt;&lt;li&gt;Install the application&lt;/li&gt;&lt;/ol&gt;Wow, that is a lot of steps, especially when you change one line of code on the day before a release. The solution is to use some scripts for automation.&lt;br /&gt;&lt;br /&gt;The first "script" will provide a source bundle with everything I need in it to upload onto the Windows computer (since I develop everything on  Linux). Luckily distutils has such a thing, and we can use the sdist command to setup.py to achieve it, as long as you have correctly specified the MANIFEST.in file. For added bonus points, you should (and notice setup.py above) make sure you are bundling into a zip, not a tar.gz (unless you have ways of unpacking the tar on Windows). This can be achieved with a command line flag --formats or in the setup.py options dict, or even in a setup.cfg file.&lt;br /&gt;&lt;br /&gt;I manually unzip the thing on the other side.&lt;br /&gt;&lt;br /&gt;The second script will install all the requirements: GTK, Python etc. To achieve this I have a directory in my source distribution that contains all the Windows binary installers. I had to build some of them (for Python modules) but this is as easy as:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;python setup.py bdist_wininst&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I change to the directory of installers and I run a batch script. Batch is a pretty evil language once you have used something as nice as Bash(!), but for our purposes it is simple. Here are the contents of that batch file, which lives in the same directory as all the installers:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;call gtk-2.10.11-win32-1.exe&lt;br /&gt;call python-2.5.1.msi&lt;br /&gt;call pycairo-1.2.6-1.win32-py2.5.exe&lt;br /&gt;call pygobject-2.12.3-1.win32-py2.5.exe&lt;br /&gt;call pygtk-2.10.4-1.win32-py2.5.exe&lt;br /&gt;call kiwi-1.9.15.win32.exe&lt;br /&gt;call gazpacho-0.7.0.win32.exe&lt;br /&gt;call py2exe-0.6.6.win32-py2.5.exe&lt;br /&gt;call SQLAlchemy-0.3.4.win32.exe&lt;br /&gt;call isetup-5.1.10.exe&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There is some [ Ok ] clicking to be done, this won't run unattended, but this script alone has saved me hours in total. Perhaps there are flags for unattended install of these things.&lt;br /&gt;&lt;br /&gt;Once the dependency installers are installed, I can set to creating the installer package which are steps 4,5,6 above. Again I automated these with a script. This time the script is in Python, because now we have Python installed! Here it is:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import os, sys, shutil, zipfile&lt;br /&gt;&lt;br /&gt;BASE_DIR = os.path.dirname(__file__)&lt;br /&gt;OUT_DIR = os.path.join(BASE_DIR, 'dist')&lt;br /&gt;GTK_ZIP = os.path.join('tools', 'windows', 'gtk', 'gtk_to_copy.zip')&lt;br /&gt;INNO_SCRIPT = os.path.join('tools', 'config-inno.iss')&lt;br /&gt;INNO_EXECUTABLE = '"c:\\Program Files\\Inno Setup 5\\ISCC.exe"'&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class Unzip(object):&lt;br /&gt;   def __init__(self, from_file, to_dir, verbose = False):&lt;br /&gt;       """Removed for brevity, you can find a recipe similar to this in the Cook Book"""&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def delete_old_out_dir():&lt;br /&gt;   if os.path.exists(OUT_DIR):&lt;br /&gt;     shutil.rmtree(OUT_DIR)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def run_py2exe():&lt;br /&gt;   # A hack really, but remember setup.py will run on import&lt;br /&gt;   sys.argv.append('py2exe')&lt;br /&gt;   import setup&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def unzip_gtk():&lt;br /&gt;   Unzip(GTK_ZIP, OUT_DIR)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def run_inno():&lt;br /&gt;   os.system(INNO_EXECUTABLE + " " + INNO_SCRIPT)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;  #Clean any mess we previously made&lt;br /&gt;  delete_old_out_dir()&lt;br /&gt;  # run py2exe&lt;br /&gt;  run_py2exe()&lt;br /&gt;  # put the GTK data files in the dist directory&lt;br /&gt;  unzip_gtk()&lt;br /&gt;  # build the single file installer&lt;br /&gt;  run_inno()&lt;br /&gt;  # prevent the windows command prompt from just closing&lt;br /&gt;  raw_input('Done..')&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;  main()&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That produces an installer in the top-level source directory which has the name of the value of the config key setup/OutputBaseFilename, which you can then test to install.&lt;br /&gt;&lt;br /&gt;I advise that you remove GTK, Python and all the dependent modules before installing, just so you can be sure that it will work on a clean Windows system.&lt;br /&gt;&lt;br /&gt;You may think that including GTK, Python and a whole load of other stuff might make for a really heavy distribution. Actually it's not, it's pretty light. For the application I am referring to we come out at just under 6 megabytes, 11 if you include all the translations. Granted, it is a huge amount when you consider how much non-library code we are using, but by today's standards, no one is going to be upset about having to download that.&lt;br /&gt;&lt;br /&gt;Please drop me a line if you have any queries.&lt;br /&gt;&lt;br /&gt;Possible future post: "Making Kiwi work with py2exe"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-108372460955666705?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/108372460955666705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/108372460955666705'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/07/pygtk-py2exe-and-inno-setup-for-single.html' title='PyGTK, Py2exe, and Inno setup for single-file Windows installers'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-2424053894682904745</id><published>2007-06-30T09:06:00.000Z</published><updated>2007-06-30T10:58:19.074Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='ide'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pida'/><title type='text'>Latest PIDA Release - Explicit is better than implicit</title><content type='html'>Well, congratulations to all the developers, we have just released PIDA 0.5. This release is probably the most significant in that we are finally happy with the core architecture.&lt;br /&gt;&lt;br /&gt;Although not everyone was agreed on how to do it, I was quite strict and employed this famous rule of thumb:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Explicit is better than implicit&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;And honestly, it really is. Previous versions of PIDA were dogged by overuse of magic and abuse of the declarative class syntax (and the associated metaclass madness). This caused one major problem. Things that should have been happening at runtime were happening at class declaration time. Which is fine if you can get your brain around it, but really evil when you want to reverse these things.&lt;br /&gt;&lt;br /&gt;As an example, PIDA services (and a service is like a plugin) can basically do anything to the application (a bit like the Eclipse plugin architecture - but we think better!). One example of this "doing anything" is to define global configuration options.&lt;br /&gt;&lt;br /&gt;In PIDA 0.4 options where defined something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class MyService:&lt;br /&gt;class Options:&lt;br /&gt;    class Opt1:&lt;br /&gt;        """Opt1 documentation"""&lt;br /&gt;        type = STRING&lt;br /&gt;        default = 'Opt1'&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;When the service was instantiated, some metaclass magic would convert the option definition to options objects which could be used elsewhere in the application. This kind of declarative style has been popularised with ORM's like SQLObject, Django and extensions for SQLAlchemy, and is pretty nice to use and read.&lt;br /&gt;&lt;br /&gt;The problem with it is that once options have been created and magically sent off into the ether, they are there, and removing them involves some similarly evil trickery. For this reason services were almost impossible to load at runtime and moreover almost impossible to unload at runtime.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So for 0.5 we did away with the class declarative syntax (with some annoyance from some of the developers) and replaced it with something more like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;self.add_option(&lt;br /&gt;name='Opt 1',&lt;br /&gt;type=STRING,&lt;br /&gt;default='Opt1',&lt;br /&gt;doc = _('Documentation for opt1')&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ok, so this is a bit more annoying to write, but I think one can get used to it easily enough. Clever readers will notice that the documentation string is now translatable. And the major bonus is that because this is not happening in the ether, self.remove_option() is an obvious possibility.&lt;br /&gt;&lt;br /&gt;Unfortunately, implementing this for the various pluggable components of PIDA: Options, Events, Commands, Actions, Contexts, Views required a complete rewrite. They say never rewrite an application, but we have done, and with great success.&lt;br /&gt;&lt;br /&gt;So, since this blog is just an obvious advertisement for a release, please visit the website at &lt;a href="http://pida.co.uk/"&gt;http://pida.co.uk/&lt;/a&gt;, and download the latest version. The feature-improvement is huge, and we are one step closer to bringing the world the One True IDE (tm). We also have Emacs support in this release which should appeal to some people.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-2424053894682904745?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2424053894682904745'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2424053894682904745'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/06/latest-pida-release-explicit-is-better.html' title='Latest PIDA Release - Explicit is better than implicit'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-1989099919693722254</id><published>2007-03-21T23:39:00.000Z</published><updated>2007-03-24T15:58:05.677Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='glade3 custom widget'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pygtk'/><category scheme='http://www.blogger.com/atom/ns#' term='glade3'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Custom PyGTK Widgets in Glade3: Part 2: Custom widget adaptors</title><content type='html'>In the last post about custom glade widgets, we briefly discussed how to add your PyGTK custom widgets to glade-3 so that they can be used in a user interface designer. In this post, this shall be extended to include how you can create a custom adaptor for a PyGTK widget to define additional behaviour.&lt;br /&gt;&lt;br /&gt;We shall be using a custom widget as an example, which I have called a "Service View". We plan that this widget has a content section, which is the main part, and also contains a close button at the bottom. We will use this widget as a general dockable view, kind of like a dialog which is a widget rather than a top-level window. This may seem utterly pointless, but it should be a useful component in an application that generates many different views in notebooks, like an IDE: Debugger, Terminal, Documentation Browser (etc). These can all share the same basic layout, but just replace the single main part of the widget.&lt;br /&gt;&lt;br /&gt;First let's write a widget:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import gtk&lt;br /&gt;&lt;br /&gt;class ServiceView(gtk.VBox):&lt;br /&gt;&lt;br /&gt;__gtype_name__ = 'ServiceView'&lt;br /&gt;&lt;br /&gt;def __init__(self):&lt;br /&gt;   self.frame = gtk.Frame()&lt;br /&gt;   self.pack_start(self.frame)&lt;br /&gt;   self._bb = gtk.HButtonBox()&lt;br /&gt;   self._bb.set_layout(gtk.BUTTONBOX_END)&lt;br /&gt;   self._bb.pack_start(gtk.Button(stock=gtk.STOCK_CLOSE))&lt;br /&gt;   self.pack_start(self._bb, expand=False)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, in this widget, we want to be able to add a single child component inside the frame, self.frame, and also to be able to set the label of self.frame.&lt;br /&gt;&lt;br /&gt;If we add this widget to Glade3 using a catalog (in pywidgets.xml) such as:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;glade-catalog name="pywidgets" library="pywidgets"&lt;br /&gt;domain="glade-3" depends="gtk+" language="python"&amp;gt;&lt;br /&gt;&amp;lt;glade-widget-classes&amp;gt;&lt;br /&gt;&amp;lt;glade-widget-class&lt;br /&gt;title="Service View"  &lt;br /&gt;name="ServiceView"      &lt;br /&gt;generic-name="serviceview"&amp;gt;&lt;br /&gt;&amp;lt;/glade-widget-class&amp;gt;&lt;br /&gt;&amp;lt;/glade-widget-classes&amp;gt;&lt;br /&gt;&amp;lt;glade-widget-group name="PythonWidgets"&lt;br /&gt;title="Python Widgets"&amp;gt;&lt;br /&gt;&amp;lt;glade-widget-class-ref&lt;br /&gt;name="ServiceView"/&amp;gt;&lt;br /&gt;&amp;lt;/glade-widget-group&amp;gt;&lt;br /&gt;&amp;lt;/glade-catalog&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;and support code (in pywidgets.py) which simply imports the widget like so:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from serviceview import ServiceView&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(where the module serviceview.py is in the python path, and contains the widget above)&lt;br /&gt;&lt;br /&gt;Install the catalog and module file in the right places, and fire up glade-3. Create a window, and add our custom widget to it. Immediately you will be queried with an input dialog as to the required size of the widget. Once you have answered this, the widget will be created.&lt;br /&gt;&lt;br /&gt;Problems? Yes, it doesn't work as we would like in the following ways:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The query asking us the size of the widget&lt;/li&gt;&lt;li&gt;The grey extra sections which you can add widgets to outside our frame&lt;/li&gt;&lt;li&gt;The inability to change the contents of the frame, and its label&lt;/li&gt;&lt;/ul&gt;All these problems stem from the same issue, that our widget is a subclass of gtk.VBox, and so will be treated as a VBox for the extent of its lifetime.&lt;br /&gt;&lt;br /&gt;So how can we fix these things? We need to do two things. Firstly override some properties in the catalog file, and secondly create a custom adaptor. (We might one day be able to do all these things from the adaptor, but this is not possible at the moment, and it is one problem highlighted by the glade-3 developers, that they have a "hybrid API".)&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Fixing the catalog&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Don't forget to &lt;a href="http://glade.gnome.org/docs/properties.html"&gt;read the excellent documentation&lt;/a&gt; at this point. Widgets in glade, and in the default catalog offer the ability to launch input windows for queries. This is not entirely necessary, as a sane default could be chosen, and then changed as required in the normal way, but it could be useful in some circumstances I guess. In this case, because we are using a subclass of the Box widget, it will ask us what size we would like it to be. Size is not a real property of the Box, it is a virtual property, and helps Glade know how to display it. If we look at this part of the default catalog, we can see this happening:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;glade-widget-class name="GtkBox" fixed="True"&lt;br /&gt;title="Box"&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;properties&amp;gt;&lt;br /&gt;&amp;lt;property save="False" query="True" id="size" default="3"&lt;br /&gt;name="Number of items"&amp;gt;&lt;br /&gt;&amp;lt;spec&amp;gt;glade_standard_int_spec&amp;lt;/spec&amp;gt;&lt;br /&gt;&amp;lt;tooltip&amp;gt;The number of items in the&lt;br /&gt;box&amp;lt;/tooltip&amp;gt;&lt;br /&gt;&amp;lt;verify-function&amp;gt;glade_gtk_box_verify_size&amp;lt;/verify-function&amp;gt;&lt;br /&gt;&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;/properties&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;/glade-widget-class&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Each property tag inside a properties tag inside glade-widget-class tag must have the &lt;span style="font-style: italic;"&gt;id&lt;/span&gt; attribute, but the rest shown here are optional. The important one for us here is &lt;tt&gt;query="True"&lt;/tt&gt; which causes the query box to be shown, and so we can replace that in our own catalog in a sort of a property "override". Note that save="False" is set on the property, because it is not a real property, only a virtual property, and so it will not be written to the glade XML file.&lt;br /&gt;&lt;br /&gt;Not only do we want to disable the query box, but we want to:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Set the default value of this to zero, so none of the grey areas are shown and no extra widgets can be added to the Box&lt;/li&gt;&lt;li&gt;Hide or disable the size widget&lt;/li&gt;&lt;/ol&gt;Again looking at the documentation, we discover that the default value can be set with &lt;tt&gt;default="0"&lt;/tt&gt; and the property can eithe be disabled with &lt;tt&gt;disabled="True"&lt;/tt&gt; or hidden with &lt;tt&gt;visible="False"&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;So the class definition of our catalog becomes:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;glade-widget-class title="Service View"      &lt;br /&gt;name="ServiceView"&lt;br /&gt;generic-name="serviceview"&amp;gt;&lt;br /&gt;&amp;lt;properties&amp;gt;&lt;br /&gt;&amp;lt;property id="size" query="False" default="0" visible="False"&lt;br /&gt;/&amp;gt;&lt;br /&gt;&amp;lt;/properties&amp;gt;&lt;br /&gt;&amp;lt;/glade-widget-class&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Reinstall the catalog, create a window, and add our widget. We see that we are no longer asked for the starting size, and the size value does not appear in our property list. There are a few properties of the Box left that we don't really want here, and removing them is left as an exercise for the reader, and we shall move on.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Using a Custom Adaptor&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;The adaptor class should appear in your support code module, and should be referenced in your catalog, for example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;glade-widget-class title="Service View" name="ServiceView" adaptor="ServiceViewAdaptor"&amp;gt;&lt;br /&gt;&amp;lt;/glade-widget-class&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Which references a Class that registers the __gtype_name__ of ServiceView Adaptor. The class should inherit from glade.get_adaptor_for_type('SomeGTKType'), and for our case of a VBox subclass, we could use glade.get_adaptor_for_type('GtkVBox') as our adaptor superclass (which it was automatically using itself), like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import glade&lt;br /&gt;&lt;br /&gt;class ServiceViewAdaptor(glade.get_adaptor_for_type('GtkVBox')):&lt;br /&gt;   __gtype_name__ = 'ServiceViewAdaptor'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the adaptor code, glade is a special Python module only available within runtime glade, and it contains the glade user interface bindings. You can get it's documentation at runtime if you want to have a look, but we will be playing with this stuff in subsequent posts.&lt;br /&gt;&lt;br /&gt;So far we have not overridden anything, and our running code should behave exactly the same as it was previously, but with our named custom adaptor (which doesn't do anything).&lt;br /&gt;&lt;br /&gt;Now we can start adding methods to our custom adaptor. In general, the available methods that will be called are of the form:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;do_&lt;span style="font-style: italic;"&gt;something&lt;/span&gt;&lt;span&gt;(self, to_object, *args)&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Where something is the action, and to_object is the created object in the user interface. An example of this will be our first method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def do_post_create(self, obj, reason):&lt;br /&gt;   # Create one of those nice-looking grey areas from Glade that mean that you can add   something to this widget&lt;br /&gt;   obj.frame.add(gobject.new('GladePlaceholder&lt;span&gt;'))&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;At this stage, if you like, you can install your catalog and support code, and test this widget out. You will see that the grey area appears correctly inside our frame. Now try to add something to it, like a label. You will notice that the label is just added to the end of the VBox, where we really don't want it. For this to be fixed we need to override our next few methods, the child methods:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def do_get_children(self, obj):&lt;br /&gt;   """Called when glade wants the children of a widget"""&lt;br /&gt;   return obj.frame.get_children()&lt;br /&gt;&lt;br /&gt;def do_add(self, obj, child):&lt;br /&gt;   """Called to add a child to the widget"""&lt;br /&gt;   obj.frame.add(child)&lt;br /&gt;&lt;br /&gt;def do_remove(self, sv, child):&lt;br /&gt;   """Called to remove a child from our widget"""&lt;br /&gt;   obj.frame.remove(child)&lt;br /&gt;&lt;br /&gt;def do_replace_child(self, obj, old, new):&lt;br /&gt;   """&lt;br /&gt;   Called to replace the child of our widget.&lt;br /&gt;   Note that this is what is called by glade when you delete,&lt;br /&gt;   or cut a widget since it actually replaces the widget with a&lt;br /&gt;   GladePlaceholder&lt;br /&gt;   """&lt;br /&gt;   obj.frame.remove(old)&lt;br /&gt;   obj.frame.add(new)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The implementation of these methods is particularly straight-forward, and they achieve our goals. The trick is knowing what does what. The get_children method needs to be in place so that the Glade widget hierarchy appears correctly with the child widget. The do_add, do_remove, and do_replace methods are for exactly those things, adding, removing and replacing widgets.&lt;br /&gt;&lt;br /&gt;If you install your catalog and support module once more, you will notice that now it actually works. The widget is added in the correct place, and the hierarchy appears correctly with a single child on your widget. You will have a few warnings from GTK about child properties, and these are silenced by writing child_property methods:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def do_child_get_property(self, obj, child, prop):&lt;br /&gt;   """Called to retrieve a property value"""&lt;br /&gt;   if prop in ['expand', 'fill']:&lt;br /&gt;       return True&lt;br /&gt;   elif prop == 'padding':&lt;br /&gt;       return 0&lt;br /&gt;   elif prop == 'position':&lt;br /&gt;       return 0&lt;br /&gt;   elif prop == 'pack-type':&lt;br /&gt;       return gtk.PACK_START&lt;br /&gt;   return True&lt;br /&gt;&lt;br /&gt;def do_child_set_property(self, obj, child, prop, val):&lt;br /&gt;   """Called to set a property value"""&lt;br /&gt;   if prop in ['expand', 'fill', 'padding', 'pack-type']:&lt;br /&gt;       pass&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Because our widget is behaving like a real container (although just via the adaptor), it will get queried as to how to set child properties. Most of these are not important for us, because a Frame has very little in the way of packing and expanding, and since it is a Bin, it has no spacing. So we can just return simple values. Likewise we don't need to set anything there.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Trying It Out&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;So now we have our fully functional widget in Glade. Try it out. Make a new project, add our widget to a window, add an example widget to the container and then load it up with some code such as (for a file called 'test.glade'):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import gtk&lt;br /&gt;import gtk.glade&lt;br /&gt;&lt;br /&gt;# Make sure this type is registered with gobject so glade can load it&lt;br /&gt;from serviceview import ServiceView&lt;br /&gt;&lt;br /&gt;gtk.glade.XML('test.glade').get_widget('window1').show_all()&lt;br /&gt;gtk.main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;How did it look? Terrible! Yes, the glade loader has put our child widget inside the VBox, rather than inside the Frame! Disaster! Except when you realise what the glade loader is doing. It is calling a method on the ServiceView object to add children.&lt;br /&gt;&lt;br /&gt;Because it is a VBox subclass, this method behaves as it would for VBoxes (to add an object to the end). There is a slight complication. The add method that we have available in a gtk.VBox in Python is not what Glade is using. It actually uses the method that Glade uses too. To override the method that Glade uses, we need to implement the method &lt;span style="font-style: italic;"&gt;do_add&lt;/span&gt; which is kind of like a way of saying "override the C add".&lt;br /&gt;&lt;br /&gt;Once we have implemented this like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def do_add(self, child):&lt;br /&gt;  self.frame.add(child)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The widget loads with the child in the correct place.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Discussion&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;For those who wish to play around with this system, you might try (now that you have implemented do_add in the widget itself, to remove the do_add method of the custom adaptor. And you will see that this does in fact work as we would want. You could furthermore use the GtkFrame adaptor as the superclass for our adaptor, and see that that has the correct effect.&lt;br /&gt;&lt;br /&gt;This leads to a general question as to where you should implement your widget functionality, in your Widget, or in your Adaptor. We could override the entire Container Interface in our widget, and it would behave correctly without an adaptor, but that has the disadvantage of not being able to behave like a VBox any more.&lt;br /&gt;&lt;br /&gt;In general the course you decide should be based on your own requirements, but it seems that at them moment, with Glade3 you will need a blend of:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Catalog for Glade3&lt;/li&gt;&lt;li&gt;Custom Adaptor for Glade3&lt;/li&gt;&lt;li&gt;Widget Methods for the Glade Loader&lt;/li&gt;&lt;/ol&gt;Still to come in this series of posts:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Custom widget editors&lt;/li&gt;&lt;li&gt;Controlling the Glade3 UI from Python&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-style: italic; font-weight: bold;"&gt;NB: The API is early and changing at this stage, and there is no emphasis on backward-compatibility, so be prepared to throw any code away!&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-1989099919693722254?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/1989099919693722254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/1989099919693722254'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/03/custom-pygtk-widgets-in-glade3-part-2.html' title='Custom PyGTK Widgets in Glade3: Part 2: Custom widget adaptors'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-2921145761097005242</id><published>2007-03-21T17:01:00.000Z</published><updated>2007-03-21T23:16:29.970Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='glade3 custom widget'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pygtk'/><category scheme='http://www.blogger.com/atom/ns#' term='glade3'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Custom PyGTK Widgets in Glade3</title><content type='html'>Glade 3, GTK User Interface Designer is really quite nice since release 3.1, since it does away with the gimp-like multi-window view and looks like a normal application. Another nice feature it has is being able to support widgets created in non-C (in our case PyGTK).&lt;br /&gt;&lt;br /&gt;Components of a plugin:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;A catalog file&lt;/li&gt;&lt;li&gt;A support module&lt;/li&gt;&lt;li&gt;Some icon pixmaps (optional)&lt;/li&gt;&lt;/ol&gt;We shall use, for our example, the Kiwi Hyperlink widget, (which I wrote). This widget is an EventBox subclass, so we shall illustrate turning off some of the additional properties that are not required in the user interface designer.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The catalog file&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;This is described in &lt;a href="http://glade.gnome.org/docs/catalogintro.html"&gt;http://glade.gnome.org/docs/catalogintro.html&lt;/a&gt; and the subsequent pages of the documentation. Essentially there are two sections of the catalog:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The Widget definitions&lt;/li&gt;&lt;li&gt;The Catalog list&lt;/li&gt;&lt;/ol&gt;The Widget Definitions are like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;glade-widget-classes&amp;gt;&lt;br /&gt;&amp;lt;glade-widget-class title="Hyper Link" name="HyperLink" /&amp;gt;&lt;br /&gt;&amp;lt;/glade-widget-classes&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;These are the most-often used attributes for the widget class, but there are a few others too that are outlined on the documentation page.&lt;br /&gt;&lt;br /&gt;The catalog listing is much more basic, and less variable, and takes the following form:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;glade-widget-group name="KiwiWidgets" title="Kiwi Widgets"&amp;gt;&lt;br /&gt; &amp;lt;glade-widget-class-ref name="HyperLink"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This lists all the widgets that are currently available. So far we have not strayed from the documentation, and this is exactly how you would add widgets that are non-PyGTK.&lt;br /&gt;&lt;br /&gt;What you have to do to make glade realise that you are using PyGTK widgets is use this in your main document element for the catalog:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;glade-catalog name="kiwiwidgets" library="kiwiwidgets" domain="glade-3" depends="gtk+" language="python"&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The &lt;tt&gt;language="python"&lt;/tt&gt; does that for you, and in addition the library attribute "kiwiwidgets" defines the location of our support library, which will be called kiwiwidgets.py.&lt;br /&gt;&lt;br /&gt;The catalog file should live with the other catalogs in the glade3 catalog directory, which is, on my system, at:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/usr/local/share/glade3/catalogs/kiwiwidgets.xml&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can turn various properties off from being displayed in the user interface designer using the &amp;lt;properties&amp;gt; tag in your widget-class tag. An example of this would be:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;property id="visible-window"&lt;br /&gt;        visible="False" /&amp;gt;&lt;br /&gt;&amp;lt;property id="text"&lt;br /&gt;        name="Text"&lt;br /&gt;        translatable="True" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ok, so we showed you two properties here. The first is a property of Event Box which we don't want shown inside the UI Designer (users don't care), and that is achieved with a simple &lt;tt&gt;visible="False"&lt;/tt&gt;. The second shows how to modify a property. We want the text of this hyperlink widget to be translatable, and access all the translation hooks in Glade with it.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;br /&gt;The support module&lt;br /&gt;&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;The support module is python code that goes with our widgets. In our basic example, we have not defined anything fancy like a custom adaptor, or even a custom editor which we could if we were feeling brave, but will probably leave that for another blog posting.&lt;br /&gt;&lt;br /&gt;The support module should live in the glade module directory, and be named as defined in the catalog file. On my system this belongs here:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/usr/local/lib/glade3/modules/kiwiwidgets.py&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;the code of the module in this simple case contains only one thing, an import statement on the HyperLink class (since that is all that is needed to register it as a GObject Type.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from kiwi.ui.hyperlink import HyperLink&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Glade3 takes care of the rest for us.&lt;br /&gt;&lt;br /&gt;That is all for now. Other items that should come up later include:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Defining custom adaptors&lt;/li&gt;&lt;li&gt;Defining custom widget editors&lt;/li&gt;&lt;li&gt;Silencing glade queries for box subclasses&lt;/li&gt;&lt;li&gt;Controlling the glade UI from Python&lt;/li&gt;&lt;/ul&gt;And I shall address these if there is any interest.&lt;br /&gt;&lt;br /&gt;NB: The API is early and changing at this stage, and there is no emphasis on backward-compatibility, so be prepared to throw any code away!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-2921145761097005242?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2921145761097005242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2921145761097005242'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/03/custom-pygtk-widgets-in-glade3.html' title='Custom PyGTK Widgets in Glade3'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-8790198613672364350</id><published>2007-03-19T03:05:00.001Z</published><updated>2007-03-19T22:04:27.716Z</updated><title type='text'>First Blog Post from the gblogger GUI</title><content type='html'>The gblogger GUI lives! It features listing the blogs for a user, listing the posts for a blog, new blog posting, and blog editing. Wysiwyg is achieved using GTKHTML3, and I have barely formatted this post because formatted posts seem to give 400 posting errors!&lt;br /&gt;&lt;br /&gt;The client lacks category management, and image uploading, which remain on the TODO list.&lt;br /&gt;&lt;br /&gt;(Now I am tentatively pressing the save button...after which I will re-edit the blog in Blogger.com, and add some screenshots!)&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_z4CWv2DsbGU/Rf3_kef-7eI/AAAAAAAAACQ/XOoE8UXh6Dw/s1600-h/he.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://3.bp.blogspot.com/_z4CWv2DsbGU/Rf3_kef-7eI/AAAAAAAAACQ/XOoE8UXh6Dw/s320/he.jpg" alt="" id="BLOGGER_PHOTO_ID_5043468159852408290" border="0" /&gt;&lt;/a&gt;&lt;div style="text-align: left;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_z4CWv2DsbGU/Rf3_def-7dI/AAAAAAAAACI/Y8-oXAIduF4/s1600-h/gb.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://3.bp.blogspot.com/_z4CWv2DsbGU/Rf3_def-7dI/AAAAAAAAACI/Y8-oXAIduF4/s320/gb.jpg" alt="" id="BLOGGER_PHOTO_ID_5043468039593323986" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-8790198613672364350?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/8790198613672364350'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/8790198613672364350'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/03/first-blog-post-from-gblogger-gui.html' title='First Blog Post from the gblogger GUI'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_z4CWv2DsbGU/Rf3_kef-7eI/AAAAAAAAACQ/XOoE8UXh6Dw/s72-c/he.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-7383513886167610873</id><published>2007-03-16T15:18:00.000Z</published><updated>2007-03-18T19:55:42.684Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='atom'/><category scheme='http://www.blogger.com/atom/ns#' term='google code'/><category scheme='http://www.blogger.com/atom/ns#' term='blogger'/><category scheme='http://www.blogger.com/atom/ns#' term='atom api'/><category scheme='http://www.blogger.com/atom/ns#' term='gdata'/><category scheme='http://www.blogger.com/atom/ns#' term='blogger.com'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Posting to Blogger.com from the command line</title><content type='html'>Well, you can tell I am procrastinating working today (but you can forgive me after the &lt;a href="http://pida.co.uk/"&gt;PIDA&lt;/a&gt; &lt;a href="http://code.google.com/soc/"&gt;GSoc&lt;/a&gt; Application was rejected as a mentoring organisation), but I have hacked up an early version of a library (for Python) to manage blogger.com posting.&lt;br /&gt;&lt;br /&gt;I was becoming slightly upset with Blogger.com, since it does some insane things like puts BR tags in the middle of PRE elements, which meant the syntax highlighting script I wanted to use was going nuts. I also gradually realised that none of the blogging clients work with the "new Blogger.com" although for me (who has only been blogging a few months) it has been the "only Blogger.com".&lt;br /&gt;&lt;br /&gt;So, with a quick search around for documentation from code.google.com, I was able to discover that Blogger.com uses the google GData API with a bit of Atom mixed in. I have no real ideas about these specifications, but &lt;a href="http://code.google.com/apis/blogger/gdata.html"&gt;http://code.google.com/apis/blogger/gdata.html&lt;/a&gt; explains it. Note that another document I found: &lt;a href="http://code.blogspot.com/archives/atom-docs.html"&gt;http://code.blogspot.com/archives/atom-docs.html&lt;/a&gt; seems depracated, but I can't confirm that at all.&lt;br /&gt;&lt;br /&gt;Python is my language of choice for these things, so I started hacking.&lt;br /&gt;&lt;br /&gt;Step 1: Authenticate with Google Login stuff. This is explained in &lt;a href="http://code.google.com/apis/accounts/AuthForInstalledApps.html"&gt;http://code.google.com/apis/accounts/AuthForInstalledApps.html&lt;/a&gt; but basically you need to:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Send a post request to &lt;code&gt;https://www.google.com/accounts/ClientLogin&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Set the service value of the data to "blogger" (this had me for a while)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Parse the body of the response for an "Auth" token&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;Include the header &lt;/code&gt;&lt;code&gt;&lt;/code&gt;Authorization: GoogleLogin auth=&lt;i&gt;yourAuthValue&lt;/i&gt;&lt;code&gt;&lt;/code&gt; in all your requests from thereon in (not obvious from the authentication docs)&lt;/li&gt;&lt;/ul&gt;Once you have successfully logged in you will want to get some information. Firstly the posting API works on the blog &lt;span style="font-style: italic;"&gt;ID&lt;/span&gt;, not the blog name. The &lt;span style="font-style: italic;"&gt;ID&lt;/span&gt; appears in the source of the blog page in this tag &lt;code&gt;&amp;lt;&lt;span class="start-tag"&gt;link&lt;/span&gt;&lt;span class="attribute-name"&gt; rel&lt;/span&gt;=&lt;span class="attribute-value"&gt;"service.post" &lt;/span&gt;&lt;span class="attribute-name"&gt;type&lt;/span&gt;=&lt;span class="attribute-value"&gt;"application/atom+xml" ...&lt;/span&gt;&lt;/code&gt; but you can also fetch it from the blog's feed from the blog's name (see the API documentation).&lt;br /&gt;&lt;br /&gt;Once you have this ID, posting is a matter of using the ID with your auth token, and entering data in an XML format as the post's body. Not forgetting to set the correct content-type for your post to "application/atom+xml". If you have posted correctly, the server returns a 201 Created response.&lt;br /&gt;&lt;br /&gt;I have spared you the boring details of making urllib/urllib2 work with these things, but I have written some sample code and a command line client which you can browse online here: &lt;a href="http://pygbloggerlib.googlecode.com/svn/trunk/gblogger/client.py"&gt;http://pygbloggerlib.googlecode.com/svn/trunk/gblogger/client.py&lt;/a&gt; as well as accessing the googlecode page (no releases yet) here: &lt;a href="http://code.google.com/p/pygbloggerlib/"&gt;http://code.google.com/p/pygbloggerlib/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;An example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; from gblogger.client import BlogClient&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; import getpass&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; passwd = getpass.getpass()&lt;br /&gt;Password:&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; c = BlogClient('my-test-atom-blog', 'aafshar@gmail.com', passwd)&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; c.post('this is a test post', 'this is the body of the test post with some &lt;b&gt;html&lt;/b&gt;')&lt;br /&gt;HTTP Error 201: Created&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;&lt;/pre&gt;You can actually see this post online (if I haven't deleted the test blog) here:  &lt;a href="http://my-test-atom-blog.blogspot.com/2007/03/this-is-test-post.html"&gt;http://my-test-atom-blog.blogspot.com/2007/03/this-is-test-post.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Since I never have any spare time, my plans for this library are a bit shaky. It is a toy library in that there are no real tests, and failure conditions are not really dealt with properly. Ideally I will:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;span class="attribute-value"&gt;Write a PIDA plugin for blogging&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="attribute-value"&gt;Merge it into something like &lt;a href="http://blogtk.sourceforge.net/"&gt;BloGTK&lt;/a&gt;, or write a miniature gui client of my own.&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;span class="attribute-value"&gt;&lt;br /&gt;I have no idea how to publicize this to the Blogger.com community, but if any of you big Blogger admins or google developers are interested in spreading the word, (or silencing the word!) please let me know.&lt;br /&gt;&lt;br /&gt;UPDATE:&lt;br /&gt;(some more useful links)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/apis/gdata/basics.html"&gt;http://code.google.com/apis/gdata/basics.html&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.google.com/base/api/demo/html/demo.html"&gt;http://www.google.com/base/api/demo/html/demo.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;UPDATE:&lt;br /&gt;&lt;br /&gt;Well, editing now works in the library! I know this because I accidentally overwrote this post! Lucky for the unnoficial planet python it was still lying around there!&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-7383513886167610873?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/7383513886167610873'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/7383513886167610873'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/03/posting-to-bloggercom-from-command-line.html' title='Posting to Blogger.com from the command line'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-2129696485706369054</id><published>2007-03-15T11:18:00.000Z</published><updated>2007-03-16T09:06:23.193Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pexpect'/><title type='text'>Using pexpect to control Django manage.py</title><content type='html'>In the early stages of developing a Django application, I was deleting my sqlite database, and rerunning:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;python manage.py syncdb&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Every few minutes as the database models were changing. You might consider this some kind of evil thing to do, because we &lt;span style="font-weight: bold;"&gt;should all have our database models in concrete before writing any code &lt;/span&gt;&lt;span&gt;but I am not that organised.&lt;br /&gt;&lt;br /&gt;One thing that was really distressing me was the fact that each time I ran a syncdb, I had to enter details for my default admin user.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;Username&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Email address&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Password&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Repeart Password&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;This was fine for the first 400 times, then I got bored. The solution was to use pexpect. (There are probably command line options that you can give manage.py, but finding them out would have been more effort than this hack)&lt;br /&gt;&lt;br /&gt;Pexpect, &lt;a href="http://pexpect.sourceforge.net/"&gt;http://pexpect.sourceforge.net/&lt;/a&gt; is a pure python module for running external commands and controlling them as if you were a user. It has good documentation, and is perfect for needs of this nature.&lt;br /&gt;&lt;br /&gt;A quick browse through the &lt;a href="http://pexpect.sourceforge.net/#overview"&gt;excellent documentation&lt;/a&gt;, and we were away. Here is the code:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="py" cols="80"&gt;&lt;br /&gt;PASSWORD = '123'    # replace this with something good&lt;br /&gt;EMAIL = 'aafshar@gmail.com'&lt;br /&gt;&lt;br /&gt;import pexpect&lt;br /&gt;&lt;br /&gt;# spawn the child process&lt;br /&gt;child = pexpect.spawn('python manage.py syncdb')&lt;br /&gt;&lt;br /&gt;# Wait for the application to give us this text (a regular expression)&lt;br /&gt;child.expect('Would you like to create one now.*')&lt;br /&gt;&lt;br /&gt;# Send this line in response&lt;br /&gt;child.sendline('yes')&lt;br /&gt;&lt;br /&gt;# Again wait for this, and send what is needed&lt;br /&gt;child.expect('Username.*')&lt;br /&gt;&lt;br /&gt;# A blank string for the default username&lt;br /&gt;child.sendline('')&lt;br /&gt;&lt;br /&gt;# And so it goes on&lt;br /&gt;child.expect('E-mail.*')&lt;br /&gt;child.sendline(EMAIL)&lt;br /&gt;child.expect('Password.*')&lt;br /&gt;child.sendline(PASSWORD)&lt;br /&gt;child.expect('Password.*')&lt;br /&gt;child.sendline(PASSWORD)&lt;br /&gt;&lt;br /&gt;# This means wait for the end of the child's output&lt;br /&gt;child.expect(pexpect.EOF, timeout=None)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And we are done. This runs the command, and enters the correct details without having to be interactive, and saving a load of time and typing.&lt;br /&gt;&lt;br /&gt;I actually combined this script with an initial data-import setup script which loaded the database with the initial data, for added fruitiness.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-2129696485706369054?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2129696485706369054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2129696485706369054'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/03/using-pexpect-to-control-django.html' title='Using pexpect to control Django manage.py'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-7830481214118074302</id><published>2007-03-13T16:40:00.000Z</published><updated>2007-03-16T09:07:30.598Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='unit testing'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pygtk'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Unit Testing PyGTK</title><content type='html'>Unit testing a GUI has always been something that scares me, and scares other people too.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Because of this there are about a gazillion tools that behave in different ways, for example in Python, just have a look at:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://pycheesecake.org/wiki/PythonTestingToolsTaxonomy#GUITestingTools"&gt;http://pycheesecake.org/wiki/PythonTestingToolsTaxonomy#GUITestingTools&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(Since the whole world is obsessed with Web Applications, and this is becoming perversely true even in Python circles, some of these GUI Testing tools are actually web testing tools, but never mind that.)&lt;br /&gt;&lt;br /&gt;How do I do it? Well, we may talk in another blog about one of those tools, the awesome hack that is &lt;a href="http://www.async.com.br/projects/kiwi/"&gt;kiwi.ui.test&lt;/a&gt; but it is slightly restrictive on what you want to do, and how you should do it. Specifically, it needs you to have every widget named (which is fine when testing Glade-created interfaces, but a pain when hand-building them).&lt;br /&gt;&lt;br /&gt;Enter this one function that I found in the kiwi source. Kiwi is LGPL (whatever that means, but you should &lt;a href="http://www.gnu.org/licenses/lgpl.html"&gt;read the license&lt;/a&gt; if you are going to use it).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import time&lt;br /&gt;import gtk&lt;br /&gt;&lt;br /&gt;# Stolen from Kiwi&lt;br /&gt;def refresh_gui(delay=0):&lt;br /&gt;  while gtk.events_pending():&lt;br /&gt;      gtk.main_iteration_do(block=False)&lt;br /&gt;  time.sleep(delay)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Simple, and tiny, and it has the effect of turning your awful asynchronous run-events-when-it-wants-to User Interface into a simple synchronous thing.&lt;br /&gt;&lt;br /&gt;How? Well simply it runs all the events that are waiting in the gtk event queue, as if you ran gtk.main() for a short while until it was done with everything.&lt;br /&gt;&lt;br /&gt;The optional delay argument is a short delay that might prove useful, but I have yet to actually use it.&lt;br /&gt;&lt;br /&gt;Let's look at an example of how to use this in a unit test.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from unittest import TestCase, main&lt;br /&gt;import gtk&lt;br /&gt;&lt;br /&gt;class MyView(gtk.VBox):&lt;br /&gt;&lt;br /&gt;  def __init__(self):&lt;br /&gt;      super(MyView, self).__init__()&lt;br /&gt;      self._button  = gtk.Button('Click Me')&lt;br /&gt;      self._label = gtk.Label()&lt;br /&gt;      self.pack_start(self._button)&lt;br /&gt;      self.pack_start(self._label)&lt;br /&gt;      self._count = 0&lt;br /&gt;      self._button.connect('clicked', self.on_button_clicked)&lt;br /&gt;&lt;br /&gt;  def on_button_clicked(self, button):&lt;br /&gt;      self._count = self._count + 1&lt;br /&gt;      self._label.set_text('clicked %s times' % self._count)&lt;br /&gt;    &lt;br /&gt;class MyViewTest(TestCase):&lt;br /&gt;&lt;br /&gt;  def setUp(self):&lt;br /&gt;      self._v = MyView()&lt;br /&gt;&lt;br /&gt;  def test_count(self):&lt;br /&gt;      self.assertEqual(self._v._count, 0)&lt;br /&gt;      self._v._button.clicked()&lt;br /&gt;      refresh_gui()&lt;br /&gt;      self.assertEqual(self._v._count, 1)&lt;br /&gt;&lt;br /&gt;   def test_label(self):&lt;br /&gt;     self._v._button.clicked()&lt;br /&gt;     refresh_gui()&lt;br /&gt;     self.assertEqual(self._v._label.get_text(), 'clicked 1 times')&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;  main()&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And we run it:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Ran 2 tests in 0.013s&lt;br /&gt;&lt;br /&gt;OK&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Great!&lt;br /&gt;&lt;br /&gt;Things we should note:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;We never had to put our custom widget into a window&lt;/li&gt;&lt;li&gt;We never had to display it to the outside world&lt;/li&gt;&lt;li&gt;We can be sure asynchronous things like signal callbacks have happened&lt;/li&gt;&lt;li&gt;We can treat our UI as a purely synchronous application&lt;/li&gt;&lt;li&gt;We use unittest.TestCase, we don't need any fancy UI testing framework's subclasses.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Some clever people might try this test without the refresh_gui call, and may even notice that the tests pass, but that is because it is extremely simple in this example. Try it with more complicated stuff that is scheduled in gobject's idle time, and your results will start to become inconsistent.&lt;br /&gt;&lt;br /&gt;Conclusion: Keep it simple! In a GUI Testing War, I would hedge this function against anything.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-7830481214118074302?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/7830481214118074302'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/7830481214118074302'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/03/unit-testing-pygtk.html' title='Unit Testing PyGTK'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-200772669573840679</id><published>2007-02-07T22:50:00.000Z</published><updated>2007-02-07T23:02:03.385Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='newforms'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='pylons'/><title type='text'>Thoughts on Django</title><content type='html'>What follows are some thoughts on &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;django&lt;/span&gt; written by Isaac Alston, a friend of mine from university. He originally wrote it as a &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;facebook&lt;/span&gt; note, but after reading it I asked if I could re-post it here. I think he gives an &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_2"&gt;enlightening&lt;/span&gt; point of view on the experience of a python newcomer beginning to use &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;django&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Here it is:&lt;br /&gt;&lt;br /&gt;This is a short review of my experience with Django (&lt;a href="http://www.djangoproject.com/" target="_blank"&gt;http://www.djangoproject.c&lt;wbr&gt;om&lt;/a&gt;). Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.&lt;br /&gt;&lt;br /&gt;In November, I was informed that the company I am working for would be leaving PHP behind. We had been looking for a faster, and cleaner development tool for a while and were deciding between Ruby on Rails  &lt;span style="text-decoration: underline;"&gt;(&lt;/span&gt;&lt;a href="http://www.rubyonrails.org/" target="_blank"&gt;http://www.rubyonrails.org/&lt;/a&gt;) and Django. I had a preference for Django immediately, since I could already program in Python, whereas Rails required Ruby skills.&lt;br /&gt;&lt;br /&gt;After much debate and discussion, my company eventually chose to use Rails. My co-worker, flatmate and (dare I say it) friend, Maciej, is currently working on our latest project for a fish company with Rails, and has produced some spectacular results. I was determined that I could do the same with Python, and so (instead of learning Ruby) launched into the docs for Django. At about the same time, I was approached by my church and asked if I would be willing to develop a new website for them. I agreed to the suggestion, because I knew that the opportunity would allow me to learn Django at a slower pace, and in a low-pressure environment.&lt;br /&gt;&lt;br /&gt;I was initially very impressed by Django. I have been coding with PHP for about three years now, and it's been slow, messy and difficult to work with at times. I was amazed at such 'auto-generation' features including the database API, and the Admin centre.&lt;br /&gt;&lt;br /&gt;Three weeks into development however, Django's niggles and problems can be seen in full view. The main problem with Django is that its Admin centre is not flexible enough.&lt;br /&gt;&lt;br /&gt;Django is in a constant state of flux because the developers are rewriting the forms model (the way in which forms on a website interact with the database). 'Newforms' as they are called, are going to replace 'old-forms'.&lt;br /&gt;&lt;br /&gt;At present, the Admin panel still makes use of the old-forms model, and will not be rewritten with newforms for a while. This is the primary cause of it's inflexibility. For example, I wanted to perform some custom validation of uploads with the default Admin. All I wanted to do was check whether the file was of a 'music type' ie, mp3 or ogg or some such format. Yet it appears that this is either very difficult or impossible. I say this because I have received no replies from the Django mailing list.&lt;br /&gt;&lt;br /&gt;It's very nice having the 'free' Admin there, but in the end, if one wants to build something really complex, one must develop a custom admin panel from scratch. This I suppose, isn't so terrible because that is what I was doing with PHP anyway; however, it is infuriating that I am forced to write additional code, when the current Admin is so close to being able to do it.&lt;br /&gt;&lt;br /&gt;The current documentation recommends to build any new websites with the newforms model, and so I downloaded the development version and set to work. This was all well and good until I discovered that there is nearly no documentation for the newforms library. Indeed, as a result, when I set about adding a simple contact form to my site, it took three hours, THREE HOURS! This was because I was literally forced to browse the source code for Django, to try and figure out what was going on.&lt;br /&gt;&lt;br /&gt;The third problem with Django, is that doing AJAX requires a little work (and of course, programmers are lazy). Rails has nice 'helper functions' which generate JavaScript for you, which Django lacks. This was the result of another huge debate that went on within the Django community, who didn't want to be seen as 'Rails copycats'. Anyway, the maintainers eventually decided that web developers should (and I paraphrase from a rant): "grow up and learn to use JavaScript properly", so for people like me who have managed to get by copy-pasting JavaScript all these years, it means we'll have to sit down and learn it. Why should we though? Rails developers aren't... ;-).&lt;br /&gt;&lt;br /&gt;I think that Django will be great when it gets the newforms library fully implemented (in the Admin as well) and in the current release (rather than the development version which constantly changes). I think it'll be great when there's some more documentation for newforms. Until then, I'm probably going to move to Pylons (&lt;a href="http://www.pylonshq.com/" target="_blank"&gt;http://www.pylonshq.com/&lt;/a&gt;), which a friend has recommended and which seems to be more Rails-like, offering a lot more flexibility. It also offers the nice 'AJAX helper functions' which I so desire, and also seems to have a more powerful ORM (Object Relational Mapping) implementation than Django's. I could of course learn Ruby, but, well... yeah.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-200772669573840679?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/200772669573840679'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/200772669573840679'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/02/thoughts-on-django.html' title='Thoughts on Django'/><author><name>Steven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-4648540759749826835</id><published>2007-01-11T22:03:00.000Z</published><updated>2007-01-12T18:57:39.059Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='newforms'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>django newforms for models</title><content type='html'>The way django does forms is in a state of transition. There's the old (or, depending on your point of view, current) way, &lt;a href="http://www.djangoproject.com/documentation/forms/"&gt;manipulators&lt;/a&gt;, and the new way, &lt;a href="http://www.djangoproject.com/documentation/newforms/"&gt;newforms&lt;/a&gt;. newforms is only available in the svn version of django at present, and is still incomplete. Despite this, newforms is usable and provides several compelling advantages over the old way. The docs at present are a little thin (although improving quickly), but the &lt;a href="http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/tests.py"&gt;unit tests&lt;/a&gt; are full of useful examples (the stuff towards the end is the most interesting, and shows off some of the flexibility of newforms).&lt;br /&gt;&lt;br /&gt;One thing that would be nice, though, is a way of easily creating a form class that uses a model class's fields. If we have this, we can avoid needless and tedious repetition.&lt;br /&gt;&lt;br /&gt;Django recently got a function that can be used for this, newforms.form_for_model. It's a start, but there are a few things I'd like it to do that it currently can't. I'd like to use the model's specified defaults, if any, as the default values for fields, and I'd like the help_text entries to be available in the fields for rendering. This saves having to specify these seperately in each form that uses the model fields. It'd also be nice to be able to specify declaritively additional fields to be used in the form and I'd like a way to easily override the default widgets for a field.&lt;br /&gt;&lt;br /&gt;It turns out it's fairly quick and easy to write our own solution to this. I've gone with a metaclass because I like doing things declaritively, and django already provides a metaclass for specifying fields that we can conveniently inherit from to get our "extra fields" feature for free. So here it is:&lt;br /&gt;&lt;br /&gt;&lt;pre style="overflow: auto;"&gt;&lt;br /&gt;from django.newforms import forms, widgets&lt;br /&gt;from django.db.models.fields import NOT_PROVIDED&lt;br /&gt;&lt;br /&gt;class DuplicateFieldError(Exception):&lt;br /&gt;   pass&lt;br /&gt;&lt;br /&gt;class MetaModelForm(forms.DeclarativeFieldsMetaclass):&lt;br /&gt;   """Creates form fields from the model in the 'model' class attribute and&lt;br /&gt;   adds them to the created form."""&lt;br /&gt;&lt;br /&gt;   # The default custom widgets we will use.&lt;br /&gt;   _default_widgets = {&lt;br /&gt;       'TextField': lambda _: widgets.Textarea({'rows': '10', 'cols': '40'}),&lt;br /&gt;       'ImageField': lambda _: widgets.FileInput()&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   # This can be overridden to provide custom widgets for particular model&lt;br /&gt;   # fields. It should be a mapping from model field class names (ie&lt;br /&gt;   # 'CharField') to callables that take one argument, the model field,&lt;br /&gt;   # returning a Widget instance. See _default_widgets above.&lt;br /&gt;   model_widgets = None&lt;br /&gt;   model = None&lt;br /&gt;  &lt;br /&gt;   def __init__(cls, name, bases, attrs):&lt;br /&gt;       super(MetaModelForm, cls).__init__(name, bases, attrs)&lt;br /&gt;       if cls.model is None:&lt;br /&gt;           # We don't need to do anything.&lt;br /&gt;           return&lt;br /&gt;       if cls.model_widgets is None:&lt;br /&gt;           cls.model_widgets = {}&lt;br /&gt;      &lt;br /&gt;       cls._update_fields()&lt;br /&gt;&lt;br /&gt;   def _update_fields(cls):&lt;br /&gt;       """Update the form's fields with fields created from cls.model. Bits&lt;br /&gt;       of this are stolen from newforms.form_for_model"""&lt;br /&gt;       opts = cls.model._meta&lt;br /&gt;       # Create cls._widgets from defaults + overrides&lt;br /&gt;       widgets = cls._default_widgets.copy()&lt;br /&gt;       widgets.update(cls.model_widgets)&lt;br /&gt;       cls._widgets = widgets&lt;br /&gt;       for f in opts.fields + opts.many_to_many:&lt;br /&gt;           if f.name in cls.fields:&lt;br /&gt;               # We wimp out and start crying rather than try to resolve this&lt;br /&gt;               raise DuplicateFieldError('Field %s is duplicated in the '\&lt;br /&gt;                                         'model' % f.name)&lt;br /&gt;           # Attempt to create a form field for the model field.&lt;br /&gt;           form_field = cls._create_field(f)&lt;br /&gt;           if form_field:&lt;br /&gt;               # If we successfully get a form field, update the Form's fields&lt;br /&gt;               # attribute with the new field.&lt;br /&gt;               cls.fields[f.name] = form_field&lt;br /&gt;      &lt;br /&gt;   def _create_field(cls, f):&lt;br /&gt;       """Create a form field from the given model field if possible,&lt;br /&gt;       otherwise return None."""&lt;br /&gt;       if f.default == NOT_PROVIDED:&lt;br /&gt;           formfield = f.formfield()&lt;br /&gt;       else:&lt;br /&gt;           # Take the inital field value from the model field's default.&lt;br /&gt;           formfield = f.formfield(f.default)&lt;br /&gt;          &lt;br /&gt;       if formfield:       &lt;br /&gt;           try:&lt;br /&gt;               # Check if there is a given field-specific widget&lt;br /&gt;               widget = cls._widgets[type(f).__name__](f)&lt;br /&gt;           except KeyError:&lt;br /&gt;               # If not, use the form field's default widget.&lt;br /&gt;               widget = formfield.widget&lt;br /&gt;           else:&lt;br /&gt;               # If a custom widget was found, the field must be updated&lt;br /&gt;               # with it. We check for extra attributes provided for this&lt;br /&gt;               # widget by the field, and update the widget accordingly.&lt;br /&gt;               extra_attrs = formfield.widget_attrs(widget)&lt;br /&gt;               if extra_attrs:&lt;br /&gt;                   widget.attrs.update(extra_attrs)&lt;br /&gt;               formfield.widget = widget&lt;br /&gt;  &lt;br /&gt;           # Add help text. Until there is a better way. ;)&lt;br /&gt;           formfield.help_text = f.help_text&lt;br /&gt;       return formfield&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I have tried to comment the code so it is clear what is going on. model_widgets is a dictionary of callables that take the model field as an argument, rather than just a bunch of widget instances. This is so aspects of the model can be taken into account when deciding the widget (or widget attributes) to use.&lt;br /&gt;&lt;br /&gt;Say we have a model as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre style="overflow: auto;"&gt;&lt;br /&gt;class Person(models.Model):&lt;br /&gt;   first_name = models.CharField(maxlength=20)&lt;br /&gt;   last_name = models.CharField(maxlength=20)&lt;br /&gt;   address = models.TextField(help_text='The home address of the person.')&lt;br /&gt;   age = models.PositiveSmallIntegerField(default=21)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We can create a form like so:&lt;br /&gt;&lt;br /&gt;&lt;pre style="overflow: auto;"&gt;&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; class PersonForm(forms.BaseForm):&lt;br /&gt;...     __metaclass__ = MetaModelForm&lt;br /&gt;...     model_widgets = {&lt;br /&gt;...         'PositiveSmallIntegerField': lambda _: widgets.TextInput({'size': 3})&lt;br /&gt;...     }&lt;br /&gt;...     model = Person&lt;br /&gt;...     date_of_birth = fields.EmailField()&lt;br /&gt;...     email = fields.EmailField()&lt;br /&gt;...&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; form = PersonForm()&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; print form&lt;br /&gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;&amp;lt;label for="id_date_of_birth"&amp;gt;Date of birth:&amp;lt;/label&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type="text" name="date_of_birth" id="id_date_of_birth" /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;&amp;lt;label for="id_email"&amp;gt;Email:&amp;lt;/label&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type="text" name="email" id="id_email" /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;&amp;lt;label for="id_first_name"&amp;gt;First name:&amp;lt;/label&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input id="id_first_name" type="text" name="first_name" maxlength="20" /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;&amp;lt;label for="id_last_name"&amp;gt;Last name:&amp;lt;/label&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input id="id_last_name" type="text" name="last_name" maxlength="20" /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;&amp;lt;label for="id_address"&amp;gt;Address:&amp;lt;/label&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;td&amp;gt;&amp;lt;textarea id="id_address" rows="10" cols="40" name="address"&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;&amp;lt;label for="id_age"&amp;gt;Age:&amp;lt;/label&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input id="id_age" type="text" name="age" value="21" size="3" /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Defining model_widgets is optional, but I wanted to show that it works (notice that the "age" field has a size="3" attribute). This shouldn't be hard to modify or extend so that it does exactly what you want. One possibility I'm considering is copying validator methods from the model to the form.&lt;br /&gt;&lt;br /&gt;I should mention field ordering. date_of_birth and email appear first because they were created first (before the metaclass methods ran). newforms uses an internal counter to keep track of the order in which fields are created, and by default renders them in order of creation. This isn't difficult to work around if you desire it. However, instead of working around it, I use a different method, taking advantage of the fact it is very simple to subclass BaseForm and customise the automatic rendering.&lt;br /&gt;&lt;br /&gt;Tomorrow I will post a simple form subclass for rendering a form using fieldsets (specified in a similar way to django's auto-generated &lt;a href="http://www.djangoproject.com/documentation/tutorial2/"&gt;admin&lt;/a&gt;), including a custom row format-string that includes help_text. I'll link to it from here when I post it.&lt;br /&gt;&lt;br /&gt;It's late here, though, and I have a lecture tomorrow at the disgustingly early time of 11am.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-4648540759749826835?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/4648540759749826835'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/4648540759749826835'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2007/01/django-newforms-for-models.html' title='django newforms for models'/><author><name>Steven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-207267571107479293</id><published>2006-12-29T11:01:00.000Z</published><updated>2006-12-31T00:45:02.994Z</updated><title type='text'>What is wrong with Berlios?</title><content type='html'>Berlios hosting was once a great replacement for Sourceforge and others. But these days it seems we have more downtime than uptime. I don't blame them (I am sure they have their reasons why nothing is ever available) but I do understand that I need something better to host my open source projects with.&lt;br /&gt;&lt;br /&gt;While we do bug tracking, and source-code hosting with Malone and Launchpad already, there is no provision on the Launchpad for hosting distribution files, and so we have had to painfully search for alternatives.&lt;br /&gt;&lt;br /&gt;For now we are going to go with Google code. This is not perfect for our needs, but it will replace everything we need Berlios for except for hosting the PIDA website, which can be easily arranged elsewhere.&lt;br /&gt;&lt;br /&gt;The main pain will be updating all the links around the web that point to the Berlios website. But that will come in time, and I am sure that PIDA users are bright enough to click a redirect link!&lt;br /&gt;&lt;br /&gt;For now the new project page is:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/pida/"&gt;http://code.google.com/p/pida/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And we also have a new &lt;a href="http://pida.co.uk/"&gt;PIDA&lt;/a&gt; web site with fancy new domain name at:&lt;br /&gt;&lt;br /&gt; &lt;a href="http://pida.co.uk/"&gt;http://pida.co.uk&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Sorry Berlios, you just aren't making the grade.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-207267571107479293?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/207267571107479293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/207267571107479293'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/12/what-is-wrong-with-berlios.html' title='What is wrong with Berlios?'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-6186529733212674463</id><published>2006-12-10T12:04:00.000Z</published><updated>2006-12-10T12:08:05.178Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pygtk'/><category scheme='http://www.blogger.com/atom/ns#' term='pida'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>PIDA 0.4.0 Released</title><content type='html'>After a slightly quiet spell (also known as an extended beta period) we have released PIDA 0.4.0. What kind of open source author would I be if I didn't push my own software on my blog!&lt;br /&gt;&lt;br /&gt;My favourite feature on this release is built in pyflakes. When you save a Python file it runs Pyflakes on it for you and prettifies the output into a GUI list for you to browse through, click on, and jump to the line number. All in Vim, as usual.&lt;br /&gt;&lt;br /&gt;Please try it, at &lt;a href="http://pida.berlios.de/"&gt;http://pida.berlios.de/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-6186529733212674463?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/6186529733212674463'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/6186529733212674463'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/12/pida-040-released.html' title='PIDA 0.4.0 Released'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-1766858057018562270</id><published>2006-12-06T15:45:00.000Z</published><updated>2006-12-06T15:52:18.360Z</updated><title type='text'>More on Python Dates, adding time with a datetime.timedelta</title><content type='html'>I needed a date 2 weeks in the future. I needed it quickly. After blogging about Python dates, I thought I had a clue how to do it. To my relief the first attempted path of least resistance just worked! Here it is:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from datetime import date, timedelta&lt;br /&gt;&gt;&gt;&gt; today = date.today()&lt;br /&gt;&gt;&gt;&gt; two_weeks = timedelta(days=14)&lt;br /&gt;&gt;&gt;&gt; two_weeks_time = today + two_weeks&lt;br /&gt;&gt;&gt;&gt; today&lt;br /&gt;datetime.date(2006, 12, 6)&lt;br /&gt;&gt;&gt;&gt; two_weeks_time&lt;br /&gt;datetime.date(2006, 12, 20)&lt;br /&gt;&gt;&gt;&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This was used to automatically populate a column in an an old database: which wanted to be an expected_date = order_date + two_weeks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-1766858057018562270?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/1766858057018562270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/1766858057018562270'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/12/more-on-python-dates-adding-time.html' title='More on Python Dates, adding time with a datetime.timedelta'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-5334097130795661836</id><published>2006-11-22T15:25:00.000Z</published><updated>2006-11-22T16:14:12.226Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='instantclient'/><category scheme='http://www.blogger.com/atom/ns#' term='cx_oracle'/><category scheme='http://www.blogger.com/atom/ns#' term='readline'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='oracle'/><title type='text'>Oracle from Python in Linux</title><content type='html'>Well, pretending to know everything can have its downsides. Like when the boss says "Could you hack up a script to enter custom data into our 3rd party invoicing software?". One choice is to admit that you would not know where to start. The other would be to attempt to muddle through. "Of course, let's have a look".&lt;br /&gt;&lt;br /&gt;So an hour later we have discovered that the software in question uses an Oracle back-end: Excellent, this is going to be easy, Oracle is an relational database (I read once somewhere) and we learned SQL at university, so it can't be that hard.&lt;br /&gt;&lt;br /&gt;So, step one: boot into windows, and use the command line client to do some stuff. Great! The SQL is coming back to me already. But this is no good. I can't use windows! The single desktop is beginning to upset me, and well we need it running on Linux so I can feel leeter than my coworkers.&lt;br /&gt;&lt;br /&gt;So what will we be needing?&lt;br /&gt;&lt;br /&gt;1. SQLPLUS for Linux (this the the command line client for Oracle). Should be easy enough, but oh man I don't have to install all of Oracle do I? No! Oracle 10 has this thing called "instantclient" which is just the basic client for connecting to an Oracle database.&lt;br /&gt;&lt;br /&gt;We need to visit the Oracle site, register some stuff with them, and then download the &lt;span style="font-weight: bold;"&gt;instantclient&lt;/span&gt; for Linux-32 (in our case), and the addon with the sqlplus libraries and executables.&lt;br /&gt;&lt;br /&gt;We unzip both of these files, and looking around, decide the best place to put them is in &lt;span style="font-weight: bold;"&gt;/usr/lib/instantclient&lt;/span&gt; (although in retrospect &lt;span style="font-weight: bold;"&gt;/usr/local/lib/instantclient&lt;/span&gt; might have been better).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ ls /usr/lib/instantclient/&lt;br /&gt;classes12.jar&lt;br /&gt;glogin.sql&lt;br /&gt;libclntsh.so.10.1&lt;br /&gt;libnnz10.so&lt;br /&gt;libocci.so.10.1&lt;br /&gt;libociei.so&lt;br /&gt;libocijdbc10.so&lt;br /&gt;libsqlplusic.so&lt;br /&gt;libsqlplus.so&lt;br /&gt;ojdbc14.jar&lt;br /&gt;sqlplus&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ok a few jar files, to make us feel unwell, but this is looking fine. Running &lt;span style="font-weight: bold;"&gt;sqlplus&lt;/span&gt; doesn't work, it can't find the library it requires. Enter a simple skill C people know about. Edit &lt;span style="font-weight: bold;"&gt;/etc/ld.so.conf&lt;/span&gt; (or put a file in &lt;span style="font-weight: bold;"&gt;/etc/ld.so.conf.d&lt;/span&gt;) and add the path of the libraries (in our case adding the single line &lt;span style="font-weight: bold;"&gt;/usr/lib/instantclient&lt;/span&gt; is enough, and then as root, run &lt;span style="font-weight: bold;"&gt;ldconfig&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;And then run sqlplus&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ sqlplus&lt;br /&gt;&lt;br /&gt;SQL*Plus: Release 10.2.0.2.0 - Production on Wed Nov 22 15:39:43 2006&lt;br /&gt;&lt;br /&gt;Copyright (c) 1982, 2005, Oracle.  All Rights Reserved.&lt;br /&gt;&lt;br /&gt;Enter user-name:&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Muahaha, that felt good didn't it! But we are not there yet (by a long chalk). Connecting to Oracle databases and providing the database description string is going to be a scary nightmare, discussed elsewhere no doubt. But here is a little trick we want to show you.&lt;br /&gt;&lt;br /&gt;Sqlplus does not use gnu readline on Linux. I know, awful, but don't despair: there are some applications that wrap command line applications with readline, and so give you awesome things (mostly history spanning sessions). The application that I ended up using is called &lt;span style="font-weight: bold;"&gt;rlwrap&lt;/span&gt; and was as easy to install as using &lt;span style="font-weight: bold;"&gt;apt-get install rlwrap&lt;/span&gt;. Combine that with the following shell script in &lt;span style="font-weight: bold;"&gt;/usr/bin/sqlplus&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ cat /usr/bin/sqlplus&lt;br /&gt;#! /bin/sh&lt;br /&gt;/usr/bin/rlwrap /usr/lib/instantclient/sqlplus $*&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And not forgetting to make it executable, we have a working Oracle command line client. And all for the price of your company details and an email address. Dear Lord, please bless those generous people at Oracle!&lt;br /&gt;&lt;br /&gt;2. Now to be able to access the database programmatically. A little snoop around and we discover the Python library &lt;a href="http://www.python.net/crew/atuining/cx_Oracle/"&gt;cx_Oracle&lt;/a&gt;. The prefix cx_ may remind you of the application cx_Freeze (for building standalone executable Python scripts) and that is because it comes from the same people.&lt;br /&gt;&lt;br /&gt;Ok first snag, it's not in Ubuntu. Ok, it is a contender for MOTU (Universe) but it's not there yet. That's fine I thought, I'll build it. Using the usual python setup.py build and Eeeeek! It needs Oracle to build against. I should have thought that it might, and here I am wondering if I should download and install all 3 cds worth of Oracle. That is going to take a day or so, and I just haven't got the time.&lt;br /&gt;&lt;br /&gt;For the second time today, we have to ask the Lord to thank the nice people at Oracle, because we can get away with just having the SDK for our already installed instantclient! It is available from the same place, and it unzips into the same place you unzipped instantclient, adding a subdirectory called &lt;span style="font-weight: bold;"&gt;sdk&lt;/span&gt;. This is nearly enough to allow building the python library. I say nearly because it searches for &lt;span style="font-weight: bold;"&gt;libclntsh.so&lt;/span&gt;, and we only have &lt;span style="font-weight: bold;"&gt;libclntsh.so.10.1&lt;/span&gt;, and the fix is as easy as symlinking one to the other:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/usr/lib/instantclient$ sudo ln -s libclntsh.so.10.1 libclntsh.so&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And then build the python bindings, remembering to set the &lt;span style="font-weight: bold;"&gt;ORACLE_HOME&lt;/span&gt; environment variable:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ ORACLE_HOME=/usr/lib/instantclient/ python setup.py build&lt;br /&gt;$ ORACLE_HOME=/usr/lib/instantclient/ sudo python setup.py install&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And I think we have a working cx_Oracle library. Let's check:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ python -c "import cx_Oracle"&lt;br /&gt;$&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;No news is certainly good news! The library comes with excellent documentation, and you will be on your way in no time. See, pretending to know everything sometimes forces you to work things out that you were clueless about!&lt;br /&gt;&lt;a href="httphttp://catherinedevlin.blogspot.com/2006/04/oracle-xe-and-ubuntu.html://"&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-5334097130795661836?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/5334097130795661836'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/5334097130795661836'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/11/oracle-from-python-in-linux.html' title='Oracle from Python in Linux'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-7176075775812980235</id><published>2006-11-17T09:51:00.000Z</published><updated>2006-12-29T16:50:25.734Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='datetime'/><category scheme='http://www.blogger.com/atom/ns#' term='time'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='date'/><title type='text'>Python Dates</title><content type='html'>At first glance the datetime module in the python standard library (full documentation available at  &lt;a href="http://docs.python.org//lib/module-datetime.html"&gt;http://docs.python.org/lib/module-datetime.html&lt;/a&gt; appears as an insane mish-mash of things.&lt;br /&gt;&lt;br /&gt;The types it makes available are date, time, datetime and timedelta. My initial confusion stemmed from the fact that the module name is the same as one of the type names (datetime and datetime.datetime) and this is one of the only times when I have thought that naming types in camel case is a good idea: datetime.DateTime might have been a saner approach. Nevertheless, we continue:&lt;br /&gt;&lt;br /&gt;A datetime.time is a time, a datetime.date is a date, and a datetime.datetime is a date with a time. A datetime.timedelta is a difference between any of these times. Simple enough.&lt;br /&gt;&lt;br /&gt;Usage of these types can be made easier by using their "alternative constructors" which are a set of class methods, and can in some cases be more useful than the actual constructor. For example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from datetime import date&lt;br /&gt;&gt;&gt;&gt; date(2000, 1, 1)&lt;br /&gt;datetime.date(2000, 1, 1)&lt;nobr&gt;&lt;b&gt;&lt;span class="typelabel"&gt;&lt;/span&gt;&lt;/b&gt;&lt;/nobr&gt;&lt;br /&gt;&gt;&gt;&gt; date.today()&lt;br /&gt;datetime.date(2006, 11, 17)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The actual constructor has the signature: date(year, month, day), but the alternate constructor that is very useful for us is the date.today() call, which returns today's date (as in the example).&lt;br /&gt;&lt;br /&gt;The datetime type is similar to the date type but also comes with time information. Here the alternative constructor that I find most used is not today(), but now(). For example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from datetime import datetime&lt;br /&gt;&gt;&gt;&gt; datetime.now()&lt;br /&gt;datetime.datetime(2006, 11, 17, 10, 1, 49, 913312)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The actual constructor here would want year, month, day and optional extra arguments for hour, minute, second and microsecond. This particular constructor (now) would be really useful for things such as SQLObject schemas, like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from sqlobject import SQLObject, DateCol&lt;br /&gt;&gt;&gt;&gt; class MySchema(SQLObject):&lt;br /&gt;...     date_of_event = DateCol(default=datetime.now)&lt;br /&gt;...&lt;br /&gt;&gt;&gt;&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;SQLObject has this nice feature of calling a callable default value, and in this case creating that object would give it a date_of_event attribute representing the time and date it was created. A really useful pattern.&lt;br /&gt;&lt;br /&gt;So we come to the time type. And remember we are datetime.time, not time.anything - the time module is something else entirely, and where this starts to become a bit more painful. datetime.time accepts (as you would expect) hour, minute, second, microsecond. But amazingly has no alternative constructors like the other types to return the current time. To do that we need to perform a small amount of trickery:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from datetime import datetime&lt;br /&gt;&gt;&gt;&gt; datetime.now().time()&lt;br /&gt;datetime.time(10, 20, 46, 720865)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Oh I know it's a bit ghastly, but it works. Now it wouldn't quite work if you wanted to use it for an SQLObject schema as above, and you would need to use a function or inline lambda, like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;time_of_event = TimeCol(default=lambda: datetime.now().time())&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To make sure the datetime object was called at the right time. Hoops are only fun if you have to jump through them!&lt;br /&gt;&lt;br /&gt;Shall we leave it there, or can we stretch ourselves to a short discussion of timedeltas? If we keep it simple. A timedelta is a difference between two dates, or datetimes, but &lt;span style="font-weight: bold;"&gt;not&lt;/span&gt; two times. And the difference has to be calculate with the same types, for example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; dt1 = datetime(2000, 1, 1)&lt;br /&gt;&gt;&gt;&gt; dt2 = datetime.now()&lt;br /&gt;&gt;&gt;&gt; delta = dt2 - dt1&lt;br /&gt;&gt;&gt;&gt; delta.days&lt;br /&gt;2512&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Apparently that is the number of days since the turn of the millennium. Sounds good to me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-7176075775812980235?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/7176075775812980235'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/7176075775812980235'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/11/python-dates.html' title='Python Dates'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-2188214989417200464</id><published>2006-11-16T09:09:00.000Z</published><updated>2006-11-16T09:40:47.667Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dialogs'/><category scheme='http://www.blogger.com/atom/ns#' term='rat'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pygtk'/><title type='text'>Rat, more easy PyGTK dialogs</title><content type='html'>We have been indulging in some Kiwi-love recently. You may have read the recent blog about easy PyGTK dialogs with Kiwi. After a chat with the author of another PyGTK helper library (called rat) and available at &lt;a href="http://python-rat.berlios.de/"&gt;http://python-rat.berlios.de/&lt;/a&gt;, who also happens to be a good friend of mine, I have decided to blog a similar example of dialogs using rat.&lt;br /&gt;&lt;br /&gt;Rat has a similar set of dialogs (a few extra, and a few missing). We shall start with a simple error dialog:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from rat.hig import error&lt;br /&gt;&gt;&gt;&gt; error('You made a mess in your trousers',&lt;br /&gt;...               'Or maybe the mess was already there',&lt;br /&gt;...                title='A big mess!')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;A similarly simple API, and it gives us a similarly HIG-compliant error dialog:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger2/5094/3413/1600/d3.0.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/blogger2/5094/3413/400/d3.0.png" alt="" border="0" /&gt;&lt;/a&gt;Rat does not contain a yes/no dialog, but it does contain an ok/cancel dialog. Usage&lt;br /&gt;is again very quick and simple:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from rat.hig import ok_cancel&lt;br /&gt;&gt;&gt;&gt; from gtk import RESPONSE_OK&lt;br /&gt;&gt;&gt;&gt; r = ok_cancel('Are you sure you want to destroy something?',&lt;br /&gt;...            'This Could be as evil as eval!',&lt;br /&gt;...            title='Please ensure...')&lt;br /&gt;&gt;&gt;&gt; r == RESPONSE_OK&lt;br /&gt;True&lt;br /&gt;&lt;/pre&gt;The resultant dialog does everything you want it to, and can be used for the same purpose as the Kiwi yes/no dialog.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger2/5094/3413/1600/d5.1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/blogger2/5094/3413/400/d5.1.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Mmmm, HIG-love!&lt;br /&gt;&lt;br /&gt;The extra dialog I want to have a look at is something only Rat has. This is the "Save Changes" dialog. It is designed for any editing application, where the user may wish to close the application while there are unsaved files or buffers e.g. a text editor, or graphical editing program. A small example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from rat.hig import save_changes&lt;br /&gt;&gt;&gt;&gt; files_to_save, response = save_changes(&lt;br /&gt;...     ['foo.txt', 'bar.txt', 'baz.txt'],&lt;br /&gt;...     title='You asked to quit the application')&lt;br /&gt;&gt;&gt;&gt; files_to_save&lt;br /&gt;['bar.txt', 'baz.txt']&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Amazing! And to show you how wonderfully beautiful it looks:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger2/5094/3413/1600/d6.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/blogger2/5094/3413/400/d6.png" alt="" border="0" /&gt;&lt;/a&gt;You will notice that of the 3 files passed to the dialog call, we selected two of them, and these are the two returned in the files_to_save variable. Lovely.&lt;br /&gt;&lt;br /&gt;I will conclude here that although there are two ways (Rat and Kiwi) of performing similar tasks, and choice is always good, each library contains things that the other doesn't. The maintainers of each library know eachother well. Isn't it time that they collaborate and join into a single library? We, the PyGTK developers will be the ultimate winners!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-2188214989417200464?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2188214989417200464'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2188214989417200464'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/11/rat-more-easy-pygtk-dialogs.html' title='Rat, more easy PyGTK dialogs'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-2675745900954729225</id><published>2006-11-15T14:21:00.000Z</published><updated>2006-11-16T10:39:01.691Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dialogs'/><category scheme='http://www.blogger.com/atom/ns#' term='kiwi'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pygtk'/><title type='text'>Kiwi, Pain free PyGTK dialogs</title><content type='html'>Since we are in love with Kiwi, we will mention another amazing thing it has done for us. HIG Dialogs. HIG are the (Gnome) Human Interface Guidelines, a set of guidelines for creating accessible user interfaces. I wish we could ignore these things, but unfortunately we can't. You can &lt;a href="http://developer.gnome.org/projects/gup/hig/"&gt;read about them here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Kiwi has a set of dialogs ready for us that conform to these guidelines. They are presented in the form of funcitons that you can call, and which give you a response of some kind. Let's have a look:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from kiwi.ui.dialogs import error&lt;br /&gt;&gt;&gt;&gt; error('You screwed something up',&lt;br /&gt;...    'Or maybe it was already screwed to begin with')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger2/5094/3413/1600/d1.0.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://photos1.blogger.com/blogger2/5094/3413/400/d1.png" alt="" border="0" /&gt;&lt;/a&gt;Yes, it is as simple as that. None of that boilerplate code. And the dialog itself actually looks quite nice:&lt;br /&gt;&lt;br /&gt;The error dialog is not the only one available of course. You have error, info, save, open, and my favourite: yesno&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; from kiwi.ui.dialogs import yesno&lt;br /&gt;&gt;&gt;&gt; from gtk import RESPONSE_YES&lt;br /&gt;&gt;&gt;&gt; resp = yesno('Are you sure you want to install this virus?')&lt;br /&gt;&gt;&gt;&gt; resp == RESPONSE_YES&lt;br /&gt;True&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger2/5094/3413/1600/d2.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://photos1.blogger.com/blogger2/5094/3413/400/d2.png" alt="" border="0" /&gt;&lt;/a&gt;Again, nice and quick. You will find yourself adding these dialogs all over the place, maybe even in too many places. But it's all good, as long as the user knows what is what.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-2675745900954729225?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2675745900954729225'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/2675745900954729225'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/11/kiwi-pain-free-dialogs.html' title='Kiwi, Pain free PyGTK dialogs'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-5109646361104495538</id><published>2006-11-13T16:03:00.000Z</published><updated>2006-11-13T16:33:34.391Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='gtk'/><category scheme='http://www.blogger.com/atom/ns#' term='kiwi'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pygtk'/><title type='text'>Kiwi proxy widgets, a common widget API</title><content type='html'>While we are on the notion of &lt;a href="http://www.async.com.br/projects/kiwi/"&gt;Kiwi&lt;/a&gt;, the PyGTK helper library, we should be discussing the proxy widgets. They offer a certain set of advantages over their plain PyGTK counterparts:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;They have a common signal emission API for change in content&lt;/li&gt;&lt;li&gt;They offer a common API for setting and reading their value.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;They offer validation, and coercion&lt;br /&gt;&lt;/li&gt;&lt;li&gt;They contain a few general improvements over the plain PyGTK widgets&lt;/li&gt;&lt;/ol&gt;What does this mean?&lt;br /&gt;&lt;br /&gt;Simply it means you can make any data widget (eg Entry, CheckButton, Combo etc), you can connect its signal "content-changed", you can read the data with widget.read(), and set the value with widget.update().&lt;br /&gt;&lt;br /&gt;If you have read any of the previous blogs about adapting Zope.schema fields to widgets, and you are alert, you will notice that this is amazingly useful. It means that we can define an interface for a general InputWidget type, something like:&lt;pre&gt;&lt;br /&gt;class IInputWidget(Interface):&lt;br /&gt;&lt;br /&gt;def read():&lt;br /&gt;"""Read the value in the widget"""&lt;br /&gt;&lt;br /&gt;def update(value):&lt;br /&gt;"""Set the value of the widget"""&lt;br /&gt;&lt;br /&gt;def connect_content_changed(callback):&lt;br /&gt;"""Set a callback to get called when the widget value is changed by the user"""&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Once we have done this we can essentially forget about what widget we are using, and what type of field it is representing. A dream come true for the "program for an interface" paradigm.&lt;br /&gt;&lt;br /&gt;At the same time, these widgets do have some downsides:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;They are bound to an arbitrary concept of a "model" and an attribute within that model&lt;/li&gt;&lt;li&gt;They insist on having a data-type set&lt;/li&gt;&lt;/ol&gt;The good thing is that the downsides can be faked by setting the appropriate PyGTK properties. Hurrah!&lt;br /&gt;&lt;br /&gt;Let's look at a basic example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from kiwi.ui.widgets.entry import ProxyEntry&lt;br /&gt;&lt;br /&gt;e = ProxyEntry()&lt;br /&gt;# Fake those things that Kiwi requires&lt;br /&gt;e.set_property('data-type', str)&lt;br /&gt;e.set_property('model-attribute', 'fake')&lt;br /&gt;&lt;br /&gt;# Test reading and writing&lt;br /&gt;e.update('I am the starting value')&lt;br /&gt;assert e.read() == 'I am the starting value'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger2/5094/3413/1600/b2.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://photos1.blogger.com/blogger2/5094/3413/200/b2.png" alt="" border="0" /&gt;&lt;/a&gt;Now imagine you had a lot of these, and that they were all automatically adapted from a zope schema (&lt;a href="http://unpythonic.blogspot.com/2006/11/zope-in-gui-fields-as-widgets.html"&gt;as discussed before&lt;/a&gt;). These widgets could all be readable using something like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;widgets[name].read()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Amazing! One might be a CheckButton, returning a boolean value. One might be a Date chooser returning a datetime, one might be an entry returning a str or a unicode. Complete automatic UI generation that things like Django would be jealous of!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-5109646361104495538?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/5109646361104495538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/5109646361104495538'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/11/kiwi-proxy-widgets-common-widget-api.html' title='Kiwi proxy widgets, a common widget API'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-6897506047429877478</id><published>2006-11-13T00:00:00.000Z</published><updated>2006-11-13T16:23:46.748Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='gtk'/><category scheme='http://www.blogger.com/atom/ns#' term='kiwi'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pygtk'/><title type='text'>PyGTK lists made easy with Kiwi</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger2/5094/3413/1600/b1.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://photos1.blogger.com/blogger2/5094/3413/200/b1.png" alt="" border="0" /&gt;&lt;/a&gt;Making nice lists is an utter pain with PyGTK. GTK+ does you the pleasure of  separating the model and the view. I guess it is a reasonable idea in theory (don't they always teach you to separate the model and the view?) and allows you to have alternative views of the same model, and allows you to extend the implementation in any sort of way. BUT, to repeat: in most cases it is an utter pain. Even the simplest list becomes a mess of boilerplate code.&lt;br /&gt;&lt;br /&gt;Anyone who has done a decent amount of PyGTK programming will have written their own implementation of model+view mixed together, but the best one I have used is from Kiwi. If you have never used it, Kiwi is a framework that lessens some of the painful aspects of PyGTK development. In fact, it's so useful, let's call it a library.&lt;br /&gt;&lt;br /&gt;Creating a pythonic view of a list of objects is easy. Let's have a look at some code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from kiwi.ui.objectlist import ObjectList, Column&lt;br /&gt;&lt;br /&gt;class Fruit(object):&lt;br /&gt;def __init__(self, name, is_red):&lt;br /&gt;self.name = name&lt;br /&gt;self.is_red = is_red&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;fruits = ObjectList(&lt;br /&gt; [&lt;br /&gt;   Column('name', title='Fruit Name'),&lt;br /&gt;   Column('is_red', title='Is the fruit red?',&lt;br /&gt;          data_type=bool, radio=True)&lt;br /&gt; ]&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;fruits.append(Fruit('Apple', True))&lt;br /&gt;fruits.append(Fruit('Melon', False))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Yes it is really that easy. Add a bit of code to put it in a window:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import gtk&lt;br /&gt;w = gtk.Window()&lt;br /&gt;w.add(fruits)&lt;br /&gt;w.show_all()&lt;br /&gt;gtk.main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And you are ready to go. If you try to achieve the same thing with pure PyGTK, you will write at least 10 times as much code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-6897506047429877478?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/6897506047429877478'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/6897506047429877478'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/11/pygtk-lists-made-easy-with-kiwi.html' title='PyGTK lists made easy with Kiwi'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-116276519858130614</id><published>2006-11-05T22:05:00.000Z</published><updated>2006-11-12T18:41:06.696Z</updated><title type='text'>Zope in a gui, fields as widgets</title><content type='html'>This is a long quest, and at this stage we have objects, which implement interfaces, and we have schemas that reflect the interfaces, and are helpers for creating views for the objects. This is all well and good, but what happens on a widget level in a gui?&lt;br /&gt;&lt;br /&gt;Well we can give you a simple example. The view is an adaption of the Schema, and which then has its content set (see &lt;a href="http://unpythonic.blogspot.com/2006/11/in-search-of-zope-gui-why-zope-web-way.html"&gt;the last blog about this topic)&lt;/a&gt;. The view can be built by adapting each of the  schemas fields into a gui element, exactly as you would build a web form.&lt;br /&gt;&lt;br /&gt;Say you have an attribute defined as a zope.schema.Text, and you wanted an editor field. Well simply, using pygtk:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import gtk&lt;br /&gt;from zope.interface import implements, Interface, Attribute&lt;br /&gt;from zope.component import adapts, provideAdapter&lt;br /&gt;from zope.schema import Text&lt;br /&gt;&lt;br /&gt;class IWidget(Interface):&lt;br /&gt;def write(value):&lt;br /&gt;    """display the value"""&lt;br /&gt;&lt;br /&gt;class TextWidget(gtk.Entry):&lt;br /&gt;adapts(Text)&lt;br /&gt;implements(IWidget)&lt;br /&gt;&lt;br /&gt;def __init__(self, field):&lt;br /&gt;    self._field = field&lt;br /&gt;    super(TextWidget, self).__init__()&lt;br /&gt;&lt;br /&gt;def write(self, value):&lt;br /&gt;    self.set_text(value)&lt;br /&gt;&lt;br /&gt;provideAdapter(TextWidget)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now everything is set up to adapt the field to a widget.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;name = Text(title=u'The name of something')&lt;br /&gt;print IWidget(name)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Gives you&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;TextWidget object (GtkEntry) at 0xb715f16c&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Yay! Now you have everything you need to create a gui from a zope object.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-116276519858130614?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/116276519858130614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/116276519858130614'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/11/zope-in-gui-fields-as-widgets.html' title='Zope in a gui, fields as widgets'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-116276237403417226</id><published>2006-11-05T21:17:00.000Z</published><updated>2007-03-15T14:22:45.283Z</updated><title type='text'>In search of a zope gui, why the zope web way might not be the best way</title><content type='html'>So in our last post, we saw how you could use the Zope interface architecture to adapt objects to views, and how Zope does it. The ultimate goal of this exercise is to have a gui way of representing Zope. This is all fine, but we have to consider that perhaps in a gui, one view per object may not be the best method.&lt;br /&gt;&lt;br /&gt;Creating a gui repeatedly for the same type of object, just to change its contents is just wrong.&lt;br /&gt;&lt;br /&gt;In our example, we had a multi-adapter for IObject, IRequest, which was a view object with a render method. No doubt perfect for web requests, but not for a gui which lives in a loop, is governed by events and can hide lots of stuff.&lt;br /&gt;&lt;br /&gt;What it seems we actually need is an adapter for ITypeOfObject, which is creaeted without an object, and can then have the content set and reset.&lt;br /&gt;&lt;br /&gt;Enter zope.schema&lt;br /&gt;&lt;br /&gt;zope.schemas are interfaces and have all the interface properties. They do have additional niceties, in that you can define attributes as constrained field types, for example Int, and in zope all of these field types are adaptable into html form elements. In our gui case we want to adapts them to widgets. The schema can be the adapted object when the view is created, and the view can have some kind of content setter which is reposnible for updating the widgets in the view. It is worth noting that this approach may become more used with increasing developments in the Javascript world.&lt;br /&gt;&lt;br /&gt;So, first we define ISchema, and an implementor of ISchema. It is important to note here that we have left how zope uses schemas. They are Interface subclasses, and although this has advantages, I fell slightly ill-at-ease with it. We will instantiate the Schema object and we will worry about iterating the fields in it later.&lt;br /&gt;&lt;pre name="code" class="py" cols="80" rows="30"&gt;&lt;br /&gt;from zope.interface import implements, Interface, Attribute, classProvides&lt;br /&gt;from zope.component import adapts, provideAdapter&lt;br /&gt;&lt;br /&gt;class IAnimal(Interface):&lt;br /&gt;    name = Attribute("""The name of the Animal""")&lt;br /&gt;&lt;br /&gt;class ISchema(Interface):&lt;br /&gt;    pass&lt;br /&gt;&lt;br /&gt;class AnimalSchema(object):&lt;br /&gt;    """This is a schema"""&lt;br /&gt;    implements(ISchema)&lt;br /&gt;    name = Text(title=u'The Name')&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now a something to implement the IAnimal/IAnimalSchema. The schema and the interface are essentially the same, and you might ask why not just use the interface. Well, simply because being an Interface subclass is a royal pain. It imposes lots of bizarre restrictions, and since we really want to push it a bit later on, we should depart from that now.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class Animal(object):&lt;br /&gt; """An animal"""&lt;br /&gt; implements(IAnimal)&lt;br /&gt; def __init__(self, name):&lt;br /&gt;   self.name = name&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now the view stuff. This view will be setup once, and then available to have its content changed.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class IView(Interface):&lt;br /&gt; &lt;br /&gt; def render(self):&lt;br /&gt;   """Render visually"""&lt;br /&gt;&lt;br /&gt; def set_content(self, content):&lt;br /&gt;   """Update the view to reflect the content"""&lt;br /&gt;&lt;br /&gt;class AnimalView(object):&lt;br /&gt;&lt;br /&gt; implements(IView)&lt;br /&gt; adapts(AnimalSchema)&lt;br /&gt;&lt;br /&gt; def __init__(self, schema):&lt;br /&gt;     self._schema = schema&lt;br /&gt;&lt;br /&gt; def render(self):&lt;br /&gt;     print 'Our schema is %s' % self._schema&lt;br /&gt;&lt;br /&gt; def set_content(self, content):&lt;br /&gt;     print 'we have new content %s' % content.name&lt;br /&gt;&lt;br /&gt;provideAdapter(AnimalView)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now let's run it.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;   view = IView(AnimalSchema())&lt;br /&gt;   view.render()&lt;br /&gt;   for i in map(Animal, [u'lion', u'tiger', u'leopard']):&lt;br /&gt;       view.set_content(i)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And you get:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Our schema is &lt;__main__.animalschema&gt;&lt;br /&gt;we have new content lion&lt;br /&gt;we have new content tiger&lt;br /&gt;we have new content leopard&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Although this is just a command line output, you can see that the stages have been fulfilled. The gui can be setup in the initial phase by drawing all the widgets, and then the values can be set at later points, asynchronously.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-116276237403417226?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/116276237403417226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/116276237403417226'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/11/in-search-of-zope-gui-why-zope-web-way.html' title='In search of a zope gui, why the zope web way might not be the best way'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-116275444281478559</id><published>2006-11-05T19:06:00.000Z</published><updated>2007-03-15T15:18:54.776Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='multiadapters'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='interfaces'/><category scheme='http://www.blogger.com/atom/ns#' term='adapters'/><category scheme='http://www.blogger.com/atom/ns#' term='zope'/><title type='text'>Using multi-adapters a la Zope</title><content type='html'>As a brief discussion (and I should mention that this has nothing to do with a web application), you have an object and you want to look at it. Imagine you have gone the whole way with this concept and you have arrived at Zope. Zope contains mechanisms for definition, storage and manipulation of objects, all using Python.&lt;br /&gt;&lt;br /&gt;So you have an interface:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;from zope.interface import Interface, Attribute&lt;br /&gt;&lt;br /&gt;class IFruit(Interface):&lt;br /&gt;    name = Attribute("""The name of the fruit""")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ok, it's simple. Now you write a class that implements this, still simple.&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;class Fruit(object):&lt;br /&gt;   implements(IFruit)&lt;br /&gt;   def __init__(self, name):&lt;br /&gt;      self.name = name&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Pretty good. You can store that in an object database like ZODB if you like, but we are going to think how to view it.&lt;br /&gt;&lt;br /&gt;A view (in Zope terminology) is something that adapts (multi-adapts) a "context" and a "request". We don't really understand what a "context" is, so we are going to call it an "object". A request can be something like "edit" or "view".&lt;br /&gt;&lt;br /&gt;So of course we need an interface for views to adhere to, and an interface for a request to adhere to. This is where interfaces stop being documentation niceties, and start being implementation nasties.&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;class IView(Interface):&lt;br /&gt;   def render():&lt;br /&gt;       """Display what needs displaying"""&lt;br /&gt;&lt;br /&gt;class IViewRequest(Interface):&lt;br /&gt;   user = Attribute("""The user that made the request""")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So imagine we implement a simple view (now remember this is nothing to do with a web application). The view will display Fruit objects.&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;from zope.component import adapts, provideAdapter, getMultiAdapter&lt;br /&gt;&lt;br /&gt;class FruitView(object):&lt;br /&gt;   implements(IView)&lt;br /&gt;   adapts(IFruit, IViewRequest)&lt;br /&gt;&lt;br /&gt;   def __init__(self, fruit):&lt;br /&gt;       self._fruit = fruit&lt;br /&gt;&lt;br /&gt;   def render(self):&lt;br /&gt;       print self._fruit.name&lt;br /&gt;&lt;br /&gt;# not forgetting to register the adapter with zope.component&lt;br /&gt;provideAdapter(FruitView)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And also a request implementation that does nothing&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;class ViewRequest(object):&lt;br /&gt;   implements(IViewRequest)&lt;br /&gt;   user = None&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And we are ready to go:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;view = getMultiAdapter((Fruit('banana'), ViewRequest()), IView)&lt;br /&gt;view.render()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;gives you&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;banana&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-116275444281478559?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/116275444281478559'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/116275444281478559'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/11/using-multi-adapters-la-zope.html' title='Using multi-adapters a la Zope'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-114740947372483078</id><published>2006-05-12T04:34:00.000Z</published><updated>2006-11-12T11:17:55.458Z</updated><title type='text'>Launchpad and Malone: Pain free bug-tracking</title><content type='html'>&lt;a href="https://launchpad.net/"&gt;Launchpad&lt;/a&gt; is a open source software management system being provided by &lt;a href="http://canonical.com/"&gt;Canonical&lt;/a&gt; (makers of &lt;a href="http://ubuntu.org/"&gt;Ubuntu&lt;/a&gt;). It has lots of services and features, and we'll just be scratching the surface today. The services it provides are somewhat like sourceforge. Launchpad is comprised of sub-systems, each of which seems a fully capable application.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://wiki.launchpad.canonical.com/Malone"&gt;Malone&lt;/a&gt; is the name of the bug tracker included with Launchpad. Malone is just the best bug tracker I have ever used. I am no expert, but I have really grown to detest using Trac and Bugzilla. The main reason being constant switching back and forth from lists to bugs and then back again, and oh yikes, it makes me dizzy. Malone seems to get around this with subtle use of javascript, and obsessive attention to user workflow.&lt;br /&gt;&lt;br /&gt;From sniffing around the development crew of Launchpad (as one does!) I found some really well known and scary names. Experts from all departments, including a decent number of GUI expert programmers. The user interface seems really important to them, and it shows in the end product.&lt;br /&gt;&lt;br /&gt;Well done everyone involved! At last, I look forward to managing bugs, not just fixing them. And at last my users have a pleasant way of reporting. And as a pleasant bonus, it is all hosted for me.&lt;br /&gt;&lt;br /&gt;(links in text for more information)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-114740947372483078?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/114740947372483078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/114740947372483078'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/05/launchpad-and-malone-pain-free-bug.html' title='Launchpad and Malone: Pain free bug-tracking'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-27964390.post-114740643849724784</id><published>2006-05-12T03:59:00.000Z</published><updated>2006-11-12T11:17:55.328Z</updated><title type='text'>Blogger doesn't look elite enough</title><content type='html'>Well, this is just a test post really, but all this css makes my head hurt. We want a nice old-fashioned looking site. Even the minimal template is far too much.&lt;br /&gt;&lt;br /&gt;(this is a test, don't get excited)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27964390-114740643849724784?l=unpythonic.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/114740643849724784'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27964390/posts/default/114740643849724784'/><link rel='alternate' type='text/html' href='http://unpythonic.blogspot.com/2006/05/blogger-doesnt-look-elite-enough.html' title='Blogger doesn&apos;t look elite enough'/><author><name>Ali Afshar</name><uri>https://profiles.google.com/118327176775959145936</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh6.googleusercontent.com/-48mVumD6cWI/AAAAAAAAAAI/AAAAAAAADCA/e10sK8rdEzo/s512-c/photo.jpg'/></author></entry></feed>
