Cython dan mingw32

Sekedar catatan supaya ngga lupa dan supaya blog ini ada isi barunya xD

Kalau make Cython, kita bisa menambahkan perintah berikut di awal skrip Python agar setiap skrip Cython langsung dikompilasi secara otomatis.

import pyximport
pyximport.install()

Namun proses kompilasi bisa berakhir dengan gagal dengan pesan berikut.

Unable to find vcvarsall.bat

Oh iya, ini saya ngejalanin skripnya di atas Windows..

Saya ngga tau apa penyebabnya, mungkin ada hubungannya dengan Visual Studio dan mingw32 yang tidak begitu akur atau bagaimanalah itu. Yang jelas, salah satu solusinya adalah dengan menambahkan parameter pada pemanggilan pyximport.install() seperti berikut.

mingw_setup_args={'options': {'build_ext': {'compiler': 'mingw32'}}}
import pyximport; pyximport.install(setup_args=mingw_setup_args)

Sumber: Stack Overflow

Gerobak

Apa ini? singkatnya: apt-web yang lebih personal. Coba buka http://gerobak.dahsy.at/

Ada yang mau bantuin ngetes? Ada yang mau nyumbang sebuah VPS atau server beneran? Ada yang mau bantuin bikin?

Kok butuh VPS/server sendiri? Karena sepertinya Gerobak butuh sumber daya komputasi yang tidak kecil. Bisa bikin tetangga bahkan pemilik kontrakan ngomel2 kalau dipasang bareng-bareng dengan yang lain.

Kode sumber: http://github.com/fajran/gerobak. Lisensi: GNU AGPL 3

django-loginurl

Setelah dibahas, akhirnya saya diperbolehkan merilis salah satu komponen yang saya kerjakan ke publik. Silakan langsung datangi salah satu tempat berikut.

Apakah django-loginurl ini?

Ini adalah sebuah aplikasi kecil untuk Django untuk membuat sebuah URL yang dapat digunakan untuk login. Jadi, hanya dengan membuka URL tersebut, kita dapat login secara otomatis sebagai user terkait pada sebuah website yang dibangun di atas Django.

URL tadi itu terlihat seperti berikut.

http://example.com/loginurl/sebuah-kunci-unik

Setiap kunci dapat diatur berapa banyak penggunaan dan kapan kunci ini kadaluarsa. Sebagai contoh, dengan mengeset maksimal 1 kali penggunaan, berarti kita bisa membuat URL/kunci sekali pakai untuk login.

Contoh skenario penggunaannya adalah menyediakan website yang memungkinkan user tidak perlu mengingat password. Saat login, user cukup memasukkan email yang sudah terdaftar dan nanti website akan mengirimi URL khusus untuk login sebagai user terkait.

Contohnya agak maksa? ada loh kasus dimana hal seperti ini dibutuhkan 🙂

oke deh.. selamat menikmati 😀

SMTP Server Bohongan

Saat lagi bikin sesuatu yg perlu ngirim-ngirim email, kita mungkin perlu sebuah SMTP alias email server bohongan yg bisa dipake tuk ngirim email tapi email tersebut tidak terkirim ke mana2. Tujuannya memang hanya untuk menguji apakah yg kita buat dapat berkomunikasi dg SMTP server dg baik.

Salah satu cara untuk menyediakannya adalah dengan menyiapkan sebuah SMTP server uji coba. Server ini sudah diatur sedemikian rupa sehingga tidak akan meneruskan email ke tujuannya, namun hanya akan menjadi “blackhole” saja.

Namun jika hal tsb dirasa ribet, coba pasang Python =D ternyata si Python ini sudah menyediakan sebuah kelas yang namanya benar-benar bisa membuat mulut tersenyum: DebuggingServer dari modul smtpd! Seperti yg dapat diperkirakan, sebuah SMTP server dapat dibuat dan kelas ini hanya akan mencetak setiap email masuk ke layar!

$ python -m smtpd -n -c DebuggingServer localhost:1025

Jalankan satu baris di atas dan SMTP server bohongan sudah siap dipakai 😀

Perkalian matrix

.. dalam satu baris! euh, maksudnya 1 statement.

hasil = map(lambda index:
      sum(
        map(lambda n: n[0] * n[1],
          zip(
            [a[i * 4 + (index % 4)] for i in range(4)],  # baris
            [b[(index / 4) * 4 + i] for i in range(4)]   # kolom
          )   # pasangan elemen baris dan kolom
        )   # kalikan setiap pasangan
      ),  # jumlahkan
    range(4*4))

Input adalah dua buah matrix berukuran 4 × 4, yaitu a dan b, dan hasilnya tentu saja matrix 4 × 4 juga yang ada di dalam variabel hasil.

Ketiga matrix ini ditulis dalam bentuk array 1 dimensi dari elemen-elemen yang ada dengan menjejerkan kolom-kolom yang ada. Sebagai contoh, matrix di bawah ini

 1   2   3   4
 5   6   7   8
 9  10  11  12
13  14  15  16

akan ditulis dalam variabel berupa [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16].

Kalau mau dibalik (urut baris), tukar saja a dan b. Tuk perkalian matrix n x n, ganti saja 4 menjadi n tersebut.

Mohon dikoreksi kalau ada yg salah. Hasil nyoba2 membandingkan hasilnya dg hasil perhitungan dg GNU Octave sih bener.. 😀

Jadi pengen latihan functional programming..

Python 32bit di Snow Leopard

Python 2.6.1 bawaan Snow Leopard ternyata secara default bekerja dalam mode 64bit. Pada beberapa kasus, hal ini bisa membawa masalah misalnya jika bekerja dengan pustaka lain yang tidak menyediakan versi 64bit. Contohnya adalah pustaka QuickTime yang digunakan oleh Pyglet.

$ python coba-pyglet.py
Traceback (most recent call last):
  ...
  File "build/bdist.macosx-10.6-universal/egg/pyglet/lib.py", line 226, in load_framework
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ctypes/__init__.py", line 423, in LoadLibrary
    return self._dlltype(name)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ctypes/__init__.py", line 345, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: dlopen(/System/Library/Frameworks/QuickTime.framework/QuickTime, 6): no suitable image found.  Did find:
    /System/Library/Frameworks/QuickTime.framework/QuickTime: no matching architecture in universal wrapper
    /System/Library/Frameworks/QuickTime.framework/QuickTime: no matching architecture in universal wrapper

Kalau dicek, memang pustaka tersebut tidak mengandung versi 64 bit.

$ file /System/Library/Frameworks/QuickTime.framework/QuickTime
/System/Library/Frameworks/QuickTime.framework/QuickTime: Mach-O universal binary with 2 architectures
/System/Library/Frameworks/QuickTime.framework/QuickTime (for architecture i386):   Mach-O dynamically linked shared library i386
/System/Library/Frameworks/QuickTime.framework/QuickTime (for architecture ppc7400):    Mach-O dynamically linked shared library ppc

Lalu caranya gimana? Ternyata manual python di Snow Leopard sudah memberitahukan informasi ini 😀

$ export VERSIONER_PYTHON_PREFER_32_BIT=yes

Cukup mengeset environment variable seperti di atas dan jalankan Python seperti biasa. Python akan dijalankan dalam mode 32 bit dan masalah di atas pun tidak ada.

Kalau ingin diset secara default, maka jalankan perintah berikut.

$ defaults write com.apple.versioner.python Prefer-32-Bit -bool yes

Setiap Python yg dijalankan setelah ini akan secara otomatis berjalan di bawah mode 32 bit. Ganti nilai yes menjadi no untuk mematikannya alias mengembalikan agar berjalan dalam mode 64 bit secara default.

Tempel sana tempel sini

Hasil iseng2 dg Django: Tempel http://github.com/fajran/tempel 😀

Demo yang bisa dilihat dan bahkan bisa dipake ada di http://tempel.sebelah.web.id/

Bisa jadi contoh bagi yang mau belajar Python dan Django. Source code nya ngga banyak jadi mudah2an mudah dipelajari. Ada satu library tambahan yg dipake, yaitu Pygments yang dipakai untuk mewarnai source code. Tuk proyek ini sendiri, Buildout dipakai tuk mempermudah penyiapan semua library yg dipakai.

Kalau mau nyoba..

$ git clone git://github.com/fajran/tempel.git
$ cd tempel
$ easy_install -U setuptools
$ python bootstrap.py
$ ./bin/buildout
$ ./bin/django syncdb
$ ./bin/django runserver

Setelah itu buka http://localhost:8000/ di browser.

Keterangan tambahan

  • Kalau ngga bisa pake git, coba download source codenya di http://github.com/fajran/tempel/tarball/master
  • Saat menjalankan ./bin/buildout, source code Django dan Pygments akan secara otomatis di-download. Jadi pastikan nyambung dg Internet.
  • Kalau ngga mau pake Buildout, siapkan sendiri Django dan Pygments. Source code Tempel sendiri ada di dalam direktori src/

Oya, apaan sih ini? ini adalah tiruan lain lagi dari “pastebin” alias tuk nyimpen apalah.

Duplikasi UUID

Menurut Wikipedia,

… the probability is about 0.00000000006 (6 × 10−11), equivalent to the odds of creating a few tens of trillions of UUIDs in a year and having one duplicate.

However, these probabilities only hold when the UUIDs are generated using sufficient entropy. Otherwise the probability of duplicates may be significantly higher, since the statistical dispersion may be lower.

Ceritanya saya lg bikin aplikasi yg cukup tergantung dengan UUID. Aplikasi ini akan nerima request, membuat sebuah UUID, lalu diasosiasikan dengan data yg dikirim pada request tersebut. Bisa dibilang cukup tergantung karena keunikan UUID yang dibuat itu bisa jadi menjadi harga mati yg harus dipenuhi. Semua ID yang dibuat harus unik, gak boleh tidak. Yang menjadi masalah adalah kemarin saya menemukan ID yang sama 🙁 bahkan saya selalu bisa mereproduksi masalahnya T_T

Aplikasi yg saya bikin ini ditulis dengan Python dan memakai CherryPy tuk HTTP framework. Saya menemukan kasus ini ketika saya menjalankan aplikasi tsb dengan Python 2.5.2 bawaan Ubuntu 8.04.3 (i386) dan Python 2.5.1 yg ada di Fedora 8 (amd64). Saat itu saya mencoba melakukan benchmark aplikasi dengan Apache Benchmark dengan membuat 1024 requests dalam 16 concurrent connections.

Sebagai informasi, CherryPy ini adalah sebuah HTTP framework multi-threading jadi setiap koneksi masuk akan ditangani oleh satu thread berbeda..

Kalau mau diperas sampe habis, kira2 ini aplikasi yg saya buat.

import cherrypy
import uuid

class Root(object):
    @cherrypy.expose
    def index(self):
        id = uuid.uuid4()
        id = str(id)
        print "#%[email protected]" % id
        return ''

cherrypy.config.update({'log.screen': False,
                        'environment': 'production'})
cherrypy.quickstart(Root())

Oh iya, UUID yang saya pakai itu adalah UUID versi 4 yg menggunakan random number generator dalam pembuatan UUIDnya.

Setelah itu saya jalankan aplikasi tsb seperti berikut.

$ python2.5 test.py > daftar

Apache Benchmark lalu saya jalankan tuk membuat 1024 requests dalam 16 concurrent connections.

$ ab -n 1024 -c 16 http://127.0.0.1:8080/

Setelah selesai, saya cek berkas daftar yang terbentuk, memastikan 1 baris hanya berisi 1 buah UUID. Lalu jadikan isi berkas tsb menjadi unik dan membandingkan hasilnya.

$ sort -u daftar > unik
$ wc -l daftar unik

Seharusnya kedua berkas tersebut memiliki jumlah baris yang sama yang berarti setiap baris adalah unik. Namun sayangnya, saya tidak menjumpai hal tsb 🙁

Berhubung masih penasaran, saya akhirnya nyoba menggunakan Python 2.5.4 dan Python 2.6.4 hasil kompilasi sendiri di mesin Fedora tsb. Saya melakukan pengujian hal yang sama dan hasil yg saya temui adalah saya masih menemukan duplikasi UUID pada saat menggunakan Python 2.5.4. Sedangkan penggunaan Python 2.6.4 memberikan hasil yang saya harapkan, tidak ada duplikasi UUID.

Apa ini artinya Python 2.5.x bermasalah dalam hal ini? Saya belum menemukan kasus serupa setelah nyoba2 googling. Ada yang punya informasi lbh?

Ups, belum juga tulisan ini dipublish, saya dah nemuin sumber masalahnya 😀 Tadi salah kata kunci spertinya hehehe.. intinya adalah Python 2.5 memang memiliki bug di UUIDv4 generatornya. Silakan baca http://bugs.python.org/issue4607

Oh ya lagi.. asya dah nyoba jg pake UUIDv1 tapi ternyata masalah masih ada di Python 2.5 🙁

Pentingnya koma

Coba lihat potongan eksekusi Python berikut.

>>> type(1)
<type 'int'>

>>> type([1])
<type 'list'>

>>> type((1))
<type 'int'>

>>> type((1,))
<type 'tuple'>

Pada saat membuat sebuah tuple, jika hanya ada satu elemen yang ingin kita masukkan, jangan lupa menambahkan tanda koma! Jadi alih-alih menulis ("isinya"), jika kita benar-benar ingin membuatnya menjadi sebuah tuple, tulislah ("isinya",).

Supaya makin jelas bedanya, coba lihat lagi contoh di bawah ini.

>>> a = ("satu", "dua")
>>> a[0]
'satu'

>>> a = ("satu")
>>> a[0]
's'

>>> a = ("satu",)
>>> a[0]
'satu'

Mengimpor modul Python secara dinamis

Di Python, untuk memakai modul lain dalam modul yang kita buat, kita perlu mengimpor modul lain tersebut dengan perintah import seperti potongan kode berikut.

import sys

Modul bernama sys akan dapat dipakai setelah perintah di atas dijalankan. Sebagai contoh, argumen (parameter) saat mengeksekusi sebuah modul dapat diperoleh dari sys.argv yang merupakan sebuah list.

Jika nama modul yang ingin dipakai sudah diketahui dari awal, kita bisa menuliskannya secara langsung dalam kode yang kita buat. Namun bagaimana jika nama modul tersebut belum diketahui dari awal sehingga harus dapat diimpor belakangan secara dinamis?

Python menyediakan fasilitas ini melalui fungsi __import__ yang menerima parameter berupa nama modul dalam bentuk string. Berikut ini adalah cara menggunakannya.

import sys
__import__("sebuah.modul")
modul = sys.modules["sebuah.modul"]

Variabel modul di atas akan menjadi “perwakilan” dari modul bernama sebuah.modul. Melalui modul inilah isi dari modul sebuah.modul dapat diakses.

Berikut ini adalah contoh penggunaannya. Maaf kalau panjang 😀 Saya memiliki sebuah direktori dan 4 buah kode Python (modul) yang disusun sebagai berikut.

- sebut.py
+ angka/
  - __init__.py
  - satu.py
  - dua.py

Bisa dilihat kita memiliki modul angka.satu dan angka.dua. Kita akan menggunakan dua modul ini untuk ujicoba impor secara dinamis. Isi modul pertama adalah seperti berikut.

def berapa():
    return "satu"

Isi berkas dua.py mirip dengan yang di atas namun mengembalikan nilai "dua" bukan "satu".

def berapa():
    return "dua"

Isi __init__.py bisa dibiarkan kosong.

Sekarang mari buat sebut.py yang akan menerima sebuah parameter berupa nama modul. Setelah modul didapat, fungsi berapa() akan dipanggil.

import sys
nama_modul = sys.argv[1]
__import__(nama_modul)
modul = sys.modules[nama_modul]
print modul.berapa()

Jika sebut.py dijalankan dengan angka.satu sebagai parameter, seharusnya kata satu akan dicetak. Begitu pula angka.dua yang akan menghasilkan kata dua.

$ python sebut.py angka.satu
satu

$ python sebut.py angka.dua
dua

Agar lebih yakin mengenai variabel-variabel yang ada dalam sebut.py, coba cetak nilai yang dikeluarkan oleh fungsi type.

print type(nama_modul)
print type(modul)
print modul.__name__

Jika dijalankan dengan angka.satu sebagai nama_modul, berikut inilah yang akan dihasilkan.

<type 'str'>
<type 'module'>
angka.satu

Bisa dilihat bahwa masukan nama_modul tetap berupa string, lalu modul yang diimpor memang benar berupa modul yang bernama angka.satu.

Sekian saja tutorial singkat dari saya. Kode lengkap bisa diunduh dari http://arsip.fajran.web.id/impor-modul.tar.gz. Semoga bermanfaat 🙂