Archiwalia w kategorii 'code'

Python: django-google

Jeśli ktoś z was czuje nieodpartą ochotę zbudowania aplikacji korzystającej z przypomnień lub kalendarza, to zapraszam do testowania django-google. Docelowo moduł powinien obsługiwać więcej API gdata Google, ale w tej chwili niewiele z nich ma sens w odniesieniu do Django (większość realizuje tylko dostęp do odczytu).

Języki…

Riddle zaprosił, a ja nie mam nic do stracenia. Języki, które znam:

Język Walety Zady
ADA Patrz Pascal Nieprzydatny w mojej pracy
Assembler Najwydajniejszy ze wszystkich Zupełnie nieprzenośny, wymaga dużo dyscypliny, żeby kod dało się czytać
C Mój pierwszy język, wydajny, masa bibliotek Brak obiektowości, strdup, malloc
C++ Wydajny, obiektowy, biblioteka templatek Dalej niski poziom, chora składnia przyjaciół i przeciążania operatorów
ECMAScript Czytelny, obiektowy; JavaScript Debugowanie ActionScript to przeżycie na miarę kamieni nerkowych
Java Czytelny, obiektowy, przestrzenie nazw, przenośność bajtkodu Spory narzut, NazwyTakPrzyjazne, że BezIdeNiePodchodź
Lisp Jeden z pierwszych języków wysokiego poziomu nieczytelny (zupełnie (naprawdę))
LUA Czytelny, obiektowy język wysokiego poziomu, banalnie łatwo osadzalny Mało popularny
Pascal Czytelny, wbudowane operacje na ciągach, binarne ciągi z prefiksową notacją długości Słaba obiektowość, praktycznie martwy, chore implementacje Delphi i Kylix
Perl Prosty jak budowa cepa, obiektowy Często używany zamiast bardziej odpowiedniego shella lub awka, TIMTOWTDI
PHP Jest wszędzie od dziewięciu lat, ostatnio prawie obiektowy; po dwóch miesiącach wydaje ci się, że znasz język na wskroś Tylko ci się wydaje; bękart Perla i shella, biblioteka standardowa nie obsługuje wyjątków, automatycznie ładuje wszelkie rozszerzenia; przez te dziewięć lat wolałbym programować w czymś normalnym
Python Czytelny, obiektowy, przestrzenie nazw, dekoratory, unikod, przenośność bajtkodu, wymaga wcięć! Niektórzy uważają, że wcięcia robi się spacjami
Ruby Mocno obiektowy, przestrzenie nazw Nie obsługuje unikodu, do dziś nie ma żadnej specyfikacji języka
Shell Prosty, oparty o strumienie Nieczytelny, służy tylko do spinania innych narzędzi
SML Czytelny, notacja matematyczna Bezstanowy, programowanie bez skutków ubocznych jest mało praktyczne

Skaranie boskie z wami dzieciaczki. A do tablicy pójdzie teraz… Czesio, znaczy Cezio.

WYSIWTF

Pozazdrościliśmy nieco z ceziem zabawy, jaką serwuje nam codziennie Worse Than Failure (jeśli ktoś jeszcze nie czyta, to migiem dodawać do czytników). W Polsce też jest masa zabawnych sytuacji w firmach, a nie każdy chce od razu pisać do WTF, dlatego też od piątku istnieje i działa (a właściwie to się dopiero powoli rozkręca) WYSIWTF.

Zapraszamy :)

Wiem, że zaraz ktoś mnie zbluzga za to, że 10przykazan miało być nowe, a jeszcze nie jest. WYSIWTF uruchomiliśmy w jakąś godzinę, a 10p potrzebuje jeszcze około dwóch dni miłości, żeby pokazać coś więcej niż poprzednia wersja. Może uda się nam do przykazań usiąść w najbliższy weekend. Poczekajcie jeszcze trochę, warto.

Wyjątkowość PHP

Podobno PHP5 jest językiem obiektowym, niektórzy twierdzą nawet, że językiem czwartej generacji. Ja bym go nazwał językiem obiektowalnym, bo zaiste, można w nim programować obiektowo, jednak sam język ani do tego nie zachęca, ani procesu nie ułatwia. Znakomita większość funkcji bibliotecznych obiektowości zwyczajnie nie obsługuje. Ciekawsze jednak są wyjątki, które w PHP są… niemal bezużyteczne.

Autorzy języka uznali, że najważniejsza jest zgodność wstecz za wszelką cenę. Zamiast jednak przygotować moduł, który dostarczałby API zgodnego ze starszymi wydaniami i zmodernizować rdzeń funkcjonalności bibliotecznej, postanowili ciągnąć za sobą brzemię średniowiecznych początków języka. Złośliwi mówią, że wystarczy zamienić część znaków dolara na małpki i otrzymamy poprawny kod Perla, a usuwając dolary w liniach deklaracji otrzymamy zgodność ze skryptami powłoki. Trudno się dziwić, bo szerokie możliwości języka to głównie posklejane kawałki bibliotek standardowych Perla i C, pomieszane z możliwościami powłoki. Faktycznie, jeśliby dodać do siebie procentowy udział języków pomnożony przez ich generację, to PHP okazuje się stać na czele i już tylko krok dzieli go od interpretacji języka naturalnego.

Nie o tym jednak chciałem pisać. Jedną z konsekwencji zachowania zgodności jest wyjątkowość języka, przejawiające się w braku wbudowanych wyjątków. Nie mam tu na myśli klasy Exception, mówię o tym, że żadna z funkcji bibliotecznych wyjątkami nie rzuca. Zwracają magiczne wartości, ustawiają liczniki błędów, ale wyjątków unikają jak ognia. Dlaczego? Może dlatego, że przeciętny konsument API o obiektowości, wyjątkach i wzorcach projektowych ma takie samo pojęcia, jak ja o ornitologii. Dlatego właśnie uważam, że wyjątki w PHP są niemal bezużyteczne.

$f = foo();
if (foo_error())
	return false;
if (!foo_write($f, 'bar'))
	return false;
if (!foo_write($f, 'baz'))
	return false;
foo_flush($f);
if (foo_error())
	return false;
foo_close($f);
if (foo_error())
	return false;

Czy tak powinien wyglądać kod? A może tak:

$result = true;
try
{
	$f = foo();
	$f->write('bar');
	$f->write('baz');
	$f->flush();
}
catch (FooException $e)
{
	$result = false;
}
finally
{
	if ($f)
		$f->close();
}
return $result;

Żeby móc osiągnąć czytelność drugiej wersji kodu, programista musi poświęcić kilka godzin na napisanie wrapperów dla większości bibliotecznych wywołań. W przeciwnym wypadku używanie wyjątków jest bezcelowe, bo i tak na każdym kroku trzeba kontrolować zwracane wartości. Czy jest tu zaszyta jakaś głębsza logika, czy słusznie wydaje mi się, że PHP5 wygląda na zaprojektowany na kolanie?

Kiedy wykopiesz węża ze sklerozą…

Zupełnie nie rozumiem, czemu na Wykop trafił artykuł o odśmiecaniu pamięci w Pythonie, wiem za to, że jest to świetna okazja, żeby się nad nim nieco popastwić. Zacznijmy od tego, że Python nie kończy się niemą samogłoską i nie odmieniamy go z apostrofem.

W przydługim wstępie do jeszcze dłuższego tekstu czytamy, że Python jest językiem. Świetnie. Zastanawiam się w tym momencie, jaki jest target audience owego dzieła, zanosi się bowiem na wykład dla początkujących. Po przecinku atakuje nas jednak automatyczna alokacja pamięci (i co to właściwie znaczy automatyczna — alokacja następuje wtedy, kiedy programista tworzy nowy obiekt).

Atakuje bez uprzedzenia, wypadałoby chociaż wspomnieć, że w językach niższego poziomu istnieją specjalne operatory i wymagane jest jawne zarządzanie zasobami. W Pythonie (w Javie, PHP…) nie jest wymagane jawne usuwanie obiektów, to prawda. Nie zgodzę się jednak z podsumowaniem, że główną zaletą tego rozwiązania jest fakt, że programiści nie muszą się martwić o zabezpieczenie pamięci.

Główną zaletą posiadania dużego trawnika nie jest możliwość rzadszego sprzątania po swoim psie, tak zbieranie psich bobków, jak pilnowanie zakresów buforów jest świętym obowiązkiem programisty i żaden język go w tym nie wyręczy. Autor tego akapitu chyba nigdy nie próbował napisać w C odpowiednika alaMaKota = ' '.join(('Ala', 'ma', 'kota')), gdyby spróbował, to wiedziałby, co jest główną zaletą. Przejrzystość kodu.

Podsumowując wstęp, Python nie jest wymarzonym narzędziem dla programisty. Python jest jednym z języków i — jak każdy z języków (nie zaczynających się słowem visual) — nadaje się do jednych rzeczy, a nie nadaje do innych. Nie wiem też, jakie powinienem mieć oczekiwania względem procesu śmieciarza, ale praktyka każe mi usuwać duże zbędne obiekty niezależnie od używanego języka.

Dalej mamy całą masę oderwanej od przykładów teorii. Wystarczyłoby powiedzieć, że z odśmiecaniem pamięci wiążą się trzy potencjalne problemy:

  • fragmentacja pamięci, która powoduje, że tworzenie dużych ciągłych struktur staje się bardzo kosztowne,
  • wykrywanie wysp, czyli obiektów odwołujących się do siebie nawzajem, ale oderwanych od aktualnego stanu programu,
  • przechowywanie obiektów, które często zmieniają swój rozmiar, na przykład typów numerycznych o nieskończonej precyzji.

Resztę można sprowadzić do stwierdzenia (którego w artykule brak, co oznacza, że ze swoim tytułem nie ma nic wspólnego), że CPython radzi sobie z wszystkimi trzema wymienionymi powyżej. Ni więcej, ni mniej. Żadnej alokacji, dzielenia stosu ani tablic uchwytów przywoływać tu nie trzeba, bo — za przeproszeniem — robi się burdel, a nic z tego nie wynika.

Podsumowując, możesz w Pythonie programować tak samo jak w Javie albo Rubym i wcale nie musisz z głowy recytować algorytmu śmieciarza. Genialne, tylko o czym był ten artykuł i po co trafił na Wykop?

Python: libchart 0.0.3

Dziś pojawiła się obiecana potrzeba i tak powstała trzecia wersja biblioteki do generowania wykresów. Tym razem nie wymaga PIL i oparta jest w całości o Cairo, a to oznacza śliczne wygładzanie krawędzi.

python-libchart 0.0.3

Jak poprzednio, do pobrania ze strony projektu.

PHP, PCRE i nagłe zgony

Jest wpół do drugiej w nocy. Siedzę w biurze. Serwer Apache wygląda na stabilny. Dokładnie, wygląda.

Prawie wszystkie działające u nas aplikacje są zaimplementowane jako usługi FastCGI serwowane przez Lighttpd. Bazy danych działają w osobnych serwerach wirtualnych, podobnie odseparowany jest Apache, który dzierży brzemię mielenia PHP i przedzierania się przez gąszcze plików .htaccess.

Wszystko byłoby pięknie, gdyby Apache nie zaczął sobie chodzić na spacerek do krainy wiecznych łowów (jak to Indianie mają w zwyczaju czynić po SIGSEGV). Szybki (błyskawiczny rzekłbym, ale to nie prawda) strace i końcówka gdb pokazały, że ślad urywa się gdzieś w PHP.

Następny test — przestawienie PHP z mod_php (użytkownicy nie mają dostępu do kodu serwisów, więc nie bawiliśmy się w żadne separacje użytkowników) na CGI. Tym razem dzielny serwer przetrwał, w przeciwieństwie do skryptu. Kolejny strace i gdb na symulowanym środowisku CGI (w bardzo wysublimowany sposób — export QUERY_STRING=… i tak dalej) i okazuje się, że zdycha jednak biblioteka implementująca PCRE.

Dużo Google. Bardzo dużo. W końcu pomaga Qwiat. Miał to samo, pomógł downgrade pakietu pcre do wersji 6.7. Przebudowa wspomnianego pakietu we wspomnianej wersji, upgrade w vserwerze, restart usług. Działa. Nieźle się maskuje.

Kod serwisów zdaje się działać bez zarzutu. Chciałem zarzucić temat, ale obiecałem znaleźć syntetyczny testcase. Więcej Google. Na liście błędów pcre jest znana podatność na przepełnienie stosu. Autorzy zdają się mieć ją w dużym poważaniu. Wersja 7.x zdycha, wersja 6.x działa. A takiego.

<?php
print 'A teraz coś z zupełnie innej beczki: ';
preg_match('/(.(?!b))*/', str_repeat("a", 10000));
?>

Powyższy kod położył mi każdą kombinację PHP i PCRE. Bardzo ładny sposób, żeby komuś zrobić DOS na PHP działające w trybie FastCGI (po n-tym padzie usługi w krótkim czasie serwer słusznie uzna, że jest uszkodzona i przestanie ją wskrzeszać, przynajmniej na jakiś czas).

I tym optymistycznym akcentem zakończę swoje gadanie od rzeczy i udam się do domu spać.

Religijnie: 10 przykazań i władza nad demonami

Ostatnie dni stanowiły dla mnie nieprzerwane pasmo imprez… wróć! Nieprzerwane pasmo pracy z kodem. A właściwie to jedno i drugie, Python jest tak wygodny, że nawet przy kilku projektach pozostaje czas na imprezy.

O Pythonie za chwilę, najpierw jednak o PHP. Wróciło 10przykazan, po 9 dniach awarii. Długa przerwa, ale głównym problemem był dostęp do internetu. Trzeba wam bowiem wiedzieć, że 1. marca kwietnia (więcej kawy) przeprowadziłem się do nowego mieszkania i do dziś nie udało się nam podpisać umowy z żadnym dostawcą. Ale nie o tym chciałem pisać, okazało się, że przyczyną problemu jest PHP w wersji 5.2.2, które psuje biblioteki RPC.

W wolnym czasie zacząłem też eksperymentować z własną implementacją niesławnego pakietu daemontools. O działalności djb słyszeli chyba wszyscy, a niezorientowanych pozostawię na pastwę Google, bo i szukać nie trzeba długo.

W ITS Ltd. używamy daemontools do zarządzania usługami FCGI. Wszystko byłoby dobrze, gdyby nie fakt, że od czasu do czasu (po zmianie kodu na serwerze) trzeba taką usługę zrestartować, a czasem i zatrzymać na dłużej. Cóż, daemontools pozwala to zrobić tylko rootowi z poziomu konsoli.

Pakiet wygląda na martwy, za trzy tygodnie kończy się jego licencja redystrybucji. Mój nowy projekt poboczny to próba odtworzenia schematu działania oryginału za pomocą odrobiny Pythona. Przy okazji dodałem nieco interfejsów D-Bus. Zainteresowanych wczesnymi testami zapraszam na stronę daemontools-ng.

Python + libchart = ładne wykresy

Długo szukaliśmy sensownej biblioteki, która pozwoli nam narysować prosty wykres kołowy, nie zajmuje pół mega i w trakcie działania nie uruchamia zewnętrznego konwertera PostScriptu. Idealny kandydat powinien do tego mieć sensowne API.

W końcu znudziło mi się szukanie i siadłem do klawiatury. Przypomniałem sobie o świetnej bibliotece libchart, której używamy w Wego CMS i postanowiłem ją odtworzyć w Pythonie.

Charting in python with libchart

Powyżej widać efekt 3 godzin mojej pracy. Kod do działania wymaga Python Imaging Library i jest objęty licencją LGPL.

Pobierz ze strony projektu i podziękuj firmie ITS Ltd. za jego sfinansowanie ;). Wersja 0.0.1 oferuje tylko wykresy kołowe, pozostałe typy pojawią się w miarę potrzeb naszych klientów.

Wego: Wtyczki

Wczoraj pisałem o dodaniu do Wego CMS wtyczek dla systemów płatności online. Cypherq poprosił o szczegóły dotyczące implementacji. Noszę się z zamiarem przygotowania kilkuetapowego howto, kiedy tylko pojawi się darmowa wersja Wego (tak, to bardzo możliwe, po szczegóły odsyłam do nbw). Póki co, o samym sposobie ładowania wtyczek.

Moduły — wtyczki z automatyczną rejestracją

Wego ma dwa rodzaje pluginów. Pierwszy rodzaj — wtyczki z automatyczną rejestracją — używany jest przez moduły. Moduł to zamknięta część systemu, widoczna jako osobna pozycja w panelu. Przykładem niech będą Artykuły lub Kosz.

Automatyczna rejestracja oznacza tyle, że plik z wtyczką zawiera klasę o nazwie takiej samej, jak plik i nie musi właściwie robić nic więcej:

<?

/* $Id: foo.module.php 1 2007-04-27 12:00:00Z patrys $ */

require_once(THE_ROOT . '/class/genericModule.class.php');

/**
 * Foo module
 */

class fooModule extends genericModule
{
	public function __construct(& $modSys)
	{
		parent::__construct($modSys);
		$this->module = 'foo';
	}

	/**
	 * Check if module is usable (dependencies, etc.)
	 */
	protected function __public_init($data)
	{
		return array('success' => true);
	}

	/**
	 * Get module info
	 */
	public function getInfo()
	{
		return array
		(
			'title' => _('Foo'),
			'author' => 'patrys [ patrys at icenter pl ]',
			'version' => '1.0',
			'description' => _('Hello world example'),
			'modified' => filemtime(__FILE__)
		);
	}
}

?>

Do tego dochodzi klasa dla panelu administracyjnego (dla oszczędzenia pamięci, ładowana tylko w przypadku, gdy jest to aktywny moduł w panelu):

<?

/* $Id: foo.panel.module.php 1 2007-04-27 12:00:00Z patrys $ */

class fooPanelModule extends fooModule
{
	protected function __internal_list()
	{
		return 'Hello world!';
	}
}

?>

Jest tu kilka magicznych metod — wszystkie __public_* dostępne są automatycznie z poziomu inych modułów i z poziomu szablonów Smarty; __internal_* za to służą za obsługę ekranów panelu administracyjnego, wymagana jest przynajmniej obsługa metody list, która jest domyślnym widokiem.

Inne wtyczki — ręczna rejestracja

W przypadku wtyczek pól dodatkowych i systemów płatności okazało się, że automatyczna rejestracja nie jest najlepszym wyjściem — jedna klasa może realizować kilka funkcji, a sztuczne po niej dziedziczenie wprowadza tylko chaos w plikach. Zdecydowaliśmy się, że moduły te same muszą zgłosić obsługiwane przez siebie funkcje:

<?

/* $Id: platnosciPayment.class.php 7234 2007-04-25 10:55:39Z patrys $ */

class platnosciPaymentType extends genericPaymentType
{
	public static $paymentList = array
	(
		'platnosci_test' => array
		(
			'code' => 't',
			'name' => 'Platnosci.pl (płatność testowa)',
		),
		'platnosci_mtransfer' => array
		(
			'code' => 'm',
			'name' => 'Platnosci.pl - mBank mTransfer',
		),
		// [...]
		'platnosci_credit' => array
		(
			'code' => 'c',
			'name' => 'Platnosci.pl - Karta kredytowa',
		),
	);

	public function getConfigData()
	{
		// [...]
	}

	public function process()
	{
		// [...]
	}
}

$parent = paymentSystem::getInstance();
foreach (platnosciPaymentType::$paymentList as $key => $val)
	$parent->register($key, 'platnosciPaymentType', $val['name']);
$parent->registerSettings('platnosci', 'platnosciPaymentType', _('Platnosci.pl'));

?>

Dzięki temu możliwa jest obsługa wszystkich sposobów zapłaty za pomocą jednej klasy (konstruktor klasy genericPaymentType otrzymuje i zapisuje wybraną formę zapłaty).

Ładowanie wtyczek

Tutaj poszliśmy na łatwiznę:

public function loadPlugins()
{
	$modDir = dir(THE_ROOT . '/payment/');

	while (($file = $modDir->read()) !== false)
	{
		$fullPath = THE_ROOT . '/payment/' . $file;

		if (!is_dir($fullPath))
		{
			include($fullPath);
		}
	}
}

Ładowanie wtyczek z automatyczną rejestracją wymaga nieco więcej zachodu (po załadowaniu pliku, trzeba jeszcze sprawdzić, czy istnieje klasa o odpowiedniej nazwie i czy dziedziczy po odpowiedniej klasie bazowej), ale samodzielna implementacja nie powinna zająć więcej niż 5 minut.

Na koniec

Po notce o zmianie firmy pojawiły się niejasne spekulacje, jakobyśmy mieli zastąpić nbw butelką wody mineralnej. Spieszę z dowodem, że młodzieniec ów żyje i ma się dobrze (a jeśli już mielibyśmy kogoś zamienić na butelkę wody, to byłby to prędzej sit0).

The Crew
Ekipa, od lewej:
patrys, sit0, nbw, emes
jarv