January 12, 2010

HYRY 's Blog

Sphinx中文搜索插件

用Sphinx写文档很爽,用Python做科学计算 就是用Sphinx编译的,但是Sphinx的搜索功能不支持中文,究其原因是因为它不支持中文分词,产生的索引文件searchindex.js中没有正确的中文单词索引。进行分词的程序在Sphinx目录的search.py文件中。此程序使用正则表达式将英文单词分出来,因此只需要将其中的两个使用 word_re变量地方替换为中文分词算法即可。
 
为了维护方便,我没有直接修改此文件,而是做了一个扩展插件,其源程序如下:
 

from os import path

import re
import cPickle as pickle

from docutils.nodes import comment, Text, NodeVisitor, SkipNode

from sphinx.util.stemmer import PorterStemmer
from sphinx.util import jsdump, rpartition

from smallseg import SEG

DEBUG = False

word_re = re.compile(r'\w+(?u)')
seg = SEG()

stopwords = set("""
a  and  are  as  at
be  but  by
for
if  in  into  is  it
near  no  not
of  on  or
such
that  the  their  then  there  these  they  this  to
was  will  with
""".split())

if DEBUG:
    testfile = file("testfile.txt", "wb")

class _JavaScriptIndex(object):
    """
    The search index as javascript file that calls a function
    on the documentation search object to register the index.
    """

    PREFIX = 'Search.setIndex('
    SUFFIX = ')'

    def dumps(self, data):
        return self.PREFIX + jsdump.dumps(data) + self.SUFFIX

    def loads(self, s):
        data = s[len(self.PREFIX):-len(self.SUFFIX)]
        if not data or not s.startswith(self.PREFIX) or not \
           s.endswith(self.SUFFIX):
            raise ValueError('invalid data')
        return jsdump.loads(data)

    def dump(self, data, f):
        f.write(self.dumps(data))

    def load(self, f):
        return self.loads(f.read())


js_index = _JavaScriptIndex()


class Stemmer(PorterStemmer):
    """
    All those porter stemmer implementations look hideous.
    make at least the stem method nicer.
    """

    def stem(self, word):
        word = word.lower()
        return word
        #return PorterStemmer.stem(self, word, 0, len(word) - 1)


class WordCollector(NodeVisitor):
    """
    A special visitor that collects words for the `IndexBuilder`.
    """

    def __init__(self, document):
        NodeVisitor.__init__(self, document)
        self.found_words = []

    def dispatch_visit(self, node):
        if node.__class__ is comment:
            raise SkipNode
        if node.__class__ is Text:
            words = seg.cut(node.astext().encode("utf8"))
            words.reverse()
            self.found_words.extend(words)

class IndexBuilder(object):
    """
    Helper class that creates a searchindex based on the doctrees
    passed to the `feed` method.
    """
    formats = {
        'jsdump':   jsdump,
        'pickle':   pickle
    }

    def __init__(self, env):
        self.env = env
        self._stemmer = Stemmer()
        # filename -> title
        self._titles = {}
        # stemmed word -> set(filenames)
        self._mapping = {}
        # desctypes -> index
        self._desctypes = {}

    def load(self, stream, format):
        """Reconstruct from frozen data."""
        if isinstance(format, basestring):
            format = self.formats[format]
        frozen = format.load(stream)
        # if an old index is present, we treat it as not existing.
        if not isinstance(frozen, dict):
            raise ValueError('old format')
        index2fn = frozen['filenames']
        self._titles = dict(zip(index2fn, frozen['titles']))
        self._mapping = {}
        for k, v in frozen['terms'].iteritems():
            if isinstance(v, int):
                self._mapping[k] = set([index2fn[v]])
            else:
                self._mapping[k] = set(index2fn[i] for i in v)
        # no need to load keywords/desctypes

    def dump(self, stream, format):
        """Dump the frozen index to a stream."""
        if isinstance(format, basestring):
            format = self.formats[format]
        format.dump(self.freeze(), stream)

    def get_modules(self, fn2index):
        rv = {}
        for name, (doc, _, _, _) in self.env.modules.iteritems():
            if doc in fn2index:
                rv[name] = fn2index[doc]
        return rv

    def get_descrefs(self, fn2index):
        rv = {}
        dt = self._desctypes
        for fullname, (doc, desctype) in self.env.descrefs.iteritems():
            if doc not in fn2index:
                continue
            prefix, name = rpartition(fullname, '.')
            pdict = rv.setdefault(prefix, {})
            try:
                i = dt[desctype]
            except KeyError:
                i = len(dt)
                dt[desctype] = i
            pdict[name] = (fn2index[doc], i)
        return rv

    def get_terms(self, fn2index):
        rv = {}
        for k, v in self._mapping.iteritems():
            if len(v) == 1:
                fn, = v
                if fn in fn2index:
                    rv[k] = fn2index[fn]
            else:
                rv[k] = [fn2index[fn] for fn in v if fn in fn2index]
        return rv

    def freeze(self):
        """Create a usable data structure for serializing."""
        filenames = self._titles.keys()
        titles = self._titles.values()
        fn2index = dict((f, i) for (i, f) in enumerate(filenames))
        return dict(
            filenames=filenames,
            titles=titles,
            terms=self.get_terms(fn2index),
            descrefs=self.get_descrefs(fn2index),
            modules=self.get_modules(fn2index),
            desctypes=dict((v, k) for (k, v) in self._desctypes.items()),
        )

    def prune(self, filenames):
        """Remove data for all filenames not in the list."""
        new_titles = {}
        for filename in filenames:
            if filename in self._titles:
                new_titles[filename] = self._titles[filename]
        self._titles = new_titles
        for wordnames in self._mapping.itervalues():
            wordnames.intersection_update(filenames)

    def feed(self, filename, title, doctree):
        """Feed a doctree to the index."""
        self._titles[filename] = title

        visitor = WordCollector(doctree)
        doctree.walk(visitor)

        def add_term(word, prefix='', stem=self._stemmer.stem):
            word = stem(word)
            word = word.strip(u"!@#$%^&*()_+-*/\\\";,.[]{}
")
            if len(word) = 1: return
            if word.encode("utf8").isalpha() and len(word)  3: return
            if word.isdigit(): return
            if word in stopwords: return
            
            try:
                float(word)
                return
            except:
                pass
                
            if DEBUG:
                testfile.write("%s\n" % word.encode("utf8"))
            self._mapping.setdefault(prefix + word, set()).add(filename)
       
        words = seg.cut(title.encode("utf8"))
        for word in words:
            add_term(word)
                
        for word in visitor.found_words:
            add_term(word)


def load_indexer(self):
    def func(docnames):
        print "############### CHINESE INDEXER ###############"
        self.indexer = IndexBuilder(self.env)
        keep = set(self.env.all_docs) - set(docnames)
        try:
            f = open(path.join(self.outdir, self.searchindex_filename), 'rb')
            try:
                self.indexer.load(f, self.indexer_format)
            finally:
                f.close()
        except (IOError, OSError, ValueError):
            if keep:
                self.warn('search index couldn\'t be loaded, but not all '
                          'documents will be built: the index will be '
                          'incomplete.')
        # delete all entries for files that will be rebuilt
        self.indexer.prune(keep)
    return func

def builder_inited(app):
    if app.builder.name == 'html':
        print "****************************"
        app.builder.load_indexer = load_indexer(app.builder)

def setup(app):
    app.connect('builder-inited', builder_inited)

 
 这个扩展插件使用smallseg中文分词库进行中文分词。

smallseg中文分词库下载地址: http://code.google.com/p/smallseg

2010-01-12 21:06:34 CST

January 06, 2010

Gawain's Jail

帮助怡帆

看到拯救怡帆:请帮助这位漂亮、坚强的女孩。我自己到今年5月底就会成为一名父亲,深感这样的事情是大不幸,希望怡帆能够挺过去,去拥有她应该拥有的童年。

捐赠方式
1. 银行转账 
怡帆妈妈农业银行账户: 
户名:周萍 
账号:6228480010211053011 
开户行:中国农业银行北京市分行白石桥支行 
2. 支付宝 
怡帆基金支付宝账户:yifanfoundation@gmail.com 
3. 和睦家基金会 (美元捐款) 
另有网站:http://www.help-yifan.org,网站上也可以看到捐款信息。望怡帆重返健康!

2010-01-06 07:11:33 CST

December 31, 2009

Gawain's Jail

终了2009

2009年又要过去了,一年又一年,日子总是追着走。

从工作、学习和生活三个方面去说,2009年做的事真是不多,有些得过且过的感觉了。年初我有许多的计划,可是到了年终,细细的数来却没能完成几样。生活上值得庆祝的事情,一来办了婚礼,二来呢做了准爸爸。工作上没有值得庆祝的事,只有值得反省的事。学习上的事情,是觉的学的太慢了,而且网撒的太大,有点收不住的感觉了。

2010年对自己的希望是:

  1. 做一个好爸爸。
  2. 多学一门外语。
  3. 把学习的重点放在计算机科学上,不要再搞民科了。要深一些!
  4. 广交好友,提升RP。
  5. 在人大的学习该有个了结了。
  6. 多了解一些微观经济学的东西。
  7. 克服拖沓症。

2009-12-31 02:23:53 CST

December 26, 2009

HYRY 's Blog

用Python做科学计算有封面了

终于开始了10天的长假,准备在假期里面好好校对一下《用Python做科学计算》,并完成一些新的章节。 

为了感觉更加正规一些,花了点时间做了一个封面。图中的算式 e**(i*pi)+1的值等于0,是一个经典的数学公式,它将圆周率pi, 欧拉常数e, 虚数单位i, 和整数0和1,通过加法 乘法和幂运算结合起了。希望本书也能起到这种大融合的作用。


2009-12-26 13:01:48 CST

December 11, 2009

Gawain's Jail

Automator Proxy Toggle Shell

人都是被逼出来的,为翻墙方便,用Automator写了一个Service,就执行一段shell,还设置了一个快捷键。

#!/bin/sh
STAT=`sudo networksetup -getwebproxy Ethernet | head -1 | cut -d: -f 2|sed 's/ //g'`
if [ "$STAT" == "No" ];then
	networksetup -setwebproxystate Ethernet on;
    /Users/guixing/bin/growlnotify -m "Proxy On";
else
	networksetup -setwebproxystate Ethernet off;
    /Users/guixing/bin/growlnotify -m "Proxy Off";
fi
growlnotify是Growl的一个命令行工具。

2009-12-11 02:29:26 CST

Limodou

mootools发布插件网站

可以在 http://mootools.net/forge/ 上访问到。关于网站发布的声明 http://mootools.net/blog/2009/12/10/the-official-mootools-plugins-repository-is-here/ 。真是不错。不过等2.0还是让人心急啊。


类别:Mootools 查看评论

2009-12-11 00:00:00 CST

December 04, 2009

Gawain's Jail

Google的公共DNS服务

Google提供了公共的DNS服务,三金和老黄马上就想到了对CDN厂商的冲击。我看了下Google的Performance Benefits,记一笔。

发生在解析服务器和其它DNS服务器的传输时间,有三个原因。

无缓存的情况有一些数据,NS服务器拿到一个无缓存的请求,会导致至少1次的外部NS查询,一般情况会是2次以上。

根据Googlebot的情况来看,平均解析时间是130ms,然而还有4-6%的请求会直接超时,这通常是UDP丢包或服务器无法到达。把丢包,死NS,NS配置错误等因素都计算进来的话,实际的解析时间是300-400ms。

无缓存的情况较难避免,原因有三:

Google采用了一些方法,如下:

其中新的东东是这个预抓取!

2009-12-04 04:24:05 CST

December 02, 2009

Gawain's Jail

记一笔keep-alive和cache-control

以前看的时候大多走马观花,补补课,记一笔吧。

HTTP Keep-alive呢,重点看以下几个文档:
  • http://en.wikipedia.org/wiki/HTTP_persistent_connection
  • http://en.wikipedia.org/wiki/Keepalive
  • Apache的KeepAlive设置与优化 - 老黄纸条箱(这篇是精华)
  • Keep-alive是指在同一个连接中发出和接收多次HTTP请求。优点是:

    在RFC 2617第47页里,一个用户客户端对任何服务器或代理不能维持2个以上的连接。代理可以维持2xN个连接。

    IE6和7使用 2个长连接,IE8使用6个,都是在60秒之后超时。 Firefox的长连接都是在300秒超时,同时使用的连接可以自定义(按每主机或总计),Opera与Firefox类似。

    2009-12-02 02:34:35 CST

    December 01, 2009

    Gawain's Jail

    内存是新的硬盘

    High Scalability有篇基于内存构建云的展望。顺手做一下笔记:

    记的比较乱,大概留个映像吧。要让数据靠CPU足够近。

    Update: 刚才看到了几幅图和这个题很近。

    2009-12-01 06:16:45 CST

    卧底经济学的小记(1)

    2009-12-01 06:09:48 CST

    November 24, 2009

    Limodou

    UliPad 4.0发布

    本来是懒得发布,因为最新更新也不是很多,不过从3.9发布到现在竟然也有1年多了,3.9发布还是在08年4月份,想不到时间过得真快啊。

    Links


    类别:Ulipad 查看评论

    2009-11-24 00:00:00 CST

    November 16, 2009

    Gawain's Jail

    Wave和科技创新

    周末参加了豆瓣举办的Python聚会,直播使用了Google的新产品----Wave,发现Wave真的可以把会议室给解放出来。

    刚刚接触到Wave的人,大多都有些失望,发现Wave和一个即时通信工具没有什么两样,只不过组的成员是不固定的。同样也没有带来想象中的信息爆炸。

    然而在这次聚会直播中,我们发现了Wave的真正实力,与IM相比Wave多出了这样几个功能。

    就上面这些功能就足以让我们不必到会议室进行讨论,而是直接在线上进行交流,快速的讨论,如果讨论过程中觉的某某人也应该参加这个讨论,那么就把他拉进来吧,就是这样,很简单,但是我们有会议记录,可以回放的会议记录。相对于会议室,缺少的可能是一个白板,我相信不久就会有这样的Widget出来。

    科技创新改变生活方式,也改变行业的规则。twitter提供了个人的即时广播电台,YouTube提供网络电视,Wave提供了网络会议室,Facebook提供了网络的社会关系。如果有一天机器人可以送快递,我们就真的可以足不出户的生活了。

    BTW: 当国际科技发展在改变国际友人的生活方式的同时,国内科技的发展同样的在改变国人的生活方式,比如翻墙。鬼子们通过网络(也许是免费的)看YouTube 1080p高清电影的时候,我们还在为自己1M ADSL付费。

    2009-11-16 01:40:32 CST

    November 12, 2009

    Limodou

    Uliweb在stdyun的部署说明

    stdyun是张沈鹏同学做的主机系统,在开始我申请了他提供了免费试用帐号,主要是想试一下Uliweb的部署。在使用过程中由于开始不太熟悉,获得了张教主的帮助,成功将uliwebproject部署成功,目前可以通过 uliweb.com.cn来访问(由于是试用期,因此可能随时关闭)。这个主机系统创建不久,目前已经开始可以租用,速度真是很快,提供ssh,mysql。下面是我将Uliweb的部署写出来,为Uliweb感兴趣的人提供一个部署的实例。所有尝试以svn中的最新版本为准。

    在stdyun上部署Uliweb还算方便。

    准备工作

    因为stdyun不提供缺省的二级域名,因此需要用户自行去注册一个域名并进行绑定。具体如何申请和绑定
    张教主在我的问题中给了很好的回答,可以参考:

    http://groups.google.com/group/python-cn/browse_thread/thread/3b00351b97b6690a/bb28c5ec135d1b7b

    stdyun环境介绍

    一旦你申请帐号成功,stdyun会发送给你有关ssh,mysql的信息,因此你可以使用putty进行登录。具体登录过程不再说明。

    stdyun已经安装好了一些环境,比如virtualenv,setuptools, easy_install。并且它所提供的python是最新的2.6.4。你可以认为在你的目录下已经有了基础的python环境了。

    virtualenv是一个很方便在一个受限环境下创建一个完整的python的工具,由于stdyun已经安装好了因此你不再需要安装。并且它已经被激活,所以激活这步也不用执行了。直接使用就好了。

    bin 目录是存放一些命令,如python,virtualenv的命令行工作,easy_install等。如果你在virtualenv下安装python模块,那么当存在scripts需要安装时,会安装到这个目录下。

    lib 用来放置python下的库。安装的python库源文件就放在这个目录下。

    Uliweb软件安装

    因为你已经有了一个完全由你控制的python 2.6的环境(Uliweb可以运行在2.6下),因此你可以按照Uliweb的安装说明进行操作,完全没有问题。这里我列出通过svn来安装的步骤,这样便于与Uliweb的svn保持同步。

    为了方便,你可以在$HOME目录下创建一个src的目录,然后进入这个目录,再在这个目录下通过svn获得Uliweb的代码,如:

      1 
      2 
      3 
      4 
      5 
    mkdir src
    cd src
    svn checkout http://uliweb.googlecode.com/svn/trunk/ uliweb
    cd uliweb
    python setup.py develop

    这样就安装好Uliweb了。

    创建Hello

    让我们回到$HOME目录下

      1 
      2 
      3 
    
    uliweb makeproject hello
    cd hello
    uliweb makeapp Hello
    这时我们的Demo项目就建好了。

    部署

    stdyun是使用fastcgi模式。而Uliweb已经提供了这么一个文件,就在hello目录下。检查一下runcgi.fcgi它的执行权限是否是x。如果不是请修改:

      1 
    
    chmod +x runcgi.fcgi
    不过这步一般不需要做,在最新的Uliweb代码中,通过svn检出的话应该已经是可执行权限了。

    然后要修改一下runcfi.fcgi,主要是第一行改为:

     1 
    
    #!/home/vhost/s63/bin/python
    这里s63是我的用户编号,你要改成你自已的编号。

    修改完毕,然后就需要在stdyun的控制面板添加url与handler的对应关系。

    绑定

    进入: http://stdyun.com/vhost/my
    在下面的绑定域名处添加你申请的域名,如demo.uliweb.com.cn,然后点击绑定域名。


    输入后进入目录绑定界面。我们想通过 demo.uliweb.com.cn来访问hello项目。配置如下:


    输入后点添加即可。

    然后过一会再访问你的域名。如 http://demo.uliweb.com.cn 你会看到一个Hello, Uliweb的信息显示出来了。


    类别:Uliweb 查看评论

    2009-11-12 00:00:00 CST

    November 05, 2009

    Limodou

    uliweb.com.cn

    感谢张教主的帮助,不过现在还有一点点小问题,因为有些包还没装上。不过首页已经可以访问了。uliweb已经在张教主的stdyun上部署成功了。几点事项:

    域名(我去的9host.cn申请的,. com.cn 只要3块钱,续费要50元)
    有了域名要设置对应的IP(IP为:119.88.56.193),具体见张教主的说明(同时要注意修改9host的DNS时要使用IE浏览器。 )   

    http://groups.google.com/group/python-cn/browse_thread/thread/3b00351b97b6690a#
    然后runcgi.py需要将后缀改为.fcgi。 不过这个我要再更新一下uliweb。

    回头我会写一个更详细的uliweb在stdyun上的部署说明。

    声明,因为我使用的是免费试用,因此可能用的时间不会太长


    类别:Uliweb 查看评论

    2009-11-05 00:00:00 CST

    November 04, 2009

    Limodou

    Uliweb之Cache

    在经过对beaker中的session替换之后,我终于又把cache给替换掉了。完全是自个儿重写。一方面它可以象一个dict一样工作,同时可以通过put, get来调用。使用这两个函数的好处就是可以有更多的参数。目前这个类在uliweb/lib/weto/cache.py中。

    创建cache

    import weto.cache

    cache = weto.cache.Cache()

    这样可以创建一个cache。因为没有指定Cache类的初始化参数,因此会自动以file格式创建cache。如果需要指定其它的格式和参数,可以设置:

    storage_type, options, expiry_time

    这三个参数。storage是类型,目前只支持'file', 'dbm', 'database',分别对应三种不同的存储类型。

    options则根据不同的类型有所区别,比如'file'类型需要传入'data_dir',用以指定cache保存的路径。而'database'则需要指明要使用的表名和数据库连接的一些信息。

    expiry_time是指定缺省的超时时间。这是当你在put时没有指定超时时间时使用的,在调用put时可以单独设定。

    cache.get(key)

    这样的调用,如果key不存在,或超时则会引发一个异常抛出CacheKeyException异常。

    如果不想引发异常,则可以:

    cache.get(key, default)

    其中default不能为None,这样当key不存在,则不会引发异常,返回default值。如果为None,则同get(key)的效果。

    cache.put(key, value, expire=None)

    这样可以设置一个cache。如果expire没有传入则使用缺省的超时时间。时间以秒为单位。

    同时为了方便函数的封装,还提供了

    cache.cache(key=None, expire=None)

    这个decorator。例如:

    @cache.cache(expire=15) 
    def f(*args, **kwargs):

    @cache.cache(key, expire=15) 
    def f(*args, **kwargs):

    这两种都是可以的,区别在于前一个没有key参数,那它将根据函数名和参数来生成一个key,而后面则是由用户主动指定了一个key。

    在进行上进的改造之后,原来的beaker就删除了,这样uliweb中不再使用beaker了。同时对原uliweb.contrib.cache进行了修改,去掉了middle_cache.py,而采用让用户自行调用的机制。同时为了同app的配置功能相结合和方便用户使用,在uliweb/utils/cache.py中编写了一个get_cache()的函数,同时对Cache类的增加了page方法。

    get_cache()可以自动从settings中找到相应的配置。目前主通过两个secion来控制的:

    [CACHE]和[CACHE_STORAGE],前者用来控制cache的类型和超时时间。后者是用来针对每一种类型来设置与存储相关的配置信息。在调用get_cache()时,你还可以传入一些参数,它的原型是:

    def get_cache(cache_setting_name='CACHE', cache_storage_setting_name='CACHE_STORAGE'):

    即可以指明使用其它的配置名,这样就可以在你的settings.ini中存放不同的cache的配置,通过get_cache来使用不同的cache机制。

    为什么要对Cache进行扩展,Cache.cache原来是针对函数本身和它的参数,但是这样对于view有一点问题。有些view可能通过QUERY_STRING来处理不同的传入参数,因此在url上它们不同,但是在view函数上是完全一样,因此这种只根据函数本身和参数是有问题的。因此扩展的page这个decorator使用了request.url值,它是一个完整的访问路径,包括了QUERY_STRING信息。因此为了生成静态cache,可以使用改造后的Cache类。当你调用get_cache()时返回的cache对象已经是使用了这个新的Cache类了。

    所以为了方便和更适合在Uliweb环境下使用,可以使用uliweb.utils.cache的get_cache()函数。例如:

      1 
    2
    3
    4
    5
    6
    from uliweb.utils.cache import get_cache()
    cache = get_cache() #使用缺省的settings.ini设置。因此需要加入uliweb.contrib.cache这个app
    @cache.cache(expire=15)
    @expose('/index')
    def index():
    return {}

    现在我把uliwebproject也添加了cache的支持,不过发现的问题是,不能简单的使用@cache.page()来对view方法进行处理。原因是:

    在view方法中有对cookie的处理,采用cache的方式会屏蔽掉这块处理

    因此在uliwebproject中并不是使用的decorator的方式,而是在view中对可以cache化的部分进行了手工处理,如:

      1 
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    cache = get_cache()
    @cache.cache(_f)
    def f():
    content = file(_f).read()
    if render:
    content = to_html(template(content, env=application.get_view_env()))
    else:
    content = to_html(content)
    return application.template('show_document.html', locals())

    response.write(f())
    return response
    完全是手工来处理了。

    类别:Uliweb 查看评论

    2009-11-04 00:00:00 CST

    November 02, 2009

    Gawain's Jail

    那个洞

    在Practice of System and Network Administration中有一章是Climb out of the Hole。这个洞就是:

    一个家伙掉到了一个深到他不可能出来的洞里。他听到有人来了,然后他引起了那个人的注意。那个路人听完了他的境况,想了一会儿,也跳进了那个洞。

    "你为什么要这样做?现在我们都被困在这儿了。"

    "呃〜至少你现在不再孤独了。"那个路人说。

    2009-11-02 04:21:56 CST

    Limodou

    uliweb性能之优化思考

    性能与功能的确有些矛盾。因为开发uliweb更多是从功能上考虑,因此在性能上我一直没有太仔细测试和分析过。最近找了一个主机部署了一下,别人告诉我刷新一下CPU占用很高,于是我想有机会的确还是要好好搞一下。于是加了一些跟踪,发现了一些问题:

    1. get_apps() 的问题

    因为uliweb是采用app方式来组织,而且app是允许使用python的包形式。在启动时,为了查找app下的settings.ini和config.ini等信息,使用的是pkgresource的resource_filename,它是要导入才可以的。但是一个导入可能会使得加载变慢。这块的确有些问题。为了查找包的目录,因此要导入,但是后面的处理未必会使用这个包,显得有些浪费。因此我修改了这块处理。如果包是由a.b.c这样形式构成的,我会只导入a,后面两个直接根据a的目录生成一个路径。这样只要保证a的__init__.py中没有太多的耗时的代码就可以了。这样优化后减少了一些时间。

    2. pytz的问题

    因为uliweb的ORM会处理时区的问题,而且我写了一个utils/date.py模块专门来处理时区转换。因此在启动时,为了省事,我自动导入了date.py模块,它会自动导入pytz模块。但是我发现pytz的导入非常慢。这样造成整个uliweb的启动变慢。因此这块我直接去掉了,让用户自已来处理好了。

    3. 可能会引起问题的地方。app的__init__.py文件。因为考虑有些bind的代码会写在__init__.py中,因此在启动app时会自动导入所有app。因此如果有app在这里写了很耗时的代码会影响整个uliweb的启动速度。也许你会认为不好,不过uliweb已经提供了许多静态化的方法,如:bind和expose可以静态化,即在settings.ini中进行配置,这样__init__.py中可以是空的。

    这里的问题主要是由于方便性考虑产生的:采用自动化的方式,减少用户的导入或初始化的工作,结果使得处理范围扩大化,影响启动效率。

    注意我说的主要是启动时的效率,一旦启动不退出,效率要好一些。

    因此uliweb如果每次都是重新启动必然会造成性能下降,比较好的方式还是驻留式,不要每次启动。象mod_wsgi, fastcgi应该是可以支持的。是不是自带一个web server采用反向代理会更好呢?象java一样。


    类别:Uliweb 查看评论

    2009-11-02 00:00:00 CST

    October 27, 2009

    HYRY 's Blog

    一个月统计

    用Google Analytics对【用Python做科学计算】的访问量做了一个月的统计,本站和GAE站合计有786位访问者,1300人次访问,4000次页面访问。

    搜索次数最多的关键词是“Python 科学计算"和"scipy"。访问最多的页面是numpy和scipy的介绍。

    目前rst的源文件大小共244.7k字节。11月份的编辑重点将放在matplotlib和scipy的介绍部分。另外预计完成一篇关于在数字信号滤波器的应用的章节。

    2009-10-27 22:41:06 CST

    October 18, 2009

    Limodou

    新的uliwiki项目

    今天开发了一个新的uliwiki的项目,不错是今天,我是照着 http://code.google.com/p/django-wikiapp/ 这个项目来做的,不过象界面,象处理许多都是重写的.因为原来的项目功能挺多,我目前还实现不了,比如:comment, tagging, notification, feed等.目前只是实现了一个基本的wiki的功能,用户认证还没有.但是wiki的功能基本上都全了:

    比如生成WikiWord链接,同时支持象[wiki:name message]的wikiword方式.

    使用reStructuredText格式(目前只支持这一种)

    编辑,删除,版本管理,恢复旧版本,查看旧版本.

    其中版本管理我做得比较简单,我是每次保留全部内容,而不是增量内容,主要是为了方便生成版本差异.而django-wikiapp是保存的增量内容,因此处理上要复杂一些.

    同时通过研究django-wikiapp发现了  http://code.google.com/p/google-diff-match-patch/ 这个好东西,可以用来生成比较后的结果,并且还可以生成patch.

    下面上几张图,看下效果:

    这是进入的首页面.

    这是某个页面的编辑页面

    这是版本信息,先选中不同的版本,然后点上面的Compare按钮可以显示下面的比较结果.

    这是使用了google-diff-match-patch显示的结果,还不错.

    现在uliwiki的地址在  http://code.google.com/p/uliwiki/  有兴趣可以试试和完善它.


    类别:Uliweb 查看评论

    2009-10-18 00:00:00 CST

    对《I have a dream》的回复

    原文: http://j-lite.net/blog/2009/10/17/i-have-a-dream

    以下是我的回复:

    我也不是为了争uliweb要比django强大,我也说了uliweb有自已的优势,而且我认为比django强的,但django也有它的优势。而且我也在邮件列表中多次声明,每个框架代表一种哲学,不同的哲学引来不同的用户,所以我离开django也可以说是我的哲学与django的哲学区别越来越多造成的。

    至于造轮的问题,因此我还是建议你有时间多了解一下uliweb。在今年9.5日的大会上我列举了自已创建的轮子的例子,比如:

    整个核心的处理完全是自已写的,它是一个框架的灵魂,负责整个框架的启动,组件管理(app管理),配置管理,request和response处理,middleware的处理,这是每个框架不同于其它框架的核心,是无法复制的。如果只是简单的复制,那么这个框架存在的意义就没有了。其中整个app的详细支持是借鉴了django的思路,但是由我完善的。还有象view的处理,借鉴了web2py的思路,但是自已实现的。详细代码可以见uliweb.core.SimpleFrame.py.

    还有许多的模块是我自已写的:

    1. web2py的模板,已经被我改造增加了象编译文件目录支持,自定义tag支持,block的支持,这些都是原模板没有了,已经是uliweb化的组件了。
    2.dispatch模块,完全是自已写的,实现类似于django signal的功能。但是整个实现是从ulipad发展来的,没有照搬任何人的东西。
    3.i18n也是自已写的,是从ulipad发展来的。
    4.weto是在我发现beaker这个session库有问题之后重写的,完全是uliweb的东西。
    5.contrib下的所有组件都是我自已写的。
    6.pyini完全是自已构思创造出来的,用于处理uliweb的配置文件。
    7.orm这块是从头一点点构建的,也是一个框架很重要的部分。
    8.form库也是自已一点点写的。
    9.url映射的处理机制是使用了werkzeug的route为基础实现的,但是只是使用了它的基本功能,主要功能是uliweb实现的。

    一个框架主要完成的功能其实不外乎:url处理,request, response,view,orm,组件管理,配置管理,提供一些实用的扩展。因此,你可以看到,从架构设计,从组件的实现,许多方面都有uliweb自已的实现,甚至完全是uliweb自已的实现。因此从web2py,从django,从werkzeug,从sqlalchemy更多不是简单的引用,而是思想的借鉴,是更多的封装。

    所以,许多东西并不是简单了解就好象明白的。这是我想要澄清的地方。别人不认可uliweb没关系,很正常。但是我只是希望,评论一个东西首先要对它多少深入地了解一下,哪怕与作者交流一下也好,而是不看些表面。不仅从合理客观的角度来谈论一件事,更是不会误导别人。所以uliweb绝不是简单地组装出来的一个东西,它有自已特性甚至独一无二的东西。许多人一谈uliweb,就是从重装造轮的角度,但是他们并不了解许多uliweb上的设计的东西,也基本上没有在技术细节上的讨论,谈得很泛泛。这样对谁都不是公平的。

    你所说的框架其实目前pylons和tg都差不多,而uliweb也是可以,django也是可以,只不过pylons和tg可能从组件上可以直接选择,而uliweb,django是可以自已定制开发,不能直接使用。而且你所说的更接近于某些人更偏重于自已去建,比如这篇文章:

    http://pythonpaste.org/webob/do-it-yourself.html

    自已建是没有问题。因此框架更多是给那些不希望,甚至不能够自已来做这件事的人准备的。但是自已建,可能需要了解的东西更多,正如我在构建uliweb过程中,我学到了比以前简单使用框架更多的东西,我甚至做了许多以前不知道自已还可以做的事情。各有各的乐趣。


    类别:Uliweb 查看评论

    2009-10-18 00:00:00 CST

    October 17, 2009

    Limodou

    关于Webob无法支持flash文件上传问题的记录

    根据邮件列表中的回复加工

    1. 提出问题:

    使用webob在处理通过fancyupload上传的文件,发现会系统挂起,但只要先把request.body读出来就没有问题。因此在uliweb中,将原来使用的webob去除,改成了werkzeug来处理了。

    2. qiangninghong的试验:

    因为limodou反应的这个问题认为是cgi模块有bug导致的,所以很重要。今天晚上稍稍有点时间,写了一个测试程序来验证一下,但是发现似乎没有这样的情况?

    测试代码在这里 http://code.google.com/p/hongqnlib/source/browse/

    就是照着 FancyUpload 的 Attach File demo 页面 ( http://digitarald.de/project/fancyupload/3-0/showcase/attach-a-file/ ) 翻译了一下。运行 server.py 后,访问 http://localhost:5002/ ,上传了几个文件,都没有出现挂住的情况啊?

    难道是我的这个测试有什么问题?请 limodou 帮忙看看。

    3. 我的试验:

    我把你的程序改了一下:

      1 
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    import os
    import mimetypes
    import time
    import socket
    from hashlib import md5
    import simplejson as json
    import webob, webob.exc
    from paste.urlparser import StaticURLParser
    from paste.cascade import Cascade
    import logging
    from paste import fileapp


    def app(environ, start_response):
    request = webob.Request(environ)
    if request.method == 'GET':
    filename = request.path_info.lstrip('/')
    response = fileapp.FileApp(filename)
    else:
    # logging.error( repr(request.body))
    filedata = request.POST['Filedata']
    r = dict(status='1', name=filedata.filename)
    text = filedata.file.read()
    r['hash'] = md5(text).hexdigest()
    if request.params.get('response') == 'xml':
    response = webob.Response(content_type='text/xml')
    print >>response.body_file, '<response>'
    for k, v in r.items():
    print >>response.body_file, \
    "<%(k)s><![CDATA[%(v)s]]></%(k)s>" % locals()
    print >>response.body_file, '</response>'
    else:
    response = webob.Response(content_type='application/json',
    body=json.dumps(r))
    return response(environ, start_response)

    #static_app = StaticURLParser('/static')
    #app = Cascade([static_app, app], (404, 405))

    if __name__ == '__main__':
    from wsgiref.simple_server import make_server
    server = make_server('localhost', 5002, app)
    server.serve_forever()
    分析如下:

    1. 使用你的程序的确没有问题,使用我改的程序就有问题
    2. 两个差别在于你在调用处理前使用了一个StaticURLParser,而我改后的程序是不使用它,在app中自已处理。这样的目的并不是使用StaticURLParser不行,而是这样的做法其实是在app处理前多了一层处理,那么这层处理会影响后面的处理,掩盖问题的发生。
    3. 在我的例子中,只要把#        logging.error( repr(request.body))这行的注释去了,程序就正常。
    4. 原因就是,只要你想办法在执行request.POST['Filedata']之前执行了象request.body之类的代码,这样会引起对body的整个读取,这时读取是按Content-Length来处理的,并且会放在缓冲区中,因此后续的处理就是从这个缓冲区来的了,因此缓冲区最后有没有回车换行是没有关系的。而使用flash控件上传文件,文件体示例如下:

    ERROR:root:'------------ei4GI3ei4Ef1Ef1GI3ei4Ij5GI3GI3\r\nContent-Disposition:
    form-data; name="Filename"\r\n\r\na.c\r\n------------ei4GI3ei4Ef1Ef1GI3ei4Ij5GI3G
    I3\r\nContent-Disposition: form-data; name="Filedata";
    filename="a.c"\r\nContent-Type:
    application/octet-stream\r\n\r\n#include
    <stdio.h>\r\n\r\n------------ei4
    GI3ei4Ef1Ef1GI3ei4Ij5GI3GI3\r\nContent-Disposition: form-data;
    name="Upload"\r\n\r\nSubmit
    Query\r\n------------ei4GI3ei4Ef1Ef1GI3ei4Ij5GI3GI3--'

    可以看到,最后是没有\r\n的。这不是浏览器的问题,是flash组件本身的问题,相关的flash的类本身的问题。但其实也不算一个问题,因为cgi的处理是基于multipart的boundary需要以\r\n结束这个假设,但这种假设在某些情况下有问题。相应的cgi的代码如下(从cgi.py中找到的703行):

      1 
    2
    3
    4
    5
    6
    7
    8
    def read_lines_to_eof(self):
    """Internal: read lines until EOF."""
    while 1:
    line = self.fp.readline(1<<16)
    if not line:
    self.done = -1
    break
    self.__write(line)
    就是这个readline造成的,如果没有\r\n它根本不会返回。

    因此前面的问题就是,如果不先执行request.body,则后面的request.POST['Filedata']会从流中进行读取,而不是缓冲区中,但flash正好又不返回\r\n,因此server在使用readline就停处了,造成挂起,进而引发浏览器的flash控件的超时。

    而你的例子使用了StaticURLParser,可能会进行request.body的读取,所以掩盖了这个问题。当然它到底怎么做的,我没有继续深究。因此我改了你的程序,就是为了不让其它的因素干扰,问题就暴露出来了。

    类别:Uliweb 查看评论

    2009-10-17 00:00:00 CST

    October 15, 2009

    Gawain's Jail

    又一次败家Canon 450D

    终于入手了本人的第一台数码单反机。败家记录如下:

    总计¥4830(免了¥10,真少啊,T_T)。

    半年前,岳父给我了一个美能达X300的单反和一个VR神镜,胶片机的,还有一些很老很老的设备,比如红梅的机器。可惜,到现在一次也没有用过。

    2009-10-15 09:13:47 CST

    Limodou

    Uliweb中ORM的新变化

    最近一段时间一直在忙着ORM的优化,其中有几点:

    1.增加__table_args__的配置。它和__tablename__一样是定义在Model类中的,可以用来增加在执行Table时添加新的一些参数,如果你使用mysql,可以如下定义:

      1 
    2
    class Todo(Model):
    __table_args__ = dict( mysql_engine='InnoDB', mysql_charset='utf8')
    这样将在建表时,向Table中添加这样的信息。上面的代码将以InnoDB和utf8编码来创建todo表。

    2.增加OnInit类方法的调用。这个方法是交给用户来定义的,可以用来执行在执行完Table之后的一些初始化操作,它将在建表之前。比如:

      1 
    2
    3
    @classmethod
    def OnInit(cls):
    Index('my_indx', cls.c.title, cls.c.owner, unique=True)
    这样将创建以title, owner的索引,并且索引不允许重复。目前只支持索引的创建。对于简单的索引,是可以在定义Field时直接设置index=True来创建的,并不需要这种方式。这种方式可以认为就是为了创建多个字段的联合索引。

    3.考虑缺省order_by的实现。但是想用户完全可以在Model中定义相应的方法来实现,类似于django的Manager的东西,比如:

      1 
    2
    3
    4
    class Todo(Model):
    @classmethod
    def files(cls):
    return cls.all().order_by(cls.c.title.desc())
    然后在使用时可以通过Todo.files()来返回按title降序排列的结果。所以使用这种方法倒是不一定要支持缺省order_by的配置项。不过有这个配置项的好处就是当需要实现通用的查询时,它可以自动生效。不过也可以考虑把结果集当成一个参数传给配置功能,总之还是有方法。

    4.最重要的一个特性。增加表的注册,并且注册的表不一定是一个Model,而是一个模块名的形式,如以下两种都是正确的注册方式:

      1 
    2
    set_model(Model, tablename='user')
    set_model('uliweb.contrib.auth.models.User', tablename='user')
    这样注册之后,你就可以通过使用真正的表名来得到对应的Model类对象了。通过:

      1 
    User = get_model('user')
    这种方式首先可以使用在关系的定义中,如:

      1 
    2
    Reference('user')
    ManyToMany('user')
    对于关系定义,它可以根据表名自动导入对应的Model。而对于在程序中想直接使用某个Model,则可以通过get_model()来获取。

    但是这种get_model()的方式要依赖于set_model()的注册过程。注册有几种方式:

    1. 在导入Model文件时,随着Model的class的创建自动会调用set_model()来注册,因此只要正常导入相应的模块,就可以使用get_model()方法了。这种情况下,对通常的开发没有什么影响,用户甚至不必使用这种方式。

    2. 由框架来注入。也就是uliweb来实现。这种方式首先要求将Model信息写入到settings.ini中,比如:

      1 
    2
    [MODELS]
    user = 'uliweb.contrib.auth.models.User'
    这样当uliweb在启动时可以自动采集注册信息。然后在orm这个app的__init__.py中执行真正的注册工作,通过调用set_model()方法。这时,注册的就不是Model了,而是字符串形式的信息。

    然后在某个地方,如views.py中使用get_model()或导入models.py时,就可以自动生效了。

    你可能会问,这有什么用呢?一般不都是直接导入来使用吗?完全够用了。

    的确,一般的程序的确是根本用不上。但是Uliweb是一个框架,它要考虑代码的复用和替换的问题。考虑一下,在你的models.py可能需要对User表进行处理,那么你会去导入它。但是有可能这个User表不适合你的应用了,你怎么办?并且这个User表不是你自已写的,而是框架或别人提供的,别人可能还要依赖于这个表。因此简单的直接修改并不一定是好办法,因为它可能不受你的控制。重新写一个可能是好办法,但是因为直接导入的原因,比如你是通过:
    from uliweb.contrib.auth.models import User
    这可是直接hard code了,所以重新写必然还要修改导入的代码。那么要解决这个问题,可以采用的一种办法就是将外部依赖的内容配置化,和java中的注入依赖有些象。也就是我在定义自已的东西时,可能需要外部的东西,那么我并不直接导入,而是通过一种获取的方式来得到。这样工并不需要知道外部的组件的实际位置,这件事由框架来完成。因此uliweb现在实现的get_model就是为了实现这一配置化而设计的。

    其实现在,象bind,expose都已经是可以配置化的了,都可以放在settings.ini中。如果完全配置化,将可以减少启动时的导入处理,并且替换会很容易。
    类别:Uliweb 查看评论

    2009-10-15 00:00:00 CST

    pyini.py的新功能

    pyini.py是处理Uliweb的settings.ini的模块,今天向它添加了三个新的方法:set_var, get_var, del_var,它们的特点集中在第一个参数,它可以是'/'分开的字符串,如:'DEFAULT/flag',它相当于:

    s = ini.add('DEFAULT') 
    s['flag'] = True 

    注意,它只支持一层的'/'切分,如:'DEFAULT/flag/name'相当于 'DEFAULT'和'flag/name'。


    类别:Uliweb 查看评论

    2009-10-15 00:00:00 CST

    关于《谈谈我对Uliweb的看法》的回复

    原文:  http://j-lite.net/blog/2009/09/28/lets-talk-about-uliweb

    回复如下:

    首先感谢对Uliweb的批评。 以下要澄清:

    1. 其实所有的框架都是大杂烩,django也不利外。而且别人做好的东西,如果没有什么问题,拿过来用当然就可以了。特别象是框架,它更多的是提供管理模式和一些工具,本身就是个大杂烩。

    2. 大一统是不可能的,正如世界上有那么多可以统一的东西为什么没有统一,如:语言,操作系统,数据库?因为多样性才是这个世界的特点。更因为人们总有不同的思念和需求。

    3. 为什么不向django和web2py做贡献?贡献我的确做过,如果你去查django和web2py相关的内容,应该还有我的名字。但是有些东西是不可调合的,比如对设计的一些看法,对一些组件的使用,这些东西我建议过,他们并不接受。所以只有以自已的方式来构造。

    重新构造也是一个很痛苦的过程,但是可以让造出来的东西完全按自已的意愿发展,也是一种不错的选择。更有一些人不建议使用框架,而是使用组件,为什么,就是不想被框架所束缚。

    而你所说的插件系统,就是uliweb实现的一个目标。uliweb的app的功能要比django还要彻底。如果有兴趣欢迎与我交流。


    类别:Uliweb 查看评论

    2009-10-15 00:00:00 CST

    October 13, 2009

    Gawain's Jail

    老婆怀孕了

    十一检查出老婆怀孕了,我要做爸爸了。开出一个新的blog-白宝宝诞生记,用来记录孩子从孕育的过程。我很激动!

    2009-10-13 06:26:10 CST

    增加iSync的同步间隔

    iSync的同步服务与MobileMe(以前的.Mac)绑的很紧,许多的设置都要在MobileMe的设置面板里设置。还好苹果提供了AppleScript来做这样的事情。

    tell application "iSync"
        synchronize
        repeat while syncing is true
        end repeat
        quit
    end tell
    
    用AppleScript Editor保存成一个文件,在Terminal.app里用osascript直接执行即可。再放在crontab里就可以设置多长时间来同步一下了。如果有MobileMe 账号,iSync的同步间隔是1分钟。

    参考

    2009-10-13 03:11:11 CST

    繁体还是简体

    昨天看了Cathayan写的繁体的优越感一文,又和几个朋友聊了会儿,恰好又看到了Snow Leopard的新中文字體一文。其实cathayan的那篇是针对这一篇的一个义气回应文。

    事情的起因是因为Apple在Snow Leopard里把繁体中文界面的字体换成了Heiti TC,然后呢,很多台湾使用者都不适应了。纷纷站出来说苹果和这个字体的不是,比如zonble的这一篇和前面的那一篇。

    就Heiti TC这个字体来说呢,阿杰的Snow Leopard的新中文字體一文说的比较中肯。另外也参看一下他所写的是誰寫錯字(-)(二)(三)(四)

    要说这简繁之争嘛,以前我是倾向于繁体的,甚至觉的简体的某些字失去了一些文化内涵。比如这个听和聽。但是多了解了一些汉字简化的历史之后,又觉的对于常用字的简化是很有必要的。简化字历来就有,在行书、草书里很常见,只不过咱政府效法秦始皇,把这事给硬推下来了。当然了过尤不及,第二批简化字试了一段时间,发现太简单了,也太乱了,于是乎下马了。虽然下马了,但是这批简化字的影响还是不小的,小时候在街上看到许多字不认识,我爸告诉我这个字是算术的算,这个字是镶牙的镶,现在记不得这些字是怎么个写法了,但是说明一个问题就是第二批简化字影响了一大批人。就目前的第一批简化字来说,还是相当有进步意义的。比如郁闷的郁,繁体要写成"鬱"。别说是写了,看着就觉的够麻烦的。还有一个比较有争议的简化字就是"发",繁体是"發財"的"發"还是"頭髪"的"髪"。简化字施行到现在有没有因为发的二义性影响了语义的理解?没有嘛,因为我们有上下文,而且我们已经白话了,单字词已经被大量的多字词代替了。

    结合前些日子那44个字的字型变化来说,如果有时光机,把那些学院派送回甲古文、金文那个时代是比较合适的,要不他们就在自己的圈子里搞点小爱好,写几篇论文,聊以解闷完事。

    最后再赞一赞阿杰和cathayan在国学上的造诣,真是我等学习的楷模。

    2009-10-13 03:09:19 CST

    升级到Snow Leopard

    昨天升级到了Snow Leopard 10A432版,这一版竟然给我的硬盘节省出了近8个G的空间。据说这一版是全64位系统,但是uname的时候并不是x86_64。看了Note,要使用64位系统就要在启动的时候按住6和4,这样开机就是64位了。还有另一个办法是使用nvrom(8)来改NVRAM的变量。

    % sudo nvram boot-args="set arch=x86_64"
    
    要回到32位,可以在启动的时候按3和2,也可以把boot-args给删了。
    % sudo nvram -d boot-args
    
    开机之后在System Profiler里看软件部分,除添加打印机X11之类的不是64位,其他大量的苹果程序都已经是64位了。

    这一次中文输入法真的是有大提升,太赞了。拼音输入太好了,五笔的改进好象没有。加入了手写输入,识别率还是挺高的,就是刚一开始很不适应。 输入法的切换上,恢复了Tiger时期一个功能----不同程序使用不同的输入法,Command+空格的切换也变的稍复杂了一些,以前只是在当前输入法和前一个输入法之间进行切换,现在变成了可对所有输入法进行切换方法,这样的话与Command+Option+空格的作用就有相似之处了(不一样),混乱!Snow Leopard就是要让你出手利索,拖泥带水的必然会换错输入法。

    人类使用的东西无非都是为了提高效率,区别在于是优雅的提高还是恶心人提高。Snow Leopard确实比Leopard更轻更快!

    2009-10-13 02:53:55 CST

    翻译了一本书中关于MacBook电池问题的解决方法

    在公司班车上闲来无事就翻了翻《Mac OS X Snow Leopard - Pocket Guide》这本书。看了其中关于电池问题的一些解决方法就随手翻译出来了。下面是译文:

    MacBook的用户要面对一个桌面电脑用户所没有的问题,电池。大多数苹果电池的目标就是在300次完全充电周期之后还能提供原电池容量80%的电力(新的MacBook上是1000次)。想查看你的电池情况,第一个地方就是System Profiler->Power。

    System Profiler的电源部分可以看到电池在完全充电后的电池容量、充电的次数以及电池的情况。如果充电次数已经接近电池的最大利用率,那也就是考虑更换新的电池的时候了。如果你的充电次数很少,但是电池还是早泄,你可以试试下面这些招数:

    校正电池
    在每个MacBook电池内部都有一个微控制器来告诉电脑还有多长将耗尽电力。有时,这个估算值可能和实际的性能偏差许多。要使电脑和这个控制器趋于一致,你需要重新校正电池。可以这样做,充满电池并且保持接通电源2个小时以上,然后拨掉电源。当出现电池电力严重不足的警告时,保存好你的文档,直到电脑进入睡眠状态,并且持续五个小时以上。再次接上电源充满电池。这时电池的指示器已经重新校正成功了。
    重置SMC
    SMC是系统管理控制器(System Management Controller)的缩写。这个芯片会对硬盘的减速、睡眠、唤醒和背光做出相应的反应。一个失灵的SMC可以阻止电池充电。要重置SMC(在关机之后),须先取下电池并且不接电源。按下电源键5秒。重新安回电池并且接通电源,启动电脑。这一招对于内置电池的MacBooks是无效的。对于这样的型号,关机并接通电源,然后按下左边的Shift-Control-Option和电源键5秒,重新启动电脑。

    如果上述的招术都不能恢复你的电池,差不多就该去当地的苹果专卖店或授权维修中心的时候了。如果你的电脑在保,并且你的充电次数很低。苹果也许会更改你的电池。如果你的充电次数超过了推荐值(300次,新型号是1000次),而且性能的哀减是在意料之中,你也许需要更换电池或者与低电池容量继续生活。

    注意:就像许多电脑制造商,苹果也有部分电池被召回。一些情况下是危险的电池被召回,另外的情况下是因为性能原因被召回。不管怎样,你都应该查查看你的电池是否在召回的范围之内。通常来说,苹果会为你更换电池,既便电脑已经过保。

    2009-10-13 02:53:54 CST

    Limodou

    Uliweb被Softpedia Team收录

    原文如下:

    Congratulations,

    Uliweb, one of your products, has been added to Softpedia's database of
    software programs for Linux. It is featured with a description text,
    screenshots, download links and technical details on this page:
    http://linux.softpedia.com/get/Internet/HTTP-WWW-/Uliweb-51417.shtml

    The description text was created by our editors, using sources such as
    text from your product's homepage, information from its help system, the
    PAD file (if available) and the editor's own opinions on the program
    itself.



    If you feel that having your product listed on Softpedia is not a benefit
    for you or simply need something changed or updated, please contact us via
    email at webmaster@softpedia.com and we will work with you to fix any
    problem you may have found with the product's listing.

    --
    Sincerely,
    The Softpedia Team

    不知道算不算好事。不过Uliweb还有许多需要完善的地方,我才把0.0.1a2发布上去,过会就收到了。


    类别:Uliweb 查看评论

    2009-10-13 00:00:00 CST

    Adding new feature about the URL suffix definition of an App

    You know, Uliweb uses expose() to define url, for example:

    @expose('/index')

    But when you want to change the url suffix for an App, you should edit every views file(of cause you can use static urls.py appoach, but I won't talk about it now). Fox example how to change '/wiki/*' to '/mywiki/*'? So I add this new feature to Uliweb, so that you can define the URL suffix for an App, just adding an entry to URL session, for example:

    [URL]
    wiki = '/wiki'

    If you didn't define an entry for wiki app, all urls in wiki app won't be changed at all. But if you define wiki='/wiki' in settings, all urls in wiki app will be added '/wiki' automatically, even though the urls is started with '/'. So '/index' will be converted to '/wiki/index'. So for above problem, you can just change wiki='/mywiki'.

    And the entry key will be the appname, it's the same with app module name, for example: uliweb.contrib.admin, etc.

    So in this feature, you can easily change an app URL suffix.

    类别:Uliweb 查看评论

    2009-10-13 00:00:00 CST

    如何在你的程序中象easy_install一样来安装模块

    在Uliweb中,用户可以自已编写App,而且这些App可能依赖于其它的模块,因此如何安装是一个问题。因此我想到利用easy_install来实现这一目的。其实很简单,找到easy_install程序,打开看一下它的实现:

    import sys

    from pkg_resources import load_entry_point
    sys.exit(
    load_entry_point('setuptools==0.6c9', 'console_scripts', 'easy_install')()
    )

    于是改造一下:

    def install(packages):
    from pkg_resources import load_entry_point

    load = load_entry_point('setuptools', 'console_scripts', 'easy_install')
    load(packages)

    可以用了。

    使用时:

    load(['SQLAlchemy'])

    就可以安装SQLAlchemy了。
    类别:Uliweb 查看评论

    2009-10-13 00:00:00 CST

    Python Web框架代码“秀”第4题已出

    http://wiki.woodpecker.org.cn/moin/FrameworksShow

    第4题:CSS文件引用 静态文件的使用,在模板文件中引入default.css(内容见下面说明)

    default.css

    h1 {border:1px solid #f00;}

    类别:Uliweb 查看评论

    2009-10-13 00:00:00 CST

    SQLAlchemy 0.6快要出来了

    变化真是不少,不过大部分都是代码的重构,因为用得少,所以体会并不深。不过至少我看到DDL可以象其它的表达式对象被打印了。比如:

    from sqlalchemy.schema import CreateTable

    create = CreateTable(mytable)

    # dumps the CREATE TABLE as a string
    print create

    这是我非常想要的。这样就可以象django一样,打印出建表的SQL语句了。现在的Uliweb用的方法有问题。考虑着什么时候换成0.6啊。

    http://www.sqlalchemy.org/trac/wiki/06Migration


    类别:Uliweb 查看评论

    2009-10-13 00:00:00 CST

    October 12, 2009

    Limodou

    Python Web Framework代码“秀”第三题已出

     ubunoon 出的,已经完成pylons和uliweb的实现。响应者不多啊。

    http://wiki.woodpecker.org.cn/moin/FrameworksShow


    类别:Python 查看评论

    2009-10-12 00:00:00 CST

    October 02, 2009

    Limodou

    使用setuptools上传Uliweb版本

    新版本发布了,是使用setuptools发布的。有几个注意的:

    1. 要首先使用setup.py register先认证或注册新用户。那么在windows下要注意,在命令行要设置一下:

    set HOME=directory

    这个目录随便,我就设成'.'了,表示当前目录。这样,在执行完register之后就会提示你将登录信息保存在这个目录下,后面在上传时会使用保存的认证信息。

    2. 上传时使用 setup.py sdist upload

    3. 我发现如果在code.google.com/p也上传了,在下载时会先从这里下载,很有意思

    4. 测试你的安装吧。如果有问题,你可以先登录pypi,然后将相应的包删除,再上传就可以了。但是如果在code.google.com/p还有版本,要把它也删除,好在现在是可以删除了。因此建议你安装测试成功后再上传到google去。
    类别:Uliweb 查看评论

    2009-10-02 00:00:00 CST

    Uliweb 0.0.1a1 is released!

    Though Uliweb is not so good for me, but I think I should release, because this is a attempt, and I'll make uliweb better and better. So from now on, you can install Uliweb via: easy_install Uliweb But not need to use svn to checkout. Of cause I suggest that you use svn, because you can keep with the newest features of uliweb.
    类别:Uliweb 查看评论

    2009-10-02 00:00:00 CST

    October 01, 2009

    Limodou

    Uliweb又在大规模调整中

    今天对Uliweb改动了不少,主要有:

    1. ORM进行了优化,利用了SQLAlchemy的Generative功能,可以实现象Model.filter().filter().order_by()等,多子句的形式,同时对一对多和多对多的结果也进行优化。希望文档尽快出来。

    2. Uliweb的结构大调整。主要是将一些对象放在uliweb/__init__.py中了。这样可以直接从uliweb进行导入,而不是uliweb.core.SimpleFrame进行导入,更加方便记忆和使用。

    3. 文档又有一部分进行了修改和完善。

    目前测试基本还正常。


    类别:Uliweb 查看评论

    2009-10-01 00:00:00 CST

    September 30, 2009

    Limodou

    打印出象Linux的tree命令一样的目录树结构程序

    差点找不找了,贴出来。不过已经集成到Ulipad中了。



    在目录浏览窗口中的Print Directory Tree就是。执行后会将当前选中的目录结构输出到messages窗口。

    代码如下:

      1 
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    #! /usr/bin/env python
    #coding=utf-8
    import os, sys
    import fnmatch

    space = 4
    stack = {0:''}

    def path_line(level, path, is_dir, is_last):
    s = []
    leading = stack[level-1]

    if is_dir:
    path = path+'/'

    if is_last:
    s.append(leading + '`-- ' + path)
    leading += ' '
    else:
    s.append(leading + '|-- ' + path)
    leading += '| '

    stack[level] = leading

    return ''.join(s)

    def walk(dir, level=1, skips=['.svn', '*.pyc']):
    r = []
    dirs = [x for x in os.listdir(dir) if not any([fnmatch.fnmatch(x, y) for y in skips])]
    num = len(dirs)
    for i, path in enumerate(sorted(dirs, cmp=lambda x,y: cmp(x.lower(), y.lower()))):
    p = os.path.join(dir, path)
    flag = os.path.isdir(p)
    r.append(path_line(level, path, flag, i==num-1))
    if flag:
    r.extend(walk(p, level+1, skips=skips))

    return r

    if __name__ == '__main__':
    if len(sys.argv) == 1:
    path = '.'
    else:
    path = sys.argv[1]
    print path
    print '\n'.join(walk(path))

    类别:Ulipad 查看评论

    2009-09-30 00:00:00 CST

    发起了Python Web Framework”秀“活动

    目前是在啄木鸟的wiki上创建的页面,具体内容参见: 

    http://wiki.woodpecker.org.cn/moin/FrameworksShow

    欢迎大家来展示和出题,不过出题及回答请看相应的说明。


    类别:Python 查看评论

    2009-09-30 00:00:00 CST

    Doto demo site is launched

    This is a demo site, very thanks to 头太晕, it's he provide me this site,
    and the domain will be changed to doto.letme.cn later. Now you can
    test it. And there is also a bug or shortcoming which I've not fixed
    yet, that's when you register an account, you shuld click settings
    first, this will let doto create UserProfile info. I'll fix it later.

    So hope you have fun. And Doto support share todos to others, so if
    you have friends, you can tell him to sign up an account also, and
    share some todos with him.

    Again, thanks, 头太晕,and this is only a demo site, so don't put
    important things on it.

    Visit: http://www.letme.cn
    Later should be: http://doto.letme.cn

    类别:Uliweb 查看评论

    2009-09-30 00:00:00 CST

    September 29, 2009

    Limodou

    编译Python 2.5.4带zlib

    因为找了一台Linux机器,想在项目中使用。它上面是红帽,Python还是2.3.4,这怎么能行呢?换。2.6?还是2.5吧,因为平时都是在2.5下测试的。但是编译后,执行setuptools 0.6C9有问题,说是zlib找不到。怎么回事?搜索了半天,原来要带--with-zlib=<zlib/include>的参数在执行./configure。得,还要先下载zlib。于是来到了zlib主页,下载,编译,安装。再到Python的目录,clean之,编译,安装,还是不行。怎么回事?再查,原来zlib要使用共享方式编译,晕,再clean,重新对./configure --shared,再整。终于OK了。


    类别:Uliweb 查看评论

    2009-09-29 00:00:00 CST

    September 28, 2009

    Limodou

    试验了一下werkzeug的上传长度设置

    在新版本werkzeug中,处理request时可以有两个属性可以设置,一个是max_content_length,它是用来控制最大的上传内容的。还有一个是max_form_memory_size,它是用来控制读取内容所占内存的最大值。好象没看到如何控制文件大小。不过一般控制比较近似就可以了,因此可以就使用max_content_length就可以了。

    想试验也比较简单:

    from werkzeug import Request

    Request.max_content_length = 1 * 1024 * 1024

    上面的代码就是限定了最大的content大小。当然如果你使用Uliweb,你也可以导入:

    from uliweb.core.SimpleFrame import Request

    后面一样就可以了。

    当上传内容超出大小时,它会抛出一个异常,返回码是413的。

    在Doto中,我使用了FancyUpload来处理,它也可以限定上传的大小,比werkzeug要精确多了。所以可以前后端相结合。

    werkzeug的文档参见: http://werkzeug.pocoo.org/documentation/0.5.1/wrappers.html


    类别:Uliweb 查看评论

    2009-09-28 00:00:00 CST

    September 26, 2009

    Limodou

    Doto增加了到期时间和过滤功能

    今天把Doto又完善了一下,主要是增加了到期时间和过滤功能。界面如下:

    左上角有一个Filter,鼠标移上去就可以弹出一个窗口,有两个过滤链接,点击会刷新Todo的列表。它是使用了一个叫SmartHoverBox( http://www.consideropen.com/demos/smart-hover-box/ )的插件,这个插件是由那个《30天学习Mootools教程》的网站提供的。具体样式是我自已弄的,凑和就行。这个插件没有做成一个app,因为比较简单,就直接放在了doto app下面。对装载todo增加了对条件的处理。

    到期时间在上面可以看到在右侧有一个时间显示。采用的是timesince的显示方式。这个timesince是从django搬过来的,不过它的代码只支持现在时间大于指定时间的处理,而todo既要能显示时间过去了,也要显示还没到的时间,所以改了一点。同时在返回的结果根据是过去还是没到会显示ago或later。同时在显示时,如时过期了,时间会显示为红色。

    下面是详细的修改界面:

    在到期日字段,左侧是timesince的显示,右侧可以直接修改,自动保存。我使用了一个mootools datepicker的插件(http://www.monkeyphysics.com/mootools/script/2/datepicker )。并且在Doto中做成了一个datepicker的app。这样使用时只要选中这个app,然后在模板中使用{{use "datepicker"}}就基本可用了。当然要写具体的创建,配置参数,特别是要处理onSelect的事件,与后台进行交互。


    类别:Uliweb 查看评论

    2009-09-26 00:00:00 CST

    SpeedyFox加速Firefox启动

    现在我基本上都是使用chrome,除了调试web时。其中有一个很主要的原因就是:firefox启动太慢了。看到speedyfox后,安装了一下,清除时间很长,但是等执行完毕后,的确,启动飞快,果然管用。看到有些评论说不明显,我不清楚,但是我感觉的确管用。

    http://www.crystalidea.com/speedyfox


    类别:心情故事 查看评论

    2009-09-26 00:00:00 CST

    September 22, 2009

    80's Blog

    如何获取Flex/Actionscript3海报

    没错, 这就是网上流行的PDF版本的那套海报,AS3一张/Flex四张, 我听说很多funs都自己掏钱打印出来的, 很多小店没法打印这么大的海报, 为了方便广大开发者, 我们特意从Adobe拿了一些来送给大家.

    as3/flex poster

    我们有以下三种途径来获取海报:

    1.RIAMeeting聚会活动,海报将作为奖品,你来参加就有机会得到哦

    2.积极参与RIAMeeting社区线上活动(参与翻译,写文章等等),我们将赠与海报以及其它的纪念品,奖励为社区做出突出贡献的人,如果你有兴趣加入社区线上活动,包括翻译等等,请在这里回帖,注明自己的联系方式(邮箱),以及对自己的一段简单介绍,我们将与您取得联系

    3.可以以企业为单位申请海报,但企业必须具备一定的资质,且我们有数量限制,每周针对企业的海报发送不超过5个。如果您是企业形式申请,请在这里回帖,说明您的企业名称,网址,业务范围,所用技术,开发人员规模,邮箱等等,一旦符合条件,我们将与您邮件联系,获得您的地址然后通过快递发送海报(收货方付款,您那边需要在快递到达后支付海报的运输费用)

    相关日志

    2009-09-22 11:55:43 CST

    Limodou

    有趣的Nutz

    碰巧看到有国人开发的Nutz项目,它是一个Java的项目,不好说它是什么,但是它实现了SSH相似的功能,至少作者是这样说的。SSH是什么,原本我也不清楚,后来才明白是Structs, Spring, Hibernate的缩写,我晕。作者很有激情,感觉比我强,但是对代码要求很高,其中作者的原话:

    Nutz 会是一个长期的项目,因为首先,我运营它的官网不需要什么开销,其次我个人最大的爱好就是写程序,最后,我虽然欢迎,但是并不假设有人会加入这个项目和我一起写代码。并且我向毛主席保证,我很欢迎大家通过 Email 等方式告诉我我代码哪里是需要修改的,但是要想拿到我的 svn commit 权限,除非你能证明你代码写的不比我差,否则门儿都没有。

    看来我也要象他一样,想加入Uliweb,要拿出你的证明,至少代码不比我差,否则门儿都没有。

    项目主页:  http://code.google.com/p/nutz/


    类别:心情故事 查看评论

    2009-09-22 00:00:00 CST

    September 20, 2009

    Limodou

    向mootools贡献失败了:P

    随着对mootools的使用越来越多,也对它越来越了解,也越来越喜欢。昨天看到了关于类的一些内容。于是乎我想到以前为jQuery写的一些字符串的扩展,其中有一个template的功能,它可以在字符串中定义如${0}, ${name}之类的东西,然后提供array或hash值进行替换。于是我将其贡献到mootools邮件列表。但是很不幸,人家告诉我在mootools中已经有了,叫substitute。当初我怎么就没看到呢?

    最终我总结是因为mootools中的许多方法的命名比较怪。比如Python中叫format,让人很明白是格式化用的。我叫template,可以很容易理解为模板。而substitute字面上是替换,不好理解。

    还有其它的命名,比如:inject,是注入的意思,在mootools中是将当前元素加到目标元素中去。grab是捕获的意思,是将其它元素添到到当前元素中。还有象adopt, erase, dispose等,并不是很容易理解的命名,可能会让别人学习起不是很容易。

    不过mootools有我所想到的非常不错,让人满意。

    我还需要再仔细阅读文档,刚才发现一个mootools文档的一个小bug,已经报告了。


    类别:Mootools 查看评论

    2009-09-20 00:00:00 CST

    September 19, 2009

    Limodou

    Code Snippets in UliPad[视频介绍]

    http://vimeo.com/6652566

    感觉Code Snippets功能应该对大家有用,就介绍一下。有兴趣可以看看。



    类别:Ulipad 查看评论

    2009-09-19 00:00:00 CST

    September 18, 2009

    Limodou

    Doto -- Uliweb application[视频介绍]

    关于Doto(http://code.google.com/p/doto)只是最新状态的一个介绍,还有许多的问题和功能需要解决。

    http://www.vimeo.com/6626434

    类别:Uliweb 查看评论

    2009-09-18 00:00:00 CST

    September 15, 2009

    Qing Feng

    Crack Apple TV

    这几天拿到 @ai829 的 Apple TV,尝试破解了一下,记录一下这次的破解历程

    2009-09-15 07:00:00 CST

    September 14, 2009

    80's Blog

    Away3D Lite is out

    Away3D lite不是Away3D的替代, 它是一款更加精简更加灵活的3D引擎, 如果你不要大量的3D交互和运算, 那你用它足够了.

    下面是它的主要特性:

  • 3DS, MD2, Collada & Metasequoia loaders
  • Bones animation
  • Viewport clipping
  • 3d mouse events
  • All standard primitive types
  • All standard camera types
  • All standard material types
  • Template classes for quick and easy setup
  • 下面是demo的screenshot, 你可以从svn上fetch, 或者你可以在这里downloads.  flex/fdt/flashdevelop and cs4 formats.

    很抱歉,答应大家在RIAMeeting上讲一下Flash 3D方面的presentation, 到现在一直没有成型, 主要是本人实在太忙, 工作和这块又不是太相关, 所以一直没时间搞. 请各位见谅. 另外这个周末19日的RIAMeeting, 是Adobe China 和 Rumania的两位Evangelist给大家分享Flash/Flex以及FMS的技术演讲, 有兴趣的请在这里报名.

    MD2Cubic

    ExSphereSpeedTest

    ExMQO

    相关日志

    2009-09-14 08:14:56 CST

    Limodou

    如何通过一段HTML代码生成DOM对象(mootools)

    这是我在开发Doto中遇到的一个问题。因为我不想直接在HTML中生成DOM对象,所以我的想法是在后台生成好HTML代码片段,然后在前端生成真正的 DOM,再加入到合适的位置上去。但是我查阅了mootools中的Element的文档,没有找到如何从一个HTML代码片段直接生成DOM元素的方法。在jQuery中是可以通过$(html)来直接生成。那么我在mootools中提出了这个问题,并且给出了我的一个解决方法,比如:

    var create_dom = function(html){
    var empty = new Element('div');
    empty.set('html', response.html);
    var el = empty.getFirst().clone();
    empty.dispose();
    return el;
    }

    不过在实际使用中发现,clone()有问题,因为id的信息丢了。于是乎去掉了clone的处理。放在邮件列表中得到了 Fábio M. Costa 响应,下面是一个比较好的方式:

    var create_dom = function(html){
    var empty = new Element('div', {'html':html});
    var el = empty.getFirst();
    return el;
    };

    这样简化了代码。但上面的代码说是不能直接table元素。

    同时, Aaron Newton 甚至给出了新版本的mootools中的一个功能。地址在:

    http://github.com/mootools/mootools-more/tree/master/Source/Element/Elements.From.js

    在这个版本中,不仅处理了table元素。而且考虑到了script的执行和有可能生成多个元素的情况。在我上面的代码只认为只有一个子元素,因此使用的是getFirst()。而Newton给出的则使用了getChildren()这样会返回多个元素。
    类别:Uliweb 查看评论

    2009-09-14 00:00:00 CST

    September 13, 2009

    Limodou

    Doto -- Uliweb application

    Doto http://code.google.com/p/doto

    是我使用Uliweb开发的一个多人todo应用,目前还在开发中,如果有兴趣可以看一看。

    状态:刚开始做。

    想法:

    1. 首先是个人的Todo管理,然后可以将此Todo分享给其它的人,作为多个的Todo管理
    2. 可以设置到期时间
    3. 可以上传附件,将使用swfupload
    4. 成员分享功能
    5. 用户管理(考虑未来会实现group功能)
    6. Tag功能
    7. Checkpoint功能。每个Todo可以有若干个checkpoints,用以检查这个Todo完成的关键指标
    8. 尽可能使用Ajax界面 (有些功能是模仿http://www.pozdeev.com/mytinytodo
    9. 使用mootools ajax库
    10. 评论功能。每个todo都可以有评论。
    11. 操作记录记录在评论中
    12. 通知功能,对于某todo的内容变化,将通知相关的人员,通过消息功能
    13. 消息功能(针对每个人),包括:相关todo的操作记录,及他人发给你的消息
    14. 个人的活动日历及活动报告
    15. Todo的活动报告

    主要是想在平时的工作中进行任务的管理及协作。

    使用的工具:

    Uliweb(底层框架)
    Mootools(Ajax框架)
    Yaml(CSS框架)
    ORM(Uliweb自带)

    类别:Uliweb 查看评论

    2009-09-13 00:00:00 CST

    September 12, 2009

    HYRY 's Blog

    scipydoc提供打包下载

    《用Python做科学计算》开始提供打包下载了。而且也有了GAE镜像: 

    请使用下面的链接下载各种打包版本,其中Html打包版本格式最为正确,CHM和PDF版都多少有些问题。 下载Html打包版 下载CHM版 下载PDF版

    本书有两个镜像地址:

     http://hyry.dip.jp/pydoc (每日更新)

     http://pyscin.appspot.com/html/index.html (每周更新)

    关于CHM和PDF的制作过程,在 关于本书的编写 章节有详细叙述,目前PDF文档还不是很完美,有程序行过长出界和无法显示matplotlib动态生成的图片等问题。

    2009-09-12 12:20:05 CST

    September 07, 2009

    Limodou

    CPUG 9-5日会课视频(含Uliweb视频)

    感谢老白

    http://you.video.sina.com.cn/a/3421652-1267293610.html


    类别:Uliweb 查看评论

    2009-09-07 00:00:00 CST

    September 06, 2009

    HYRY 's Blog

    下载Scipy09会议视频

    Scipy09会议已经结束,这里提供了许多视频下载,也可以在线观看:

    http://www.archive.org/search.php?query="SciPy%202009"

    为了将其中的一些教程写入《用Python做科学计算》中,我买了一个1TG的硬盘,开始下载视频,不到5个小时的时间已经下载下了约40G的内容,融汇贯通之后将写进教材中。

    观看视频的时候,想把觉得有用的部分记录下来,因此用c#写了一个小程序:

    http://hyry.dip.jp/files/MPCCommand.exe

    这个程序通过HTTP控制 Media Player Classic,可以通过热键记录下视频的播放点,并添加说明。点击播放点可以直接从选中的时间开始播放。需要打开Media Player Classic的Web interface功能。目前只是能用,还没有错误处理和使用说明。日后功能完善之后再单独作为小软件发布。

    2009-09-06 21:18:24 CST

    Limodou

    Uliweb中的几点变化

    1. 命令行增加了一个makepkg的命令。它可以用来创建一个目录,同时自动在目录下创建一个__init__.py的文件。方便创建自已的Python包。可以使用:uliweb makepkg name来在当前目录下创建名为name的包结构。

    2. 开始支持setup方式来安装app,需要setuptools的支持。目前支持两种类型,一种是app的集合entry_point的group名是uliweb_apps。另一种是单个的app,entry_point的group名为uliweb_app。前者会自动搜索目录下所有的app,而后者则认为只有一个。因此在处理上有所不同。具体如何做我会另写一篇文章来介绍。

    3. 可以在settings.ini中写bind和expose。expose是用来绑定url和函数的。而bind是用来绑定topic和函数的。这样的好处是,不需要在python源文件中绑定了,虽然有一些麻烦,但是可以不用导入python文件,只有在真正使用时才需要导入,可以提高效率。但是对于用户,需要去修改settings.ini文件。我认为支持还是有用的。比如,在python源文件中的绑定如果要生效,那么只有去运行相关的代码,怎么做,依靠导入。为了让所有的app中的绑定全部生效,uliweb在启动时会自动导入所有有效的app和它的views.py文件。这样,你可能在__init__.py中写topic的绑定,在views.py写url的映射。所以,在启动时,每个app都会导入若干的文件,如果文件代码比较多,甚至有一些复杂的处理,会影响启动。采用配置的方式要好得多。同时象写在__init__.py中的bind代码,如果你没有把相应的app加入到INSTALLED_APPS中,那么如果导入了可能会引起一些执行上的问题。采用配置的方式可以避免了相关代码的执行,会好一些。

    4. 为了可以在settings.ini写bind,因此扩展了dispatch模块,可以让bind, call, get支持字符串形式的函数,形式为module.function_name。这样当call, get在处理时,如果发现函数是字符串,会去先导入再使用。

    5. hack了一个setup.py。因为有人反映setup.py install有问题。发现的确,于是跟踪setuptools的模块才发现了问题。对于packages,setup.py只会查找第一层的。所以setuptools提供了一个find_packages的函数,可以自动将子目录下的包列出来。和我理解的差别很大。我原以为就是递归方式,结果不是。再有就是数据文件的处理,也只是一层,不是递归的。结果uliweb有一个目录是template_files,是用来生成project和app的模板目录,放了一些创建时要拷贝的文件,因此它们不是真正的包结构,并且还有象static和templates这样的空目录。这些都在安装时无法拷贝过去。因此没办法,我hack了setuptools的build_py类,用我自已的build_package_data来替换原来的方法。它是会在拷贝完package之后执行的。在这个函数里,我将进行递归操作,把所有文件,除掉象.svn, *.pyc之类的文件,不管是不是包结构都会进行拷贝。这样你只要在setup的package中写顶层的包就可以了。在安装时会将这个包目录下的所有文件包括子目录都进行拷贝。同时把主要部分写到uliweb/utils/setup.py中去了,这样用户在使用时可以从这里导入setup函数。同时我写的setup.py会在执行时自动删除build和dist目录。感觉setuptools不会自动删。


    类别:Uliweb 查看评论

    2009-09-06 00:00:00 CST

    September 01, 2009

    Limodou

    如何通过http代理来使用github

    经过前阵子的试验,我基本掌握了git的使用,特别是在windows下使用TortoiseSVN。但是由于单位有HTTP代理,所以并没有怎么去使用。后来经过刘磊的提醒,给了一个关于ssh-https-tunnel的使用链接,可以通过它来访问代理,然后使用git。刘磊也将其加入他的文档中《Git使用指南》,不过并没有描述详细的配置过程,下面由我向大家说一下。

    1. ssh的认证不要使用Tortoise自带的,一定要使用git的openSSH的方式,这一点很重要。如果你已经指定了Tortoise的方式,那么通过修改环境变量GIT_SSH就可以了,当然重装也是一种办法。

    2. 配置时使用git bash来操作。

    3. 通过执行

    ssh-keygen -t rsa

    创建公私钥,文件就保存在~/.ssh/id_rsa下就行了。执行时一路回车就好了。

    4. 创建ssh的config文件,如:

    vi ~/.ssh/config

    内容为:

    Host github.com 
    ProxyCommand ~/.ssh/ssh-https-tunnel %h %p ~/.ssh/proxyauth
    Port 443
    Hostname ssh.github.com
    这里你可以看到第二行最后有一个~/.ssh/proxyauth。这是因为我单位的代理有口令,所以要再生成一个proxyauth文件,格式就是:username:password。如果你没有,去掉它就行了。
    同时Hostname的目的是为了创建一个别名,其实我们使用的是ssh.github.com,但是因为平时都使用git@github.com,所以为了不进行修改,创建一个别名。

    5. 下载ssh-https-tunnel,可以从 http://zwitterion.org/software/ssh-https-tunnel/ssh-https-tunnel ,保存到你的git的~/.ssh目录下

    同时要打开这个文件进行修改,将:

    my $proxy      = "";

    my $proxy_port = ;

    改成你的实际的代理服务器地址。
    6. 使用ssh git@github.com 来测试
    Hi limodou! You've successfully authenticated, but GitHub does not provide shell
     access.
    Connection to ssh.github.com closed.
    这里的难点一个是代理认证的配置,这是我在网上搜到的。还有就是GIT_SSH的设置。如果安装了TortoiseSVN,选择了ssh客户端,那么有可能ssh的代理设置通过,但是git使用时会出错,因为它会去看环境变量。

    类别:心情故事 查看评论

    2009-09-01 00:00:00 CST


    Powered by Planet!
    Last updated: 2010-02-09 02:32:51 CST