PyXMPP 2

Osiem lat temu coś mi odbiło i zacząłem tworzyć PyXMPP. Wtedy jeszcze właściwie nie było XMPP (specyfikacja dopiero powstawała, RFC 3920 wydano dopiero ponad rok później), tym bardziej żadnej biblioteki XMPP dla Pythona, czy nawet jakiejkolwiek implementacji XMPP. Było jakieś jabber.py, ale mnie nie zadowalało. Zacząłem więc pisać własną bibliotekę, najpierw z myślą o serwerze, potem dla mojego klienta – CJC.

Wtedy musiałem pisać wszystko od zera, SASL, Stringprep, jakieś sensowne StartTLS, bo dostępnych implementacji nie było, albo nie nadawały się do sensownego, czy bezpośredniego użycia. Język w którym pisałem (Python 2.2) nie przypominał bardzo dzisiejszego Pythona. Testować też nie było z czym, bo właściwie żaden serwer SASL czy StartTLS nie obsługiwał (dlatego myśląc o implementacji serwera musiałem też pisać kod klienta). Myślę, że były nawet czasy, gdy moje PyXMPP było „tą jedyną sensowną” biblioteką XMPP dla Pythona.

Niestety, niektóre moje decyzje zaczęły na projekcie ciążyć. W szczególności wykorzystanie libxml2. API zupełnie „niepythonowe”, gdzie trzeba pamiętać o zwalnianiu pamięci o tym który obiekt używa pamięci przydzielonej innemu obiektowi. To sprawiało nie tylko, że kod był brzydki, ale się po prostu czasem sypał w niewyjaśnionych okolicznościach, ciekł pamięcią i średnio nadawał się do integracji z czymkolwiek innym. Dodatkowo API libxml2 używało stringów zakodowanych UTF-8, gdzie w Pythonie używałoby się raczej obiektów unicode, co wymagało ciągłych konwersji. Mimo wszystko libxml2 miało swoje zalety, szczególnie nad alternatywami dostępnymi w tamtym czasie: było szybkie, obsługiwało przestrzenie nazw XML i serializowało XML do postaci strawnej przez implementacje XMPP (ElementTree sam z siebie wciąż tego nie umie).

Mijały miesiące i lata. PyXMPP rozwijało się, najpierw szybko, potem powoli, z ewentualnymi gwałtownymi zrywami. Stary kod coraz mniej mi się podobał, ale był i działał, szkoda było dużo zmieniać. Czasem próbowałem wprowadzić jakieś nowe pomysły (np. interfejsy z Zope), z średnim skutkiem. W końcu rozwój praktycznie zamarł, z wyjątkiem drobiazgów których czasem zachciałem w CJC (np. ostatnio IPv6)…

W między czasie pojawiło się wiele implementacji XMPP, także w Pythonie. Nie tylko biblioteki ale i całe popularne klient (chociażby Gajim). Pojawił się Python 3, wyszły nowe RFC dla XMPP. PyXMPP praktycznie w ogóle się teraz nie liczy… a mnie dwa miesiące temu znowu „odbiło”…

Postanowiłem spróbować przerobić stary kod na coś nowocześniejszego. Przede wszystkim miało wylecieć `libxml2` i być zastąpione przez ElementTree. Przy okazji chciałem wyczyścić API i w ogóle poprawić jakość kodu. W starym nie tylko były jeszcze miejscami konstrukcje z Pythona 2.2 (np. ‚0’ i ‚1’ zamiast ‚True’ i ‚False’), to jeszcze kiedyś miałem zwyczaj oszczędzania na spacjach, przez co kod był po prostu nieczytelny. Postanowiłem przepisać kod pod kątem Pythona 2.7 ale z myślą o obsłudze też 3.2.

Tak sobie grzebałem w tym przez ostatnie parę tygodni. W modułach które przerobiłem zmieniłem praktycznie każdą linijkę kodu. Miejscami wprowadziłem fundamentalne zmiany w całej wewnętrznej strukturze pakietu. Napisałem nowy framework pętli głównej i I/O. Wiem, wymyślanie na nowo koła, jest Twisted, ale przecież implementacje XMPP na Twisted też już są…

Powoli kod zaczął nawet być używalny… postarałem się go jakoś udokumentować, napisałem trzy przykładowe skrypty, jakiś tutorial na wiki… i wczoraj postanowiłem pokazać to światu.

Nie wiem na co liczyłem, w końcu teraz to tylko „kolejna implementacja XMPP dla pythona”… ale może komuś się przyda… może w czymś jest jednak lepsza od istniejących. No i szkoda by było, jakby mój kod pisany od 2003 roku umarł jako to brzydkie stare PyXMPP (tak, to nie jest racjonalne myślenie)…

Od wczorajszego wydania ‚alpha’ znalazł się tylko jeden ‚obserwujący’ na GitHub, jedna odpowiedź na maila na liście dyskusyjnej i 30 pobrań pakietu… No cóż, kariery tym nie robię. ;-)

Reklamy

CJC i PyXMPP 1.0.1

Ostatnie wydanie CJC i PyXMPP było ponad trzy lata temu – w grudniu 2005 roku. Po tamtym wydaniu zacząłem poważne zmiany w API… i nigdy ich nie skończyłem, a niedokończonego dzieła wydawać nie chciałem. Potem przyszły kłopoty ze zdrowiem, pośrednio wynikłe też z mojego zaangażowania w Open Source (czyt. siedzenia całymi dniami przed kompem, świata nie widząc) i postanowiłem unikać angażowania się w kodowanie. Tak więc, planowane zmiany nigdy nie miały być dokończone.

W międzyczasie jednak w starym kodzie znajdowane były błędy, od czasu do czasu jakiś błąd poprawiłem, czasem ktoś podesłał patcha, to wrzucałem do SVN. Pojawiały się nowe wersje Pythona, czy M2Crypto, to kod dostosowywałem odpowiednio (CJC używam na co dzień, moja żona też, więc to musi działać). Właściwie, jak ktoś jeszcze używał PyXMPP albo CJC, to któryś z ostatnich snapshotów.

Ostatnio zastanawiałem się też, czy nie spróbować do CJC wreszcie dodać obsługi UTF-8. Wcześniej tego odmawiałem, bo Pythonowy moduł curses w ogóle nie ma obsługi Unicode, tylko start 8-bitowe API. I nic nie wskazuje na to, że to ktoś poprawi, ale uż jakiś czas temu jeden z użytkowników udowadniał mi, że to się jednak da zrobić, jeśli tylko moduł curses jest zlinkowany z biblioteką „libncursesw”. W PLD długo tak nie było, ale w Th już jest. To czemu by nie spróbować?

Wczoraj więc spróbowałem. Wyświetlanie UTF-8 działało bez żadnych zmian w kodzie, ale żeby wczytywać znaki Unicode z klawiatury musiałem dodać brzydkiego hacka. I w ogóle trochę obsługi klawiatury przerobić. Udało się. A skoro wreszcie się pojawiła funkcjonalność tak wyczekiwana przez użytkowników, to czemu by nie wydać nowej wersji? Resztę dnia spędziłem
przygotowując paczki z PyXMPP 1.0.1 (w tym jeszcze przed wydaniem poprawiłem jednego paskudnego babola) i CJC 1.0.1. Dzisiaj wysłałem informacje o wydaniach na listy mailowe dotyczące tych produktów. No to chyba udało mi się coś wydać. :-)

Po następne wersje zapraszam za trzy lata. ;-)

uinput_beeper, czyli jak zrobiłem sobie biperka w nowym laptopie

Podczas mojego pobytu w Reptach mój stary laptop umarł. Do zacinającej się klawiatury dołączyła wysiadająca grafika i w końcu zupełnie nie dało się z niego korzystać. No cóż, przeżył swoje. Tak więc w zeszłym tygodniu kupiłem sobie na allegro nowego, ślicznego i fajniusiego Della D620, a od poniedziałku już się nim bawię.

W laptopie właściwie wszystko działało od razu bez zarzutu. No, mały hack był potrzebny, żeby framebuffer działał w natywnej rozdzielczości. Niestety jeden feler wydał mi się poważniejszy: głośności pc-speakera nie da się kontrolować. Gdy nie załadowałem modułu pcspkr nie brzęczał w ogóle, gdy załadowałem – odzywał się na cały głos, co było nie do przyjęcia. Musiał zostać niezaładowany, ale bez biperka jest ciężko. Dla mnie konsola tekstowa to wciąż podstawowe narzędzie i mam tam np. odpalonego klienta Jabbera. I gdy przychodzi wiadomość, to klient pika… a mnie nie pikał. I było mi smutno. ;-)

Z wcześniejszego goolania wynikło, że to ograniczenie sterowników ALSA do tej wypasionej karty muzycznej HD, którą mam w laptopie i że właściwie powinienem się cieszyć, że mnie gra za głośno, albo wcale, bo innym tylko wcale.

Dzisiaj postanowiłem poszukać jakiejś alternatywy dla standardowego dźwięku, którego nie da się zciszyć. Uznałem, że powinno się dać podpiąć jakąś własną akcję w miejsce tego co robi moduł pcspkr. Okazało się, że mam rację. Jednak, o dziwo, wygodnego gotowca nie znalazłem. Znalazłem tylko dodatkowy moduł do kernela beeper, który współpracował z odpowiednimi demonami w userspace, ale z kompilacją dodatkowych modułów zawsze jest problem. A przecież dowiedziałem się też, że standardowy kernel ma już funkcjonalność potrzebną do obsługi bipania w userspace, bez potrzeby dodawania niestandardowych modułów. Można to zrobić przez interfejs uinput. Nawet znalazłem jakiś przykładowy kod.

Przykładowy kod okazał się być chyba pisanym z pamięci – brakowało includów itp. i się nawet nie kompilował. Do tego używał API esd, a to przecież bez sensu. Na szczęście przykładowy kod zawierał co najważniejsze i po przejrzeniu go i zerknięciu do źródeł kernela byłem w stanie napisać coś użytecznego.

Tak więc powstał uinput_beeper – prosty programik mojego autorstwa, który uruchamia zewnętrzny program za każdym razem, gdy terminal ma zabibczeć. Domyślnie jest to aplay beep.wav. I działa to świetnie, a przy okazji, z przygotowanym przeze mnie na szybko plikiem beep.wav, jest nawet zabawne (ale pewnie nie długo). :-) Kod oczywiście dostępny na GPL, może jeszcze komuś się przyda.

Odświeżenie bloga

Postanowiłem trochę odświeżyć mojego bloga. Najpierw zmieniłem moje
skrypty do generowania joggerowych szablonów – generowanie z XMLa
i automatyczne walidowanie wyniku było fajne w założeniach, ale w praktyce
chyba zbyt skomplikowane. Przesiadłem się na M4 i jest fajnie.
:-)

Później postanowiłem dodać trzecią kolumnę do layoutu. Archiwum
i kategorie chciałem przenieść na lewą stronę, a po prawej dać
linkownie. Szablon przerobiłem, ale efekt mi się nie podobał. Do tego,
gdy przyszedł czas na dodanie linków, stwierdziłem, że nie umiem się
zdecydować co tam wrzucić – wszystko co przeglądam, to za chwilę połowa
byłaby nieaktualne, a jeśli miałbym wybierać te najważniejsze, to niby
jak? W końcu zmiany w szablonie zachowałem sobie gdzieś na boku w postaci
łatki i wróciłem do starych, dobrych dwóch kolumn.

Czyli wizualnie byłem w punkcie wyjścia. A przecież chciałem coś
pozmieniać. Najbardziej mnie wkurzało archiwum – ciągnące się jak papier
toaletowy. Wczoraj, odrobiną magii w JavaScripcie, dodałem do niego
hierarchię, a dzisiaj zwijanie niepotrzebnych lat. Z efektu jestem bardzo
zadowolony – chyba całkiem nie źle, jak na kogoś, kto nie zna
JavaScriptu. :-)

Poza tym, dołożyłem linki do następnej/poprzedniej strony w szablonie
wpisów i do następnego/poprzedniego wpisu w szablonie komentarzy. A na końcu
dorzuciłem reklamy AdSense. Miałem tego na bloga nie dawać, ale na stronach
moich projektów niewiele te reklamy dawały – nic się tam nie dzieje, to
nikt nie zagląda. A na Joggerze kolejne osoby dodają sobie reklamy na bloga
i nikt na nich nie krzyczy, więc może i mnie czytelnicy wybaczą.
;-)

Rewolucja w kompie

Niedawno pojawiło się nowe XFCE: 4.4.
Najpierw chciałem to w pracy sobie zainstalować, ale okazało się, że ani tego
nie ma w PLD Ac, ani nie da się w tym Ac zainstalować. Instalacji Th wolałem
nie ryzykować na maszynie która służy mi do pracy… co innego w domu, tam
ostatnio używam jedynie CJC i Firefoksa. Uznałem, że dwie aplikacje jakoś do
działania doprowadzę.

Upgrade nie był prosty. Masy rzeczy w repo Th brakuje. Wiele zależności
jest zepsutych. Cała masa pakietów zbudowanych w Ac wymaga X11-*, albo
XFree86-*, których w Th już nie ma (wystarczyłyby zależności od libX*, które
pociągnęłyby odpowiednie pakiety xorg-*). Dodatkowo, żeby nie było za prosto,
mam u siebie pomieszane pakiety 32- i 64-bitowe. Ale jakoś się udało…

Pierwsze co chciałem odpalić, to Xy. W końcu dla nowego XFCE jest ta cała
szopka… Poprzednio używałem 64-bitowego X-serwera i zamkniętych sterowników
ATI. Otwarte nie dawały akceleracji, a zamknięte nie działały, gdy X-serwer był
32-bitowy (generalnie większość systemu mam 32-bitowe), a kernel 64-bitowy (bo
tylko taki pozwala mi odpalać binarki i 32- i 64-bitowe). Teraz chciałem
spróbować z serwerem 32-bitowym i sterownikami Open Source, w końcu między X.org
7.0, a 7.2 coś mogło się zmienić…

Jedno się nie zmieniło – sterownik z X.org wciąż nie rozpoznaje mojej
karty (ATI Technologies Inc RV370 secondary [Sapphire X550 Silent]) i obsługuję
ją dopiero po dodaniu do xorg.conf: ChipId 0x5b60. Akceleracja 3D też nie
ruszyła, chociaż sterownik DRM w kernelu się załadował i kartę poprawnie
wykrył… logi sugerowały, że znowu może być coś z tymi bitami… Zainstalowałem
więc 64-bitowy X-serwer z 64-bitowymi sterownikami. DRI ruszyło. Rozszerzenie
„Composite” też. Mogłem podziwiać piękną przezroczystość w XFCE. Najpierw
działało to strasznie wolno, ale po poprawieniu paru opcji da się tego używać.
Jednak z OpenGL coś wciąż było nie tak… mimo że glxinfo pokazywało, że
wszystko jest OK i nawet Direct rendering: yes. To glxgears nie działało.
Znaczy się działało, ale nic nie wyświetlało. Podobnie wszystkie inne aplikacje
3D…

Spróbowałem więc zamkniętych sterowników… musiałem przebudować, bo w Th
były stare, niekompatybilne z nowymi Xami. Nowe się zbudowały, nawet działały,
ale bez akceleracji 3D. Linker dynamiczny nie mógł znaleźć jakiegoś symbolu od
DRI w modułach X-serwera… pewnie te zamknięte binarki nie są zgodne z naszym
buildem X.org… no cóż, tym razem lepiej wypadły sterowniki otwarte.

Wróciłem do otwartych sterowników i spróbowałem jeszcze czegoś:
zainstalowałem 64-bitowe glxgears… i ruszyło. Czyli tym razem, pod 64-bitowym
kernelem, nie tylko X-serwer musi być 64-bitowy, ale i wszystkie aplikacje
OpenGL. Przykre… ale chwilowo mogę z tym żyć. Ostatnio i tak wiele takich
aplikacji nie używam. Właściwie, to tylko StepManię.

Dzisiaj więc postanowiłem sobie skompilować StepManię na 64-bity. Środowisko
do budowania zrobiłem sobie w chroocie z czystym 64-bitowym Th. Zainstalowałem
co StepMania potrzebowała i zacząłem budowanie. Od razu się wykrzaczyło… SM
jest w C++, a C++ ma to do siebie, że kod napisany dla starszego kompilatora
często nie da się skompilować nowszym. W PLD Th mamy GCC 4.2 i to GCC kodu
StepManii nie polubiło. Jednak potrzebnych poprawek nie było dużo i większość
z nich była nawet dla mnie (nie znającego i nie lubiącego C++) oczywista.
W końcu się skompilowało. I nawet dało się uruchomić.

Skompilowana przez mnie StepMania działała, ale tak jakby nie do końca. Nie
dało się wejść do ustawień gry, a rozpoczętej gry nie dało się wygrać. Jak się
poprawnie przeszło którąś piosenkę, to maszyna puszczała ją od początku.
Można by się było zajechać ;-). Podczas kompilacji widziałem
warningi dotyczące, między innymi, strict aliasing rules, więc
postanowiłem spróbować kompilacji z innymi opcjami. Okazało się, że skrypt
configure StepManii na sztywno ma wpisane -O3. To już mogło sprawiać
problemy. Zmieniłem na -O2 i dodałem -fno-strict-aliasing i całość
skompilowałem od nowa. Podczas kompilacji ćwiczyłem sobie na tej niedorobionej
binarce – ciekawie się gra, gdy komputer się czasem zagapi i nie
zauważy
, że się strzałkę wcisnęło na czas. ;-)

Po kilkunastu minutach miałem nową 64-bitową binarkę gotową. I tym razem
działa dobrze. To sobie jeszcze kiedyś poskaczę. :-)

W kolejce czeka zbudowanie GComprisa dla
Krysi, bo stare wyleciało razem z Ac (w Th tego brak).

Jak homofobi zniszczyli geniusza

Dziwię się, że tej historii nie znałem wcześniej. O samym Alanie Turingu trudno
było nie słyszeć – maszyna Turinga i test Turinga, to terminy znane
każdemu informatykowi. Biografią tego naukowca nigdy się specjalnie nie
interesowałem, aż do dzisiaj, gdy przeczytałem wzmiankę o nim w
artykule o badaniach naukowych dotyczących seksualności owiec
.

Odkrycie zaskoczyło mnie na tyle, że od razu zajrzałem na Wikipedię, czy
tam ta historia też jest odnotowana. Jest.

Alan Turing, jeden z największych (jeśli nie największy) geniuszy
w historii Informatyki, był gejem. W tamtych czasach było to uznawane za
przestępstwo. Naukowca osądzono, skazano i postawiono przed wyborem: albo
podda się terapii, albo wyląduje w więzieniu. Niezależnie od tego, jako
skazany wyrokiem sądu, nie mógł już pracować jako kryptolog w rządowej
agencji.

Turing wybrał terapię. Polegała ona na przyjmowaniu estrogenów w celu
zmniejszenia libido. Nie trudno się domyślić działania estrogenów na organizm
mężczyzny: został impotentem i rozwinęły mu sie piersi. Niedługo później zmarł
zatruty cyjankiem – najprawdopodobniej popełnił samobójstwo, gdyż nie
mógł sobie poradzić z życiem po wyroku. Geniusz, który bardzo przyczynił się
do rozwoju Informatyki, został poniżony i popełnił samobójstwo przez głupie
uprzedzenia… W dwudziestym wieku… A ja myślałem, że palenie czarownic na
stosie to taka odległa historia…

Committed revision 666.

Mam dosyć sztywnego trzymania się niektórych zasad dobrego stylu
programowania. Pisząc CJC starałem się
w ogóle nie używać zmiennych globalnych, bo przecież każdy wie, że tak trzeba.
No i dało się, jednak z tak zrobionego kodu nie byłem zadowolony. Mam tam
takie trzy obiekty, właściwie singletony, do których dostęp potrzebny był
w bardzo wielu miejscach. No i dostęp ten odbywał się właściwie na dwa
sposoby: albo referencja do takiego obiektu była przekazywana w konstruktorze i potem
zapisywana w atrybucie obiektu który tego potrzebował, albo dostęp odbywał się
przez inne obiekty (np.: self.plugin.app.screen) –
jedno i drugie nie wyglądały najlepiej. Standardowym, Javowe,
rozwiązanie, w postaci statycznej metody zwracającej instancję singletona,
niespecjalnie mi pasowało – to takie niepythonowe odwoływać się do
klasy, gdy tylko jej instancja jest potrzebna. Do tego to więcej pisania by
było, a przecież miałem API uprościć.

W końcu zdecydowałem się na rozwiązanie proste i skuteczne –
zrobiłem moduł cjc_globals z trzema zmiennymi globalnymi. I wszystko
byłoby dobrze, gdyby nie komunikat przy commicie, wyraźnie sugerujący, że
za moją decyzją stoi Zło: Committed revision 666.
;-)