Tuesday, December 29, 2009

go语言的声明

看go主页上的介绍时, 有点给我还不解的做法: 就是变量声明时的语法。

golang的声明语言如下:

keywork var_name type = value

1. keywork包括:var, const, type, func
2. var_name为变量名
3. type为变量的类型(与keywork中的type有区别)

看过的语言一就是没有类型, 一就是把类型放在变量名前面:表示这个变量是什么类型。

感觉很自然。 但是golang就反这个习惯。 把类型的位置反过来了。


在golang的手册上给出了两个原因:

原因一: Also functions read better and are consistent with other declarations.


说是与函数形式一致

golang中的函数的定义方式:
func sum(a, b int) int { return a + b }

golang中的变量的定义方式:
var p, q *int

大概的意思是:
    keywork               var_name         type
函数的声明关键字
         函数名       返回值类型

原因二: short declaration


golang中的变量声明有两种缩写方式:
var var_name = value
* 放在后面就方便缩写, 成为没有类型的写法?
* 不过看上去真的比较像javascript的声明

这时没有了类型, 类型为根据value的类型决定

还有一种缩写:
var_name := value
* 这就像python的声明了


顺眼了~~~~

想了一下, 感觉还是不错的. 当我们想声明一个变量时:
1. 想到var ( 想到一个keywork, 因为它是语言的一部分 )
2. 想到变量的名称var_name
3. 想到变量的初始化. (1)这时的初始化golang是提供缩写方式的. 这就与平时的脚本一样. (2)当不想这时初始化时, 再定下这个变量的类型(毕竟golang是强类型语言)

也是很好的一种使用感觉.( 个人的感觉~~~~ )

BTW:: 其实上, golang在你没有初始化的情况下它会帮你初始化. 称为'zero value', 这个值会根据变量的类型变化. 如下:
The zero value depends on the type: integer 0,  floating point 0.0, false, empty string, nil  pointer, zeroed struct, etc.

Monday, December 28, 2009

安装golang玩玩

make软件只有两种結果:
1. 通过
2. 不通过

-_- ! 等于没有说~~。 其实我想说的是:
1. 通过: 表示程序的源代码没有问题的情况。 与用户的环境有没有配置好没有关系
2. 不通过: 表示程序的源代码本身就有问题。

之前下载chromium下来make就有这样的问题: 人家还在开发, 刚好check out了一份有问题的版本下来make。 那是浪费时间。
* 就算是ubuntu的daily build都不是每天都make得过的。

今天我取得的是changeset:   4476的代码。可以make得过。
顺便看看那个牛B人最新的提交:

changeset:   4398:683ed10f7832
user:        Ken Thompson <ken@golang.org>
date:        Sat Dec 12 14:36:52 2009 -0800
summary:     more on the optimizer

golang的主页上其实就写得很清楚了。 只是有个容易出问题的地方: enviroment variable一节。

请老实把提到的每个环境变量都export一次。 如果$GOBIN不是在PATH中的话, 请:
export PATH=${PATH}:${GOBIN}/bin


其实只是这句需要注意的。


C中检测头文件的存在

C中没有像python的try語句, 当需要引入系统的函数时会有个问题, 例子:

1. 使引入外部函数时更加友好

try:
    import lxml
except:
    print "no exist lxml module"
    sys.exit()

2. 根据情况使用不同的函数

try:
   import lxml
except:
    try:
       import xml
    except:
       print "no exist lxml and xml module
       sys.exit()


在看同事的代码分析后, 知道了C是怎么处理这种问题: 使用preprocessor处理:

# if ! defined _SYS_TYPES_H
you must include <sys/types.h> before including this file
# endif

这将会产生错误, 不被编译. 这就把问题放在一个宏中. 那么这个宏是怎么获得的呢?

这个问题抛给了GNU autoconf. 在autoconf产生的configure文件被运行后, 会产生一个叫config.h的头文件, 里面将会有系统所有的头文件的宏.

如果C程序需要安全地使用外部函数. 这种机制无疑可以使编译过程更加友好.

Sunday, December 27, 2009

locale

python这部分与libc的函数相似.

setlocale函数有两种用法:
1. 取得指定项的值: 第二个参数为None(Null)

In [5]: locale.setlocale(locale.LC_CTYPE,None)
Out[5]: 'en_US.UTF-8'

2. 设置locale
默认的locale都是C.
设置为指定的locale
In [6]: locale.setlocale(locale.LC_CTYPE, 'en_GB.UTF-8')
Out[6]: 'en_GB.UTF-8'

当setlocale的第二个参数为空字符串时, 使用父进程的locale
In [2]: locale.setlocale(locale.LC_CTYPE, None)
Out[2]: 'C'

In [3]: locale.setlocale(locale.LC_CTYPE, '')
Out[3]: 'en_US.UTF-8'


与locale有关的几个环境变量
LC_*
LC_ALL
LANG

当LANG被定义, LC_*又没有定义时, LC_*都使用LANG的值, 当LC_*单独定义时, 会覆盖LANG的值.
当LANG没有被定义时, LC_*都为"POSIX"
当LC_ALL被定义时, LC_*都被强制使用LC_ALL的值, 这是最高的策略.

还有一个GNU里定义的环境变量: LANGUAGE 它的作用与LC_MESSAGE的作用一致.




http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html#tag_08_02

gcc的预处理选项

C的预处理选项在代码里很常见. 对控制生成的目标代码有控制作用. 如:
int main()
{
     do_something();

#ifdef __DEBUG__
     printf("__DEBUG__ DEFINED\n");
     printf("%d\n",A);
#endif

    return 0;
}

可以通过__DEBUG__去控制是否加入测试代码. 现在就有个问题: 怎么在编译时改变这一个值? 如:
#if __DEBUG__ == 1

难道要去修改源文件? 显然这是不对的做法. gcc是有参数去处理这些东西:
-D
name=definition

-U name

下面做做测试:
jessinio@jessinio-laptop:/tmp$ cat test.c

void *main(int argc, char *argv[]){

#ifdef __DEBUG__
    printf("%s\n", "debug info");
#endif


}

jessinio@jessinio-laptop:/tmp$ gcc -E test.c
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.c"


void *main(int argc, char *argv[]){






}

可以看出, 因为没有那个__DEBUG__ 宏, 所以printf没有被加入代码中. 如下就可以:
jessinio@jessinio-laptop:/tmp$ gcc -E -D__DEBUG__ test.c
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.c"


void *main(int argc, char *argv[]){


    printf("%s\n", "debug info");



}

不过这有个问题, 就是每次都是在gcc命令中这样的参数不是很难看? 环境是环境变量? 找个Makefile看一下就知道了
看到这句:
# Compiler options
OPT=        -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes

很明显, 是使用变量的方式. gcc怎么知道呢?
又看到这句话:
$(CC) $(OPT) $(LDFLAGS) $(PGENOBJS) $(LIBS) -o $(PGEN)

很明显, 就是在命令行中指定~~~~  (-_-)!






python默认对待unicode的方式

我不是故意去找问题的。 但是我很不喜欢问题不请自来。谁会愿意把青春花在这个鸟问题上面.

不过话也说回来, 我又不玩游戏, 不下棋. 解决这个问题就当是一场“抓迷藏游戏”吧。

这里, 主要涉及到两个编码问题:
1. 文件系统使用编码方式. 这个值由 sys.getfilesystemencoding() 取得
2. python的unicode函数使用的默认解码方式. 这个值由 sys.getdefaultencoding() 取得.

世界的编码是非常之烦的一类事, 一看到locale -m的输出結果我就没有胃口了. 还是集中解决UTF-8, unicode, ascii之间的问题就够用了

locale的重要性

可以说, locale对程序的行为影响是很大的. linux下的libc提供了机制方便处理这种问题. 举个例子:
jessinio@jessinio-laptop:/$ export LC_ALL='POSIX'
jessinio@jessinio-laptop:/$ locale
LANG=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=POSIX
jessinio@jessinio-laptop:/$ bash

这时的bash环境是无法使用中文的. 就算你copy过去它也不买单, locale影响到程序对字节流的处理方式(这水深, 主要是于C函数上, 这里先不钻进去).

事件的源由


前段时间把时区的问题搞清楚了。 今夜也跑来一个i18n问题。 只能发挥宅男的本色。碰个杀一个。

这是一句很平常的語句: os.path.exists( path ), 在哪里使用都很正常。但是在mod_wsgi中使用就狗日的有问题:

File "/home/jessinio/data/workspace/project/home/views.py" in index
  32.     os.path.exists(path)
File "/usr/lib/python2.6/genericpath.py" in exists
  18.         st = os.stat(path)

Exception Type: UnicodeEncodeError at /
Exception Value: ('ascii', u'/tmp/\u6881\u5e86\u559c', 5, 8, 'ordinal not in range(128)')

os.stat出问题。为什么在一些地方python解释器可以解码, 但是在mod_wsgi中又无法解码?

开始关注于C语言的i18n的处理方式。环境变量则是问题的入手点. 下面看一个证据:
python文件内容:
jessinio@jessinio-laptop:~$ cat /tmp/en.py
# coding: utf-8
import os

s = u'/tmp/梁庆喜'
os.path.exists(s)

# 下面证明了LANG环境变量的作用:
jessinio@jessinio-laptop:~$ env|grep LANG
LANG=en_US.UTF-8
GDM_LANG=en_US.UTF-8
jessinio@jessinio-laptop:~$ python /tmp/en.py

jessinio@jessinio-laptop:~$ export LANG=zh_CN.GBK
jessinio@jessinio-laptop:~$ python /tmp/en.py
Traceback (most recent call last):
  File "/tmp/en.py", line 6, in <module>
    os.path.exists(s)
  File "/usr/lib/python2.6/genericpath.py", line 18, in exists
    st = os.stat(path)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 5-7: ordinal not in range(128)

先看看os.stat到底做了什么, 在Modules/posixmodule.c文件里的posix_do_stat函数这样写:

    if (!PyArg_ParseTuple(args, format,
                          Py_FileSystemDefaultEncoding, &path))
        return NULL;
    pathfree = path;

    Py_BEGIN_ALLOW_THREADS
    res = (*statfunc)(path, &st);
    Py_END_ALLOW_THREADS


那么Py_FileSystemDefaultEncoding哪里来? 下面的内容来自Python/pythonrun.c. 这个文件是python启动时使用的. 这里有设置了Py_FileSystemDefaultEncoding

#if defined(Py_USING_UNICODE) && defined(HAVE_LANGINFO_H) && defined(CODESET)
    /* On Unix, set the file system encoding according to the
       user's preference, if the CODESET names a well-known
       Python codec, and Py_FileSystemDefaultEncoding isn't
       initialized by other means. Also set the encoding of
       stdin and stdout if these are terminals, unless overridden.  */

    if (!overridden || !Py_FileSystemDefaultEncoding) {
        saved_locale = strdup(setlocale(LC_CTYPE, NULL));
        setlocale(LC_CTYPE, "");
        loc_codeset = nl_langinfo(CODESET);
        if (loc_codeset && *loc_codeset) {
            PyObject *enc = PyCodec_Encoder(loc_codeset);
            if (enc) {
                loc_codeset = strdup(loc_codeset);
                Py_DECREF(enc);
            } else {
                loc_codeset = NULL;
                PyErr_Clear();
            }
        } else
            loc_codeset = NULL;
        setlocale(LC_CTYPE, saved_locale);
        free(saved_locale);

        if (!overridden) {
            codeset = icodeset = loc_codeset;
            free_codeset = 1;
        }

        /* Initialize Py_FileSystemDefaultEncoding from
           locale even if PYTHONIOENCODING is set. */
        if (!Py_FileSystemDefaultEncoding) {
            Py_FileSystemDefaultEncoding = loc_codeset;
            if (!overridden)
                free_codeset = 0;
        }
    }

Py_FileSystemDefaultEncoding 的值在python环境下也是可以取得的: sys.getfilesystemencoding()

不过, 没有set函数, python里也没有C代码提供了修改的方法. 也就是说: python启动后这个值是被固定.( 有点郁闷~~~ )

python提供了一个叫locale的module, 类似C的locale处理函数(其实就是C的locale函数封装), 但是:
* 这个库无法修改Py_FileSystemDefaultEncoding. 不要希望在在启动python后通过这个库的函数修改Py_FileSystemDefaultEncoding
* 也就是说, locale无法修改python对待file system encoding的处理方法.

BTW:: 本人试图在python启动后修改这个值做了N个努力, 我日~~~~

file system encoding的作用

文件系统里存在的是文字的交换码. 比如一个文件的路径在文件系统内是utf-8方式存放的. 如:
In [34]: os.listdir('/tmp')
Out[34]:
['\xe6\xa2\x81\xe5\xba\x86\xe5\x96\x9c',]

当试图在python里使用一个unicode的字符串对象去对应文件系统里的资源时, python就会使用file system encoding的方式去编码, 如:
In [41]: a = unicode('/tmp/梁庆喜', 'utf-8')
In [42]: a
Out[42]: u'/tmp/\u6881\u5e86\u559c'
In [43]: os.path.exists(a)
Out[43]: True

python对待unicode的方法和下面的方式一致:
In [36]: a = '/tmp/梁庆喜'
In [37]: a
Out[37]: '/tmp/\xe6\xa2\x81\xe5\xba\x86\xe5\x96\x9c'
In [38]: os.path.exists(a)
Out[38]: True

如果os.path.exist的参数是unicode的话, 它将会使用file system encoding的方式去对unicode编码. 然后使用系统的API.

default encoding的作用

下面使用一个例子就可以看到这个问题:
>>> import sys
>>> sys.getdefaultencoding()
'ascii'
>>> a = '梁庆喜'
>>> unicode(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)

unicode函数在不提供第二个参数时, 就要使用default encoding的解码方式.

平时启动python的方式python会把setdefaultencoding方法从sys中删除.

我们可以重载这个编码方式: 使用python的-S参数启动python:
>>> import sys
>>> sys.getdefaultencoding()
'ascii'
>>> sys.setdefaultencoding('utf-8')
>>> sys.getdefaultencoding()
'utf-8'
>>> a = '梁庆喜'
>>> unicode(a)
u'\u6881\u5e86\u559c'

如果不想启动时使用-S参数, 也可以修改/usr/lib/python2.6/site.py里的setencoding函数

Thursday, December 24, 2009

mod_wsgi发布django

web framework的分布, 一般就用两种方式:
1. embed
2. daemon

其实这两种方式都有自己的特点.  只是本人喜欢使用embed方式分布. 因为有http server在把关. 藕合性好点。

python的web分布, 无疑WSGI比较正统. 都成了PEP文档了.

apache + mod_wsgi支持framework的embed方式. 并且配置简单.

安装mod_wsgi后, 加载module:

LoadModule wsgi_module /usr/lib/apache2/modules/mod_wsgi.so

配置站点
<VirtualHost *:80>
        ServerAdmin jessinio@gmail.com

        WSGIScriptAlias / /home/jessinio/data/workspace/project/django.wsgi

        <Directory /home/jessinio/data/workspace/project/>
        Order allow,deny
        Allow from all
        </Directory>
</VirtualHost>


django.wsgi文件的内容:

#!/usr/bin/env python

import os
import sys
# mod_wsgi对stdout的写做了限制. 如果代码里直接使用了print这样对stdout操作的語句的话, 可以使用下面修改stdout的方法.
# 这样, 代码里对stdout的操作都会成为stderr, 而stderr的内容在apache的error日志文件内

sys.modules['sys'].stdout = sys.modules['sys'].stderr

# 不希望使用绝对路径的硬编码方式, 可以使用下面的__file__变量这种好方法
# 此文件放在project目录下
file_path = os.path.dirname(__file__)
parent_path = os.path.dirname(file_path)

sys.path.append(file_path)
sys.path.append(parent_path)


os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'

import django.core.handlers.wsgi
# WSGI的核心工作函数
application = django.core.handlers.wsgi.WSGIHandler()


OK, 很方便的收工了.