Taki drobny „WTF?”

Dotychczas takie rzeczy spotykałem właściwie tylko na The Daily WTF,
jako fajne ciekawostki. Sam raczej unikałem grzebania w cudzym kodzie, najwyżej
zastanawiałem się jakie WTF przeżywają ci co przejmują lub kiedyś przejmą mój kod. ;-)

W obecnej robocie robię pewien system, w którym jako interfejs użytkownika
służy aplikacja webowa w Javie zbudowana na Strutsie. Do niedawna nawet mnie specjalnie
nie interesowało w jakiej to technologii i co to ten Struts. Ważne, że byłem
w stanie to uruchomić w swoim systemie i robiło to co trzeba – tworzyło różne pliki
i uruchamiało różne skrypty. Jednak ostatnio wyszło parę drobiazgów, które
należałby jak najszybciej naprawić.

Niestety Javowiec zajmujący się tą częścią kodu jest niedysponowany
(zdaje się, że się żeni) i nie dał rady tego zrobić, ani w najbliższym czasie
nie zrobi. Sprawa zrobiła się pilna, więc sam się za to zabrałem –
w sumie dobrze wiedzieć co w systemie siedzi. Najpierw poczytałem co to ten
Struts i gdzie mam szukać odpowiedniego kodu, potem zabrałem się za
poprawki…

Pierwsza rzecz banalna: zrobić stronę z komunikatem. Właściwie statyczną.
Znalazłem szablon wyświetlający coś podobnego. Skopiowałem. W środku znalazłem
miejsce na nagłówek i treść. Wypełniłem. Uruchomiłem aplikację… i treść jest,
bardzo brzydko ułożona (brak marginesów), ale jest. Nagłówka brak. Zaglądam do
źródła strony… kod paskudny, ale nagłówek jest. Zaglądam do CSS, a tam coś
takiego:

#titleBox p {
    background #FFFFFF;
    ....
    color: white;
}

No i miałem problem… co się stanie, jak zmienię kolor nagłówków na czarny…

Nie było źle… okazało się, że w całym interfejsie było dużo więcej opisów
niż wcześniej było widać, wyjaśniła się kwestia wolnej przestrzeni we
wszystkich formularzach. Ale oczywiście layout się popsuł, trudno to nawet było
wyjaśnić tą drobną zmianą koloru, ale poprawić było trzeba. No to podłubałem
jeszcze trochę w tym CSS i jest ok… ufff..

Potem chciałem dodać jeszcze parę komunikatów, w szczególność w przypadku
niepowodzenia pewnego skryptu… Zajrzałem do kodu a tam nie jest sprawdzany
kod wyjścia żadnego polecenia. A ja za cholerę nie wiem jak to ładnie w tym
Strutsie obsłużyć. Na razie poszedłem na łatwiznę i wszystkie niepowodzenia
przekierowuję na statyczną stronę z Operation failed. Przy okazji wyszła
kupa innych problemów… tym razem to już chyba tylko Javę można za to skląć,
ech…

Update: Zacząłem czytać dzisiejszy The Daily WTF i poprawiłem tytuł wpisu :-)

Reklamy

Okularków nie będzie, przenosiny w pełni

Dzisiaj wybrałem się do okulisty (przy optyku) w celu dobrania okularów.
Ten mnie zbadał, próbował dobrać odpowiednie szkła… i właściwie nic to nie
dawało. Na siłę okularów sprawiał sobie nie będę – to by mi mogło
bardziej zaszkodzić niż pomóc. To co zaobserwowałem u okulistki podczas badań
kontrolnych najprawdopodobniej było tylko złudzeniem spowodowanym
powiększającym działaniem szkieł. Dzisiaj więc zamiast recepty na okulary
odebrałem receptę na krople do oczu. Zresztą podobne to tych co już mam –
do Starazolinu, który wychwalałem już w zeszłym roku.

W związku ze zmianą pracy (do nowej już w czwartek) przenoszę różne
rzeczy. Listy dyskusyjne moich projektów (CJC, PyXMPP, Transport GG)
przeniosłem na swój własny serwerek (lists.jajcus.net), a z rozpędu zabrałem
się za przenoszenie do siebie reszty tych projektów z JabberStudio (które najwyraźniej
umiera). To ostatnie jeszcze trochę potrwa, bo to większa robota, ale pewnie
w tym tygodniu skończę – wtedy dam znać co i jak, a przede wszystkim,
gdzie.

Dzisiaj walczyłem jeszcze z dwie godziny z przenoszeniem kontaktów ze
starego telefonu komórkowego (firmowego) na mój własny, prywatny. Ta marna
namiastka IrDA/OBEX w moim Samsungu nie ułatwiała sprawy, ale stary laptop
z IRDA, Multisync, trochę Pythona, trochę ręcznego grzebania w plikach
wizytówek (bez tego, przy próbie importu czterech z moich kontaktów telefon
się po prostu rebootował) i jakoś się udało. BTW: jak ktoś ma mój stary numer,
a chciałby aktualny, to niech się do mnie zgłosi.

Z przenosin czeka mnie jeszcze wyniesienie swoich fizycznych zabawek (talerz,
słuchawki, puszka na herbatę, kalendarz z gołymi babami itp.) ze starej roboty
i zaniesienie sprzętu (komputer, monitor, telefony) do nowej. Z mniej
materialnych rzeczy – posprzątanie po sobie na serwerach w starej firmie
(wszelkie konta, aliasy, skrypty itp. zabawki) i podczepienie się do nowej
(też se będę musiał jakiegoś majla założyć). System niby sobie już na nowym
kompie zainstalowałem, ale nie pamiętam w jakim jest stanie… więc może
jeszcze to mi trochę czasu zająć… a pracodawca pewnie będzie chciał już
pierwszego dnia widzieć wyniki… ;-)

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.

Wreszcie jest nowy transport GG

Od jakiegoś czasu do źródeł transportu GG włączona jest biblioteka libgadu
(na dostępnych pakietach z libgadu nie można polegać). W libgadu ostatnio
znaleziono poważne błędy, więc trzeba było ją uaktualnić. Przy tej okazji
zajrzałem do kodu transportu GG… i wreszcie się porządnie za niego zabrałem,
co w rezultacie dało dzisiejsze
wydanie wersji 2.2.0
.

Od wersji 2.1.0 zmieniło się wiele. Większość jeszcze w lutym. Najważniejsze
zmiany to:

  • Wspomniane już włączenie libgadu do źródeł transportu.
  • Ignorowanie wybranych numerków.
  • Ustawianie osobnych statusów dla GG, można też ustawić specjalny status
    używany podczas niedostępności i niewidzialności.
  • Wywalenie całkiem zmiany haseł. Stary kod i tak nie działał z nowym
    protokołem (wymagającym czytania kodów z obrazków), a nowy protokół byłby
    dość niepraktyczny w implementacji.
  • Kupa różnych poprawek.
  • I chyba najważniejsze: obsługa subskrypcji obecności per-kontakt. Teraz
    powinny działać listy prywatności, ustawianie online/offline dla konkretnych użytkowników,
    jednokierunkowe subskrypcje (ja go widzę, a on mnie nie) itp.

Z ostatnim punktem związana jest pewna niedogodność: po upgrade’zie
użytkownicy będą musieli potwierdzić autoryzację dla każdego swojego
kontaktu z GG. To może być bardzo dużo klikania. Użytkownicy niektórych
serwerów mają już to za sobą, więc dobrze wiedzą o co chodzi.

Z poprawek wspomnieć mogę np. rezygnację z używania
GIOChannel
do czegoś, do czego się zupełnie nie nadają (śledzenie aktywności na sockecie obsługiwanym
przez coś innego niż GLib), czy wywalenie zupełnie niepotrzebnego (a pełnego
błędów) buforowania danych zapisywanych do strumienia XMPP. Ta druga zmiana
podobno spowodowała znacznie zmniejszenie obciążenia w dużych (tysiące zarejestrowanych użytkowników)
instalacjach transportu. A to buforowanie miało niby być optymalizacją… Poza
tym usunąłem ileś memleaków i innych błędów wykrytych przez Valgrinda.

Wersja
z wczorajszego wieczora już podobno całkiem stabilnie działała, a ja mam
nadzieję, że dzisiaj nie tylko poprawiłem kilka kolejnych błędów, ale i nie
wprowadziłem żadnych nowych. Jednak, jak znam życie, to pewnie za chwilę
dostanę parę bugreportów dotyczących jakiś nowych (a może i starych)
i strasznie głupich błędów. Jeśli tak, to postaram się szybko wydać poprawioną
wersję, jeśli nie, to pewnie kolejne wydanie będzie za jakiś rok albo i później.

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