Skip to main content

Ralsina.Me — Roberto Alsina's website

About Japan and God and Lilita

As ev­ery­one knows, there was a big quake in Japan, then a Tsunami, then a vol­cano erupt­ed, then a nu­cle­ar plant caught fire. All things con­sid­ered, a re­al­ly crap­py week.

Then again, if I were ja­pa­nese and I had to read id­iots telling me this was be­cause god is pu­bish­ing me be­cause of (what­ev­er the id­iot does­n't like about Japan), I would be sore­ly tempt­ed to find the mo­rons and ... ok, con­sid­er­ing the ja­pa­nese are show­ing they are very rea­son­able peo­ple, prob­a­bly just tell him some­thing po­lite­ly.

OTO­H, I am not ja­pa­nese. Which means I can ex­plain in great de­tail why those who say "maybe it's <what­ev­er> pun­ish­ing | telling japan <some­thing>" are a com­plete waste of oxy­gen.

I will fo­cus on one ex­am­ple, be­cause it's a very spe­cial re­li­gious mo­ron: a pres­i­den­tial can­di­date in Ar­genti­na, called Lili­ta Car­rió.

Here's what she said (s­pan­ish is the orig­i­nal, of course):

"Dios nos es­tá di­cien­do que debe­mos cuidar el plan­e­ta, que no sig­amos de­struyen­do la tier­ra, que vi­va­mos en la ver­dad, en la de­cen­ci­a, en la jus­ti­ci­a, que no use­mos la tec­nología, aunque sea de man­era pací­fi­ca. Hay que leer los sig­nos de los tiem­pos"

"God is telling us that we should take care of the plan­et, that we should stop de­stroy­ing the earth, that we should live in truth, in de­cen­cy, in jus­tice, and stop us­ing tech­nol­o­gy, even if it's peace­ful­ly. We should read the sign of the times".

Let's con­sid­er that lit­tle by lit­tle.

"God is telling us that we should take care of the plan­et"

I must con­fess I am amazed that an almight­ly be­ing is less ca­pa­ble of com­mu­ni­cat­ing ideas than my 3.9 year old kid. When he wants me to play bal­l, he brings the ball and tells me "Dad, let's play bal­l".

On the oth­er hand, god ap­par­ent­ly, to tell us to stop us­ing tech­nol­o­gy, caus­es a se­ries of cat­a­stroph­ic events in the oth­er end of the world, then brings us the news over the In­ter­net (a tech­no­log­i­cal mir­a­cle), so that Lili­ta can di­vine god's in­ten­tions and then re-broad­cast them to us over the ra­dio (of course, an er­ar­li­er tech­no­log­i­cal mir­a­cle).

Now, does that make sense to any­one? I mean, why does­n't god just, you know, say what he means in a rea­son­able man­ner? Be­cause for re­li­gious peo­ple, the fun is in the div­ina­tion. They are act­ing like ro­man priests di­vin­ing the fu­ture in the en­trails of an an­i­mal, ex­cept they are us­ing the life and suf­fer­ing of peo­ple.

Oh, look, suf­fer­ing in Japan, that means we should stop us­ing the Wi­i!

Not on­ly is that ap­proach com­plete­ly against ev­ery­thing chris­tian doc­trine teach­es, from with the virtue of char­i­ty (if god did it to tell us some­thing, by def­i­ni­tion they de­served it!) to the in­junc­tion against di­vin­ing god's mes­sages in por­tents (yes, it is for­bid­den, go ask a priest).

"[­God is telling us] that we should stop de­stroy­ing the earth"

Oh, gee, ok then! OTO­H, maybe a more sub­tle way than half-break­ing ev­ery­thing in a whole coun­try to let us know next time? Please?

"[­God is telling us] that we should live in truth"

Ok, yes, let's do that. I will start by not be­liev­ing in god, who tru­ly does not ex­ist. When you catch up to that we'll ar­gue some more, ok?

"[­God is telling us to] stop us­ing tech­nol­o­gy, even if it's peace­ful­ly."

I would love if this pres­i­den­tial can­di­date did­n't use tech­nol­o­gy be­cause it would mean I would not have to see her sanc­ti­mo­nious stu­pid­i­ty ev­er again. OTO­H, if we would­n't have tech­nol­o­gy, we would prob­a­bly not know about the earth­quake yet. I sup­pose she may have been say­ing "nu­cle­ar tech­nol­o­gy" and this is out of con­tex­t.

OTO­H, num­ber of peo­ple killed by peace­ful nu­cle­ar tech­nol­o­gy since 1950: 1000? 10000?

num­ber of peo­ple killed by earth­quakes and tsuna­mi in the last 5 years: 100000? 200000?

Yes, those are num­bers I just made up, but I am bet­ting they are more right than wrong, so, ba­si­cal­ly, god has killed more peo­ple this week telling us not to use nu­cle­ar pow­er, than nu­cle­ar pow­er has killed in the last 50 years. Not ex­act­ly good com­mu­ni­ca­tion skill­s.

"We should read the sign of the times"

Ok, here it is:

The New York Times

Don't vote for this blither­ing id­iot. She's dan­ger­ous, and prob­a­bly men­tal­ly il­l.

New golfing challenge: PatoCabrera

In the spir­it of the De Vi­cen­zo web browser, I am start­ing a new pro­gram, called Pa­to Cabr­era. Here are the rules:

  • Twit­ter client (no iden­ti.­­ca in the first ver­­sion, but to be added lat­er)

  • Has these fea­­tures: http://­­paste­bin.lug­­men.org.ar/6464

  • Has to be im­­ple­­men­t­ed be­­fore April 4th

  • Smal­l­­er than 16384 bytes (of python code) but may be larg­er be­­cause of art­­work.

Let's see how it works :-)

OK, so THAT is how much browser I can put in 128 lines of code.

I have al­ready post­ed a cou­ple of times (1, 2) about De Vi­cen­zo , an at­tempt to im­ple­ment the rest of the browser, start­ing with PyQt's We­bKit... lim­it­ing my­self to 128 lines of code.

Of course I could do more, but I have my stan­dard­s!

  • No us­ing ;

  • No if what­ev­er: f()

Oth­er than that, I did a lot of dirty trick­s, but right now, it's a fair­ly com­plete browser, and it has 127 lines of code (ac­cord­ing to sloc­coun­t) so that's enough play­ing and it's time to go back to re­al work.

But first, let's con­sid­er how some fea­tures were im­ple­ment­ed (I'll wrap the lines so they page stays rea­son­ably nar­row), and al­so look at the "nor­mal" ver­sions of the same (the "nor­mal" code is not test­ed, please tell me if it's bro­ken ;-).

This is not some­thing you should learn how to do. In fac­t, this is al­most a trea­tise on how not to do things. This is some of the least python­ic, less clear code you will see this week.

It is short, and it is ex­pres­sive. But it is ug­ly.

I'll dis­cuss this ver­sion.

Proxy Support

A brows­er is not much of a brows­er if you can't use it with­out a prox­y, but luck­i­ly Qt's net­work stack has good proxy sup­port. The trick was con­fig­ur­ing it.

De Vicenzo supports HTTP and SOCKS proxies by parsing a http_proxy environment variable and setting Qt's application-wide proxy:

 proxy_url = QtCore.QUrl(os.environ.get('http_proxy', ''))
 QtNetwork.QNetworkProxy.setApplicationProxy(QtNetwork.QNetworkProxy(\
 QtNetwork.QNetworkProxy.HttpProxy if unicode(proxy_url.scheme()).startswith('http')\
 else QtNetwork.QNetworkProxy.Socks5Proxy, proxy_url.host(),\
 proxy_url.port(), proxy_url.userName(), proxy_url.password())) if\
'http_proxy' in os.environ else None

How would that look in nor­mal code?

if 'http_proxy' in os.environ:
    proxy_url = QtCore.QUrl(os.environ['http_proxy'])
    if unicode(proxy_url.scheme()).starstswith('http'):
        protocol = QtNetwork.QNetworkProxy.HttpProxy
    else:
        protocol = QtNetwork.QNetworkProxy.Socks5Proxy
    QtNetwork.QNetworkProxy.setApplicationProxy(
        QtNetwork.QNetworkProxy(
            protocol,
            proxy_url.host(),
            proxy_url.port(),
            proxy_url.userName(),
            proxy_url.password()))

As you can see, the main abus­es against python here are the use of the ternary op­er­a­tor as a one-­line if (and nest­ing it), and line length.

Persistent Cookies

You re­al­ly need this, since you want to stay logged in­to your sites be­tween ses­sion­s. For this, first I need­ed to write some per­sis­tence mech­a­nis­m, and then save/re­store the cook­ies there.

Here's how the persistence is done (settings is a global QSettings instance):

def put(self, key, value):
    "Persist an object somewhere under a given key"
    settings.setValue(key, json.dumps(value))
    settings.sync()

def get(self, key, default=None):
    "Get the object stored under 'key' in persistent storage, or the default value"
    v = settings.value(key)
    return json.loads(unicode(v.toString())) if v.isValid() else default

It's not terribly weird code, except for the use of the ternary operator in the last line. The use of json ensures that as long as reasonable things are persisted, you will get them with the same type as you put them without needing to convert them or call special methods.

So, how do you save/restore the cookies? First, you need to access the cookie jar. I couldn't find whether there is a global one, or a per-webview one, so I created a QNetworkCookieJar in line 24 and assign it to each web page in line 107.

# Save the cookies, in the window's closeEvent
self.put("cookiejar", [str(c.toRawForm()) for c in self.cookies.allCookies()])

# Restore the cookies, in the window's __init__
self.cookies.setAllCookies([QtNetwork.QNetworkCookie.parseCookies(c)[0]\
for c in self.get("cookiejar", [])])

Here I con­fess I am guilty of us­ing list com­pre­hen­sions when a for loop would have been the cor­rect thing.

I use the same trick when restor­ing the open tab­s, with the added mis­fea­ture of us­ing a list com­pre­hen­sion and throw­ing away the re­sult:

# get("tabs") is a list of URLs
[self.addTab(QtCore.QUrl(u)) for u in self.get("tabs", [])]

Using Properties and Signals in Object Creation

This is a fea­ture of re­cent PyQt ver­sion­s: if you pass prop­er­ty names as key­word ar­gu­ments when you cre­ate an ob­jec­t, they are as­signed the val­ue. If you pass a sig­nal as a key­word ar­gu­men­t, they are con­nect­ed to the giv­en val­ue.

This is a re­al­ly great fea­ture that helps you cre­ate clear, lo­cal code, and it's a great thing to have. But if you are writ­ing evil code... well, you can go to hell on a hand­bas­ket us­ing it.

This is all over the place in De Vi­cen­zo, and here's one ex­am­ple (yes, this is one line):

QtWebKit.QWebView.__init__(self, loadProgress=lambda v:\
(self.pbar.show(), self.pbar.setValue(v)) if self.amCurrent() else\
None, loadFinished=self.pbar.hide, loadStarted=lambda:\
self.pbar.show() if self.amCurrent() else None, titleChanged=lambda\
t: container.tabs.setTabText(container.tabs.indexOf(self), t) or\
(container.setWindowTitle(t) if self.amCurrent() else None))

Oh, boy, where do I start with this one.

There are lambda expressions used to define the callbacks in-place instead of just connecting to a real function or method.

There are lamb­das that con­tain the ternary op­er­a­tor:

loadStarted=lambda:\
    self.pbar.show() if self.amCurrent() else None

There are lambdas that use or or a tuple to trick python into doing two things in a single lambda!

loadProgress=lambda v:\
(self.pbar.show(), self.pbar.setValue(v)) if self.amCurrent() else\
None

I won't even try to un­tan­gle this for ed­u­ca­tion­al pur­pos­es, but let's just say that line con­tains what should be re­placed by 3 meth­od­s, and should be spread over 6 lines or more.

Download Manager

Ok, call­ing it a man­ag­er is over­reach­ing, since you can't stop them once they start, but hey, it lets you down­load things and keep on brows­ing, and re­ports the pro­gress!

First, on line 16 I created a bars dictionary for general bookkeeping of the downloads.

Then, I need­ed to del­e­gate the un­sup­port­ed con­tent to the right method, and that's done in lines 108 and 109

What that does is basically that whenever you click on something WebKit can't handle, the method fetch will be called and passed the network request.

def fetch(self, reply):
    destination = QtGui.QFileDialog.getSaveFileName(self, \
        "Save File", os.path.expanduser(os.path.join('~',\
            unicode(reply.url().path()).split('/')[-1])))
    if destination:
        bar = QtGui.QProgressBar(format='%p% - ' +
            os.path.basename(unicode(destination)))
        self.statusBar().addPermanentWidget(bar)
        reply.downloadProgress.connect(self.progress)
        reply.finished.connect(self.finished)
        self.bars[unicode(reply.url().toString())] = [bar, reply,\
            unicode(destination)]

No re­al code golf­ing here, ex­cept for long lines, but once you break them rea­son­ably, this is pret­ty much the ob­vi­ous way to do it:

  • Ask for a file­­name

  • Cre­ate a pro­­gress­bar, put it in the sta­­tus­bar, and con­nect it to the down­load­­'s progress sig­­nal­s.

Then, of course, we need ths progress slot, that updates the progressbar:

progress = lambda self, received, total:\
    self.bars[unicode(self.sender().url().toString())][0]\
    .setValue(100. * received / total)

Yes, I de­fined a method as a lamb­da to save 1 line. [facepalm]

And the finished slot for when the download is done:

def finished(self):
    reply = self.sender()
    url = unicode(reply.url().toString())
    bar, _, fname = self.bars[url]
    redirURL = unicode(reply.attribute(QtNetwork.QNetworkRequest.\
        RedirectionTargetAttribute).toString())
    del self.bars[url]
    bar.deleteLater()
    if redirURL and redirURL != url:
        return self.fetch(redirURL, fname)
    with open(fname, 'wb') as f:
        f.write(str(reply.readAll()))

No­tice that it even han­dles redi­rec­tions sane­ly! Be­yond that, it just hides the progress bar, saves the data, end of sto­ry. The long­est line is not even my fault!

There is a big in­ef­fi­cien­cy in that the whole file is kept in mem­o­ry un­til the end. If you down­load a DVD im­age, that's gonna sting.

Also, using with saves a line and doesn't leak a file handle, compared to the alternatives.

Printing

Again Qt saved me, be­cause do­ing this man­u­al­ly would have been a pain. How­ev­er, it turns out that print­ing is just ... there? Qt, spe­cial­ly when used via PyQt is such an awe­some­ly rich en­vi­ron­men­t.

self.previewer = QtGui.QPrintPreviewDialog(\
    paintRequested=self.print_)
self.do_print = QtGui.QShortcut("Ctrl+p",\
    self, activated=self.previewer.exec_)

There's not even any need to golf here, that's exactly as much code as you need to hook Ctrl+p to make a QWebView print.

Other Tricks

There are no oth­er trick­s. All that's left is cre­at­ing wid­get­s, con­nect­ing things to one an­oth­er, and en­joy­ing the awe­some ex­pe­ri­ence of pro­gram­ming PyQt, where you can write a whole web brows­er (ex­cept the en­gine) in 127 lines of code.

El Conde de Montecristo

Cover for El Conde de Montecristo

Review:

Fi­nal­ly fin­ished. This is one *long* book (I read the unabridged ver­sion), and as usu­al, it's hard to read such an old book with­out the writ­ing style mak­ing it some­what hard­er.

A big part of this is be­cause the trans­la­tion avail­able at the guten­berg project is... aw­ful, prob­a­bly be­cause of its own age. I had read it maybe 10 years ago in a mod­ern span­ish trans­la­tion that was much su­pe­ri­or.

In any case, you can't un­der­stand, for ex­am­ple, Neal Stephen­son, with­out read­ing Du­mas first, and this one is prob­a­bly Du­mas' best work. It's one of those few se­lect books ev­ery­one thinks they have read but prob­a­bly has­n't.

For ex­am­ple, if I say "S­in­bad the Sailor", does it mean any­thing to you in the con­text of "The Count of Mon­te­cristo"? No? Then you have not read it. You may have read it on com­ic book for­m, or some ex­cerp­t, or maybe clif­f's notes, but you have not read the re­al thing.

I loved the at­ten­tion to de­tail­s, like con­coct­ing a rea­son­able way to sim­u­late a stock mar­ket cri­sis (a cute us­age of what's now called a "Man in the Mid­dle" se­cu­ri­ty ex­ploit!), or how a cer­tain char­ac­ter is al­ways de­scribed in ways that make you think she's a les­bian, but with­out ev­er re­al­ly say­ing it out loud.

All in al­l, a great book to have read. But I would rec­om­mend those who want to read it to in­ves­ti­gate and find a more mod­ern trans­la­tion than project Guten­berg's.

De Vicenzo: A much cooler mini web browser.

It seems it was on­ly a few days ago that I start­ed this projec­t. Oh, wait, yes, it was just a few days ago!

If you don't want to read that again, the idea is to see just how much code is need­ed to turn Qt's We­bKit en­gine in­to a ful­ly-fledged brows­er.

To do that, I set my­self a com­plete­ly ar­bi­trary lim­it: 128 lines of code.

So, as of now, I de­clare it fea­ture-­com­plete.

The new fea­tures are:

  • Tabbed brows­ing (y­ou can ad­d/re­­move tab­s)

  • Book­­marks (y­ou can ad­d/re­­move them, and choose them from a drop-­­down menu)

This is what al­ready worked:

  • Zoom in (C­tr­l++)

  • Zoom out (C­tr­l+-)

  • Re­set Zoom (C­tr­l+=)

  • Find (C­tr­l+F)

  • Hide find (Esc)

  • But­­tons for back­­/­­for­ward and reload

  • URL en­try that match­es the page + au­­to­­com­­plete from his­­to­ry + smart en­try (adds http://, that kind of thing)

  • Plug­ins sup­­port (in­­clud­ing flash)

  • The win­­dow ti­­tle shows the page ti­­tle (with­­out brows­er ad­ver­tis­ing ;-)

  • Progress bar for page load­­ing

  • Sta­­tus­bar that shows hov­­ered links URL

  • Takes a URL on the com­­mand line, or opens http://python.org

  • Mul­ti­­plat­­form (works in any place QtWe­bKit work­s)

So... how much code was need­ed for this? 87 LINES OF CODE

Or if you want the PEP8-­com­pli­ant ver­sion, 115 LINES OF CODE.

Be­fore any­one says it: yes, I know the ren­der­ing en­gine and the tool­kit are huge. What I wrote is just the chrome around them, just like Aro­ra, Rekon­q, Ga­le­on, Epiphany and a bunch of oth­ers do.

It's sim­ple, min­i­mal­is­tic chrome, but it works pret­ty good, IMVHO.

Here it is in (bug­gy) ac­tion:

It's more or less fea­ture-­com­plete for what I ex­pect­ed to be achiev­able, but it still needs some fix­es.

You can see the code at it's own home page: http://de­vi­cen­zo.­google­code.­com


Contents © 2000-2023 Roberto Alsina