HTMLクラスの中身

私は、PerlCGIモジュールにならったHTMLを取り扱うクラスを作り、それを使ってCGIを書いています。例えば次のように書くと、

import html

sitetitle = u'サイト名'
ht = html.HTML(encode=u'utf-8', lang=u'ja', sitetitle=sitetitle)

ht.printe(ht.p(ht.a(attrs={u'href': u'./example.html'},
                    content=u'例えばのリンク')))

こんな風に出力してくれます。

<p><a href="./example.html">例えばのリンク</a></p>

これが本当に便利なのかはわからないけれど、私にとっては慣れたやりかたなので、当座、これでいこうと思っています。

このp()メソッドやa()メソッドは、内部で_create_element()メソッドを呼び出しているだけです。

    def p(self, content, attrs=None):
        return self._create_element(u'p', content, attrs)

    def a(self, content, attrs=None):
        return self._create_element(u'a', content, attrs)

で、この_create_element()メソッドは、内部で_create_start_tag()メソッドと_create_end_tag()メソッドを使っています。この_create_start_tag()が生成するHTMLの開始タグ内の属性値を、cgi.escape()を使ってエスケープするようにしました。というか、当然最初からやっとくべきことなんだけど、わかっていながら放ってました。

タグの生成を、こうしてメソッド(関数)を使うようにしておくと、修正箇所が少なくて便利ですね。というか、ここだけ修正すればいいってわかってたから、ずっと放っておいたともいえます。

エレメントやタグを作るメソッドには、_create_empty_element()メソッドというのもあって、これは空要素を作ります。これの属性値もエスケープするようにしたので、いちいちエスケープしてから投げる必要がなくなりました。

エレメントの中身については、HTMLが入れ子になることもあるので、ユーザーが入力した値など、エスケープしておきたい、あるいはしなければならない文字列は、自分でやっておかないといけません。このへんは使う際に意識しておかないと、ごっちゃになりそうなので、一応メモとして残しておきます。

以下に_createなんたらのメソッドの中身を書いておきます。しかし、私の書くのはいちいちまどろっこしい、スマートさに欠けるものです。うまい人が書いたら、もっとコンパクトになるんでしょうね。

    def _create_start_tag(self, elemname, attrs=None):
        starttag = u'<' + elemname
        if isinstance(attrs, dict):
            for attrname in (attrs.keys()):
                if attrs[attrname] is not None:
                    attrvalue = attrs[attrname]
                    if isinstance(attrvalue, int):
                        attrvalue = str(attrvalue)
                    starttag = starttag + u' ' + cgi.escape(attrname, True) + \
                               u'="' + cgi.escape(attrvalue, True) + u'"'
        starttag = starttag + u'>'
        return starttag

    def _create_end_tag(self, elemname):
        endtag = u'</' + elemname + u'>'
        return endtag

    def _create_element(self, elemname, content, attrs=None):
        starttag = self._create_start_tag(elemname, attrs)
        endtag = self._create_end_tag(elemname)
        if isinstance(content, int):
            content = str(content)
        if isinstance(content, basestring):
            return starttag + content + endtag
        else:
            raise TypeError(u'need string or int, got %r' % \
                            type(content))
        
    def _create_empty_element(self, elemname, attrs=None):
        tag = u'<' + elemname

        if isinstance(attrs, dict):
            for attrname in (attrs.keys()):
                if attrs[attrname] is not None:
                    attrvalue = attrs[attrname]
                    if isinstance(attrvalue, int):
                        attrvalue = str(attrvalue)
                    tag = tag + u' ' + cgi.escape(attrname, True) + u'="' + \
                          cgi.escape(attrvalue, True) + u'"'
        tag = tag + u' />'
        return tag