Django na Lighttpd + FastCGI

Opublikowane przez Jarosław Zabiełło Wed, 31 May 2006 21:16:00 GMT

Okazuje się, że uruchomienie Django na serwerze Lighttpd i FastCGI nie jest wcale trudne. Taki zestaw pożera znacznie mniej zasobów komputera niż Apache i mod_python. Efekt dostępny jest tutaj. Projekt Django został stworzony w /home/django_project. Użyłem Django w wersji SVN (pozbawionej “magii”), MySQL 5, Python 2.4.3 oraz system to Debian 3.1. Cała konfiguracja to:

Plik lighttpd.conf (aktualny):

$HTTP["host"] == "koran.apologetyka.com" {
  server.document-root = "/home/django_project/koran/public"
  url.rewrite-once = (
    "^/(media|stylesheets|images|javascripts).*" => "$0",
    "^/admin/(.*)$" => "/django-admin.fcgi/admin/$1",
    "^/(.*)$" => "/django.fcgi/$1"
  )
  fastcgi.server = (
    "/django.fcgi" => (
      (
        "bin-path" => "/home/django_project/django.fcgi",
        "socket" => "/tmp/django.sock",
        "check-local" => "disable",
        "min-procs" => 1,
        "max-procs" => 1,
      )
    ),
    "/django-admin.fcgi" => (
      (
        "bin-path" => "/home/django_project/django-admin.fcgi",
        "socket" => "/tmp/django-admin.sock",
        "check-local" => "disable",
        "min-procs" => 1,
        "max-procs" => 1,
      )
    )
  )
  server.error-handler-404 = "/django.fcgi"
}
Plik django.fcgi:
#!/usr/local/bin/python2.4
import os, sys
sys.path += ['/home'] 
from flup.server.fcgi import WSGIServer
from django.core.handlers.wsgi import WSGIHandler
os.environ['DJANGO_SETTINGS_MODULE'] = 'django_project.settings'
WSGIServer(WSGIHandler()).run()

Później opiszę dokładniej sam kod Django.

Posted in  | Tagi , ,  | 4 comments

Python 2.4 Quick Reference Card

Opublikowane przez Jarosław Zabiełło Wed, 31 May 2006 08:53:00 GMT

Wyszła świetna, 18 stronicowa, ściąga do Pythona 2.4. Plik jest w formacie PDF i można pobrać stąd.

Posted in  | Tagi  | brak comments

MySQL5 - rehabilitacja

Opublikowane przez Jarosław Zabiełło Tue, 30 May 2006 12:04:00 GMT

Po paru godzinach testów i walki z bazami MySQL 5.0.21 i PostgreSQL 8.1.4, muszę zwrócić trochę honoru bazie MySQL. PostgreSQL to bardzo zaawansowana i potężna baza danych, jednakże, jak pisałem wcześniej, ma jeden, zasadniczy feler, który ją wyklucza z moich zastosowań – kiepską obsługę różnych kodowań w ramach jednej bazy danych. Próba napisania aplikacji wielojęzycznej sortującej i wyszukującej z uwzględnianiem wielu różnych języków jest bardzo trudna. Praktycznie nie ma sensu aby do tego użyć PostgreSQL.

Tutaj pokazuje swoje możliwości MySQL 5. na razie nie mam potwierdzonych informacji o słabej stabilności tak jak to było z wersjami wcześniejszymi. Zaś problem dziwacznego zachowania się warunku LIKE… otóż udało mi się za pomocą prób i błędów połączonych z przeszukiwaniem dokumentacji, ustalić ostateczne rozwiązanie.

Po kolei. Wpierw trzeba wiedzieć, że MySQL 5 (choć jest to już od wersji 4.1) udostępnił obsługę wielojęzyczności na znacznie lepszym poziomie niż inne bazy. Otóż można łatwo zdefiniować niezależne traktowanie tekstu dla każdej tabeli w ramach tej samej bazy. Mało tego, można pójść dalej i zdefiniować niezależne kodowanie dla każdego pola tekstowego oddzielnie.

Daje to niebywałą elastyczność i komfort w obsłudze tekstów międzynarodowych. Można bowiem wymusić aby w tekstach polskich działało sortowanie zgodne z zasadami języka polskiego oraz aby duże i małe polskie ogonki były traktowane tak samo przy wyszukiwaniu. Inne pole może posiadać identyczne reguły dla języka niemieckiego, szwedzkiego, itp. itd. Jedynym założeniem jest aby kodowanie takich tabel i/lub pól było w UTF8. To jest wewnętrzny format w jakim trzeba trzymać dane tekstowe. Nie ma on nic wspólnego z kodowaniem jakie uzyskuje klient. Odpada w ogóle konieczność pisania własnych procedur aby to przekodowywać. Cała robotę za nas “odwali” MySQL. Chcesz w swoich skryptach wypluwać do przeglądarki polskie ogonki w formacie ISO-8859-2? Żaden problem. Wywołaj kwerendę “SET NAMES latin2” i potem wykonuj SELECT

Samo kodowanie znaków to dopiero początek. Chcemy także aby działało sortowanie i wyszukiwanie bez wrażliwości na wielkość liter. Aby to działało należy zadbać o to aby każde pole posiadało odpowiednią definicję tzw. collation. Dla języka polskiego będzie to utf8_polish_ci. Przykładowa definicja tabeli (z dodatkowymi bajerami jakie daje MySQL, np. kompresję indeksów) mogłaby wyglądać np. tak:
CREATE TABLE `artykul` (
  `id` int(11) NOT NULL auto_increment,
  `tytul` varchar(255) collate utf8_polish_ci NOT NULL,
  `tresc` text collate utf8_polish_ci NOT NULL,
  PRIMARY KEY  (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci PACK_KEYS=1 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=COMPRESSED;

MySQL udostepnia także słowo COLLATE do bardzo wygodnej obsługi tekstów międzynarodowych. Lista dostępnych języków jest podana w dokumentacji i jest imponująca.

Gdzie leżał zatem problem z dziwnym zachowaniem się bazy? Po prostu nie było zdefinowanego żadnego collation i baza trochę zwariowała. Na pewno byłoby lepiej, aby w takiej sytuacji zachowywała się bardziej przewidywalnie. Ale wystarczyło dodanie definicji odpowiedniego zestawu języka i wszystko wróciło do normy.

Posted in  | 6 comments

Google mail

Opublikowane przez Jarosław Zabiełło Tue, 30 May 2006 12:00:00 GMT

Konto mailowe udostępniane za darmo przez Google nie dość że obsługuje prawie 3GB miejsca, ma zintegrowany (na ajaksie) komunikator, to udostepnia także prowadzenie notatek. Świetna sprawa. :)

Tagi  | 17 comments

PostgreSQL - inne problemy

Opublikowane przez Jarosław Zabiełło Mon, 29 May 2006 19:00:00 GMT

Zauważyłem, że problem z dziwacznym działaniem kwerend MySQL5 korzystających z LIKE nie występuje pod Linuksem. Być może to po prostu przypadłość kiepsko przygotowanej binarki dla Windowsów…

Najbardziej bolesna sprawa związana z PostgreSQL (dalej: PG) jest zła implementacja wersji międzynarodowych. Tzn. PG ustawia jedną collations dla całej bazy co skutecznie uniemożliwia to, aby w ramach jednej bazy używać różnych tabel z różnymi sortowaniami (wg różnych języków).

Zaś MySQL potrafi ustawić niezależnie collation nawet dla poszczególnych kolumn!

Próbowałem przeszukać listy dyskusyjne na temat PG, ale to, co znalazłem, potwierdza moje obawy: PG ma to źle zrobione. Jeśli chcę mieć efekt sortowania i takiego samego traktowania dużych i małych znaków (istotne przy większości wyszukiwań tekstu) to musiałbym dla każdego języka tworzyć oddzielną bazę. To raczej nie wchodzi w grę.

Posted in  | Tagi  | brak comments

MySQL 5 - strzeż się się tego koszmaru

Opublikowane przez Jarosław Zabiełło Mon, 29 May 2006 07:54:00 GMT

Baza MySQL nigdy nie uchodziła za wzór poprawnej pracy, ale to co ostatnio się z nią dzieje woła o pomstę do nieba… Zainstalowałem sobie najnowszą wersję stabilną MySQL 5 pod win32. Wpierw myslałem że znalazłem jakiś błąd we frameworku Django. Ale krótki czat z innymi programistami i przejrzenie netu, pozbawił mnie złudzeń. Błąd leży w silniku samej bazy. Włączyłem logowanie zapytań i uruchomiłem kwerendę bezpośrednio z poziomu klienta.

Otóż okazuje się, że w MySQL5 kompletnie popsuta jest obsługa warunku “LIKE”. Ilość zwracanych rekordów jest większa niż być powinna. I to nie chyba ma nic wspólnego z tym, czy kodowanie tabel jest w UTF8 czy nie, gdyż nadmierna ilość rekordów jest znajdowana nawet, jak wyszukuje się słowo zawiera tylko znaki ASCII.

Inne “kwiatki” związane z MySQL, o których trzeba sobie jasno powiedzieć, to notoryczne niszczenie plików indeksowych w wypadku zbyt dużego obciążenia bazy. Oczywiście to można łatwo naprawić za pomocą kwerendy REPAIR TABLE tabelka. Ale dopóki nie wykona się tej operacja, tabela nie jest dostępna i aplikacja nam się wywali. Tego typu problemy zauważyłem na MySQL 4.x i 4.1. Nie miałem okazji poddać większym obciążeniom bazę MySQL 5.x, ale nie zdziwiłbym się jakby też z nią były problemy.

Zawsze tyle się mówi w branży, że MySQL to niepoważny projekt amatorski (słaba stabilność i niszczenie swoich tabel pod dużym obciążeniem, koszmarnie wolne tabele transakcyjne innodb, słaba obsługa lockowania – tylko na poziomie tabel a nie wierszy itp, itd) Niszczenie swoich tabel indeksowych mogłem jeszcze zdzierżyć, ale błędna obsługa wyszukiwania tak podstawowej operacji jak LIKE? No way. Całe szczęście, że Django ma dobry ORM i można łatwo zmigrować do PostgreSQL. Chyba nie ma innego wyboru jak powiedzieć: “Goodbye MySQL and welcome PostgreSQL!”

Zobacz c.d. MySQL5 rehabilitacja

Posted in  | Tagi  | 13 comments

JEdit i Rails

Opublikowane przez Jarosław Zabiełło Sun, 28 May 2006 01:46:00 GMT

RadRails ma silną konkurencję. Edytor JEdit jest nie tylko szybszy i mniej zabiera zasobów komputera, to na dodatek, przy dodaniu odpowiednich pluginów doskonale nadaje się do pracy z Railsami. Wygląda to bardzo dobrze, wystarczy rzucić okiem na tę stronę.

Posted in ,  | Tagi ,  | 3 comments

Django - zabójcza aplikacja. Część II.

Opublikowane przez Jarosław Zabiełło Sat, 27 May 2006 16:53:00 GMT

Jak wspomniałem w pierwszej części, Django jest frameworkiem który wyrósł na bazie zastosowań komercyjnych. Zanim twórcy zdecydowali się otworzyć i udostępnić jego kod na zasadach open-source, Django był już wcześniej intensywnie używany w zastosowaniach komercyjnych1.

Przewaga Django

Wcześniej pisałem o zaletach Pylonsów. Jednakże, po moich ostatnich testach z “oczyszczoną z magii” wersją Django muszę przyznać, że jest to chyba aktualnie najlepszy framework, z jakim miałem do czynienia w tej klasie zastosowań.

Django oferuje znacznie więcej helperów i udogodnień niż świetny Pylons. Poza wbudowaną autoryzacją i zaawansowanym panelem admina, Django pracuje na wyższym poziomie abstrakcji niż Pylons czy Rails. Zamiast prostych generatorów HTML dla formularzy, Django wprowadza kontrolki, które pracują na wyższym poziomie abstrakcji. Bardzo przejrzyście obsługuje także walidowanie formularzy. Kto się męczył z obsługa formularzy, nareszcie odetchnie z ulgą. Idea manipulatorów bardzo upraszcza pisanie obsługi formularzy. Ktoś powie, że to można sobie samemu napisać. Owszem. Ale Django daje ci to już gotowe. Uruchamiasz i używasz zamiast tracić czas.

Sprawa dokumentacji, i promocji w ogólności, w wypadku wiekszości frameworków Pythona jest po prostu żenująca. Zdumiewające jest jak to jest możliwe, że nawet tak potężne środowisko jakim jest Zope ma tak słabą stronę i chaotycznie porozrzucaną dokumentację.

Kilka mądrych posunięć.

Pierwszą, mądrą rzeczą, jaką zrobiono po otwarciu kodu Django, było skupienie się na pisaniu dokumentacji. Dzięki temu, Django ma dziś jedną z najlepszych dokumentacji pomiędzy pythonowymi frameworkami. Bez dobrej dokumentacji, trudno aby ktokolwiek się zainteresował projektem. Dokumentacja Django nie równa się jeszcze temu co jest dostępne dla Railsów ale jest i tak lepsza od większości frameworków pythonowych.

Drugim, równie dobrym posunięciem, był refaktoring pierwotnego kodu zgodnie z zasadami filozofii Pythona. Wg zasady, ze działanie jawne jest lepsze od domyślnego, nastąpił proces usuwania “magii” z kodu. Np. teraz zamiast tajemniczych prefiksów do nazw dodatkowych metod modelu, można tworzyć je bardziej naturalnie – jako metody klasowe Pythona. Aktualnie wersja pozbawiona “magii” jest dostępna w repozytorium SVN. Frameworkiem, który jest mocno przeładowany “magicznymi” metodami jest Rails. Może to i ładnie wygląda, ale filozofia Python potępia takie podejście, gdyż to utrudnia programiście w szybkim zorientowaniu się o co chodzi w kodzie.

Trzecim, dobrym pomysłem, jest możliwości testowania środowiska w sposób interaktywny w interpreterze. Domyślne uruchomia się to w miejscu stworzonego projektu za pomocą komendy: manage.py shell. Ta komenda odpala ipythona (ile mamy go zainstalowanego) i udostępnia w nim łatwy dostep do całego środowiska Django, jego modeli, bibliotek itp. Ipython to podrasowana wersja interpretera Pythona. Świetnie uzupełnia metody, ma dobrą historię edycji i cały szereg dodatkowych możliwości2.

Przykładem różnic w podejściu do kwestii jawności i “magii” jest inny sposób podejścia do modułów i przestrzeni nazw między Pythonem a Ruby. Python stosuje przejrzystą do bólu zasadę: moduł to po prostu plik. Wszystko co w nim jest, automatycznie jest ładowane do przestrzeni nazw określonej nazwą pliku. Python umożliwia także selektywne ładowanie z modułu wybranych klas i metod. Wszystko jest niezmiernie czytelne.

W wypadku Rubiego mamy tylko wyrażenie require które odpala i włącza plik. Przy czym z tego kodu kompletnie nic nie można wywnioskować, nawet tego, czy w środku zawiera jakikolwiek moduł. Trzeba zajrzeć aby się dowiedzieć, co zostało dorzucone do naszej przestrzeni nazw. Pythonowa zasada “jawności ponad domyślaniem się”, w tym wypadku stanowi kolejny argument na rzecz większej produktywności Pythona wobec Rubiego.

Django jest szybsze.

Django jest cholernie szybkie. Nie tylko dlatego, że korzysta z Pythona i bytecodu, ale także dlatego, że jest dobrze napisane. Np. szablony Django są szybsze od Cheetah i nie wymagają żadnej dodatkowej kompilacji! Wszystkie wyrażenia regularne używane np. do parsowania adresów URL są jednokrotnie kompilowane, są więc wykonywane bardzo szybko. Mało tego, w Django można włączyć obsługę akceleratora Psyco. Wystarczy w mojprojekt/settings.py dodać linijkę ENABLE_PSYCO = True. Railsy nie mają wydajnościowo szans z Django. Chyba tylko Pylons może się zbliżyć i nawiązać jakąś walkę na tym polu. Rails i PHP są tu bez szans.

Django pracuje na wyższym poziomie abstrakcji.

Obsługa formularzy, ich walidacja są zwykle uciążliwe i programiści mają z nimi ciągle do czynienia. Rails oraz Pylons (który to skopiował z Railsów) udostępniają cała grupę wygodnych helperów do generowania kodu html np. do formularzy. Mimo, że to jest na pewno lepsze podejście od bezpośredniego dłubania w kodzie HTML, to Django oferuje coś znacznie lepszego – kontrolki. Coś, co używa np. ASP.NET, tylko że znacznie prostsze w obsłudze.

Djangowe modele korzystające z mapowania relacyjno-obiektowego (ORM) również pracują na wyższym poziomie abstrakcji niż tylko odwzorowania prostych typów danych jakie posiada relacyjna baza danych. Uważam też, że djangowy ORM jest lepszy od SQLObject. Nie dość że ma znacznie lepszą dokumentację, to na dodatek ma więcej możliwości. Np. niby taka trywialna sprawa, ale jak w SQLObject odwzorować takie zapytanie (z takim samym traktowaniem dużych i małych polskich znaków)?

SELECT * FROM tabelka WHERE pole LIKE '%wartość%'

W Django to jest trywialna operacja i na dodatek operuje na generatorze:

for row in Tabelka.objects.filter(pole__icontains="wartość"):
    print row.pole

W kolejnych częściach, zajmę się przykładem stworzenia prostej aplikacji internetowej. Pokażę też jak przeciążyć modyfikator pluralize aby uwzględniał polskie znaki, i jak obsługiwać formularze (włącznie z dynamicznym budowaniem ich treści)

-

1 Poza całym szeregiem złożonych portali, developerzy Django sprzedają Elington – profesjonalny CMS zbudowany na bazie tego frameworku. Oczywiście Railsy również powstały pierwotnie jako projekt do zastosowań komercyjnych. Stąd nic dziwnego, że oba środowiska oferują sporo interesujących rozwiązań.

2 Aby działały uzupełniania metod (po wciśnięciu klawisza Tab) trzeba doinstalować moduł readline oraz ctypes. Jeśli interpreter nie wyrzuca wyjątku po wpisaniu import readline, to znaczy że mamy go już zainstalowanego.

(Zobacz: część III)

Posted in ,  | Tagi  | 10 comments

Django - zabójcza aplikacja.

Opublikowane przez Jarosław Zabiełło Sat, 27 May 2006 08:17:00 GMT

Muszę przyznać, że podoba mi się zarówno sposób, jak i kierunek, w którym rozwijany jest pythonowy framework Django. Nie jest to kolejny domowy projekt tworzony w ramach hobby (jak to było z CherryPy). Django powstawało od razu z założeniem pracy w ciężkim środowisku produkcyjnym. Ostatnio napisałem sobie (w ramach zabawy) pewną aplikację w Django i muszę przyznać, że praca z Django jest faktycznie komfortowa. Wyższa niż w Pylons, i wyższa nawet niż w Railsach. Aby tekst nie był za długi, podzielę go na kilka mniejszych tekstów. Przedstawię swoje spostrzeżenia co do Pythona jako języka na tle Rubiego. Potem opiszę powody dla których Django ma wielkie szanse stać się killer application dla Pythona. Oraz w kolejnych częściach opiszę krok po kroku tworzenie konkretnej aplikacji w Django. Dziś będzie krótko o Pythonie.

Przewaga Pythona

Gdy zaczynam mówić o Django, to od razu nasuwa mi się porównanie do Ruby on Rails. Django jest oparty na języku Python, a Rails na języku Ruby. Co by nie mówić o zaletach Rubiego, to Python posiada kilka cech, które moim zdaniem, stawiają go wyżej1:

Python jest szybszy.

To po prostu jest fakt i to widać gołym okiem. Python automatycznie kompiluje każdy moduł do bytecodu. To jest operacja całkowicie przezroczysta dla programisty. Bytecode przy dużym projekcie składającym się w setek plików przyśpiesza znacznie czas ładowania kodu i może od biedy służyć również jako proste zabezpieczenie źródeł. Python jest szybszy nie tylko od Rubiego, ale także od PHP. Robiłem kiedyś porównanie prostej aplikacji w mod_php5 vs Myghty. Zdziwiło mnie wtedy to, że Python był tu szybszy, bo PHP jest zoptymalizowany do szybkiego wykonywania prostych skryptów. Oczywiście, im większy kod, tym rośnie przewaga Pythona z powodu użycia bytecodu2.

Python jest łatwiejszy do nauki.

Pythona łatwiej można się nauczyć i posługiwać niż Rubiego czy nawet PHP (sic!). To jest bardzo ważny punkt, bo dotyczy kluczowej kwestii jaką jest produktywność programisty.

Filozofią Pythona jest aby było jak najmniej możliwych dróg do rozwiązania tego samego problemu. Z tego wynika, że Python stara się implementować jak najmniejszą ilość funkcji i metod. Znacznie to ułatwia poruszanie się po tych metodach. W wypadku Rubiego, nie dość, że mamy znacznie więcej metod na obiekt, to na dodatek, lista metod jest dodatkowo zaśmiecona aliasami. W Ruby mamy po kilka metod, które wykonują to samo. Po kilka konstrukcji językowych, które robią to samo. Widać tu korzenie fascynacji Perlem. Ale dla programistów to jest uciążliwość.

Python jest bardziej produktywny.

Dodatkowym czynnikiem ułatwiającym szybką prace, jest integracja prostego mechanizmu dokumentacji kodu na poziomie składni Pythona. Mowa o docstringach. W praktyce znaczy to, że prawie wszystkie edytory IDE (których jest sporo, są darmowe i są bardzo dobre jak na tę klasę języków) nie tylko rozwijają mniej metod, ale także wyświetlają ich zintegrowaną dokumentację. Efektem jest znacznie mniej czasu traconego na zaglądanie do dokumentacji czy książek. To duża, odczuwalna różnica. Mniej rzeczy do zapamiętania, podpowiedzi do składni na każdym kroku, to musi się odbijac na produktywności tworzenia kodu3.

Python ma pełną obsługę Unicode.

Pełnej obsługi Unicode nie ma ani PHP ani Ruby. Obsługują co najwyżej UTF-8 i to w ograniczonym zakresie. Np. próby konwersji znaków ze standardu cp1250 (typowa sytuacja dla polskiego serwera MSSQL) do latin2 (typowe dla serwera MySQL w internecie) skazane są na korzystanie z biblioteki iconv, co jest dosyć chorym rozwiązaniem. Chorym, bo iconv nie umie dawać sobie rady z wieloma znakami, które nie mają odpowiednika w standarcie latin2. Chodzi nie o polskie ogonki, ale o różnego rodzaju długie myślniki, polskie, dolne cudzysłowy itp.

>>> s = " „To jest jakiś tekst — spróbuj to skonwertować z cp1250 do latin2.”  "
' \x84To jest jaki\x9c tekst \x97 spr\xf3buj to skonwertowa\xe6 z cp1250 do latin2.\x94  '
>>> s = " „To jest jakiś tekst — spróbuj to skonwertować z cp1250 do latin2.”  "
>>> s
' \x84To jest jaki\x9c tekst \x97 spr\xf3buj to skonwertowa\xe6 z cp1250 do latin2.\x94  '
>>> u = unicode(s, 'cp1250')
>>> u
u' \u201eTo jest jaki\u015b tekst \u2014 spr\xf3buj to skonwertowa\u0107 z cp1250 do latin2.\u201d  '
>>> print uTo jest jakiś tekstspróbuj to skonwertować z cp1250 do latin2.”  
>>> latin2 = u.encode('iso-8859-2', 'xmlcharrefreplace')
>>> latin2
' „To jest jaki\xb6 tekst — spr\xf3buj to skonwertowa\xe6 z cp1250 do latin2.”  '

Nie chodzi tylko o konwersje znaków. Dzięki Unicode, w Pythonie możesz dokonywać rzeczy bardzo trudnych do uzyskania w PHP czy Ruby. Operacje na tekście potrafią poprawnie traktować polskie znaczki. Np. napisanie funkcji implementującej podświetlanie wyszukiwanych słów bez względy na to czy uzyto dużych, czy małych polskich znaków. Dla bazy SQL to żaden problem4 ale dla kodu?

Chcielibyśmy aby wyrażeniem regularnym podmienić jakieś słowa zawierające polskie znaczkiNp. wyrażenia regularne umieją reagować poprawnie na polskie ogonki!

#-*- coding: utf-8 -*-
import re
def highlight(tekst, slowo, encoding):
    if not isinstance(tekst, unicode):
        tekst = unicode(tekst, encoding)
    if not isinstance(slowo, unicode):
        slowo = unicode(slowo, encoding)
    replace = re.compile('('+re.escape(slowo)+')', re.I|re.U).sub
    return replace(r'<b>\1</b>', tekst)
print
print highlight("podświetl gęś GĘŚ, GęŚ", "gęś", 'cp1250') 
# => podświetl <b>gęś</b> <b>GĘŚ</b>, <b>GęŚ</b>

Jeszcze jedno. Microsoft “uszczęśliwił” nasz kilkoma standardami kodowania polskich znaków. Inny jest dla windows (cp1250) a inny dla konsoli okienka DOS (cp852). Python pozwala całkowicie zapomnieć o stringach i kodowaniach. Po prostu należy pracować na obiektach Unicode. Powtarzam: obiektach Unicode a nie stringach UTF-8, bo to nie to samo. Instrukcja print wyświetli poprawnie polskie znaki, jeśli operujemy na obiekcie unicode.

Python ma stabilniejsze i dojrzalsze biblioteki.

Przekonałem się na swojej skórze, co znaczy przekonwertować setki plików graficznych za pomocą biblioteki RMagick (Ruby), a za pomocą PIL (Python). Kod w Ruby pożarł mi cała pamięć i odmówił pracy…

-

1 Osobną kategorią jest język PHP. Właściwie szkoda gadać. Ci, co zakosztowali pracy z Pythonem i Ruby, tracą szybko zapał do mozolnego dłubania w chaotycznym, niespójnym, źle zaprojektowanym języku PHP. Na ten temat to chyba trzeba napisać oddzielny tekst. Jak znajdę czas, to sobie “poużywam” na PHP. :)

2 Aby PHP aż tak nie odstawał, trzeba użyć akcelerator. Niektóre złożone projekty PHP, jak ezPublish, bez akceleratora są koszmarnie wolne.

3 Najlepszym przykładem różnicy między Pythonem a np. .NET jest ostatnio zaimplementowany przez mnie system synchronizacji danych między serwerami. Projekt tworzony przez 6 miesięcy w VB.NET (.NET) został napisany od zera w 5 roboczych dni (z czego działający prototyp był dostępny po paru godzinach). Żeby było ciekawiej, nowy kod Pythona w tym wypadku działa i szybciej i stabilniej.

4 Np. dla MySQL (od wersji 4.1 wzwyż) trzeba tylko ustawić aby baza pracowała natywnie w utf8 (domyślnie jest latin1) i tabele oraz ich pola tekstowe miały ustawiony parametr collations na utf8_polish_ci.

(Zobacz: część II)

Posted in ,  | Tagi  | 10 comments

Arachno IDE 0.6.11

Opublikowane przez Jarosław Zabiełło Wed, 24 May 2006 14:48:00 GMT

Udostępniono kolejną wersję beta świetnego edytora Arachno IDE dedykowanego do Ruby i Railsów. Poprawiono sporo błędów. Dodano parę nowych możliwości. Arachno IDE ewoluuje w kierunku edytora łatwego do rozbudowy o nowe funkcje. Jeszcze to nie jest w pełni gotowe. Ale do programu został dodany zagnieżdżony interpreter Pythona…

Posted in ,  | Tagi ,  | 1 comment

RadRails 0.6.3 - dużo lepiej

Opublikowane przez Jarosław Zabiełło Mon, 15 May 2006 09:21:00 GMT

Wyszła kolejna wersja edytora RaiRails (0.6.3). Poprawiono denerwujące (dla uzytkowników windozy) błędy. Działa całkiem dobrze. Na dodatek dodano zintegrowaną obsługę pluginów. Można je instalować zdalnie prosto z RaidRails.

Posted in ,  | Tagi ,  | 1 comment

JRuby on Rails - ruszył

Opublikowane przez Jarosław Zabiełło Sun, 14 May 2006 18:21:00 GMT

JRuby – projekt będący implementacją Rubiego w języku Java został przyśpieszony w związku z konferencją JavaOne. Developerom udało się w końcu uruchomić pełną aplikację Railsów działającą na wirtualnej maszynie Javy. Na razie jeszcze nie ma informacji o jakimś znaczącym przyśpieszeniu działania, ale pierwszy krok został dokonany. Zobacz więcej…

Zobacz też listę języków potrafiących pracować w javowskiej JVM – jest ich całkiem sporo. Kto by pomyślał, że nawet napisano assembler generujący klasy Javy. :)

Posted in ,  | Tagi ,  | brak comments

Dlaczego Ruby on Rails jest wyjątkowy?

Opublikowane przez Jarosław Zabiełło Sun, 14 May 2006 04:22:00 GMT

Niektórym osobom stykającym się z po raz pierwszy z Railsami wydaje się, że jest to tylko jakieś kolejne, tradycyjne środowisko developerskie pracujące wg wzorca projektowego MVC (model-widok-kontroler). Przywiązani do swoich języków i frameworków czasami się dziwią, dlaczego temat Railsów wywołuje od jakiegoś czasu tyle emocji i komentarzy. Na pewno po częsci jest tak pewnie dlatego, że RoR jest bardzo dobrze wypromowany. Dobra strona główna, dobra dokumentacja, książki, filmy, bardzo aktywna społeczność – nic tylko naśladować. Z drugiej strony, trzeba też przyznać, że jest to środowisko świetnie zaprojektowane – w Railsach sie pracuje po prostu komfortowo.

Nie dziwią więc ciągłe próby naśladowania Railsów w innych językach (PHP, Python, Java, C# itp) Jednakże między nimi a Railsami będzie ciągle pewna, trudna do osiągnięcia, jeśli nie w ogóle niemożliwa – bariera. Railsy posiadają nie tylko wszystko, co potrzeba do bardzo produktywnego tworzenia aplikacji internetowych, ale są przy tym równocześnie bardzo eleganckie i czytelne. Eleganckie i czytelne czyli łatwe do nauki. Tak, nauka Rubiego nie jest przeszkodą. Przekona się o tym każdy, kto trochę bliżej przyjrzy się jak działa RoR.

Miałem okazję porównywać ze sobą kilka różnych frameworków. Gdy chciałem przekonać się do któregoś z nich, zawsze ostatecznie wracałem z powrotem do RoR. Po prostu żaden z nich nie jest jak elegancki i prosty w użyciu1. Co jest główną przyczyną takiego wrażenia? Twórca Railsów, David Heinemeier Hansson powiedział kiedyś, że gdyby nie Ruby to nie powstałby Ruby on Rails. Powiedział także, że uważa iż w żadnym innym języku nie da się napisać tak eleganckiego i pięknego kodu. Zatem można powiedzieć, że prawdziwą siłą Railsów jest Ruby. Razem tworzą nierozłączną parę i ta łączność nie dotyczy bynajmniej tylko nazwy. ;) Ruby posiada bowiem pewne unikalne cechy, które pozwoliły stworzyć RoR w postaci, która jest raczej mało nieosiągalna dla innych języków. Już to wyjaśniam.

Panuje powszechnie mniemanie, że Ruby to połączenie cech Pythona i Perla. Ruby (podobnie jak Perl) posiada np. wbudowaną w składnię obsługe wyrażeń regularnych. Posiada także (podobnie jak Python) pełną obiektowość i bardzo elegancką, czytelną (choć nie taką samą) składnię. Pewnym odkryciem było dla mnie to, że językie, do którego Ruby ma najwięcej podobieństw to innego języka – Smalltalk. Lektura cech, filozofii (i nawet do pewnego stopnia składni) Smalltalka pokazuje zdumiewające podobieństwo do Rubiego. Można wręcz odnieść wrażenie, że Ruby to swego rodzaju przeróbka Smalltalka2. Podobieństw jest bardzo wiele. Od słów kluczowych po symbole, sposób tworzenia instancji klas, bloki kodu (ang. closures), kontynuacje3 itp. Jak ktoś jeszcze nie rozumie filozofii Rubiego, jego modelu obiektowego i powodu istnienia otwartych klas, powinien poczytać sobie trochę o Smalltalku.

Ruby jak i Smalltalk posiadają bardzo podobny model obiektowy. Wszystkie obiekty (na drodze dziedziczenia) wywodzą się z jednej, ostateczniej klasy Object. Oba języki mają zaimplementowaną pełną obiektowość. Nie ma (tak ja w Javie) podziału na prymitywy i typy referencyjne. Wszystko jest obiektem i wszystko posiada metody. Dotyczy to nie tylko liczb i napisów ale także obiektu nil, true czy false. Każdy obiekt można przeciążyć i/lub zmodyfikować wewnetrznie (dynamicznie dodając lub usuwając jego metody w trakcie pracy programu)

Z tego wynika, że właściwie to można modyfikować sam język. Daje to możliwości zupełnie nieosiągalne nawet dla tak dobrego i obiektowego języka jakim jest Python. Ruby pozwala na łatwe dodawanie nowych metod do liczb czy napisów. Pozwala na taką modyfikację samego siebie, aby optymalnie nadawał się do realizacji pewnych, specyficznych zadań. Ruby umożliwia zatem tworzenie tego, co się określa mianem języków domenowych (Domain-specific Programming Languages). Są to języki, które w przeciwieństwie do języków ogólnego zastostosowania, zostały zaprojektowane do wykonywania specyficznego zadania/zadań. Zarówno Smalltalk jak i Ruby pozwalają na tworzenie języków domenowych.

Ruby on Rails to nic innego jak framework napisany za pomocą Rubiego zmodyfikowanego w celu uzyskania wysoce produktywnego środowiska do tworzenia nowoczesnych aplikacji internetowych.

Przykładowe helpery dostępne w Rails, które korzystają z nowych metod nie będących standardową częścią Rubiego. Poniższe przykłady pochodzą z 1-g wydania książki Agile Web Development in Rails.

puts 20.bytes #=> 20
puts 20.kilobytes #=> 20480
puts 20.megabytes #=> 20971520
puts 20.gigabytes #=> 21474836480
puts 20.terabytes #=> 21990232555520
puts 20.minutes.ago #=> Tue May 10 16:43:43 CDT 2005
puts 20.hours.from_now #=> Wed May 11 13:03:43 CDT 2005
puts 20.weeks.from_now #=> Tue Sep 27 17:03:43 CDT 2005
puts 20.months.ago #=> Thu Sep 18 17:03:43 CDT 2003
now = Time.now
puts now #=> Tue May 10 17:15:59 CDT 2005
puts now.ago(3600) #=> Tue May 10 16:15:59 CDT 2005
puts now.at_beginning_of_day #=> Tue May 10 00:00:00 CDT 2005
puts now.at_beginning_of_month #=> Sun May 01 00:00:00 CDT 2005
puts now.at_beginning_of_week #=> Mon May 09 00:00:00 CDT 2005
puts now.at_beginning_of_year #=> Sat Jan 01 00:00:00 CST 2005
puts now.at_midnight #=> Tue May 10 00:00:00 CDT 2005
puts now.change(:hour => 13) #=> Tue May 10 13:00:00 CDT 2005
puts now.last_month #=> Sun Apr 10 17:15:59 CDT 2005
puts now.last_year #=> Mon May 10 17:15:59 CDT 2004
puts now.midnight #=> Tue May 10 00:00:00 CDT 2005
puts now.monday #=> Mon May 09 00:00:00 CDT 2005
puts now.months_ago(2) #=> Thu Mar 10 17:15:59 CST 2005
puts now.months_since(2) #=> Sun Jul 10 17:15:59 CDT 2005
puts now.next_week #=> Mon May 16 00:00:00 CDT 2005
puts now.next_year #=> Wed May 10 17:15:59 CDT 2006
puts now.seconds_since_midnight #=> 62159.215938
puts now.since(7200) #=> Tue May 10 19:15:59 CDT 2005
puts now.tomorrow #=> Wed May 11 17:15:59 CDT 2005
puts now.years_ago(2) #=> Sat May 10 17:15:59 CDT 2003
puts now.years_since(2) #=> Thu May 10 17:15:59 CDT 2007
puts now.yesterday #=> Mon May 09 17:15:59 CDT 2005
puts "cat".pluralize #=> cats
puts "cats".pluralize #=> cats
puts "erratum".pluralize #=> errata
puts "cats".singularize #=> cat
puts "errata".singularize #=> erratum

Z racji tego, że Railsy zmodyfikowały Rubiego do realizacji swoich zadań, uważam, że próba sklonowania tego środowiska w jakimś innym języku (poza Smalltalkiem), jest z góry skazana na niepowodzenie. Po prostu nigdy to nie będzie tak piękny kod jak Ruby dla Railsów. Na otarcie łez dla pythonistas, mogę powiedzieć, że przy wszystkich zaletach Rubiego, nadal uważam że Python (jako język) jest nie tylko łatwiejszy do opanowania ale także bardziej produktywny. Ale jeśli chodzi o frameworki, to Rails jest nie do pobicia jeśli chodzi o komfort, możliwości i DRY[4].

1 Zupełnie identyczne wrażenia prostoty jak z Railsami miałem w przypadku szukania dobrego systemu CMS’a. Taki np. pythonowy Plone w porównaniu do ezPublish i całej masy innych, pehapowych rozwiązań jest nie tylko poteżniejszy ale także niezrównanie prostszy i wygodny (po 5 minutach od instalacji, praktycznie bez czytania dokumentacji i bez znajomości Pythona, można stworzyć prosty serwis o całkiem przyzwoitej, podstawowej funkcjonalności)

2 Smalltalk posiada tylko 5 słów kluczowych i proste zasady: wszystko jest obiektem, wszystkie operacje polegają na przesyłaniu metod (nazywanych tu: wiadomościami) między obiektami. Moim zdaniem Smalltalk trochę przesadził z tą zasadą, bo nawet bo zamiast do sterowania kontrolą kodu używane są także wiadomości. Może jest jest spójne, ale trochę dziwnie wygląda. Osobiście bardziej podobają mi się możliwości Smalltalka ale wyrażone w składni …Rubiego. :) Zobacz też Ruby Instead of Smalltalk

3 Kontynuacje są jedną z wbudowanych cech Rubiego o której może być za jakiś czas głośno. Umożliwiają bowiem budowanie tzw. internetowych serwerów kontynuacyjnych, które w pełni zachowują stan pomiędzy requestami i ogromnie upraszczają prace programistów. W tej chwili najlepiej opracowanym frameworkiem tego typu jest smalltalkowy Seaside.

4 DRY to skrót od ang. Dont’t Repeat Yourself. Railsy zostały napisane z wręcz obsesyjną ;) cechą unikania powtarzania kodu. Mniej powtórzeń to nie tylko mniej niepotrzebnej, dodatkowej pracy ale także mniejsze ryzyko popełniania błędu.

Posted in , ,  | Tagi , ,  | 32 comments

Programować jak kaczka

Opublikowane przez Jarosław Zabiełło Fri, 12 May 2006 16:12:00 GMT

“Jeśli coś chodzi jak kaczka, wygląda jak kaczka i kwacze jak kaczka, to z pewnością jest to… kaczka”

Powyższe zdanie jest żartobliwym ujęciem istoty pewnej praktyki programistycznej (popularnej szczególnie w wysokopoziomowych językach dynamicznych takich jak Python, Ruby czy Smalltalk). Najlepiej wyjaśnić to na przykładzie (zresztą wziętym z życia).

Tak się złożyło, że ktoś napisał pewną aplikację w Delphi która wypełniała jedno z pól bazy MSSQL w formacie… RTF. I wszystko było w porządku, do czasu, aż zapadła decyzja, aby te dane wykorzystywać na stronie internetowej. Przeglądarki nie potrafią wyświetlić poprawnie formatu RTF, te dane trzeba jakoś przekonwertować do HTML.

Oczywiście, do tego zadania wybrałem Pythona. Głównie dlatego, że cały system synchronizacji serwerów już wcześniej napisałem w Pythonie. Więc naturalnym było, że dla dodania dodatkowej opcji użyję tego samego języka.

Trzeba było trochę poszukać gotowego kodu do parsowania formatu RTF, bo komu by się chciało samemu pisać parser od podstaw? :) Na szczęście biblioteka się znalazła . Jak to najczęściej bywa z drobnymi projektami open-source: dokumentacji: niet. Ale na szczęście Python jest bardzo przejrzysty i posiada cudowną cechę dla leniwych programistów: docstringi. Chwila oglądania kodu i już widzę, że w środku jest jedna z klas o obiecującej nazwie: Rtf2Html. Patrzę na wywołanie:

if __name__ == '__main__':
    if len(sys.argv) != 2:
        print 'Usage : %s fichier.rtf' % sys.argv[0]
    else:
        finput = open(sys.argv[1],'r')
        foutput = open(finput.name + '.html','w')
        foutput.write('<html><body>')
        tester = Rtf2Html(foutput)
        # reszta kodu...

Widać, że jest tu jeden problem. Ja potrzebuję funkcji operującej na łańcuchach znaków, a tu kod operuje na … obiekcie plikowym. Nie chce mi się zmodyfikować tego kodu. Z pomocą przychodzi duck typing. Zamiast obiektu plikowego podsuniemy kontruktorowi klasy coś co będzie udawać plik. Dla pliku tak naprawdę najważniejsze są dwie metody: read() i write(). Stworzymy więc klasę, która będzie miała te dwie metody i… nic więcej. Skoro będzie kwakać jak kaczka i zachowywać się jak kaczka, to dla tego kodu niczym się nie będzie różnić od pełnej, pierzastej kaczki.

from rtf.Rtf2Html import Rtf2Html

class PseudoPlik(object):
    """Kwa, kwa, kwa ;)"""
    __content = ''
    def write(self, txt):
        self.__content += txt
    def read(self):
        return self.__content

def rtf2html(rtf):
    """RTF to HTML converter"""
    fake = PseudoPlik()
    r = Rtf2Html(fake)
    r.feed(rtf)
    return r.read()

Jak widać, nie trzeba było w ogóle modyfikować niczego w kodzie biblioteki. Wystarczyło tylko podsunąć jej coś, co wygląda jak plik. Reszta tu nie miała znaczenia, bo i tak wykorzystywane były tylko te dwie metody obiektu plikowego.

Posted in  | Tagi  | 6 comments