SwapOff

Python hacking, redux. 

A Python script for testing XPath expressions

I often find myself wanting to quickly test XPath expressions, so I whipped up a quick script to do so. Usage is simple:

$ cat test.html
<html>
<body>
  Hello ${name}.
  <div id="foo">Foo</div>
  <form id="login">
    <label for="username">Username: </label> <input id="username" />
  </form>
</body>
</html>
$ xpathsh test.html '//form[@id="login"]/label[@for="username"]/text()'
Username:

And here's the script itself:

import sys
import optparse

from lxml import etree

parser = optparse.OptionParser(usage='usage: %prog <file> <xpath>')
options, args = parser.parse_args()

if len(args) < 2:
    parser.error('Need at least a file and an X-Path.')

file = args.pop(0)
xpath = args.pop(0)
tree = etree.parse(open(file))
for match in tree.xpath(xpath):
    try:
        text = etree.tostring(match)
    except TypeError:
        text = str(match)
    print text.strip()

Loading mentions Retweet

Comments [0]

Google as an application navigation system

I was trying to figure out how to make the fonts in Firefox look less ugly today - and failing. But while failing, I realised that I no longer even bother with trying to figure out how an application lays out its configuration dialogs. I make a cursory attempt in the location I think is most likely, and if I don't find it I use Google. If I hit the right search terms, I have the exact location of the option I need.

This happens so frequently that it wouldn't surprise me if applications started leveraging this somehow. A builtin help system backed by Google?

Loading mentions Retweet

Comments [0]

Hackers and inventors

During the writing of this post I wanted to look up a synonym for the word "efficient", having been a little too liberal in its use. Context switching out to an online thesaurus from GMail is disruptive, so my first thought was some kind of Firefox plugin for looking up synonyms, or perhaps a simple Ubiquity plugin might do the trick?

Making a cup of tea this morning, I became frustrated with the process of walking out to the kitchen to start the kettle, walking back to my desk to continue working while the kettle boils, walking back again to pour the tea, waiting while the tea steeped, then finally removing the tea bag and pouring the milk. How could I make this less time consuming? Perhaps a kettle that pours water into your cup after boiling? Perhaps a cup that boils the water then drops the tea bag in? Perhaps use a microwave? Perhaps I should just treat the time as a respite from work :)

The common ground between these two scenarios is trying to make some task more efficient, whatever the motivation (in this case, laziness). This, I think, is the core of invention, and far from being the sole domain of a select few, I suspect the inventive process is an intrinsic part of being human:

I'll put my most frequently used clothes in the top drawer, least used in the bottom. Less day to day effort!

Perhaps the difference between this, and someone being labelled an inventor, is simply having the will to follow through and create.

Loading mentions Retweet

Comments [0]

Simplifying PyGTK signals in Python

This is a followup to my previous post.

A fairly common pattern in GTK code seems to be updating a widget whenever a value changes in your controller. That is, something like this:

class Counter(gobject.Gobject):
  __gsignals__ = {
    'counter-changed': (gobject.SIGNAL_RUN_FIRST,
                        gobject.TYPE_NONE, (int,)),
    }

  def __init__(self):
    self.counter = 0

  def inc(self):
    self.counter += 1
    self.emit('counter-changed', self.counter)

  def dec(self):
    self.counter -= 1
    self.emit('counter-changed', self.counter)


Needless to say, this can get a bit tedious. With a bit of stack frame hackery we can reduce that boilerplate to the following:

class Counter(gobject.GObject):
  counter = SignalProperty('counter-changed', 0)

  def inc(self):
    self.counter += 1

  def dec(self):
    self.counter -= 1


Here's the code:

class SignalProperty(object):
  def __init__(self, name, default=None):
    self.name =  name
    self.default = default
    frame = sys._getframe(1)
    locals_ = frame.f_locals
    signals = locals_.setdefault('__gsignals__', {})
    signals[name] = (gobject.SIGNAL_RUN_FIRST,
                     gobject.TYPE_NONE, (object,))

  def __get__(self, instance, owner):
    if instance is None:
      return self
    return getattr(instance, '_signal_' + self.name, self.default)

  def __set__(self, instance, value):
    setattr(instance, '_signal_' + self.name, value)
    instance.emit(self.name, value)



Loading mentions Retweet
Filed under  //   gtk   python   signals  

Comments [0]

Decoupling UI from logic with PyGTK

Last week I set out to learn PyGTK by doing. Quickly. As can be imagined, the resulting code was less than stellar, but I did learn a lot.

By far the most glaring design error I made was in not completely decoupling my UI objects from "business" logic, ala MVC. This failure was the result of not totally comprehending the role signals should play in GTK applications. I made the first step correctly, with distinct objects for the UI panel and logic, but sadly that was the last smart choice I made. My next was deciding how to update the UI when the state of the system changed. "Hmm", I thought. "If I just pass the appropriate UI control through to the logic layer, I can update the view as necessary! Brilliant!". BZzzzzzzzzzzzt! Wrong.

The right approach in GTK is to use signals to notify the UI of changes to your objects. To do this, inherit from gobject.Gobject and add a __gsignals__ attribute. Here's a mocked up example of a simulation that emits a "simulation-step" signal every time the simulation is updated. To update a hypothetical counter widget we simply connect the signal to a callback that updates the widget.

class Controller(gobject.GObject):
  __gsignals__ = {
    'simulation-step': (gobject.SIGNAL_RUN_FIRST,
                        gobject.TYPE_NONE, (int,)),
  }

  def step_simulation(self, step):
    self.emit('simulation-step', step)

class View(object):
  def __init__(self, builder, controller):
    # "builder" is a Glade gtk.Builder object.
    builder.connect_signals(self)
    self.step_counter = builder.get_object('step_counter')
    controller.connect('simulation-step', self.on_simulation_step)

  def on_simulation_step(self, simulation, step):
    self.step_counter.set_value(step)

controller = Controller()
view = View(controller)


The fundamental mistake I made was assuming that GTK signals were purely for notifying my application of UI changes. In reality, signals are equally important in notifying the UI of application state changes.

Loading mentions Retweet
Filed under  //   design   gtk   mvc   python   signals  

Comments [0]

Python "multiprocessing" and sockets

It seems obvious in retrospect (doesn't it always), but using the multiprocessing module doesn't exempt you from the fundamental laws of UNIX. Laws such as "forks duplicate file descriptors". Consequently, when you launch a multiprocessing.Process() and pass it a socket, ensure that you close the socket in the parent. If you don't, it will stay open after the child has closed it, resulting in leaking badness.

Loading mentions Retweet
Filed under  //   multicore   network   processes   python  

Comments [0]

Python Finite State Machine

There's a pretty good implementation of an abstract FSM class available on ASPN.

(posted here to promote nice solid code)

Loading mentions Retweet
Filed under  //   fsm   python  

Comments [0]

Multi-threading and PyGTK

The "easiest" way of integrating threads with PyGTK applications seems to be by using gobject.

Initialisation before starting the GTK main loop:

import gobject
gobject.threads_init()

Schedule any rendering functions from your thread with:

gobject.idle_add(window.queue_draw)

Also useful is the timeout_add function, which schedules periodic callbacks:

gobject.timeout_add(250, window.queue_draw)

Loading mentions Retweet
Filed under  //   gtk   multicore   python   threads  

Comments [6]