• Trac是一款使用Python开发的WEB项目管理系统,集SVN、文档、任务管理于一体,基于BSD协议发布。

    Trac在Apache上的选择一般只有CGI、FastCGI、mod_python几种选择,CGI速度太慢,FastCGI在Apache上的表现又不够稳定,似乎只有一个mod_python的选择可用,而WSGI的出现改变了这一情况,这个2006年诞生的标准得到了Python WEB开发领域的一致欢迎,Trac也对此提供了支持。

    mod_wsgi的下载安装请参考这里:http://code.google.com/p/modwsgi/

    编译完成后,修改httpd.conf文件,确认如下选项是打开的:

    LoadModule wsgi_module modules/mod_wsgi.so

    创建trac.wsgi文件,这是一个Python文件:

    import os
    
    os.environ['TRAC_ENV'] = '/path/to/trac'
    os.environ['PYTHON_EGG_CACHE'] = '/tmp'
    
    import trac.web.main
    application = trac.web.main.dispatch_request

    Apache配置,请将以下代码放入httpd.conf:

    WSGIScriptAlias /trac /paty/to/trac/trac.wsgi
    <Directory /paty/to/trac>
        WSGIApplicationGroup %{GLOBAL}
        Order deny,allow
        Allow from all
    </Directory>
    

    配置完成后,若显示不正常,可将wsgi脚本改为如下内容,测试是否可输出“Hello World!”

    def application(environ, start_response):
            start_response('200 OK',[('Content-type','text/html')])
            return ['<html><body>Hello World!</body></html>']
    

     

    若以上脚本执行成功,则代表wsgi安装正确,但如果你使用的是Python 2.4以上版本可能会遇到另一个问题,那就是Trac始终显示白页,没有任何输出!

    检查error.log文件

    httpd: Objects/stringobject.c:105: PyString_FromString: Assertion `str != ((void *)0)' failed.
    [Fri Mar 20 20:39:03 2009] [notice] child pid 8734 exit signal Aborted (6)

    由此可得知mod_wsgi线程崩溃掉了,Google一下,在这里得到一些资料,看来wsgi应用在Apache下崩溃的不止是Trac,Django也是有可能的。

    根据作者的提示得知,这是由于Apache与Python的expat库版本不匹配导致。

    Apache expat版本:

    httpd-2.2.8/lib $ strings libexpat.so.0.1.0 | grep expat_
    expat_1.95.2

    Python expat版本:

    /opt/httpd-2.2.8/lib $ python
    Python 2.4.3 (#1, May 24 2008, 13:47:28)
    [GCC 4.1.2 20070626 (Red Hat 4.1.2-14)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import pyexpat
    >>> pyexpat.EXPAT_VERSION
    'expat_1.95.8'

    找到原因后就好办了,但是作者只提了一句下载expat 2.0.1安装,却没说如何安装到Apache,Apache的configure本身并没有with-expat参数,只有apache_src/srclib/apr-util/configure有这个参数却不知如何关联,无奈之下另找方案。

    检查httpd依赖的库:

    /opt/httpd-2.2.8/bin $ ldd httpd
            linux-gate.so.1 =>  (0x002e6000)
            libm.so.6 => /lib/libm.so.6 (0x00cd8000)
            libaprutil-1.so.0 => /opt/httpd-2.2.8/lib/libaprutil-1.so.0 (0x00c1e000)
            libexpat.so.0 => /opt/httpd-2.2.8/lib/libexpat.so.0 (0x00925000)
            libapr-1.so.0 => /opt/httpd-2.2.8/lib/libapr-1.so.0 (0x003df000)
            libuuid.so.1 => /lib/libuuid.so.1 (0x00110000)
            librt.so.1 => /lib/librt.so.1 (0x00d91000)
            libcrypt.so.1 => /lib/libcrypt.so.1 (0x0023d000)
            libpthread.so.0 => /lib/libpthread.so.0 (0x00d01000)
            libdl.so.2 => /lib/libdl.so.2 (0x00cd2000)
            libc.so.6 => /lib/libc.so.6 (0x00402000)
            /lib/ld-linux.so.2 (0x00b6f000)

    由此可知Apache使用的是/opt/httpd-2.2.8/lib/libexpat.so.0,而这个文件是一个软链接,尝试将其删除,重新指到2.0.1:

    /opt/httpd-2.2.8/lib $ ln -s /opt/expat-2.0.1/lib/libexpat.so.1.5.2 libexpat.so.0

    重新启动Apache, 测试成功!

    参考资料:

    expat下载地址

    Trac官方安装资料

    mos_wsgi官方对此问题的方案

  • MediaWiki是一款非常优秀的资料管理系统,用来管理技术文档是一个不错的选择,在很长的一段时间,我们用的也是这个。

    近期随着Trac在部门内使用和一些超强的插件如:ChangeLogPluginIncludeSourcePartialPlugin给文档编写带来的巨大便利,于是我们决定转投Trac来维护文档资料。

    做完决定后就要考虑将文档转帖过来,但是由于两者WIKI语法并不兼容,重新整理的工作量太大,貌似有批量转换工具,但是由于我们原来的文档比较混乱,各部门的都有,因此不想使用。幸好trac-hacks.org有一个MediaWikiPluginMacro可用,此插件可使用MediaWiki语法,如:

    {{{
    #!mediawiki
    = Heading =
    Everything here is interpreted by ''mediawiki'' processor.
    
    == List ==
    # first
    ## first.a
    ## first.b
    
    [[Wiki link]]
    }}}

    但是这个两年多没有维护的插件刚开始用就给我提示:TypeError in nextItem(),在http://trac-hacks.org/ticket/2941有热心人提供了解决方案。下载parser.py后覆盖原文件,重新安装,OK,这下不提示错误了,不过还有另外一个问题,就是WIKI中文链接貌似没有解析,全成了 [[文字]] 这样子,而英文链接则表现正常,初步判断为解释器没有对中文进行匹配,这个只能自己动手了。先拿最大的parser.py开刀,在925-963行找到了如下代码:

    _linkPat = re.compile(ur'^([A-Za-z0-9\s]+:)?([A-Za-z0-9_\.\-\s\/]+)(?:\|([^\n]+?))?\]\](.*)$', re.UNICODE | re.DOTALL)
    def replaceInternalLinks(text):
            arr = text.split('[[')
            sb = []
            sb.append(arr.pop(0))
            for bit in arr:
                    namespace, link, alt, rest = None, None, None, None
                    match = _linkPat.match(bit)
                    if match:
                            namespace, link, alt, rest = match.groups()
                    if link:
                            if not namespace:
                                    namespace = u'wiki'
                            namespace = slugify(namespace)
                            if namespace == "image":
                                    sb.append(u'<a href="/image/')
                                    sb.append(slugify(link))
                                    sb.append(u'"><img src="/static/')
                                    sb.append(link)
                                    if alt:
                                            sb.append(u'" alt="')
                                            sb.append(alt)
                                    sb.append(u'" /></a>')
                            elif namespace == 'wiki':
                                    sb.append(u'<a href="')
                                    #sb.append(u'<a href="/')
                                    #sb.append(namespace)
                                    #sb.append(u'/')
                                    sb.append(slugify(link))
                                    if alt:
                                            link = alt
                                    sb.append(u'/">')
                                    sb.append(link)
                                    sb.append(u'</a>')
                            sb.append(rest)
                    else:
                            sb.append(u'[[')
                            sb.append(bit)
            return u''.join(sb)

    简单分析后我判断这段代码就是解析链接标签并进行相应替换。分析后可知,925行的正则表达式只匹配了英文字母、数字、英文.、-、空白字符与/,中文字符并不在此范围内,增加一个\w后如下:

    _linkPat = re.compile(ur'^([A-Za-z0-9\s]+:)?([A-Za-z0-9_\.\-\s\/\w]+)(?:\|([^\n]+?))?\]\](.*)$', re.UNICODE | re.DOTALL)

    使用python setup.py bdist_egg重新编译为egg文件后复制到trac_dir/plugins目录,问题解决。