用 Jinja 的 Babel 支持来提取 Bottle STPL 模板里的待翻译文本

Bottle 的 SimpleTemplate (STPL) 模板引擎虽然颇为简单, 但是从我们长期的使用看来还是十分方便的. 然而美中不足的是, Bottle 并没有像 Jinja 那样对 Babel 的待翻译文本提取工具进行集成支持. 因此, 当我们需要提取模板中的待翻译文本的时候, 便只好把主意打到语法有些接近的 Jinja 上面了.

安装 jinja2 模块, 然后直接使用 Jinja 处理全部模板文件:

[jinja2: **.html]

(如果你仍在使用 Bottle STPL 的默认后缀 .tpl, 可以按照需要进行修改.)

这时我们会发现, 大部分的文本被提取出来了, 但是仍然可能会缺一些, 这是为什么呢?

阅读 Jinja 关于 Babel 集成部分的文档, 我们看到:

Until 2.7 template syntax errors were always ignored. This was done since many people are dropping non template html files into the templates folder and it would randomly fail.

也就是说, 如果遇到含有语法错误的模板, 这个模板会在提取时被忽略, 于是里面所有的待翻译文本都不会被成功提取出来.

在命令行里简单使用 jinja2.Template 测试了一下, 我发现问题主要出在 STPL 的 {{!variable}} 语法上面. STPL 使用 {{!variable}} 来表示禁用 XSS 过滤, 然而这个语法在 Jinja 中是非法的. 稍微研究了一下相关的代码, 我并没有找到通过配置就能解决的方案 (包括试图添加一个可选的 variable_start_string 以求 {{! 和 {{ 都能被识别, 但是这也被认为是不可能的).

于是, 我只好在 pybabel 的 wrapper 中用 monkey-patch 的方法来简单 hack 一下这个问题了:

#!/usr/bin/env python
import sys
from pkg_resources import load_entry_point

# Hack jinja2 to recognize bottle STPL's {{!variable}} syntax
try:
    from jinja2 import ext
    from StringIO import StringIO

    _babel_extract = ext.babel_extract
    def babel_extract_with_stpl_support(fileobj, *args, **kwargs):
        new_fileobj = StringIO(fileobj.read().replace("{{!", "{{"))
        return _babel_extract(new_fileobj, *args, **kwargs)

    ext.babel_extract = babel_extract_with_stpl_support
except ImportError:
    print("jinja2 not present, ignoring monkey-patching...")

if __name__ == '__main__':
    sys.exit(
        load_entry_point('Babel', 'console_scripts', 'pybabel')()
    )

这样, 所有的 {{! 会在尝试提取时被替换为 {{. 经过测试, 这个修改过的 babel_extract_with_stpl_support 成功地提取出了我们所有模板里的所有待翻译文本.

pybabel 不支持 PO 文件 “Language” 字段的临时解决方法

Babel 的上游似乎很不活跃, 我有根据之前一个 Pull Request 添加测试后再次提交这个更改, 但是没有得到任何回应… 因为急用, 就先采用了这个临时的方法.

如你所料, 这又是一个丑陋的 monkey-patch 😀

使用这个脚本来执行 pybabel extract, update, compile 等操作时, 如果用 -l 参数正确指定了语言, 生成的相应 .po/.mo 文件里就能保留相应的 Language 字段了.

#!/usr/bin/env python
import sys
from pkg_resources import load_entry_point

import babel.messages.catalog
_get_mime_headers = babel.messages.catalog.Catalog._get_mime_headers


def _get_mime_headers_with_language(self):
    headers = _get_mime_headers(self)
    if self.locale is not None:
        headers.append(('Language', str(self.locale)))
    return headers

babel.messages.catalog.Catalog.mime_headers = property(_get_mime_headers_with_language, babel.messages.catalog.Catalog._set_mime_headers)

if __name__ == '__main__':
    sys.exit(
        load_entry_point('Babel', 'console_scripts', 'pybabel')()
    )

让 PyMongo + MongoDB 2.6 继续支持空 $set

tl;dr 这只是一个丑陋的 monkey-patch 方法.

在 MongoDB 2.4 及以前版本中, db.foo.update({…}, {“$set”: {}}), 也即 “空 $set” 是可以正常执行的. 配合 upsert 等参数执行时可以有不同的方便用法. 但是升级到 MongoDB 2.6 以后, 由于引入了严格的参数检查, 试图进行空 $set 操作时, 会出现这样的错误:

OperationFailure: '$set' is empty. You must specify a field like so: {$mod: {: ...}}"

由于本猫不太想改依赖这个方法的代码逻辑(有点多…), 于是写了一个丑陋的 monkey patch 来 workaround 这个问题:

# Monkey patch pymongo to allow empty $set
from pymongo import collection
pymongo_collection_update = collection.Collection.update

def pymongo_collection_update_with_empty_set_support(self, spec, document, *args, **kwargs):
    if "$set" in document and document["$set"] == {}:
        document.pop("$set")
        if document == {}:
            return None
    return pymongo_collection_update(self, spec, document, *args, **kwargs)

collection.Collection.update = pymongo_collection_update_with_empty_set_support

上游 Bug Report: https://jira.mongodb.org/browse/SERVER-12266 (已经确定不会修)

计算SMSC的方法 (附带Py小工具)

早上小青蛙 @hexchain 发不了短信找俺要SMSC号码, 于是…嗯…
于是…步骤就用代码里的注释描述算了, 直接上代码:

#!/usr/bin/env python2
#coding:utf-8
import sys

# 获取短信中心号码
if len(sys.argv) > 1:
    orig = sys.argv[1]
else:
    print "输入短信中心号码:",
    orig = raw_input()
    
# 去掉+号
if orig[0] == "+":
    orig = orig[1:]

# 不是偶数在后面加F
if len(orig) % 2 == 1:
    orig = orig + "F"

# 把奇位和偶位互换
orig = "".join(["".join(x) for x in zip(orig[1::2],orig[::2])])

# 在号码前加91(国际化)+字符长度/2
orig = "91" + orig
orig = "%02d" % (len(orig)/2) + orig

print orig

参考资料: http://read.pudn.com/downloads179/sourcecode/embed/835292/new/SMSTABLE.h__.htm

检查 /usr 目录哪些文件不在包管理里 For Arch Linux

当然改改里面的命令也就能用在其他发行版了= =
随手写的, 各种不靠谱勿喷哦><

#!/usr/bin/env python2
import subprocess
import os
from os.path import join

pkg_list = subprocess.check_output(["pacman", "-Q"]).split("\n")[:-1]
pkg_list = map(lambda line:line.split()[0], pkg_list)

file_mapper = {}
for pkg in pkg_list:
    file_list = subprocess.check_output(["pacman", "-Ql", pkg]).split("\n")[:-1]
    file_list = map(lambda line:line.split(" ",1)[1], file_list)
    for filename in file_list:
        file_mapper[filename] = pkg

for root, dirs, files in os.walk('/usr'):
    if "__pycache__" in root:
        continue
    for name in files:
        filename = os.path.join(root, name)
        if filename not in file_mapper:
            if filename.endswith(".pyc") or filename.endswith(".cache"):
                continue
            print filename, "Not Found!"

给用NexusPHP的PT站写的分流/保种员自动发工资脚本

如题.使用说明:
1, 为实现统计本月流量/保种时间(NexusPHP默认的数据表没有留这些数据), 需要在users表里增加 downloaded_lastmonth, uploaded_lastmonth, seedtime_lastmonth 三个字段.
2, 需要获取分流员/保种员信息, 也需指定pipeliner/guarder字段(enum或boolean, 后者需略微改动此代码)
3, 需要python2.x环境, 以及python-MySQLDb库.
4, 需要服务器上有正常的crontab, 每月1日0点执行即可.
5, 此脚本还提供了记录上月实际上传下载(不含优惠信息), 以及下载时间的统计部分, 相应部分默认已注释掉, 可以手动替换启用. 如需显示在userdetails页面上, 只需做一个简单的减法(当前-上月). 实际上传/下载量的统计还需修改announce.php.
6, 工资(魔力/邀请)参考下面的设置修改. 本初始数据来自CMCT-PT的考核标准和奖励细则.
7, 修改数据库连接字符串的相应部分, 即可用.

Continue reading 给用NexusPHP的PT站写的分流/保种员自动发工资脚本

内网Windows XP用户使用ISATAP隧道的方法(附自动配置脚本)

声明: 本文采用方法来自 http://blog.lifetoy.org/2010/01/31/isatap-behide-nat/, 我仅仅是写了一个自动配置脚本(不包括配置路由器).

首先, 这个方法只能让内网一台机器用上ISATAP隧道(仅仅是简单的转发了相关数据包而已). 主要步骤分为两个:

  • 配置路由器转发协议号41的数据包到你的内网机器
  • 修改本机的ISATAP相关配置(拥有外网环境则会自动完成这一步, 内网环境需要手动处理一些问题)

只有一部分路由器本来的FIRMWARE支持转发协议号41的数据包, 如果你用的路由器不支持, 那就只能尝试dd-wrt或者openwrt了. 如果你的路由器很不幸的也不能刷这类自定义固件, 那也就没办法了..(嗯)

Linux类环境可以使用iptables实现转发(假设你的内网IP地址为192.168.0.10, 网关IP为192.168.0.1, 下同):

iptables -t nat -A PREROUTING -d 192.168.0.10 -p 41 -j DNAT --to 192.168.0.1

openwrt则可以修改文件 /etc/config/firewall: (此段为转载)

config redirect
option src                      wan
option dest                     lan
option dest_ip          192.168.0.10
option proto            41

至此, 第一步就做完了.

第二步, 可以用下面的脚本自动完成.
原文给出的bat脚本我在win7和winxp里都试过, 不能发挥应有的作用, 而且每次外网ip变动时都需要修改, 这对于动态ip用户更是无法接受的. 于是我用Python重写了一个脚本, 目前只能在XP系统使用. (其实要在Win7下用应该只需要很小的修改, 不过我暂时没测试环境:P)
注: 脚本中写的是上交的isatap隧道服务器配置, 如需使用其他服务器, 稍加修改即可.

Continue reading 内网Windows XP用户使用ISATAP隧道的方法(附自动配置脚本)

PyQt作品 – PingTester – 多点Ping测试工具

由于猫每次在一个临时测试点此测试一大片服务器的延迟和丢包, 一个个去跑太蛋疼, 于是用PyQt做了这么个小工具来测试各种线路的延迟丢包等信息.
这是我的第二个PyQt作品= =|||

截图:

(Archlinux / KDE4 环境下)


(Windows XP)

这个工具我已经初步实现了跨平台(在以上截图环境下运行正常), 在编写过程中, 我有如下的收获:

  • 不可以在子线程中直接操作UI, 以免引起资源冲突导致Segmentation Fault
  • 使用Queue类可以初步实现子线程与UI线程更新界面的通信. Signal方面, 我实例了一个QTimer, 每隔一定时间处理一次消息队列.
  • QTableView比QTableWidget效率高得多, 尤其是在Win32平台下. 因此应尽量采用 QTableView + QStandardItemModel 的搭配来做Table
Continue reading PyQt作品 – PingTester – 多点Ping测试工具

Linux 命令行调节屏幕到任意分辨率的 Python 脚本

本猫把一台19寸显示器插在本本VGA插口上, 结果在KDE的分辨率管理界面上发现只能最高选择分辨率到1024×768, 甚是不爽! 于是…写了一个脚本, 以后就可以一步做到啦!
此外: 运行此脚本后, KDE的分辨率管理列表中也会出现运行此脚本的时候指定的分辨率哟!(即使是显示器不支持的)
当然啦, 显卡不支持的分辨率是不会设置成功的…

实现的功能比较简单, 但是很方便, 希望对你也有用~

使用方法:

resolution <设备名> <分辨率> [刷新率]

使用示例:

resolution VGA1 1366x768 60
resolution LVDS1 1280x800

Changelog:
2011-4-29 发布第一个版本

下面…就是脚本啦!

Continue reading Linux 命令行调节屏幕到任意分辨率的 Python 脚本
QR Code Business Card