2009-09-06

My Python Gochas: Non UTF Encoding support does not always work

    As claimed by a lot of tutorials, Python is well known to be a very good platform for manipulating XML. Yeah it's true, however there is a trap waiting for you.

Comparing with other XML APIs, such as MSXML or IBM's Xcerces-C, the default Python XML APIs (xml.dom, etree, or even PyXML) does not provide full supports for multiple encodings. For example, if you comes from China, you might want to encode your document in GB2312/GBK or BIG5. However, you might receive some information like below when you are parsing it (tried on Python 2.6.2) :

<?xml version="1.0" encoding="GB2312"?>
<hello>world</hello>

import xml.dom.minidom
xml.dom.minidom.parse('test.gbk.xml')
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python2.6/xml/dom/minidom.py", line 1918, in parse
return expatbuilder.parse(file)
File "/usr/lib/python2.6/xml/dom/expatbuilder.py", line 924, in parse
result = builder.parseFile(fp)
File "/usr/lib/python2.6/xml/dom/expatbuilder.py", line 207, in parseFile
parser.Parse(buffer, 0)
xml.parsers.expat.ExpatError: unknown encoding: line 1, column 30

It's confusing to most of the Python newbies because the document above is actually a valid XML document. It's also surprising because python itself can correctly handle GB2312 since python 1.6.

But my friend, don't just start complaining: Python does nothing wrong.

The strange behavor comes from the library. Python uses Expat as the implementation of almost all XML libraries. Currently Expat supports very limited encodings: US-ASCII,  UTF-8,  UTF-16, and ISO-8859-1 (If you are working on Linux/UNIX, check man page of xmlwf tool). However, it is compliant with XML standard, section 2.2:

The mechanism for encoding character code points into bit patterns may vary from entity to entity. All XML processors MUST accept the UTF-8 and UTF-16 encodings of Unicode [Unicode]; the mechanisms for signaling which of the two is in use, or for bringing other encodings into play, are discussed later, in 4.3.3 Character Encoding in Entities.

As we see, XML standard requires only supports on UTF-8 and UTF-16, and XML parsers can freely make their decisions to support other encodings. Expat supports four encodings, that's all.

So, if you want to use XML with Python, please think about it carefully when choosing your default encodings. If there are requirements to support "any" encodings, don't just use Python blindly.

2009-09-04

GoTask:工作模型(二) 查询和任务管理

上一篇里我说过,GoTask的三个要素是:提醒,分类和查询。但问题是:这三者是所有的时间管理软件都必须有的,那么GoTask和我们很熟悉的Outlook或者iCal有什么区别呢?

GoTask设计的主要目的是为了支持长期任务管理,而在我看来Outlook和iCal更适合管理短期任务。所以要回答这个问题,我们不妨先看看短期任务和长期任务的差异。

在我的经验中,多数情况下短期任务往往以小时为单位计算,这一点相信每一个Outlook用户都会很熟悉:当需要定义一个会议邀请时,Outlook总是以半小时为默认单位指定时间,而一个任务一般最长也就是全天,即所谓的All Day Event。但是Outlook并不擅长显示或处理持续时间超过一天的任务——或许重复任务(Recursive Tasks)算是一种,但其实际上是可以被分解的短期任务集合。在工作中我发现,我的同事们很少会在Outlook中大量定义跨天任务。

显然,两种任务在时间安排上的差异首先会影响任务提醒功能。Outlook中的提醒虽然选择很多,但其主要目的都是通知用户某个任务将要开始。有人也许会注意到在任务开始之后Outlook实际上无法自定义提醒的频度,那是因为它假设任务开始之后总是能在短期内完成,除非用户忘记,否则没有必要多次提醒。但长期任务则不同,它常常需要对用户做多次提醒。这主要是为了便于划分任务阶段,同时也是为了能定期确定进度。如果在Outlook中要做这件事,那就得定义多个项目,然后每个项目定义单独的提醒时机。

另一个问题是,我认为每个任务单独定义提醒频率的方案不适合长期任务。因为长期任务需要的提醒时机较多,如果每个任务都必须分别制定多个提醒时间,用起来比较麻烦;而且考虑到长期任务时间安排往往较灵活,我也没有必要将提醒定得过于死板。我的习惯是每周分两次检查任务列表,同时调整接下来几天的安排。所以长期任务的提醒方式最好是能统一定义的,比如要求提醒我所有“开始日期到今天大于十天,并且尚未完成的任务”。每次系统只需要查阅数据库里所有的项目,然后找到满足要求的就可以了。


那么现在问题就变成了:我怎么才能定义这样的提醒?我的设想是通过查询来完成。换句话说,我希望提醒规则是定义于每个查询,而非每个任务中。例如,假定一个任务需要持续四个星期,而我需要第一和第三个星期提醒一次,那么我可以设定两个查询:
  1. 查找每个已经开始,并且起始日期在一周前的任务。
  2. 查找每个已经开始,并且起始日期在三周前的任务。
这样随着时间推移,我就能得到两次提醒。同样地,这个规则还可以被应用到所有其他的任务去。这种做法的好处是:如果我需要修正提醒的时机或频率,只需修改查询的定义,而后所有工作任务的提醒规则都能相应地更新。在长期任务数量巨大的情况下,这种组织方式比Outlook的方案更容易管理。

这种设计在程序上还有一个好处:每个查询结果可以被再次利用。我可以用查询来构造一个简单的统计系统。比如我想知道过去两个星期里所有完成的任务占总任务的比例,我需要定义两个查询表达式,在得到查询的结果后做一个简单的百分比,就能得到我需要的数据。

最后,在我的设想中这种设计也应当能够在一定程度上处理短期任务,但需要一个前提:GoTask的时间计量必须支持到小时或分钟。这样我就能通过定义一个检查“开始时间在当前15分钟之后”的查询来模拟Outlook的行为了。


现在我们还剩下最后一个问题:分类。我将它留到下一篇中讨论。