I’m a shortcut freako

I like shortcuts, keyboard shortcuts. I like javascript. I have greasemonkey installed. I navigate lots of sites with buttons/links in uncomfortable positions. Therefore, I must definitely do something about it !

I’ve made a userscript that offers me a way to bind a key combination to a clickable element chosen by a xpath expression. It still misses a way to easy edit previous bindings but I’ll provide it later, I promise!

You can still edit the bindings (well, more likely remove them) via about:config (the ‘greasemonkey.scriptvals.ShortcutBinder/Shortcut Binder.bindings’ setting).

Grab the script from here.

For a quick overview (bunch of screenshots) check this: http://code.google.com/p/webmonkey-userscripts/wiki/ShortcutBinder

Ah heck, here’s one quick and dirty:

Finished my formal CS studies – quite disappointing

Finally ! After 4 years of half extremely boring lab-work and excruciating math exams i get a Computer Science diploma. Fuckintastic !

Was it worth it ? Well, for one thing, i get a diploma – we’ll see if that really helps me at anything at all. But did it learnt me anything? Little.

Set aside the maths this faculty didn’t learn me shit. You don’t learn advanced stuff – from functional to unix programming everything is introductory stuff – you can finish the faculty with your head empty and your time wasted, easy. Just for a damn diploma.

You’ll even have to write a project for the diploma thesis, you’ll have to present it and at the end you’ll find yourself very unsatisfied because your audience will be clueless (they didn’t even know what a coroutine is, damn it !) and uninterested (they won’t even bother to read your paper that took time and effort).

I doubt working though the pile of arrogant, bored teachers – some not even deserving their degree – that don’t give a shit about you was worth giving 4 years of my life. I’m not saying not to do it – I’m saying to have very low expectations.

First flex css hack

Well, might be not, might be the fact i’m just doing it wrong. I’m using flex 2.

So here is the problem: I want to add sounds to buttons and whatever from css in a similar fasion to skinning.  I have a mess of all sorts of buttons everywhere and I don’t want to patch all of them with a mouseOverEffect, mouseUpEffect or whatever.

I would think having a <mx:SoundEffect source="@Embed(source='blabla.mp3')" id="foo" /> would make this snippet work (but it doesn’t):

Button {
    mouseOverEffect: foo; /* neither does "foo" */
}

So what to do ? StyleManager and CSSStyleDeclaration perhaps.

var myDynStyle:CSSStyleDeclaration;
myDynStyle = new CSSStyleDeclaration('Button');
myDynStyle.setStyle("mouseUpEffect", Sounds.clickButtonEffect);
StyleManager.setStyleDeclaration("Button", myDynStyle, true);

This is very cool. Allows me to set behaviors (via Effects) on whatever object classes.
Except it doesn’t work.

I’ve discovered by accident that setting those effect props to some bogus values in the css beforehand (like Button { mouseUpEffect: none; }) makes that programmatic styling work.

I wonder if this is a flex bug, though by the looks of it seems that is a compile-time vs run-time issue – some stuff that makes the effect work on the buttons is setted only on compile time?

Rewrite temptation

Well, not exactly a rewrite, merely a major internal refactor.

The thought of cleaning up the socket and reactor internals really nags me. I would move the platform dependant code from the sockets module in the reactors – so, say, the reactors would have the platform specific code for recv, send, connect, accept instead of each socket operation class. Also, the fact is that the iocp was added later and the code was patched to support a what was a design scheme based on a reactor pattern – so adding support for another proactor based api (say, linux’s aio) would really mudge the internals. Also, moving the readline stuff out of the socket operations in a coroutine would make things real fancy and easy to follow.

Meh, on second thought, i would not have real gain, besides making more easy to add support for another proactor api like aio (wich btw just sucks, it wasn’t made for sockets – no support for accept or connect) and priding myself for the fancier internals i would probably get lower performance – and I still have yet to fine tune the performance part.

Now that I have relieved myself I’d better concentrate myself on that diploma thesis. =]

ClearType is for color blind people

I was going to title it as ‘ClearType sucks’ but anyway.

ClearType uses a technique named subpixel rendering that absolutely sucks – at least for me. Say for example i have some black text on white background then the subpixel rendering will make some adjacent pixels have some colors. Take a example, Consolas, a font that works only with ClearType:
Consolas ClearType sample
That just looks painful for my eyes, i see every colored pixel in something that should had been only black and white (or gray at least) – it’s just friggin painful. If you don’t notice it (well, I might see better than you) look here:
What\'s with all the colors?!
And I’m not just running on crap hardware – I think a Dell 2407WPF-HC is good enough !

I’ve also tried to tune the ClearType a bit and it still looks like crap.

comet chat in pylons (with cogen)

There is something very cool about wsgi: asynchronicity at it’s core ! The spec was made with this in mind – I absolutely love wsgi.

I’ve been playing recently with pylons and i’ve made a example chat app – just a proof-of-concept comet application with long pooling in a pylons app using a custom wsgi server (cogen.wsgi).

Here’s the controller code that give me a feeling I will be burned to the stake for too much magic. =]

import logging

from pylons import request, response, session
from pylons import tmpl_context as c
from pylons.controllers.util import abort, redirect_to, url_for

from chatapp.lib.base import BaseController
# import chatapp.model as model

log = logging.getLogger(__name__)
from cogen.core import queue, events
from cogen.core.coroutines import coro
from cogen.core.pubsub import PublishSubscribeQueue
pubsub = PublishSubscribeQueue()

class Client:
    def __init__(self):
        self.messages = queue.Queue(10)
        self.dead = False
    @coro
    def watch(self):
        """This is a coroutine that runs permanently for each participant to the
        chat. If the participant has more than 10 unpulled messages this
        coroutine will die.

        `pubsub` is a queue that hosts the messages from all the
        participants.
          * subscribe registers this coro to the queue
          * fetch pulls the recent messages from the queue or waits if there
        are no new ones.

        self.messages is another queue for the frontend comet client (the
        pull action from the ChatController will pop messages from this queue)
        """
        yield pubsub.subscribe()
        while 1:
            messages = yield pubsub.fetch()
            try:
                yield self.messages.put_nowait(messages)
            except:
                print 'Client %s is dead.' % self
                self.dead = True
                break
class ChatController(BaseController):

    def push(self):
        """This action puts a message in the global queue that all the clients
        will get via the 'pull' action."""
        yield request.environ['cogen.call'](pubsub.publish)(
            "%X: %s" % (id(session['client']), request.body)
        )
        # the request.environ['cogen.*'] objects are the the asynchronous
        # wsgi extensions offered by cogen - basicaly they do some magic to
        # make the code here work as a coroutine and still work with any
        # middleware
        yield str(request.environ['cogen.wsgi'].result)

    def pull(self):
        """This action does some state checking (adds a object in the session
        that will identify this chat participant and adds a coroutine to manage
        it's state) and gets new messages or bail out in 10 seconds if there are
        no messages."""
        if not 'client' in session or session['client'].dead:
            client = Client()
            print 'Adding new client:', client
            session['client'] = client
            session.save()
            yield request.environ['cogen.core'].events.AddCoro(client.watch)
        else:
            client = session['client']

        yield request.environ['cogen.call'](client.messages.get)(timeout=10)

        if isinstance(request.environ['cogen.wsgi'].result, events.OperationTimeout):
            pass
        elif isinstance(request.environ['cogen.wsgi'].result, Exception):
            import traceback
            traceback.print_exception(*request.environ['cogen.wsgi'].exception)
        else:
            yield "%s\r\n"% '\r\n'.join(request.environ['cogen.wsgi'].result)

The frontend code is fairly simple:

    <h1>chat powered by <a href="http://code.google.com/p/cogen/">cogen</a></h1>
    <form name="chat">
    <textarea id="ouput" name="output" rows="10" cols="80" readonly="readonly"></textarea><br/>
    <input id="tosend" name="tosend" value="" size="80"/>
    </form>
<script language="javascript">
    function xhr(url, callback, data) {
        var req = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');
        req.onreadystatechange = function() {
            if (req.readyState == 4) {
                if (callback) callback(req);
            }
        }
        req.open(data?"POST":"GET", url, true);
        req.send(data);
    }
    function pull(req) {
        if(req.status == 200) {
            document.chat.output.value = req.responseText + document.chat.output.value;
            xhr('/chat/pull', pull)
        } else {
            alert(req.responseText);
        }
    }
    xhr('/chat/pull', pull);
    document.chat.tosend.onkeydown = function (event) {
        event = event || window.event;
        var key = event.which || event.keyCode;
        if (key == 13) {
            xhr('/chat/push', null, document.chat.tosend.value);
            document.chat.tosend.value = '';
        }
    }
    document.chat.onsubmit = function() { return false; }
</script>

I think the javascript part looks fairly obvious, but the controller uses a bunch of magic – here’s the workflow:

  • a client load the page and calls the pull page via xhr
  • the pull page waits 10 sencond for a new message to be posted in the messages queue
  • another client sends a message via push page via xhr
  • the push page adds the message in the pubsub queue
  • the coroutines (the ‘watch’ methods) are waken up and get the new message from the fetch() calls
  • each ‘watch’ coroutine posts that message in the client’s queue (via messages.put_nowait)
  • also, the messages queue is limited to 10 messages so if there are 10 updates and a client doesn’t get them it’s associated ‘watch’ coroutine will die
  • each loading pull page gets that message (via messages.get(timeout=10)) and send it to the client

The fairly obscure cogen docs might also be helpfull :)

Almost forgot, to run this you need a trunk version of cogen (easy_install cogen==dev or check out http://cogen.googlecode.com/svn/trunk/)
and the ChatApp example (you can view all the code here and get it from here)

Once you have installed cogen (easy_install or setup.py develop), install the ChatApp (setup.py develop) and start the app with paster serve test.ini. And try finding some bugs (just joking) at http://127.0.0.1:5000/

setuptools + nosetests oddness

Looks like using setuptools’s test runner (eg, run python setup.py test) with the nose.collector has some weird issues – well, unexpected, for me at least.

I was using nose with coverage via –with-coverage option to get some cover reports. But setup.py test borked with “setup.py: error: no such option: –cover-package”. After some digging in the sources i’ve found that some plugins aren’t compatible with setuptools’s test runner – you can’t run post test hooks with that runner apparently (sadly, the coverage plugin is one of them). I wish setuptools had a option to ignore bogus options and show only a warning – well, on second thought, that’s not setuptools related at all, the error is raised from optparse.

Luckily, setuptools has aliases for commands and instead of

[nosetests]
with-coverage = 1
cover-package = cogen

I can do:

[aliases]
test = nosetests --cover-package=cogen --with-coverage

in setup.cfg.

Qt reactor in cogen

I’ve implemented a reactor to integrate the cogen loop in the qt main loop.

You whould use it like this:

from PyQt4 import QtGui

app = QtGui.QApplication([])
hello = QtGui.QPushButton( "Hello world!" )
hello.resize(100, 30)
hello.show()

from cogen.core import reactors, schedulers
m = schedulers.Scheduler(reactor=reactors.QtReactor)

def lorem_ipsum_app(environ, start_response):
    start_response('200 OK', [('Content-type','text/plain'), ('Content-Length','19')])
    return ['Lorem ipsum dolor..']

server = wsgi.WSGIServer(
  ('0.0.0.0', 9001),
  lorem_ipsum_app,
  m,
)
m.add(server.serve)
m.run()
# voila ! this actualy runs the QApplication loop with the cogen hooks

Though the reactor isn’t tested, well, unittested – i have a bunch of tests to keep the code sane.
The problem is that Qt is a big nasty monolithic framework, well, it’s nice if you don’t need to do any weird stuff – like running the main application loop in a second thread.

I just don’t understand why they made this ugly limitation in qt: the application loop must run in the main thread. I’ll fix those tests somehow, but in the meanwhile i’ll just continue cursing Qt for making me feel miserable about my unittests.

Twitter fails at teh interwebs

I mean really, i would run a development site like that. I wonder where they learnt to do web programming since I see a bunch of failures:

  • javascripts aren’t concatenated, not even compressed – a lot of unnecessary http requests.
  • static content doesn’t have proper caching headers – more unnecessary http requests.
  • slow servers – i mean really, amazon aws might be cool and neato, but really slow – i just want my page loaded in 300ms.

And those are barely a scratch on the surface – there’s a lot of stuff that can be done towards a faster page.

Yes, I’m the cranky user that doesn’t like pages that load in 5seconds (yes, 5 god damn seconds!).

Sorry twitter – you suck. I hope your developers will fix you though – don’t worry, I have other stuff to waste my time on.

That history meme

Python version, shorter than Paddy’s version:

# history|python -c'import sys;c=[i.split()[1] for i in sys.stdin]; [sys.stdout.write("%s %s\n"%x) for x in sorted(set((c.count(i),i) for i in c), key=lambda (a,b):a)[::-1][:10]]'
198 python
71 ab
37 cat
36 /root/python/bin/python
31 cd
21 nc
21 strace
10 echo
9 history
8 easy_install

Still not shorter than the bash version:

history|awk '{a[$2]++} END{for(i in a){print a[i]" "i}}'|sort -rn|head