2007年10月15日月曜日

FePyのElementTreeモジュール

以前の記事でpyexpat.pyが、DTDの検証を行う設定になっていることを報告しました。この度、FePyのリーダーであるSeo Sanghyeonさんから連絡があり、マージしたそうです。コード自体は、キーワード引数にされたそうです(行数が少なくて済む)。
それ以外にエンコーディングの提案を行っていたのですが、こちらはFePyには盛り込まれませんでした。理由は、CPythonの方がエンコーディングの処理を行っていないためだと思われます。HTMLに関して別として、XMLとしてはエンコーディングをやった方が標準の実装として正しいのではないかと私は感じますが、互換性という観点からは仕方がないですね。

2007年5月21日月曜日

FePy R5がリリースされています

更新がご無沙汰していますが、表題の通りFePy r5がリリースされました。
http://sourceforge.net/projects/fepy/
また4月にIronPython1.1がリリースされているのですが、FePy r5ではIronPython1.1に対応したとありました。
IronPython関係では、2.0アルファのリリースとASP.NET Featurs(IronPython for ASP.NETの後継)、SilverLight1.1アルファのリリースが行われています。この3種類とも、DLR(Dynamic Language Runtime)を利用するように開発されています。中でも、SilverLight1.1を使うと、XAMLのコードビハインドにpyとjsxを指定することで、IronPythonからJavaScriptの関数を呼び出したりできます。これがDLRを使った動的言語の相互運用と言えるものでしょう。

忙しいもので、更新が滞りがちですので、ご容赦をお願いします。
最近も忙しいです。

2007年3月21日水曜日

書籍「IronPythonの世界」


「IronPythonの世界」という書籍が、ソフトバンククリエイティブメディアから出版されます。
時間がかかりましたし、家族にも迷惑をかけました。家族の協力がなかったら、きっと完成しませんでした。
(注)何故か、画像の色がおかしい。まあ、いいや。

2007年1月30日火曜日

Visual Studio 2005 SP1 がインストールできない

Windows Server 2003 SP1(worlgroup)でVisaul Studio 2005を使っていて、サービスパック1をインストールshなきゃと思って、インストールを始めました。
リリースノートを読んで、エラーが出るのかな(?)と思いつつ、試したらエラーがでました。 Windows Server 2003のインストールでは、「エラー 1718。ファイルFilenameはデジタル署名ポリシーによって拒否されました」との内容がリリースノートに記載されていたのですが、発生したエラーは「インストールリソース(あやふや)にアクセスできません」というものです。
エラー番号が出てないので、「?」と思って色々と試したのですが、同じエラーが発生してインストールできません。仕方がないのでアンインストールしてからインストールして、再度SP1をインストールしても一緒でした。リリースノートに書かれていた、対策を読んでローカルセキュリティポリシーを設定しようとしたら、ソフトウェア制限ポリシーに「実行」 というポリシーが無いんですね。
ふーむと考えながら、該当するのはどのポリシーかなと思いながら「強制」を選択し、ローカルの管理者を除くすべてのユーザーに設定しました。
この状態でSP1をインストールしたら、動き出しました。で、色々と調べていったらイベントビューアに「エラー1718」が出力されていました。あっ、と思ったけど。
Windows XPやVistaにインストールするときは、ここまで手間がかからなかったから、気がつきませんでした。
やっぱし、Serverに開発製品をインストールするもんじゃないですね。

2007年1月28日日曜日

FePyのElementPathモジュール

IronPythonでFePyのElementTreeモジュールを使用するときにXPathがうまく使えないということを以前に説明しました。これは正規表現のreモジュールの実装の違いであると説明しました。
具体的には、ElementPathはXPathを分解するのにre.filndall()メソッドを使ってリストの要素としてタプルが含まれていることを期待しているのですが、IronPython 1.0.1のre.findall()メソッドはリストのみを返すためにエラーとなります。
この現象を回避するために、ElementPathモジュールをxpath_tokenizerをIPCEかIronPythonかで正規表現の扱いかたを変更する必要があります。それには、以下のようにElementPathを書き換える必要があります。


##

# Deference IronPython-1.0.1 and IPCE-r5

# IPCE-r5 re.findall return a list includes tuple

# IP-1.0.1 re.findall return a list only

## 正規表現をコンパイルします

_token_pattern = re.compile(

"(::|\.\.|\(\)|[/.*:\[\]\(\)@=])|((?:\{[^}]+\})?[^/:\[\]\(\)@=\s]+)|\s+")

# When use IPY 1.0.1, return True

# IronPython 1.0.1 の正規表現かどうかをテストします

def _check_ipy():

ret = _token_pattern.findall("/root/test")

itm = ret[0]

if isinstance(itm, tuple): return False

return True

## for IPY 1.0.1 Only

## IronPython 1.0.1の場合に呼び出される関数を定義します

def _ipy_tokennizer(xpath):

return [m.groups() for m in _token_pattern.finditer(xpath)]



xpath_tokenizer = _token_pattern.findall

## IronPython 1.0.1なら呼び出し先を関数に変更します

if _check_ipy():

xpath_tokenizer = _ipy_tokennizer

##

# original code 以下は、元のオリジナルなコードです

#xpath_tokenizer = re.compile(

# "(::|\.\.|\(\)|[/.*:\[\]\(\)@=])|((?:\{[^}]+\})?[^/:\[\]\(\)@=\s]+)|\s+"

# ).findall

##

上記のように変更すれば、IronPython 1.0.1でもXPath式が利用できるようです。

2007年1月23日火曜日

FePyのElementtreeモジュール 3

既に見つけていたpyexapt.pyですが、これに手を加えてXMLであればXML宣言のencodingを使い、HTMLであればmetaのcharsetを使って、ユニコードに変換するように書き加えてみました。 そのソースコードを以下に示します。




# Copyright (c) 2005, 2006 Seo Sanghyeon



# A chapter from Dan Wahlin's "XML for ASP.NET Developers" is useful

# for understanding this code. Posted to informit.com, 2002-02-22.

# http://www.informit.com/articles/article.asp?p=25485



# 2005-11-16 sanxiyn Created

# 2006-08-18 sanxiyn Merged changes from Mark Rees

# * Adapted to the new way to load .NET libraries

# * Handle empty elements

# 2006-08-29 sanxiyn Added support for XML namespaces

# Simplified code a lot

# 2006-10-21 sanxiyn Minimal support for xml.sax

# 2006-10-24 sanxiyn Implemented ordered_attributes, namespace_prefixes

# 2006-10-27 sanxiyn Added expat.error

# 2006-10-29 sanxiyn Implemented Start/End NamespaceDeclHandler

# 2006-11-20 sanxiyn Merged changes from Fredrik Lundh

# * Handle multiple calls to Parse()



import clr

clr.AddReference("System.Xml")



from System import Enum

from System.IO import StringReader

from System.Xml import XmlReader, XmlNodeType



# xml.sax passes an undocumented keyword argument "intern" to ParserCreate.

# Let's ignore it.



def ParserCreate(encoding=None, namespace_separator=None, **kw):

return xmlparser(namespace_separator)



# Used by xml.sax

XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE = 1

# Used by Kid

XML_PARAM_ENTITY_PARSING_ALWAYS = 2



class error(Exception):

pass



class xmlparser(object):



__slots__ = [

# Internal

"_data",

"_separator",

"_reader",

"_ns_stack",

"_enc",



# Attributes

# Implemented

"ordered_attributes",

"namespace_prefixes",

# Stub for xml.dom

"buffer_text",

"specified_attributes",



# Handlers

# Implemented

"StartElementHandler",

"EndElementHandler",

"CharacterDataHandler",

"StartNamespaceDeclHandler",

"EndNamespaceDeclHandler",

# Stub for ElementTree

"DefaultHandlerExpand",

# Stub for xml.sax

"ProcessingInstructionHandler",

"UnparsedEntityDeclHandler",

"NotationDeclHandler",

"ExternalEntityRefHandler",

# Stub for xml.dom

"StartDoctypeDeclHandler",

"EntityDeclHandler",

"CommentHandler",

"StartCdataSectionHandler",

"EndCdataSectionHandler",

"XmlDeclHandler",

"ElementDeclHandler",

"AttlistDeclHandler",

# Stub for Kid

"DefaultHandler",

]



returns_unicode = True

intern = {}



def __init__(self, separator):

self._data = []

self._separator = separator

self._ns_stack = []

self.ordered_attributes = False

self.namespace_prefixes = False

from System.Text import Encoding

self._enc = Encoding.ASCII.WebName #デフォルトエンコーディング



def Parse(self, data, isfinal=False):

self._data.append(data)

if isfinal:

data = "".join(self._data)

self._data = None

self._parse(data)



def _qname(self):

separator = self._separator

reader = self._reader

if separator is None:

# Name convert unicode

return self._handle_unicodechar(reader.Name)

#return reader.Name

if reader.NamespaceURI:

# qname covert unicode

temp = reader.NamespaceURI + separator + self._handle_unicodechar(reader.LocalName)

#temp = reader.NamespaceURI + separator + reader.LocalName

if self.namespace_prefixes:

if reader.Prefix:

return temp + separator + reader.Prefix

else:

return temp

else:

# qname convert unicode

return self._handle_unicodechar(reader.LocalName)

#return reader.LocalName



def _parse(self, data):

#パッチ XmlReaderSettingでDTDの検証をしないに追加

from System.Xml import XmlReaderSettings

readersettings = XmlReaderSettings()

readersettings.ProhibitDtd = False

reader = XmlReader.Create(StringReader(data), readersettings)

self._reader = reader

while reader.Read():

nodetype = reader.NodeType

typename = Enum.GetName(XmlNodeType, nodetype)

# meta tag case in html, then call meta handler( get chareset) charsetを処理するハンドラの設定

if reader.Name.lower() == "meta":

typename = "meta"

handler = getattr(self, '_handle_' + typename, None)

if handler is not None:

handler()



def _handle_Element(self):

reader = self._reader

name = self._qname()

ns_stack = self._ns_stack

ns_stack.append(None)

if self.ordered_attributes:

attributes = []

else:

attributes = {}

while reader.MoveToNextAttribute():

if reader.Prefix == 'xmlns':

# LocalName convert unicode

prefix = self._handle_unicodechar(reader.LocalName)

#prefix = reader.LocalName

uri = reader.Value

ns_stack.append(prefix)

if hasattr(self, "StartNamespaceDeclHandler"):

self.StartNamespaceDeclHandler(prefix, uri)

continue

key = self._qname()

# attribute value convert unicode

value = self._handle_unicodechar(reader.Value)

#value = reader.Value

if self.ordered_attributes:

attributes.append(key)

attributes.append(value)

else:

attributes[key] = value

reader.MoveToElement()

if hasattr(self, "StartElementHandler"):

self.StartElementHandler(name, attributes)

# EndElement node is not generated for empty elements.

# Call its handler here.

if reader.IsEmptyElement:

self._handle_EndElement()



def _handle_EndElement(self):

name = self._qname()

if hasattr(self, "EndElementHandler"):

self.EndElementHandler(name)

ns_stack = self._ns_stack

while True:

prefix = ns_stack.pop()

if prefix is None:

break

if hasattr(self, "EndNamespaceDeclHandler"):

self.EndNamespaceDeclHandler(prefix)



def _handle_Text(self):

reader = self._reader

# text convert unicode

data = self._handle_unicodechar(reader.Value)

#data = reader.Value

if hasattr(self, "CharacterDataHandler"):

self.CharacterDataHandler(data)



# handle encoding from XmlDeclation XML宣言からencodingを取得

def _handle_XmlDeclaration(self):

enc = self._reader.GetAttribute("encoding")

if enc == "" :

return

self._enc = enc



# hadle charset from meta tag in html metaタグからcharsetを取得

def _handle_meta(self):

content = self._reader.GetAttribute("content")

if content == "":

return

content = content.lower()

i = content.find("charset=")

if i == -1:

return

chardata = "".join([content[j] for j in range(i + 8, len(content))])

charset = chardata.split(";")[0].strip()

self._enc = charset



# ユニコードに変換

def _handle_unicodechar(self, data):

if self._enc == "":

return data

try:

return unicode(data, self._enc)

except:

return data



# Stub for xml.sax

def SetBase(self, base):

pass

def SetParamEntityParsing(self, flag):

return True

# Stub for Kid

def UseForeignDTD(self):

pass

2007年1月21日日曜日

IronPythonとElementTreeモジュール 2

FePyプロジェクトで提供しているElementTreeモジュールがIronPython 1.0.1でも使えることを前回に説明しましたが、日本語などの文字列をデコードしてくれないのはなぁと思っていて、いろいろと調べました。結局、pyexpat.pyの_parseメソッドには読み込み済みのデータが流れているので、ElementTreeモジュールを何とかしないといけないんです。
ElementTreeのコンストラクタを見るとopen関数でファイルを開いて、XMLの場合はXMLTreeBuilderモジュールで処理をしていました。ああ、open関数か...これを使っているとファイルのエンコードを判別してくれないんですね。さて、どうしたものでしょうねぇ。
XMLだけなら方法は無くないけど、HTMLとかも考えると。うーん。どうしたもんだろう。

結局はpyespat.pyの中でXML宣言のエンコーディングを取り出して、タグ名やらアトリビュート名、値を愚直にunicode関数で変換しました。encoding指定がない時は、デフォルトをUTF-8指定にしています。ネームスペースとかに日本語などを使わない限りは、大丈夫でしょう。
FePyプロジェクトがpyespat.pyモジュールを作成した理由は、CPythonだとxmlparserが提供されるけど、IronPythonでは提供されないという理由からのようです。