16

ใช้ swath จาก python

swath เป็นโปรแกรมตัดคำที่ดีโปรแกรมหนึ่ง เอา text อะไรยัดเข้าไปก็มักจะไม่เจ๊ง ไม่ต้องเขียนอะไรครอบมาก แต่ว่าบอกทีก็อยากใช้จาก python วีธีหลักที่จะเรียกใช้ ก็อาจจะสร้าง module ด้วย c++ ขึ้นมาเชื่อม swath กับ python วิธีแรกนี้โปรแกรมทำงานได้รวดเร็ว แต่ว่ามันเหนื่อยนะกว่าจะเสร็จ

ผมก็เลยใช้วิธีที่โปรแกรมทำงานช้าๆ แต่เขียนเสร็จเร็วแทน โดยเรียกผ่าน subprocess

import subprocess

def wordbreak(utxt):
    if not isinstance(utxt, unicode):
        raise TypeError, "wordbreak needs unicode"
    tis_txt = utxt.encode("CP874", 'replace')
    p = subprocess.Popen("swath", stdin = subprocess.PIPE, \
            stdout = subprocess.PIPE, close_fds = True)
    p.stdin.write(tis_txt)
    p.stdin.close()
    raw_output = p.stdout.read().decode("CP874", "replace")
    words = raw_output.split("|")
    p.stdout.close()
    return words

# Copyright (c) 2010 Vee Satayamas
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.

เวลาเรียกใช้ก็สั่ง wordbreak(u”ทดลองตัดคำ”) อะไรแบบนี้ได้ แล้ว wordbreak ก็จะ return list ของคำออกมา ผมใช้ python 2.5.x นะครับ

3

KUCut API

ตั้งแต่ version 1.2.7 เป็นต้นไป KUCut มี API ให้เรียกใช้ง่ายๆ จาก Python แล้ว เวลาเรียกใช้งานทำแบบนี้

from kucut import SimpleKucutWrapper as KUCut
myKUCut = KUCut()
result = myKUCut.tokenize([u"ทดสอบทดสอบ"])

ใน result ก็จะได้ list 3 ชั้นออกมา

1

python: สร้าง type ใหม่จาก function

ผมต้องการสร้าง function ที่เอาไว้สร้าง class อีกทีแบบ struct ใน ruby (ถ้าผมจำไม่ผิดนะครับ) ก็เลยใช้ function: type(name, bases, dict) มาช่วย จริงๆ แล้วผมก็ไม่รู้หรอกว่าเป็นท่าที่ควรทำหรือเปล่า แต่ก็เอามาโพสก่อน เพราะถ้างมอยู่คนเดียวโอกาสที่จะแก้ไขอะไรก็คงยาก

class DefaultDataModelBase(object):
    def __init__(self, **kwargs):
        for key in self.keys:
            if kwargs.has_key(key):
                self.__dict__[key] = kwargs[key]
            else:
                self.__dict__[key] = None

def defaultDataModel(*keys):
    return type("DefaultDataModel", (DefaultDataModelBase,), dict(keys = keys))

ก็ได้ defaultDataModel ออกมาคล้าย struct ล่ะ เวลาใช้ก็ใช้แบบนี้

Student = defaultDataModel('name', 'score')
a = Student(name="นาย ก.", score = 99.99)
print a.score

อะไรประมาณนี้ จริงๆ อยากจะใช้ namedtuple แต่ว่า Python 2.5 ไม่มีก็เลยลองทำแบบนี้ดู

0

Python libthai binding (th_brk only)

I want to use libthai from Python so I wrote this extension.

#include 
#include "structmember.h"
#include 

static PyObject*
th_brk_(PyObject *self, PyObject *args)
{
    PyObject *result = NULL;

    Py_UNICODE *s1;
    int s1_len;

    if (PyArg_ParseTuple(args, "u#", &s1, &s1_len)) {
        int *pos = (int *)malloc(sizeof(int) * (s1_len + 1));
        PyObject *txt_cp874 = PyUnicode_Encode(s1, s1_len, "CP874", NULL);
        Py_ssize_t len = PyString_Size(txt_cp874);
        char *c_txt_cp874 = PyString_AsString(txt_cp874);
        int n = th_brk((unsigned char *)c_txt_cp874, pos, len);
        int i, s = 0;
        char *buffer = (char *)malloc(sizeof(char) * (s1_len + 1));
        result = PyList_New(0);
        for(i = 0; i < n; i++) {
            PyObject *tok_cp874 = PySequence_GetSlice(txt_cp874, s, pos[i]);
            Py_ssize_t tok_len;
            PyObject *tok;
            PyString_AsStringAndSize(tok_cp874, &buffer, &tok_len);
            tok = PyUnicode_Decode(buffer, tok_len, "CP874", NULL);
            s = pos[i];
            PyList_Append(result, tok);
        }
        if(s < len) {
            PyObject *tok_cp874 = PySequence_GetSlice(txt_cp874, s, len);
            Py_ssize_t tok_len;
            PyObject *tok;
            PyString_AsStringAndSize(tok_cp874, &buffer, &tok_len);
            tok = PyUnicode_Decode(buffer, tok_len, "CP874", NULL);
            PyList_Append(result, tok);
        }
        free(pos);
    }
    return result;
}

static PyMethodDef module_methods[] = {
    {"th_brk", (PyCFunction)th_brk_, METH_VARARGS,
     "Thai word break"},
    {NULL, NULL, 0, NULL}   /* sentinel */
};

#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
initlibthai(void)
{
    PyObject* m;
    m = Py_InitModule3("libthai", module_methods,
                       "libthai module");
    if (m == NULL)
      return;
}
0

AuthKit + OpenID + MysqlStore

In previous examples, I always use file store, which I found that it is quite difficult to use when I deploy BasaAsa to the server so I change to use MySQL store. To use MySQL store, I modified websetup.py in order to create table for OpenID library.

websetup.py:

"""Setup the basaasa application"""
import logging

from basaasa.config.environment import load_environment
from basaasa.users.authkit_elixir_driver import UsersFromDatabase
from basaasa import model
from authkit.authenticate.open_id import make_store #added
from pylons import config #added

log = logging.getLogger(__name__)

def setup_app(command, conf, vars):
    """Place any commands to setup basaasa here"""
    load_environment(conf.global_conf, conf.local_conf)
    from elixir import metadata
    metadata.create_all(checkfirst=True)
    store_type = config.get('authkit.openid.store.type') #added
    stote_config = config.get('authkit.openid.store.config') #added
    conn, cstore = make_store(store_type, stote_config) #added
    if store_type == 'mysql': #added
        cstore.createTables() #added

Then after I run paster setup-app development.ini, tables for storing OpenID information have been created.

2

OpenID + AuthKit + Pylons (simple)

I try to post simple example how to use OpenID with AuthKit 0.4.3 and Pylons 0.9.7. by adapting example from AuthKit. So I just create new Pylons app named “topenid” and modify it as follow:

  1. development.ini:
    These lines below are added to development.ini

    authkit.setup.enable = true
    authkit.setup.method = openid, cookie
    authkit.openid.store.type = file
    authkit.openid.store.config =
    authkit.openid.charset = UTF-8
    authkit.openid.path.signedin = /auth/signin
    authkit.cookie.signoutpath = /auth/signout
    authkit.openid.store.baseurl = http://localhost:5000
    authkit.cookie.secret = somesecret
    
  2. midddleware
    ...
    import authkit.authenticate # this line is added
    ...
        app = PylonsApp()
        app = authkit.authenticate.middleware(app, app_conf) # this line is added
    ...
  3. topenid/controllers/auth.py
    import logging
    
    from pylons import request, response, session, tmpl_context as c
    from pylons.controllers.util import abort, redirect_to
    from authkit.permissions import RemoteUser
    from authkit.authorize.pylons_adaptors import authorize
    
    from topenid.lib.base import BaseController, render
    
    log = logging.getLogger(__name__)
    
    class AuthController(BaseController):
        def index(self):
            return 'Hello World'
    
        @authorize(RemoteUser())
        def test(self):
            return 'test'
    
        def signout(self):
            return 'signout'
    
        def signin(self):
            return 'signed in'

These are all modifications. I can access http://localhost:5000/auth/index as expect and http://localhost:5000/auth/test is redirected to an OpenID signin page. And I can sign out by visiting http://localhost:5000/auth/signout.

This example seems to work well but I need something more complex for example every user doesn’t have the same role. So I may post another more complex example soon.

8

แก้ปัญหาเล็กๆ น้อยๆ กับ Pylons + mod_wsgi + virtualenv

เดิมที่ผมมีปัญหาว่า app ที่ใช้ pylons เขียนใช้ได้บ้างไม่ได้บ้าง ชนิดที่ว่าเข้าครั้งแรกไม่ได้พอ reload ก็ใช้ได้ เป็นแบบนี้ไปเรื่อยๆ โดยไม่รู้ว่าเกิดจากอะไรกันแน่. แต่เท่าที่ดู error log ของ Apache พอว่าเกิดจากหา package ไปเจอ. ผมก็ลองแก้ไปแบบชนิดที่ว่าคิดอะไรออกก็ลองดูหมด จนผ่านไปหลายชั่วโมง. สิ่งที่ทำคือ แทนที่จะเพิ่ม path เข้าไปใน app ที่วางไว้เฉยๆ โดยไม่ได้ install เช่น ผมเอา app ชื่อ basa ไปวางไว้ใน home ก็จะอยู่ที่ /home/vee/basa ผมก็เพิ่ม /home/vee เข้าไปใน python path เลย โดย site.addsitedir(‘/home/vee’) แต่ก่อนหน้านั้นก็ site.addsitedir(‘/home/vee/env/PYLONS/lib/python2.5/site-packages’) ไปแล้ว. ตอนนี้เลยเปลี่ยนไปเรียก python setup.py build และ python setup.py install ก่อนก็จะทำให้ basa นี้ไปอยู่ใน path อย่าง /home/vee/env/PYLONS/lib/python2.5/site-packages เป็นต้น ก็ทำให้ add แค่ path เดียวคือ /home/vee/env/PYLONS/lib/python2.5/site-packages ก็พอ. ผลคือตอนนี้ยังไม่พบปัญหาอะไร app ใช้งานได้ปกติดี.

ปกติชาวบ้านเขาอาจจะ install กันแบบนี้อยู่แล้ว แต่ผมดันทำอะไรประหลาดเองหรือเปล่าก็ไม่รู้ … และไม่อยากรู้เท่าไหร่ ขอให้ app ทำงานเนียนๆ ก็พึงพอใจมากแล้ว ^_^.

0

KunyitTST: Taking list of string as key

KunyitTst is a ternary search tree library in Python. Normally, it takes string or unicode as key since I use it for Thai word segmentation or something similar. By the way, now I want to use it to store list of string key too for storing (Moses) phrase table so it needs some modification. I’m still not sure how much it will effect performance. Anways, the patch is as follow:

Index: tests/test_basic.py
===================================================================
--- tests/test_basic.py	(revision 15)
+++ tests/test_basic.py	(working copy)
@@ -26,6 +26,27 @@
     def setup_method(self, method):
         self.setup()

+    def test_insert_has_key_list(self):
+        self.tst.insert(['i', 'have', 'a', 'key'], 10)
+        self.tst.insert(['i', 'have', 'a', 'book'], 20)
+        self.tst.insert(['a', 'book'], 30)
+        assert self.tst.has_key(['i', 'have', 'a', 'key']) == True
+        assert self.tst.has_key(['i', 'have', 'a', 'book']) == True
+        assert self.tst.has_key(['a', 'book']) == True
+        assert self.tst.has_key(['a', 'look']) == False
+
+    def test_iterator_list(self):
+        self.tst.insert(['a', 'book'], 30)
+        i = self.tst.iterator()
+        assert i.apply(u"a") == True
+        assert i.is_break_pos() == False
+        assert i.apply(u"book") == True
+        assert i.is_break_pos() == True
+        assert i.get_value() == 30
+
+        i = self.tst.iterator()
+        assert i.apply(u"book") == False
+
     def test_happy_insert(self):
         self.tst.insert(u"abc", 1)

Index: kunyittst/tst.py
===================================================================
--- kunyittst/tst.py	(revision 15)
+++ kunyittst/tst.py	(working copy)
@@ -27,7 +27,8 @@
         if not self._valid:
             return False
         if isinstance(ch, str) or isinstance(ch, unicode):
-            ch = ord(ch)
+            if len(ch) == 1:
+                ch = ord(ch)
         self._n = self._tst.goto_child(self._n)
         if self._n == None:
             return False
@@ -163,7 +164,10 @@
     def insert(self, k, v):
         p = self._root * Tst.SIZE
         for i, ch in enumerate(k):
-            c = ord(ch)
+            if len(ch) == 1:
+                c = ord(ch)
+            else:
+                c = ch
             while self._nodes[p + Tst.KEY] != c:
                 if self._nodes[p + Tst.KEY] == None:
                     self._nodes[p + Tst.KEY] = c