<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0"><id>http://blogg.ingspree.net/blog/</id><title>Blogging spree</title><updated>2009-09-15T17:48:35Z</updated><link href="http://blogg.ingspree.net/blog/" rel="alternate" /><author><name>Alexander Solovyov</name></author><generator version="r33" uri="http://code.google.com/p/django-atompub/">django-atompub</generator><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/BloggingSpree" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="bloggingspree" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry><id>http://blogg.ingspree.net/blog/2009/09/14/opster/</id><title type="html">Opster</title><updated>2009-09-15T17:48:35Z</updated><published>2009-09-14T13:54:27Z</published><category term="opster" /><category term="programming" /><category term="python" /><link href="http://blogg.ingspree.net/blog/2009/09/14/opster/" rel="self" /><link href="http://blogg.ingspree.net/blog/2009/09/14/opster/" rel="alternate" /><content type="html">&lt;p&gt;Two months ago I&amp;#8217;ve released little command line parsing library for Python
called &lt;a href="http://pypi.python.org/pypi/finaloption"&gt;opster&lt;/a&gt; (actually it was called finaloption then, but I&amp;#8217;ve renamed
it because of remark from native speaker ;-)). What&amp;#8217;s the reason to write one
more command line parser when Python already has getopt and optparse in standard
library and not so long time ago argparse and optfunc were&amp;nbsp;released?&lt;/p&gt;

&lt;p&gt;Well&amp;#8230; as usually, because I think that they are going wrong way and doing
wrong things. Of course, &lt;span class="caps"&gt;IMO&lt;/span&gt; (but what matters if not opinion? :P).&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;It started to happen when Zed Shaw wrote &lt;a href="http://zedshaw.com/blog/2009-05-29.html" title="article itself is quite good"&gt;big article&lt;/a&gt; on Python warts and
mentioned that Lamson has much better command line parsing solution than
argparse/optparse. I was interested in this topic a little at the time and I
looked at the code. It would be lie to say that I liked it. In fact I thought
that this is a heresy of the same level as optparse.&amp;nbsp;;-)&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve &lt;a href="http://twitter.com/asolovyov/status/1969773034"&gt;written&lt;/a&gt; in Twitter that it&amp;#8217;s funny to say that Lamson has superior
command system and got some amount of sarcasm from Zed and clear understanding
that Zed see nothing bad&amp;nbsp;when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;command functions should be defined in a single&amp;nbsp;module&lt;/li&gt;
&lt;li&gt;default settings are defined by calling separate function inside a command&amp;nbsp;function&lt;/li&gt;
&lt;li&gt;specifying option in command line with mistake wouldn&amp;#8217;t raise&amp;nbsp;error&lt;/li&gt;
&lt;li&gt;formatting of help text on options is done by&amp;nbsp;hands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I thought that world needs Mercurial&amp;#8217;s command system. ;) And I&amp;#8217;ve rewritten
it as library, keeping main&amp;nbsp;idea.&lt;/p&gt;

&lt;p&gt;Small example of&amp;nbsp;usage:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from opster import command

@command(usage='[-l HOST] DIR')
def main(dirname,
         listen=('l', 'localhost', 'ip to listen on'),
         port=('p', 8000, 'port to listen on'),
         daemonize=('d', False, 'daemonize process'),
         pid_file=('', '', 'name of file to write process ID to')):
    '''Command with option declaration as keyword arguments

    Otherwise it's the same as previous command
    '''
    print locals()

if __name__ == '__main__':
    main()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I think that you should understand what&amp;#8217;s going on here. For example, option is
required to have long name (keyword argument name), possibly short name (using
&lt;code&gt;''&lt;/code&gt; will discard short name), some default value and help string. Default value
determines what should be done to incoming&amp;nbsp;data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;string &amp;mdash; nothing happens, incoming value will remain as&amp;nbsp;string&lt;/li&gt;
&lt;li&gt;int &amp;mdash; incoming value is parsed by calling &lt;code&gt;int()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;list &amp;mdash; incoming value is appended to the&amp;nbsp;list&lt;/li&gt;
&lt;li&gt;function is called with incoming value and output is&amp;nbsp;used&lt;/li&gt;
&lt;li&gt;True/False/None &amp;mdash; option needs no argument, just switching default value in
opposite&amp;nbsp;value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After wrapping with &lt;code&gt;@command&lt;/code&gt; your function &lt;code&gt;main()&lt;/code&gt; can be&amp;nbsp;called:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;without arguments at all; it will parse &lt;code&gt;sys.argv&lt;/code&gt; in this&amp;nbsp;case&lt;/li&gt;
&lt;li&gt;with argument named &lt;code&gt;argv&lt;/code&gt;, which needs to be list of strings (same as
&lt;code&gt;sys.argv[1:]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;with usual arguments/keyword arguments, which are defined in&amp;nbsp;function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think it may be not obvious that you will get clean values in your function
(for example, &lt;code&gt;port&lt;/code&gt; will contain value &lt;code&gt;8000&lt;/code&gt;), and not some strange&amp;nbsp;three-tuples.&lt;/p&gt;

&lt;p&gt;And you get such help for&amp;nbsp;free:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;piranha@gto ~/dev/misc/opster&amp;gt;./test_opts.py --help
test_opts.py [-l HOST] DIR

Command with option declaration as keyword arguments

    Otherwise it's the same as previous command

options:

 -l --listen     ip to listen on (default: localhost)
 -p --port       port to listen on (default: 8000)
 -d --daemonize  daemonize process
    --pid-file   name of file to write process ID to
 -h --help       show help
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Furthermore, underscores in argument names are converted to hyphens to support
conventions of command line.&amp;nbsp;;)&lt;/p&gt;

&lt;p&gt;I should mention that option names (and subcommand names, if you&amp;#8217;re using them)
can be shortened: i.e. you can say &lt;code&gt;--pi&lt;/code&gt; instead of &lt;code&gt;--pid-file&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If I&amp;#8217;m going to compare opster with something, this should be &lt;a href="http://github.com/simonw/optfunc/tree/master"&gt;optfunc&lt;/a&gt; by
Simon Willison.  Most noticeable differences are syntax of command definitions
and subcommand support. Actually optfunc has subcommand support, but it&amp;#8217;s pretty&amp;nbsp;incomplete.&lt;/p&gt;

&lt;p&gt;Opster uses getopt inside to parse options and that&amp;#8217;s the reason why it&amp;#8217;s
somewhat bigger than optfunc (which is essentially optparse wrapper). Opster&amp;#8217;s
internal &lt;span class="caps"&gt;API&lt;/span&gt; &amp;mdash; options are list of four-tuples (short name, long name, default
value, help string) &amp;mdash; is exactly the same as Mercurial&amp;#8217;s &lt;span class="caps"&gt;API&lt;/span&gt; for defining
options. This means that such code will work (taken from &lt;a href="http://hg.piranha.org.ua/opster/file/tip/test_cmd.py"&gt;test_cmd.py&lt;/a&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import opster

config_opts=[('c', 'config', 'webshops.ini', 'config file to use')]

@opster.command(options=config_opts)
def initdb(config):
    """Initialize database"""
    pass

@opster.command(options=config_opts)
def runserver(listen=('l', 'localhost', 'ip to listen on'),
              port=('p', 5000, 'port to listen on'),
              **opts):
    """Run development server"""
    print locals()

if __name__ == '__main__':
    opster.dispatch()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Interesting thing happens in definition of &lt;code&gt;runserver&lt;/code&gt;, help and output of which
looks like&amp;nbsp;this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;piranha@gto ~/dev/misc/opster&amp;gt; ./test_cmd.py help runs
test_cmd.py runserver [OPTIONS]

Run development server

options:

 -l --listen  ip to listen on (default: localhost)
 -p --port    port to listen on (default: 5000)
 -c --config  config file to use (default: webshops.ini)
 -h --help    display help

piranha@gto ~/dev/misc/opster&amp;gt; ./test_cmd.py runs
{'port': 5000, 'opts': {'config': 'webshops.ini'}, 'listen': 'localhost'}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can factor out common options and pass them to @command decorator, keeping
your pants &lt;a href="http://en.wikipedia.org/wiki/DRY"&gt;&lt;span class="caps"&gt;DRY&lt;/span&gt;&lt;/a&gt;.&amp;nbsp;;-)&lt;/p&gt;

&lt;p&gt;So&amp;#8230; Read &lt;a href="http://hg.piranha.org.ua/opster/docs/"&gt;documentation&lt;/a&gt; and use it! :) Any feedback, questions, suggestions and patches are highly
welcome.&amp;nbsp;;-) &lt;/p&gt;
</content></entry><entry><id>http://blogg.ingspree.net/blog/2009/04/12/revcanonical/</id><title type="html">Short urls — rev=canonical</title><updated>2009-04-12T19:02:41Z</updated><published>2009-04-12T19:02:02Z</published><category term="byteflow" /><category term="django" /><link href="http://blogg.ingspree.net/blog/2009/04/12/revcanonical/" rel="self" /><link href="http://blogg.ingspree.net/blog/2009/04/12/revcanonical/" rel="alternate" /><content type="html">&lt;p&gt;Just yesterday my feed reader fetched Simon Willison&amp;#8217;s &lt;a href="http://simonwillison.net/2009/Apr/11/revcanonical/"&gt;article&lt;/a&gt; about
relatively new method of url&amp;nbsp;shortening.&lt;/p&gt;

&lt;p&gt;The idea is that url shorteners in fact are not very good thing, especially from
&lt;span class="caps"&gt;URL&lt;/span&gt; term of life point of view. What if tinyurl.com is going to die? A lot of
URLs will appear unusable, even if their destination is still alive. And
discussion gave birth to thought that it could be nice to allow sites to specify
their own short urls, even using their own url&amp;nbsp;shorteners.&lt;/p&gt;

&lt;p&gt;There is &lt;a href="http://revcanonical.appspot.com/"&gt;site&lt;/a&gt; with specification (if it can be named so), in short &amp;mdash; you
should specify it in your &lt;code&gt;&amp;lt;head&amp;gt;...&amp;lt;/head&amp;gt;&lt;/code&gt; section with such&amp;nbsp;link:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;link rev="canonical" href="short-url-here"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In his article Simon tells about how he has implemented &lt;code&gt;rev="canonical"&lt;/code&gt; in
it&amp;#8217;s own blog &amp;mdash; I like his solution for not saving anything in database. ;) But
hardcoding actual types of content is not that cool, so I&amp;#8217;ve done small Django
application &amp;mdash; &lt;a href="http://hg.piranha.org.ua/django-revcanonical/"&gt;revcanonical&lt;/a&gt;, whose task is to generate and redirect such
short&amp;nbsp;links.&lt;/p&gt;

&lt;p&gt;Every url is a two strings joined by a dot &amp;mdash; it&amp;#8217;s a base62-coded&lt;sup class="footnote-ref" id="fnref-1"&gt;&lt;a href="#fn-1"&gt;1&lt;/a&gt;&lt;/sup&gt;
identification numbers of content type and object in database. So you can use
that application for any object without any configuration. &lt;span class="caps"&gt;README&lt;/span&gt; has
instructions on how to install and use&amp;nbsp;application.&lt;/p&gt;

&lt;p&gt;Of course, functionality is already incorporated into Byteflow.&amp;nbsp;;-)&lt;/p&gt;

&lt;div class="footnotes"&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id="fn-1"&gt;
&lt;p&gt;Digits, big and small latin letters&amp;nbsp;&lt;a href="#fnref-1" class="footnoteBackLink" title="Jump back to footnote 1 in the text."&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content></entry><entry><id>http://blogg.ingspree.net/blog/2008/12/02/mercurial-11/</id><title type="html">Mercurial 1.1</title><updated>2008-12-02T22:49:46Z</updated><published>2008-12-02T22:45:31Z</published><category term="hg" /><category term="news" /><link href="http://blogg.ingspree.net/blog/2008/12/02/mercurial-11/" rel="self" /><link href="http://blogg.ingspree.net/blog/2008/12/02/mercurial-11/" rel="alternate" /><content type="html">&lt;p&gt;Mercurial 1.1 is out and &lt;a href="http://www.selenic.com/mercurial/wiki/index.cgi/WhatsNew#head-b1d1f9a535adb686d6e0a490e049261313f10d7d"&gt;list of changes&lt;/a&gt; takes 2.5 pages. There are some my efforts in this release, ;-) but in overall two most appealing features for me is a graph of changeset tree in webinterface and &lt;code&gt;zeroconf&lt;/code&gt; extension &amp;mdash; it allows &lt;code&gt;hg paths&lt;/code&gt; to display all &lt;code&gt;hg serve&lt;/code&gt; instances running in a local network. Very useful &amp;mdash; now I don&amp;#8217;t need to ask and answer about IPs.&amp;nbsp;:)
&lt;/p&gt;
&lt;p&gt;Of course, there is long-awaited result of Google SoC &amp;mdash; &lt;code&gt;rebase&lt;/code&gt; and also &lt;code&gt;bookmarks&lt;/code&gt; &amp;mdash; git-like branches. Sad news is that bookmarks are only local now, but I hope that 1.2 will handle&amp;nbsp;that.
&lt;/p&gt;</content></entry><entry><id>http://blogg.ingspree.net/blog/2008/11/24/firepython-no-prints/</id><title type="html">FirePython - no prints?</title><updated>2008-11-24T20:05:26Z</updated><published>2008-11-24T20:05:25Z</published><category term="django" /><category term="programming" /><category term="python" /><link href="http://blogg.ingspree.net/blog/2008/11/24/firepython-no-prints/" rel="self" /><link href="http://blogg.ingspree.net/blog/2008/11/24/firepython-no-prints/" rel="alternate" /><content type="html">&lt;p&gt;While I&amp;#8217;m developing some web application I almost never use any debuggers or supporting tools: in 90% of cases usual &lt;code&gt;print variable&lt;/code&gt; is enough for understanding a trouble. Of course, there are some very complicated cases, when I do &lt;code&gt;import pdb; pdb.set_trace()&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;But I was pointed at very cool thing yesterday &amp;mdash; &lt;a href="http://github.com/woid/firepython/tree/master"&gt;FirePython&lt;/a&gt;. This thing consists of two parts &amp;mdash; small library on python and plugin for &lt;a href="http://getfirebug.com/"&gt;Firebug&lt;/a&gt;. This tandem is engaged with very useful business &amp;mdash; it displays all python logged activity&lt;sup id="fnr1-242239821"&gt;&lt;a href="#fn1-242239821"&gt;1&lt;/a&gt;&lt;/sup&gt; in Firebug tab.&lt;!--more--&gt;
&lt;/p&gt;
&lt;p&gt;This tool is really helpful &amp;mdash; now I need only two applications for development: an editor and a browser. Good news is that I don&amp;#8217;t need to look for print output in &lt;code&gt;runserver&lt;/code&gt; output. Regarding usual usage of &lt;code&gt;logging&lt;/code&gt; there are some benefits too &amp;mdash; at least you don&amp;#8217;t need to follow files and their paths (for creation of which you may not have rights), moreover immediately you have nice viewer for logs with ability to filter&amp;nbsp;them.
&lt;/p&gt;
&lt;p&gt;I&amp;#8217;ve contributed today to development of FirePython by development of two middlewares &amp;mdash; one for Django and one for &lt;span class="caps"&gt;WSGI&lt;/span&gt; applications, so now its usage is just a question of few movements. ;) In short: you need to install &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/9602"&gt;plugin&lt;/a&gt; itself, which depends on &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/1843"&gt;Firebug 1.3&lt;/a&gt; (it&amp;#8217;s still in beta stage, but I&amp;#8217;m using Firefox 3.1, so this is not scary for me :P). After that do &lt;code&gt;easy_install firepython&lt;/code&gt;. Another option: clone project from github and put folder &lt;code&gt;firepython&lt;/code&gt; from &lt;code&gt;python&lt;/code&gt; folder in &lt;code&gt;sys.path&lt;/code&gt; or your project&amp;nbsp;directory.
&lt;/p&gt;
&lt;p&gt;After that you just need to enable middleware by adding its path in &lt;code&gt;MIDDLEWARE_CLASSES&lt;/code&gt;: &lt;code&gt;firepython.django.FirePythonDjango&lt;/code&gt;. &lt;span class="caps"&gt;WSGI&lt;/span&gt; middleware has another path (how strange! :D): &lt;code&gt;firepython.wsgi.FirePythonWSGI&lt;/code&gt;, which you should use as any other &lt;span class="caps"&gt;WSGI&lt;/span&gt;&amp;nbsp;middleware.
&lt;/p&gt;
&lt;p&gt;Usage of whole system in code looks&amp;nbsp;like:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import logging
logging.debug('what you want to debug today?')
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Naturally you can use any level instead of debug &amp;mdash; you can filter them in FirePython&amp;nbsp;interface.
&lt;/p&gt;

&lt;div class="footnotes"&gt;&lt;div class="footnote" id="fn1-242239821"&gt;&lt;sup&gt;&lt;a href="#fnr1-242239821" class="footnoteBackLink" title="Jump back to footnote 1 in the text"&gt;1&lt;/a&gt;&lt;/sup&gt; &amp;mdash; &lt;p&gt;Precisely saying you will get logs which was logged while FirePython was registered as a handler in&amp;nbsp;logging.
&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;</content></entry><entry><id>http://blogg.ingspree.net/blog/2008/11/16/blogging-spree/</id><title type="html">Blogging spree</title><updated>2008-11-16T18:17:47Z</updated><published>2008-11-16T11:42:11Z</published><category term="blog" /><link href="http://blogg.ingspree.net/blog/2008/11/16/blogging-spree/" rel="self" /><link href="http://blogg.ingspree.net/blog/2008/11/16/blogging-spree/" rel="alternate" /><content type="html">&lt;p&gt;That&amp;#8217;s my first post in this blog. The idea of having blog in English arose a lot of time ago but I always had some lame excuses for myself to not do that. But two weeks ago I&amp;#8217;ve added &lt;code&gt;ForeignKey&lt;/code&gt; to &lt;code&gt;Site&lt;/code&gt; to &lt;code&gt;blog.Post&lt;/code&gt; model in &lt;a href="http://byteflow.su/" title="Django-based blog engine"&gt;Byteflow&lt;/a&gt; (which naturally reflects my posts in blog ;-)) along with &lt;code&gt;DynamicSiteMiddleware&lt;/code&gt; and that allows me to simultaneously have two blogs on different domain serving by single Byteflow instance, what, I think, is rather cool.&amp;nbsp;:D
&lt;/p&gt;
&lt;p&gt;So&amp;#8230; welcome to exhaust of my blogging spree, hope you will enjoy it!&amp;nbsp;;-)
&lt;/p&gt;</content></entry></feed>
