Specialbehandla eller arbeta runt?

Jag upptäckte just ett fel i SFST, dvs den databasen som innehåller alla svenska lagtexter i konsoliderad form, och som ligger till grund för lagen.nu Inget unikt med det, det förekommer fel där då och då, vilket jag vanligtvis märker genom att någon kodsnutt går sönder eftersom den inte var skriven för att indatat skulle vara så trasigtmärkligt. Över tiden har jag utvecklat ett system för att hantera sådana här felaktigheter – jag skriver helt enkelt en patch, som appliceras först av allt innan resten av parsekoden kör.

Som ett exempel på ett typiskt fel, kolla 3 § PUL i SFST:

3 § I denna lag används följande beteckningar med nedan angiven
betydelse.


Beteckning                      Betydelse

Behandling (av person)          Varje åtgärd eller serie av
uppgifter)                      åtgärder som vidtas i fråga om
                                personuppgifter, vare sig det
                                sker på automatisk väg eller inte,
                                t.ex. insamling, registrering,
                                organisering, lagring, bearbetning
                                eller ändring, återvinning, inhämtande,
                                användning, utlämnande genom
                                översändande, spridning eller annat
                                tillhandahållande av
                                uppgifter, sammanställning eller
                                samkörning, blockering,
                                utplåning eller förstöring.

Dvs vänstercellen i tabellen har en högerparantes där det borde vara ett bindestreck.

Detta löses genom en patch i unified diff-format. Den appliceras, resten av parsningen tar vid, resultatet blir fint (och, nota bene, med en liten förklaring om att texten faktiskt har ändrats – transparens är viktigt).

(jo, jag har rapporterat det upstream också, och de som sköter SFST vet var de kan hitta mitt patcharkiv. Vissa fel har rättats med tiden, men inte alla).

Generellt försöker jag låta bli att lösa parsningsproblem med en patch – i en korpus så stor som SFST kan man nästan utgå från att alla tänkbara klasser av fel förekommer fler gånger. Om det är möjligt att lösa problemet generellt i själva parsningen, genom någon typ av workaround, är det att föredra framför att specialbehandla lagtext för lagtext. Ovanstående fel tror jag inte är möjligt att hantera generellt.

Men nu har jag hittat ett fel som är sådär på gränsen: I köplagens första paragraf, fjärde stycket finns texten ”Vid köp av byggnad som uppförts för stadigvarande bruk gäller […] vad som föreskrivs i 4 kap. 11, 12 och 18–19d §§ jordabalken”

Felet här är i slutet av meningen – den avslutas med en lista av hänvisningar, där den sista går till 4 kap. 19 d § jordabalken. När lagstiftaren ändrar en lag så vill denne ibland stoppa in nya paragrafer mellan exv 19 § och 20 § — dessa får då beteckningarna 19 a §, 19 b § osv.

(Sidnot: Amerikanska lagstiftare har i allmänhet löst det här på ett sätt som är mer tilltalande för gamla basicprogrammerare – kolla exempelvis Oregons reglering av forumregler i civilmål och notera att numreringen lämnar gott om utrymme att stoppa in nya bestämmelser mellan de befintliga, vilket också görs då och då).

Det svenska sättet att stoppa in nya paragrafer gör det krångligare att skriva en parser för lagtext — den måste klara av uttrycket ”3 §”, ”3 och 4 §§”, ”3 e §”, ”3 eller 4 §” och många många fler (glöm för allt i världen inte att ”10 § f” är något helt annat än ”10 f §”). Den EBNF-grammatik som används har vuxit fram organiskt över några år, och kan ge känsliga personer mardrömmar.

Av den anledningen blir jag lite kallsvettig varje gång jag ser ett nytt sätt att hänvisa till lagrum som systemet inte klarar av att parsea. Exemplet ovan — som kan förenklas till ”19d § jordabalken” — är ett sådant. Med den skillnaden att hänvisningen faktiskt är felaktig: paragrafen har nummer ”19 d”, inte ”19d”.

Så frågan är: Ska jag fixa det med specialbehandling, dvs skriva en patch, eller ska jag försöka arbeta runt problemet genom att ändra min EBNF-grammatik? En vägledning kan man få genom grep — vi kan helt enkelt kolla hur många andra förekomster av det här felaktiga sättet att hänvisa som vi hittar:

find data/sfs/intermediate -name "*.txt" | xargs  egrep '[0-9][a-z] §'
data/sfs/intermediate/1980/770.txt:5 § Vid tillämpning av 11a § tredje stycket kungörelsen (1972:412 ) om
data/sfs/intermediate/1990/931.txt:kap. 11, 12 och 18--19d §§ jordabalken. Om en tomträttsupplåtelse enligt
data/sfs/intermediate/1995/238.txt:-- 22--23, 25--26, 30 och 35--44a §§ kungörelsen (1969:380) om
data/sfs/intermediate/1995/238.txt:vapenfri tjänst skall 12--17a §§ kungörelsen (1966:414) med vissa
data/sfs/intermediate/1996/844.txt:d) Bestämmelserna om sanktioner i 31a-31e §§ lokalradiolagen
data/sfs/intermediate/1997/1157.txt:reglerna, jfr 72a § lagen nr 612 den 24 juni 1996 om social
data/sfs/intermediate/1998/488.txt:stycket finns i 2a §. I fråga om utfärdande av nytt
data/sfs/intermediate/1999/1229.txt:Vad som sägs i 2 kap. 3 §, 6 kap. 5, 10a och 16a §§, 17kap.

Ok, åtta fel är inte jättemånga, men jag får väga risken av att introducera nya fel genom en ändrad grammatik (vilken hålls nere av ett stort batteri med testfall) mot det tråkiga och tidskrävande arbetet att göra åtta patchfiler (samt den potentiella nyttan av att de appliceras upstream).

Destroy, erase, improve

Nu har det varit tyst här ett tag igen. Jag har ägnat sommaren åt
extrajobb, studier inför tentan i C4, samt jobb på nästa version av
lagen.nu, som nu börjar närma sig ”färdig”-stadiet. På ferenda.lagen.nu finns en betaversion av
siten, och de utåt sett viktigaste nyheterna är att
formgivningen är fräshare, samt att rättsfallsreferaten nu finns med i
fulltext.

Men den stora nyheten bakom kulisserna är att kodbasen till 90% är
helt omskriven, och att den nu är öppen källkod under
BSD-licensen
. Det har tagit lång tid – betydligt längre än det tog
att utveckla den ursprungliga versionen – och frågan som inställer sig
är om det verkligen var en sån lysande idé? Det finns två
förhållningssätt till att slänga gammal kod och skriva om den från
scratch — Joel säger att det är en av sakerna you
never should do
, medans Fred menar att man ska plan to throw one
away; you will, anyhow
.

De flesta utvecklares första impuls när de sitter med en kodbas som
vuxit till en ogenomtränglig röra är att slänga skiten och starta
om. Det är för det mesta en dålig idé, inte minst för att det inte
finns någon garanti för att man inte hamnar i samma sits tre år
senare, när man är klar. Men eftersom det är mycket roligare att bygga
nytt än att reparera gammalt skrivs ofta system om från scratch, trots
att det skulle varit mycket billigare att steg för steg rensa i det
gamla röriga systemet.

Så varför skrev jag då om koden? På grund av glädje och skam. Glädje,
eftersom lagen.nu inte har någon affärsplan, några inkomster ellern
några krav på företagekonomiskt rationell styrning. Det är ett
hobbyprojekt som jag sysslar med för att det är kul, och det har varit
mycket roligare att skriva nytt än att rota runt i den gamla
koden. Skam, för att det hela tiden har varit meningen att släppa
koden fri, och den gamla koden var helt enkelt för dålig för att visa
för folk. En kompis har teorin att det här är anledningen till att
inte mer kod är öppen — det är helt enkelt för pinsamt.

Beslutet att släppa koden fri, tillsammans med en viss andra
systemet-effekt
, har gjort att jag i varje steg i utvecklingen har
funderat kring det bästa sättet att göra en viss sak. För att slippa
skämmas måste man helt enkelt skriva så bra kod som man någonsin
kan. När jag skrev det första systemet var jag ganska grön på python,
och försökte mest komma på något sätt att göra samma sak. Det tar
mycket mer tid att göra rätt än att göra snabbt — särskilt i början
— men känslan av yrkesstolthet gör att det är värt det.

Det betyder inte att det inte finns en massa fulheter i koden. Jag är
inte superstrukturerad, och inte min kod heller. När jag kommit på ett
visst mönster för att göra saker överanvänder jag det gärna, fram till
att jag kommer på nästa mönster. Och namngivning samt övrig
kodstandard ska vi inte prata om. Men grunden är stabil. Arkitekturen
är ganska enkel att förklara, även om det är många (drygt 9000) rader
kod. Jag tror att en ny programmerare kan sätta sig in i det hela
ganska snabbt, och att det kan vara ett ganska kul system att vara med
och bygga på.

Dagens patch

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.

Fyra snabba

Det verkar som jag är inne i en bloggningsperiod. Men långa
utläggningar har jag inte tid med, jag måste korta ner en
Techworld-krönika från (just nu) 4356 tecken till 2600 innan jag får
gå och lägga mig. Vi får ta det snabbt:

Kort är det nya svarta: Det började med jQuerys fokus på att förkorta vanliga
javascriptidiom till en eller ett par tokens, fortsatte med Paul
Grahams sneak preview på Arc och dess galet kompakta
uttrycksfullhet
, och idag liknar Steve Yegge babbliga
programmerare (och språk) vid tvååringar.
Juristmetafor: EG-rätt = Java, Handelsbalken = Lisp.

Lagar och kod: En sak som är lite frustrerande är att jag efter tre
års juriststudier fortfarande inte har bra svar på varför lagen är så
svårtolkad och oförutsägbar. Precis i början av studierna, när jag
fortfarande utgick
från det tankesätt som jag tillägnat mig som programmerare
,
undrade jag om man inte kunde ha nån sorts enhetstester för
paragraferna, så att man kunde utvärdera om lagen uppfyllde de syften
den skapats för. Bennet Haselton har utvecklat
ett förslag
längs samma linjer som han tror skulle kunna lösa de
problem han stött på i amerikanska
underrättsdomstolar. Problemställningen saknar helt uppenbart inte
verklighetsförankring, men är det en bra lösning? More to the point,
är det en genomförbar lösning? Jag drar paralleller till den
kurs jag läser just nu
, och dess diskussioner om olika
rättskällors legitimitet. Den rättskälla som för de flesta är the
rättskälla, lagstiftningen, behöver bara ha en yttre legitimitet (vara
utfärdad av någon med folkets eller vapenmaktens mandat) — en
eventuell inre legitimitet (i betydelsen fungerande systematik,
exakthet, logisk konstistens, fullständighet) är bara en
nice-to-have. Åtminstone så länge avsaknaden av inre legitimitet inte
har någon bäring på den yttre legitimiteten (dvs så länge väljarna
inte kräver lagstiftning vars faktiska utformning är begriplig).

Ska vi ha bättre spärrar — eller inga? Utredningen Musik och film på
internet – hot eller möjlighet? (Ds 2007:29)
, med dess förslag att
ålägga ISP:er att stänga av fildelare, har väl (milt sagt) inte blivit
så väl mottagen i bloggosfären, och har även blivit mer
genomgående kritiskt granskad
. På andra sidan Atlanten ställs
också krav på att ISP:er ska ”ta sitt ansvar” för att hejda olaglig
fildelning. Nicholas Weaver föreslår, på bästa code-is-law-manér, en
bittorrentspecifik lösning för att spärra just bara den olagliga
fildelningen, som han menar är en mer ändamålsenlig lösning som
minimerar ”collateral damage”. Min spontana tanke är att en sådan
spärrning – om den implementeras – kommer att påskynda utvecklingen av
mindre darknets
som är ännu svårare att övervaka eller stoppa. Och gör existensen av
en mer ändamålsenlig (”bättre”) spärr det svårare att argumentera för
ett övervaknings- och filtreringsfritt Internet?

Alla måste läsa: Kevin Kelly, Better
Than Free
.

Slutligen: Hela förra veckan gick tankeverksamheten på
halvfart. Inte bra när man har mycket att göra. Så jag fick jobba sent
och hann inte ut och springa något. Igår snodde jag åt mig några
timmar och körde nästan en halvmara runt
kungsholmen och söder
. Idag har jag haft träningsvärk och ett helt
fantastiskt flyt på tankarna. Lärdom (som jag lyckas glömma bort då
och då): Träning är inte en hobby man kan göra i mån av tid, utan en
förutsättning för att man ska orka med allt annat. Ungefär som frukost
(fast svettigare).

Oj, det blev visst fem snabba. Jag är så otroligt busy att jag inte hinner kontrollräkna.

Keep It Simple, Staffan

Mycket med det mesta nu. Några deadlines som närmar sig med oroväckande hastighet, och det jag tänker mest på är den föreläsning jag ska hålla på specialkursen Experimentell immaterialrätt om en vecka. Temat för kursen är ”Immaterialrätten på internet”, och rubriken på min föreläsning är ”Tekniken på Internet”. Det blir lite paragrafer och mycket trebokstäversförkortningar.

Jag satt halva söndagen och försökte strukturera upp vad jag ville säga. Jag vill på något sätt förmedla en insikt i vad digital information och öppna nätverk innebär, den där outtryckbara kärnan från vilken man kan härleda allt annat. Den solida grund som gör att man kan titta på ny teknisk kunskap och förstå hur den ska sorteras in och relateras till allt annat, vilka liknelser som funkar och vilka som är missvisande, problem den löser och problem den skapar, hur den borde regleras (eller inte), och så vidare. Så jag satt i några timmar och försökte formulera en mening om digitalisering som skulle kunna förklara allt det där.

Men allvarligt, det är få förunnat att kunna förmedla kunskap på det sättet. Efter att ha snärjt in mig i högtravande formuleringar om informationens sanna innersta väsen försökte jag sätta mig in i hur det skulle uppfattas av en åhörare. ”Osubstansiellt flummande” blev omdömet.

Så jag raderade texten och började om med inriktning på handfasta kunskaper. 10 minuter om hur man digitaliserar text, ljud, och bild, 10 minuter om komprimering, 10 minuter om grundläggande internet, 10 minuter om tillämpningar som web och fildelningsprotokoll, 5 minuter om DNS… Mindre ambitiöst, men ger förmodligen mycket mer behållning.

I övrigt är jag sjukt sugen på att koda lite. Det blev inte så mycket av det i sommar, och jag har abstinens. Någon gång i slutet av september kan jag förhoppningsvis få tid över till lite mysprogrammering. Utvecklingsversionen av Django har äntligen fått unicodestöd, och jQuery verkar vara ett smutt javascriptramverk nu när mochikit verkar ha ramlat i glömska. Vi får se vad som händer.

Lägesrapport

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!

Culpatic programming

In the legal world, there is this latin term ”culpa”, which translates
roughly to ”carelessness” or ”neglience”. The term does not seem to be
used in US Law, but I’m guessing a similar concept still exists in all
Common Law countries (it might be worth mentioning to the casual
Google refereant that this post is written based in my understanding
of swedish law — if you’re in some other jurisdiction you
should take this with an even larger grain of salt).

The concept of culpa, or rather careless behaviour, is very important
when it comes to assessing whether a certain harmful action (or
sometimes inaction) by a person should make that person liable for
damages. The rules differ somewhat if the two parties were in a legal
agreement of some kind, but basically — if you cause harmful effects,
you’re generally worse off if it was due to carelessness than if it
was by accident (”casus”).

For example, if a supplier did not deliver his goods to the customer
at the agreed time and place, and this was harmful to the customer in
some way, the supplier is more likely to be liable for damages if the
reason for the failed delivery was due to him forgetting about it, as
opposed to there being a unforeseeable traffic jam that prevented him
from reaching the delivery place on time.

To determine wheter a particular action should be categorized as casus
or culpa, a court need to have some sort of guideline as to what is
generally considered to be careless in the context of the harmful
action. In some cases (such as traffic, or working environments), there
are rules that more or less spell out what is considered careless. In
the absence of those rules, courts generally observe what level of
carefulness that is considered adequate by those proficient in the
profession or trade.

In the programming world, there’s a whole lot of ”culpatic
programming” going on — proficient developers know about appropriate
steps to ensure at least some level of quality in their delivered
products, ranging from design methologies to source code management to
customer involvement to established QA techniques. They also know that
these steps often are not followed — for whatever reason. The result
is buggy software that cost time, money and sometimes lives.

Yet, the fact that many programs and systems are carelessly written
ise rarely discussed — most of the time, it is observed that bugs
will happen, a EULA or
other agreement that absolves the company of liabilities due to bugs
in the code is slapped on, and that’s the end of that.

While is true that it’s impossible (in any practical sense) to write
bug-free programs, it is possible to make a meaningful
distinction between bugs cased by carelessness (”culpa” bugs) and bugs
cased by accident (”casus” bugs). The intented usage for the system
will probably affect how the distinction is made (a off-by-one bug
might well be determined to be a ”casus” bug in a admin UI for a
in-house CMS, but is probably a ”culpa” bug in the firmware for a
electronic pacemaker). This also means that software that, when
written, is likely to capture a mass market (such as the RPC server in
the next Windows version) could and should be held to a higher
standard than software written on a hobby basis, when it comes to
determine carelessness.

Often, when the issue of software liability is raised, the
discussion is cut short by the observation that software will always
have bug, and that some of them will cause disastrous effect. It’s
also observed that there is a point in software development where the
cost of finding and fixing the remaining bugs is larger than what the
customers are prepared to pay, and if we make the law demand software
developer liability, no programs (particularly open source ones) will
get released. While it’s true, it’s a all-or-nothing argument. As a
(semi-retired) developer, I’ve caused my fair share of bugs in my
time. Many of these were casus bugs, but some were culpa bugs, bugs
that I would not have created had I followed adequate development
procedures. It’s the latter sort of bug I’m talking about — the ones
that are technologically AND economically feasible to eliminate, but
developers lack incentive to do so (other than pride of
craftmanship).

One large problem is that very few are qualified to determine
whether a bug is culpa or casus. Then again, very few are qualified to
determine if a certain food handling procedure is adequate or
neglient, or wheter a car’s failing braking system was carelessly
designed or not. The legal system still demands that we make a
destinction between culpa and casus in these cases, and does not allow
the food or car industry to sell their products with attached general
disclaimers that frees them from responsibility. Wheter enough care
was taken is determined by experts in the respective fields. For the
software case, this will mean letting independent parties review the
code and particular the conditions of the bug. This will, in many
cases, be expensive, but so are the problems that software bugs
cause.

Part 7: Regression testing

(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 3: Understanding what was fetched

(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

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.