<?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-14094861</id><updated>2011-11-16T17:48:40.178Z</updated><category term='debian'/><category term='emacs'/><category term='solaris'/><category term='python'/><title type='text'>devork</title><subtitle type='html'>E pur si muove</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.devork.be/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.devork.be/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default?start-index=101&amp;max-results=100'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>192</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-14094861.post-9010795439453401389</id><published>2011-09-15T01:21:00.000+01:00</published><updated>2011-09-15T01:22:43.398+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>Small Emacs tweaks impoving my Python coding</title><content type='html'>&lt;p&gt;Today I've spent a few minutes tweaking Emacs a little.  The result is very simple yet makes a decent impact on usage.&lt;/p&gt;&lt;p&gt;Firstly I remembered using the c-subword-mode a long time ago, I couldn't believe I never used that in Python.  Turns out there is a more genericly named &lt;a href="https://www.gnu.org/software/emacs/manual/html_node/ccmode/Subword-Movement.html"&gt;subword-mode&lt;/a&gt; by now (emacs 23 IIUC) and it's very easy to enable for Python by default:&lt;/p&gt;&lt;code&gt;&lt;pre&gt;(add-hook 'python-mode-hook (lambda () (subword-mode 1)))&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;
&lt;p&gt;The second simple improvement was finally figuring out how to automatically enable &lt;a href="http://www.emacswiki.org/emacs/FlySpell"&gt;flyspell-mode&lt;/a&gt; when editing Restructured Text:&lt;/p&gt;&lt;code&gt;&lt;pre&gt;(add-hook 'rst-mode-hook (lambda () (flyspell-mode 1)))&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;
&lt;p&gt;Both where so simple I can't believe I didn't do them earlier.&lt;/p&gt;&lt;p&gt;And while on the subject, &lt;a href="http://www.emacswiki.org/emacs-ru/DevelockMode"&gt;develock-mode&lt;/a&gt; is great and I've been using it a very long time.  Unfortunately python isn't supported out of the box, but no worries, &lt;a href="https://metalinguist.wordpress.com/2010/02/14/emacs-develock-customization-for-python/"&gt;someone has done the work already&lt;/a&gt;.  So all you have to do is something along the lines of:&lt;/p&gt;&lt;code&gt;&lt;pre&gt;(load "~/.emacs.d/develock-py.el")&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;
&lt;p&gt;I don't think I'd still want to edit a python file without it&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-9010795439453401389?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/9010795439453401389/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2011/09/small-emacs-tweaks-impoving-my-python.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/9010795439453401389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/9010795439453401389'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2011/09/small-emacs-tweaks-impoving-my-python.html' title='Small Emacs tweaks impoving my Python coding'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-2467605366396165666</id><published>2011-06-17T19:25:00.000+01:00</published><updated>2011-06-17T19:25:48.500+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Using __getattr__ and property</title><content type='html'>&lt;p&gt;Today I wasted a lot of time trying to figure out why a class using both a __getattr__ and a property mysteriously failed.  The short version is: &lt;em&gt;Make sure you don't raise an AttributeError in the property.fget()&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The start point of this horrible voyage was a class which looked roughly like this:&lt;/p&gt;
&lt;code&gt;&lt;pre class="hightlight"&gt;class Foo(object):

    def __getattr__(self, name):
        return self._container.get(name, 'some_default')

    @property
    def foo(self):
        val = self._container.get(foo)
        if test(val):
            return some_helper(val)
        return val
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;This sees fine enough.  Only it turns out that &lt;code&gt;some_helper()&lt;/code&gt; raised an AttributeError for some invalid input.  Certainly reasonable since it was never meant to deal with incorrect input, that was meant to have been sanitised already (that was a bug in the caller which was actually just an incorrect unittest).  The main gotcha was that it seems that python doesn't just check whether a "foo" is present in all the relevant &lt;code&gt;__dict__&lt;/code&gt;'s along the mro.  Instead it seems to use &lt;code&gt;getattr(inst, "foo")&lt;/code&gt; and then delegate to &lt;code&gt;__getattr__()&lt;/code&gt; if it gets an AttributeError.  Now suddenly finding a bug in &lt;code&gt;some_helper()&lt;/code&gt; has turned into a puzzling question as to why &lt;code&gt;__getattr__()&lt;/code&gt; was called.&lt;/p&gt;
&lt;p&gt;Personally I can't see why it doesn't use the mro to statically look up the required object instead of using the AttributeError-swallowing approach.  But maybe there's a good reason.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-2467605366396165666?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/2467605366396165666/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2011/06/using-getattr-and-property_17.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2467605366396165666'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2467605366396165666'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2011/06/using-getattr-and-property_17.html' title='Using __getattr__ and property'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-4286134061659279871</id><published>2011-03-17T19:43:00.002Z</published><updated>2011-03-18T10:31:40.643Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Synchronising eventlets and threads</title><content type='html'>&lt;p&gt;&lt;a href="http://eventlet.net"&gt;Eventlet&lt;/a&gt; is an asynchronous
  network I/O framework which combines an event loop
  with &lt;a href="http://pypi.python.org/pypi/greenlet"&gt;greenlet&lt;/a&gt;-based
  coroutines to provide a familiar blocking-like API to the developer.
  One of the reasons I like eventlet a lot is that the technology it
  builds on allows it's event loop to run inside a thread or even run
  multiple event loops in different threads.  This makes it a lot more
  amenable to slowly evolving existing applications to be more
  asynchronous then solutions like
  e.g. &lt;a href="http://www.gevent.org/"&gt;gevent&lt;/a&gt; which only allow
  one event loop per process.&lt;/p&gt;

&lt;p&gt;Eventlet isn't the most mature of tools however and
  it's &lt;a href="http://eventlet.net/doc/modules.html"&gt;API&lt;/a&gt; shows
  signs of being developed as needs arose.  Not that this is
  necessarily a bad thing, APIs do need to grow from being used, but
  don't be surprised if you need to dig down into some parts and
  discover rough edges (hi IPv6!).  The API does have a decent
  collection of tools you'll be familiar with however: greenlet-local
  storage, semaphores, events (though not quite
  the &lt;a href="http://docs.python.org/library/threading.html#event-objects"&gt;
  event&lt;/a&gt; you're used too) and even some extra goodies like pools,
  WSGI
  servers, &lt;a href="http://www.python.org/dev/peps/pep-0249/"&gt;DBAPI2&lt;/a&gt;
  connection pools and &lt;a href="http://www.zeromq.org/"&gt;ZeroMQ&lt;/a&gt;
  support.  But you'll notice that all these goodies are designed to
  work in a greenlet-only world.  And the one place where threads are
  acknowledged, a global threadpool of workers as a last resort to
  make things behave asynchronous, looks very messy and entirely not
  reusable (it's full of module globals for one).  So if you're
  introducing eventlet into an existing application and you need to
  communicate data and events between threads and eventlets you'll
  find a void.&lt;/p&gt;

&lt;p&gt;So after some studying of
  the &lt;a href="http://eventlet.net/doc/threading.html#tpool-simple-thread-pool"&gt;
  tpool&lt;/a&gt; module I decided to build a class which could synchronise
  between threads and eventlets.  This class, which I called
  a &lt;em&gt;Notifier&lt;/em&gt;, can be basically thought of as
  a &lt;a href="http://docs.python.org/library/threading.html#condition-objects"&gt;Condition&lt;/a&gt;
  without the lock, i.e. there are three
  methods: &lt;code&gt;.wait()&lt;/code&gt;, &lt;code&gt;.notify()&lt;/code&gt; and the rather
  similar &lt;code&gt;.notify_all()&lt;/code&gt;.  The idea is that any thread or
  eventlet which calls &lt;code&gt;.wait()&lt;/code&gt; will block (cooperatively
  block in the case of a greenlet) until it gets woken up by a call to
  one of the notifying methods.  That's all there is to it.&lt;/p&gt;


&lt;h2&gt;Building a Notifier&lt;/h2&gt;

&lt;p&gt;(Be prepared to look at the source code
  for &lt;code&gt;eventlet.hubs.hub&lt;/code&gt;, &lt;code&gt;threading&lt;/code&gt; and
  related code when reading this.)&lt;/p&gt;

&lt;p&gt;Firstly the class will need to be constructed.  For now there's
  only one interesting instance attribute and that
  is &lt;code&gt;_waiters&lt;/code&gt; which is a set which will contain all the
  threads and eventlets currently blocking on a call
  to &lt;code&gt;.wait()&lt;/code&gt;.
  &lt;code&gt;&lt;pre&gt;
def __init__(self, hubcache=GLOBAL_HUBCACHE):
        self._waiters = set()
        self.hubcache = hubcache&lt;/code&gt;&lt;/pre&gt;
  Don't worry yet about what goes into the set of waiters and also
  ignore the hubcache for now.  We'll get to those later.&lt;/p&gt;

&lt;p&gt;Now lets build the &lt;code&gt;.wait()&lt;/code&gt; method: it needs to block
  until notified.  But blocking is significantly different when you're
  running in a thread then when running in an eventlet.  The basics
  are that in a thread you want to really block using the locking
  primitives provided by the OS (exposed to Python in
  the &lt;code&gt;thread&lt;/code&gt; module) while an eventlet essentially wants
  to switch to the event loop, called the &lt;em&gt;hub&lt;/em&gt;, in the hope
  someone will eventually switch back to it when it needs to wake up.
  These two are so different that they easily divide in two
  methods: &lt;code&gt;.gwait()&lt;/code&gt; for blocking eventlets
  and &lt;code&gt;.twait()&lt;/code&gt; for blocking threads.
  &lt;code&gt;&lt;pre&gt;
def wait(self, timeout=None):
    hub = eventlet.hubs.get_hub()
    if hub.running:
        self.gwait(timeout)
    else:
        self.twait(timeout)&lt;/pre&gt;&lt;/code&gt;
  You could call these directly obviously, but as you can see
  abstracting them away is not that hard.  You can easily detect if
  you're in an eventlet by checking if the hub (which is thread-local)
  is actually running.  The price you pay for this is that this will
  create a hub instance in each thread, even if it is not used.  But
  the worst this does is waste some memory.  (This could be avoided by
  checking for the &lt;code&gt;eventlet.hubs._threadlocal.hub&lt;/code&gt;
  attribute, but that's even more internal
  then &lt;code&gt;.get_hub()&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;As already mentioned the basics of blocking in an eventlet is to
  switch to the hub and then wait until some other
  greenlet &lt;em&gt;running in the same thread&lt;/em&gt; switches back to you.
  So a notifier needs to have a reference to your geenlet instance so
  it can call &lt;code&gt;.switch()&lt;/code&gt; on it when the time comes to wake
  you up.  But what does another thread do?  Well it turns out another
  thread could ask your hub to schedule a function to run in your
  thread using the hub's &lt;code&gt;.schedule_call_global()&lt;/code&gt; method
  since the only thread-critical operation is an append on the
  hubs's &lt;code&gt;next_timers&lt;/code&gt; list, which is a thread-safe
  operation.  Now there is another catch, remember that the hub is
  basically an event loop?  Well if no events happen then it will not
  be going round it loop!  And appening something to a list is not
  creating an event.  So what you need to do is
  use &lt;code&gt;&lt;a href="http://docs.python.org/library/os.html#os.pipe"&gt;os.pipe&lt;/a&gt;&lt;/code&gt;
  so you have a filedescriptor which you can register with the hub and
  now you can just write some data into this pipe when you want the
  hub of the waiter to wake up.  Setting up this pipe is what the
  mysterious call to &lt;code&gt;._create_pipe()&lt;/code&gt; does, we'll see it
  in detail later.  The rest is just simple sugar: dealing with
  timeouts and returning the correct values for them:
  &lt;code&gt;&lt;pre&gt;
def gwait(self, timeout=None):
    waiter = eventlet.getcurrent()
    hub = eventlet.hubs.get_hub()
    self._create_pipe(hub)
    self._waiters.add((waiter, hub))
    if timeout and timeout &gt; 0:
        timeout = eventlet.Timeout(timeout)
        try:
            with timeout:
                hub.switch()
        except eventlet.Timeout, t:
            if t is not timeout:
                raise
            self._waiters.discard((waiter, hub))
            return False
        else:
            return True
    else:
        hub.switch()
        return True&lt;/pre&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next on lets look at how you block in a thread.  This is actually
  surprisingly simple, just copy
  what &lt;code&gt;threading.Condition.wait()&lt;/code&gt; does for it: it is
  perfectly non-blocking for a greenlet to call &lt;code&gt;.release()&lt;/code&gt;
  on a lock which was acquired by another thread.  Notice the ugly
  CPU-consuming spinning which happens when a timeout is in use,
  luckily this has been fixed in python 3.2
  (&lt;a href="http://bugs.python.org/issue7316"&gt;issue7316&lt;/a&gt;).
  &lt;code&gt;&lt;pre&gt;
def twait(self, timeout=None):
    waiter = threading.Lock()
    waiter.acquire()
    self._waiters.add((waiter, None))
    if timeout is None:
        waiter.acquire()
        return True
    else:
        # Spin around a little, just like the stdlib does
        _time = time.time
        _sleep = time.sleep
        min = __builtin__.min
        endtime = _time() + timeout
        delay = 0.0005      # 500 us -&gt; initial delay of 1 ms
        while True:
            gotit = waiter.acquire(0)
            if gotit:
                break
            remaining = endtime - _time()
            if remaining &lt;= 0:
                break
            delay = min(delay * 2, remaining, .05)
            _sleep(delay)
        if not gotit:
            self._waiters.discard((waiter, None))
            return False
        else:
            return True&lt;/pre&gt;&lt;/code&gt;
  The last thing of interest here is how the hub does not matter, so
  we just place &lt;code&gt;None&lt;/code&gt; in the set of waiters.&lt;/p&gt;

&lt;p&gt;Now lets have a look at what notify looks like.  We've already
  discussed what needs to happen here: to notify an eventlet we need
  schedule a call with it's hub to switch to it (no point special
  casing when we're already running in that same hub).  In case we
  notified it from a different thread we also need to signal the hub
  using the pipe set up by &lt;code&gt;.wait()&lt;/code&gt; so it will actually
  start to go round it's loop and execute this just scheduled call.
  Notifying a thread is even easier: just unlock the lock it's trying
  to acquire.
  &lt;code&gt;&lt;pre&gt;
def notify(self):
    if self._waiters:
        waiter, hub = self._waiters.pop()
        if hub is None:
            # This is a waiting thread
            try:
                waiter.release()
            except thread.error:
                pass
        else:
            # This is a waiting greenlet
            def notif(waiter):
                waiter.switch()
            hub.schedule_call_global(0, notif, waiter)
            if hub is not eventlet.hubs.get_hub():
                self._kick_hub(hub)&lt;/pre&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Admittedly I've hidden some of the cute trickery away in
  those &lt;code&gt;._create_pipe()&lt;/code&gt; and &lt;code&gt;._kick_hub()&lt;/code&gt;
  calls, so lets leave the boring &lt;code&gt;.notify_all()&lt;/code&gt; and skip
  straight to them.&lt;/p&gt;

&lt;p&gt;The principle for this is that each eventlet which
  calls &lt;code&gt;.gwait()&lt;/code&gt; needs to ensure there is a pipe
  available to which a thread can write something.  If the hub of the
  eventlet was waiting for the reading end of this pipe to become
  readable it will wake up and notice it has to run
  the &lt;code&gt;notif()&lt;/code&gt; function which was scheduled by our call
  to &lt;code&gt;.notify()&lt;/code&gt;.  But creating a pipe for each call
  to &lt;code&gt;.gwait()&lt;/code&gt; does seem rather wasteful and this is where
  the mysterious hubcache comes into play: it is a dictionary keeping
  track of the pipe associated with each hub.
  &lt;code&gt;&lt;pre&gt;
def _create_pipe(self, hub):
    if hub in self.hubcache:
        return
    def read_callback(fd):
        os.read(fd, 512)
    rfd, wfd = os.pipe()
    listener = hub.add(eventlet.hubs.hub.READ, rfd, read_callback)
    self.hubcache[hub] = (rfd, wfd, listener)&lt;/pre&gt;&lt;/code&gt;
  You can see this asks the hub to wake up when the reading end of the
  created pipe becomes readable and when this
  happens &lt;code&gt;read_callback()&lt;/code&gt; will be called.  The only
  purpose of &lt;code&gt;read_callback()&lt;/code&gt; is to read all the data
  written to the pipe so that the OS buffers are emptied and the pipe
  can be re-used to wake the hub up the next time.&lt;/p&gt;

&lt;p&gt;Now there is just &lt;code&gt;._kick_hub()&lt;/code&gt; left.  This should now
  be obvious: look up the hub in the hubcache and write some data to
  the writing end of the pipe.  The only gotcha here is that while we
  might be called from another thread, this does not mean the calling
  thread itself can't be part of an eventlet mainloop.  So in that
  case make sure not to do a blocking write (as unlikely as that might
  be).
  &lt;code&gt;&lt;pre&gt;
def _kick_hub(self, hub):
    rfd, wfd, r_listener = self.hubcache[hub]
    current_hub = eventlet.hubs.get_hub()
    if current_hub.running:
        def write(fd):
            os.write(fd, 'A')
            current_hub.remove(w_listener)
        w_listener = current_hub.add(eventlet.hubs.hub.WRITE, wfd, write)
    else:
        os.write(wfd, 'A')&lt;/pre&gt;&lt;/code&gt;
  Notice here how in the async case we remove the listener which was
  used to wake the hub up right from the callback itself.  If we
  didn't do this then the hub would most likely find the writing end
  of the pipe writable again on the next loop which would trigger
  another notification of the other hub, not what we want!&lt;/p&gt;


&lt;h2&gt;That's cute, but what now?&lt;/h2&gt;

&lt;p&gt;We've now got a great way of notifying other threads and eventlets
  at will.  But this is an entirely non-standard tool!  Using this is
  strange, unfamiliar and unwieldy.  This isn't one of the
  synchronisation primitives we know and wanted to use.&lt;/p&gt;

&lt;p&gt;But look how easy it is to build a lock now: we just need a real
  lock and one of these strange notifiers:
  &lt;code&gt;&lt;pre&gt;
class Lock(object):

    def __init__(self, hubcache=GLOBAL_HUBCACHE):
        self.hubcache = hubcache
        self._notif = Notifier(hubcache)
        self._lock = threading.Lock()
        self.owner = None

    def acquire(self, blocking=True, timeout=None):
        gotit = self._lock.acquire(False)
        if gotit or not blocking:
            if gotit:
                self.owner = eventlet.getcurrent()
            return gotit
        if timeout is None:
            while not gotit:
                self._notif.wait()
                gotit = self._lock.acquire(False)
            if gotit:
                self.owner = eventlet.getcurrent()
                return True
        else:
            if timeout &lt; 0:
                raise RuntimeError('timeout must be greater or equal then 0')
            now = time.time()
            end = now + timeout
            while not gotit and (now &lt; end):
                self._notif.wait(end - now)
                gotit = self._lock.acquire(False)
                now = time.time()
            if gotit:
                self.owner = eventlet.getcurrent()
            return gotit

    __enter__ = acquire

    def release(self):
        self._lock.release()
        self.owner = None
        self._notif.notify()

    def __exit__(self, exc_type, exc_value, traceback):
        self.release()

    def __repr__(self):
        return ('&amp;lt;gsync.Lock object at 0x%x (%r, %r)&amp;gt;' %
                (id(self), self.owner, self._notif))&lt;/code&gt;&lt;/pre&gt;
  Most of this code is to deal with the timeout of the lock.  If you
  would only implement a lock as how it was before Python 3.2
  this would have been even simpler.  (Oh, also notice
  the &lt;code&gt;owner&lt;/code&gt; attribute, that will come in handy for the
  next tool.)&lt;/p&gt;

&lt;p&gt;Of course now we have a lock we can easily build the next tool: a
  proper &lt;code&gt;Condition&lt;/code&gt;.  It's almost like I planned this!
  &lt;code&gt;&lt;pre&gt;
class Condition(object):

    def __init__(self, lock=None, hubcache=GLOBAL_HUBCACHE):
        if lock is None:
            self._lock = Lock(hubcache=hubcache)
        else:
            self._lock = lock
        self._notif = Notifier(hubcache=hubcache)

        # Export the lock methods
        self.acquire = self._lock.acquire
        self.release = self._lock.release
        self.__enter__ = self._lock.__enter__
        self.__exit__ = self._lock.__exit__

    def __repr__(self):
        return '&amp;lt;gsync.Condition (%r, %r)&amp;gt;' % (self._lock, self._notif)

    def wait(self, timeout=None):
        if self._lock.owner is not eventlet.getcurrent():
            raise RuntimeError('Can not wait on un-acquired lock')
        self._lock.release()
        try:
            self._notif.wait(timeout)
        finally:
            self._lock.acquire()

    def notify(self):
        if self._lock.owner is not eventlet.getcurrent():
            raise RuntimeError('Can not notify on un-acquired lock')
        self._notif.notify()

    def notify_all(self):
        if self._lock.owner is not eventlet.getcurrent():
            raise RuntimeError('Can not notify on un-acquired lock')
        self._notif.notify_all()&lt;/pre&gt;&lt;/code&gt;
  No surprises here.  This is truly getting trivial to implement
  thanks to our previous two primitives.&lt;/p&gt;

&lt;p&gt;Now once we have a condition we can finally get to the real prise:
  a queue to move data freely between threads and eventlets.
  &lt;code&gt;&lt;pre&gt;
import Queue as queue

class BaseQueue(object):

    def __init__(self, maxsize=0, hubcache=GLOBAL_HUBCACHE):
        self.hubcache = hubcache
        self.maxsize = maxsize
        self._init(maxsize)
        self.mutex = Lock(hubcache=hubcache)
        self.not_empty = Condition(self.mutex, hubcache=hubcache)
        self.not_full = Condition(self.mutex, hubcache=hubcache)
        self.all_tasks_done = Condition(self.mutex, hubcache=hubcache)
        self.unfinished_tasks = 0

class Queue(BaseQueue, queue.Queue):
    pass&lt;/pre&gt;&lt;/code&gt;
  Great, only had to provide a new &lt;code&gt;.__init__()&lt;/code&gt; which uses
  our own locking primitives, everything else of the
  stdlib &lt;code&gt;Queue&lt;/code&gt; class can be re-used.&lt;/p&gt;

&lt;p&gt;But why the strange diversion to create a
  separate &lt;code&gt;BaseQueue&lt;/code&gt; class?  Well, it makes making the
  priority and lifo queues very easy:
  &lt;code&gt;&lt;pre&gt;
class PriorityQueue(BaseQueue, queue.PriorityQueue):
    pass

class LifoQueue(BaseQueue, queue.LifoQueue): pass&lt;/pre&gt;&lt;/code&gt; That's
    right, &lt;a href="http://www.python.org/download/releases/2.3/mro/"&gt;mro&lt;/a&gt;
    FTW!&lt;/p&gt;

&lt;h2&gt;Caveats when mixing threads and eventlets&lt;/h2&gt;

&lt;p&gt;There is one issue to watch out for: imagine a thread which
  consumes items from a queue, spawning eventlets to do the work.
  &lt;code&gt;&lt;pre&gt;
class Worker(threading.Thread):
    def __init__(self, inputq):
        self.inputq = inputq

    def run(self):
        eventlet.sleep(0)  # start the hub
        while True:
            item = self.inputq.get()
            if item is PoisonPill:
                break
            eventlet.spawn(self.do_stuff, item)&lt;/code&gt;&lt;/pre&gt;
  Notice that &lt;code&gt;eventlet.sleep(0)&lt;/code&gt; line?  It basically
  switches to the hub, thereby implicitly starting it, which then
  switches back immediately.  But why?&lt;/p&gt;

&lt;p&gt;Remember the code for &lt;code&gt;Notifier.wait()&lt;/code&gt;, it checked if
  the hub was running to detect whether it was being called from
  inside an eventlet or not.  So if you didn't manage to start the hub
  before calling this method the whole thread will block!  Hence the
  minor hack to start the hub manually beforehand.&lt;/p&gt;


&lt;h2&gt;All the code&lt;/h2&gt;

&lt;p&gt;Here all the code for the notifier in one piece, including the
  docstrings and comments.  This also includes the global hubcache
  with a tiny bit of extra magic to be able to clear the cache if you
  so desire.  Having this as a parameter to pass in allows you to use
  different hub caches if you have a reason to do so.&lt;/p&gt;

&lt;code&gt;&lt;pre&gt;
import os
import thread
import threading
import time

import eventlet


class HubCache(dict):
    """Cache used by Notifier instances

    This is a dict-subclass to overwrite the .clear() method.  It's
    keys are hubs and values are (rfd, wfd, listener).  Using this
    means you can clear the cache in a way which will unregister the
    listeners from the hubs and close all filedescriptors.

    XXX This is hugely incomplete, only remove items from this cache
        using the .clear() method as the other ways of removing items
        will not release resources properly.
    """

    def clear(self):
        while self:
            hub, (rfd, wfd, listener) = self.popitem()
            hub.remove(listener)
            os.close(rfd)
            os.close(wfd)

    def __del__(self):
        self.clear()


"""The global hubcache

This is the default hubcache used by Notifier instances.
"""
GLOBAL_HUBCACHE = HubCache()


class Notifier(object):
    """Notify one or more waiters

    This is essentially a condition without the lock.  It can be used
    to signal between threads and greenlets at will.
    """
    # This doesn't use eventlet.hubs.trampoline since that results in
    # a filedescriptor per waiting greenlet.  Instead each eventlet
    # that calls .gwait() will ensure there's a filedescriptor
    # registered for reading for with it's hub.  This filedescriptor
    # is then only used when another thread wants to wake up the hub
    # in order for a notification to be delivered to the eventlet.

    def __init__(self, hubcache=GLOBAL_HUBCACHE):
        """Initialise the notifier

        The hubcache is a dictionary which will keep pipes used by the
        notifier so that only ever one pipe gets created per hub.  The
        default is to share this hubcache globally so all notifiers
        use the same pipes for intra-hub communication.
        """
        # Each item in this set is a tuple of (waiter, hub).  For an
        # eventlet the waiter is the greenlet while for a thread it is
        # a lock.  For a thread the hub item is always None.
        self._waiters = set()
        self.hubcache = hubcache

    def wait(self, timeout=None):
        """Wait from a thread or eventlet

        This blocks the current thread/eventlet until it gets woken up
        by a call to .notify() or .notify_all().

        This will automatically dispatch to .gwait() or .twait() as
        needed so that the blocking will be cooperative for greenlets.

        Returns True if this thread/eventlet was notified and False
        when a timeout occurred.
        """
        hub = eventlet.hubs.get_hub()
        if hub.running:
            self.gwait(timeout)
        else:
            self.twait(timeout)

    def gwait(self, timeout=None):
        """Wait from an eventlet

        This cooperatively blocks the current eventlet by switching to
        the hub.  The hub will switch back to this eventlet when it
        gets notified.

        Usually you can just call .wait() which will dispatch to this
        method if you are in an eventlet.

        Returns True if this thread/eventlet was notified and False
        when a timeout occurred.
        """
        waiter = eventlet.getcurrent()
        hub = eventlet.hubs.get_hub()
        self._create_pipe(hub)
        self._waiters.add((waiter, hub))
        if timeout and timeout &gt; 0:
            timeout = eventlet.Timeout(timeout)
            try:
                with timeout:
                    hub.switch()
            except eventlet.Timeout, t:
                if t is not timeout:
                    raise
                self._waiters.discard((waiter, hub))
                return False
            else:
                return True
        else:
            hub.switch()
            return True

    def twait(self, timeout=None):
        """Wait from an thread

        This blocks the current thread by using a conventional lock.

        Usually you can just call .wait() which will dispatch to this
        method if you are in an eventlet.

        Returns True if this thread/eventlet was notified and False
        when a timeout occurred.
        """
        waiter = threading.Lock()
        waiter.acquire()
        self._waiters.add((waiter, None))
        if timeout is None:
            waiter.acquire()
            return True
        else:
            # Spin around a little, just like the stdlib does
            _time = time.time
            _sleep = time.sleep
            min = __builtin__.min
            endtime = _time() + timeout
            delay = 0.0005      # 500 us -&gt; initial delay of 1 ms
            while True:
                gotit = waiter.acquire(0)
                if gotit:
                    break
                remaining = endtime - _time()
                if remaining &lt;= 0:
                    break
                delay = min(delay * 2, remaining, .05)
                _sleep(delay)
            if not gotit:
                self._waiters.discard((waiter, None))
                return False
            else:
                return True

    def notify(self):
        """Notify one waiter

        This will notify one waiter, regardless of whether it is a
        thread or eventlet, resulting in the waiter returning from
        it's .wait() call.

        This will never block itself so can be called from either a
        thread or eventlet itself and will wake up the hub of another
        thread if an eventlet from it is notified.
        """
        if self._waiters:
            waiter, hub = self._waiters.pop()
            if hub is None:
                # This is a waiting thread
                try:
                    waiter.release()
                except thread.error:
                    pass
            else:
                # This is a waiting greenlet
                def notif(waiter):
                    waiter.switch()
                hub.schedule_call_global(0, notif, waiter)
                if hub is not eventlet.hubs.get_hub():
                    self._kick_hub(hub)

    def notify_all(self):
        """Notify all waiters

        Similar to .notify() but will notify all waiters instead of
        just one.
        """
        for i in xrange(len(self._waiters)):
            self.notify()

    def _create_pipe(self, hub):
        """Create a pipe for a hub

        This creates a pipe (read and write fd) and registers it with
        the hub so that ._kick_hub() can use this to signal the hub.

        This keeps a cache of hubs on ``self.hubcache`` so that only
        one pipe is created per hub.  Furthermore this dict is never
        cleared implicitly to avoid creating new sockets all the time.

        This method is always called from .gwait() and therefore can
        only run once for a given hub at the same time.  Thus it is
        threadsave.
        """
        if hub in self.hubcache:
            return
        def read_callback(fd):
            # This just reads the (bogus) data just written to empty
            # the os queues.  The only purpose was to kick the hub
            # round it's loop which is now has.  The notif function
            # scheduled by .notify() will now do it's work.
            os.read(fd, 512)
        rfd, wfd = os.pipe()
        listener = hub.add(eventlet.hubs.hub.READ, rfd, read_callback)
        self.hubcache[hub] = (rfd, wfd, listener)

    def _kick_hub(self, hub):
        """Kick the hub around it's loop

        Threads need to be able to kick a hub around their loop by
        interrupting the sleep.  This is done with the help of a
        filedescriptor to which the thread writes a byte (using this
        method) which will then wake up the hub.
        """
        rfd, wfd, r_listener = self.hubcache[hub]
        current_hub = eventlet.hubs.get_hub()
        if current_hub.running:
            def write(fd):
                os.write(fd, 'A')
                current_hub.remove(w_listener)
            w_listener = current_hub.add(eventlet.hubs.hub.WRITE, wfd, write)
        else:
            os.write(wfd, 'A')

    def __repr__(self):
        return ('&amp;lt;gsync.Notifier object at 0x%x (%d waiters)&amp;gt;' %
                (id(self), len(self._waiters)))&lt;/code&gt;&lt;/pre&gt;


&lt;h2&gt;That's all folks&lt;/h2&gt;

&lt;p&gt;So it seems that with some careful thinkering you can create all
your tried and tested tools to communicate between threads and
eventlets or between eventlets in different threads.  This makes
adopting eventlet into an existing application a whole lot more
approachable.  It certainly helped me!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-4286134061659279871?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/4286134061659279871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2011/03/synchronising-eventlets-and-threads.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4286134061659279871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4286134061659279871'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2011/03/synchronising-eventlets-and-threads.html' title='Synchronising eventlets and threads'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8723281590136023076</id><published>2011-02-09T00:30:00.001Z</published><updated>2011-02-09T00:30:30.245Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='solaris'/><title type='text'>Creating subprocesses in new contracts on Solaris 10</title><content type='html'>&lt;p&gt;Solaris 10 introduced "contracts" for processes.  You can read all about it in the &lt;code&gt;contract(4)&lt;/code&gt; manpage but simply put it's a grouping of processes under another ID, and you can "monitor" these groups, e.g. be notified when a process in a group coredumps etc.  This is actually one of the tools of Solaris' init replacement &lt;code&gt;smf(5)&lt;/code&gt;, which is probably the main reason people care about contracts.&lt;/p&gt;
&lt;p&gt;Suppose you have a daemon managed by SMF which executes subprocesses as part of it's life (e.g. because of &lt;a href="http://docs.python.org/library/multiprocessing.html"&gt;multiprocessing&lt;/a&gt;).  If your application is not aware of contracts all subprocesses will be in the same contract.  Which is fine until the main process dies a miserable death and one expects SMF to restart it.  Only it doesn't since it sees the contract as still being alive and sees no harm.  Time to start your subprocesses in a different contract then.&lt;/p&gt;
&lt;p&gt;Anyway, whatever your motivation, starting the subprocesses in new contracts is normally done by activating the process contract template, i.e. opening &lt;code&gt;/system/contract/process/template&lt;/code&gt; and then calling &lt;code&gt;ct_tmpl_activate(3)&lt;/code&gt; on the filedescriptor.  Only problem is that this isn't exposed to python.&lt;/p&gt;
&lt;p&gt;Fortunately the &lt;code&gt;libcontract(3)&lt;/code&gt; functions we need for this are very simple, so &lt;a href="http://docs.python.org/library/ctypes.html"&gt;ctypes&lt;/a&gt; can handle this very nicely.  The code is pretty simple:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;import ctypes
import os

fd = os.open('/system/contract/process/template', os.O_RDWR)
libct = ctypes.cdll.LoadLibrary('libcontract.so.1')
rv = libct.ct_tmpl_activate(fd)
if rv != 0:
    raise Exception('oops')
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;That's all there is to it.  Each &lt;code&gt;fork(2)&lt;/code&gt; call will now result in a process running in it's own contract.&lt;/p&gt;
&lt;p&gt;It's nice when C-APIs are simple enough to be used from inside python instead of having to write wrappers.&lt;/p&gt;
&lt;p&gt;PS: Needless to say you can do a whole lot more with contracts, just read up on the libcontract API docs.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8723281590136023076?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8723281590136023076/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2011/02/creating-subprocesses-in-new-contracts.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8723281590136023076'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8723281590136023076'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2011/02/creating-subprocesses-in-new-contracts.html' title='Creating subprocesses in new contracts on Solaris 10'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-6186477964512742069</id><published>2010-12-19T22:13:00.000Z</published><updated>2010-12-19T22:13:04.296Z</updated><title type='text'>re.search() faster then re.match()</title><content type='html'>&lt;p&gt;This is a counter-intuitive discovery, in &lt;a href="http://ipython.scipy.org/"&gt;IPython&lt;/a&gt;:
&lt;pre class="prettyprint"&gt;&lt;code&gt;
In [18]: expr = re.compile('foo')

In [19]: %timeit expr.search('foobar')
1000000 loops, best of 3: 453 ns per loop

In [20]: %timeit expr.match('foobar')
1000000 loops, best of 3: 638 ns per loop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So now I'm left wondering why &lt;code&gt;.match()&lt;/code&gt; exists at all.  Is it really such a common occurrence that it's worth an extra function/method?&lt;/p&gt;
&lt;p&gt;Just to be complete, if this is actually what you want there is no performance gap:&lt;/p&gt;
&lt;pre class="prettyprint"&gt;&lt;code&gt;
In [25]: expr = re.compile('^foo')

In [26]: %timeit expr.search('foobar')
1000000 loops, best of 3: 617 ns per loop

In [27]: %timeit expr.match('foobar')
1000000 loops, best of 3: 612 ns per loop
&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-6186477964512742069?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/6186477964512742069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/12/research-faster-then-rematch.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6186477964512742069'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6186477964512742069'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/12/research-faster-then-rematch.html' title='re.search() faster then re.match()'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-5387653356314019983</id><published>2010-11-02T00:10:00.000Z</published><updated>2010-11-02T00:10:14.353Z</updated><title type='text'>Storm and SQLite in-memory databases</title><content type='html'>&lt;p&gt;When using an SQLite in-memory databases in &lt;a href="http://storm.canonical.com"&gt;storm&lt;/a&gt; the different stores created from the same database are not modifying the same SQLite database.  E.g.&lt;/p&gt;
&lt;code&gt;&lt;pre calss="prettyprint"&gt;
db = storm.locals.create_database('sqlite:')
store1 = storm.locals.Store(db)
store2 = storm.locals.Store(db)
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Here &lt;code&gt;store1&lt;/code&gt; and &lt;code&gt;store2&lt;/code&gt; will not refer to the same database, despite the fact that this is what would be natural.  The reason is that SQLite in-memory databases are specific to their connection object.  And the connection object is part of the store object, not the database object.&lt;/p&gt;
&lt;p&gt;The upshot is that I can't use in-memory databases inside my unittests that easily because the code under tests assumes creating stores is cheap (not caring too much about the caching).  Which all kind of sucks.&lt;/p&gt;
&lt;p&gt;PS: A whole different rant is about libraries designed for "from foo import *", e.g. storm.locals.*, fabric.api.*.  At least for the later you can do "import fabric.api as fab", "import storm.locals as storm" has it's limitations...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-5387653356314019983?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/5387653356314019983/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/11/storm-and-sqlite-in-memory-databases.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5387653356314019983'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5387653356314019983'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/11/storm-and-sqlite-in-memory-databases.html' title='Storm and SQLite in-memory databases'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8053242777203521042</id><published>2010-09-02T13:10:00.000+01:00</published><updated>2010-09-02T13:10:54.790+01:00</updated><title type='text'>Finding the linux thread ID from within python using ctypes</title><content type='html'>&lt;p&gt;So I've got a multi-threaded application and suddenly I notice there's one thread running away and using all CPU.  Not good, probably a loop gone wrong.  But where?  One way to find this is revert history in the VCS and keep trying it out till you find the bad commit.  Another way is to find out which thread is doing this, this is of course much more fun!&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;ps -p PID -f -L&lt;/code&gt; you'll see the thread ID which is causing the problems.  To relate this to a Python thread I subclass &lt;code&gt;threading.Thread&lt;/code&gt;, override it's &lt;code&gt;.start()&lt;/code&gt; method to first wrap the &lt;code&gt;.run()&lt;/code&gt; method so that you can log the thread ID before calling the original &lt;code&gt;.run()&lt;/code&gt;.  Since I was already doing all of this apart from the logging of the thread ID this was less work then it sounds.  But the hard part is finding the thread ID.&lt;/p&gt;
&lt;p&gt;Python knows of a &lt;code&gt;&lt;a href="http://docs.python.org/library/thread.html#thread.get_ident"&gt;threading.get_ident()&lt;/a&gt;&lt;/code&gt; method but this is merely a long unique integer and does not correspond to the actual thread ID of the OS.  The kernel allows you to get the thread ID: &lt;tt&gt;getid(2)&lt;/tt&gt;.  But this must be called using a system call with the constant name &lt;tt&gt;SYS_gettid&lt;/tt&gt;.  Because it's hard to use constants in ctypes (at least I don't know how to do this), and this is not portable anyway, I used this trivial C program to find out the constant value:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/syscall.h&amp;gt;

int main(void)
{
    printf("%d\n", SYS_gettid);
    return 0;
}
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;In my case the constant to use is 186.  Now all that is left is using &lt;a href="http://docs.python.org/library/ctypes.html"&gt;ctypes&lt;/a&gt; to do the system call:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
import ctypes

SYS_gettid = 186
libc = ctypes.cdll.LoadLibrary('libc.so.6')
tid = libc.syscall(SYS_gettid)
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;That's it!  Now you have the matching thread ID!&lt;/p&gt;
&lt;p&gt;Going back to the original problem you can now associate this thread ID with the thread name and you should be able to find the problematic thread.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8053242777203521042?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8053242777203521042/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/09/finding-linux-thread-id-from-within.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8053242777203521042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8053242777203521042'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/09/finding-linux-thread-id-from-within.html' title='Finding the linux thread ID from within python using ctypes'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8667174982809911331</id><published>2010-08-14T19:35:00.000+01:00</published><updated>2010-08-14T19:35:49.198+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Return inside with statement (updated)</title><content type='html'>&lt;p&gt;Somehow my brain seems to think there's a reason not to return inside a with statement, so rather then doing this:&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
def foo():
    with ctx_manager:
        return bar()
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;I always do:&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
def foo():
    with ctx_manager:
         result = bar()
    return result
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;No idea why nor where I think to have heard/read this.  Searching for this brings up absolutely no rationale.  So if you know why this is so, or know that the first version is perfectly fine, please enlighten me!&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Seems it's only relevant if you're reading a file using the with statement.  This seems to have come from the &lt;a href="http://docs.python.org/howto/doanddont.html#exceptions"&gt;python documentation itself&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The last version is not very good either — due to implementation details, the file would not be closed when an exception is raised until the handler finishes, and perhaps not at all in non-C implementations (e.g., Jython).&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
def get_status(file):
    with open(file) as fp:
        return fp.readline()
&lt;/pre&gt;&lt;/code&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sadly it doesn't say what the implementation details are nor how to do this correctly.  I can have several more or less educated guesses at why and which way to do this better.  But I'd love to get a more detailed description of what happens in the implementations when doing this, because the worst-case way of interpreting that example is that using &lt;code&gt;open()&lt;/code&gt; or &lt;code&gt;file()&lt;/code&gt; as a context manager is completely useless.  Which I would hate.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8667174982809911331?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8667174982809911331/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/08/return-inside-with-statement.html#comment-form' title='16 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8667174982809911331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8667174982809911331'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/08/return-inside-with-statement.html' title='Return inside with statement (updated)'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1704176700132965224</id><published>2010-08-09T15:03:00.001+01:00</published><updated>2010-08-09T22:16:06.996+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Templating engine in python stdlib?</title><content type='html'>&lt;p&gt;I am a great proponent of the &lt;a href="http://docs.python.org/library/"&gt;python standard library&lt;/a&gt;, I love having lots of tools in there and hate having to resort to thirdparty libs.  This is why I was wondering if there could be a real templating engine in the stdlib some day?  I'm not a great user of templates, but sometimes &lt;code&gt;&lt;a href="http://docs.python.org/library/string.html#template-strings"&gt;string.Template&lt;/a&gt;&lt;/code&gt; is just not good enough and real templating would make things a lot more readable.&lt;/p&gt;
&lt;p&gt;Not being a great templating user I don't know how the world of templating looks.  Is there a template engine out there which would be a candidate to move to the stdlib?  Or do most people think this is a stupid idea?  Or maybe this has been discussed before and I didn't find the discussion?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1704176700132965224?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1704176700132965224/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/08/templating-engine-in-python-stdlib.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1704176700132965224'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1704176700132965224'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/08/templating-engine-in-python-stdlib.html' title='Templating engine in python stdlib?'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1212399035108298826</id><published>2010-07-26T22:02:00.000+01:00</published><updated>2010-07-26T22:02:26.165+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='debian'/><title type='text'>Using Debian source format 3.0 (quilt) and svn-buildpackage</title><content type='html'>&lt;p&gt;Searching the svn-buildpackage manpage for the 3.0 (quilt) format I thought that it wasn't able to apply the patches in debian/patches during build time.  Instead I was doing a horrible dance which looked something like "&lt;code&gt;svn-buildpackage --svn-export; cd ../build-area/...; debuild&lt;/code&gt;".  Turns out I was completely wrong.&lt;/p&gt;
&lt;p&gt;svn-buildpackage &lt;em&gt;doesn't need to know&lt;/em&gt; about the source format.  Instead it simply invokes dpkg-buildpackage which will automatically notice that the patches are not applied and apply them before building.  That simple!&lt;/p&gt;
&lt;p&gt;Thanks to Niels Thykier to point this out to me on IRC.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1212399035108298826?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1212399035108298826/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/07/using-debian-source-format-30-quilt-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1212399035108298826'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1212399035108298826'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/07/using-debian-source-format-30-quilt-and.html' title='Using Debian source format 3.0 (quilt) and svn-buildpackage'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-6749131034550447440</id><published>2010-07-23T14:32:00.000+01:00</published><updated>2010-07-23T14:32:19.317+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Europython, threading and virtualenv</title><content type='html'>&lt;center&gt;
&lt;p&gt;I use threads&lt;br/&gt;&lt;small&gt;correctly&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;I do &lt;strong&gt;not&lt;/strong&gt; use virtualenv&lt;br/&gt;&lt;small&gt;and dont't want to&lt;/small&gt;&lt;/p&gt;
&lt;/center&gt;
&lt;p&gt;Just needed to get that out of my system after europython, now mock me.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;PS: I should probably have done this as a lightening talk but that occurred to me too late.&lt;/small&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-6749131034550447440?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/6749131034550447440/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/07/europython-threading-and-virtualenv.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6749131034550447440'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6749131034550447440'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/07/europython-threading-and-virtualenv.html' title='Europython, threading and virtualenv'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1581827670981953566</id><published>2010-06-13T16:44:00.005+01:00</published><updated>2010-06-14T21:02:14.838+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>py.test test generators and cached setup</title><content type='html'>&lt;p&gt;Recently I've been enjoying &lt;a href="http://codespeak.net/py/dist/test/"&gt;py.test&lt;/a&gt;'s &lt;a href="http://codespeak.net/py/dist/test/funcargs.html"&gt;test function arguments&lt;/a&gt;, it takes a little getting used too but soon you find that it's quite likely a better way then the &lt;a href="http://codespeak.net/py/dist/test/xunit_setup.html"&gt;xUnit-style&lt;/a&gt; of setup/teardown.  One slightly more advanced usage was using cached setup together with test generators however.  While not difficult that took me some figuring out, so let me document it here.&lt;/p&gt;
&lt;p&gt;Since &lt;a href="http://bruynooghe.blogspot.com/2008/09/generative-tests.html"&gt;I haven't been a fan of generative tests before&lt;/a&gt; I'll explain why I think I can make use of them now.  I was writing a wrapper around &lt;a href="http://pysnmp.sourceforge.net/"&gt;pysnmp&lt;/a&gt; to handle SNMP-GET requests transparently between the different versions.  For this I wrote a number of test functions which do some GET requests and check the results, the basic outline of such a test is:&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
def test_some_get(wrapper_v1):
    oids = ...
    result = wrapper_v1.get(oids)
    assert ...
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Here &lt;code&gt;wrapper_v1&lt;/code&gt; is a &lt;a href="http://codespeak.net/py/dist/test/funcargs.html"&gt;funcarg&lt;/a&gt; which returns an instance of my wrapper class configured for SNMPv1.  The extra catch here is that this funcarg uses a function which tries to find an available SNMP agent, trying if one is running on the local host (for the developer) or if a well-know test host is reachable (for lazy developers on our dev network and for buildbots), skipping the test otherwise.  But to avoid the relatively long timeouts involved for each individual test this function needs to be cached.  Here's the outline of this funcarg:&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
def pytest_funcarg__wrapper_v1(request):
    cfg = request.cached_setup(setup=check_snmp_v1_avail, scope='session')
    if not cfg:
        py.test.skip('No SNMPv1 agent available')
    return SnmpWrapper(cfg)
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Once having all the tests using this &lt;code&gt;wrapper_v1&lt;/code&gt; funcarg I obviously want exactly the same tests for SNMPv2 since that's the whole point of the wrapper.  For this I'd need a &lt;code&gt;wrapper_v2&lt;/code&gt; funcarg which is configured for SNMPv2, but that would mean duplicating all the tests!  Enter test generators.&lt;/p&gt;
&lt;p&gt;The trick to combine test generators with cached setup is not to use the &lt;code&gt;funcargs&lt;/code&gt; argument to &lt;code&gt;&lt;a href="http://codespeak.net/py/dist/test/funcargs.html#the-metafunc-addcall-method"&gt;metafunc.addcall()&lt;/a&gt;&lt;/code&gt; but rather use the &lt;code&gt;param&lt;/code&gt; argument in combination with a normal funcarg.  The normal funcarg can then use &lt;code&gt;&lt;a href="http://codespeak.net/py/dist/test/funcargs.html#managing-fixtures-across-test-modules-and-test-runs"&gt;request.cached_setup()&lt;/a&gt;&lt;/code&gt; and use the &lt;code&gt;&lt;a href="http://codespeak.net/py/dist/test/funcargs.html#funcarg-factory-request-objects"&gt;request.param&lt;/a&gt;&lt;/code&gt; to decide how to configure the wrapper object returned.  This is what that looks like:&lt;p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
def pytest_generate_tests(metafunc):
    if 'snmpwrapper' in metafunc.funcargnames:
        metafunc.addcall(id='SNMPv1', param='v1')
        metafunc.addcall(id='SNMPv2', param='v2')

def pytest_funcarg__snmpwrapper(request):
    cfg = request.cached_setup(setup=lambda: check_snmp_v1_avail(request.param),
                               scope='session', extrakey=request.param)
    if not cfg:
        py.test.skip('No SNMP%s agent available' % request.param)
    return SnmpWrapper(cfg)
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;&lt;em&gt;Don't forget the &lt;code&gt;extrakey&lt;/code&gt; argument to &lt;code&gt;cached_setup&lt;/code&gt;.&lt;/em&gt;  The caching uses the name of the requested object, "snmpwrapper" in this case, and the extrakey value to decide when to re-use the caching.  If you forget &lt;code&gt;extrakey&lt;/code&gt; both calls will return the same &lt;code&gt;cfg&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And that's all that's needed!  Test now simply ask for the &lt;code&gt;snmpwrapper&lt;/code&gt; funcarg and will get run twice, once configured for SNMPv1 and once for SNMPv2.  Running the tests will now look like this:&lt;/p&gt;
&lt;pre&gt;
flub@signy:...$ py.test -v snmp_test.py
============================= test session starts ==============================
python: platform linux2 -- Python 2.6.5 -- pytest-1.1.1 -- /usr/bin/python
test object 1: /home/flub/.../snmp_test.py

snmp_test.py:57: test_get_one.test_get_one[SNMPv1] PASS
snmp_test.py:57: test_get_one.test_get_one[SNMPv2] PASS
snmp_test.py:63: test_get_two.test_get_two[SNMPv1] PASS
snmp_test.py:63: test_get_two.test_get_two[SNMPv2] PASS
snmp_test.py:71: test_get_two_bad.test_get_two_bad[SNMPv1] PASS
snmp_test.py:71: test_get_two_bad.test_get_two_bad[SNMPv2] PASS
snmp_test.py:79: test_get_many.test_get_many[SNMPv1] PASS
snmp_test.py:79: test_get_many.test_get_many[SNMPv2] PASS

=========================== 8 passed in 1.31 seconds ===========================
&lt;/pre&gt;
&lt;p&gt;This wasn't very complicated, but having an example of using the &lt;code&gt;param&lt;/code&gt; argument to &lt;code&gt;metafunc.addcall()&lt;/code&gt; would have made figuring this out a little easier.  So I hope this helps someone else, or at least me at some time in the future.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; Originally I forgot the &lt;code&gt;extrakey&lt;/code&gt; argument to &lt;code&gt;cached_setup()&lt;/code&gt; and thus the funcarg was returning the same in both cases.  Somehow I assumed the caching was done on function identity of the setup function.  Oops.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1581827670981953566?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1581827670981953566/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/06/pytest-test-generators-and-cached-setup.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1581827670981953566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1581827670981953566'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/06/pytest-test-generators-and-cached-setup.html' title='py.test test generators and cached setup'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-4400532577868311714</id><published>2010-05-29T14:34:00.001+01:00</published><updated>2010-05-29T22:32:23.854+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Selectable queue</title><content type='html'>&lt;p&gt;Sometimes you'd want to use something like &lt;a href="http://docs.python.org/library/select.html#select.select"&gt;&lt;code&gt;select.select()&lt;/code&gt;&lt;/a&gt; on &lt;a href="http://docs.python.org/library/queue.html#queue-objects"&gt;Queues&lt;/a&gt;.  If you do some searching for this &lt;a href="http://stackoverflow.com/questions/1123855/select-on-multiple-python-multiprocessing-queues"&gt;it turns out&lt;/a&gt; that this question has been answered for &lt;a href="http://docs.python.org/library/multiprocessing.html#multiprocessing.Queue"&gt;multiprocessing Queues&lt;/a&gt; where you can simply use the real select on the underlying socket (IIRC), but for a good old fashioned &lt;code&gt;Queue&lt;/code&gt; you're stuck.&lt;/p&gt;
&lt;p&gt;Now it's easy to argue that this isn't that high a need, when I wanted this a while ago it turned out to be surprisingly simple to re-structure the design a little so that I no longer desired a selectable queue.  But it's still something that hung around the back of my mind for a while, so I've kept thinking about it.  My conclusion for now (which I haven't bothered implementing) is that simply cloning the normal queues but replacing the &lt;code&gt;not_empty&lt;/code&gt; and &lt;code&gt;not_full&lt;/code&gt; Conditions by Events gives you selectable queues.  It changes the semantics slightly, but that doesn't seem harmful.  This obviously isn't enough, so the second change to the queue is that you should be allowed to pass in the events to use.  And now that you can share the &lt;code&gt;not_full&lt;/code&gt; event between two queues you can simply wait on this event and you have your select.&lt;/p&gt;
&lt;p&gt;Next time I want this I might actually implement this idea rather then re-design so that I don't want selectable queues anymore.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-4400532577868311714?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/4400532577868311714/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/05/selectable-queue.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4400532577868311714'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4400532577868311714'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/05/selectable-queue.html' title='Selectable queue'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-7653906375860056382</id><published>2010-05-12T19:09:00.001+01:00</published><updated>2010-05-12T22:23:41.129+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>weakref and circular references: should I really care?</title><content type='html'>&lt;p&gt;While Python has a &lt;a href="http://docs.python.org/library/gc.html"&gt;garbage collector&lt;/a&gt; pretty much whenever circular references are touched upon it is advised to use weak references or otherwise break the cycle.  But should we really care?  I'd like not to, it seem like something the platfrom (python vm) should just provide for me.  Are all those mentions really just for the cases where you can't (or don't want to, e.g. embedded) use the normal garbage collector?&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; By some strange brain failure I seemed to have written "imports" rather then "references" in the title originally.  They are obviously a bad thing.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-7653906375860056382?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/7653906375860056382/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/05/weakref-and-circular-imports-should-i.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7653906375860056382'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7653906375860056382'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/05/weakref-and-circular-imports-should-i.html' title='weakref and circular references: should I really care?'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-3452450476329451219</id><published>2010-05-08T14:19:00.000+01:00</published><updated>2010-05-08T14:19:09.563+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>python-prctl</title><content type='html'>&lt;p&gt;There is sometimes a need to set the process name from within python, this would allow you to use something like "&lt;code&gt;pkill myapp&lt;/code&gt;" rather then the process name of your application just being yet another "python".  Bugs (which I'm now failing to find in Python's tracker) have been filed about this and many wannabe implementations made, all of them seemed to have many problems however.   Aiming for UNIX cross-compatibility they all messed up and end up trying to make the abstraction everywhere, and ususally the code was less then beautiful&lt;/p&gt;
&lt;p&gt;But I've just discovered the &lt;a href="http://github.com/seveas/python-prctl"&gt;python-prctl&lt;/a&gt; module by Dennis Kaarsemaker which does something far more sensible: rather then trying to be cross-platform it just wraps the Linux &lt;code&gt;prctl&lt;/code&gt; system call (as well as libcap).  The code looks well written and the API, while a little bit overloaded on the get-set names, seems nice.  It even includes what is probably the most sensible implementation of clobbering &lt;code&gt;argv&lt;/code&gt; that I've ever seen (but don't use that, no normal person should ever clobber argv!).&lt;/p&gt;
&lt;p&gt;If someone writes a nice module like this to cater for the MacOSX guys, the only other system I know of has a system call to set the process name, then I may never have to worry about getting someting like this into &lt;a href="http://bitbucket.org/chrismiles/psi"&gt;PSI&lt;/a&gt; at some distant point in the future.  (And speaking about PSI, yes the windows port is still slowly under way.  I"m just busy with lots of other things at the same time.)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-3452450476329451219?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/3452450476329451219/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/05/python-prctl.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3452450476329451219'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3452450476329451219'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/05/python-prctl.html' title='python-prctl'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-4462111738716822516</id><published>2010-05-08T02:44:00.000+01:00</published><updated>2010-05-08T02:44:14.170+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Storm and sqlite locking</title><content type='html'>&lt;p&gt;The &lt;a href="https://storm.canonical.com"&gt;Storm ORM&lt;/a&gt; struggles with &lt;a href="http://docs.python.org/library/sqlite3.html"&gt;sqlite3's&lt;/a&gt; transaction behaviour as they &lt;a href="http://bazaar.launchpad.net/~storm/storm/trunk/annotate/head:/storm/databases/sqlite.py#L195"&gt;explain&lt;/a&gt; in their source code.  Looking at the implementation of &lt;a href="http://bazaar.launchpad.net/~storm/storm/trunk/annotate/head:/storm/databases/sqlite.py#L133"&gt;&lt;code&gt;.raw_execute()&lt;/code&gt;&lt;/a&gt; the side effect of their solution to this is that they start an explicit transaction on every statement that gets executed.  &lt;em&gt;Including &lt;code&gt;SELECT&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This, in turn, sucks big time.  If you look at &lt;a href="http://www.sqlite.org/lockingv3.html"&gt;sqlite's locking behviour&lt;/a&gt; you will find that it should be possible to read from the database using concurrent connections (i.e. from multiple processes and/or threads at the same time).  However since storm explicitly starts that transaction for a select it means the connection doing the read now holds a &lt;code&gt;SHARED&lt;/code&gt; lock until you end the transaction (by doing a commit or rollback).  But since it's holding this shared lock, for no good reason, it means no other connection can acquire the &lt;code&gt;EXCLUSIVE&lt;/code&gt; lock at the same time.&lt;/p&gt;
&lt;p&gt;The upshot of this seems to be that you need to call &lt;code&gt;.commit()&lt;/code&gt; even after just reading the database, thus ensuring you let go of the shared lock.  Can't say I like that.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-4462111738716822516?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/4462111738716822516/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/05/storm-and-sqlite-locking.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4462111738716822516'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4462111738716822516'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/05/storm-and-sqlite-locking.html' title='Storm and sqlite locking'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8142345892667698015</id><published>2010-04-27T20:43:00.001+01:00</published><updated>2010-04-27T20:43:45.664+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Python optimisation has surprising side effects</title><content type='html'>&lt;p&gt;Here's something that surprised me:&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
a = None
def f():
    b = a
    return b
def g():
    b = a
    a = 'foo'
    return b
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;While &lt;code&gt;f()&lt;/code&gt; is perfectly fine, &lt;code&gt;g()&lt;/code&gt; raises an &lt;code&gt;UnboundLocalError&lt;/code&gt;.  This is because Python optimises access to local variables using the LOAD_FAST/STORE_FAST opcode, you can easily see why this is looking at the code objects of those functions:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
&gt;&gt;&gt; f.__code__ .co_names
()
&gt;&gt;&gt; f.__code__ .co_varnames 
('a', 'b')
&gt;&gt;&gt; g.__code__ .co_names
('a',)
&gt;&gt;&gt; g.__code__ .co_varnames 
('b',)
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;I actually found out this difference thanks to finally watching the &lt;a href="http://pycon.blip.tv/file/3259830/"&gt;Optimizations And Micro-Optimizations In CPython&lt;/a&gt; talk by Larry Hastings from PyCon 2010.  I never realised that you could create a situation where the nonlocal scope would not be looked in.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8142345892667698015?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8142345892667698015/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/04/python-optimisation-has-surprising-side.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8142345892667698015'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8142345892667698015'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/04/python-optimisation-has-surprising-side.html' title='Python optimisation has surprising side effects'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-4875243573709373863</id><published>2010-04-03T00:40:00.000+01:00</published><updated>2010-04-03T00:40:46.037+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Judging performance of python code</title><content type='html'>&lt;p&gt;Recently I"ve been messing around with python bytecode, as a result I now know approximately how the python virtual virtual machine works at a bytecode level.  I've found this quite interesting but other then satisfying my own curiosity had no benefit from this knowledge.  Until today I was wondering which code would be more efficient:&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
a += 1
if b is not None:
    a += 2
&lt;/pre&gt;&lt;/code&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
if b is None:
    a += 1
else:
    a += 3
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;From a style point of view I'd prefer to write the first: I find it slightly more readable since it's got fewer indented blocks and it uses one line less on my screen.  But my gut feeling tells me the second is faster, particularly if b is not None (this because in the first sample we'd do 2 add operations instead of one if b is not None).  But now I can verify my gut feeling!  All I need to do is compile both fragments and use the disassembler to investigate:&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyrpint"&gt;
&gt;&gt;&gt; co1 = compile(sample1, '&lt;stdin&gt;', 'exec')
&gt;&gt;&gt; dis.dis(co1)
  2           0 LOAD_NAME                0 (a) 
              3 LOAD_CONST               0 (1) 
              6 INPLACE_ADD          
              7 STORE_NAME               0 (a) 

  3          10 LOAD_NAME                1 (b) 
             13 LOAD_CONST               2 (None) 
             16 COMPARE_OP               9 (is not) 
             19 POP_JUMP_IF_FALSE       35 

  4          22 LOAD_NAME                0 (a) 
             25 LOAD_CONST               1 (2) 
             28 INPLACE_ADD          
             29 STORE_NAME               0 (a) 
             32 JUMP_FORWARD             0 (to 35) 
        &gt;&gt;   35 LOAD_CONST               2 (None) 
             38 RETURN_VALUE
&gt;&gt;&gt; co2 = compile(sample2, '&lt;stdin&gt;', 'exec')
&gt;&gt;&gt; dis.dis(co2)
  2           0 LOAD_NAME                0 (b) 
              3 LOAD_CONST               2 (None) 
              6 COMPARE_OP               8 (is) 
              9 POP_JUMP_IF_FALSE       25 

  3          12 LOAD_NAME                2 (a) 
             15 LOAD_CONST               0 (1) 
             18 INPLACE_ADD          
             19 STORE_NAME               2 (a) 
             22 JUMP_FORWARD            10 (to 35) 

  5     &gt;&gt;   25 LOAD_NAME                2 (a) 
             28 LOAD_CONST               1 (3) 
             31 INPLACE_ADD          
             32 STORE_NAME               2 (a) 
        &gt;&gt;   35 LOAD_CONST               2 (None) 
             38 RETURN_VALUE
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;My analysis of this is pretty simple: count the number of instructions for when &lt;code&gt;b is None&lt;/code&gt; and for when &lt;code&gt;b is not None&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;tr&gt;&lt;td/&gt;&lt;th&gt;b is None&lt;/th&gt;&lt;th&gt;b is not None&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;sample1&lt;/th&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;sample2&lt;/th&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;So ultimately the best performance depends on whether b will be None or not.  However the difference in the best case is only one instruction, but the difference in the worst case is a whole mighty 4 instructions!  This would seem to confirm my gut feeling: the ugly code is better.  It also makes me wonder if this is not the sort of optimisation a compiler should be doing: create the bytecode for sample2 regardless of the source code (I'm not a compiler guy and do realise it might not be as simple as that since python is a dynamic language which allows you to change stuff, including executed code, at runtime, yada yada).&lt;/p&gt;
&lt;p&gt;There's one more catch tough: I seriously doubt each python instruction can be executed in the same time!  So let's use &lt;a href="http://docs.python.org/py3k/library/timeit.html"&gt;timeit&lt;/a&gt; to actually verify this.  I'm omitting the trivial code, but this is the result:&lt;/p&gt;
&lt;pre&gt;
$ python3 test.py 
sample1, b is None: 0.128307104111
sample2, b is None: 0.128338098526
sample1, b is not None: 0.244062900543
sample2, b is not None: 0.12109208107
&lt;/pre&gt;
&lt;p&gt;To be honest, the result is exactly as speculated: the first sample is slower when b is not None, all others are pretty much the same.  The one odd thing is the pretty much doubling of time for the bad case, this suggest the python virtual machine is spending most of the time doing the INPLACE_ADD instruction while all others are probably very quick.&lt;/p&gt;
&lt;p&gt;Anyway, in conclusion I guess you can speculate about performance and get an idea by knowing what bytecode will be generated.  But at the end of the day you'll get a simple and better answer by simply using &lt;code&gt;timeit&lt;/code&gt;.  So knowing something about python bytecode still hasn't gained me any benefit.  It was still an interesting exercise tough.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-4875243573709373863?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/4875243573709373863/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/04/judging-performance-of-python-code.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4875243573709373863'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4875243573709373863'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/04/judging-performance-of-python-code.html' title='Judging performance of python code'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8054901863961041810</id><published>2010-04-01T21:45:00.000+01:00</published><updated>2010-04-01T21:45:55.860+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Hacking mock: Mock.assert_api(...)</title><content type='html'>&lt;p&gt;&lt;a href="http://www.voidspace.org.uk/python/mock/"&gt;Mock&lt;/a&gt; is a great module to use in testing, I use it pretty much all the time.  But one thing I have nerver felt great about is the syntax of it's &lt;code&gt;call_args&lt;/code&gt; (and &lt;code&gt;call_args_list&lt;/code&gt;): it is a 2-tuple of the positional arguments and the keyword arguments, e.g. &lt;code&gt;(('arg1', 'arg2'), {'kw1': None, 'kw2': None})&lt;/code&gt;.  This does show you exactly how the mock object was called, but the problem I have is that it's more restrictive then the signature in python:&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
def func(foo, bar, baz=None):
    pass

func(0, 1, 2)
func(0, 1, baz=2)
func(0, bar=1, baz=2)
func(foo=0, bar=1, baz=2)
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;In this example all the calls to &lt;code&gt;func()&lt;/code&gt; are exactly the same from python's point of view.  But they will all be different in mock's &lt;code&gt;call_args&lt;/code&gt; attribute.  To me this means my test will be too tightly coupled to the exact implementation of the code under test.  This made me wonder how it could be better.&lt;/p&gt;
&lt;p&gt;Firstly I started out writing a function which would take the &lt;code&gt;call_args&lt;/code&gt; tuples and know the function signature.  This obviously isn't very nice as you need a new function for each signature.  But this lead me on to adding the &lt;code&gt;.assert_api()&lt;/code&gt; method to the mock object itself.  I've been using this method for a while and am still not disappointed in it, so tought I should write about it.  Here's how to use it:&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
mobj = mock.Mock(api='foo,bar,baz')
do_stuff()
mobj.assert_api(foo=0, bar=1, baz=2)
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;It seems to me this is a fairly good compromise to an extra method on the mock object (and attribute, not shown) and nice concise way of asserting if a function was called correctly.&lt;/p&gt;
&lt;p&gt;There are a number of side effects, mainly due to my implementation.  The major one is that it &lt;em&gt;consumes &lt;code&gt;.call_args_list&lt;/code&gt;&lt;/em&gt;!  This means each time you call &lt;code&gt;.assert_api()&lt;/code&gt; the first item disappears from &lt;code&gt;.call_args_list&lt;/code&gt;.  Again, I like this as it allows me easily to check multiple calls by just doing multiple &lt;code&gt;.assert_api()&lt;/code&gt; calls.&lt;/p&gt;
&lt;p&gt;Another side effect, of less importance, is a new attribute "&lt;code&gt;api&lt;/code&gt;" on the mock object, I can imagine people disliking that one.  But it's never been in my way and means you can just assign to it instead of using the keyword argument when creating the mock.  I find this handy in combination with patch decorators where the syntax to fine-tune the mock is rather heavy to my liking.&lt;/p&gt;
&lt;p&gt;The last strange thing is &lt;code&gt;.assert_api_lazy()&lt;/code&gt;, which is just a horribly bad name.  It ignores any arguments which are present in the call on the mock object, but where not passed in as part of the &lt;code&gt;api='...'&lt;/code&gt; parameter.  It's effectively saying you only want to check a few of the arguments and don't care about the others.&lt;/p&gt;
&lt;p&gt;Finally here is the code, for simplicity here implemented (and unchecked) as a subclass of &lt;code&gt;Mock&lt;/code&gt;:&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
class MyMock(Mock):
    def __init__(api=None, **kwargs):
        Mock.__init__(self, **kwargs)
        self.api = api

    def _assert_api_base(self, **kwargs):
        """Return call_kwargs dict for assert_api and assert_api_lazy

        WARNING, this consumes self.call-args_list
        """
        if self.call_args_list:
            call_args, call_kwargs = self.call_args_list.pop(0)
        else:
            raise AssertionError('No call_args left over')
        call_args = list(call_args)
        if self.api is None:
            raise AssertionError('self.api is not initialised')
        for p in self.api.split(','):
            if call_args:
                call_kwargs[p] = call_args.pop(0)
        return call_kwargs

    def assert_api(self, **kwargs):
        """WARNING, this consumes self.call_args_list"""
        call_kwargs = self._assert_api_base(**kwargs)
        assert kwargs == call_kwargs, \
            'Expected: %s\nCalled with: %s' % (kwargs, call_kwargs)

    def assert_api_lazy(self, **kwargs):
        """WARNING, this consumes self.call_args_list"""
        call_kwargs = self._assert_api_base(**kwargs)
        for k in call_kwargs.copy().iterkeys():
            if k not in kwargs:
                del call_kwargs[k]
        assert kwargs == call_kwargs, \
            'Expected: %s\nCalled with (truncated): %s' % (kwargs, call_kwargs)
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;I'd be interested in feedback!  It's definitely not perfect, but I do like the syntax of &lt;code&gt;m=Mock(api='...'); ...; m.assert_api(...)&lt;/code&gt;.  One problem I can think of is that it won't deal gracefully with default arguments on the api yet, e..g.:&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
def func(foo, bar=42):
    pass

func(foo, 42)
func(foo)
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;These two calls are identical, but &lt;code&gt;.assert_api()&lt;/code&gt; won't see them as the same.  This hasn't bothered me yet, which is why I haven't looked into it.  But I guess it should be considered for a general-purpose implementation of this idea.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8054901863961041810?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8054901863961041810/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/04/hacking-mock-mockassertapi.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8054901863961041810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8054901863961041810'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/04/hacking-mock-mockassertapi.html' title='Hacking mock: Mock.assert_api(...)'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-9082960493656275570</id><published>2010-03-26T07:40:00.000Z</published><updated>2010-03-26T07:40:57.078Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Using lib2to3 in setup.py</title><content type='html'>&lt;p&gt;It seems that many people think you need &lt;a href="http://pypi.python.org/pypi/distribute"&gt;distribute&lt;/a&gt; if you want to run &lt;a href="http://docs.python.org/library/2to3.html"&gt;2to3&lt;/a&gt; automatically in your &lt;code&gt;setup.py&lt;/code&gt;.  But personally I don't like setuptools (aka distribute) and hence don't like forcing this on users.  No worries since plain old &lt;a href="http://docs.python.org/distutils/"&gt;distutils&lt;/a&gt; supports this as well, but it simply appears less well known.&lt;/p&gt;
&lt;p&gt;All you need to do is use the &lt;code&gt;build_py_2to3&lt;/code&gt; command supplied by &lt;code&gt;distutils.command.build_py&lt;/code&gt; in python3 instead of the normal &lt;code&gt;build_py&lt;/code&gt; command.  This is how you can do this:&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
try:
    from distutils.command.build_py import build_py_2to3
except ImportError:
    pass

COMMANDS = {}
if sys.version_info[0] == 3:
    COMMANDS['build_py'] = build_py_2to3
setup(..., cmdclass=COMMANDS, ...)
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;That's it!  Ok, you have to do slighty more then just add a keyword argument to the &lt;code&gt;setup()&lt;/code&gt; call.  But just the 2to3 feature is not worth using distribute for.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-9082960493656275570?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/9082960493656275570/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/03/using-lib2to3-in-setuppy.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/9082960493656275570'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/9082960493656275570'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/03/using-lib2to3-in-setuppy.html' title='Using lib2to3 in setup.py'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-2763722260565338314</id><published>2010-02-10T20:26:00.001Z</published><updated>2010-02-10T20:27:08.743Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Discovering basestring</title><content type='html'>&lt;p&gt;This may seem simple and trivial, but today I discovered the &lt;code&gt;basestring&lt;/code&gt; type in Python.  It is essentially a base type for &lt;code&gt;str&lt;/code&gt; and &lt;code&gt;unicode&lt;/code&gt;, which comes in handy when writing &lt;code&gt;isinstance(foo, basestring)&lt;/code&gt; in test code for example.&lt;/p&gt;
&lt;p&gt;Strangely, despide &lt;a href="http://www.python.org/dev/peps/pep-0237/"&gt;PEP 237&lt;/a&gt; mentioning the equivalent for &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;long&lt;/code&gt; as "&lt;code&gt;integer&lt;/code&gt;" I can't actually find what has become of that, so still writing &lt;code&gt;isinstance(foo, (int, long))&lt;/code&gt; for now.&lt;/p&gt;
&lt;p&gt;Note that this is Python 2.x only, in 3.x it's all unicode so there is no longer a need for a common base type.  &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;long&lt;/code&gt; are unified as well in Python 3.x obviously.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-2763722260565338314?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/2763722260565338314/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/02/discovering-basestring.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2763722260565338314'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2763722260565338314'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/02/discovering-basestring.html' title='Discovering basestring'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1335138696316390872</id><published>2010-02-05T16:20:00.000Z</published><updated>2010-02-05T16:20:25.829Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Registering callbacks: to quack like a duck or tell which bit will quack the duck way?</title><content type='html'>&lt;p&gt;When you have someplace where you register a set of callbacks you have a few options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use an interface-style API.  I.e. you have a &lt;code&gt;Mixin&lt;/code&gt; style class defining the methods expected to be present, probably just raising &lt;code&gt;NotImplementedError&lt;/code&gt;.  You then just pass in the entire object to the &lt;code&gt;.register()&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;Just try if it quacks like a duck.  IMHO this is just the pythonic equivalent of the above, simply without the explicit mixin class.&lt;/li&gt;
&lt;li&gt;Explicitly pass in the callback functions.  This would be something like: &lt;code&gt;.register(func1, ...)&lt;/code&gt;.  Secret benefit here is using the result of &lt;code&gt;functools.partial()&lt;/code&gt; and things like it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;What I don't like about the first two options is that you force the method name of the callback onto the user.  The third option allows you to achieve the same but at the cost of a more complicated register function.  But I tend to prefer it because it's more explicit and it avoids "hiding" things in the object (via the mixin) thus keeping the interfacace between caller and callee cleaner.&lt;/p&gt;
&lt;p&gt;Are there other design considerations when making this choice?  Maybe there's already an essay somewhere exploring these ideas in more detail then I'm doing here?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1335138696316390872?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1335138696316390872/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/02/registering-callbacks-to-quack-like.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1335138696316390872'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1335138696316390872'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/02/registering-callbacks-to-quack-like.html' title='Registering callbacks: to quack like a duck or tell which bit will quack the duck way?'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8473064427458723349</id><published>2010-01-25T10:53:00.000Z</published><updated>2010-01-25T10:53:33.471Z</updated><title type='text'>Pointer arithmetic in C</title><content type='html'>&lt;p&gt;Pointer arithmetic in C is great.  The only downside is that it's only defined when pointing to an array (or an item just after the array).  That is one giant downside.&lt;/p&gt;
&lt;/p&gt;Allocate a chunk of memory and you can't use pointer arithmetic.  So if you are building a linked list this means you will have to allocate each item in the list separately, even if you know the total length the list will ever have.&lt;/p&gt;
&lt;p&gt;Even K&amp;R must have realised this was an annoying limitation.  They point this out when they show how to write malloc (K&amp;R2 8.7 Example -- A Storage Allocator)&lt;/p&gt;
&lt;blockquote&gt;
There is still one assumption, however, that pointers to different blocks returned by &lt;code&gt;sbrk&lt;/code&gt; can be meaningfully compared.  This is not guaranteed by the standard, which permits pointer comparisons only within an array.  Thus this version of &lt;code&gt;malloc&lt;/code&gt; is portable only among machines for which general pointer comparison is meaningful.
&lt;/blockquote&gt;
&lt;p&gt;If only the standard would have relaxed this restriction slightly (e.g. to continuously allocated regions) life would be so much better&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8473064427458723349?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8473064427458723349/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/01/pointer-arithmetic-in-c.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8473064427458723349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8473064427458723349'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/01/pointer-arithmetic-in-c.html' title='Pointer arithmetic in C'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-9156426487845342445</id><published>2010-01-18T21:19:00.000Z</published><updated>2010-01-18T21:19:17.074Z</updated><title type='text'>The master plan</title><content type='html'>&lt;p&gt;Catherine Devlin &lt;a href="http://catherinedevlin.blogspot.com/2010/01/fear-is-not-virtue.html"&gt;describes it very nicely&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
I finally understand Al-Qaeda's master plan, and it's freaking &lt;em&gt;brilliant&lt;/em&gt;. [...] I'm just surprised that we're choosing to participate in the plan. I thought we were on opposite sides?
&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-9156426487845342445?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/9156426487845342445/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/01/master-plan.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/9156426487845342445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/9156426487845342445'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/01/master-plan.html' title='The master plan'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-3855688937737814871</id><published>2010-01-10T18:02:00.003Z</published><updated>2010-01-19T14:10:11.356Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Terminology</title><content type='html'>&lt;p&gt;Why was it ever considered desirable to call a directory containing a __init__.py file a "package" rather then just "module".  They are after all simply "modules containing other modules".  It's not like that solved some sort of problem was it?  But, as sad as that sometimes might be, we can't change the past.&lt;/p&gt;
&lt;p&gt;Anyway, people are continiously confused when talking about python and all the module distributions made available on &lt;a href="http://pypi.python.org/pypi"&gt;&lt;strike&gt;the cheeseshop&lt;/strike&gt;PyPI&lt;/a&gt; by various projects.  And now yet &lt;a href="http://mail.python.org/pipermail/distutils-sig/2010-January/015232.html"&gt;another discussion rages&lt;/a&gt; over at distutils-sig where they want to re-work this confusion by using unambigous terms.  &lt;b&gt;The only possible outcome I can think of is more confusion.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;What is so hard about accepting the current status quo?  The thing that would help most is documenting something like the following in &lt;a href="http://docs.python.org/distutils/introduction.html#distutils-specific-terminology"&gt;distutils documentation&lt;/a&gt;:
&lt;blockquote&gt;
The word "package" is often used to refer to a &lt;em&gt;module distribution&lt;/em&gt;, usually it is perfectly clear form the context whether it is talking about a module distribution or a module containing other modules.
&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-3855688937737814871?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/3855688937737814871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/01/terminology.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3855688937737814871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3855688937737814871'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/01/terminology.html' title='Terminology'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-4968489321347551576</id><published>2010-01-09T16:00:00.001Z</published><updated>2010-01-19T14:41:56.731Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='debian'/><title type='text'>Installing Debian on a Sun Fire X2200 M2</title><content type='html'>&lt;p&gt;Most important thing first: &lt;em&gt;the eLOM console lives on ttyS1 (9600n8)&lt;/em&gt;.  If that gets you going you do not need to read any more.&lt;/p&gt;
&lt;p&gt;This works pretty easily, but there's isn't much information about how this works if you don't like using a keyboard and a screen (having these things appears to be the norm for x86 hardware).  But the X2200 comes with this "Embedded Lights Out Manager" which, although not as good as the LOM on a T1000, does the job nicely.  It allows you to connect via the serial console, IPMI and ssh.  So initially I used the serial console to setup the IP configuration of the eLOM and then I could ssh to it (and more importantly sit in a quiter room far away from the rack).&lt;/p&gt;
&lt;p&gt;Once inside the eLOM it allows you to connect to the "serial port" of the machine (with the rather obsusucre "&lt;code&gt;start /SP/AgentInfo/console&lt;/code&gt;" command, disconnect by hitting Esc + Shift-9).  This allows you to see the BIOS settings etc.  Now is the time to insert the Debian CD (or netboot or whatever), if inserting the CD you'll just get a blank screen since it's displaying pretty graphics, hitting escape gives you the boot prompt as explained in the Debian installation manual.  Now the serial console you're seeing will appear as the second serial port to the OS (this is configurable in the BIOS I think but I didn't play with those settings).  So the correct way to start the installation is:&lt;/p&gt;
&lt;code&gt;
expert fb=false console=ttyS1,9600n8
&lt;/code&gt;
&lt;p&gt;(or "install" instead of "expert" if you prefer that)&lt;/p&gt;
&lt;p&gt;This will start the normal install process you're used too (and which is described in other guides) with the output appearing on your terminal emulator.  One tip tough is to enable the installation via ssh when it allows you to select the installer components to load.  The serial console is 9600 baud and painting the dialog windows over that is just painfully slow, this is a lot nicer over ssh.&lt;/p&gt;
&lt;p&gt;The last thing to note is that debian-installer is smart enough to enable a getty on the serial console you've used for the installation (if fact it doesn't enable any gettys on the normal ttys).  But it doesn't append the &lt;code&gt;console=ttyS1,9600n8&lt;/code&gt; to the grub menu, so you'll want to do this yourself if you like seeing the kernel output on the console.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-4968489321347551576?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/4968489321347551576/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2010/01/installing-debian-on-sun-fire-x2200-m2.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4968489321347551576'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4968489321347551576'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2010/01/installing-debian-on-sun-fire-x2200-m2.html' title='Installing Debian on a Sun Fire X2200 M2'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-7972808143528439294</id><published>2009-12-30T13:22:00.003Z</published><updated>2010-03-25T17:49:37.044Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Decorators specific to a class</title><content type='html'>&lt;p&gt;My gut tells me it's horribly wrong but I am failing to formulate a decent argument, let me show an example what I mean (somewhat contrived):&lt;/p&gt;
&lt;code&gt;&lt;pre class="prettyprint"&gt;
def deco(f):
    def wrapper(self, *args, **kw):
        with self.lock:
            f(self, *args, **kw)

class Foo:
    def __init__(self):
        self.lock = threading.Lock()

    @deco
    def method(self):
        pass
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Here the decorator knows something about the arguments of the function it will end up calling, the first argument is "self" and it is an object with a "lock" attribute which is a context manager.  Somehow I feel like that's more knowledge about the wrapped object then a decorator should have.  It just is an indirection of logic my bain doesn't cope with.&lt;/p&gt;
&lt;p&gt;There are obviously places where I could construct something like this.  But I do never naturally think of doing it that way, I always end up with some other way which I find more elegant and I think the resulting logic is easier to follow.  It's just that whenever I encounter code like this my brain starts hurting and I'm not sure I have a decent argument to tell people writing code like this off (you can hardly regard "it's a level of logic indirection that makes my brain hurt" as an argument).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-7972808143528439294?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/7972808143528439294/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/12/decorators-specific-to-class.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7972808143528439294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7972808143528439294'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/12/decorators-specific-to-class.html' title='Decorators specific to a class'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-7123846626359700104</id><published>2009-12-26T12:05:00.000Z</published><updated>2009-12-26T12:05:59.955Z</updated><title type='text'>Calling COM methods in C</title><content type='html'>&lt;p&gt;So Windows has this strange thing called &lt;a href="http://en.wikipedia.org/wiki/Component_Object_Model"&gt;COM&lt;/a&gt;.  It allows you to share objects between unrelated processes and languages, a bit like &lt;a href="http://en.wikipedia.org/wiki/CORBA"&gt;CORBA&lt;/a&gt; in that sense, only very different.  Anyway, if you'd like to get information out of this other Windows thing called &lt;a href="http://en.wikipedia.org/wiki/Windows_Management_Instrumentation"&gt;WMI&lt;/a&gt; (which provides a lot of information you can't get hold of using native APIs, at least not if you don't digg into unpublished internals) then you've got to use COM.&lt;/p&gt;
&lt;p&gt;But Microsoft seems to have decided some long time ago that C++ is such an amazing language (because if it's got ++ in the name it must be better then say just C, you know) that it solves all of the world's problems.  So obviously that is the language you favour when designing APIs, meaning you end up with very convoluted APIs when using good old plain C.  And obviously there is no need to document the way you do things in C because no one would use it.  Anyway, my conclusion is that COM is crazy and I can only assume that .NET must somehow make this a bit easier (just like using WMI from &lt;a href="http://python.org"&gt;Python&lt;/a&gt; is easy with &lt;a href="http://starship.python.net/crew/mhammond/"&gt;Mark Hammond's pythoncom&lt;/a&gt; and &lt;a href="http://timgolden.me.uk/python/wmi/index.html"&gt;Tim Golden's wmi&lt;/a&gt; module) which is probably the reason that C# is popular among developers who choose Windows as their platform.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Back to the point however&lt;/b&gt;: how do you call a method on a COM object using C?  Microsoft will always show &lt;tt&gt;instance-&gt;Method()&lt;/tt&gt; in their examples, but in C it's not that much different:&lt;/p&gt;
&lt;code&gt;instance-&gt;lpVtbl-&gt;Method(instance)&lt;/code&gt;
&lt;p&gt;There is also supposed to be a way to use some macro's using the class name of the object joined up with the method name.  For this you need to define the &lt;tt&gt;COBJMACROS&lt;/tt&gt; before your &lt;tt&gt;#include&lt;/tt&gt;s and then you can call:&lt;/p&gt;
&lt;code&gt;ClassName_Method(instance)&lt;/code&gt;
&lt;p&gt;Only I can't get that way to work.  No clue why.&lt;/p&gt;
&lt;p&gt;For completeness here is the full example of the &lt;a href="http://msdn.microsoft.com/en-us/library/aa390423%28VS.85%29.aspx"&gt;WMI local computer example in the MSDN library&lt;/a&gt;, ported to C.  For extra fun the error handling is done by setting Python exceptions, but that shouldn't confuse things.  It also prints the result string in both unicode and ascii, from the unicode string you could have easily made a PyUnicodeObject.  There's probably many ugly things in there as I'm really not a win32 developer.  It's also example code and not what I'd take into production (e.g. I think the looping over the results is broken but I'm sticking close the the MSDN example).&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
static int
example(void)
{
    HRESULT hr;
    IWbemLocator *pLoc;
    IWbemServices *pSvc;
    IEnumWbemClassObject *pEnumerator;
    BSTR bstr, bstr2;

    hr = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
    if (hr == RPC_E_CHANGED_MODE)
        hr = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (hr != S_OK &amp;&amp; hr != S_FALSE) {
        PyErr_Format(PyExc_WindowsError,
                     "Failed to initialise COM (HRESULT=0x%x)", hr);
        return -1;
    }
    hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
                              RPC_C_AUTHN_LEVEL_DEFAULT,
                              RPC_C_IMP_LEVEL_IMPERSONATE,
                              NULL, EOAC_NONE, NULL);
    if (FAILED(hr)) {
        PyErr_Format(PyExc_WindowsError,
                     "Failed to initialise COM security (HRESULT=0x%x)", hr);
        CoUninitialize();
        return -1;
    }
    hr = CoCreateInstance(&amp;CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
                          &amp;IID_IWbemLocator, (LPVOID *) &amp;pLoc);
 
    if (FAILED(hr)) {
        PyErr_Format(PyExc_WindowsError,
                     "Failed to get IWBemLocator (HRESULT=0x%x)", hr);
        CoUninitialize();
        return -1;
    }
    bstr = SysAllocString(L"ROOT\\CIMV2");
    if (bstr == NULL) {
        PyErr_SetString(PyExc_WindowsError, "BSTR allocation failed");
        pLoc-&gt;lpVtbl-&gt;Release(pLoc);
        CoUninitialize();
    }
    hr = pLoc-&gt;lpVtbl-&gt;ConnectServer(pLoc, bstr, NULL, NULL,
                                     NULL, 0, NULL, NULL, &amp;pSvc);
    SysFreeString(bstr);
    if (FAILED(hr)) {
        PyErr_Format(PyExc_WindowsError,
                     "Localhost connection for WMI failed (HRESULT=0x%x)", hr);
        pLoc-&gt;lpVtbl-&gt;Release(pLoc);
        CoUninitialize();
        return -1;
    }
    hr = CoSetProxyBlanket((IUnknown*)pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
                           NULL, RPC_C_AUTHN_LEVEL_CALL,
                           RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
    if (FAILED(hr)) {
        PyErr_Format(PyExc_WindowsError,
                     "Failed to set ProxyBlanket (HRESULT=0x%x)", hr);
        pSvc-&gt;lpVtbl-&gt;Release(pSvc);
        pLoc-&gt;lpVtbl-&gt;Release(pLoc);
        CoUninitialize();
        return -1;
    }
    bstr = SysAllocString(L"WQL");
    if (bstr == NULL) {
        PyErr_SetString(PyExc_WindowsError, "BSTR allocation failed");
        pSvc-&gt;lpVtbl-&gt;Release(pSvc);
        pLoc-&gt;lpVtbl-&gt;Release(pLoc);
        CoUninitialize();
        return -1;
    }
    bstr2 = SysAllocString(L"SELECT * FROM Win32_OperatingSystem");
    if (bstr2 == NULL) {
        PyErr_SetString(PyExc_WindowsError, "BSTR allocation failed");
        SysFreeString(bstr);
        pSvc-&gt;lpVtbl-&gt;Release(pSvc);
        pLoc-&gt;lpVtbl-&gt;Release(pLoc);
        CoUninitialize();
        return -1;
    }
    hr = pSvc-&gt;lpVtbl-&gt;ExecQuery(pSvc, bstr, bstr2,
                                 WBEM_FLAG_FORWARD_ONLY |
                                 WBEM_FLAG_RETURN_IMMEDIATELY, 
                                 NULL, &amp;pEnumerator);
    SysFreeString(bstr);
    SysFreeString(bstr2);
    if (FAILED(hr)) {
        PyErr_Format(PyExc_WindowsError, "WMI query failed (HRESULT=0x%x)", hr);
        pSvc-&gt;lpVtbl-&gt;Release(pSvc);
        pLoc-&gt;lpVtbl-&gt;Release(pLoc);
        CoUninitialize();
        return -1;
    }
    {
        IWbemClassObject *pclsObj;
        ULONG uReturn;
        VARIANT vtProp;       
            
        while (pEnumerator) {
            hr = pEnumerator-&gt;lpVtbl-&gt;Next(pEnumerator, WBEM_INFINITE,
                                           1, &amp;pclsObj, &amp;uReturn);
            if(uReturn == 0)
                break;
            hr = pclsObj-&gt;lpVtbl-&gt;Get(pclsObj, L"Name", 0, &amp;vtProp, 0, 0);
            wprintf(L"XXX OS Name: %s\n", vtProp.bstrVal);
            {
                /* XXX Need error checking in here */
                /* Allocating the UTF-16 string size since that will be at
                 * least double the ASCII size, which is fine. */
                char *prop;
                int r;

                prop = psi_malloc(SysStringByteLen(vtProp.bstrVal));
                /* if (prop == NULL) */
                r = WideCharToMultiByte(20127, 0, vtProp.bstrVal, -1, prop,
                                        SysStringByteLen(vtProp.bstrVal),
                                        NULL, NULL);
                /* if (!r) */
                printf("XXX OS Name: %s\n", prop);
            }
            VariantClear(&amp;vtProp);
            pclsObj-&gt;lpVtbl-&gt;Release(pclsObj);
        }
    }
    pEnumerator-&gt;lpVtbl-&gt;Release(pEnumerator);
    pSvc-&gt;lpVtbl-&gt;Release(pSvc);
    pLoc-&gt;lpVtbl-&gt;Release(pLoc);
    CoUninitialize();
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-7123846626359700104?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/7123846626359700104/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/12/calling-com-methods-in-c.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7123846626359700104'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7123846626359700104'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/12/calling-com-methods-in-c.html' title='Calling COM methods in C'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-3200088800931842584</id><published>2009-12-23T16:59:00.000Z</published><updated>2010-01-19T14:43:25.806Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Setting descriptors on modules</title><content type='html'>&lt;p&gt;This counts for one of the more crazy things I'd like to do with Python: insert an instance of a &lt;a href="http://docs.python.org/reference/datamodel.html#implementing-descriptors"&gt;descriptor object&lt;/a&gt; into the class dict of a module.&lt;/p&gt;
&lt;p&gt;While you can get hold of the module type class by using the &lt;tt&gt;__class__&lt;/tt&gt; attribute of any module or use &lt;tt&gt;types.ModuleType&lt;/tt&gt; you obviously can't do this since the &lt;tt&gt;__dict__&lt;/tt&gt; of the module class is actually a &lt;tt&gt;DictProxy&lt;/tt&gt; and hence immutable.  Which I think is rather sad this time round.&lt;/p&gt;
&lt;p&gt;My use case is to be able to set an attribute on a module that would lazily evaluate a sort of semi-singleton.  Suppose you have the instance of your application and to make it available to other modules you place the instance in a module attribute.  You want it to be available since it has references to useful global things like the configuration instance in use etc (and you hate having singletons for all these useful things).  To now get this application instance from another module, without getting it passed in with some sort of dependency-injection (which can result in intangible spaghetti all too easily), you can now get hold of it like this:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
import app_package.app_module
app_package.app_module.app_instance # the instance
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;But what you very likely can't do is this:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
from app_package.app_module import app_instance
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;The reason is that import statements are usually at the top of modules and thus you will very likely execute this import statement before the &lt;tt&gt;app_instance&lt;/tt&gt; existed and you will get &lt;tt&gt;None&lt;/tt&gt; (I will anyway, since I initialised that attribute with None before the app gets instanced).  And obviously even once the application is instanced and the attribute gets set, I'm still stuck with the reference to &lt;tt&gt;None&lt;/tt&gt; instead of the application instance.&lt;p&gt;
&lt;p&gt;So my reason to want to set a descriptor instance in the module class is so that the descriptor could lazily evaluate the application instance when accessing it.  Getting and using the instance would reduce to something like:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
from app_package.app_module import app_instance
app_instance # the instance!
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Which I think is much cleaner.  But for now I'll have to stick with:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
from app_package.app_module import get_instance
get_instance() # the instance
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Or perhaps Python has another trick up it's sleeve I haven't found yet?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-3200088800931842584?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/3200088800931842584/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/12/setting-descriptors-on-modules.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3200088800931842584'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3200088800931842584'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/12/setting-descriptors-on-modules.html' title='Setting descriptors on modules'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8721890879118903566</id><published>2009-12-19T20:33:00.000Z</published><updated>2010-01-19T14:43:25.806Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Skipping slow test by default in py.test</title><content type='html'>&lt;p&gt;If you got a test suite that runs some very slow tests it might be troublesome to run those all the time.  There is of course the "-k" option to &lt;a href="http://codespeak.net/py/dist/test/index.html"&gt;py.test&lt;/a&gt; which allows you to selectively enable only a few tests.  But what I really wanted was a way to have it skip slow tests by default but still allow me to enable slow tests with a command line option.&lt;p&gt;
&lt;p&gt;But this is not impossible, py.test provides a couple of things that make it possible to do this surprisingly easy:&lt;p&gt;
&lt;ul&gt;
&lt;li&gt;You can &lt;a href="http://codespeak.net/py/dist/test/customize.html#writing-per-project-plugins-conftest-py"&gt;add command line options&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;You can &lt;a href="http://codespeak.net/py/dist/test/plugin/mark.html"&gt;mark tests&lt;/a&gt; as with whatever name you like&lt;/li&gt;
&lt;li&gt;You can &lt;a href="http://codespeak.net/py/dist/test/plugin/skipping.html"&gt;skip tests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So how do you pull this together?  I decided that i want to mark slow tests using the "slow" keyword (@py.test.mark.slow) and skip those tests by default.  And the option to enable those tests would be called "--slow".  The following &lt;a href="http://codespeak.net/py/dist/test/customize.html#conftest-py-project-specific-hooks-and-configuration"&gt;conftest.py&lt;/a&gt; is staggeringly simple:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
import py.test

def pytest_addoption(parser):
    parser.addoption('--slow', action='store_true', default=False,
                      help='Also run slow tests')

def pytest_runtest_setup(item):
    """Skip tests if they are marked as slow and --slow is not given"""
    if getattr(item.obj, 'slow', None) and not item.config.getvalue('slow'):
        py.test.skip('slow tests not requested')
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Finding this solution was a little harder however, but essentially it involved nothing more then looking at the &lt;tt&gt;pytest_skipping&lt;/tt&gt; and &lt;tt&gt;pytest_mark&lt;/tt&gt; plugins to figure out what APIs the various objects provide.  But it must be said that extending py.test is staggeringly simple, I tend to expect a much higher learning curve when I decide that I want to write a plugin for some tool I use.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8721890879118903566?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8721890879118903566/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/12/skipping-slow-test-by-default-in-pytest.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8721890879118903566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8721890879118903566'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/12/skipping-slow-test-by-default-in-pytest.html' title='Skipping slow test by default in py.test'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-5308628314981305128</id><published>2009-12-18T01:51:00.000Z</published><updated>2010-01-19T14:43:25.806Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>setuptools vs distribute</title><content type='html'>&lt;p&gt;&lt;a href="http://reinout.vanrees.org/weblog/2009/12/17/managing-dependencies.html"&gt;Reinout van Rees&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
In case you heard of both setuptools and distribute: distribute fully replaces setuptools. Just use distribute. Setuptools is “maintained” (for various historically dubious values of “maintain”) by one person (whom all should applaud for creating the darn thing in the first place, btw!). Distribute is maintained by a lot of people, so bugs actually get fixed. And “bugs” meaning “it doesn’t break with subversion 1.6 because patches that fix it don’t get applied for half a year”.
&lt;/blockquote&gt;
&lt;p&gt;Pretty much one of the best descriptions of the differences between &lt;a href="http://pypi.python.org/pypi/setuptools"&gt;setuptools&lt;/a&gt; and &lt;a href="http://pypi.python.org/pypi/distribute"&gt;distribute&lt;/a&gt; that I've seen.  Not that I use either, but whatever.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-5308628314981305128?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/5308628314981305128/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/12/setuptools-vs-distribute.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5308628314981305128'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5308628314981305128'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/12/setuptools-vs-distribute.html' title='setuptools vs distribute'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-6791424566587204384</id><published>2009-12-07T18:29:00.001Z</published><updated>2009-12-07T18:31:34.875Z</updated><title type='text'>Dictionary entry</title><content type='html'>&lt;p&gt;&lt;b&gt;innovative company&lt;/b&gt; &lt;i&gt;noun&lt;/i&gt; &lt;b&gt;1&lt;/b&gt; a company which can't afford patent lawyers &lt;b&gt;2&lt;/b&gt; one of the lucky handful companies who are innovative and have money for patent lawyers &lt;b&gt;HELP&lt;/b&gt; Not to be confused with most companies lobying in patent litigation.&lt;/p&gt;
&lt;p&gt;PS: It's also possible to have companies that can't afford patent lawyers but still don't innovate.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-6791424566587204384?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/6791424566587204384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/12/dictionary-entry.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6791424566587204384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6791424566587204384'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/12/dictionary-entry.html' title='Dictionary entry'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-5997826472574058238</id><published>2009-12-06T21:47:00.000Z</published><updated>2009-12-06T21:47:21.409Z</updated><title type='text'>How to drive on a motorway</title><content type='html'>&lt;p&gt;If you are driving on a motorway where I am driving too, here are
some rules you should follow.  I think you should even follow them
when I'm not around.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Be sensible, this one overrules all the others&lt;/li&gt;
  &lt;li&gt;Keep left unless overtaking&lt;/li&gt;
  &lt;li&gt;KEEP LEFT UNLESS OVERTAKING&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;KEEP LEFT UNLESS OVERTAKING&lt;/b&gt;&lt;/li&gt;
  &lt;li&gt;Always indicate when changing lanes&lt;/li&gt;
  &lt;li&gt;Pick a speed and stick to it, avoid speeding up or slowing down
  whenever possible&lt;/li&gt;
  &lt;li&gt;Be courteous, even when people make mistakes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's not that hard, try it and you'll realise traffic would be a
lot more fluent if everyone stuck to these rules.&lt;/p&gt;

&lt;p&gt;PS: Don't forget to substitute &lt;em&gt;left&lt;/em&gt; with &lt;em&gt;right&lt;/em&gt; depending on your country.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-5997826472574058238?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/5997826472574058238/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/12/how-to-drive-on-motorway.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5997826472574058238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5997826472574058238'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/12/how-to-drive-on-motorway.html' title='How to drive on a motorway'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-6281621455755488372</id><published>2009-12-03T19:33:00.000Z</published><updated>2009-12-03T19:33:34.294Z</updated><title type='text'>How to make a crosslinked RS-232 cable</title><content type='html'>&lt;h2&gt;Because it's easier then buying one&lt;/h2&gt;

&lt;p&gt;When connecting two computes together (e.g. your laptop to a server or router) to get access to a console you need to use a &lt;a href="http://en.wikipedia.org/wiki/Null-modem_cable"&gt;crosslinked RS-232&lt;/a&gt; cable, usually with two female &lt;a href="http://en.wikipedia.org/wiki/D-subminiature"&gt;DE-9&lt;/a&gt; connectors these days.  This cable is more commonly known as a null-modem cable for historical reasons.&lt;/p&gt;

&lt;p&gt;Of course you can easily buy a cable under the name of "null modem cable", but you're almost guaranteed to get a sloppy made one.  In fact I haven't been able to find a decently made one.  The problem is that according to the standard the &lt;a href="http://en.wikipedia.org/wiki/Data_Terminal_Ready"&gt;DTR&lt;/a&gt; is supposed to be connected to the &lt;a href="http://en.wikipedia.org/wiki/Data_Set_Ready"&gt;DSR&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Data_Carrier_Detect"&gt;CD&lt;/a&gt;, but most cables as sold won't connect the carrier detect line.  No idea why, perhaps making the bridge is something other connectors don't need and therefore expensive to automate for production?&lt;/p&gt;

&lt;p&gt;So to make one yourself you can cut up an existing cable and solder new DE-9 connectors onto it yourself, keeping to &lt;a href="http://www.hardwarebook.info/Nullmodem_%289-9%29"&gt;the proper schema&lt;/a&gt;.  It's really not that hard, all you need is a soldering iron and two &lt;a href="http://maplin.co.uk/Module.aspx?ModuleNo=1113"&gt;D-sub 9 pin connectors&lt;/a&gt; and corresponding hoods.  I'm guessing you could even use cheap CAT-5e networking cable instead of cutting up an expensive pre-made null-modem cable since Sun uses network cables for their serial cables (whith an adaptor).&lt;/p&gt;

&lt;p&gt;Now most devices actually seem to ignore the carrier detect line anyway.  But in case you are lucky enough to be using IBM's &lt;a href="http://www-03.ibm.com/systems/power/software/aix/about.html"&gt;AIX&lt;/a&gt; or &lt;a href="http://www-03.ibm.com/systems/p/hardware/whitepapers/ivm.html"&gt;IVM&lt;/a&gt; on one of their &lt;a hef="http://www-03.ibm.com/systems/p/"&gt;System p5&lt;/a&gt; servers you will discover that you are able to log into the service processor with a "normal" (i.e. non-standard) serial cable just fine.  You will also be able to perform an installation from that searial cable just fine.  But when you then need to access the console of that just installed system you're stuck and just see nothing.  Until you use a proper cable that is.  (Just for the record, you can most likely telnet or ssh into the system by now, just try it).&lt;/p&gt;

&lt;p&gt;And if my love for AIX is not yet clear from the above paragraph let me be a bit more explicit: if you are in a position to choose get something else then AIX.  Sun's hardware as well as Solaris are lovely (they ship with a serial cable for instance - as does cisco).  Obviously GNU/Linux is a great choice for an OS too.  This doesn't mean that I approve of cable vendors not making RS-232 crosslink cables propperly of course.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-6281621455755488372?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/6281621455755488372/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/12/how-to-make-crosslinked-rs-232-cable.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6281621455755488372'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6281621455755488372'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/12/how-to-make-crosslinked-rs-232-cable.html' title='How to make a crosslinked RS-232 cable'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8271557268945676161</id><published>2009-12-01T18:51:00.000Z</published><updated>2010-01-19T14:43:25.807Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>On using .__getstate__()</title><content type='html'>&lt;p&gt;Do not, I repeat, &lt;em&gt;do not&lt;/em&gt; define &lt;tt&gt;.__getstate__()&lt;/tt&gt; idly.&lt;/p&gt;
&lt;p&gt;Thank you very much&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8271557268945676161?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8271557268945676161/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/12/on-using-getstate.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8271557268945676161'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8271557268945676161'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/12/on-using-getstate.html' title='On using .__getstate__()'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-2285130797644103175</id><published>2009-11-29T14:35:00.000Z</published><updated>2010-01-19T14:43:25.807Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Tuple unpacking goodness</title><content type='html'>&lt;p&gt;Todays pleasant surprise:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
&gt;&gt;&gt; a = {'a': (0, 1), 'b': (2, 3)}
&gt;&gt;&gt; for k, (v1, v2) in a.iteritems():
...     print k, v1, v2
... 
a 0 1
b 2 3
&gt;&gt;&gt;
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Nice!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-2285130797644103175?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/2285130797644103175/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/11/tuple-unpacking-goodness.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2285130797644103175'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2285130797644103175'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/11/tuple-unpacking-goodness.html' title='Tuple unpacking goodness'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-7501272925649291980</id><published>2009-11-28T13:57:00.000Z</published><updated>2009-11-28T13:57:48.966Z</updated><title type='text'>The new faces  of Europe</title><content type='html'>&lt;p&gt;I can't help &lt;a href="http://np237.livejournal.com/27242.html"&gt;but agree&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-7501272925649291980?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/7501272925649291980/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/11/new-faces-of-europe.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7501272925649291980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7501272925649291980'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/11/new-faces-of-europe.html' title='The new faces  of Europe'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-5266830531697272694</id><published>2009-11-25T00:07:00.000Z</published><updated>2010-01-19T14:43:25.807Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Finding memory leaks in extension modules</title><content type='html'>&lt;p&gt;After reading &lt;a href="http://nedbatchelder.com/blog/200911/memory_leak_mystery.html"&gt;Ned Batchelder's post on his experience hunting a memory leak&lt;/a&gt; (which turned out to be a reference counting error) it occurred to me that even tough I have a script to check memory usage I should also really be checking reference counts with &lt;tt&gt;sys.gettotalrefcount()&lt;/tt&gt;.  And indeed, after adding this to my script I found &lt;a href="http://bitbucket.org/chrismiles/psi/changeset/e49d29dacc55/"&gt;one reference count leak&lt;/a&gt;.  I still have faith in my script as it was before really since the reference leak in question was not making me loose memory - subtle bugs eh?&lt;/p&gt;
&lt;p&gt;But how do you check an extension module for memory leaks?  This seems pretty undocumented so here my approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;First you really need a debug build of python, this helps a lot since you get to use &lt;tt&gt;sys.gettotalrefcount()&lt;/tt&gt; and get more predictable memory behaviour.  The most complete way to build this is something like this (the MAXFREELIST stuff &lt;a href="http://code.google.com/p/apsw/source/browse/tools/valgrind.sh"&gt;adapted from this&lt;/a&gt;):&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
s="_MAXFREELIST=0"
./configure --with-pydebug --without-pymalloc --prefix=/opt/pydebug \
CPPFLAGS="-DPyDict$s -DPyTuple$s -DPyUnicode$s -DPySet$s -DPyCFunction$s -DPyList$s -DPyFrame$s -DPyMethod$s"
make
make install
&lt;/pre&gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now run the test suite using &lt;a href="http://valgrind.org/"&gt;valgrind&lt;/a&gt;, this is troublesome but a very useful thing to do.  The valgrind memory checker will help you identify problems pretty quickly.  It can be confused about Python however, but you only care about your extension module so you need to filter most of this.  Luckily the python distribution ships with a valgrind suppression file in &lt;tt&gt;Misc/valgrind-python.supp&lt;/tt&gt; that you can use, it's not perfect but helps.  This is how I invoke valgrind:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
$ /opt/pydebug/bin/python setup.py build
$ valgrind --tool=memcheck \
    --suppression=~/python-trunk/Misc/valgrind-python.supp \
    --leak-check=full /opt/pydebug/bin/python -E -tt setup.py test
==8599== Memcheck, a memory error detector
==8599== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==8599== Using Valgrind-3.5.0-Debian and LibVEX; rerun with -h for copyright info
==8599== Command: /opt/pydebug/bin/python -E -tt setup.py test
==8599== 
==8599== Conditional jump or move depends on uninitialised value(s)
==8599==    at 0x400A66E: _dl_relocate_object (do-rel.h:65)
==8599==    by 0x4012492: dl_open_worker (dl-open.c:402)
==8599==    by 0x400E155: _dl_catch_error (dl-error.c:178)
==8599==    by 0x4011D0D: _dl_open (dl-open.c:616)
==8599==    by 0x405AC0E: dlopen_doit (dlopen.c:67)
==8599==    by 0x400E155: _dl_catch_error (dl-error.c:178)
==8599==    by 0x405B0DB: _dlerror_run (dlerror.c:164)
==8599==    by 0x405AB40: dlopen@@GLIBC_2.1 (dlopen.c:88)
==8599==    by 0x8132727: _PyImport_GetDynLoadFunc (dynload_shlib.c:130)
==8599==    by 0x81199D9: _PyImport_LoadDynamicModule (importdl.c:42)
==8599==    by 0x81161FE: load_module (import.c:1828)
==8599==    by 0x8117FAF: import_submodule (import.c:2589)
...
running test
...
FAILED (failures=4, errors=2)
==8599== 
==8599== HEAP SUMMARY:
==8599==     in use at exit: 1,228,588 bytes in 13,293 blocks
==8599==   total heap usage: 280,726 allocs, 267,433 frees, 70,473,201 bytes allocated
==8599== 
==8599== LEAK SUMMARY:
==8599==    definitely lost: 0 bytes in 0 blocks
==8599==    indirectly lost: 0 bytes in 0 blocks
==8599==      possibly lost: 1,201,420 bytes in 13,014 blocks
==8599==    still reachable: 27,168 bytes in 279 blocks
==8599==         suppressed: 0 bytes in 0 blocks
==8599== Rerun with --leak-check=full to see details of leaked memory
==8599== 
==8599== For counts of detected and suppressed errors, rerun with: -v
==8599== Use --track-origins=yes to see where uninitialised values come from
==8599== ERROR SUMMARY: 75 errors from 5 contexts (suppressed: 19 from 6)
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Note that the output is very verbose, usually I actually start with &lt;tt&gt;--leak-check=summary&lt;/tt&gt;.  Firstly notice that valgrind gives a lot of warnings already before your extension module gets loaded, that's python's problems and not yours so skip over that.  The stuff output after (and during) the output of the test suite is what interests you.  Most importantly look at the &lt;tt&gt;definitely lost&lt;/tt&gt; line if that's not zero the you have a leak.  The &lt;tt&gt;possibly lost&lt;/tt&gt; is just python's problem (which sadly might hide problems you created too).  When you do have lost blocks valgrind will give you a stack trace to pinpoint it, but you'll have to swim trough lots of "possibly lost" stack traces of python to find it.  Best is probably to grep for your source files in the output.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next you should create function you want to execute in a loop, this should be exercising the code you want to tests for leaks.  If you're really thourough possibly the entire test suite wrapped up in a function call would be good.&lt;/p&gt;
&lt;p&gt;Wrap it all up in a script that checks the memory usage and reference counts on each loop and compares the start and end values.  Getting memory usage might be tricky from python (or you can use &lt;a href="http://bitbucket.org/chirsmiles/psi"&gt;PSI&lt;/a&gt; of course) so depending on your situation you might prefer to do this with an script from your operating system.&lt;/p&gt;
&lt;p&gt;For &lt;a href="http://bitbucket.org/chirsmiles/psi"&gt;PSI&lt;/a&gt; &lt;a href="http://bitbucket.org/chrismiles/psi/src/tip/misc/mem_test.py"&gt;this is the script&lt;/a&gt; I currently use.  I clearly have it easy since I can be sure PSI will be available :-).  The reason I don't automate this script further (you could turn it into a unittest) is that I prefer to manually look at the output.  Both memory and reference counting are funny and will most likely grow a little bit anyway.  By looking at the output I can easily spot if it keeps growing or stabilises, there is only a problem if it keeps growing with every iteration (don't be afraid to run with many many iterations from time to time).  When automating this you probably end up allowing some margin and might miss small leaks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hopefully some of this was useful for someone.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-5266830531697272694?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/5266830531697272694/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/11/finding-memory-leaks-in-extension.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5266830531697272694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5266830531697272694'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/11/finding-memory-leaks-in-extension.html' title='Finding memory leaks in extension modules'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-2238116773993494288</id><published>2009-11-21T15:59:00.000Z</published><updated>2010-01-19T14:43:25.807Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>New Python System Information release!</title><content type='html'>&lt;p&gt;I've just released &lt;a href="http://pypi.python.org/pypi/PSI/0.3b2"&gt;a new version of PSI&lt;/a&gt;!  &lt;a href="http://bitbucket.org/chrismiles/psi/wiki/Home"&gt;PSI&lt;/a&gt; is a cross-platform &lt;a href="http://python.org"&gt;Python&lt;/a&gt; package providing real-time access to processes and other miscellaneous system information such as architecture, boottime and filesystems.  Among the highlights of this release are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Improved handling of time:&lt;/em&gt; We now have our &lt;a href="http://bitbucket.org/chrismiles/psi/wiki/TimeAPI"&gt;own object to represent time&lt;/a&gt;.  This may seem silly at first but it actually makes it easier to use &lt;em&gt;all&lt;/em&gt; the normal ways of representing time easily as well as provide the highest possible accuracy.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Improved handling of process names and arguments:&lt;/em&gt; &lt;a href="http://bitbucket.org/chrismiles/psi/wiki/ProcArgsExe"&gt;There is an entire wiki page dedicated to this&lt;/a&gt;, but basically this simplifies presenting sensible process names to users massively since some attributes will always have meaningful values.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Restructured exceptions:&lt;/em&gt; Whenever accessing attributes you will get a subclass of &lt;tt&gt;AttributeError&lt;/tt&gt; like it should be so now you can happily use &lt;tt&gt;getattr()&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;New experimental methods on Process objects:&lt;/em&gt;  You can now send signals to processes using the &lt;tt&gt;.kill()&lt;/tt&gt; method and find their children by using the &lt;tt&gt;.children()&lt;/tt&gt; method.
&lt;li&gt;&lt;em&gt;New experimental mount module:&lt;/em&gt; You can get detailed information about all mounted filesystems using this new module.  It provides mount information as well as usage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another notable improvement is the ability to read the arguments of a 64-bit process while running inside a 32-bit python process on Solaris.  It's small and almost no-one will notice it but make it so much more consistent!&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Release early, release often: fail&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Now for the bad news: all this means the API has changed in a backwards incompatible way.&lt;/p&gt;
&lt;p&gt;It was already pretty obvious shortly after the last release that this would happen and was the reason I was hoping to release a new version soon.  But that didn't happen.  Although the last version had a "beta" version number on it it's trove classifier still claimed to be "alpha" and in end we don't promise API stability till we hit 1.0.  But it's still not nice.  Once we hit 0.3 we will actually try not to introduce changes to the API if possible.  We intend to help this by using &lt;tt&gt;FutureWarning&lt;/tt&gt; for APIs we're not sure about yet.  In the mean time let's see how the &lt;tt&gt;Process&lt;/tt&gt; API holds out during this release, hopefully it will prove to be good and require no more changes.&lt;/p&gt;

&lt;b&gt;Credits&lt;/b&gt;
&lt;p&gt;As before, thanks to &lt;a href="http://chrismiles.info/"&gt;Chris Miles&lt;/a&gt; and Erick Tryzelaar for helping out.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-2238116773993494288?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/2238116773993494288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/11/new-python-system-information-release.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2238116773993494288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2238116773993494288'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/11/new-python-system-information-release.html' title='New Python System Information release!'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-572116048905556250</id><published>2009-11-16T13:39:00.000Z</published><updated>2010-01-19T14:43:25.808Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Synchronous classes in Python</title><content type='html'>&lt;p&gt;What I'd like to build is an object that when doing anything with it would first acquire a lock and release it when finished.  It's a pattern I use fairly-regularly and I am getting bored of always manually defining a lock next to the other object and manually acquiring and releasing it.  It's also error prone.&lt;/p&gt;
&lt;p&gt;Problem is, I can't find how to do it!  The &lt;a href="http://docs.python.org/reference/datamodel.html#object.__getattribute__"&gt;__getattribute__&lt;/a&gt; method is bypassed by implicit special methods (like &lt;tt&gt;len()&lt;/tt&gt; invoking &lt;tt&gt;.__len__()&lt;/tt&gt;).  That sucks.  And from the &lt;a href="http://docs.python.org/reference/datamodel.html#new-style-special-lookup"&gt;description of this by-passing&lt;/a&gt; there seems to be no way to get round it.  For this one time where I thought I found a use for meta-classes they still don't do the trick...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-572116048905556250?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/572116048905556250/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/11/synchronous-classes-in-python.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/572116048905556250'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/572116048905556250'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/11/synchronous-classes-in-python.html' title='Synchronous classes in Python'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-431641100310337878</id><published>2009-11-03T22:59:00.001Z</published><updated>2010-01-19T14:43:25.808Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Python modules and the GPL: I still don't get it</title><content type='html'>&lt;p&gt;I've never understood if you can use GPL python modules (or packages) in GPL-incompatibly licensed code.  Today I re-read the GPL looking for this and am tempted to think &lt;em&gt;yes, you can&lt;/em&gt;.  The GPL says:&lt;/p&gt;
&lt;blockquote&gt;
1. You may copy and distribute verbatim copies of the Program's source code as you receive it [...]
&lt;/blockquote&gt;
&lt;p&gt;This is simple enough, what if you need to change it?  This gets more interesting:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, [...] provided that you also meet all of these conditions:&lt;/p&gt;
&lt;p&gt;[...]&lt;/p&gt;
&lt;p&gt;b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.&lt;/p&gt;
&lt;p&gt;[...]&lt;/p&gt;
&lt;p&gt;These requirements apply to the modified work as a whole.  If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works.  But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As I uderstand this it means you can happily ship a GPL python module next to a GPL-incompatible module, even when the second uses API calls from the first.  As long you are always offering to give the source of the GPL module, including modifications if you made any, you are fine.  The two modules are &lt;em&gt;individually identifiable sections&lt;/em&gt; and &lt;em&gt;can be reasonably considered independent and separate works&lt;/em&gt;, or so I reckon at least.&lt;/p&gt;
&lt;p&gt;But wait, the non-GPL module uses the API of the GPL module, is it still independent and separable then?  In my humble opinion the GPL says it must be &lt;em&gt;&lt;b&gt;reasonably&lt;/b&gt; considered independtent and separate&lt;/em&gt;, so yes.  It's feasable to take away the GPL module and replace it by another one that offers the same API, hence I would argue they are &lt;em&gt;separable&lt;/em&gt; (feasable and easy are not synonyms!).&lt;/p&gt;
&lt;p&gt;According to my reasoning it should also be legal to use GPL C libraries in non-GPL programs as long as you use them as shared libraries and not as a static library.  But why does the Lesser General Public License (LGPL) exist then?  Qoting the the LGPL preamble (emphasis mine):&lt;/p&gt;
&lt;blockquote&gt;
&lt;em&gt;When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a &lt;b&gt;derivative&lt;/b&gt; of the original library.&lt;/em&gt;  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.
&lt;/blockquote&gt;
&lt;p&gt;Now a shared C library is linked by the dynamic linker before the application gets started.  If the library is not present the application will fail horribly, I guess this is why they consider it &lt;em&gt;legally speaking a combined work&lt;/em&gt;.  When using a python module the python interpreter will happily start and depending on how the application is written can fail gracefully or even provide partial functionality.&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://www.gnu.org/licenses/gpl-faq.html#GPLAndPlugins"&gt;GPL FAQ argues&lt;/a&gt; that it depends on the technical means used to run the final application whether the modules should both be GPL or not.  The arguments seem to suggest that if code runs in shared address space as GPL-licensed code, then the whole must comply to the GPL.  Given that &lt;em&gt;translation&lt;/em&gt; of a work is considered a derrivative we can assume python translates the python code (interprets it) and then executes the results in the same address space, therefore all must comply to the GPL (including python itself).&lt;/p&gt;
&lt;p&gt;I'm still hard pushed to call the non-GPL module a derrivative work of the GPL module however.  And in a &lt;a href="http://www.law.washington.edu/lct/swp/Law/derivative.html"&gt;lenghty article&lt;/a&gt; (apparenlty written by real lawyers, definitely worth a read!) the authors argue that legally it depends on a lot more then the technicality to determine if a work becomes a derrivative work or not (and that's where it all revolves around: if its a derrivative &lt;em&gt;and&lt;/em&gt; you distribute it the GPL applies).  This does indeed seem a lot more logical: imagine you accept the interpretation in the GPL FAQ, all you need to do to distribute the two modules is use &lt;a href="http://pyro.sourceforge.net/"&gt;pyro&lt;/a&gt; or some other form of inter-process communication (excluding shared memory according to the GPL FAQ, this again I find hard to accept) and use the APIs of the modules over this IPC layer.&lt;/p&gt;
&lt;p&gt;The above article describes how some courts have judged this derrivation question and gives a couple of pointers itself.  Essentially it common sense, but that's hard to qantify as even the GPL FAQ fails.  I should really summarise the factors in a paragraph here, but I was formally trained to be an engineer and not a lawyer so am not used to summarising lawyer articles (and frankly I'm too lazy to perform the exercise right now).  Beside I'd probably skip a lot of subtle points so you're best off reading &lt;a href="http://www.law.washington.edu/lct/swp/Law/derivative.html"&gt;the article&lt;/a&gt; yourself.&lt;/p&gt;
&lt;p&gt;I do have a &lt;b&gt;conclusion&lt;/b&gt; however: if you have a python module which is not GPL-compatible but uses the API of another python module covered by the GPL chances are you are fine if: (i) you are not taking away market share of the GPL module.  And (ii) you are not derriving or extending the &lt;em&gt;creative work&lt;/em&gt; or &lt;em&gt;copyrightable content&lt;/em&gt; of the module.  But there's no distinct line, common sense is your friend (and enemy).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-431641100310337878?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/431641100310337878/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/11/python-modules-and-gpl-i-still-dont-get.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/431641100310337878'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/431641100310337878'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/11/python-modules-and-gpl-i-still-dont-get.html' title='Python modules and the GPL: I still don&apos;t get it'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-7994861545205035273</id><published>2009-10-24T20:59:00.001+01:00</published><updated>2010-05-08T02:19:38.592+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Delny 0.4.1</title><content type='html'>A little while ago I released a &lt;a href="http://bruynooghe.blogspot.com/2009/09/new-delny-release.html"&gt;new version of Delny&lt;/a&gt; (my python wrapper around &lt;a href="http://qhull.org/"&gt;Qhull&lt;/a&gt; for Delaunay triangulations), the main purpose to use &lt;a href="http://numpy.scipy.org/"&gt;numpy&lt;/a&gt; instead of numeric.&amp;nbsp; Impressively enough people actually seemed to care and I got a few bug reports and hints for improvements.&lt;br /&gt;
&lt;br /&gt;
So I just released 0.4.1 with some of these updated:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;I forgot to change some python code to numpy, so was still importing numeric&lt;/li&gt;
&lt;li&gt;Use numpy.get_include() to find the numpy header files&lt;/li&gt;
&lt;/ul&gt;
At the same time I fixed the 2D square issue by using the &lt;i&gt;Qz&lt;/i&gt; option and removing the extra point from all output.&amp;nbsp; Finally fixing a unittest that has been failing for ages.&lt;br /&gt;
&lt;br /&gt;
To compensate however I added a new test that fails to triangulate a 3x3 2D square (well, it works but doesn't create simplical facets), thanks to Stephen McQuay's bug report.&amp;nbsp; AFAIK there's not bug for this in Delny's code, Qhull just seems to return the wrong result (but it does work on the command line).&lt;br /&gt;
&lt;br /&gt;
Now if someone would donate me windows binaries that would save me answering those questions too...&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-7994861545205035273?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/7994861545205035273/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/10/delny-041.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7994861545205035273'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7994861545205035273'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/10/delny-041.html' title='Delny 0.4.1'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-2651806250477561149</id><published>2009-10-22T23:47:00.000+01:00</published><updated>2009-10-22T23:47:19.265+01:00</updated><title type='text'>No permission to see Ubuntu bugs?</title><content type='html'>&lt;p&gt;So I'm looking at the &lt;a href="http://www.ubuntu.com/getubuntu/releasenotes/910"&gt;release notes for Ubuntu 9.10&lt;/a&gt; and am interested in the hibernation issue.  Naturally I follow the link to the &lt;a href="https://bugs.launchpad.net/bugs/354126"&gt;bug report&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First I need to log in to launchpad.  That's weird, since when do I need to log in simply to view bugs?  But after logging in I see this:&lt;/p&gt;
&lt;blockquote&gt;&lt;pre&gt;
Not allowed here
Sorry, you don't have permission to access this page.
You are logged in as Floris Bruynooghe.
&lt;/pre&gt;&lt;/blockquote&gt;

&lt;p&gt;Since when is Ubuntu hiding bugs?  Sure security bugs might need to be hidden for a while but you could be more descriptive about that.  And surely you shouldn't be linking to such bugs?&lt;/p&gt;

&lt;p&gt;Just weird.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-2651806250477561149?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/2651806250477561149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/10/no-permission-to-see-ubuntu-bugs.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2651806250477561149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2651806250477561149'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/10/no-permission-to-see-ubuntu-bugs.html' title='No permission to see Ubuntu bugs?'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-4804047006474813305</id><published>2009-09-24T21:18:00.000+01:00</published><updated>2009-09-24T21:18:21.563+01:00</updated><title type='text'>Cross platform shell scripts</title><content type='html'>&lt;p&gt;At work we have an rather elaborate collection of shell scripts that builds our softweare and all it's dependencies on about 7 UNIX variants as well as on Windows.  Shell seemed like a good choice when we started to write those scripts: it is available on all hosts (using &lt;a href="http://www.cygwin.com/"&gt;Cygwin&lt;/a&gt; on Windows) and with some care you can do most task in a portable way, ssh and scp are at your fingertips to do things on remote hosts without any extra hassle etc.&lt;/p&gt;
&lt;p&gt;The thing I didn't realise is just how extrordinary expensive forking is on Windows.  And when having lots of shell that's what you're doing all the time (cut, tr, grep, awk, ...), on UNIX you generally don't even notice it but on Windows it just grinds to a halt.&lt;/p&gt;
&lt;p&gt;This basically means I'm slowly moving to replacing the scripts in Python.  Each time I need to modify something I consider the cost of re-writing it in Python and of keeping it in shell.  Some critical parts are already replaced by Python code and the speedup is impressive.&lt;/p&gt;
&lt;p&gt;So next time you need cross-platform scripts, think about how much work it will need to do on Windows.  Forking is expensive.  Sadly.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-4804047006474813305?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/4804047006474813305/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/09/cross-platform-shell-scripts.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4804047006474813305'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4804047006474813305'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/09/cross-platform-shell-scripts.html' title='Cross platform shell scripts'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-5446853790381362783</id><published>2009-09-23T08:07:00.002+01:00</published><updated>2009-09-23T08:13:55.379+01:00</updated><title type='text'>Battery life</title><content type='html'>&lt;p&gt;Normally I'm quite happy with the 3h of battery life of my laptop, it covers all my disconnected time on trains etc.  But it just doesn't cut it on a 10h flight, especially painful if my brain has loads of ideas to try out and things to do.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-5446853790381362783?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/5446853790381362783/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/09/battery-life.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5446853790381362783'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5446853790381362783'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/09/battery-life.html' title='Battery life'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-2605627704553553064</id><published>2009-09-20T17:50:00.004+01:00</published><updated>2010-01-19T14:43:25.808Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>New Delny release</title><content type='html'>&lt;p&gt;A few days ago I got another of those two-a-year inquiries about &lt;a href="http://flub.stuffwillmade.org/delny"&gt;Delny&lt;/a&gt;, the python wrapper around &lt;a href="http://qhull.org"&gt;Qhull&lt;/a&gt; for Delaunay triangulations.  But increadably indirectly it finally got me to do a new release, it's only been a few years since the last one!  Nothing has changed really, the only difference it that it finally uses &lt;a href="http://numpy.scipy.org/"&gt;numpy&lt;/a&gt; to compile instead of the deprecated numeric&lt;/p&gt;
&lt;p&gt;I've also taken the opportunity to move the revision control to &lt;a href="http://mercurial.selenic.com/wiki/"&gt;mercurial&lt;/a&gt; and &lt;a href="http://bitbucket.org/flub/delny"&gt;host it on bitbucket&lt;/a&gt;.  And uploaded the tarball to &lt;a href="http://pypi.python.org/pypi/Delny"&gt;PyPI&lt;/a&gt;, not sure why I didn't do that before.&lt;/p&gt;
&lt;p&gt;Hope this keeps being of use to some people.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-2605627704553553064?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/2605627704553553064/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/09/new-delny-release.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2605627704553553064'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2605627704553553064'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/09/new-delny-release.html' title='New Delny release'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1999155703676999057</id><published>2009-08-20T12:03:00.003+01:00</published><updated>2009-08-20T12:13:02.481+01:00</updated><title type='text'>Resuming an scp file transfer</title><content type='html'>&lt;p&gt;Sometimes you have to transfer data over dodgy links.  And when you're transferring a large file this can hurt when it fails.  So as &lt;a href="http://joen.dk/wordpress/?p=34"&gt;explained&lt;/a&gt; &lt;a href="http://yyab.wordpress.com/2006/12/18/resume-a-large-scp-transfer/"&gt;elsewhere&lt;/a&gt; rsync can safe the day:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
$ scp host:remote_file local_file
# now interrupt and resume with
$ rsync --partial --progress --rsh=ssh host:remote_file local_file
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;You need rsync on the remote server for this however.  But usually that's not too much of a hurdle.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1999155703676999057?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1999155703676999057/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/08/resuming-scp-file-transfer.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1999155703676999057'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1999155703676999057'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/08/resuming-scp-file-transfer.html' title='Resuming an scp file transfer'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1771821288734721586</id><published>2009-08-13T11:46:00.003+01:00</published><updated>2010-01-19T14:43:25.809Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Should bare except statements be allowed in the Python stdlib?</title><content type='html'>&lt;p&gt;Firstly to clarify the terminology, this is bare except statement:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
try:
    ...
except:
    ...
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;And this is a non-bare except statement, but bear in mind the type of the exception that is caught can be anything:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
try:
    ...
except Exception:
    ...
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;The point is that both fragments are a catch-all exception handler, only the second is slightly more better/restrictive since it won't catch a &lt;code&gt;SystemExit&lt;/code&gt; exception for example (which you rarely want to catch).  This is obviously discussed before and even made it to a &lt;a href="http://www.python.org/dev/peps/pep-0348/"&gt;(rejected) PEP&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So I'm tempted to say that the stdlib should not use bare except statements.  If you need to catch more then &lt;code&gt;Exception&lt;/code&gt; they can always catch &lt;code&gt;BaseException&lt;/code&gt;.  However grepping the stdlib reveals 384 cases of bare except statements, to be fair many of these are in test cases but still.&lt;/p&gt;
&lt;p&gt;The one that hurt me today was in &lt;code&gt;socketserver.BaseServer.handle_request&lt;/code&gt;, now I have to re-write the &lt;code&gt;.handle_error()&lt;/code&gt; function to call &lt;code&gt;sys.exc_info()&lt;/code&gt; and check that it's a subclass of &lt;code&gt;Exception&lt;/code&gt; before handling the error normally.  That's not nice.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1771821288734721586?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1771821288734721586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/08/should-bare-except-statements-be.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1771821288734721586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1771821288734721586'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/08/should-bare-except-statements-be.html' title='Should bare except statements be allowed in the Python stdlib?'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8997839548130760724</id><published>2009-08-04T00:27:00.003+01:00</published><updated>2010-01-19T14:45:52.393Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Pain</title><content type='html'>&lt;p&gt;I've spent the last 2 days trying to get a stack trace from a crashing python extension module in windows.  And I still haven't figured it out.  That's sooo very motivating.&lt;/p&gt;
&lt;p&gt;Give me GNU/Linux any day.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8997839548130760724?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8997839548130760724/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/08/pain.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8997839548130760724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8997839548130760724'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/08/pain.html' title='Pain'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8812293613827050703</id><published>2009-08-03T22:07:00.004+01:00</published><updated>2009-08-03T22:41:55.792+01:00</updated><title type='text'>cifs why don't you follow unix conventions?</title><content type='html'>&lt;p&gt;Often it's nice to just have conventions and gentleman's agreements.  But then sometimes someone doesn't and the harm is done, it's too hard to reverse.&lt;/p&gt;
&lt;p&gt;NFS has introduced the de-facto standard of using "&lt;code&gt;&amp;lt;host&amp;gt;:&amp;lt;path&amp;gt;&lt;/code&gt;" for the defice of a remote mount.  Yes this break because you can have ":" in a filename, but in practice it works.&lt;/p&gt;
&lt;p&gt;&amp;lt;rant&amp;gt;&lt;/p&gt;
&lt;p&gt;But somehow cifs (formerly smbfs), decided that adhering to the windows syntax was more important then adhering to the unix standard, never mind that it's a filesystem implementation for unix-like systems.  So the remote mountpoint for a cifs filesystem is "&lt;code&gt;//&amp;lt;host&amp;gt;/&amp;lt;path&amp;gt;&lt;/code&gt;".  Yes, this breaks too, you could start any path with "//".  But it's just plain annoying when trying to figure out if a filesystem is remote or not.&lt;/p&gt;
&lt;p&gt;&amp;lt;/rant&amp;gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8812293613827050703?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8812293613827050703/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/08/cifs-why-dont-you-follow-unix.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8812293613827050703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8812293613827050703'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/08/cifs-why-dont-you-follow-unix.html' title='cifs why don&apos;t you follow unix conventions?'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-129227183615523839</id><published>2009-07-30T15:49:00.003+01:00</published><updated>2009-07-30T16:19:59.772+01:00</updated><title type='text'>Letters on keyboard stop working</title><content type='html'>&lt;p&gt;Dear Lazy Web&lt;/p&gt;
&lt;p&gt;I have some Very weird behaviour where the lowercase letters "C" and "V" stop generating the Correct key press event after a short while (which is why I am typing them as upper Case here...).  But only when using Compiz as window manager, when switching away from X to the Console they work again but in X they don't.  When looking at the keypress in &lt;CODE&gt;xeV&lt;/CODE&gt; it shows up like this:&lt;/p&gt;
&lt;CODE&gt;&lt;PRE&gt;
FocusOut event, serial 34, synthetic NO, window 0x5000001,
    mode NotifyGrab, detail NotifyAncestor

FocusIn event, serial 34, synthetic NO, window 0x5000001,
    mode NotifyUngrab, detail NotifyAncestor

KeymapNotify event, serial 31, synthetic NO, window 0x0,
    keys:  82  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   
           0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
&lt;/PRE&gt;&lt;/CODE&gt;
&lt;p&gt;For Comparison with a normal keypress:&lt;/p&gt;
&lt;CODE&gt;&lt;PRE&gt;
KeyPress event, serial 31, synthetic NO, window 0x5000001,
    root 0xa9, subw 0x0, time 18685100, (888,414), root:(898,513),
    state 0x10, keycode 53 (keysym 0x78, x), same_screen YES,
    XLookupString gives 1 bytes: (78) "x"
    XmbLookupString gives 1 bytes: (78) "x"
    XFilterEvent returns: False

KeyRelease event, serial 34, synthetic NO, window 0x5000001,
    root 0xa9, subw 0x0, time 18685275, (888,414), root:(898,513),
    state 0x10, keycode 53 (keysym 0x78, x), same_screen YES,
    XLookupString gives 1 bytes: (78) "x"
    XFilterEvent returns: False
&lt;/PRE&gt;&lt;/CODE&gt;
&lt;p&gt;I am totally at a loss what the problem Could be.  And searching for this is really hard and so far entire unsuccessful.  The most suspicious thing I Can find in Xorg.log is:&lt;/p&gt;
&lt;CODE&gt;&lt;PRE&gt;
(WW) Logitech USB Multimedia Keyboard: unable to handle keycode 267
&lt;/PRE&gt;&lt;/CODE&gt;
&lt;p&gt;But I have no idea if this is related.&lt;/p&gt;
&lt;p&gt;Any hints?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-129227183615523839?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/129227183615523839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/07/letters-on-keyboard-stop-working.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/129227183615523839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/129227183615523839'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/07/letters-on-keyboard-stop-working.html' title='Letters on keyboard stop working'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-5948175746226939873</id><published>2009-07-28T12:56:00.003+01:00</published><updated>2010-01-19T14:45:52.394Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>PyCon UK 2009</title><content type='html'>&lt;p&gt;So there's a &lt;a href="http://www.pyconuk.org/"&gt;PyCon UK 2009 Unconference&lt;/a&gt;.  I wish I could go but have already planned to be on &lt;a href="http://www.isleofbarra.com/for-visitors/barra-head-islands/pabbay.html"&gt;Pabbay&lt;/a&gt; and &lt;a href="http://www.isleofbarra.com/for-visitors/barra-head-islands/mingulay.html"&gt;Mingulay&lt;/a&gt; in the &lt;a href="http://en.wikipedia.org/wiki/Outer_Hebrides"&gt;Outer Hebrides, Schotland&lt;/a&gt;.  So instead of hearing all about python I'll be clinging on to their &lt;a href="http://www.flickr.com/photos/dave_aberdeen_rock/341060265/in/set-72157594452065957/"&gt;amazing&lt;/a&gt; &lt;a href="http://picasaweb.google.com/floris.bruynooghe/Pabbay#5241200352403133938"&gt;cliffs&lt;/a&gt;.  Guess I'm not that bad off anyway...&lt;/p&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_IqA2awU0luQ/Sm7uErGRrTI/AAAAAAAABgI/9Z5VrRFwt44/s1600-h/img_1546.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_IqA2awU0luQ/Sm7uErGRrTI/AAAAAAAABgI/9Z5VrRFwt44/s320/img_1546.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5363485970300775730" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-5948175746226939873?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/5948175746226939873/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/07/pycon-uk-2009.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5948175746226939873'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5948175746226939873'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/07/pycon-uk-2009.html' title='PyCon UK 2009'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_IqA2awU0luQ/Sm7uErGRrTI/AAAAAAAABgI/9Z5VrRFwt44/s72-c/img_1546.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-2293120094122784547</id><published>2009-07-25T14:06:00.002+01:00</published><updated>2010-01-19T14:45:52.394Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>How to bring a running python program under debugger control</title><content type='html'>&lt;p&gt;Of course &lt;a href="http://docs.python.org/library/pdb.html#module-pdb"&gt;pdb&lt;/a&gt; has already got functions to start a debugger in the middle of your program, most notably &lt;code&gt;pdb.set_trace()&lt;/code&gt;.  This however requires you to know where you want to start debugging, it also means you can't leave it in for production code.&lt;/p&gt;
&lt;p&gt;But I've always been envious of what I can do with &lt;a href="http://www.gnu.org/software/gdb"&gt;GDB&lt;/a&gt;: just interrupt a running program and start to poke around with a debugger.  This can be handy in some situations, e.g. you're stuck in a loop and want to investigate.  And today it suddenly occurred to me: just register a signal handler that sets the trace function!  Here the proof of concept code:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
import os
import signal
import sys
import time


def handle_pdb(sig, frame):
    import pdb
    pdb.Pdb().set_trace(frame)


def loop():
    while True:
        x = 'foo'
        time.sleep(0.2)


if __name__ == '__main__':
    signal.signal(signal.SIGUSR1, handle_pdb)
    print(os.getpid())
    loop()
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Now I can send &lt;code&gt;SIGUSR1&lt;/code&gt; to the running application and get a debugger.  Lovely!&lt;/p&gt;
&lt;p&gt;I imagine you could spice this up by using &lt;a href="http://winpdb.org/"&gt;Winpdb&lt;/a&gt; to allow remote debugging in case your application is no longer attached to a terminal.  And the other problem the above code has is that it can't seem to resume the program after pdb got invoked, after exiting pdb you just get a traceback and are done (but since this is only &lt;code&gt;bdb&lt;/code&gt; raising the &lt;code&gt;bdb.BdbQuit&lt;/code&gt; exception I guess this could be solved in a few ways).  The last immediate issue is running this on Windows, I don't know much about Windows but I know they don't have signals so I'm not sure how you could do this there.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-2293120094122784547?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/2293120094122784547/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/07/how-to-bring-running-python-program.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2293120094122784547'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2293120094122784547'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/07/how-to-bring-running-python-program.html' title='How to bring a running python program under debugger control'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-4502761170651473560</id><published>2009-07-13T16:48:00.002+01:00</published><updated>2009-07-13T17:15:34.856+01:00</updated><title type='text'>devenv.com via cygwin ssh (visual studio 2003)</title><content type='html'>&lt;p&gt;Integrating an automatic build on a windows host with the rest of your one-command cross-platform build system can be quite a pain.  Luckily there is &lt;a href="http://www.cygwin.com/"&gt;cygwin&lt;/a&gt; which makes things like ssh, bash, make etc all work on windows.  It's great.&lt;/p&gt;
&lt;p&gt;The trick to building using visual studio from there is to use the &lt;code&gt;devenv.com&lt;/code&gt; tool, which is a version of the full blown visual studio (&lt;code&gt;devenv.exe&lt;/code&gt;) that does not pop up windows on your screen (ahem, should not- see below) but instead shows all output nicely on your terminal (which you tee to the logfiles of your build system of course).  Life still looks good.&lt;/p&gt;
&lt;p&gt;So you set up your build system to do remote, unattended logins to the windows build slave using ssh public keys.  This is trivial as you do this on all your build slaves.  But all of a sudden &lt;code&gt;devenv.com&lt;/code&gt; just hangs.  That's weird.  Do some searching on the mighty Internet and it turns out there's a bug somewhere in Mircosofts authentication handling.  There is some token that does something weird and somehow when you use public key authentication visual studio 2003 (and 2005) think they're the system service (since that is what the ssh service runs as) and don't run properly (various errors possible).  But everyone reports errors instead of just hanging, how is this possible?  So you go to the ssh service and tick the "Allow service to interact with desktop" box, restart ssh and try again.  Success!  Now you get a window from &lt;code&gt;devenv.com&lt;/code&gt; that visual studio crashed on the screen.  Never mind that &lt;code&gt;devenv.com&lt;/code&gt; was supposed to be command-line only.&lt;/p&gt;
&lt;p&gt;But still not closer to the solution.  It turns out that Microsoft &lt;a href="http://support.microsoft.com/kb/920770"&gt;actually fixed this error for visual studio 2005&lt;/a&gt;, but you're stuck with 2003 for some reason.&lt;/p&gt;
&lt;p&gt;Time for ugly solutions.&lt;/p&gt;
&lt;p&gt;Since this Windows box is a build slave anyway, it's not supposed to be used for anything else.  So the only account of use on it is the one used by the build system.  What if we run the ssh service in the name of this account?  This requires some fiddling with the permissions of the ssh files (&lt;code&gt;/etc/ssh_*&lt;/code&gt; and &lt;code&gt;/var/log/sshd.log&lt;/code&gt;) in cygwin, but once the ssh service is happy and wants to start it all works!  No more errors from &lt;code&gt;devenv.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Ugly, but it finally works.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-4502761170651473560?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/4502761170651473560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/07/devenvcom-via-cygwin-ssh-visual-studio.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4502761170651473560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4502761170651473560'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/07/devenvcom-via-cygwin-ssh-visual-studio.html' title='devenv.com via cygwin ssh (visual studio 2003)'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-7375360900509875191</id><published>2009-07-05T14:11:00.002+01:00</published><updated>2010-01-19T14:45:52.394Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Importing modules in C extension modules</title><content type='html'>&lt;p&gt;It seems that if you need another module in a function of your extension module, the way modules in the standard library seem to solve this is like this:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
static PyObject *
func(void)
{
    PyObject *foo;

    foo = PyImport_ImportModuleNoBlock("foo");
    if (foo == NULL)
        return NULL;
    /* do stuff with foo */
    Py_DECREF(foo);
    return something;
}
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;This means that you have to import the module each time you enter the function (yes, it's looked up in the modules dict by &lt;code&gt;PyImport_ImportModuleNoBlock()&lt;/code&gt; but that function is only avaliable since 2.6, before you have to use &lt;code&gt;PyImport_ImportModule()&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Personally I like storing the module in a static variable so that it only needs to be imported the first time:&lt;p&gt;
&lt;code&gt;&lt;pre&gt;
static PyObject *
func(void)
{
    static PyObject *foo = NULL;

    if (foo == NULL) {
        foo = PyImport_ImportModuleNoBlock("foo");
        if (foo == NULL)
            return NULL;
    }
    /* do stuff with foo */
    return something;
}
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Note here that the &lt;code&gt;Py_DECREF()&lt;/code&gt; is gone.  This function will effectively leak a reference to the module object.  But is this really bad?  How often do module objects get deleted in production code?  My guess is that they normally stay loaded until the application exits.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-7375360900509875191?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/7375360900509875191/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/07/importing-modules-in-c-extension.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7375360900509875191'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7375360900509875191'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/07/importing-modules-in-c-extension.html' title='Importing modules in C extension modules'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8811644162470540756</id><published>2009-06-23T08:46:00.003+01:00</published><updated>2010-01-19T14:45:52.394Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Singletons in a Python extension module</title><content type='html'>&lt;p&gt;If you want a singleton in a C extension module for CPython you basically have to do the same as when doing this in plain Python: the &lt;code&gt;.__new__()&lt;/code&gt; method needs to return the same object each time it is called.  Implementing this has a few catches though.&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
static PyObject *
MyType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    static MyObject *self = NULL;

    if (self == NULL)
        self = (MyObject *)type-&gt;tp_alloc(type, 0);
    Py_XINCREF(self);
    return (PyObject *)self;
}
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Then assign this function to the &lt;code&gt;tp_new&lt;/code&gt; slot in the type.  There's two things of interest in this function:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;self&lt;/code&gt; is declared as &lt;code&gt;static&lt;/code&gt;.  Normally this would not be the case, but declaring it as &lt;code&gt;static&lt;/code&gt; makes it stay alive after the function has returned and it will still be pointing to the first instance of our object, so the next time a new object is asked for this is simply returned.&lt;/li&gt;
&lt;li&gt;Before returning the pointer to the static self, the reference count is increased.  This may seem odd but is the right thing to do, otherwise the reference count will not go up for subsequent calls to the new function since the reference count is increased by &lt;code&gt;PyType_GenericAlloc()&lt;/code&gt; (via the call from the pointer in the &lt;code&gt;tp_alloc&lt;/code&gt; slot).  So if you don't do this you end up with negative reference counts, which doesn't make python very happy.  This does mean you never end up deallocating the object since the lowest reference count you have is 1, but you wanted a singleton right?.  If you really wanted to get the reference count to drop to 0 then you can always put the &lt;code&gt;Py_INCREF()&lt;/code&gt; in an &lt;code&gt;else&lt;/code&gt; clause.&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8811644162470540756?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8811644162470540756/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/06/singletons-in-python-extension-module.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8811644162470540756'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8811644162470540756'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/06/singletons-in-python-extension-module.html' title='Singletons in a Python extension module'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-6307376499982500447</id><published>2009-06-07T12:27:00.004+01:00</published><updated>2009-06-07T12:52:00.362+01:00</updated><title type='text'>Voting and identification in the UK</title><content type='html'>&lt;p&gt;Last Thursday I had the pleasure of being able to vote for the local council as well as for the European Parliament.  Since I'm a Belgian citizen living in the United Kingdom this involved convincing the officials at the voting office to re-read their instructions (at first they only allowed me to vote for the local council but not for Europe, I think they must have realised how silly that sounded) but otherwise was quite easy.  &lt;em&gt;Too easy.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When I went to the polling station I forgot my poll card, but my colleagues at work said that would be fine (not that going home to pick it up was very far) so I tried it anyway.  No problem, the only thing they asked is where I lived, then found my name on their list and let me vote (after above-mentioned debacle).  &lt;em&gt;There was absolutely no verification that I was who I claimed to be.&lt;/em&gt;  Seriously, it is rather trivial to vote for someone else, I'm sure you can visit 2 or 3 polling stations and vote in someone else their name.  Just talk to friends and work a little bit together and you can all cast half a dozen votes if you're a little careful.  And they have no way of recovering from this, other then having to let everyone using that voting station re-cast their vote.&lt;/p&gt;
&lt;p&gt;After some talking to people they seemed to think that there is no single means of identifying someone in an official way in the UK.  This means that buying alcohol is better controlled then voting, since there you either have a form of ID (if you look under 21) or you don't get to buy it.  But for voting?  No ID required, because there is none.&lt;/p&gt;
&lt;p&gt;I'm sure the ID card scheme as currently proposed in the UK is not any good, but there does appear to be a problem that might have to be fixed somehow.  By now I'm pretty sure that if you give me 2 years I can create a fictitious John Smith person, he'll have a passport, driving license, voting rights, bank account and be native British.  Seriously, it's easy.  You just need some time.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-6307376499982500447?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/6307376499982500447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/06/voting-and-identification-in-uk.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6307376499982500447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6307376499982500447'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/06/voting-and-identification-in-uk.html' title='Voting and identification in the UK'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-7221028643377483474</id><published>2009-06-06T13:05:00.003+01:00</published><updated>2010-01-19T14:45:52.394Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Raising exceptions in threads</title><content type='html'>&lt;p&gt;It's not simply raising an exception in a thread that I want.  I want to raise an exception in a thread &lt;em&gt;from another thread&lt;/em&gt;.  It's like sending signals to threads, only signals in pyhon can only be delivered to the main thread (for portability).  But Python has a other asynchronous signaling system: exceptions.&lt;/p&gt;
&lt;p&gt;I'd like to be able to do something like:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
for t in threading.enumerate():
    thread.raise(MyAppDoesntWantYouAnymoreError)
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Is this possible?  Are there other ways to do this sort of thing?&lt;/p&gt;
&lt;p&gt;Alternatively I might be happy with a fix for &lt;a href="http://bugs.python.org/issue1856"&gt;issue1856&lt;/a&gt;, but I do think it might be nice to be able to signal threads in an asynchronous way in any case.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-7221028643377483474?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/7221028643377483474/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/06/raising-exceptions-in-threads.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7221028643377483474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7221028643377483474'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/06/raising-exceptions-in-threads.html' title='Raising exceptions in threads'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-4546095436378205432</id><published>2009-05-30T15:04:00.003+01:00</published><updated>2010-01-19T14:45:52.395Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>New home for PSI</title><content type='html'>&lt;p&gt;PSI, the Python System Information package that pulls interesting information from the kernel and makes it available in a nice Python API, has a &lt;a href="http://bitbucket.org/chrismiles/psi/"&gt;new home at bitbucket&lt;/a&gt;.  This means that the source conde now lives inside a &lt;a href="http://www.selenic.com/mercurial/wiki/"&gt;mercurial&lt;/a&gt; repository instead of subversion.&lt;/p&gt;
&lt;p&gt;This actually happened about a week ago, but better announce it late then never...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-4546095436378205432?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/4546095436378205432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/05/new-home-for-psi.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4546095436378205432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4546095436378205432'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/05/new-home-for-psi.html' title='New home for PSI'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-5625181783906905555</id><published>2009-05-21T10:04:00.000+01:00</published><updated>2010-01-19T14:45:52.395Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Python System Information 0.3b1^W0.3b1.1</title><content type='html'>&lt;p&gt;Short summary: &lt;a href="http://www.psychofx.com/psi/"&gt;PSI&lt;/a&gt; is alive and we've just &lt;a href="http://pypi.python.org/pypi/PSI/"&gt;released the first beta&lt;/a&gt; of a much improved upcoming 0.3 release!&lt;/p&gt;
&lt;p&gt;Back in 2007 Chris Miles announced &lt;a href="http://www.psychofx.com/psi/"&gt;PSI - Python System Information&lt;/a&gt;, a &lt;a href="http://python.org"&gt;Python&lt;/a&gt; extension module to provide access to some system information not normally available to Python.  Most notably it allows you to look at all processes on the system and get details like memory usage, cpu usage, users and many more things the kernel knows about a process.  And all this in a pythonic way!&lt;/p&gt;
&lt;p&gt;At the time the implementation was not perfect (when will an implementation ever be?), it had many memory leaks and reference counting errors, and sadly no one seemed to have the time and motivation to work on it for a long time.  But since the beginning of this year I finally found some time to fix these issues and soon some more people joined in.  Chris has been amazing in allowing access to almost anything I asked for and since then there has been steady development improving the code base, tests and API.  Right now PSI does not leak any memory, and provides basic system indentification and detailed process information on Linux (2.4 &amp; 2.6 kernels), Darwin 10.3 and up, SunOS (5.8-5.11) and AIX (5.3) and it does this for any version of Python greater then 2.3, including 3.0.  Not too shabby!&lt;/p&gt;
&lt;p&gt;There are many things we would like to do in the future too. more platform support for one.  Ideally PSI should run on all the major platforms supported by Python itself (and all minor ones too).  And more information too, getting information about processes is one very useful and common thing but there is so much more the kernel can tell us: CPU information and statistics, network interfaces, etc.  It's a massive and never ending task, but hopefully we can do the common things on all major platforms.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Update&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Version 0.3b1.1 has been released now.  Seems the last version did only ship the sources for Linux and not all platforms.  This bugfix release also adds the MANIFEST file so that distutils can build binary distributions from the tarball.&lt;/p&gt;
&lt;p&gt;Sorry!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-5625181783906905555?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/5625181783906905555/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/05/python-system-information-03b1.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5625181783906905555'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5625181783906905555'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/05/python-system-information-03b1.html' title='Python System Information 0.3b1^W0.3b1.1'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-41828353846810826</id><published>2009-05-11T20:16:00.000+01:00</published><updated>2010-01-19T14:45:52.395Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Compiling applications in setup.py</title><content type='html'>&lt;p&gt;If you need to compile a random application in setup.py this is not that hard:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
cc = distutils.ccompiler.new_compiler()
distutils.sysconfig.customize_compiler(cc)
cc.link_executable(['test.c'], 'a.out')
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;There is no need to create the object files explicitly with &lt;code&gt;cc.compile()&lt;/code&gt; first if you have no need for them, the command line invoked by &lt;code&gt;.link_executable()&lt;/code&gt; will do the compilation step in one go for you.&lt;/p&gt;
&lt;p&gt;This part of distutils is actually documented, so &lt;a href="http://docs.python.org/distutils/apiref.html#module-distutils.ccompiler"&gt;check it out&lt;/a&gt;, you can pass in many optional arguments and modify the compiler object to customize things.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-41828353846810826?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/41828353846810826/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/05/compiling-applications-in-setuppy.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/41828353846810826'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/41828353846810826'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/05/compiling-applications-in-setuppy.html' title='Compiling applications in setup.py'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-3727467444906547087</id><published>2009-05-01T22:45:00.000+01:00</published><updated>2010-01-19T14:45:52.395Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>What's new in Python 2.6: logging</title><content type='html'>&lt;p&gt;The &lt;a href="http://docs.python.org/whatsnew/2.6.html"&gt;What's New in Python 2.6&lt;/a&gt; document is very good and contains loads of information, congrats for making it.  But it misses out a nice addition to the logging module: &lt;a href="http://docs.python.org/library/logging.html#loggeradapter-objects"&gt;LoggerAdapter Objects&lt;/a&gt;.  It allows you to create a logger object with extra contextual information really easy:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
log = logging.LoggerAdapter(logging.getLogger("subsystem"),
                            {"argv1": sys.argv[1]})
logging.basicConfig(format="%(argv1)s %(message)s")
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Neat&lt;/p&gt;
&lt;p&gt;Secondly it decided to define &lt;code&gt;__all__&lt;/code&gt; which means not all object are exported anymore.  Unfortunately this &lt;code&gt;__all__&lt;/code&gt; is incomplete in 2.6.2, even &lt;code&gt;getLogger&lt;/code&gt; is not listed in it!  The good news is that this is one of the &lt;a href="http://bugs.python.org/issue5854"&gt;fastest bug report turnaround times&lt;/a&gt; I have seen, so expect this to be fixed in the next stable release.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-3727467444906547087?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/3727467444906547087/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/05/whats-new-in-python-26-logging.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3727467444906547087'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3727467444906547087'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/05/whats-new-in-python-26-logging.html' title='What&apos;s new in Python 2.6: logging'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-5278882896462854632</id><published>2009-04-16T17:52:00.002+01:00</published><updated>2009-04-16T17:58:40.227+01:00</updated><title type='text'>Sun Fire T1000 not closet friendly</title><content type='html'>&lt;p&gt;Obviously the &lt;a href="http://www.sun.com/servers/coolthreads/t1000/"&gt;Sun Fire T1000&lt;/a&gt; is a noisy machine, that's why it's sitting in the closet in the first place.  But it has more shortcomings that make it a closet-unfriendly server:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It shuts down at 50 degrees Celsius.  That means wrapping it in a duvet to damp the noise is not a great idea if you want it to do any work.&lt;/li&gt;
&lt;li&gt;The power button sticks out.  That makes it way to easy to press it if you're getting something else from the closet.&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-5278882896462854632?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/5278882896462854632/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/04/sun-fire-t1000-not-closet-friendly.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5278882896462854632'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5278882896462854632'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/04/sun-fire-t1000-not-closet-friendly.html' title='Sun Fire T1000 not closet friendly'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-3012677061171453148</id><published>2009-03-28T21:33:00.004Z</published><updated>2010-01-19T14:45:52.396Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Time, floating points and python modules handling time</title><content type='html'>&lt;p&gt;Some memory in the back of my mind tells I've once read a rant about storing time that argued you should never store time in floating point variables.  My memory seems to think the rant convinced me and it does indeed seem better to store time in integers so that time doesn't go and change little bits due to rounding etc.  and indeed the C library seems to stick to that, mostly (&lt;code&gt;difftime()&lt;/code&gt; returns a double for example).&lt;/p&gt;
&lt;p&gt;When looking at Python the stdlib &lt;code&gt;datetime&lt;/code&gt; module seems to do this too.  However ofter people scoff at the &lt;code&gt;datetime&lt;/code&gt; module and recommend the use of &lt;code&gt;mxDateTime&lt;/code&gt; instead, it is a lot better supposedly.  But looking at how it stores time it seems to use &lt;code&gt;double&lt;/code&gt; values interally.&lt;/p&gt;
&lt;p&gt;So I am wondering, if &lt;code&gt;mxDateTime&lt;/code&gt; gets away with storing time as floating points is there really a disadvantage to it?  Is there a point to avoiding floating point numbers while handling time?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-3012677061171453148?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/3012677061171453148/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/03/time-floating-points-and-python-modules.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3012677061171453148'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3012677061171453148'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/03/time-floating-points-and-python-modules.html' title='Time, floating points and python modules handling time'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8928217664183442975</id><published>2009-03-28T16:15:00.003Z</published><updated>2009-03-28T16:24:45.016Z</updated><title type='text'>Reading a 64-bit core from a 32-bit proc on solaris?</title><content type='html'>&lt;p&gt;I'm trying to read some pointers from a 64-bit address space file (from /proc/pid/as) while running in a 32-bit process - all this on Solaris.  To do this I'm using the transitional large file compilation environment, i.e. where you get both &lt;code&gt;open()/pread()&lt;/code&gt; and &lt;code&gt;open64()/pread64()&lt;/code&gt; since I still want to be able to read a 32-bit core.  However when doing the &lt;code&gt;pread64()&lt;/code&gt; call on the address space I keep getting &lt;tt&gt;EOVERFLOW&lt;/tt&gt;, no matter what values I put in - even trying to read the first byte of the core.  And reading the first byte of a 64-bit core using simply &lt;code&gt;pread()&lt;/code&gt;, which as far as I can tell should work, fails too.  &lt;tt&gt;EOVERFLOW&lt;/tt&gt; whatever I do.&lt;/p&gt;
&lt;p&gt;Is this simply Solaris stopping me from reading a core from a 64-bit proc?  But why wouldn't it stop me at the time I do the &lt;code&gt;open64()&lt;/code&gt; call in that case?  Any clues would be appreciated.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8928217664183442975?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8928217664183442975/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/03/reading-64-bit-core-from-32-bit-proc-on.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8928217664183442975'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8928217664183442975'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/03/reading-64-bit-core-from-32-bit-proc-on.html' title='Reading a 64-bit core from a 32-bit proc on solaris?'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-4281485203957079128</id><published>2009-03-23T23:19:00.002Z</published><updated>2009-03-23T23:28:26.371Z</updated><title type='text'>Replacing the screen of your digital camera</title><content type='html'>&lt;p&gt;The screen of my Canon Digital Ixus 40 compact camera broke on my last trip (sport climbing in Geyikbayiri near Antalya, Turkey - if you climb: it's an amzing place!) which made me rather sad.  But with the help of Ebay I found a new screen and have just managed to replace it.&lt;/p&gt;
&lt;p&gt;It only took about 1h40 of careful fiddling and figuring out how the camera was stuck together while being nervous of breaking it even more.  But the end result was successful!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-4281485203957079128?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/4281485203957079128/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/03/replacing-screen-of-your-digital-camera.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4281485203957079128'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4281485203957079128'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/03/replacing-screen-of-your-digital-camera.html' title='Replacing the screen of your digital camera'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1987848750856461764</id><published>2009-03-23T20:32:00.002Z</published><updated>2009-03-23T20:34:20.022Z</updated><title type='text'>How to send emails</title><content type='html'>&lt;p&gt;Dear World&lt;/p&gt;
&lt;p&gt;Subject lines in emails are there for a reason.&lt;br/&gt;
Please use them.&lt;/p&gt;
&lt;p&gt;Love&lt;br&gt;
Floris&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1987848750856461764?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1987848750856461764/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/03/how-to-send-emails.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1987848750856461764'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1987848750856461764'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/03/how-to-send-emails.html' title='How to send emails'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-2310597215328682580</id><published>2009-02-28T16:13:00.003Z</published><updated>2009-02-28T16:32:54.274Z</updated><title type='text'>Virtual memory size (VSZ) on AIX</title><content type='html'>&lt;p&gt;If you have ever looked at the &lt;code&gt;ps(1)&lt;/code&gt; output on an AIX box and wondered why the virtual memory size (VSZ) is so small, often even smaller then the resident set size (RSS), then here's the answer: &lt;em&gt;it's not the virtual memory size&lt;/em&gt;.  It is actually the size of the &lt;em&gt;data section of the virtual memory&lt;/em&gt; of the process.&lt;/p&gt;
&lt;p&gt;If you really do want to know the VSZ of a process you'll have to use &lt;code&gt;svmon -P 123&lt;/code&gt;, there you will find it.  But do multiply this by the pagesize (&lt;code&gt;pagesize(1)&lt;/code&gt; is a user tool on AIX).  If only somehow this would be documented.&lt;/p&gt;
&lt;p&gt;Oh, and if someone knows where the source of &lt;code&gt;svmon&lt;/code&gt; lives I'd love to know how they actually find that, using the &lt;code&gt;struct procent64&lt;/code&gt; structure from &lt;code&gt;procinfo.h&lt;/code&gt; I can not figure out a way of finding it.  And the VSZ information in &lt;code&gt;/proc/123/psinfo&lt;/code&gt; is completely whacky, no idea what that is supposed to be.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;For completeness sake: this is on AIX 5.3.&lt;/small&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-2310597215328682580?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/2310597215328682580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/02/virtual-memory-size-vsz-on-aix.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2310597215328682580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2310597215328682580'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/02/virtual-memory-size-vsz-on-aix.html' title='Virtual memory size (VSZ) on AIX'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-2725233011451676510</id><published>2009-02-26T14:56:00.004Z</published><updated>2009-02-26T15:36:47.872Z</updated><title type='text'>Closed source frustration</title><content type='html'>&lt;p&gt;Developing against a closed source libraries is jut painful and wastes your time.  Function definitions are missing from the header files, so you get to declare them yourself from the manpages.  Then you get random error messages even though you do everything just like the manpage tells you.  Some dark corners of the internet then suggest one of the parameters is actually the 32-bit variant of the type instead of the generic one.  Still no luck.&lt;/p&gt;
&lt;p&gt;Just let me look inside the code to figure out why it's not working, thank you very much.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;This post was brought to you by the joys of the &lt;code&gt;getprocs64(3)&lt;/code&gt; call on AIX 5.3.  Any hints on it's usage would be appreciated.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update:&lt;/em&gt; It finally works!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-2725233011451676510?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/2725233011451676510/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/02/closed-source-frustration.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2725233011451676510'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2725233011451676510'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/02/closed-source-frustration.html' title='Closed source frustration'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-30860961811941364</id><published>2009-02-19T20:42:00.002Z</published><updated>2009-02-19T20:51:49.619Z</updated><title type='text'>ssh magic</title><content type='html'>&lt;p&gt;Dear Lazy Web&lt;/p&gt;
&lt;p&gt;If I write the following in my &lt;code&gt;~/.ssh/config&lt;/code&gt;:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
Host bartunnel
  HostKeyAlias bar
  HostName bar
  LocalForward 8822 foo:22

Host barjump
  HostKeyAlias bar
  HostName localhost
  Port 8822
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I can connect to host &lt;em&gt;bar&lt;/em&gt; via host &lt;em&gt;foo&lt;/em&gt; (circumnavigating a firewall that stops me from going to &lt;em&gt;bar&lt;/em&gt;directly) just like am connecting to it directly.  E.g. in two separate shells (in this order):&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
$ ssh bartunnel # this sets up the tunnel
# different shell (or use -n on the last one)
$ ssh barjump # now I'm connected normally
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Now is there something I could write in my ssh configuration file that I could just do this in one step?  I want to simply do:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
$ ssh barjump
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;and the tunnel should be set up for me in the background.  Likewise if I close the connection the tunnel should go.  Is this possible?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-30860961811941364?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/30860961811941364/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/02/ssh-magic.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/30860961811941364'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/30860961811941364'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/02/ssh-magic.html' title='ssh magic'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1437618237552994571</id><published>2009-02-15T12:32:00.002Z</published><updated>2009-02-15T12:47:36.191Z</updated><title type='text'>Planning upgrades</title><content type='html'>&lt;p&gt;So Debian has finally &lt;a href="http://debian.org/News/2009/20090214"&gt;released&lt;/a&gt; again.  Congratulations.  Time to start planning upgrades of servers then.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1437618237552994571?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1437618237552994571/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/02/planning-upgrades.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1437618237552994571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1437618237552994571'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/02/planning-upgrades.html' title='Planning upgrades'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-5575624752497634053</id><published>2009-02-13T23:18:00.000Z</published><updated>2010-01-19T15:01:35.463Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Compiling 32-bit Python  on amd64</title><content type='html'>&lt;p&gt;If you ever feel the need to compile a 32-bit version of Python on a amd64 bit machine, this is how you do it on a Debian/Ubuntu system.&lt;/p&gt;
&lt;p&gt;Firstly you need the correct compiler stuff, this means you need gcc-multilib and 32-bit development libraries of at least libc.  On Debian/Ubuntu installing the &lt;code&gt;gcc-multilib&lt;/code&gt; package will pull in most if not all of the required dependencies.&lt;/p&gt;
&lt;p&gt;Next is invoking the &lt;code&gt;configure&lt;/code&gt; script of Python.  Sadly Python is one of those autoconf using projects who advertise the use of environment variables like &lt;code&gt;CFLAGS&lt;/code&gt; in the &lt;code&gt;--help&lt;/code&gt; output of &lt;code&gt;./configure&lt;/code&gt; but don't actually respect them, this is all too common for autoconf-but-no-automake using projects.  So the correct way to start building is using the &lt;code&gt;OPT&lt;/code&gt; environment variable instead of &lt;code&gt;CFLAGS&lt;/code&gt;:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
OPT=-m32 LDFLAGS=-m32 ./configure --prefix=/opt/pym32
make
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;You may want to finetune the &lt;code&gt;OPT&lt;/code&gt; value, since this is where normally &lt;code&gt;-g -O3&lt;/code&gt; etc appears in so you've just got rid of those.  I'm not quite convinced of this design but anyway.&lt;/p&gt;
&lt;p&gt;Now you can watch the compilation, depending on your machine you may have time to make a cup of tea or so.  Near the end you'll see something like this:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
Failed to build these modules:
_hashlib           _sqlite3           _ssl
_tkinter           bz2                gdbm
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;This is pretty much exactly the same as when normally compiling python: go find the packages that provide the development libraries needed for these modules.  Only this time you need to look for the 32-bit variety of these.  On Debian/Ubuntu look out for packages named like &lt;code&gt;lib32foo-dev&lt;/code&gt;.  After installing all applicable ones I could find this is what I got it down too in the end (only using system packages):&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
Failed to build these modules:
_hashlib           _sqlite3           _ssl
_tkinter           gdbm
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Just in case you aren't quite happy with your achievement so far you could now try compiling an extension module against your 32-bit python:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
LDFLAGS=-m32 /opt/pym32/bin/python setup.py build
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Now wasn't this useful?  Silly binary-only libraries...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-5575624752497634053?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/5575624752497634053/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/02/compiling-32-bit-python-on-amd64.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5575624752497634053'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5575624752497634053'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/02/compiling-32-bit-python-on-amd64.html' title='Compiling 32-bit Python  on amd64'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-7400161592817154208</id><published>2009-02-13T10:59:00.003Z</published><updated>2009-02-13T11:09:20.635Z</updated><title type='text'>sed for binary files: bbe</title><content type='html'>&lt;p&gt;While GNU sed takes care not to munge NULL characters this is not the POSIX standard, therefore it's no surprise some implementations don't manage to edit binary files properly (not to mention that many implementations will struggle with long lines).  Hence the search for a binary sed:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://sourceforge.net/projects/bbe-/"&gt;bbe&lt;/a&gt; is just that.  It allows you to do operations on blocks --you can define the block size in a variety of flexible ways-- as well as on bytes.  So the most simple substitution command from sed just looks exactly the same.  It's gorgeous.&lt;/p&gt;
&lt;p&gt;It is a C program that you'll need to compile, but has no build dependencies so you can use it pretty much anywhere.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-7400161592817154208?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/7400161592817154208/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/02/sed-for-binary-files-bbe.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7400161592817154208'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7400161592817154208'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/02/sed-for-binary-files-bbe.html' title='sed for binary files: bbe'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-2388442183107801999</id><published>2009-02-09T18:49:00.002Z</published><updated>2009-02-09T19:14:00.812Z</updated><title type='text'>Specifying the RUNPATH in sofiles</title><content type='html'>&lt;p&gt;When you create a shared object that depends on another shared object there are multiple ways to tell it where to look for the shared object.  Two of these are by encoding (at linking time) a &lt;code&gt;DT_RPATH&lt;/code&gt; and &lt;code&gt;DT_RUNPATH&lt;/code&gt; entry in the &lt;code&gt;.dynamic&lt;/code&gt; section.  A third one is to use the &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; environment variable (at runtime).  The order of precedence of these is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;RPATH&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RUNPATH&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The question is how to encode these into your shared object.  This is normally done with the &lt;code&gt;--rpath&lt;/code&gt; or &lt;code&gt;-R&lt;/code&gt; options to the linker.  But as the name suggest this will create an &lt;code&gt;RPATH&lt;/code&gt;.  When using GNU &lt;code&gt;ld(1)&lt;/code&gt; you add an &lt;code&gt;--enable-new-dtags&lt;/code&gt; option which does add the (newer) &lt;code&gt;RUNPATH&lt;/code&gt;, and when both &lt;code&gt;RPATH&lt;/code&gt; and &lt;code&gt;RUNPATH&lt;/code&gt; are present the runtime linker will ignore the &lt;code&gt;RPATH&lt;/code&gt;.  On Solaris however the linker does add a &lt;code&gt;RUNPATH&lt;/code&gt; by default as soon as you use &lt;code&gt;-R&lt;/code&gt;, thus you will always end up with &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; overriding the value you gave with &lt;code&gt;-R&lt;/code&gt;.  Good to keep that in mind.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-2388442183107801999?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/2388442183107801999/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/02/specifying-runpath-in-sofiles.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2388442183107801999'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2388442183107801999'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/02/specifying-runpath-in-sofiles.html' title='Specifying the RUNPATH in sofiles'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8073607245756706897</id><published>2009-01-31T22:05:00.002Z</published><updated>2010-01-19T15:01:35.464Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Resistance to change</title><content type='html'>&lt;p&gt;Why can C developers be happy with using &lt;tt&gt;config.h&lt;/tt&gt; to get constants about where to look for configuration or data files for example, yet Python developers seem to refuse to think of any way they might want to support finding these files in a portable way?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8073607245756706897?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8073607245756706897/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/01/resistance-to-change.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8073607245756706897'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8073607245756706897'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/01/resistance-to-change.html' title='Resistance to change'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1645964667689461813</id><published>2009-01-31T14:50:00.004Z</published><updated>2009-01-31T16:46:53.698Z</updated><title type='text'>FreeBSD on Virtualbox</title><content type='html'>&lt;p&gt;&lt;a href="http://www.virtualbox.org/"&gt;Virtualbox&lt;/a&gt; is my desktop virtualisation technology of choice most of the time and I wanted to have a play with &lt;a href="http://www.freebsd.org/"&gt;FreeBSD&lt;/a&gt;.  Seems they don't get along tough and you won't get a network connection.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Solution 1: Change the network adaptor in Virtualbox to PCnet-PCI II (instead of PCnet-PCI III).  I've tried this and it works.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.wzdftpd.net/blog/index.php?2008/02/22/10-freebsd-7-installation-in-virtualbox"&gt;Solution 2&lt;/a&gt;: I haven't tried this.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While we're on the subject, when running the Ubuntu 8.10 server jeos edition on Virtualbox don't forget to enable PAE in Virtualbox or you get to do a kernel dance using the resque mode of the installer.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1645964667689461813?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1645964667689461813/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/01/freebsd-on-virtualbox.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1645964667689461813'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1645964667689461813'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/01/freebsd-on-virtualbox.html' title='FreeBSD on Virtualbox'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-5349302263071037392</id><published>2009-01-27T11:25:00.004Z</published><updated>2010-01-19T15:01:35.464Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>datetime.datetime.totimestamp()</title><content type='html'>&lt;p&gt;There exists a &lt;tt&gt;&lt;a href="http://docs.python.org/library/datetime.html#datetime.datetime.fromtimestamp"&gt;datetime.datetime.fromtimestamp()&lt;/a&gt;&lt;/tt&gt; and many a time I've wondered why there is no &lt;tt&gt;.totimestamp()&lt;/tt&gt; equivalent.  The answer is that not all &lt;tt&gt;datetime&lt;/tt&gt; objects can be represented by a timestamp.  But if you know this restriction and want it anyway, this is the magic:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
time.mktime(datetime_object.timetuple())
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Would be so cool if the docs actually mentioned this.&lt;/p&gt;
&lt;p&gt;PS: Both functions have a &lt;tt&gt;utc&lt;/tt&gt; variant too&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt;&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
datetime_object.strftime('%s')
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;This solution does not give you sub-second resolution, but otherwise is rather elegant.  Funny thing is that the &lt;tt&gt;%s&lt;/tt&gt; specifier is &lt;a href="http://docs.python.org/library/time.html#time.strftime"&gt;not documented by the stdlib&lt;/a&gt;, but seems to exist on the underlying implementations at least on UNIX.&lt;/p&gt;
&lt;p&gt;And for completeness, these issues are being discussed in the bug tracker.  See &lt;a href="http://bugs.python.org/issue2736"&gt;issue2736&lt;/a&gt; and &lt;a href="http://bugs.python.org/issue1673409"&gt;issue1673409&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-5349302263071037392?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/5349302263071037392/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/01/datetimedatetimetotimestamp.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5349302263071037392'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5349302263071037392'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/01/datetimedatetimetotimestamp.html' title='datetime.datetime.totimestamp()'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-3730626897309722228</id><published>2009-01-22T17:52:00.002Z</published><updated>2009-01-22T18:02:52.116Z</updated><title type='text'>Resident Set Size (RSS) from /proc/pid/stat</title><content type='html'>&lt;p&gt;Most UNIX-like systems have information about processes stored in &lt;tt&gt;/proc/&lt;em&gt;pid&lt;/em&gt;/&lt;/tt&gt; files, so does &lt;a href="http://kernel.org/"&gt;Linux&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you would want to get the &lt;a href="http://en.wikipedia.org/wiki/Resident_Set_Size"&gt;Resident Set Size (RSS)&lt;/a&gt; of a process on Linux you could find this in a number of files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;tt&gt;/proc/&lt;em&gt;pid&lt;/em&gt;/stat&lt;/tt&gt;: targetted for &lt;tt&gt;scanf(3)&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt&gt;/proc/&lt;em&gt;pid&lt;/em&gt;/statm&lt;/tt&gt;: targetted for &lt;tt&gt;scanf(3)&lt;/tt&gt; but just memory information (more detailed)&lt;/li&gt;
&lt;li&gt;&lt;tt&gt;/proc/&lt;em&gt;pid&lt;/em&gt;/status&lt;/tt&gt;: targetted at humans&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Oddly enough if you check these three files for the RSS memory you will get different results!  It seem both &lt;tt&gt;stat&lt;/tt&gt; and &lt;tt&gt;statm&lt;/tt&gt; have the wrong RSS information.  It is a mystery to me why.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-3730626897309722228?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/3730626897309722228/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/01/resident-set-size-rss-from-procpidstat.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3730626897309722228'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3730626897309722228'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/01/resident-set-size-rss-from-procpidstat.html' title='Resident Set Size (RSS) from /proc/pid/stat'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-2188810044419626652</id><published>2009-01-15T22:22:00.002Z</published><updated>2010-01-19T15:01:35.464Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Porting a C extension module to py3k</title><content type='html'>&lt;p&gt;This was not that hard!  First look at what everyone else has to say about this, nice aggregated on the &lt;a href="http://wiki.python.org/moin/cporting"&gt;python wiki&lt;/a&gt;.  It covers a lot.&lt;/p&gt;
&lt;p&gt;Now for what I discovered on top of this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you're defining a new type &lt;tt&gt;self-&gt;ob_type&lt;/tt&gt; doesn't exist anymore.  This is a problem as you need it in the deallocator for example.  The solution is to use &lt;tt&gt;Py_TYPE(self)&lt;/tt&gt;.  So the deallocator becomes: &lt;tt&gt;Py_TYPE(self)-&gt;tp_free((PyObject*)self);&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;When you're paranoid and use &lt;tt&gt;-Werror -Wredundant-decls&lt;/tt&gt; you'll notice a duplicate declaration in &lt;tt&gt;pyerror.h&lt;/tt&gt;.  &lt;a href="http://bugs.python.org/issue4950"&gt;Bug filed&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The module initialisation is a lot easier then in the &lt;a href="http://docs.python.org/howto/cporting.html#module-initialization-and-state"&gt;official docs&lt;/a&gt;.  You seem perfectly fine without module state, so all you need to fill out in your &lt;tt&gt;struct PyModuleDef&lt;/tt&gt; is &lt;tt&gt;m_base&lt;/tt&gt;, &lt;tt&gt;m_name&lt;/tt&gt; and &lt;tt&gt;m_size&lt;/tt&gt;.  Of course &lt;tt&gt;m_doc&lt;/tt&gt; and &lt;tt&gt;m_methods&lt;/tt&gt; are pretty useful too, but not strictly required.  Copy &lt;a href="http://docs.python.org/3.0/extending/extending.html#the-module-s-method-table-and-initialization-function"&gt;the tutorial&lt;/a&gt; here.&lt;br/&gt;
And if you use &lt;tt&gt;PyMODINIT_FUNC&lt;/tt&gt; to declare it all you need to &lt;tt&gt;#ifdef&lt;/tt&gt; is &lt;tt&gt;PyInitmymodule(void)&lt;/tt&gt;, &lt;tt&gt;PyModule_Create()&lt;/tt&gt; and the return value.&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-2188810044419626652?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/2188810044419626652/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/01/porting-c-extension-module-to-py3k.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2188810044419626652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/2188810044419626652'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/01/porting-c-extension-module-to-py3k.html' title='Porting a C extension module to py3k'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-377310610457603032</id><published>2009-01-09T12:24:00.004Z</published><updated>2010-01-19T15:01:35.464Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Generating source files in setup.py</title><content type='html'>&lt;p&gt;I have a Python extension module written in C and one of the weirder things it needs to do is have a number of module constants with their values which are defined (e.g. &lt;tt&gt;#define PREFIX_FOO 0x01&lt;/tt&gt; etc.) in an external C header file.  All the defined names in that header file start with a common prefix so it's easy enough to write a python function that will read the file and spit out the correct C source code that enables me to expose these in my python module.  The tricky part however is where to hook this up in the &lt;tt&gt;setup.py&lt;/tt&gt; script.&lt;/p&gt;
&lt;p&gt;At first I tried to extend the &lt;tt&gt;distutils.command.build_ext.build_ext&lt;/tt&gt; class to generate this file.  That doesn't work however as the distribution is not very happy about having a file listed as required (&lt;tt&gt;Extension(sources=['generated_file.c', ...], ...)&lt;/tt&gt;) which isn't actually there at the time the &lt;tt&gt;distutils.dist.Distribution&lt;/tt&gt; instance is created.&lt;/p&gt;
&lt;p&gt;So the two (AFAIK) remaining options are to subclass &lt;tt&gt;distutils.dist.Distribution&lt;/tt&gt; and pass into the setup method: &lt;tt&gt;setup(distclass=MyDistribution, ...)&lt;/tt&gt;.  Or secondly, don't add the generated source file to the list of required files and created in the extended &lt;tt&gt;build_ext&lt;/tt&gt; command.&lt;/p&gt;
&lt;p&gt;For now I've gone for the last option as it seems the more appropriate place to do things (I've overwritten the &lt;tt&gt;.initialize_options()&lt;/tt&gt; method).  But I wonder if other more elegant solutions exist?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-377310610457603032?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/377310610457603032/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2009/01/generating-source-files-in-setuppy.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/377310610457603032'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/377310610457603032'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2009/01/generating-source-files-in-setuppy.html' title='Generating source files in setup.py'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-3258947114596196040</id><published>2008-12-24T09:09:00.001Z</published><updated>2010-01-19T15:01:35.465Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>import skynet</title><content type='html'>&lt;p&gt;So who's going to make the &lt;a href="http://xkcd.com/521/"&gt;skynet&lt;/a&gt; module?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-3258947114596196040?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/3258947114596196040/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/12/import-skynet.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3258947114596196040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3258947114596196040'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/12/import-skynet.html' title='import skynet'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8196926571712877700</id><published>2008-12-13T12:02:00.002Z</published><updated>2010-01-19T15:01:35.465Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Finding memory leaks in Python extension modules</title><content type='html'>&lt;p&gt;&lt;a href="http://valgrind.org/"&gt;Valgrind&lt;/a&gt; is amazing, even if you've never used it before it's basic usage is really simple:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
$ valgrind --tool=memcheck --leak-check=full /usr/bin/python ./test.py
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;This is enough to point you at the places in your extension module where you allocated stuff that didn't get freed later.  This is a massive timesaver with looking over the entire source file again to find out where you made your mistakes.&lt;/p&gt;
&lt;p&gt;I must admit that the extension module in question uses &lt;tt&gt;malloc(3)&lt;/tt&gt; and &lt;tt&gt;free(3)&lt;/tt&gt; directly instead of allocating on the Python heap using &lt;tt&gt;PyMem_Malloc()&lt;/tt&gt; and &lt;tt&gt;PyMem_Free()&lt;/tt&gt;, so I don't know if that would make it harder to find the leaks.  I can imagine that in that case the "blocks definitely lost" list might point to somewhere in Python's source files instead of your own source files, but I don't know.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8196926571712877700?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8196926571712877700/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/12/finding-memory-leaks-in-python.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8196926571712877700'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8196926571712877700'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/12/finding-memory-leaks-in-python.html' title='Finding memory leaks in Python extension modules'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-4858774822581838577</id><published>2008-12-11T00:48:00.002Z</published><updated>2008-12-11T01:08:43.433Z</updated><title type='text'>gcc, LD_RUN_PATH &amp; Solaris</title><content type='html'>&lt;p&gt;I actually started writing a long post about how linking agains shared libraries works and what and how &lt;tt&gt;ld.so&lt;/tt&gt; does, but that seemed to get very long.  So here the short version that assumes pre-existing knowledge.&lt;/p&gt;
&lt;p&gt;If you want an RPATH in your DSOs (dynamic shared objects: executables and shared libraries) you can pass in &lt;tt&gt;-R/-rpath&lt;/tt&gt; to the linker (the &lt;tt&gt;runtime_library_dir&lt;/tt&gt; keyword argument to distutils's &lt;tt&gt;Extension&lt;/tt&gt; class).  This is not always very practical though, e.g. when building Python it would be a major pain to modify all the makefiles and setup.py.  Error prone too.&lt;/p&gt;
&lt;p&gt;So the linkers (both GNU and Solaris) also accept an environment variable: &lt;tt&gt;LD_RUN_PATH&lt;/tt&gt;.  When that is set it is used to populate the RPATH field, but only when no explicit &lt;tt&gt;-R/-rpath&lt;/tt&gt; is specified.  So far so good.&lt;/p&gt;
&lt;p&gt;On Solaris however your gcc will usually not be installed in the root system, rather in &lt;tt&gt;/usr/sfw&lt;/tt&gt; (sun supplied) or &lt;tt&gt;/opt/csw/gccX&lt;/tt&gt; (&lt;a href="http://opencsw.org"&gt;opencsw&lt;/a&gt;).  So the gcc libs will also not be in the default library search path.  But gcc is nice and helps you out, it will implicitly add a &lt;tt&gt;-R&lt;/tt&gt; option to &lt;tt&gt;ld&lt;/tt&gt; pointing to it's own library directory (for libgcc).  Now I'm not sure how nice exactly this is since it screws your LD_RUN_PATH environment variable over and you actually need to run gcc traced to see this happening, have fun finding that!  It would be nice if gcc would extend the environment variable if you had it set but where using no &lt;tt&gt;-R&lt;/tt&gt; flags instead.  Oh well, at least now you know.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-4858774822581838577?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/4858774822581838577/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/12/gcc-ldrunpath-solaris.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4858774822581838577'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4858774822581838577'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/12/gcc-ldrunpath-solaris.html' title='gcc, LD_RUN_PATH &amp; Solaris'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-7792282866754372228</id><published>2008-12-06T22:47:00.003Z</published><updated>2010-01-19T15:01:35.465Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Mocking away the .__init__() method</title><content type='html'>&lt;p&gt;I wanted to test a method on a class that didn't really depend on a lot of other stuff off the class.  Or rather what it did depend on I had already turned into &lt;a href="http://www.voidspace.org.uk/python/mock.html#mock"&gt;&lt;tt&gt;Mock&lt;/tt&gt;&lt;/a&gt; objects with appropriate &lt;tt&gt;.return_value&lt;/tt&gt;s and asserting with &lt;tt&gt;.called&lt;/tt&gt; etc.  Problem was that the &lt;tt&gt;.__init__()&lt;/tt&gt; method of the object invoked about half the application framework (option parsing, configuration file loading, setting up logging etc.).  Firstly I don't really feel like testing all of that (hey, these are called &lt;em&gt;unittests&lt;/em&gt; after all and those fuctionalities have their own!) and secondly then I had to worry about way too much, quite a lot to setup.&lt;/p&gt;
&lt;p&gt;That's how the whacky idea of replacing the &lt;tt&gt;.__init__()&lt;/tt&gt; method with a mock occurred to me:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
class TestSomething(object):
    @mock.patch('module.Klass.__init__',
                mock.Mock(return_value=None))
    def test_method(self):
        inst = module.Klass()
        inst.other_method = mock.Mock()
        inst._Klass__log = mock.Mock()
        # more of this
        inst.method()
        assert inst.other_method.called
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;The ugly side effects of deciding to mock away the &lt;tt&gt;.__init__()&lt;/tt&gt; method like this is that I have to create mocks for more internal stuff.  The one shown for exampls is normally provided by &lt;tt&gt;self.__log = logging.getLogger('foo')&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;I must admit that I'm still trying to find my way in how to use the &lt;a href="http://www.voidspace.org.uk/python/mock.html"&gt;mock&lt;/a&gt; module effectively and hence I'm not really sure how sane this approach is.  One of my objections with this is that not only am I meddling with clearly hidden attributes of the class, but I also have to do this again and again for each test method.  So the next revision (I'm using &lt;a href="http://codespeak.net/py/dist/test.html"&gt;py.test&lt;/a&gt; as testing framework here btw):
&lt;code&gt;&lt;pre&gt;
class TestSomething(object):
    @classmethod
    def setup_class(cls):
        cls._original_init_method = module.Klass.__init__
        module.Klass.__init__ = mock.Mock(return_value=None)

    @classmethod
    def teardown_class(cls):
        module.Klass.__init__ = cls._original_init_method

    def setup_method(self, method):
        self.inst = module.Klass()
        self.inst._Klass__log = mock.Mock()
        # more of this

    def test_method(self):
        self.inst.other_method = mock.Mock()
        self.app.method()
        assert self.inst.other_method.called
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;This is actually workable and I'm testing what I want to test in a pretty isolated way.  I'm still wondering whether I've gone insane or not tough.  Is it reasonable to replace &lt;tt&gt;.__init__()&lt;/tt&gt; by mock objects?  Have other people done this?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-7792282866754372228?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/7792282866754372228/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/12/mocking-away-init-method.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7792282866754372228'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/7792282866754372228'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/12/mocking-away-init-method.html' title='Mocking away the .__init__() method'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-3145590006394499089</id><published>2008-11-26T17:41:00.002Z</published><updated>2008-11-26T18:08:53.240Z</updated><title type='text'>Preserving modification times in subversion</title><content type='html'>&lt;p&gt;This is an oft requested feature apparently.  But who know if and when it will be there.  Usually things get done when someone has an itch to scratch, but given that no one has done this yet -but it's been requested for ages- it seems that it must either be really difficult or work arounds are easier.  Turns out that in my case a work around is &lt;em&gt;a lot&lt;/em&gt; easier.&lt;/p&gt;
&lt;p&gt;I need to build various bits of software on various platforms and they can have local modifications.  In the case of &lt;a href="http://python.org"&gt;Python&lt;/a&gt; for example we strip out the bit where it looks up the modules search path in the registry on Windows (instead of clobbering a hopefully unique string in the resulting binary by a hopefully non-existing registry key like py2exe does IIRC - I'm surprised that hasn't blow up in anyones face yet but nevermind).  Anyway, the point of this is that the natural format to store all of this in was in unpacked form in the revision control system, subversion in our case, so we can diff and merge as we like.  Problem with this is that when we check out the source on a build host all timestamps are lost and for some projects this messes up the makefile logic and things break down horribly &lt;em&gt;and randomly&lt;/em&gt; on the build hosts.  I got tired of finding this out every time and adding yet another random &lt;tt&gt;touch&lt;/tt&gt; to the build scripts so was looking for something better.&lt;/p&gt;
&lt;p&gt;Turns out that this is actually suprisingly easy to do.  I just store the timestamps of the unpacked tarball in a file, and call touch on all files first thing in our build script.  These shell functions are not bomb-proof yet (it won't cope with spaces in filenames e.g.) but do the job for now:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
create() {
    if [ -f .mtimes ]; then
        echo "E: .mtimes exists already!" &gt;&amp;2
        exit 1
    fi

    for file in $(find . -print); do
        mtime=$(stat --format=%y "$file")
        echo $file $mtime &gt;&gt; .mtimes
    done
}


restore() {
    if [ ! -f .mtimes ]; then
        echo "E: No .mtimes file found!" &gt;&amp;2
        exit 1
    fi

    while read file mtime; do
        # This would be the simple GNU option, POSIX however...
        #touch --date="$mtime" $file
        CCYY=$(echo $mtime | cut -d- -f1)
        MM=$(echo $mtime | cut -d- -f2)
        DD=$(echo $mtime | cut -d- -f3 | cut -d' ' -f1)
        hh=$(echo $mtime | cut -d' ' -f2 | cut -d: -f1)
        mm=$(echo $mtime | cut -d' ' -f2 | cut -d: -f2)
        SS=$(echo $mtime | cut -d' ' -f2 | cut -d: -f3 | cut -d. -f1)
        touch -t $CCYY$MM$DD$hh$mm.$SS $file
    done &lt; .mtimes
}
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Note that &lt;tt&gt;stat&lt;/tt&gt; is a GNU tool in the coreutils package, so &lt;tt&gt;create()&lt;/tt&gt; will only work on a GNU-based system.  &lt;tt&gt;restore()&lt;/tt&gt; however &lt;em&gt;should&lt;/em&gt; work on all POSIX compliant systems (and so far it seems to do so).&lt;/tt&gt;
&lt;p&gt;Only thing is that touching each and every file is a rather slow process...  And before you ask, I actually do the timestamp conversion at creation time as that happens less often, but this seems clearer.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-3145590006394499089?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/3145590006394499089/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/11/preserving-modification-times-in.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3145590006394499089'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3145590006394499089'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/11/preserving-modification-times-in.html' title='Preserving modification times in subversion'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-6661630869655697387</id><published>2008-09-16T20:48:00.002+01:00</published><updated>2010-01-19T15:01:35.465Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Generative Tests</title><content type='html'>&lt;p&gt;Both &lt;a href="http://somethingaboutorange.com/mrl/projects/nose/"&gt;nose&lt;/a&gt; and &lt;a href="http://codespeak.net/py/dist/test.html"&gt;py.test&lt;/a&gt; allow you to write &lt;em&gt;generative tests&lt;/em&gt;.  These are basically generators that yield the actual test functions and their arguments.  The test runners will then test all the yielded functions with their arguments.&lt;/p&gt;
&lt;p&gt;However I'm left wondering why this is better then just iterating in the test function and doing many asserts?  Let me demonstrate with the py.test example:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
def test_generative():
    for x in (42,17,49):
        yield check, x

def check(arg):
    assert arg % 7 == 0   # second generated tests fails!
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Why is that code better then:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
def test_something():
    for x in (42,17,49):
        assert x % 7 == 0
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;I know that it could possibly tell you slightly more, but in general I keep testing until all of my tests succeed so I don't really mind if one test includes more then one assert statement.  I'll just keep fixing things till the test passes.&lt;/p&gt;
&lt;p&gt;What is the motivation for generative tests?&lt;/p&gt;

&lt;p&gt;As an aside (and the inspiration for this post thanks to Holger
      Krekel talking about py.test), &lt;a href="http://www.pyconuk.org/"&gt;PyCon UK&lt;/a&gt; was great.  I was considering a post titled "&lt;a href="http://www.resolversystems.com/"&gt;Resolver Systems Ltd is Evil&lt;/a&gt; since they provided the Sunday morning hangovers, but thought that was a bit too sensationalist.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-6661630869655697387?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/6661630869655697387/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/09/generative-tests.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6661630869655697387'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6661630869655697387'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/09/generative-tests.html' title='Generative Tests'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1909044583713119383</id><published>2008-08-19T12:08:00.003+01:00</published><updated>2008-08-19T12:14:34.292+01:00</updated><title type='text'>Solaris equivalent of dpkg -S</title><content type='html'>&lt;p&gt;In the past I have spent quite some time trying to figure out what package did install a certain file on Solaris.  What I wanted was really just the equivalent of &lt;tt&gt;dpkg -S /path/to/file&lt;/tt&gt;.  But those searches where not very fruitfull, even talking to more experienced Solaris admins didn't help, they could only tell me how to find out what files where in a certain package.&lt;/p&gt;
&lt;p&gt;Today however, mostly by accident (I was trying to remember how to list the files in a package), I finally found it!&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
neptune:~# pkgchk -l -p /usr/local/bin/wget 
Pathname: /usr/local/bin/wget
Type: regular file
Expected mode: 0755
Expected owner: bin
Expected group: bin
Expected file size (bytes): 392572
Expected sum(1) of contents: 32029
Expected last modification: Nov 25 10:57:55 2006
Referenced by the following packages:
        SMCwget        
Current status: installed

neptune:~# 
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Wonderful&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1909044583713119383?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1909044583713119383/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/08/solaris-equivalent-of-dpkg-s.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1909044583713119383'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1909044583713119383'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/08/solaris-equivalent-of-dpkg-s.html' title='Solaris equivalent of dpkg -S'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1936175300889252870</id><published>2008-06-14T23:51:00.005+01:00</published><updated>2008-06-15T11:16:45.428+01:00</updated><title type='text'>Bluetooth on a Toshiba Tecra A9-127</title><content type='html'>&lt;p&gt;I got a &lt;a href="http://uk.computers.toshiba-europe.com/cgi-bin/ToshibaCSG/jsp/productPage.do?service=UK&amp;PRODUCT_ID=137102&amp;PRODUCT_ID=137102&amp;toshibaShop=true"&gt;Toshiba Tecra A9-127&lt;/a&gt;, listening to the model number of PTS52E-06002LEN as written on the back, from work to replace my dying old HP Compaq nx9030.  As my laptop OS of choice is &lt;a href="http://ubuntu.com"&gt;Ubuntu&lt;/a&gt;, currently in it's Hardy release, it's not completely coincidence that the hardware is almost all &lt;a href="http://intel.com"&gt;Intel&lt;/a&gt; based since that's what &lt;a href="http://video.google.com/videoplay?docid=7753780993269800999&amp;hl=en"&gt;Matthew Garrett recommends&lt;/a&gt;.  And indeed, it all works effortlessly!  Apart from bluetooth.&lt;/p&gt;
&lt;p&gt;For bluetooth you need a tool called &lt;a href="http://www.schwieters.org/toshset/"&gt;&lt;tt&gt;toshset&lt;/tt&gt;&lt;/a&gt;.  Once you have that you can enable the internal bluetooth device:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
flub@signy:~$ sudo toshset -bluetooth on
bluetooth: attached
flub@signy:~$
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;And all of a sudden you'll have an hci device, probably &lt;tt&gt;hci0&lt;/tt&gt;, check it with &lt;tt&gt;hciconfig -a&lt;/tt&gt; if you fancy.  Magic!  It's just like plugging in a USB dongle...&lt;/p&gt;
&lt;p&gt;Only toshset is not available for the amd64 flavour of hardy, only in the i386 version.  No panic though, Debian has an up to date package (Ubuntu intrepid also has the right 1.73 version but doesn't build it for amd64 yet - &lt;a href="https://bugs.launchpad.net/ubuntu/+source/toshset/+bug/240079"&gt;bug filled&lt;/a&gt;).&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
$ dget http://the.earth.li/debian/pool/main/t/toshset/toshset_1.73-2.dsc
$ dpkg-source -x toshset_1.73-2.dsc
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Don't quite rejoice yet, Debian seems to have changed from the &lt;tt&gt;pciutils-dev&lt;/tt&gt; package to &lt;tt&gt;libpci-dev&lt;/tt&gt;.  So go and edit &lt;tt&gt;debian/control&lt;/tt&gt; to build depend on &lt;tt&gt;pciutils-dev&lt;/tt&gt; again.  Then just build the package, install and enjoy.&lt;/p&gt;
&lt;p&gt;Hopefully someone will now spend less time then me figuring this out...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1936175300889252870?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1936175300889252870/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/06/bluetooth-on-toshiba-tecra-a9-127.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1936175300889252870'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1936175300889252870'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/06/bluetooth-on-toshiba-tecra-a9-127.html' title='Bluetooth on a Toshiba Tecra A9-127'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-3159881317820370779</id><published>2008-05-10T19:24:00.003+01:00</published><updated>2008-05-10T19:41:26.978+01:00</updated><title type='text'>Ripping videos from DVD</title><content type='html'>&lt;p&gt;Physical discs are a nuisance, I really just want to play what I want to watch in the room I want to watch it just streaming it over the wireless.  This actually works wonderfully well.  Unfortunately just copying the file structure of a DVD works and gives you DVD-quality video, but the size is huge and streaming this over the wireless tends to create some trouble (not to mention that every byte must also be encrypted/decrypted for ssh so this is getting CPU intensive too, ssh offloading onto hardware would be so cool).  Hence the need to "rip" the DVD and encode it to some smaller format arises.&lt;/p&gt;
&lt;p&gt;After spending a while looking at various options I finally found the great &lt;a href="http://thoggen.net/"&gt;thoggen&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Only problem left is how much to compress.  After some very un-scientific tests (Google failed to find me any nice studies/graphs!) I decided on a quality of 35 and no resizing.  But if anyone knows of a better study on what settings to prefer it would be greatly appreciated!  It would be great to see quality vs file size vs frame size for different types of video.  Ideally with a subjective "human quality" level too so you'd know what still looks good.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-3159881317820370779?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/3159881317820370779/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/05/ripping-videos-from-dvd.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3159881317820370779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3159881317820370779'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/05/ripping-videos-from-dvd.html' title='Ripping videos from DVD'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1610000325058812407</id><published>2008-05-09T11:31:00.001+01:00</published><updated>2010-01-19T15:01:35.465Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Discovery of the day</title><content type='html'>&lt;code&gt;&lt;pre&gt;
&gt;&gt;&gt; def f(a, b):
...     print 'a', a, 'b', b
... 
&gt;&gt;&gt; f(1, b=2)
a 1 b 2
&gt;&gt;&gt; f(a=1, b=2)
a 1 b 2
&gt;&gt;&gt; f(b=2, a=1)
a 1 b 2
&gt;&gt;&gt;
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;Now I'm debating if I should really use that in production code...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1610000325058812407?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1610000325058812407/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/05/discovery-of-day.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1610000325058812407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1610000325058812407'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/05/discovery-of-day.html' title='Discovery of the day'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-6817547197977343719</id><published>2008-04-17T08:45:00.002+01:00</published><updated>2008-04-17T08:48:08.715+01:00</updated><title type='text'>Time to read standards</title><content type='html'>&lt;p&gt;Sometimes I like quotes out of context...&lt;/p&gt;
&lt;blockquote&gt;
Anyway, I don't think it really is an ambiguity in practice -- only in the minds of those that have too much time to read standards documents.&lt;br/&gt;
    -- Greg Ewing (on distutils-sig)
&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-6817547197977343719?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/6817547197977343719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/04/time-to-read-standards.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6817547197977343719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6817547197977343719'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/04/time-to-read-standards.html' title='Time to read standards'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-140437538151694493</id><published>2008-04-11T20:49:00.002+01:00</published><updated>2010-01-19T15:01:35.466Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>@x.setter syntax in Python 2.5</title><content type='html'>&lt;p&gt;The new &lt;a href="http://docs.python.org/dev/whatsnew/2.6.html#other-language-changes"&gt;property.setter&lt;/a&gt; syntax in Python 2.6 and 3.0 looks very clean to me.  So I couldn't wait and wanted it in Python 2.5.  With the help of comp.lang.python I finally got there (thanks especially to  Arnaud Delobelle):&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
_property = property

class property(property):
    def __init__(self, fget, *args, **kwargs):
        self.__doc__ = fget.__doc__
        super(property, self).__init__(fget, *args, **kwargs)

    def setter(self, fset):
        cls_ns = sys._getframe(1).f_locals
        for k, v in cls_ns.iteritems():
            if v == self:
                propname = k
                break
        cls_ns[propname] = property(self.fget, fset,
                                    self.fdel, self.__doc__)
        return cls_ns[propname]
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;The &lt;tt&gt;__init__()&lt;/tt&gt; wrapper is needed to get &lt;tt&gt;__doc__&lt;/tt&gt; set properly (this surprised me).  The whole &lt;tt&gt;cls_ns&lt;/tt&gt; stuff and the loop are required since the properties are defined in C and their &lt;tt&gt;fset&lt;/tt&gt; attribute is read-only.  Which is why the entire property needs to be replaced.  The implementation of &lt;tt&gt;deleter()&lt;/tt&gt; can now be regarded as an exercise to the reader...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-140437538151694493?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/140437538151694493/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/04/xsetter-syntax-in-python-25.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/140437538151694493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/140437538151694493'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/04/xsetter-syntax-in-python-25.html' title='@x.setter syntax in Python 2.5'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1964261822418034948</id><published>2008-04-11T20:42:00.002+01:00</published><updated>2008-04-11T20:48:57.114+01:00</updated><title type='text'>(O)OXML and ISO voting processes</title><content type='html'>&lt;p&gt;Many have recently complained about ISO's voting processes, mainly how they need to be revised as currently it seems it's possible to buy yourself a standard given enough lobbyist and money.&lt;/p&gt;
&lt;p&gt;But part of this is ISO's trust in ECMA.  ISO allows ECMA to submit standards for the fast-track process because it trusts it to approve good standards.  If OXML (as it seems to be called now it's approved, formerly OOXML) was indeed such a bad standard (which I don't doubt personally) then ISO should maybe review it's relationship with ECMA too?&lt;/p&gt;
&lt;p&gt;This is only a tiny part of the picture obviously, but one I haven't seem mentioned elsewhere.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1964261822418034948?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1964261822418034948/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/04/ooxml-and-iso-voting-processes.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1964261822418034948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1964261822418034948'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/04/ooxml-and-iso-voting-processes.html' title='(O)OXML and ISO voting processes'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-4972913515494589654</id><published>2008-04-10T13:57:00.002+01:00</published><updated>2008-04-10T14:03:08.636+01:00</updated><title type='text'>Shell history</title><content type='html'>&lt;p&gt;hehe&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
flub@signy:~$ history | awk '{a[$2]++ } END{for(i in a){print a[i] " " i}}'|sort -rn |head
79 nosetests
74 ls
46 cd
36 ssh
28 man
22 apt-cache
19 vi
19 ack
17 svn
17 sudo
flub@signy:~$ 
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;It must be noted that most of my editing happens in emacs, but that only gets started a few times a day and then stays there (oh and emacs is started from a shortcut icon, not the shell).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-4972913515494589654?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/4972913515494589654/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/04/shell-history.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4972913515494589654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/4972913515494589654'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/04/shell-history.html' title='Shell history'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-5054045621818455914</id><published>2008-04-06T13:36:00.002+01:00</published><updated>2010-01-19T15:01:35.466Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>nose and ipython</title><content type='html'>&lt;p&gt;I wish there was a simple way to integrate nose and ipython.  Something like a command line option &lt;em&gt;not&lt;/em&gt; to catch exceptions would be sufficient I think, so that you could do:&lt;/p&gt;
&lt;code&gt;
In [1]: %run tests/test_module.py --do-not-catch-exceptions
&lt;/code&gt;
&lt;p&gt;Obviously you'd want a shorter option switch...&lt;/p&gt;
&lt;p&gt;Seems like &lt;tt&gt;Test.run()&lt;/tt&gt; in &lt;tt&gt;case.py&lt;/tt&gt; is where this happens, but I tried changing that with no success.&lt;/p&gt;
&lt;p&gt;And to be really useful I'd want the default behaviour of test selection in a module where &lt;tt&gt;if __name__ == '__main__': nose.main()&lt;/tt&gt; is used to be just that module.  But maybe that's already supported and I'm just not finding it.&lt;/p&gt;
&lt;p&gt;Last random thought: Maybe if nose's -d option did expand dotted names I wouldn't be wishing for any of this.  Who knows.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-5054045621818455914?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/5054045621818455914/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/04/nose-and-ipython.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5054045621818455914'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/5054045621818455914'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/04/nose-and-ipython.html' title='nose and ipython'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-313886604941815890</id><published>2008-04-03T12:38:00.003+01:00</published><updated>2008-04-03T12:47:27.249+01:00</updated><title type='text'>One s3fs to rule them all?</title><content type='html'>&lt;p&gt;Sometimes I wish we could fast forward 6 or 12 months or so.  Hopefully by then there will be just one &lt;a href="http://google.com/search?q=s3fs"&gt;s3fs&lt;/a&gt; that is mature and maintained.  Right now it's hard to tell which one will turn out the best.&lt;/p&gt;
&lt;p&gt;Interstingly, of the ones that look like they could have potential (trying to word it carefully here) two are written in &lt;a href="http://python.org"&gt;Python&lt;/a&gt; (all are using &lt;a href="http://fuse.sourceforge.net/"&gt;fuse&lt;/a&gt;).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/s3fs/wiki/FuseOverAmazon"&gt;http://code.google.com/p/s3fs/wiki/FuseOverAmazon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/s3fs-fuse/"&gt;http://code.google.com/p/s3fs-fuse/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fedorahosted.org/s3fs/"&gt;https://fedorahosted.org/s3fs/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-313886604941815890?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/313886604941815890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/04/one-s3fs-to-rule-them-all.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/313886604941815890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/313886604941815890'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/04/one-s3fs-to-rule-them-all.html' title='One s3fs to rule them all?'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-6091146420804320881</id><published>2008-03-28T23:42:00.003Z</published><updated>2010-01-19T15:01:35.466Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>GPL and python modules</title><content type='html'>&lt;p&gt;I should probably google this, but can't think of good keywords.  So instead this is a "dear lazy web" post.&lt;/p&gt;
&lt;p&gt;Is importing a python module considered linking for the GPL?  I.e. are you allowed to import a python module into an script or module that has a non-GPL-comptible license?&lt;/p&gt;
&lt;p&gt;Thanks!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-6091146420804320881?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/6091146420804320881/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/03/gpl-and-python-modules.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6091146420804320881'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/6091146420804320881'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/03/gpl-and-python-modules.html' title='GPL and python modules'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-3162343668016894383</id><published>2008-03-27T21:35:00.002Z</published><updated>2008-03-27T21:39:40.999Z</updated><title type='text'>Cheating the KAME turtule</title><content type='html'>&lt;p&gt;This is shocking.  Even though I have not yet used IPv6 I can see the the KAME turtle dance!  I won't give you a direct link but &lt;a href="http://sixxs.net"&gt;SixXS&lt;/a&gt; privde an IPv6-&gt;IPv4 service as well as an IPv4-&gt;IPv6 service.  Feels bad having used the 4-&gt;6 bit...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-3162343668016894383?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/3162343668016894383/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/03/cheating-kame-turtule.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3162343668016894383'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/3162343668016894383'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/03/cheating-kame-turtule.html' title='Cheating the KAME turtule'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-8839245039211089480</id><published>2008-03-23T18:54:00.000Z</published><updated>2008-03-23T18:55:05.333Z</updated><title type='text'>OmniORB 4.1.1 in lenny!</title><content type='html'>&lt;p&gt;Something that happened about a week ago I think.  But omniORB 4.1.1 finally make it into Debian testing aka lenny! This means that python-omniorb 3.1, which has been waiting for a while now, also make it into lenny!&lt;/p&gt;
&lt;p&gt;It has been stalled for ages since we had problems with the ARM port not managing to compile omniORB 4.1.X.  Thanks to Thomas Girard for his work on this.&lt;/p&gt;
&lt;p&gt;I've already committed the update to 4.1.2 to the &lt;a href="http://svn.debian.org/wsvn/pkg-corba"&gt;svn repo&lt;/a&gt;, omniORBpy 3.2 will follow soon...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-8839245039211089480?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/8839245039211089480/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/03/omniorb-411-in-lenny.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8839245039211089480'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/8839245039211089480'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/03/omniorb-411-in-lenny.html' title='OmniORB 4.1.1 in lenny!'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14094861.post-1977606605742731479</id><published>2008-03-23T18:23:00.002Z</published><updated>2008-03-23T18:29:37.247Z</updated><title type='text'>Compiling libtool examples</title><content type='html'>&lt;p&gt;This is more a reminder to myself, but here's how to compile the libtool examples:&lt;/p&gt;
&lt;code&gt;&lt;pre&gt;
$ cd demo
$ aclocal
$ libtoolize --automake
$ automake --add-missing
$ autoconf
$ ./configure
$ make
&lt;/pre&gt;&lt;/code&gt;
&lt;p&gt;It's really how to bootstrap any GNU autotools using application I suppose.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14094861-1977606605742731479?l=blog.devork.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.devork.be/feeds/1977606605742731479/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.devork.be/2008/03/compiling-libtool-examples.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1977606605742731479'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14094861/posts/default/1977606605742731479'/><link rel='alternate' type='text/html' href='http://blog.devork.be/2008/03/compiling-libtool-examples.html' title='Compiling libtool examples'/><author><name>Floris Bruynooghe</name><uri>http://www.blogger.com/profile/10253047282079078146</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-lIPplmIFnCE/TsP3XrM8c1I/AAAAAAAACdQ/a1xCCs3bK3w/s220/headshot_square_bw.jpg'/></author><thr:total>2</thr:total></entry></feed>
