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. ;-)

Kalendarzowo

Ja mam pamięć dobrą, ale krótką – jak to mój tata mówi. Potrafię
zapomnieć o wszystkim, jak mi się w porę nie przypomni. Pół biedy jak zapomnę,
że właśnie posłodziłem herbatę i wrzucę kolejne trzy łyżeczki (serio, zdarza
mi się), gorzej jak się np. z kimś umówię, a potem nawalę.

Dlatego zawsze ciągnęło mnie trochę do elektronicznych przypominaczy.
Jednak zwykle i tak nie znajdowałem dla siebie nic odpowiedniego. Nie może to
być coś, do czego miałbym ciągle zaglądać (dlatego odpadają papierowe
terminarze), najfajniej by było, jakby nie trzeba było nawet tam nic wpisywać
(ale tego to już się raczej nie przeskoczy)… Dobrze, żeby wpisać można było
wszystko, i przypominać o ważnych sprawach mogłoby to mi zawsze i wszędzie (no
chyba, że śpię – budzić mnie nie może)…

Próbowałem paru kalendarzy komputerowych. Kombajnów typu Evolution nawet
nie tykałem – do poczty mam mutta, a nie kalendarz! W ogóle chciałem
uniknąć GUI – uruchomienie Xów nie powinno być wymagane dla
przypomnienia o ważnej sprawie. Z drugiej strony… jak już te Xy mam odpalone
(zwykle mam), to fajnie jakby przypomnienie było w jakimś okienku… Konsolowe
programiki tego typu miały jeszcze jeden feler – jakiś własny format
danych niekompatybilny z niczym. Właściwie obsługa czegoś takiego niewiele się
różniła od obsługi crontaba – do sensownego używania trzebai by sobie to
nieźle oskryptować. A ja przecież chciałem coś, co by mi samo
przypominało, a nie wymagało programowania…

Oczywiście praktycznie wszystkie rozwiązania oparte o komputer miały pewną
wadę: mogły mi o czymś przypomnieć tylko wtedy, gdy akurat byłbym przy
komputerze. Czasem nawet wymagały, żeby je specjalnie uruchomił. Dlatego
doceniłem aplikację kalendarza w telefonie. Wtedy to był jakiś pierwszy
Sony-Ericsson. To była pierwsza przypominajka, którą w miarę regularnie
używałem. Jakieś wizyty u dentysty itp. tam wpisywałem. Potem, w dużo
prostszym Samsungu X200 też. Już wtedy uznałem, że fajnie byłoby komórkowy
terminarz synchronizować z komputerowym, ale szybko się przekonałem, że raczej
nic mi z tego nie wyjdzie. Po pierwsze wciąż brakowało mi odpowiedniego
kalendarza w komputerze, a po drugie wymiana jakichkolwiek danych między tym
Samsungiem a Linuksem była praktycznie niemożliwa (odniosłem umiarkowany
sukces jedynie w przypadku książki adresowej).

Używając sobie po trochy komórkowego kalendarza wciąż kombinowałem
z kalendarzami w komputerze. Gdy usłyszałem, że Google Calendar potrafi wysyłać
SMSy, to postanowiłem to wypróbować. Nie bardzo mi się podobało, że swoje
plany, czy wręcz rozkład dnia, miałbym publikować w sieci, ale coś za coś.
Wyglądało na to, że w tej aplikacji Google mógłbym łatwo wprowadzać swoje
terminy, a przypomnienia przychodziłyby mi na komórkę. Przy okazji dostałbym
parę łebdwazerowatych bajerów, jak publikacja kalendarzy, czy rozsyłanie
terminów do znajomych. Przez jakiś czas się tym bawiłem, powpisywałem różne
terminy… czasem dziwne rzeczy z tego wychodziły (aplikacja nie działała
idealnie). A najgorsze było to, że Google potrafiło mi wysyłać SMSy w środku
nocy, co było dość nieprzyjemne, gdy zapomniałem wyłączyć telefon… No
i aplikacja webowa, to nie to, co tygryski lubią najbardziej.

Jakiś czas później trafiłem na ogłoszenie o nowym wydaniu Sunbirda.
Pomyślałem sobie, że to może wreszcie będzie jakaś użyteczna aplikacja
kalendarzowa pod Linuksa. Trudno, że GUI. Przynajmniej używa jakiś otwartych
standardów… i nawet jest wtyczka do Google Calendar. Zainstalowałem sobie
więc tego słonecznego ptaszka i zasubskrybowałem swój googlowy kalendarz.
Rzeczywiście dało się tego używać. Żeby nie publikować wszystkich swoich spraw
w Google zrobiłem sobie dodatkowy lokalny kalendarz. Potem
z lokalnego zrobiłem sieciowy (opublikowany przez WebDAV na moim
serwerku), bo przecież nie z jednego komputera korzystam. Pobawiłem się
trochę… i jakoś nie zacząłem regularnie z tego korzystać. Jak coś było na
tyle ważne, że potrzebowałem przypomnienia, to i tak wpisywałem w komórkę. Dla
innych spraw nie chciało mi się kalendarza odpalać.

W końcu kupiłem sobie nową komórkę. Już wybierałem pod kątem obsługi jakiś
standardów wymiany informacji. Zakładałem, że taką nowoczesną komórkę,
obsługującą standardy łatwo ze swoim Sunbirdem zsynchronizuje. Rzeczywistość
okazała się niestety nie taka piękna. Do sychronizacji takich urządzeń pod
Linuksem jest właściwie tylko OpenSync (inne aplikacje zwykle korzystają z tej
biblioteki), a ja nieźle musiałem się nagimnastykować, żeby tym jakiekolwiek
dane między komputerem a telefonem wymienić. A przy synchronizacji kalendarza
znikały z terminów informacje o przypomnieniu, czyli to, co dla mnie
najważniejsze. Zgłosiłem swoje problemy na odpowiednią listę mailową i nic.
Nie wygląda, żeby w tej kwestii miało się coś zmienić.

Nie poddałem się jednak. Skoro telefon ma funkcje synchronizacji, to musi
się dać to wykorzystać. Jest funkcja synchronizacji online, może to się nada?
Najpierw sprawdziłem Google Calendar, przecież taka aplikacja, takiej poważnej
firmy musi mieć taką funkcjonalność… gdzie tam. Wygląda na to, że Google
SyncML zignorowało. No to
rozejrzałem się za jakimiś publicznymi serwerami SyncML. Spośród paru
kandydatów najbardziej przekonało mnie ScheduleWorld. Po poprzednich
doświadczeniach miałem spore wątpliwości czy to zadziała, ale spróbowałem…
i zadziałało.

Teraz na komputerze mam Sunbirda, w nim swój sieciowy kalendarz
i wtyczkę (rozszerzenie) ScheduleWorld. Jak dodam na komputerze termin i wcisnę
sync, to po uruchomieniu synchronizacji w telefonie i tam się ten
kontakt pojawi. Tak samo w drugą stronę – kontakt dodany w telefonie
pojawi się na komputerze. Zachowane są przy tym informacje o przypomnieniach.
Był jeden problem – zdarzenie wpisane w komputerze na całą dobę
w telefonie było widoczne jak dwudniowe (od 00:00 jednego dnia, do 00:00
drugiego dnia). Okazało się jednak, że wystarczy zaznaczyć jedną opcję
w ScheduleWorld i problem zostaje rozwiązany. Jest tam też sporo innych
przełączników, umożliwiających ominięcie potencjalnych problemów z innymi
urządzeniami.

Do pełni szczęścia brakowało mi jeszcze jednej funkcji – jakieś
minimum funkcjonalności bez potrzeby odpalania Xów. No to napisałem sobie
Pythonowy skrypt, przy użyciu biblioteki icalendar, który wyciąga dzisiejsze
i jutrzejsze terminy z mojego kalendarza (niech żyją otwarte formaty plików!).
Lista ta jest wyświetlana przy logowaniu. Wydaje mi się to rozsądnym sposobem
przypominania mi o rzeczach, dla których alarm z telefonu byłby przesadą.

No to wydaje mi się, że już mam użyteczny zestaw, z którego będę w stanie
regularnie korzystać. Jedyna słabość, to publiczny serwer, który pośredniczy
w synchronizacji z komórką… no cóż, czasem można poświęcić odrobinę
prywatności dla wygody. I chyba nie mam zbyt dużych powodów, żeby
ScheduleWorld nie ufać.

Teraz pytanie: czy rzeczywiście będę z tego wszystkiego korzystał, czy
dalej tylko raz na ruski rok coś sobie w komórkę wklepię?

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.
;-)

Pythonowe frameworki…

(wpis z wczoraj)

Pierwszy Pythonowy framework dla aplikacji webowych jaki poznałem to był Zope 2. Potrzebne mi było coś takiego do
stworzenia interfejsu użytkownika dla naszego systemu zarządzania siecią. W PHP
babrać się nie miałem zamiaru, a robienie całości jako CGI to byłaby mordęga.
Padło na Zope’a, nic innego nie było, a przynajmniej nic na tyle popularnego,
żeby obiło mi się o uszy. Zope, z jednej strony potężna maszyna, z drugiej
wkurzające monstrum. W szczególności, że źle się za niego zabrałem. Wszystkie
szablony robiłem w DTML (bo wyczytałem w ZopeBook, że równie dobrze jak ZPT,
a dla programistów nawet lepsze), a cały kod rozwijałem through the web,
a więc to było bardziej skryptowanie a’la PHP, niż poważne tworzenie aplikacji.
W końcu przekonałem się do ZPT (bardzo porządny język szablonów, pozwalający
w miarę dobrze oddzielać logikę od prezentacji i utrudniający tworzenie
niepoprawnego XHTML), ale kod dalej rozwijany jest TTW, bo zmiana tego, to byłoby pisanie aplikacji (sporej już) od
zera.

Widząc słabości Zope zacząłem się rozglądać za alternatywami. Żeby
alternatywę poznać to trzeba spróbować, postanowiłem więc pobawić się czymś
innym. Po wstępnym rozpoznaniu tego co jest na rynku, postanowiłem spróbować od
CherryPy. Próba polegała na
przeportowania mojego generatora i przeglądarki statystyk dla Joggera (ta
czerwona kropa poniżej) na ten framework. Wcześniej to było zrealizowane
w postaci skryptów CGI z prymitywnymi printami. Zrezygnowałem
z języka szablonów wbudowanego w CherryPy, bo znając ZPT nie chciałem się
babrać w czymś nie opartym o XML (nie pilnującym składni generowanego kodu),
użyłem więc SimpleTAL. Sportowałem tak dużą część tego generatora statystyk,
z błędami i dałem sobie spokój (zająłem się innymi rzeczami). CherryPy na
początku zrobiło niezłe wrażenie jak prosto się w tym aplikację WWW
buduje
, a potem okazało się raczej prymitywne. Architektura CherryPy wręcz
wymuszała bałagan, zamiast porządkować kod (to IMHO jedna z funkcji
frameworków) — na dłuższą metę reprezentacja hierarchii URL poprzez
atrybuty obiektów to niezbyt dobry pomysł. Do tego stan aplikacji (aktualne
żądanie, odpowiedź, etc.) trzymany w zmiennej globalnej (i dostępny przez tę
zmienną)… wiem że to działa, ale wydaje się niezbyt eleganckie. Krótko mówiąc
CherryPy mnie nie zachwycił.

Kolejny był Nevow, oparty o Twisteda. Od początku wydawał się
bardziej skomplikowany niż CherryPy. Ale po doświadczeniach z prostotą
(prymitywizmem) CherryPy to mnie nie zrażało. Nevow intensywnie korzysta
z interfejsów i adapterów (jak się później okazało, zapożyczonych od Zope3), ma
własny, ciekawy język szablonów — oparty o XML i jeszcze bardziej niż ZPT
oddzielający prezentację od logiki (w szablonach nie ma nawet pętli).
Portowanie prostego CGI do Nevowa to była droga przez mękę, bo trzeba było się
nauczyć nowego, skomplikowanego narzędzia. Niektóre rzeczy w tym frameworku
okazywały się ostatecznie proste i logiczne, przy innych trzeba było się
nakombinować. Doprowadziłem conieco (prezentację listy wejść) do działania, ale
nie rozwiązałem wszystkich problemów i walka z Nevow mi się znudziła.
Ostateczne wrażenie raczej pozytywne, chociaż miejscami było pod górkę.
Doceniłem też wsparcie na IRCu.

Jakiś czas temu na grupie pl.comp.lang.python przeczytałem
sporo dobrego o Zope3. Wtedy to było
jeszcze eksperymentalne Zope-X3. Framework przepisany od zera, docelowo mający
zapewniać częściową kompatybilność wstecz (czy raczej wsparcie dla portowania
starych aplikacji) z Zope2. Co do tego że Zope należało przepisać od zera, nie
miałem wątpliwości. W to, że w Zope krył się potencjał też. Więc, gdy pojawiły
się release candidate Zope 3.1, postanowiłem spróbować.
Poczytałem dokumentację, zacząłem łapać o co w tych interfejsach i adapterach
(dużo intensywniej używanych niż w Nevow) chodzi i zabrałem się za portowanie
statystyk. Znowu początki były ciężkie, ale prawie na każdy problem Zope
oferował jakieś fajne rozwiązanie. Wszystko w postaci przejrzystego obiektowego
podejścia. W przeciwieństwie do takiego CherryPy Zope3 zachęca do porządku
i przemyślenia co jest obiektem, co jego prezentacją. Daje potężne mechanizmy
kontroli dostępu, bezpieczeństwa kodu, i18n, skórkowania, tworzenia i walidacji
formularzy i wielu innych rzeczy które w aplikacji są potrzebne. Jest to
architektura i zestaw narzędzi, czyli to czym framework powinien być. I się
sprawdza. Dzisiaj skończyłem portowanie statystyk i efekt jest lepszy niż
oryginał, a ewentualny dalszy rozwój będzie banalnie prosty (prostszy niż
w przypadku CGI).

Już po rozpoczęciu swojej zabawy z Zope3 stwierdziłem, że warto to
zastosować w firmie zamiast Zope2. Większość systemu (interfejs administratora,
serwisu, BOKu) zostanie na starym Zope’ie, bo przepisanie tego to znowu byłoby
wiele miesięcy, ale już interfejs użytkownika końcowego robię na Zope3. Tego
jest niewiele i i tak muszę to przepisać od zera. Obie części komunikują się
przez bazę danych i chodzą na różnych maszynach, więc zastosowanie dwóch
Zope’ów nie jest problemem. Mam nadzieję, że tej decyzji nie będę za jakiś czas
żałował, jak tego, że wpakowałem się z Zope2 i aplikację rozwijaną
TTW. No i to, że w ogóle wpakowałem się w kodowanie zamiast
adminowania…

Jest jeszcze wiele innych frameworków i, do tego, ciągle powstają nowe.
Części w ogóle nie znam, o części poczytałem w sieci i zrezygnowałem, bo ich
założenia mi nie pasowały. Niektóry zdawały się zbytnio iść w kierunku PHP,
którego przecież celowo unikam, inne miały jakąś dziwną architekturę, np.
Webware składające się z iluś komponentów, w których nie udało mi się ujrzeć
spójnej całości. Ciekawe co wygra — na świecie i wśród moich narzędzi.

Coś dla nieprzekonanych do dynamicznego typowania

Natrafiłem dzisiaj na artykuł Bruce’a Eckela

Strong Typing vs. Strong Testing
. Autor, znawca języków programowania,
znany autorytet w tej dziedzinie (autor słynnej książki Thinking in
Java
), opisuje w nim jak przekonał się do dynamicznego typowania
mimo, że wcześniej za jedyne słuszne uznawał statyczne systemy typów.
Oczywiście język z dynamicznym typowaniem, którym się zachwyca, to Python.
Interesująca jest też kontynuacja
tematu
— odpowiedź na list profesora, który z poprzednim artykułem
się nie zgadza. Blog Bruce’a
Eckela
dodałem do linkowni — czuję, że będę tam często zaglądał.

Miłe jest, że do Pythona przekonuje się coraz więcej znawców poważnych
języków
i miłe jest, że potrafią opisać czemu Python jest taki dobry
— zawsze to kolejne argumenty do przekonania nieprzekonanych. Co do
statycznego typowania, to też nie uważam tego za coś niezbędnego, ale raczej, że
w większości przypadków przeszkadza, jednak nie jestem też przeciwnikiem
wprowadzenia takich, opcjonalnych oczywiście, rozszerzeń do Pythona. Dobrze
czasem jest mieć możliwość zaznaczyć w kodzie dla jakich argumentów jest on
przygotowany. Często jest to istotna informacja dla innych developerów (ale tu
wystarcza odpowiednia dokumentacja) i dla interpretera/kompilatora (pozwala na
lepszą optymalizację krytycznych elementów kodu). Problem polega na tym, żeby
wprowadzić to nie psując przejrzystości i elastyczności języka i aby nie było
to nadużywane.

PyXMPP — coraz poważniej

Zrobiłem w końcu stronę WWW dla
PyxMPP
. To dopiero początek, ale na jakiś czas powinno starczyć. Zacząłem
też robić zestawy testów jednostkowym (opartych o PyUnit — moduł
unittest) do testowania całości. Na pierwszy ogień poszło
pyxmpp.jabber.vcard do którego już jakieś testy były i trzeba
było je tylko przerobić na PyUnit i uzupełnić. Oczywiście wyszło parę błędów,
które poprawiłem. Później pyxmpp.jabber.disco, w którym chciałem
uzupełnić API, testy miały mi pomóc zrobić to tak, żeby nic nie zepsuć.
Pomogły, a przy okazji poprawiłem w testowanym module sporo błędów. Ogólnie
fajna rzecz takie testy i wbrew pozorom nie tak bardzo upierdliwa (mniej niż
uzupełnianie docstringów).

Dzisiaj zrobiłem jeszcze małą rewolucję. Wkurzało mnie już to, że
importowanie jakiegokolwiek modułu z pyxmpp powodował
importowanie całości (prawie). Wynikało to z tego, że dla wygody developerów
pakiet pyxmpp importował najważniejsze obiekty ze swoich modułów.
Od dłuższego czasu głowiłem się na tym, jak to rozwiązań nie tracąc tej wygody
i nie psując za bardzo kompatybilności wstecz i w końcu coś wymyśliłem. Teraz
samo z siebie nic niepotrzebnie się nie importuje, ale jak ktoś chce po
staremu, to wystarczy że raz sobie zaimportuje pyxmpp.all, i/lub
pyxmpp.jabber.all albo pyxmpp.jabber.all (jeśli
któregoś z nich używa). W ten sposób chyba i wilk syty i owca cała.

Następnie zabrałem się za CJC. Wkurzało mnie długie ładowanie konfiga, okazało
się, że to przez częste wywoływanie logging.debug()
wywaliłem i jest lepiej. Potem postanowiłem porawić pewnego upierdliwego i
często zgłaszanego buga — część wpisów w rosterze pojawiało się przed
zamiast za nazwą grupy. Okazało się, że porównywanie JIDów z innymi obiektami
było w PyXMPP spieprzone i niektóre JIDy były mniejsze od None. I
znowu testy jednostkowe się przydały, a zgłoszenie mogłem zamknąć.

I po cholerę to wszystko piszę? Nie wiem… może, żebyście wiedzieli jak
wygląda prawdziwie geekowski sylwester… Nie, żebym nie imprezował —
specjalnie z tej okazji mam tu colę (z cytrynką) i chipsy
;-).

Doczego to doszło…

… portuję PyXMPP na
Windows. To znaczy gościu mi pisze co mu nie działa, a ja poprawiam. Wczoraj
wymieniłem swój resolver (który nie umiał znaleźć serwerów nazw na Windows) na
dnspython. Dzisiaj poprawiłem
streambase, żeby na sockecie używało recv() zamiast
read (Windows sucks, POSIX rulez! ;-)). I podobno
zadziałało.

Oprócz tego pracuję nad dokumentacją. Docstringi są już w całym kodzie,
HTML jakiś się z tego generuje. Właśnie kończę skrypt tworzący z drzewa
dokumentacji wyciągniętego przez epydoc plik XMI, który mogę
załadować do Umbrello aby narysować diagramy UML. Jeszcze tylko stworzyć
stronę WWW z tym wszystkim i może wreszcie jacyś developerzy się tym PyXMPP
zainteresują i ostatecznie pogrążę XMPPPY (konkurencję) ;-).