Posts Tagged ‘python’

Dagens patch

lördag, juni 14th, 2008

Det blir mycket RDF i lagen.nu 1.5. Och det blir mycket användande av RDFLib. Efter att ha hittat finfina instruktioner om hur man får biblioteket - inklusive SPARQL-parser - att snurra under windows har jag gett mig på min pet peeve i n3-serialiseringskoden, nämligen dess ovana att definera egna anonyma namespaceprefix. Normalt serialiserar den nämligen n3-formatet något i stil med såhär:

@prefix _8: http://lagen.nu/.
@prefix _9: http://lagen.nu/1962:700#.
@prefix dct: http://dublincore.org/documents/dcmi-terms/.
@prefix rinfo: http://rinfo.lagrummet.se/taxo/2007/09/rinfo/pub#.

_8:NJA_2005_s_878 dct:identifier "NJA 2005 s. 878 (NJA 2005:95)";
rinfo:lagrum _9:K29P7;
rinfo:rattsfallshanvisning _8:NJA_1996_s_63,
_8:NJA_2000_s_421;

Men nu blir det det oändligt mycket mer läsbara:

@prefix dct: http://dublincore.org/documents/dcmi-terms/.
@prefix rinfo: http://rinfo.lagrummet.se/taxo/2007/09/rinfo/pub#.

 <http://lagen.nu/NJA_2005_s_878> dct:identifier "NJA 2005 s. 878 (NJA 2005:95)";
     rinfo:lagrum <http://lagen.nu/1962:700#K29P7>;
     rinfo:rattsfallshanvisning <http://lagen.nu/NJA_1996_s_63>,
         <http://lagen.nu/NJA_2000_s_421>;

Här är patchen:

C:\Users\staffan\tmp\rdflib-2.4.0\rdflib>diff -u syntax\NamespaceManager.py~ syntax\NamespaceManager.py
--- syntax\NamespaceManager.py~ 2007-04-04 22:05:32.000000000 +0200
+++ syntax\NamespaceManager.py  2008-06-14 21:36:32.606307200 +0200
@@ -59,8 +59,7 @@
namespace = URIRef(namespace)
prefix = self.store.prefix(namespace)
if prefix is None:
-                prefix = "_%s" % len(list(self.store.namespaces()))
-                self.bind(prefix, namespace)
+                raise Exception("Prefix for %s not bound" % namespace)
self.__cache[uri] = (prefix, namespace, name)
return self.__cache[uri]

Den som förstår RDFLib bättre kan säkert få till samma effekt utan att patcha källkoden genom att subklassa NamespaceManager och trycka in den i kedjan någonstans, men mina försök till det misslyckades.

Lägesrapport

torsdag, augusti 23rd, 2007

Liten uppdatering om vad jag håller på med:

Träning: Har börjat komma igång så smått efter kalmar, målsättningen är fyra lättare pass i veckan (ett simpass, två löppass och ett långpass på cykeln) med mycket fokus på bra teknik.

För löpningen innebär det att öka stegfrekvensen till ca 180 steg i minuten (Jag har några podrunner-mixar med lämplig BPM i lurarna för att hålla takten vilket hjälper mycket - synd bara att musiken är så trist) och se till att landa på fotsulan (inte hälen) med foten under kroppen (inte framför). 180 steg i minuten är vansinnigt mycket fortare än de kanske 150-160 jag brukar springa med, men jag kan redan känna att det här är mindre slitigt för lederna och mer slitigt för flåset.

För simningen innebär det att lära mig växelvis andning, dvs andas var tredje simtag på omväxlande höger och vänster sida. Tidigare har jag andats varannat simtag, alltid på höger sida, och det är lite svårt att få in så mycket luft i lungorna den korta sekund munnen är ovan vattenlinjen att det räcker för tre simtag. Idag skedde dock någon form av litet genombrott och jag kunde köra 400 m oavbrutet i en lugn rytm utan att syret tog slut. Försöken att lära mig voltvändning är dock än så länge fruktlösa.

För cyklingen innebär det att få till ett bra rundtramp, dvs att utnyttja benens alla muskler till att inte bara trycka ner pedalen (mellan klockan 2-5 om man tänker sig vevpartiet som en urtavla) utan även dra den bakåt (5-7), uppåt (7-11) och slutligen trycka framåt (11-2). Det övar jag främst genom att klicka ur ena skon ur pedalen och dra runt cykeln enbart med andra foten under några minuter (det är sjukt tungt!). Det svåra är att få jämnt tryck och hastighet så att inga “döda punkter” finns på hela varvet. Jag försöker även öka kadensen - det rekommenderas att man trampar 90 varv / minut. Jag har ingen kadensmätare så jag har ingen aning om vad jag ligger på, men förmodligen alldeles för lågt.

Jobb: På måndag slutar min semester från min huvudsyssla som amanuens på IRI (vars webbplats för övrigt numera är uppfräshad, XHTML-validerad, allmänt semantisk och mikroformatbeströsslad). Hösten lär bjuda på en hel del löpande administrationsarbete, men också arrangemang av konferenser och liknande evenemang. Boka den 29 november för konferensen “Den rättsliga informationsförsörjningen: Säkerhetskrav?” redan nu!

Men jag har ett gäng sidouppdrag av varierande omfattning. Under sommaren har jag jobbat lite med det offentliga rättsinformationssystemet (som jag även var aktiv inom under förra hösten) med att skriva lite skön pythonkod som genererar RDF och XHTML2-versioner av de (konsoliderade) författningar som ingår i SFS. Lite samma som gamla lagen.nu-koden, men den här gången med en kodkvalité som jag faktiskt inte behöver skämmas över. Bland annat har jag därigenom lärt mig att använda Genshi för XML-generering medelst templates - inte alls dumt. Koden kommer förhoppningsvis snart göras tillgänglig under någon lämpligt öppen licens, tillsammans med resten av rättsinfoprojektets kodbas.

Det finns några ytterligare saker i pipen, bland annat medförfattande av en lite mer akademisk artikel om ett spännande ämne, en permanent post som krönikör i en större branschtidning, och en föreläsning på en av juridiska programmets specialkurser. Mer detaljer kommer när det börjar närma sig slutförandet. I övrigt försöker jag hålla nere på extraknäcken så att jag kan fokusera på…

Studier: Har tyvärr varit lite eftersatta under våren. Nu är det bot och bättring som gäller. Skatterätt (som jag inte direkt sett fram emot, men däremot alla mina kompisar som vill göra smarta avdrag) och förvaltningsrätt (vilket ska bli märkligt kul) är det som gäller under året. Jag ska även tenta av de två kurser jag har släpande (fastighetsrätt och processrätt). Banne mig.

Sen är det bara teoretiska terminen (med ffa rättshistoria och allmän rättslära), specialkursterminen (där jag ska försöka begränsa mig till två kurser från ett betydligt större smörgåsbord) och examensarbetet (där jag kommer på ett nytt ämne i veckan som jag vill skriva om) kvar innan jag blir jur kand. Nu är det bara hemvägen (mindre än hälften) kvar, två år. Det är ju ING-EN-TING!

Unicode och python

tisdag, juli 18th, 2006

UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0×84 in position 1: ordinal not in range(128)

Om du programmerat python känner du säkert igen ovanstående felmeddelande. Roten av problemet är att python har två strängtyper; den ursprungliga str som innehåller en singlebytesträng utan någon vidare information om vilken teckenkodning som använts, och unicode, som är en fullständig unicodesträngtyp.

Det faktum att båda dessa typer finns, fungerar och ser nästan likadana ut gör att en pythonprogrammerare måste vara på tårna hela tiden för att vara säker på vad för sorts data han bollar med — något som tar bort mycket av fördelarna med dynamismen i språket. Annars blir det lätt så att kod som verkar fungerande helt plötsligt krashar om någon stoppar in ett åttabitarstecken nånstans där ingen hade tänkt på att kunde göra det. Och om man inte är disciplinerad är det lätt att man snärjer in sig i en röra av unicode(…) och foo.encode(’iso-8859-1′). [1]

För lagen.nu 2.0-koden försöker jag använda unicode rakt igenom, så mycket som möjligt, och behandla str som om det vore en bytearray. Det går …sådär. ElementTree ger alltid ifrån sig unicode och man mår bäst om man bara stoppar in unicode i den (även om det går att stoppa in str, men det är inte att rekommendera att använda något med höga bitten satt). BeautifulSoup är unicode-only från och med version 3, vilket är ett stort steg framåt.

Django är dock ett sorgebarn i sammanhanget. Gränssnitten både utåt, mot webbläsaren och innåt, mot databasen, är str, dessutom utf-8-kodat (om man inte ändrat DEFAULT_CHARSET, vilket dock inte är en egentlig lösning), något som är det sämsta av bägge världar. Jag menar, len av en sträng som innehåller ‘räksmörgås’ ska vara 10, aldrig 13, men kolla vad len(u’räksmörgås’.encode(’utf-8′)) är.

Vad värre är, det går inte att stoppa in unicode överallt heller. De modeller man skapar behöver få sina strängvärden som utf-8-kodade str, annars får man ‘Warning: Data truncated for column ’subject’ at row 1′ och frågetecken i databasen. Åtminstone mot min MySQL-databas, som internt lagrar i utf-8 (tror jag). Är det likadant vad gäller Postgres och SQLite? Vem vet?

Det går väl att lära sig leva med. Sätt upp en barriär mellan django-land och min egen kod, och se till att all str-data kodas om till unicode, och vice versa. Det kanske ordnas innan 1.0, men det är mycket att göra.

Django är dessvärre inte ensam i detta — igår tillbringade jag alldeles för lång tid på att försöka använda email-modulen med unicodesträngar, men den modulen är inte unicodesäker. Simpleparse klarar inte heller unicodeindata. Det finns säkert fler exempel.

Just unicodehantering[2] är ett av de få områdena där jag saknar COM/CLR/Java-världen. Jag menar, multibytetecken funkade smärtfritt i VB6 för typ en miljon år sedan.

På temat unicode saknade jag igår några bra teststrängar för att testa unicodedata med lite exotiskare tecken (allt över U+0255, typ) — utländska motsvarigheter till Räksmörgås, typ. Jag hittade inget direkt, men klistrade ihop något baserat på wikipedias förstasida:

Unicode/UTF-8-test

Som en added bonus innehåller den en hel del BiDi-tester med vad jag tror är korrekt rendering. Jag hade ingen aning om att det var ett så komplicerat ämne, men nu har jag lärt mig en ny HTML-tag. Det är inte varje dag!


[1] Så ser lagen.nu 1.0-koden ut. Ibland blir jag påmind om en kompis som förklarade hur han programmerar C: “klagar kompilatorn så sätter jag dit en ‘*’, om inte det hjälper byter jag ut den mot ett ‘&’”

[2] Och till en mindre del debugging, även om det har ändrats sedan jag upptäckte Wing IDE

Jack of all trades, master of none

lördag, juni 24th, 2006

För att bygga lagen.nu 2.0 kommer det krävas ett rätt stort lass med teknologier. Jag har väl ungefär samlat ihop alla pusselbitar och känner mig ungefär som när man storhandlat inför en riktigt ambitiös middagsbjudning — alla råvaror och alla verktyg finns på plats, nu gäller det bara att hantera dem. Lite om vad som ingår:

XML: Lagen.nu 1.0 använde XML en hel del bakom kulisserna, men XML-formaten var odokumenterade, ad-hoc’iga och i största allmänhet inte så väldigt XMLiga (inga namespaces, data som borde varit i noder lagrades i attribut och vice versa, inget utnyttjande av befintliga standarder som RDF och XLink). Det här ska på något sätt bli bättre i 2.0 — det finns en hel del internationella projekt kring hur juridisk information ska representeras i XML, men de flesta känns som kommittéprodukter med mycket snack och lite spec — det känns som XML inbjuder till sånt, i högre grad än andra komponenter i stacken nedan. Metalex verkar dock lovande.

XSLT: För att få ut de semantiskt korrekta XML-dokumenten på webben måste de transformeras till nån sorts HTML. För 1.0 gjorde jag en del riktigt knepiga transformationer då mantrat där var “vem behöver databaser när vi kan slänga in allt i ett gigantiskt XML-dokument och XPath()a oss fram” — för 2.0 kommer en vanlig old-school relationsdatabas att användas, så förhoppningsvis blir det bara en fråga om att ta befintliga stylesheets och trimma lite.

SOAP: Det här är väl mera på nice-to-have-listan snarare än release-blocker-listan, men det vore kul att åtminstone doppa tårna i hela webservicesträsket. Två problem är dock att jag aldrig har jobbat mot någon skarp web service från klientsidan, så jag vet inte riktigt hur ett WS-API ska kännas för att vara bra, samt att jag egentligen inte har en bra känsla för vilka rättskälleinformationsfunktioner som skulle vara vettiga att göra åtkomliga som webservices.

HTML: Allt ska ju bli HTML till slut, så viss koll på läget måste jag ju ha även här. Det blir förmodligen XHTML 1.0 Strict, en viss grad av divitis (den lösning jag skissade på här visade sig vara ohållbar när man kör den på exempelvis inkomstskattelagen och många kommentarer), och förmodligen tyvärr sämre semantisk meningsfullhet än i 1.0 (det största problemet är numrerade listor — för att sätta en kommentar bredvid en punkt i en punktlista kan jag inte representera själva punkten som en LI-tag — det går inte att placera en P-tag med kommentaren vid sidan av när man är i ett UL-kontext). Dock så ska ett flertal tabeller bort och ersättas med DL-listor, efter tipsen här.

CSS: Ärligt talat är inte 1.0 särskilt visuellt upphetsande. Det kanske inte kommer bli pastellfärger och rundade hörn, men en något mer genomtänkt visuell känsla hoppas jag kunna åstadkomma. Jag har med min nya op-center-setup möjlighet att testa på Firefox, IE, Konqueror, Opera och Safari, och ser med skräckblandad förtjusning fram emot utmaningen att få det hela att se bra ut utan att degenerera till tabellsoppa.

DHTML/AJAX: Inte bara för att det är en nödvändig beståndsdel om man vill vara web 2.0, mycket av wikifunktionaliten skulle vinna på att med lite AJAX göra det möjligt att redigera kommentarer rakt på sidan, a la flickr’s fotobeskrivningsmagi. Och vore det inte coolt om sökrutan föreslog lagtexter a la google suggest? Jag har kollat runt på vad för hjälpramverk som finns på javascriptutvecklingsscenen (jag har läst DHTML Utopia och såg inte alls fram enmot att göra all crossbrowserkompatibilitetssörja för hand) och fastnade tillslut för MochiKit (efter att ha kollat på Dojo, Prototype/script.aculo.us, YUI och den här artikeln) — att det beskrevs som kraftigt pythoninfluerat var ett tungt vägande skäl.

EBNF: En av de bästa sakerna med lagen.nu 1.0 var den EBNF-baserade tolkningen av lagtextreferenser i löpande text. Den är, i ärlighetens namn, mest ihophackad utan någon djupare förståelse för textparsning i den högre skolan, och det känns som att jag borde ägna lite tid åt SimpleParse-dokumentationen för att hantera andra typer av referenser, nu när jag ska hantera flera typer av rättskällor.

Python: Jag hamnar ofta i fällan att när jag, när jag lärt mig ett verktyg tillräckligt bra för att få riktigt jobb gjort med det, slutar lära mig mera. Under våren har jag dock gått tillbaks till grunderna för python och försökt utnyttja mer än de få procent av språket som 1.0 använder. Det blir vettig objektorientering, bättre utnyttjande av standardbiblioteket och kanske lite riktigt funktionell programmerings-tänk (första kapitlet i TPiP är en riktig ögonöppnare här). De tre viktigaste bitarna kommer nog bli Django för allt dynamiskt, BeautifulSoup för allt HTML-tröskande (tillsammans med SimpleParse enligt ovan), och ElementTree för allt XML-byggande.

MySQL: Och så ska ju saker lagras i en databas också. Jag är som tidigare nämnts skeptisk mot RDBMS-användande, men med tanke på hur fint django abstraherar bort det hela känns det motiverat att faktiskt plocka in en databasserver i mixen. Det blir förmodligen MySQL även om alla de coola kidsen använder Postgresql, men då jag redan har en MySQL-server körandes för Wordpress, MediaWiki och lite annat, har jag än så länge inte sett nån verklig anledning att byta.

Ungefär så — det blir en ganska diger lista när man kollar på den. Vilket anknyter till den här bloggpostningens titel, det kan inte bli några djupa kunskaper inom varje enskild del (särskilt som en stor komponent inte är med här, nämligen domänkunskapen om just rättsinformation) men förhoppningsvis tillräckligt för att ro det hela i land.

Cachning och throttling för webspindlar i python

lördag, april 15th, 2006

Om du inte ägnar dig åt att programmera webspindlar i Python är dagens post förmodligen inte av så stort intresse.

Men: Jag har ägnat långfredagen åt att bland annat putsa på några utökningar till urllib2. För länge sedan skrev jag något i stil med att urllib2 var alldeles för komplext, men jag lutar nu åt att det “bara” är underdokumenterat.

Jag behövde ett sätt att från ett pythonprogram hämta websidor utan att hämta samma sida två gånger i onödan. Jag ville dessutom att förfrågningarna inte skulle ske allt för tätt inpå varandra — jag ville med andra ord ha en cache och en throttling-mekanism. Det visar sig att urllib2’s modell med kedjade handlers som anropas efter varandra lät mig göra det hela på ett mycket smidigt sätt.

Här är resultatet: StandaloneRobot.py. Testkoden i slutet förklarar hur det funkar; en anropare kan observera hur cache/throttling har använts genom att titta efter x-cache och x-throttling-headers i resultatet.

Lagringsmekanismen för cachen är inte den smartaste, men den funkar. Den implementerar inte en “riktig” cache såsom en sådan ska funka enligt HTTP/1.1, utan har en GET-förfrågan till en viss URL skickats så får alla efterföljande förfrågningar till samma URL det cacheade svaret — tills man raderar cachen.

Veckans Del.icio.us-länkar

söndag, juli 31st, 2005
Den här veckans intressanta länkar, från del.icio.us/staffanmalmgren

Part 7: Regression testing

söndag, december 19th, 2004

(A series of blog posts about the tech behind lagen.nu. Earlier parts are here: first, second, third, fourth, fifth and sixth)

Like most developers that have been Test infected, I try to create regression tests whenever I can. A project like lagen.nu, which has no GUI, no event handling, no databases, just operations on text files, is really well suited for automated regression testing. However, when I started out, I didn’t do test-first programming since I didn’t really have any idea of what I was doing. As things solidified, I encountered a particular section of the code that lended itself very nicely to regression testing.

Now, when I say regression testing, I don’t neccesarily mean unit testing. I’m not so concerned with testing classes at the method level as my “API” is really document oriented; a particular text document sent into the program should result in the return of a particular XML document. Basically, there are only two methods that I’m testing:

  • The lawparser.parse() method: Given a section of law text, returns the same section with all references marked up, as described in part 5.
  • Law._txt_to_xml(), which, given a entire law as plaintext, returns the xml version, as described in part 4

Since both these tests operate in the fashion “Send in really big string, compare result with the expected other even bigger string”, I found that pyunit didn’t work that well for me, as it’s more centered around testing lots of methods in lots of classes, where the testdata is so small that it’s comfortable having them inside the test code.

Instead, I created my tests in the form of a bunch of text files. For lawparser.parse, each file is just two paragraphs, the first being the indata, and the second being the expected outdata:

    Vid ändring av en bolagsordning eller av en beviljad koncession
    gäller 3 § eller 4 a § i tillämpliga delar.

    Vid ändring av en bolagsordning eller av en beviljad koncession
    gäller <link section="3">3 §</link> eller <link section="4a">4 a §</link> i tillämpliga delar.
  

The test runner then becomes trivial:

def runtest(filename,verbose=False,quiet=False):
    (test,answer) = open(filename).read().split("\n\n", 1)
    p = LawParser(test,verbose)
    res = p.parse()
    if res.strip() == answer.strip():
        print "Pass: %s" % filename
        return True
    else:
        print "FAIL: %s" % filename
        if not quiet:
            print "----------------------------------------"
            print "EXPECTED:"
            print answer
            print "GOT:"
            print res
            print "----------------------------------------"
            return False
  

Similarly, the code to test Law._txt_to_xml() is also pretty trivial. There are two differences: Since the indata is larger and already split up in paragraphs, the indata and expected result for a particular test is stored in separate files. This also lets me edit the expected results file using nXML mode in Emacs.

Comparing two XML documents is also a little trickier, in that they can be equivalent, but still not match byte-for-byte (since there can be semantically insignificant whitespace and similar stuff). To avoid getting false alarms, I put both the expected result file, as well as the actual result, trough tidy. This ensures that their whitespacing will be equivalent, as well as easy to read. Also, a good example of piping things to and from a command in python:

def tidy_xml_string(xmlstring):
    """Neatifies a XML string and returns it"""
    (stdin,stdout) = os.popen2("tidy -q -n -xml --indent auto --char-encoding latin1")
    stdin.write(xmlstring)
    stdin.close()
    return stdout.read()
  

If the two documents still don’t match, it can be difficult to pinpoint the exact place where they match. I could dump the results to file and run command-line diff on them, but since there exists a perfectly good diff implementation in the python standard libraries I used that one instead:

    from difflib import Differ
    differ = Differ()
    diff = list(differ.compare(res.splitlines(), answer.splitlines()))
    print "\n".join(diff)+"\n"
  

The result is even easier to read than standard diff output, since it points out the position on the line as well (maybe there’s a command line flag for diff that does this?):

[...]
      suscipit non, venenatis ac, dictum ut, nulla. Praesent
      mattis.</p>
    </section>
-   <section id="1" element="2">
?                   ^^^

+   <section id="1" moment="2">
?                   ^^

      <p>Sed semper, ante non vehicula lobortis, leo urna sodales
      justo, sit amet mattis felis augue sit amet felis. Ut quis
[...]
  

So, that’s basically my entire test setup for now. I need to build more infrastructure for testing the XSLT transform and the HTML parsing code, but these two areas are the trickiest.

Since I can run these test methods without having a expected return value, they are very useful as the main way of developing new functionality: I specify the indata, and let the test function just print the outdata. I can then work on new functionality without having to manually specifying exactly how I want the outdata to look (because this is actually somewhat difficult for large documents), I just hack away until it sort of looks like I want, and then just cut’n paste the outdata to the “expected result” file.

Part 5: Finding and formatting inline law references

lördag, december 18th, 2004

(Earlier posts: 1, 2, 3, 4)

The second part of the convert-things-to-xml approach deals with finding inline references to other paragraphs in the law text. I’ve written about it in part in this blog post, but to recap:

Swedish law text contains lots and lots of internal and external references to other sections. These references have a semi-standardized format, but they are clearly meant to be parsed by humans, not machines.

The simplest case is a single reference to a single section (example from 4 § Försäkringsrörelselagen (1982:713)):

    Vid ändring av en bolagsordning eller av en beviljad koncession
    gäller 3 § i tillämpliga delar.
  

Here, the string ‘3 §’ signifies a reference to the third section in the current section of the current law. If we can identify and mark that reference up, we can make the ‘3 §’ into a hyperlink leading to the definition of section 3. You know, the stuff hyperlinking was designed for. Currently, this gets transformed into the following XML (how the XML gets transformed into clickable HTML is the subject of a later article):

    Vid ändring av en bolagsordning eller av en beviljad koncession
    gäller <link section=”3″>3 §</link> i tillämpliga delar.
  

For cases like that, the transformation is trivial, and could be done with regexps or just simple string matching. But for cases like this (Patentbesvärsrätten 96-837):

    14 § 1 st. 6) och 6 § varumärkeslagen (1960:644)
  

or better yet, this (49 a § URL, my personal favourite):

    Bestämmelserna i 2 § andra och tredje styckena, 3, 7--9 och 11 §§,
    12 § första stycket, 13, 15, 16, 18--20 och 23 §§, 24 § första
    stycket, 25--26 b, 26 d -- 26 f, 26 i -- 28, 31--38, 41, 42 och
    50--52 §§ skall tillämpas på bilder som avses i denna paragraf.
  

things get way more complicated. Enter EBNF-grammar-based parsing with the dynamic duo that is SimpleParse and mxTextTools. Also, the book Text Processing in Python by David Mertz should be mentioned, as it helped me in the right direction when I realized regexes weren’t going to cut it.

My previous post describes the actual EBNF grammar and how SimpleParse is used to build a parse tree from it, so you might want to read that too.

However, that is really only half of the problem. After having a tree of parsed tokens, that might (for a somewhat complicated scenario) look like the following:

refs': '14 § 1 st. 6) och 6 § varumärkes|lagen (1960:644)'
    'ExternalRefs': '14 § 1 st. 6) och 6 § varumärkes|lagen (1960:644)'
        'MultipleGenericRefs': '14 § 1 st. 6) och 6 §'
            'GenericRefs': '14 § 1 st. 6)'
                'GenericRef': '14 § 1 st. 6)'
                    'SectionPieceRef': '14 § 1 st. 6)'
                        'SectionRef': '14 §'
                            'SectionRefID': '14'
                                'number': '14'
                            'Whitespace': ' '
                        'Whitespace': ' '
                        'PieceRef': '1 st. 6)'
                            'PieceRefID': '1'
                                'ordinal': '1'
                            'Whitespace': ' '
                            'PieceOrPieces': 'st.'
                            'Whitespace': ' '
                            'ItemRef': '6)'
                                'ItemRefID': '6'
                                    'number': '6'
                                'RightParen': ')'
            'WAndOrW': ' och '
                'Whitespace': ' '
                'AndOr': 'och'
                    'And': 'och'
                'Whitespace': ' '
            'GenericRefs': '6 §'
                'GenericRef': '6 §'
                    'SectionRef': '6 §'
                        'SectionRefID': '6'
                            'number': '6'
                        'Whitespace': ' '
        'Whitespace': ' '
        'ExternalLawRef': 'varumärkes|lagen (1960:644)'
            'NamedExternalLawRef': 'varumärkes|lagen (1960:644)'
                'word': 'varumärkes'
                'Pipe': '|'
                'LawSynonyms': 'lagen'
                'Whitespace': ' '
                'LawRef': '(1960:644)'
                    'LeftParen': '('
                    'LawRefID': '1960:644'
                        'digit': '1'
                        'digit': '9'
                        'digit': '6'
                        'digit': '0'
                        'Colon': ':'
                        'digit': '6'
                        'digit': '4'
                        'digit': '4'
                    'RightParen': ')'
  

, how do we turn it into the following XML?

    <link law="1960:644" section="14" piece="1" item="6">
      14 § 1 st. 6)
    </link>
    och 
    <link law="1960:644" section="6">
      6 §
    </link>
    varumärkes|lagen (
    <link law="1960:644">
      1960:644
    </link>
    )
  

Turns out this is a problem that can be solved in a rather generic manner with a small amount of planning and a little recursiveness. Basically, all tokens that end in ‘Ref‘ should generally end up formatted as a <link>. All tokens underneath such tokens that ends in ‘RefID‘ are used to find the attributes for these tags. Start at the root, then recurse depth-first over all child nodes until done. Sometimes there are notes that end in Ref underneath other nodes also ending in Ref, in those cases it’s the top node that is turned into a <link> tag

Of course, there are exceptions and things to be aware of. Two of these are illustrated in the above example. To correctly insert the law reference (the SFS id ‘1960:644′) for tag to be produced from the MultipleGenericRefs token ‘14 § 1 st. 6) och 6 §‘, we have to plan ahead, when dealing with the parent node (ExternalRefs). The formatter has built-in knowledge of the special handling needed, and, when encountering a ExternalRefs node, finds the ExternalLawRef node child, stores the underlying LawRefID value, and later picks this value up when formatting the tags for the two GenericRef tokens.

To make this looping and recursing easier, I build a OO wrapper around the array-based parse tree that mxTextTools return. This was the subject of another post.

Note also that the ExternalLawRef token did not result in a <link> tag, but the underlying LawRefID token did. This was a consious decision (I thought it looked better that way), and was implemented by creating a number of extra subroutines in the formatter. Basically, the main function acts as a generic dispatcher, by looping over all subtokens in a tree, and for each token, checks if there’s a corresponding format_tokenname() function. If so, call that, otherwise call a generic formatter (which may, recursivly, make use of the generic dispatcher). The code is pretty simple, but is indicative of how neat stuff like this can be done in dynamic langugages:

        try:
            formatter = getattr(self,"format_"+part.tag)
            res = formatter(part)
        except AttributeError:
            res = self.format_tokentree(part)
  

The other wierd thing is that ‘|’ sign in the term ‘varumärkes|lagen‘. This is swedish for ‘the trademark law’, but since we like to write words together, this creates an interesting challenge for creating the EBNF gramar. Basically, I cannot find a way to match a word that ends in a specific suffix, such as ‘lagen‘. The resulting parser is always ‘greedy’, so there seem to be no way of matching these words without matching all words. So, to fix this, I preprocess the text before putting it through the parser, using normal python regular expressions, which can be non-greedy, to put in that ‘|’ sign, which solves the problem. Then, after retrieving the string from the tag formatter, I remove those signs.

One particular satisfying thing of the problem described in this post is that how well it lends itselfs to automated regression testing. Any new feature can easily be specified in a test case before coding begins, and after it’s done, it’s easy to verify that nothing that previously worked has been broken. More on regression testing in a later post.

Part 3: Understanding what was fetched

onsdag, december 15th, 2004

(Earlier posts in this series: here and here)

There are a lot of ways to extract data from a HTML file. You can do simple string searching (by the way, why is the python documentation for basic string objects hidden under the non-descript heading “Sequence types”, and why is there no reference to that part of the documentation from the separate string module, which hardly does anything?) and rexep munging, or you can use more sophisticated HTML parsers. Funnily enough, there are two of these in the Python standard library, and both of them are callback based — why no tree-based interface? If the HTML code is modern and well-formed, you can even use a vast array of XML tools (and if it’s not, you can fix it with HTML Tidy).

I ended up using the BaseHTMLProcessor approach from Dive Into Python., which has a whole chapter devoted to the art of HTML parsing. Basically, you subclass BaseHTMLProcessor, implementing callbacks for various tags, which are called as these tags are encountered in the document. Your class is responsible for keeping track of whatever state (ie what depth you are in the document, what tags were encountered before this one, and so on) that needs to be kept.

There are some things that are cumbersome with this approach. For example, automatic HTML entity resolving would be good. The HTML fragment “<h1>r&auml;ksm&ouml;rg&aring;s</h1gt;” represents a single header with the string “räksmörgås” (a common test phrase for swedish programmers), and so it should only result in three callbacks: start_h1, handle_data (which should be called with the string “räksmörgås“), and end_h1.

Instead, the following callbacks are called:

  • start_h1
  • handle_data (called with the string ‘R‘)
  • handle_entityref (called with the string ‘auml‘)
  • handle_data (called with the string ‘ksm‘)
  • handle_entityref (called with the string ‘ouml‘)

…you get the idea. There exists a mapping that helps with the entity resolving, but for the HTML case, this could have been solved at a lower-level stage.

Still, for the parsing problems I have, the callback-based/keep-your-own-goddam-state-approach works. Most of the time I’m just concerned with finding the elements in a table, meaning I have to keep track of what cells I’ve seen and when a new table row starts, things like that. As I go along, build up a list of mappings or something similar, and then just use that list once done. The calling code gets quite nice and simple:

cl = SFSChangelogExtractor()
cl.feed(open("downloaded/lawinfo/%s.html" % self.basefile).read())
for c in cl.changelog:
    if c.item('SFS-nummer') == current_transitional_id: ...
  

(Note that the ‘c’ object here is not a standard dictionary, but a mapping-ish object that also keeps track of the order keys have been inserted. That’s why it’s c.item(’SFS-nummer’) and not c[’SFS-nummer’]. That, and the fact that I was too lazy to implement the special methods needed to do a proper Duck Typed dictionary.)

The one exception is the problem of finding all the plaintext in a law text like this one, but it’s even easier: Just increment a counter whenever a <pre> tag is encountered, decrement it when seing </pre>. In handle_entityref and handle_text, just check if the counter is > 0 and if so, append the text to a StringIO object.

Lagen.nu behind the scenes

måndag, december 13th, 2004

Now that lagen.nu has been out for some time, it might be a good idea to write down what I’ve learned from it so far, in blog form. Much of the discussion will be centered around python, a language I’m far from proficient in, but it’s possible that someone will learn at least something from it.

First, take a look at this post that explains what lagen.nu is, from a user perspective.

This post is about how the site is produced. When I started out, I had no clear idea of what I wanted to do, other than to download the text of all swedish laws and convert it to some sort of nice HTML. I knew I wanted to do as much as possible with static HTML files, and I had a hunch that XML would be involved in some way.

So, essentially, the code only needs to run off-line, with no GUI required.

I thought about doing this in C#, since it would be a good experience building project in a language for which expertise is highly sought after. But since I’m no longer programming for food (actually I am, for another four days, but still), I took the opportunity to do it in python, a language which I’ve always liked but never become friends with.

From a high level, the code does the following:

  • Finds out what laws are available
  • Downloads the law text HTML documents
  • Converts the text to XML
  • Transforms the XML to HTML

There are some extra steps involved in creating the front page, RSS feeds, and handling the verdicts database, but these are the main steps.

The result of the program is a tree with static HTML files, ready for deployment.

I started out by looking for a good Python IDE. I did not find it, and settled for Emacs with python-mode.

Once set up with a recent version of python-mode, properly configured, I had a nice light-weight development environment. Here’s my minimal configuration (this goes into your .emacs file):

(autoload 'python-mode "python-mode" "Python Mode." t)
(add-to-list 'auto-mode-alist '("\.py'" . python-mode))
(add-to-list 'interpreter-mode-alist '("python" . python-mode))
(setq py-python-command "C:\Python23\python.exe")
  

My code lives in classes, and to test things out, I have code at the end of the main code file that looks sort of like the following:

if __name__ == "__main__":
    vc = VerdictCollection()
    vc.get(2004,refreshidx=True)
  

(That is, if I want to test the get method of the VerdictCollection class). To test the code, I just press C-c C-c in the python editor window. The entire python buffer gets sent to the python shell, and the last part (after if __name__ == “__main__”:) executes.

Things that are good about this environment:

  • Free, in both senses of the word
  • The intendation support really works, which is quite important with python
  • Reasonably fast edit-run cycle
  • The interactive python shell

Things that are bad:

  • I can’t debug stuff. It seems like it should be possible, but I have no pdb.exe, which seems to be a requirement. In particular, it would be nice to be able to automatically start debugging when an unhandled exception is raised.
  • Copy and paste from the *Python* buffer has character set problems. For example, if my code outputs a § sign, and I cut’n paste it into another file, emacs will complain:
    These default  coding systems were tried: 
    iso-latin-1-dos
    However, none of them safely encodes the target text.
    This is bogus, since the § sign is perfectly legal in latin-1.

I use the standard python.org distribution of Python 2.3 (I haven’t gotten around to upgrading to 2.4 yet), not the ActiveState one. I tried it, and like the fact that the win32com module is bundled, but the python.org version is a leaner download and has a more usable HTML help application (particularly the good index).

To get a grip of how to do things with python, I’ve used the online version of Mark Pilgrim’s Dive Into Python, as well as the Python cookbook. This, together with the reference manual, (the eff-bot guide to) The Standard Python Library and Text Processing in Python has been all I need so far.