jonEbird

February 19, 2012

Automatically positioning and sizing windows with Devilspie

Filed under: adminstration,blogging,linux — jonEbird @ 1:48 pm

I often talk about my media PC in the living room and how I like to stream music via Pandora in the house. Since I am using Pithos, I can maximize the desktop real estate and enjoy whatever wallpaper suits my mood. In order to best accomplish this, I tend to resize Pithos so that it only shows the current album and song as well as move it off to the corner of the desktop. Although this only takes a moment to accomplish, I’ve grown tired of doing it each and every time I need relaunch Pithos. What I really want to be able to do is automatically resize and position the window immediately when I launch Pithos. Someone has to have solved this before and it’s bugging me enough that I need to solve this once and for all.

I started looking at wmctrl to solve my problem per the recommendation of Patrick Shuff. While familiarizing myself with the utility, I ironically stumbled upon a wmctrl tips page which included a list of other similar tools and the description of devilspie sounded exactly like what I needed:

devilspie is a window-matching utility. It can be configured to detect windows as they are created, and match the window to a set of rules. If the window matches the rules, it can perform a series of actions on that window.

My needs are modest. A resize and a positioning is probably accomplished with a simple declaration to set the window geometry. Let’s install it and start playing.

Fortunately I could install devilspie directly from yum and while the manpage is quite sparse it is packaged with a sufficient README that after a few trial and error iterations, I got a basic configuration working which just ran devilspie in debug mode and could report on the current windows opened and new ones being launched.

sudo yum -y install devilspie
[ ! -d ~/.devilspie ] && { mkdir -m 0755 ~/.devilspie; echo "(debug)" > ~/.devilspie/debug.ds; }
devilspie

That will continue to run in your terminal, so you may want to open another tab/terminal as we explore.

Since I am a devoted Emacs user, I certainly wasn’t disappointed to learn that devilspie uses s-expressions for it’s configurations. I also found a good unofficial devilspie documentation page written up by Gina Häußge which helps round out the education. Indeed, it looks like I’ll be able to solve my problem with devilspie. After meticulously positioning my Pithos window, I used xwininfo to record my current window geometry. I then created the simplistic config file “~/.devilspie/pithos.ds” as the following:

(if (is (application_name) "Pithos") (begin (geometry "481x163--20-17") (unshade) ))

Some more background on setting up your own configurations. If you notice when you launched devilspie, after the initial install, you can see the information on the current windows and their associated application names. For example, here is what I see for Pithos:

Window Title: 'Pithos - Bankrupt On Selling by Modest Mouse'; Application Name: 'Pithos'; Class: 'Pithos'; Geometry: 483x197+797+523

That should make it easy for coming up with rules to match application names, window names or classes when targeting your applications.

The frustrating thing for me was how each utility was reporting a different geometry value for the windows currently launched. Between xwininfo, devilspie and wmctrl they all had differing opinions on what I should use. You’d think that I could use what devilspie was telling me since that is the utility I’m indented on using but it didn’t work out for me. I’ve used xinwinfo for over a decade and I guess I’m sticking with it.

Finally, I added a final few touches to my desktop configuration before calling it complete. I created a simple shell script to relaunch Pithos indefinitely because occasionally I need to relaunch it but I actually never intend to leave it off: (See installing pithos with virtualenv if you want to see how I installed Pithos)

#!/bin/bash

PITHOS_HOME=~/pithos
PITHOS_VENV=~/pithos_venv

#--------------------------------------------------
source ${PITHOS_VENV}/bin/activate
cd $PITHOS_HOME
while :; do
    pithos
    sleep 1
done

My cheap way of launching apps indefinitely is to run them within my main screen session. I’ll create one for the Pithos restart script and another for devilspie. Here is an excerpt from my ~/.screenrc:

screen -t emacs     0  /usr/bin/emacs -nw
screen -t local     1  /bin/bash
screen -t local     2  /bin/bash
screen -t local     3  /bin/bash
screen -t local     4  /bin/bash
screen -t local     5  /bin/bash
screen -t synergy   10 /usr/bin/synergyc -f 192.168.1.23:6700
screen -t devilspie 11 /usr/bin/devilspie
screen -t pithos    12 ~/bin/run_pithos.sh

And all is well in my household again. Should something get borked with Pithos, perhaps due to being paused for too long or whatever, I can simply kill the window which triggers the restart script to launch another copy and devilspie will position and resize it perfectly for me. How will you use devilspie?

February 7, 2012

Python Memoize Decorator with TTL Argument

Filed under: blogging,linux,python — jonEbird @ 8:51 pm

I have been working on a troubleshooting effort on and off over the past couple of weeks. In the process of troubleshooting, I ended up writing a script to query and report on un-read buffered data for sockets across the system and today I wanted to correlate the sockets to a particular set of processes. That turned out to be not so bad knowing I could look down /proc/<pid>/fd/ for a list of the current file descriptors the particular process has open. Here is my winning Python implementation.

def get_pid_socket_nodes(pid):
    """Return a list of socket device numbers for the given process ID (pid)
    pid may be an integer or a string
    Akin to: ls -l /proc/
/fd/ | sed -n 's/^.*socket:\[\([0-9]*\)\]$/\1/p'
    """
    nodes = []
    for fd in os.listdir('/proc/%s/fd' % pid):
        link = os.readlink('/proc/%s/fd/%s' % (pid, fd))
        if link.startswith('socket:['):
            nodes.append(link[8:-1])
    return nodes

What I haven’t told you is the overall program may be scanning /proc/net/{tcp,udp} a lot and with each scan I’ll want to correlate values found with a particular process’s sockets. That means a straight forward implementation could mean calling my helper function get_socket_devices() at each interval for each process of interest. Also, the interval is currently controlled by a sleep interval specified by the caller. If the caller wants to monitor for socket information at an 0.1s sleep interval for five processes, I’ll be scanning /proc/<pid>/fd/ entries 50 times a second. Sure, it won’t crash the machine but it’s certainly a waste of resources assuming the processes you are interested in aren’t closing and opening sockets at an alarming rate.

I wanted to reduce the amount of /proc/<pid>/fd/ scans but I also did not want to clutter the code. “Ah ha, what I want is a memoize decorator”, I told myself, but I want to be able to specify a time-to-live (ttl) value to my decorator. Admittedly, I have only ever needed to write decorators without arguments, so this was a first for me. I also don’t write enough decorators to be able to write one correctly without looking at a sample implementation for a refresher. My implementation is basically a merge between Memoize and Cached Properties from the Python Decorator Library wiki page. (I also spent some time re-reading Bruce Eckel’s Decorator Arguments writeup as well as Elf Sternberg’s Decorators With Arguments writeup.)

class memoized_ttl(object):
    """Decorator that caches a function's return value each time it is called within a TTL
    If called within the TTL and the same arguments, the cached value is returned,
    If called outside the TTL or a different value, a fresh value is returned.
    """
    def __init__(self, ttl):
        self.cache = {}
        self.ttl = ttl
    def __call__(self, f):
        def wrapped_f(*args):
            now = time.time()
            try:
                value, last_update = self.cache[args]
                if self.ttl > 0 and now - last_update > self.ttl:
                    raise AttributeError
                #print 'DEBUG: cached value'
                return value
            except (KeyError, AttributeError):
                value = f(*args)
                self.cache[args] = (value, now)
                #print 'DEBUG: fresh value'
                return value
            except TypeError:
                # uncachable -- for instance, passing a list as an argument.
                # Better to not cache than to blow up entirely.
                return f(*args)
        return wrapped_f

Now let’s put the decorator into use and test it. (My testing is uncommenting the “print DEBUG” lines from the memoized_ttl decorator.) To use the decorator, you use the standard decorator calling syntax and specify your desired ttl. I am restricting the /proc/<pid>/fd/ scans to a minute interval which, based on my experience, will drop the load of the script down to below the radar.

@memoized_ttl(60)
def get_pid_socket_nodes(pid):
    """Return a list of socket device numbers for the given process ID (pid)
    pid may be an integer or a string
    Akin to: ls -l /proc/
/fd/ | sed -n 's/^.*socket:\[\([0-9]*\)\]$/\1/p'
    """
    nodes = []
    for fd in os.listdir('/proc/%s/fd' % pid):
        link = os.readlink('/proc/%s/fd/%s' % (pid, fd))
        if link.startswith('socket:['):
            nodes.append(link[8:-1])
    return nodes

And finally, here is what it looks like looking at a couple of processes of mine with open sockets:

>>> from buffered_sockets import *
>>> pid = 23283
>>> pid2 = 23279
>>> get_socket_devices(pid)
DEBUG: fresh value
['46287188']
>>> get_socket_devices(pid)
DEBUG: cached value
['46287188']
>>> get_socket_devices(pid2)
DEBUG: fresh value
['46287165']
>>> get_socket_devices(pid2)
DEBUG: cached value
['46287165']
>>> time.sleep(60)
>>> get_socket_devices(pid)
DEBUG: fresh value
['46287188']
>>> get_socket_devices(pid)
DEBUG: cached value
['46287188']
>>>

A handy decorator to keep around which I suspect will get a lot of mileage from other scripts I end up authoring. Aside from the helpful blog posts listed above, I also found Will McGugan’s Timed Caching Decorator page after writing this entire blog post. Apparently I need to improve my google searching skills because I certainly didn’t want to re-invent what others have already completed. On the other hand, if you spend this much time creating something for the first time, you tend to remember the lessons better. I’ll take that.

Upgrading WordPress to 3.3.1

Filed under: blogging,emacs,usability — jonEbird @ 8:36 pm

Upgrading WordPress tonight from version 2.6.2 to the latest version 3.3.1. About time, right? Funny enough, my motivation was when I was configuring org2blog and noticed the URLs it was trying to hit were non-existent. That was my first clue that I needed to update. For the most users, I’d suggest following the very well documented Upgrading WordPress documentation, but I thought I’d show you how I do it. (Read: This post is pretty much for my own reference for the next time)

  1. Grab the latest wordpress release
upgrade_dir=wordpress_upgrade_$(date +%Y%m%d)
mkdir ~/${upgrade_dir} && cd ~/${upgrade_dir}
wget http://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
  1. Following along with the Upgrading WordPress documentation.
  2. Take a backup of the current install.
mysqldump -u root -p wordpress > wordpress.dump
# Using the contents of the extracted latest.tar.gz WordPress release as my tarball file list
\ls wordpress | xargs tar -C /var/www/html -czf wordpress_backup.tar.gz
  1. Now for the actual manual upgrade
WPH="/var/www/html/" # My WordPress Home (WPH)
rm -rf ${WPH}wp-{includes,admin}
rsync -av wordpress/wp-includes ${WPH}/
rsync -av wordpress/wp-admin ${WPH}/
rsync -av wordpress/wp-content ${WPH}/
(cd wordpress; for f in *; do if [ -f "${WPH}/${f}" ]; then echo "Updating $f"; cat "${f}" > "${WPH}/${f}"; fi; done)
  1. Re-log into your site and proceed with the DB upgrade.
    Since my versions were so far off, the first thing I was greeted with upon login was a button to update the DB.

All done. The good news is I can now successfully log into my blog via org2blog plugin. Here is my elisp excerpt:

;; Setting up org2blog (Installed via ELPA)
(require 'org2blog-autoloads)
(require 'netrc)
(setq blog (netrc-machine (netrc-parse "~/.netrc") "jonebird" t))
(setq org2blog/wp-blog-alist
      '(("jonebird.com"
         :url "http://jonebird.com/xmlrpc.php"
         :username (netrc-get blog "login")
         :password (netrc-get blog "password")
         :tags-as-categories t)))
(org2blog/wp-login)

Now to post this, I believe I’m supposed to M-x org2blog/wp-post-subtree. Well, that worked well and posted this as a draft. I noticed a few markup problems and after fixing them, I can finalize the posting via C-u M-x org2blog/wp-post-subtree. 2012-02-07 Tue 20:36