Thursday, October 22, 2009

django的session处理

I. cookie与session的关系

HTTP是无状态协议, 它区别请求是借且于HTTP中的cookie字段来实现.
服务器端发出带cookie字段的HTTP response大致如下:
Set-Cookie: name=newvalue; expires=date; path=/; domain=.example.org
客户端发出带cookie字段的HTTP request大致如下:
Cookie: name=newvalue; expires=date; path=/; domain=.example.org

HTTP协议中对cookie有约定的属性, 见: http://en.wikipedia.org/wiki/HTTP_cookie#Cookie_attributes

session是在cookie的机制的基础上, 增加一个可以被WEB服务使用的token. 此token对应于服务器端的一个些具体的, 不希望在HTTP中传输的数据.

这就是{key:vaule}的关系

II. django里的cookie与session

在django的request对象中, 有两个与会话相关的对象:
request.COOKIES: 一个dict实例
request.session. 一个SessionStore类的实例. 在SessionMiddleware中被创建.

django在默认时, cookie被加入了一个叫sessionid的属性. 如下:
(Pdb) print request.COOKIES
{'sessionid': '40c340a947b41e4def92ed70c25affbe'}
* 可以通过settings.py修改

这个属性就是django里与session对应的token. 被称为session_key, 如:
(Pdb) print request.session.session_key
40c340a947b41e4def92ed70c25affbe

III. 服务器端返回set-cookie段

要把session对应的session_key放在HTTP的cookie段中, 是用到response.set_cookie方法. 一般情况下, 我们不需要直接调用它, 因为:
SessionMiddleware的process_response方法中调用了set_cookie方法.
需要被中间件调用se_cookie方法需要一个条件:
modified = request.session.modified
if modified or settings.SESSION_SAVE_EVERY_REQUEST:

要想request.session.modified == True, 有两种方法:
1. 生成一个新的session对象
2. 修改或者增加session的key对应的值
也就是说, request.session.modified为True. 这涉及到SessionStore类. 下面理理这个类.

SessionStore类

在看django代码时, 会扯到python的一种语法: descriptor. 具体见:
1. http://users.rcn.com/python/download/Descriptor.htm
2. http://www.ibm.com/developerworks/cn/linux/l-python-elegance-2.html

SessionStore类主要起到session管理的作用. 如生成新的session, 修改session中key的value. 检查session_key是否有对应的session对象等等

1. 产生Session对象

SessionStore产生新的session对象(就是新的session_key)有两种方法:
1. load()
2. create()
load是从数据库中对指定session_key对应的session对象, 如果没有就调用create, 所以看create方法:
    def create(self):
        while True:
            self.session_key = self._get_new_session_key() #其实就是产生一个唯一的key
            try:
                # Save immediately to ensure we have a unique entry in the
                # database.
                self.save(must_create=True) #应用了数据库的models对象
            except CreateError:
                # Key wasn't unique. Try again.
                continue
            self.modified = True
            self._session_cache = {}
            return

2. 修改Session实例

在SessionStore类中, 定义了N多对Session实例的操作, 但是这些操作都是引用self._session这个descriptor. 这个descriptor到底返回了什么呢?
   def _get_session(self, no_load=False):

        """
        Lazily loads session from storage (unless "no_load" is True, when only
        an empty dict is stored) and stores it in the current instance.
        """
        self.accessed = True
        try:
            return self._session_cache
        except AttributeError:
            if self._session_key is None or no_load:
                self._session_cache = {}
            else:
                self._session_cache = self.load()
        return self._session_cache

    _session = property(_get_session)

* 引用self._session 其实是引用了self.load(), 这就是descriptor用法!
* 上面的self._session_cache就是session实例的session_data对应的那个dict实例


SessionStore对session实例的一个修改方法:
    def setdefault(self, key, value):
        if key in self._session:
            return self._session[key]
        else:
            self.modified = True
            self._session[key] = value
            return value
* 其中的 self._session[key]其实等于 self.load()[key] = value, 结合self.load的代码后同于 self._session_cache[key] = value

session对应的字典的保存:
在SessionBase中定义了一方法:
    def encode(self, session_dict):
        "Returns the given session dictionary pickled and encoded as a string."
        pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL)
        pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
        return base64.encodestring(pickled + pickled_md5)
可以看出, 字典是被pickled成一堆字符串了. 需要被使用时, 就使用SessionBase的decode方法


BTW:: 使用eclipse看代码真是他妈的爽!! 前段时间花了不少功夫在eclipse上面, 还是有回报的. 看来除了firefox外, 还有一个一定要是GUI的软件

Wednesday, October 21, 2009

django的auth与session

一直对网站的认证感兴趣. 看了一把django在这方面的实现方式:
认证有两部分, 一是session, 二是用户的信息(密码等).

django的contrib.auth是基于contrib.session的
django是怎么生成request.session和request.user的呢?

因为request.session和user都是在view被调用之前生成的. 所以使用到了中间件:
MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
)

django.contrib.sessions.middleware.SessionMiddleware的内容:
class SessionMiddleware(object):                                                                                  
    def process_request(self, request):                 
        engine = import_module(settings.SESSION_ENGINE)
        session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)
        request.session = engine.SessionStore(session_key)    
django在默认情况下使用数据库存放session, 它还实现了方式存放session的方法. 可以通过SESSION_ENGINE来配置
初始化SessionStore时需要一个参数, 如果没有显式给出就是None, 代码:
class SessionBase(object):
    """
    Base class for all Session classes.
    """
    TEST_COOKIE_NAME = 'testcookie'
    TEST_COOKIE_VALUE = 'worked'

    def __init__(self, session_key=None):
        self._session_key = session_key
        self.accessed = False
        self.modified = False
SessionStore可以使用的方法在SessionStore和SessionBase两个类中可以找到

对于request.user的情况:
django.contrib.auth.middleware.AuthenticationMiddleware的内容
class LazyUser(object):
    def __get__(self, request, obj_type=None):
        if not hasattr(request, '_cached_user'):
            from django.contrib.auth import get_user
            request._cached_user = get_user(request)
        return request._cached_user

class AuthenticationMiddleware(object):
    def process_request(self, request):
        assert hasattr(request, 'session'), "The Django authentication middleware requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
        request.__class__.user = LazyUser()
        return None

很短,就一个构造函数! 但是python的__get__有意思! AuthenticationMiddleware中只是实现了request.user的引用. 平时django应用使用request.user的方式:
1. request.user.name
2. request.user.is_authenticated()
上面的request.usr不只是一个LazyUser的实例吗? 怎么与django.contrib.auth.models.User关联在一起?

重点在于LazyUser的__get__方法. 关于这种用法, 下次认真做记录

Monday, October 12, 2009

linux代码浏览好去处

在看人家的blog文章时, 发现一个很好的网站: http://lxr.linux.no

此网站对linux内核代码索引. 方便交叉引用.

Linux的代码比森林还要复杂. 本人觉得, 最好不急着看linux代码, 这步之前应该把linux内核的功能与之结构搞明白. 然后才走这一步. 否则很痛苦.

比如:
多看点这样的图片: http://iamanush.com/wp-content/uploads/2008/06/linux_kernel_map_img_3.png

多看点这样的文章: http://docs.huihoo.com/linux/kernel/a2/index.html


哈, 本人想像中的走法. 别人有别人的路. 如果有好的, 推介一把吧.

Saturday, October 10, 2009

ebuild依赖死循环

使用gentoo其实也不是很久. 但是起码几个月了. 呃.... 对portage还是很陌生. 看来只能慢慢来了.

今天碰到这个问题:

[nomerge      ] dev-python/pycairo-1.8.8 [1.8.2] USE="doc%* -svg%"
[nomerge      ]  dev-python/sphinx-0.6.1-r1  USE="doc -test"
[ebuild  N    ]   dev-python/jinja2-2.1.1-r1  USE="doc -examples -i18n -test"
[ebuild  N    ]    dev-python/sphinx-0.6.1-r1  USE="doc -test"

 * Error: circular dependencies:

('ebuild', '/', 'dev-python/sphinx-0.6.1-r1', 'merge') depends on
  ('ebuild', '/', 'dev-python/jinja2-2.1.1-r1', 'merge') (buildtime)
('ebuild', '/', 'dev-python/jinja2-2.1.1-r1', 'merge') depends on
  ('ebuild', '/', 'dev-python/sphinx-0.6.1-r1', 'merge') (buildtime)

 * Note that circular dependencies can often be avoided by temporarily
 * disabling USE flags that trigger optional dependencies.

看来是一个死循环, 不过, emerge做得很好, 知道了! 也给出了建议: 临时去掉一(些)USE可以解决这种optional dependencies

在/etc/portage/package.use加入dev-python/pycairo -doc 果真好了.

另:

emerge的用法中:
emerge [options] [action] [ebuild | tbz2file | file | set | atom]....

有一个叫atom的东西, 指定它是可以使用如: >, >=, <之类的符号的. 也就是说, 在/etc/portage/package.use配置文件中:
dev-python/pycairo -doc

=dev-python/pycairo-1.8.8 -doc

是不同的!!

emerge的输出提示

在console和terminal下的命令行有两种提示方式:
1. 颜色
2. 额外的字符

I. 颜色

颜色在视觉上给人一定的提示. 不论是terminal下还是console下, 本人严重依赖于颜色! 如果两者的输出没有颜色是非常难看, 很容易看少, 看漏信息

使用得最多的就是ls命令的颜色了, 如:
jessinio@niolaptop ~ $ type ls
ls is aliased to `ls --color=auto'

平时使用有颜色的ls也是使用的参数去控制的.

还好, gentoo的emerge在这方面做得不错, 来两幅例子, 下面两幅输出文字相同, 只是颜色的区别:

看一幅没有color的输出:

下面是有color的输出:

颜色还是很鲜明的, 特别是在一堆文字中强调一小部分文字, 比如画中的-idn


II. 额外的字符

还是与ls命令为例:
jessinio@niolaptop ~ $ ls --color=none -F
结果为:
输出的文件名后有额外的字符: "/", "*"
这些额外的字符其实是表示不同的强调含义!
       -F, --classify
              append indicator (one of */=>@|) to entries

III. 两种方式的比较

颜色只要是给人类看到的. 对于computer是对颜色没有兴趣的. 反而它对额外的字符比较感兴趣. 用一个grep就可以方便处理带有"*"的文件名, 多方便!

IV. emerge的输出

今天update world, 感受emerge的提示帮助本人大大提高了升级的便利. 随便做做笔记. 方便日后回顾

emerge的输出同时有两种提示, 从上面emerge的输出图片都可以看得出.
1. 颜色由--color参数指定(默认是打开的)
2. 额外的symbols是由-v(--verbose)打开. 如下:
              Symbol   Location    Meaning
              ──────────────────────────────────────────────────────────────
              -        prefix      not enabled (either disabled or removed)
              *        suffix      transition to or from the enabled state
              %        suffix      newly added or removed
              ()       circumfix   forced, masked, or removed

从实例中, 本人注意到:
-前缀的都是这种颜色的!
*后缀的USE都是这种颜色的!
有%的USE都是这种颜色的!

下面翻译一下symbol的意思:
-前缀: 被禁止的USE.
*后缀: 有两层意思:
1. 与USE的默认值不同. 即被人为enabled了;
2. 与已安装的包的USE不同. 这又有两种情况:
1. 与已安装的包相比, USE被修改
2. 正在使用的USE在新的版本中被remove了. 这时与%同时存在
%后缀: 被增加或者是被移除了.

这些symbol本身不难, 难在它们之间可以同时存在! 上面对*后缀描述得详细, 因为它很敏感. 它的存在表示:
* 此USE被修改或者被enable了!
* transition to or from the enabled state 这句话其在太有难度了!

所以, 应该留言!

其它:
man 5 color.map就可以看到: color.map - custom color settings for Portage
用户是可以自定义颜色的. 当然也有默认的颜色信息在这个man手册里了!

Wednesday, October 7, 2009

麦克风与music

最近玩起skype了. 说到skype, 我就被折腾得.... 呃!!!
搞alsa就扯到一堆与声音, 音乐相关的术语. 现在还没有搞清楚
不过最后使用了一个旧版本的skype才完成了语音通话

在保持通话连接的情况下有时需要来一段music, 想到了软件关掉microphone:

#!/bin/bash                                                                  
# shutdown microphone, otherwise no un-pause music 
amixer -c 0 set Capture nocap && mocp -U                   

当对方有话来时, 停掉music, 打开microphone:
#!/bin/bash                                                                                                         
# an error will raise if mocp server no running, but this is no-importance                
# example: You can stop microphone but no play music if mocp server no running 
mocp -P                                                                                                               
amixer -c 0 set Capture cap                                                                                  

把上面的两段代码绑定到两个hotkey上, 结果.... 就像DJ 一样, 想说话就说话, 想来music也行. 哈

Monday, October 5, 2009

动态链接库

多人使用的发行版本就是方便. 下个bin就可以使用. 比如chromium有deb包的.
今天懒了, 想下载个bin版本的mysql-gui-tools用. 结果给我报了个Error:
jessinio@niolaptop /tmp/mysql-gui-tools-5.0 $ ./mysql-administrator-bin
./mysql-administrator-bin: error while loading shared libraries: libpcre.so.3: cannot open shared object file: No such file or directory    

这其实就是dynamic share library的问题. 因为下载的binary文件中包括了需要使用的动态链接库的绝对路径. 三个问题:
1. 我的gentoo的动态链接库放在哪里呢?
2. 我的gentoo有没有安装这个library呢?
3. 怎么得知这个二进制文件中需要使用的动态链接库的绝对路径呢?

还有一个是不希望出现的问题: 动态链接库的版本问题. 出这个问题还不如干脆自己emerge一个.

对于第一个问题: 哪里放library文件的呢?
linux下没有强制, 只有约定! 一般放在下面的文件夹中:
1. /lib
2. /usr/lib
3. /usr/local/lib

随便看到两个比较有意思的文夹:
1. /usr/local/libexec
2. /usr/libexec
* 文档是这样描述这两个文件夹的用法的: Note that if your library calls programs that can only be called via
libraries, you should place those programs in /usr/local/libexec
这两个文件夹下的文件说白了就是可运行的程序! 只不是我们说常用的CLI. 而是只为了给API调用的程序

第二个问题: 我的gentoo有没有安装这个library呢?
在上面的基础上:
jessinio@niolaptop ~$ find /lib/ /usr/lib /usr/local/lib -name "*pcre*"

可以轻松出来:
/lib/libpcre.so.0
/lib/libpcre.so.0.0.1

可知道没有名叫: libpcre.so.3 的文件, 应该为找到的/lib/libpcre.so.0创建一个link文件(这其实是so文件的命名方式). 问题是这个link文件应该放在哪里才会被运行的程序使用呢? 这就来了第三个问题: 怎么得知这个二进制文件中需要使用的动态链接库的绝对路径呢?

jessinio@niolaptop /tmp/mysql-gui-tools-5.0 $ ldd mysql-administrator-bin |grep pcre
        libpcre.so.3 => /lib/libpcre.so.3 (0xb7261000)
这就被强大的ldd命令挖出来了, 创建一个link文件:
jessinio@niolaptop /tmp/mysql-gui-tools-5.0 $ sudo ln -s /lib/libpcre.so.0.0.1 /lib/libpcre.so.3

这就解决了下载的binrary文件在gentoo下的运行问题. 当然, 不是万能的! 要根据实际问题找解决方法. gentoo下有时间还是emerge(至少make)一个binrary!

于关dynamic share library的文档: http://www.linux.org/docs/ldp/howto/Program-Library-HOWTO/shared-libraries.html