2014年1月16日 | 分类: 生活感悟 | 标签:

在三年前狠狠折腾了一番ios5.x后就我再没有折腾过ios,如今,在港行iphone5s全面支持移动3,4G后,才终于入5s开始使用ios7,终于又需要对ios7折腾一番了。目前来说ios7.x的越狱以及配套的越狱插件已经趋于稳定,虽然还有一些著名插件在64位的cpu上无法兼容,但ios7系统本身已经在很大程度上优化了操作。一下是一些总结,这些内容网络上都有,我全部已经尝试过。

越狱:
从ios7越狱工具放出的那一刻起,国内各种iphone辅助软件(itools,pp助手)也立马更新,而且剔除了太极助手这样的流氓软件,这使得越狱变的更加简单快捷。这些辅助软件还能自动备份shsh和手机资料,的确很是方便。越狱后最大好处就在于可以安装插件和精简系统,我不是那种插件爱好者,装插件的目的是把手机调整到最适合自己使用的状态,我装了以下一些插件:

搜狗输入法
搜狗号码通(先试用,不行再装回kuaidial和短信拦截)
adBlocker
AskToCall
biteSMS
CCSettings
中国农历
iCleaner pro
iFile
LessAD
noSpot
WiCarrier

以上,感觉ios7里Activator和sbsetting(目前还装不上)已经没有太多必要了,自带的通知中心和CCsettings提供的额外的快捷按钮已经能提供大部分功能了。

资料迁移:
我需要将iphone4上的短信,照片,备忘录,通讯录以及日历迁移到到iphone5s上,我不使用icloud,通讯录和日历使用的google同步;短信,照片和备忘录可以用辅助软件itools备份到pc上,其中短信和备忘录可以直接用itools导入5s,照片和视频的话,在网上找了一个方法,先把需要导入的照片上传到/DCIM/100APPLE文件中,然后删除/PhotoData下的Photos.sqlite,重启机器,进入相册后系统会自动将张片恢复。

系统和启动项精简:
首先是可以删除键盘和语言,这个我都没去删,好像不能空出用户空间,其次是可以删除那些系统自带的软件,如nike,股票什么的,这个删不删其实作用也不大,主要需要修改的是ios的启动项,启动项存在于/System/Library/LaunchDaemons目录下,每个plist文件对应一个服务,在这里可以删除一下不必要的服务,当然,操作之前请先备份。根据网上的各种贴子,我最后删除了如下服务:
com.apple.AOSNotification.plist
com.apple.CrashHousekeeping.plist
com.apple.DumpBasebandCrash.plist
com.apple.DumpPanic.plist
com.apple.OTACrashCopier.plist_
com.apple.OTATaskingAgent.plist_
com.apple.ReportCrash.DirectoryService.plist
com.apple.ReportCrash.Jetsam.plist
com.apple.ReportCrash.SafetyNet.plist
com.apple.ReportCrash.SimulateCrash.plist
com.apple.ReportCrash.StackShot.plist
com.apple.ReportCrash.plist
com.apple.VoiceOverTouch.plist
com.apple.appsupport.cplogd.plist
com.apple.aslmanager.plist
com.apple.awdd.plist
com.apple.certui.relay.plist
com.apple.crash_mover.plist
com.apple.crashreportcopymobile.plist
com.apple.daily.plist
com.apple.fseventsd.plist
com.apple.gamed.plist
com.apple.itunescloudd.plist
com.apple.midiserver-ios.plist
com.apple.mobile.obliteration.plist
com.apple.mobile.softwareupdated.plist_
com.apple.passd.plist
com.apple.powerlog.plist
com.apple.printd.plist
com.apple.scrod.plist
com.apple.search.appindexer.plist
com.apple.searchd.plist
com.apple.sharktrace.plist
com.apple.softwareupdateservicesd.plist_
com.apple.syslog_relay.plist
com.apple.syslogd.plist
com.apple.voiced.plist
com.apple.vsassetd.plist
com.apple.wifi.wapic.plist

经测试,删除以上文件,对系统运行没有任何问题,当然也屏蔽了一些我用不到的系统功能,如语音输入(voiced),游戏中心(gamecenter),icloud(itunescloudd)。

以上,经过上面的折腾,手机算是调整到了一个比较良好的状态,现在5s一晚待机只消耗4%的电量,还是可以接受的。

2012年5月17日 | 分类: 兴趣所在 | 标签:

vim插件的管理和部署一直是让人纠结的事情,前段时间xtao同学推荐我使用spf13-vim项目,让这件事情变得简单可依赖了。

spf13-vim是一个.vimrc+vundle的集合,主流的.vimrc的配置都基本包含在内了,如果还有其他特殊需求,也可以通过.vimrc.local文件对当前的配置文件扩展。我从github上folk了spf13-vim的项目,checkout一个新的branch进行了符合自己习惯的修改。

对于.vimrc文件主要是取消一些不太想用的插件,比如各种snippets插件,有关vim-tab的插件,一些多余的colorschemes,还有就是autoclose(有些时候输入不需要autoclose)。除此之外在我的branch中加入了.vimrc.local 覆盖了原来的一些设置也加入了其他map,具体如下:

set wrap    "我需要折行
set splitbelow    "split在下边
set mouse=""    "不要鼠标选择

"编码设置
set enc=utf-8
set fenc=utf-8
set fencs=utf-8,ucs-bom,gb18030,gbk,gb2312,cp936
set fileencodings=utf-8,gbk,latin-1

"快捷换行
nmap  ddkP
nmap  ddp
vmap  xkP`[V`]
vmap  xp`[V`]

set foldmethod=marker "folder

set t_Co=256 "如果是在模拟终端需要把颜色设置成256色

"cscope 快捷映射
if has("cscope")
    set cscopetag
    set csto=0
    if filereadable("cscope.out")
        cs add cscope.out
    endif
    set csverb
    nmap s :cs find s =expand("")..
    nmap g :cs find g =expand("")..
    nmap c :cs find c =expand("")..
    nmap t :cs find t =expand("")..
    nmap e :cs find e =expand("")..
    nmap f :cs find f =expand("")..
    nmap i :cs find i ^=expand("")$
    nmap d :cs find d =expand("")
endif

在配置的尝试中终端的颜色配置让我纠结了很久,最后在某个插件的文档中看到了在模拟终端需要加set t_Co=256。

最后在自己的branch中顺便加入了.gitconfig的配置,改了一下原来的配置脚本,再像项目那样,在主机上加了一个好记的地址做了一个redirect,这样就可以一条命令部署这个vim+git环境了。当然,vim的版本最好是7.3,最后只需要:

curl -kLo - http://xw2423.byr.edu.cn/tools/vim.php |sh

运行之后就可以倒杯水等着自动部署vim配置,安装vim插件了。

最后提一下screen下的vim的颜色显示,也就是256色的显示,这个配置我也尝试了很久,首先参考了http://www.frexx.de/xterm-256-notes/对screen 256的配置,试了后发现solarized配色有大面积的黑色背景,最后仔细看注释发现 erase background with current bg color 于是把defbce “on”注释了,颜色就恢复过来了,此配置在cygwin+mintty和SecureCRT中测试没有问题,如果是用SecureCRT作为终端需要把模拟终端的类型设置成xterm并勾选ANSI color。

以前感觉很麻烦的vim配置管理和部署终于有了一个比较满意的解决方法了。

2012年1月3日 | 分类: 兴趣所在 | 标签:

作为新年的礼物,nForum1.1在经过为期一个月的线上测试后,今天终于push到github上去了,相关的文档我也完善了一下,能有助于快速理解nForum1.1的前端结构,新增的文档如下:

nForum1.1 Changelog
nForum1.1 前端说明
nForum1.1 Ajax说明

由于一次性push了60多个commits,所以相对于nForum1.0版本代码上的修改还是很多的,如果在原来的基础上修改很大的话,merge起来会是一个比较痛苦的过程,不过大家想想fancyrabbit@newsmth,我觉得再困难的merge都是浮云了,哈哈。merge的过程除了仔细核对代码外也需要仔细对比app/config/nforum.php.example的不同,配置文件千万不要写错了。
对于kbs,最好更新的最新版本,目前我测试的nForum1.1是基于kbs r10955。
nForum1.0版本已经终结于这之前的提交,我已经打了一个1.0的tag,需要持续使用老版本的用户可以git checkout 1.0或直接去tags页面下载tar包。

还有就是可能有些小修改在changelog中没有提及到,不过会存在于commit log里面,如果有不清楚的地方可以先查看一下commit log。
web上的文档已经全部更新,欢迎访问http://xw2423.byr.edu.cn/blog/nforum 

2011年12月2日 | 分类: 兴趣所在 | 标签:

时隔一个月,在经历了从正常人到“矿工”再到正常人的变化后,一个新的nForum就即将折腾出来了。

如果说原来的版本是1.0的话,这个版本为1.1比较合适,最大的改动即是前端,nForum1.1的前端结构发生根本的改变,而促使我改变的原因可能是想再次体验一下 沉醉于代码中的感觉吧。

nForum1.1致力于提供一个完整的前端基础,以实现页面的ajax化并规范一些简单的交互接口。技术路线的灵感来自于某月末日在google reader上看到豆瓣某前端工程师的演讲ppt。所以nForum1.1采用了和豆瓣说同样的javascript框架Backbone.js作为基础来构建前端MVC,在弹出窗口方面继续沿用jquery-ui的dialog组件,不过nForum进行了进一步的封装以实现与应用的紧密结合并屏蔽了jquery-ui的特性。暴露给window的变量有APP,BODY,SESSION,DIALOG,SYS,这些对象足以完成对页面的控制,具体是怎样的,可能还需要一个文档来说明。
此外,nForum1.1在html5的道路上更进一步,整体的标签在往html5上演变,对于支持html5的浏览器,在css和文件上传上都开始使用html5特性,相应的,新版本取消了原来的javascript圆角功能,对老版本的IE做了某些交互特性上的放弃,当然诸如IE6这样的s13浏览器还是可以正常访问的,不过体验会差点。。。
nForum1.1在原来的model层面上修改的很少,主要是整合了文章和邮件的转寄和回复功能;controller主要的修改是满足ajax请求并把请求方式和渲染格式完全分离,在渲染数据时html,json,xml是地位相等的。view融合api插件的思想,把所有渲染的方式统一起来,也就是说api插件和nForum web是用的相同的view。
最后就是要解决新的架构带来的新的问题,比如SEO,nForum1.1的html内的链接呈现和现在的本版一致,系统自动识别不同的spider输出原始页面以便spider能很好的访问。除此之外,IE6下对于hash的不完整识别导致url中的search部分不能很好的解析,最后无奈hacker了backbone.js的代码。
其余的特性以及详细的修改都会出现在changelog的,之后的时间里要review一下代码,在各种浏览器中测试一下关键功能,整合,测试,merge出北邮人论坛的版本,测试,发布到github日子可能还要晚点吧。

其实代码的实现是漫长的,枯燥的,痛苦的,不过当整体结构很清晰时这些也都只是时间问题了;把你对整个系统的理解用代码实现出来,在这个过程中可能会遇到各种结构问题和设计缺陷,但在一次次的迭代中结构会变的更加合理,耦合会变得更加自然,这也许就是根本的乐趣之所在。去年的这个时候nForum1.0开源了,一年过去了,nForum1.1就作为今年的礼物吧。

2011年5月26日 | 分类: 兴趣所在 | 标签:

现在可以用这个标题了,我是挺高兴的。nForum API终于被我merge进nForum的主干了,这可是今年一年工作目标啊,居然在不到半年的时间里就可以发布一个可用的版本,甚是欣慰。

对于开放API来说,在我写nForum时就是由此想法,在写手机版的时候是此想法甚是强烈,从那时起基本上就开始做相关的知识积累,看过了《Restful Web Services》,或多或少的研究或使用了google,twitter,sina,qq的API后,开始尝试写适合于nForum的api。验证从简单的basic auth开始,输出支持json和xml的格式。在逻辑上api和web没有太大的区别,甚至比web更简单,最值得推敲的就是API的设计,在兼顾性能的同时,考虑开放什么样的数据,这是非常关键的。在写的过程中也碰到许多棘手的问题,比如状态,把API请求变成无状态的着实让我纠结了很久,kbs的web机制必须每次请求都要附加cookie而API确不能这样,当然这些cookie信息也不能放在url里,最后只能让服务器缓存这些信息使得外界看来API是无状态的。最后就是这次充分使用了一下phpunit,API还是挺好写测试用例的。

nForum前段时间更新了地图标记功能,原以为这个功能挺实用的,能在帖子中嵌入自定义地图和位置,对于food,travel这样的版来说应该挺有用的,但是好像没什么人对这个很感兴趣,就纯当熟悉一下google map api的用法吧。

水木终于把上线nforum提上日程了,挺高兴,能有更多的人来使用它就是一件好事,兔总fancyrabbit@newsmth也提了不少修改意见,非常感谢,希望nforum能越来越好。

nForum API的文档:http://xw2423.byr.edu.cn/blog/nforum-api
代码在:http://github.com/xw2423/nForum

还有就是有想使用北邮人论坛API的童鞋请直接找我吧,wei.xiao.bupt#gmail.com

2011年1月23日 | 分类: 兴趣所在 | 标签: ,

项目地址:https://github.com/xw2423/nForum

今天总算把一个历史悠久的bug解决了,我也好意思把地址贴出来了。

然后弄了一个页面,把一些文档帖出来了,但是格式还没调好,家里的网络访问blog太扭曲了。
http://xw2423.byr.edu.cn/blog/nforum

Life is like a box of chocolates, you never konw what you’re going to get!

2010年12月26日 | 分类: 他山之石 | 标签:

虽然对这类型的文章不是很感兴趣,不过很多有些东西还是很现实的。

———————————————分割线——————————————————-

2010,有一种美女叫“凤姐”,有一种帅哥叫“犀利哥”,有一种女朋友叫“小月月”,有一种爸爸叫“李刚”……
2010,有一个新词叫“给力”,有一个群族叫“爱疯”,有一名中医喜欢绿豆和茄子,有一位道长会水下闭气……
2010,有一种节能减排叫“拉闸限电”,有一种足球协会叫“足囚协会”,有一种慈善叫“裸捐”,有一种房地产调控叫“空调”……
2010,有一种住宅叫“蛋屋”,有一种理财叫“群租”,有一种集装箱叫“蜗居”,有一种别墅叫“经适房”……
2010,吃小龙虾可能肌溶解,送孩子上学时可能被拆房,盖被子可能会被闷死,赶不上火车可能白买票……
2010,很多“大牌”都不淡定,郭德纲称徒弟“民族英雄”被反三俗,周立波嘲弄网络舆情获封“周自宫”,连腾讯都被360“逼”出了一个“艰难的决定”……
2010,爱写日记的不光有雷锋,还有韩峰,大美女不光天上有,天上人间也有……
他们的发现—
2010,想买房的人发现,“国十条”猛烈的来了,却没有带走一片云彩。房产调控年年高高举起,年年轻轻放下,年年政策给力,年年执行不力。哎。不是狼来了,而是狼都来腻了。
2010,剩男剩女们发现,其实找对象不用去江苏卫视的《非诚勿扰》,更不用去山东卫视的《爱情来敲门》,只要去上海世博会排几小时队就行,患难之中见真情。
2010,创业者们发现,原来蒙牛董事长牛根生的“小胜靠智,大胜靠德”不靠谱,小胜是要靠智,但大胜得缺德。
2010,唐骏发现,英雄也要问出处,一个人,不论多么辉煌过,一个不小心,就有被集体的口水迅速淹没的那天。
2010,一些精英们发现,他们迷信的李一,其实不是神仙,只是很清楚他们的病根–他们已经成为权力、资本、色。欲。的奴隶,无法在欣赏到自然的美。
2010,媒体发现,电影《山楂树》玩了把纯情,于是揶揄道:世道这么乱,装纯给谁看?其实在乱世,人们更需要纯情的东西。不管环境多么残酷,留给自己一个纯情的时间,一个纯情的对象,日子就过来了。
2010大家的发现:
A:我发现,以前该省钱的时候我一分钱没省下来;现在真的省下来了,钱又不值钱了。
B: 我发现我找个合适的工作很难,但是,找个合适的对象比找个合适的工作还要难!
C:我发现我女朋友开始催我抓紧时间买房子了,吃饺子也不给我拌蒜泥了。
D:我发现100元越来越像10元了。
E:发现“国考”越来越热了,考上的概率越来越小了,发现考研的越来越多了但含金量越来越低了,发现学校毕业就业率越来越高了,但薪水“低破”一千了。还发现。我长白头发了……
F:发现懂了这句话:他不爱你,不是你不够好,而是你无论怎样,都不好。
G:发现身边的楼房阳台都变成菜园了。
2010的悲喜:
1.长得帅有什么用?考清华有什么用?学经济有什么用?将来还不是要娶罗玉凤!
2.新闻联播的牛叉之处在于就算你一直在换台,也能完整的看完一条新闻。
3.现在的人穿衣服不是为了保暖,而是为了关键时刻有衣服可脱;现在的人同居不是为了结婚,而是为了不用结婚。
4.人类社会发展有很多问题,但归根到底只有三个:长相问题、纯洁问题和人品问题。任何不解的事情,包括《十万个为什么》里面找不到答案的,都可以在这三大问题中找到答案。
结语:做自己的事,活自己的命。因为,神马都是浮云

2010年11月20日 | 分类: 兴趣所在 | 标签:

概述

urllib2中的核心类:
Request :一个具体的url请求,包含了请求的所有信息,不仅仅试用于http协议
OpenerDirector:与BaseHandler组合,通过组合不同得handler处理不同的请求
BaseHandler :参与完成请求处理的类,不同的请求处理都继承这个类

在urllib2中,一次请求被分为三个过程,分别是request,open,response
request:目的在于构造本次请求Request对象所需得所有信息,如http协议中的header信息
open:处理具体请求的过程,封装Request对象,调用更底层的类完成请求并返回response
response:对返回的Response对象做处理
当然后有一个error处理的过程,但这个不是主动触发的。

OpenerDirector

因为每次请求的具体实现是不同的handler,而且一次请求可能由很多handler组成。所以实现这一耦合机制的类就是OpenerDirector,这个类可以注册(添加)各种不同的handler用来帮助处理一次请求。通常来说handler中的命名规则为 protocol_request|open|response,这分别对应不同协议的三个过程。还是直接上代码,写了一点中文的注释。

class OpenerDirector:
    def __init__(self):
        # manage the individual handlers
        # 所有已注册的handler
        self.handlers = []
        # 已注册的不同过程的方法
        self.handle_open = {}
        self.handle_error = {}
        self.process_response = {}
        self.process_request = {}

    # 添加一个handler
    #
    def add_handler(self, handler):
        # 通过检测BaseHandler中的方法 确保handler继承于BaseHandler
        if not hasattr(handler, "add_parent"):
            raise TypeError("expected BaseHandler instance, got %r" %
                            type(handler))

        # 省略一些handler验证代码,主要是检查,这些handler是否有处理过程函数

        # 如果这个handler验证成功,会调用add_parent,这是BaseHandler的方法
        # 使得在handler中可以使用self.parent访问OpenerDirector,在HTTPErrorProcessor有用到
        if added:
            # the handlers must work in an specific order, the order
            # is specified in a Handler attribute
            bisect.insort(self.handlers, handler)
            handler.add_parent(self)

    def close(self):
        # Only exists for backwards compatibility.
        pass

    # 调用某个chain中的某种协议的方法
    def _call_chain(self, chain, kind, meth_name, *args):
        # Handlers raise an exception if no one else should try to handle
        # the request, or return None if they can't but another handler
        # could.  Otherwise, they return the response.
        handlers = chain.get(kind, ())
        for handler in handlers:
            func = getattr(handler, meth_name)

            result = func(*args)
            if result is not None:
                return result

    # 核心的方法,在此方法中实现了一次请求的三个过程
    def open(self, fullurl, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
        # accept a URL or a Request object
        if isinstance(fullurl, basestring):
            req = Request(fullurl, data)
        else:
            req = fullurl
            if data is not None:
                req.add_data(data)

        req.timeout = timeout
        protocol = req.get_type()

        # pre-process request
        # 调用所有已注册的handler的request处理方法
        meth_name = protocol+"_request"
        for processor in self.process_request.get(protocol, []):
            meth = getattr(processor, meth_name)
            req = meth(req)

        # 处理open过程
        response = self._open(req, data)

        # post-process response
        # 调用所有已注册的handler的respone处理方法
        meth_name = protocol+"_response"
        for processor in self.process_response.get(protocol, []):
            meth = getattr(processor, meth_name)
            response = meth(req, response)

        return response

    # 对于open处理过程,还分了三个小类别default,protocol,unknow
    # 按照这个数序如果存在某个处理方法则调用,返回结果
    def _open(self, req, data=None):
        result = self._call_chain(self.handle_open, 'default',
                                  'default_open', req)
        if result:
            return result

        protocol = req.get_type()
        result = self._call_chain(self.handle_open, protocol, protocol +
                                  '_open', req)
        if result:
            return result

        return self._call_chain(self.handle_open, 'unknown',
                                'unknown_open', req)

    #error处理过程是一个被动过程,它会调用handle_error中注册的错误处理方法
    def error(self, proto, *args):
        #省略代码

Handler

urllib2提供很多handler来处理不同的请求,常用的HTTPHandler,FTPHandler都比较好理解。这里提一下HTTPCookieProcessor和HTTPRedirectHandler

HTTPCookieProcessor是处理cookie的,在很多需要身份验证的请求中cookie是必不可少的,python中对cookie的操作是有cookielib模块来完成的,而这个handler只是调用了其方法,在request和response过程中将cookie加到请求中和把cookie从响应中解析出来。

HTTPRedirectHandler是处理30x状态的handler,直接看源码,貌似英文的注释已经讲的很明白了

class HTTPRedirectHandler(BaseHandler):
    # maximum number of redirections to any single URL
    # this is needed because of the state that cookies introduce
    max_repeats = 4
    # maximum total number of redirections (regardless of URL) before
    # assuming we're in a loop
    max_redirections = 10

    # 这个方法把当前Requst头中的信息附加到新的url中,就是跳转的目的url
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        """Return a Request or None in response to a redirect.

        This is called by the http_error_30x methods when a
        redirection response is received.  If a redirection should
        take place, return a new Request to allow http_error_30x to
        perform the redirect.  Otherwise, raise HTTPError if no-one
        else should try to handle this url.  Return None if you can't
        but another Handler might.
        """
        m = req.get_method()
        if (code in (301, 302, 303, 307) and m in ("GET", "HEAD")
            or code in (301, 302, 303) and m == "POST"):
            # Strictly (according to RFC 2616), 301 or 302 in response
            # to a POST MUST NOT cause a redirection without confirmation
            # from the user (of urllib2, in this case).  In practice,
            # essentially all clients do redirect in this case, so we
            # do the same.
            # be conciliant with URIs containing a space
            newurl = newurl.replace(' ', '%20')
            newheaders = dict((k,v) for k,v in req.headers.items()
                              if k.lower() not in ("content-length", "content-type")
                             )
            return Request(newurl,
                           headers=newheaders,
                           origin_req_host=req.get_origin_req_host(),
                           unverifiable=True)
        else:
            raise HTTPError(req.get_full_url(), code, msg, headers, fp)

    # Implementation note: To avoid the server sending us into an
    # infinite loop, the request object needs to track what URLs we
    # have already seen.  Do this by adding a handler-specific
    # attribute to the Request object.
    # 处理302错误
    def http_error_302(self, req, fp, code, msg, headers):
        # Some servers (incorrectly) return multiple Location headers
        # (so probably same goes for URI).  Use first header.
        # 获取跳转的url
        if 'location' in headers:
            newurl = headers.getheaders('location')[0]
        elif 'uri' in headers:
            newurl = headers.getheaders('uri')[0]
        else:
            return

        # fix a possible malformed URL
        urlparts = urlparse.urlparse(newurl)
        if not urlparts.path:
            urlparts = list(urlparts)
            urlparts[2] = "/"
        newurl = urlparse.urlunparse(urlparts)

        newurl = urlparse.urljoin(req.get_full_url(), newurl)

        # XXX Probably want to forget about the state of the current
        # request, although that might interact poorly with other
        # handlers that also use handler-specific request attributes
        # 构造新的请求
        new = self.redirect_request(req, fp, code, msg, headers, newurl)
        if new is None:
            return

        # loop detection
        # .redirect_dict has a key url if url was previously visited.
        # 循环检测机制,防止跳转循环
        # 把已经访问的url添加到redirect_dict中并对跳转的次数做了限制
        if hasattr(req, 'redirect_dict'):
            visited = new.redirect_dict = req.redirect_dict
            if (visited.get(newurl, 0) >= self.max_repeats or
                len(visited) >= self.max_redirections):
                raise HTTPError(req.get_full_url(), code,
                                self.inf_msg + msg, headers, fp)
        else:
            visited = new.redirect_dict = req.redirect_dict = {}
        visited[newurl] = visited.get(newurl, 0) + 1

        # Don't close the fp until we are sure that we won't use it
        # with HTTPError.
        fp.read()
        fp.close()

        # 获取新url的内容
        return self.parent.open(new, timeout=req.timeout)

    # 对于30x的错误都用302的方法实现
    http_error_301 = http_error_303 = http_error_307 = http_error_302

    inf_msg = "The HTTP server returned a redirect error that would " \
              "lead to an infinite loop.\n" \
              "The last 30x error message was:\n

Error handler

错误处理需要单独讲就是因为其特殊性,在urllib2中,处理错误的hanlder是HTTPErrorProcessor完成的

class HTTPErrorProcessor(BaseHandler):
    """Process HTTP error responses."""
    handler_order = 1000  # after all other processing

    def http_response(self, request, response):
        code, msg, hdrs = response.code, response.msg, response.info()

        # According to RFC 2616, "2xx" code indicates that the client's
        # request was successfully received, understood, and accepted.
        # 对于不是2xx的返回状态一概认为产生了一个错误
        # 都使用OpenerDirector的error方法来分发到相应的handler的处理方法中
        if not (200 <= code < 300):
            response = self.parent.error(
                'http', request, response, code, msg, hdrs)

        return response

    https_response = http_respons

urlopen,install_opener,build_opener

这是urllib2模块的方法,在urllib2模块中存在一个全局变量保存OpenerDirector实例。
urlopen方法则是调用了OpenerDirector实例的open方法
install_opener方法把一个OpenerDirector实例做为当前的opener
最关键的是build_opener,它决定了OpenerDirector中存在哪些handler

def build_opener(*handlers):
    """Create an opener object from a list of handlers.

    The opener will use several default handlers, including support
    for HTTP, FTP and when applicable, HTTPS.

    If any of the handlers passed as arguments are subclasses of the
    default handlers, the default handlers will not be used.
    """
    import types
    def isclass(obj):
        return isinstance(obj, types.ClassType) or hasattr(obj, "__bases__")

    opener = OpenerDirector()
    # 默认会加载的handler
    # 如果有这些类的子类则用子类代替他们
    default_classes = [ProxyHandler, UnknownHandler, HTTPHandler,
                       HTTPDefaultErrorHandler, HTTPRedirectHandler,
                       FTPHandler, FileHandler, HTTPErrorProcessor]
    if hasattr(httplib, 'HTTPS'):
        default_classes.append(HTTPSHandler)
    skip = set()
    # 获取默认handler中可以被替换的handler
    for klass in default_classes:
        for check in handlers:
            # 传入的handler可以是类名也可以是一个实例
            if isclass(check):
                if issubclass(check, klass):
                    skip.add(klass)
            elif isinstance(check, klass):
                skip.add(klass)
    # 去掉可以替换的handler
    for klass in skip:
        default_classes.remove(klass)
    # 添加handler
    for klass in default_classes:
        opener.add_handler(klass())
    # 再添加传入的handler
    for h in handlers:
        # 实例化
        if isclass(h):
            h = h()
        opener.add_handler(h)
    return opener

总结

显而易见urllib2的扩展性是很好的,opener很handler的低耦合可以使我们添加其他对于其他任何协议的handler,这里提供一个实现了文件上传功能的HTTPClient类(点击下载),这个类使用了https://github.com/seisen/urllib2_file提供的上传文件功能模块,不过这个与HTTPCookieProcessor有冲突,所以我添加了两个方法使在需要上传文件的时候用文件上传功能。
可以在urllib2_file.py后添加

def install_FHandler():
    urllib2._old_HTTPHandler = urllib2.HTTPHandler
    urllib2.HTTPHandler = newHTTPHandler
    urllib2._opener = None

def uninstall_FHandler():
    urllib2.HTTPHandler = urllib2._old_HTTPHandler
    urllib2._opener = None
2010年10月15日 | 分类: 兴趣所在 | 标签:

小白的我终于发现了这款音乐播放器,很多人推荐使用它代替rhythmbox,所以偶也赶紧尝试了一下。

项目主页:http://www.exaile.org/

最开始是通过学校的源装的,0.3.1的版本,虽然软件本身没什么问题,但是从网上找的douban电台的插件死活运行不了,遂换了官方的源,https://launchpad.net/~exaile-devel/+archive/ppa,官方的版本是0.3.2.0。

试用了一下exaile,还是挺nice的。在界面和易用性上比rhythmbox要好多了,比如对歌手和专辑的筛选统一放在来一个列面里面而且筛选的结果是按字幕排序。exaile内置的插件比rhythmbox要多了许多,自带了文件浏览器很均衡器。
另外还有一个exaile-cn项目 ,其中为中国用户提供了一些好用的patch,比如修正了mp3 的id3显示乱码,添加了自动下载歌词的功能,里面还包括了最新的豆瓣电台插件,微微麻烦的是这些patch要自己打上去。

随着使用的深入,问题还是存在的,我想把exaile最为gmbox的播放器使用,但是在命令行下调用exaile播放一个uri,如:

exaile http://XXX

exaile直接报错退出,看异常信息是因为这些uri被自动加上了当前目录的前缀变成了/foo/bar/http://XXX,找到相应的文件,在xl/main.py和xl/xldbus.py发现了:

args = [ os.path.abspath(arg) for arg in self.args ]

相当于把 uri变成了绝对路径,我直接改成args=self.args,勉强能过,但是问题又来了,在存在一个exaile实例时运行exaile uri还是会报错,这个错误无论的本地文件还是远程文件都会出。无奈只能去官方的bug tracker寻找,还真有其他人提交了这个bughttps://bugs.launchpad.net/exaile/+bug/601235 而且这个bug已经修复了,不过是在0.3.2.1的版本里面,当时我个内牛满面,然后看了一下这个项目的版本控制系统居然我还没怎么听过,名叫bazzar,而且google这个词第一搜索结果居然是女性时尚杂志。。。最后我还是装了下这个软件,但是7kB/s的连接速度(还不稳定)让我实在没勇气去下载全部的代码了,只能盼着官方的源早点更新。

最后的结果就是,我应该可以把rhythmbox卸载了。。。

2010年9月7日 | 分类: 兴趣所在 | 标签: , ,

最近发现在WPMU(在MU还没整合进WP之前搭的)中即使在mu-plugins中加了强大的Akismet插件还是不能组织垃圾评论,遂写了一个简单的验证码插件,把原来在论坛上的写的验证码组件弄过来改了改,于是现在博客的回复要输入一个计算等式的验证码了,这样我想垃圾评论会更少了吧。

单击此下载插件 下载后请解压缩至wp-content/plugins目录,再开启即可。

顺带提一下wp的本地化的东西,linux下用xgettext生成po文件,用msgfmt生成mo文件,他们都在gettext包中。操作方法如下

#生成po文件,其中--key的值是wp中的本地化函数
$ xgettext -d auth_img_post --from-code=utf-8 --key=__ auth_img_post.php

修改po文件的注释信息替换掉其中的charset,并翻译

#生成mo文件
$ msgfmt -o auth_img_post.mo auth_img_post.po

在wp插件中用load_plugin_textdomain函数加载mo文件,此函数接受三个参数分别是域标识,mo所在文件夹的绝对路径,mo所在文件件的相对路径。第三个参数覆盖第二个参数,具体文档参看http://codex.wordpress.org/Function_Reference/load_plugin_textdomain

第1页/6页12345...尾页