Czyszczenie PyXMPP

Najwyraźniej jest jakieś zainteresowanie moją pythonową biblioteką
XMPP. Ludzie (przynajmniej paru) chcą tego używać, ale wielu od razu się
zniechęca brakiem dokumentacji, inni po próbach zrozumienia o co w tym biega.
Nie dziwię się — kod zamotany jak rzadko, a dokumentacji praktycznie brak.
Bardziej dziwię się ludziom takim jak pete, czy Zgoda, którzy tego używają i nawet mnie
z tego powodu nie męczą. Jednak uznałem, że trzeba coś z tym zrobić…

Więc od paru dni zamiast dodawać nowe ficzery do CJC, poprawiać
w nim bugi, czy implementacje protokołów w PyXMPP to ślęczę nad kodem PyXMPP
i, terroryzowany przez pylinta czyszczę kod,
co jakiś czas robiąc: cvs commit -m "cleaning up...". Przy jakiś
czas coś psuję głupimi literówkami, albo zamierzonymi zmianami w API PyXMPP
(sorry developerzy), np. zamiana nazwy parametru konstruktora klasy Stanza
z type na typ, bo ta pierwsza nazwa zakrywała nazwę wbudowanej
funkcji. Jak już wspominałem większość marudzenia pylinta dotyczy braku
docstringów, a więc je dodawałem na bieżąco. Oczywiście nie
"", ale w miarę konkretną dokumentację, która się może jakiemuś
developerowi przydać. I tu wkracza Epydoc

Normalnie do przetwarzania dokumentacji w pythonie służy narzędzie
pydoc. Jednak jest ono głupie, bo traktuje docstringi jako
czysty tekst, bez formatowania czy hyperlinków. Taka dokumentacja jest mało
czytelna i niewygodna. Już jakiś czas temu szukałem informacji na temat
robienia porządnej dokumentacji do kodu Pythona i dowiedziałem się, że the
right way
to jest (lub raczej będzie) używanie w docstringsch reStructuredText.
Narzędzi do przetwarzania tego wtedy nie znalazłem (słabo szukałem, albo
jeszcze nie było), znalazłem tylko docutils które, między innymi,
takim narzędziem mają się stać, ale na razie umieją tylko parsować
reStructuredText i konwertować to do mądrzejszych formatów.
Mimo braku narzędzia do obróbki tego jednak już wtedy postanowiłem
reStructuredText używać. I nawet użyłem w tych skrawkach
dokumentacji które zdążyłem zrobić.

Teraz przy czyszczeniu kodu znowu szukałem narzędzia do generowania
dokumentacji z kodu. Tym razem znalazłem. Docutils wciąż nie
umieją wyciągać dokumentacji z kodu Pythona, ale mogą być wykorzystane
przez Epydoca, który to potrafi.
Ma on niby swój markup dla docstringów, ale potrafi, przy pomocy
docutils obsłużyć także reStructuredText (ale tylko tworząc
dokumentację w HTML). Więc to właśnie podpiąłem pod doc/Makefile.
Okazało się, że stare kawałki dokumentacji musiałem popoprawiać, żeby
Epydoc sobie z tym poradził. Nowe tworzyłem od razu pod
tandem Epydoc+docutils. I nawet są jakieś
efekty. Otrzymana dokumentacja wygląda nieźle, jest dość przejrzysta… tylko
ma olbrzymie braki. :-( Kiedy ja to wszystko opiszę?

Wieczorem sam w domu…

Przez moją sklerozę żona poszła się upić. )-; A ja zostałem w
domu dziecka (śpiącego na szczęście) pilnować.

Może ten czas spożytkuję na porządki w kodzie PyXMPP.
Do pomocy zatrudniłem pylinta, który mi
pokazuje wszelkie moje grzechy w kodowaniu. Teraz plik
TODO.pylint liczy 1158 linii (905 ostrzeżeń, 68 błędów) mimo,
że i tak pomocnika trochę uciszyłem. Ale lista się kurczy co oznacza,
że jest chyba coraz lepiej. Najczęstrzym moim grzechem jest brak
docstrings, a więc dokumentacji kodu. A w przypadku biblioteki to spore
przewinienie… Jak dobrze pójdzie, to dzisiaj przynajmniej ten największy z
modułów — pyxmpp.stream — uporządkuję.

Zaginiona entropia i stary Python musi odejść

Wczoraj w firmie przeżyliśmy chwilę grozy. Zaczęło się niewinnie. Nie mogłem
commitnąć zmian w systemie zarządzania użytkownikami. svn commit
zawisał, strace pokazywało, że na dostępie do /dev/random. Nie
było to pilne, więc bardzo się tym nie przejąłem. Potem dzwonił gościu, że
poczty nie może odebrać. Chwila śledztwa wykazała, że zacina się na autoryzacji
DIGEST-MD5. Od razu skojarzyłem to z problemami z /dev/random.
Potem było już tylko gorzej — przy niedziałającym /dev/random posłuszeństwa
odmówił też PostgreSQL. A ja ciągle nie wiedziałem co się dzieje.
/proc/sys/kernel/random/entropy_avail pokazywało 0 i nie chciało
nawet drgnąć. Nie pomogło też wybicie procesów korzystających z /dev/random i
/dev/urandom. Wyglądało na to, że coś się zacieło. Ten serwer już długo działał,
a jeszcze nie było takich problemów.

Pomyślałem o sprzętowym generatorze liczb losowych — podobno niektóre płyty
takie coś mają. find /lib/modules -name "*rng*" pokazało dwa
moduły. Jeden (amd768_rng) miał nazwę podobną do chipsetu płyty.
Załadowałem i nawet działa — cat /dev/misc/amd768_rng pokazywało
krzaczki. Jednak aplikacje odwoływały się do /dev/random, a tam
dalej bez zmian. Spróbowałem ln -s /dev/misc/amd768_rng /dev/random
Permission denied, mv /dev/random /dev/random.orig — to
samo. Cholerny devfs – próbuje być mądrzejszy ode mnie! Zawsze lubiłem ideę
devfs — w /dev/ jest tylko to co mam w komputerze, a dysk o
konkretnym SCSI-id podłączony do konkretnego kontrolera ma zawsze tę samą nazwę,
a nie zależną od tego co jeszcze jest podłączone. Jednak implementacja jest
fatalne, a ten przypadek przeważył — devfs wyleciał z tego serwera. I tak
robiliśmy reboot, bo innych pomysłów nie było.

Po reboocie okazało się, że /dev/amd768_rng nie jest całkiem
zgodny z /dev/random — po podlinkowaniu niektóre aplikacje (w
szczególności SVN) zawisały przy próbie korzystania z tegoż. Jednak
/proc/sys/kernel/random/entropy_avail było już większe od zera,
więc problem chwilowo zniknął. Jednak nie na długo — entropy_avail
zaczęło się zmniejszać i znowu doszło do zera blokując wiele usług. Zamiany
/dev/random na /dev/urandom nie brałem pod uwagę – po
to używam SSL, SASL itp. żeby było bezpiecznie, a /dev/urandom bez
entropii generuje liczby bardzo pseudolosowe, a więc do kryptografii
niespecjalnie się nadające. Na szczęście kumpel znalazł winowajcę —
iptables -m random. Jedna taka regułka była w naszym firewallu i po
podłączeniu któregoś klienta najwyraźniej zabrakło na nią entropii. Po zamianie
tego na coś innego entropy_avail urosło do sensownej wartości i
wszystko zaczęło znowu działać. Ufff…

W PyXMPP i CJC obsługa MUC zaczyna działać. Można już wejść na takiego czata
i pogadać. Ale pierwszym felerem, który się rzuca w oczy, są niewłaściwe czasy
przy wiadomościach z historii pokoju. Jest tam czas aktualny, zamiast tego
podanego w <x xmlns="jabber:x:delay"/>. JEP-91 jest banalnie prosty,
więc postanowiłem go od razu zaimplementować. Oczywiste było, że przydałby się
typ opisujący czas, którego w Pythonie zawsze brakowało. Zwykle używałem
mx.Datetime, ale nie chciałem dla takiej dupereli do PyXMPP dodawać
kolejnej zależności. Więc użyłem modułu time, ale on jest do bani.
Można pobrać aktualny czas, przedstawić ją w postaci UTC, czy czasu lokalnego,
czy skonwertować tekst o znanym formacie na czas — ale tylko w czasie lokalnym.
Na zamianę czasu UTC w postaci tekstowej na czas lokalny nie znalazłem sposobu.
Poczytałem dokumentację — z niej wynikało że %Z w formacie
dla time.strptime czasem może działać — u mnie nie działa.
Pogooglałem i okazało się że moje wiadomości na temat braku typu dla czasu są
nieaktualne. W Pythonie 2.3 jest moduł datetime, który daje
wszystko co potrzebuję wraz z sensownym interfejsem. Dotychczas jednak PyXMPP
działało także na pythonie 2.2.x. Chyba przestanie — już dawno zastanawiałem się
nad porzuceniem wsparcia dla Pythona 2.2 — chociażby dlatego, że nie ma w nim
obsługi stringprep, czy IDN (oba standardy wymagane przez XMPP). Teraz PyXMPP
zawiera moją własną implementację stringprep dla Pythona 2.2 (wraz z wymaganą przez nią
normalizacją Unicode — to było sporo roboty), a IDN po prostu nie są obsługiwane gdy używany jest Python 2.2
(użycie znaku nie-ASCII w domenie JID powoduje bład). Ciekawe dla ilu
użytkowników takie podniesienie wymagań będzie dużym kłopotem. A może w ogóle
się tym nie przejmować? W końcu już teraz użytkownicy Debiana muszą kombinować,
żeby z PyXMPP skorzystać, bo standardowo mają historyczne wersje Pythona i
libxml2. Chyba jednak wywalę obsługę pythona 2.2 — kodowi wyjdzie to tylko na
dobre.

jggtrans ma błędy, PyXML ssie, a ja udzielam wywiadów…

Zaraz po wydaniu transportu GG okazało się, że jednak nie zawsze wysyła
<presence/> poprawnie – tzn. z zasobem „GG”. Przez to czasami kontakty GG
są widoczne podwójnie i nie znikają wtedy kiedy trzeba… Czyli niedługo 2.0.5.

PyXML jest tak wooolneeee, że dzisiaj pół dnia spędziłem na szukaniu
alternatywy, bo z taką szybkością działania jabgraph właściwie traci sens.
Oczywiście niepotrzebnie narobiłem pakietów do PLD z dziwnymi parserami,
bo ta przecież już był libxml2, także w postaci modułu do pythona. Zacząłem
więc przerabiać jabgraph z API DOM na API libxml2 – na razie zrobiłem jedynie
wczytywanie pliku konfiguracyjnego ale już witać dużą poprawę.

Udzieliłem też wywiadu – poważnie to brzmi :). Pytania (z błędami
ortograficznymi) zostały wysłane na listę dyskusyjną i „osoby upoważnione”,
czyli „ważne persony” miały na nie odpowiedzieć. Mnie też przypadł ten zaszczyt,
stąd wzmianka w blogu :). Trzeba przyznać, że same pytania były merytorycznie
OK – jestem ciekaw jak odpowiedzą pozostałe „persony”.

Jeszcze ciekawostka – jak zrobiłem sobie ikonki do transportów w tkabberze.
Umieściłem następujący fragment w ~/.tkabber/config.tcl:

proc postload {} {

	# ... inne ustawienia
    set roster::show_only_online 1
    set roster::show_transport_icons 1
    set roster::show_transport_user_icons 1
    set roster::roster(cached_category_and_subtype,jit.jabber.bnet.pl) {service icq}
    set roster::roster(cached_category_and_subtype,headlines.jabber.bnet.pl) {service rss}
    set roster::roster(cached_category_and_subtype,gg.jabber.bnet.pl) {service x-gadugadu}

    global serviceicon
    set serviceicon(x-gadugadu,available)   [Bitmap::get [pixmap services gg_online.xpm]]
    set serviceicon(x-gadugadu,chat)        [Bitmap::get [pixmap services gg_chat.xpm]]
    set serviceicon(x-gadugadu,away)        [Bitmap::get [pixmap services gg_away.xpm]]
    set serviceicon(x-gadugadu,xa)          [Bitmap::get [pixmap services gg_xa.xpm]]
    set serviceicon(x-gadugadu,dnd)         [Bitmap::get [pixmap services gg_dnd.xpm]]
    set serviceicon(x-gadugadu,unavailable) [Bitmap::get [pixmap services gg_offline.xpm]]
}

Ikonki przerobiłem z pakietu ikonek dla Psi jaki znalazłem w „plikowni”.
IMHO mogłoby to być zrobione lepiej – ale tak też jakoś działa. Może podyskutuję
jeszcze z autorami tkabbera na ten temat.

PyXML nie taki straszny

Jednak PyXML ma wszystko co trzeba… z wyjątkiem dokumentacji. Okazało się
że można dokument wczytać jako obiekt DOM, jednocześnie go walidując. Przy
okazji znalazłem parę innych przydatnych mi funkcji. Ale to wszystko dopiero po
przejrzeniu źródeł, bo dokumentacja okazała się do niczego :-(.

Grzebię się w tym XMLu, żeby napisać programik do rysowania wykresików ze
statystyk Jabberowych otrzymywanych za pomocą protokołu
JEP-39. Transport
GG oczywiście ten protokół obsługuje, więc przynajmniej do niego będzie
można jabgrapha zastosować. Jabgraph już prawie skończony, poprosiłem
już o odpowiedni projekt na JabberStudio i, jeśli nic mi nie pokrzyżuje
planów, lada dzień powinien być dostępny do ściągnięcia.

Tymczasem transport GG po ostatnich poprawkach ciągle strasznie
niestabilny, a ja nie mogę znaleźć błędu który to powoduje –
użytkownicy jabber.bnet.pl niedługo gotowi będą mnie zlinczować.

Koszmar XML w pythonie o raz inne duperele

Chciałem wczytać plik XML, zwalidować go zgodnie z pewnym DTD,
przetwarzać poprzez API DOM i wypluć uzupełniony. Nic specjalnego. To co
jest załączone w pythonie (minidom) ani nie waliduje, ani nie potrafi
ładnie wypluć (taki pretty-print z wcięciami itd.). Jest też PyXML. Ma
kompletny DOM, z PrettyPrint oraz parser walidujący – można użyć
albo jednego, albo drugiego. Dwa razy tego samego pliku
parsować nie mam zamiaru – walidację sobie odpuściłem. Do tego doszedł
jeszcze jabber.py z własnym parserem i niby-DOMem. 😦 A mogłoby być tak
pięknie – python to wspaniały język, tylko coś porządnej biblioteki
dorobić się nie może.

Transport GG po ostatnich przeróbkach mi się wywala. Ale oczywiście
nigdy pod gdb. To znaczy raz mi się wywalił pod gdb i jeden głupi błąd
poprawiłem, ale wywala się nadal.

Córeczka właśnie się drze z krzesła. Nie mam pojęcia jak na nie
wlazła… Wcześniej przeglądała (z bardzo mądrą miną) „Układy
półprzewodnikowe” Tietze’a i Schenka. Czy nie za szybko ona nam
dorośleje (przypominam ma roczek).

Znalazłem dzisiaj też coś dla wszystkich co się uważają (tak jak ja)
za nerdów i/lub geeków. Przeczytajcie kto to:
nerd
i geek.
Szczególnie pierwsze znaczenie (w obu przypadkach) oraz trzecie (w
przypadku geeka) jest ciekawe 🙂 .