Planet をプチ修正

突然 Planet で収集・再構築したフィードが XML の構文エラーでパースできない事態が発生。
問題の行を1文字ずつダンプして調べてみたところ、0x03 (ETX/テキスト終了) が紛れ込んでいました。UTF-8 としては ETX を含んでいても問題無いが、well-formed な XML としてはダメらしいのですね。
そこで TAB,CR,LF 以外の制御文字を削除するように planet/cache.py を修正。(差分には EUC/SJIS 対応も含んでいます)
ETX を含んでいたフィードのキャッシュを削除した後、planet.py を再実行すると、問題なくパースできるようになりました。

diff -ur planet.orig/planet/cache.py planet/planet/cache.py
--- planet.orig/planet/cache.py
+++ planet/planet/cache.py
@@ -19,6 +19,7 @@
 re_slash         = re.compile(r'[?/]+')
 re_initial_cruft = re.compile(r'^[,.]*')
 re_final_cruft   = re.compile(r'[,.]*$')
+re_ctrl_chars    = re.compile(r'[\x00-\x08\x0b\x0c\x0e-\x1f]')
 
 
 class CachedInfo:
@@ -295,12 +296,18 @@
 def utf8(value):
     """Return the value as a UTF-8 string."""
     if type(value) == type(u''):
-        return value.encode("utf-8")
+        return re_ctrl_chars.sub("", value.encode("utf-8"))
     else:
         try:
-            return unicode(value, "utf-8").encode("utf-8")
+            return re_ctrl_chars.sub("", unicode(value, "utf-8").encode("utf-8"))
         except UnicodeError:
             try:
-                return unicode(value, "iso-8859-1").encode("utf-8")
+                return re_ctrl_chars.sub("", unicode(value, "euc-jp").encode("utf-8"))
             except UnicodeError:
-                return unicode(value, "ascii", "replace").encode("utf-8")
+                try:
+                    return re_ctrl_chars.sub("", unicode(value, "cp932").encode("utf-8"))
+                except UnicodeError:
+                    try:
+                        return re_ctrl_chars.sub("", unicode(value, "iso-8859-1").encode("utf-8"))
+                    except UnicodeError:
+                        return re_ctrl_chars.sub("", unicode(value, "ascii", "replace").encode("utf-8"))


あと、planet の設定とフィード一覧の分離は以下のような、安直な方法で。
list.ini を編集する Web インターフェースも PHP で作っていたりするのですが、それはまた別の機会にでも。

diff -ur planet.orig/planet.py planet/planet.py
--- planet.orig/planet.py
+++ planet/planet.py
@@ -22,10 +22,11 @@
 
 import planet
 
-from ConfigParser import ConfigParser
+from ConfigParser import SafeConfigParser as ConfigParser
 
 # Default configuration file path
 CONFIG_FILE = "config.ini"
+LIST_FILE = "list.ini"
 
 # Defaults for the [Planet] config section
 PLANET_NAME = "Unconfigured Planet"
@@ -93,6 +94,15 @@
     feed_timeout   = config_get(config, "Planet", "feed_timeout", FEED_TIMEOUT)
     template_files = config_get(config, "Planet", "template_files",
                                 TEMPLATE_FILES).split(" ")
+
+    # Read the feed list and append each item to the configuration
+    config2 = ConfigParser()
+    config2.read(LIST_FILE)
+    for site in config2.sections():
+        if config2.has_option(site, "name"):
+            if not config.has_section(site):
+                config.add_section(site)
+            config.set(site, "name", config2.get(site, "name"))
 
     # Default feed to the first feed for which there is a template
     if not planet_feed: