Changed mt2rest to hatena2rest

I released mt2rest at 30 March that is tool to convert(GEEK DAY TOKYOに参加しました。). It is to convert Hatena Diary exported into reST format. I had migrated from Hatena Diary to Tinkerer. But it was incomplete conversion with this tool, because I made improvised for that time event.

I rewrote this tool with using Hatena diary XML format instead of MovableType format as input. The two reason is as follows. Firstly it is too hard to distinguish the notation of Hatena Diary written in the code block to separate the entries of daily correctly. Secondary it is also hard to parse distinguish the code of blog-parts when I use the MovableType format as input file. I renamed a project name “hatena2rest. It is now not use MovableType format why “mt” of mt2rest is Abbreviation of “MovableType”.

Use this tool if you will migrate tinkerer from Hatena Diary. Usage is written in README.

I make a note that I had a hard time.

Detection of half-width characters and full-with characters

String must has border line as “=” * character length at section, subsection and simple table of Sphinx. Single-byte character is simple, so character length of border equals character length of string. String encoded with unicode is also the same.

>>> len(r"single")
>>> len(u"single")

But double-byte character is complicated. Character length of string is different with encoded with unicode and raw.

>>> len(r"全角")
>>> len(u"全角")

“Hankaku kana” is below

>>> len(r"テスト")
>>> len(u"テスト")

“Zenkaku” must be two or more characters, “Hankaku” must be at least one character when Sphinx border character. It is hard to detect with len(). So I used unixcodedata.east_asian_width().

unicodedata module is embedded with Python. east_asian_width() has next 6 values.

  • F: Fullwidth

  • H: Halfwidth

  • W: Wide

  • Na: Narrow

  • A: Ambiguous

  • N: Neutral

Describe East_Asian_Width properties defined by Unicode Standard Annex #11 (UAX#11).

fig. Describe East_Asian_Width properties defined by Unicode Standard Annex #11 (UAX#11).

According to “WikiPedia”, “A” is processed as 1 or 2 characters, but Sphinx processes as like below probably.

  • F: 2 character width

  • H: 1 character width

  • W: 2 character width

  • Na: 1 character width

  • A: 1 character witdh

  • N: 1 character width

Then next code is enable to get width of string.

def length_str(string)
    fwa = ['F', 'W', 'A']
    hnna = ['H', 'N', 'Na']

    if isinstance(string, unicode):
        zenkaku = len([unicodedata.east_asian_width(c)
                       for c in string
                       if unicodedata.east_asian_width(c) in fwa])
        hankaku = len([unicodedata.east_asian_width(c)
                       for c in string
                       if unicodedata.east_asian_width(c) in hnna])
        return (zenkaku * 2 + hankaku)
    elif isinstance(string, str):
        return len(string)

Exception occurs when use “&” in raw directive of html

“&” is disable to use in raw directive of html. Blog parts is converted to html raw directive. Then exception occurs when running build(tinker -b command)..

# Sphinx version: 1.1.3
# Python version: 2.7.3
# Docutils version: 0.8.1 release
# Jinja2 version: 2.6
Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.7/sphinx/", line 189, in main, filenames)
  File "/usr/lib/pymodules/python2.7/sphinx/", line 204, in build
  File "/usr/lib/pymodules/python2.7/sphinx/builders/", line 196, in build_update
    'out of date' % len(to_build))
  File "/usr/lib/pymodules/python2.7/sphinx/builders/", line 255, in build
  File "/usr/lib/pymodules/python2.7/sphinx/builders/", line 433, in finish
    for pagename, context, template in pagelist:
  File "/usr/lib/python2.7/dist-packages/tinkerer/ext/", line 85, in html_collect_pages
    for name, context, template in rss.generate_feed(app):
  File "/usr/lib/python2.7/dist-packages/tinkerer/ext/", line 54, in generate_feed + post[:11])),
  File "/usr/lib/python2.7/dist-packages/tinkerer/ext/", line 91, in patch_links
    doc = xml.dom.minidom.parseString(in_str)
  File "/usr/lib/python2.7/xml/dom/", line 1930, in parseString
    return expatbuilder.parseString(string)
  File "/usr/lib/python2.7/xml/dom/", line 940, in parseString
    return builder.parseString(string)
  File "/usr/lib/python2.7/xml/dom/", line 223, in parseString
    parser.Parse(string, True)
ExpatError: not well-formed (invalid token): line 70, column 363

This problem is solved with escaping to character entity references, but there is no meaning as hyperlink. So I extracted URI as simple hyperlink.


I spent a lot of regular expression.

See also