チュウトリアルについての独りよがりな無駄話
初回公開:2019/01/20
最終更新:未
「チュウトリアル: Python MarkdownのExtensionを記述する」についての補足を。
【 目次 】
Extensionを作るための関連記事など
Extensions APIのリファレンス,公式ドキュメント。
それを当サイトで翻訳したのが
Extensions APIのリファレンスだけではわかりずらいので、
チュウトリアルのドキュメントも,
このチュウトリアルを当サイトで翻訳したのが
上記のチュウトリアルと同じような内容が他にも
- Python-Markdown Tutorial Part 1: Adding InlinePatterns Insert and Delete - achinghead.com
- Python-Markdown Tutorial Part 2: Changing Bold and Italics - achinghead.com
上記の参考サイトの中でもおもにチュウトリアルの内容についてさらに疑問な点がでてきて...
そこで独りよがりな補足を加えてみることにした。
strongとかemタグというのは
この記事に出てくるstrongとかemタグというのは
html strong bold 違い
チュウトリアルで実装する機能
txt2tagsというマークアップ言語というかソフトウェアーがあって、そのtxt2tagsでサポートされているインライン構文の以下のようなサブセットの機能を実装を試みているようだ。
- ストライキ・2つのハイフン:--del--=><del>del</del>=>
del - アンダーライン・2つの下線:__ins__=><ins>ins</ins>=>ins
- 太字・2つのアスタリスク:**strong**=><strong>strong</strong>=>strong
- イタリック体・2つのスラッシュ://emphasis//=><em>emphasis</em>=>emphasis
txt2tags
本題から離れるが、txt2tagsというマークアップ言語というかソフトウェアーがある。
txt2tagsはmarkdownと同様にテキストをhtmlに変換するツールのようで、
英語版のWikipediaによると
- txt2tags - Wikipedia
txt2tags is a document generator software that uses a lightweight markup language. txt2tags is free software under GNU General Public License
txt2tagsはhtml形式だけでなくいろいろなドキュメント形式に対応しているようで
-
テキストから各種ドキュメントへ変換する·txt2tags MOONGIFT
今回紹介するオープンソース・ソフトウェアはtxt2tags、一つのテキストフォーマットから各種文書形式に変換するソフトウェアだ。
txt2tagsはすでに7年も開発が行われているソフトウェアで、テキスト文書から各種ドキュメントに変換する機能がある。 -
txt2tags は nvda で HTML ドキュメントの生成に使われているツール。
NVDAというのは
- NVDA [ja.nishimotz.com]
NVDA はオープンソースの Windows 対応スクリーンリーダーである。
さらに、スクリーンリーダーというのは
- スクリーンリーダー - Wikipedia
スクリーンリーダー(英語: screen reader)とは、コンピュータの画面読み上げソフトウェアである。視覚障害者がパーソナルコンピュータを操作するために、視覚的に使うことが必要であるマウスに変わり、情報を音声で読み上げることによって、操作を補助するアクセシビリティである。
txt2tagsの使い方については
ボイラープレートコード(Boilerplate Code)って何?
ボイラープレートコード(Boilerplate Code)というのがあって
- ボイラープレート - boilerplate | プログラマメモ2
ボイラープレートとは、《お決まりのソースコード断片》とかそんな意味のようです。
extensionsを記述するためのお決まりのコードの事みたい。
markdownとは関係無いがJavaのコードって冗長な部分が多いと思う。
セッター,ゲッターは典型的な例で、rubyだとかC#ならもっと綺麗に書けるし、pythonにはクラスのメンバーをセッター,ゲッターでくるむなんて慣習はないし。
Javaって他にも、やたらとインターフェースを多用したり退屈なとコーディングが多いと思っている私は異端?
「HTML5 Boilerplate」なんてゆうのも
- HTML5 Boilerplate を使用して Web 開発を容易に始める
- HTML5のサイトが素早く作れるフレームワーク「HTML5 Boilerplate」をちゃんと触ってみた - WEBCRE8.jp
汎用パターンクラスSimpleTextPatternでの正規表現の使われ方
オカレンスとは
正規表現のグループ化
正規表現のグループ化というのは
貪欲なマッチと貪欲でないマッチ
貪欲なマッチは最長一致,,そして貪欲でないマッチとは最短一致。
欲張りなマッチと控え目なマッチとも言うのかな
また、貪欲でないマッチは非貪欲なマッチとも呼ばれる。
最長一致,最短一致は
group(3)
に何故テキストコンテンツが含まれるのか?
チュウトリアルでは--del--
構文で<del>
タグでテキストをラップするための正規表現が以下のように定義されている。
DEL_RE = r'(--)(.*?)--'
正規表現のgroup(3)
にテキストコンテンツが含まれるとあるが、この正規表現だけをみると奇妙なことに感じられる。
単純に考えれば、group(1)
には最初のカッコでくくられた--
が,そしてgroup(2)
にテキストコンテンツがはいるはずである。
- Pythonで個人的によく使う正規表現モジュールの機能 - Qiita
print(m.group(0)) #マッチした箇所全体(m.group()と等価?)
print(m.group(1)) #1番目のグループ
print(m.group(2)) #2番目のグループ
print(m.groups()) #グループ全体をタプルで
実際に使われる正規表現は、Python-marakdowwnのソースコード(inlinepatterns.py)をのぞいてみると、
SimpleTextPatternの親クラスであるPatternクラスのコンストラクタ__init__メソッドのなかで次のように記述されている。
self.compiled_re = re.compile(r"^(.*?)%s(.*)$" % pattern, re.DOTALL | re.UNICODE)
つまり、正規表現文字列DEL_REはそのまま使われるのでは無く、前後に別の正規表現が追加されるため結果的にgroup(3)
にテキストコンテンツが含まれる事になる。
'>not_strong'は何を意味するか
チュウトリアルにおけるMyExtensionのコードでは
# delタグPatternオブジェクトをmarkdownパーサーに追加 md.inlinePatterns.add('del', del_tag, '>not_strong')
この、inlinePatternsのaddメソッドの3つ目の引数'>not_strong'は何を意味するのであろうか?
extendMarkdownメソッドの引数mdはMarkdownクラスのインスタンスでありinlinePatternsメンバーを持っている。
inlinePatternsはOrderedDictクラスのインスタンスで順序付きの辞書オブジェクトとして機能する。
そして、OrderedDictsクラスのaddメソッドの3つ目の引数'>not_strong'は何を意味しているかというと、not_strongキーの要素の後にdel_tagを挿入しなさいという意味になる。
Markdown記法をhtmlタグに変換する際に、どのMarkdown記法から順番に処理をするかによって結果が変わってしまう為、処理の順番の指定が必要になるという事になる。
以下のようなMyExtensionクラスとそれを利用するモジュールを作成して
from markdown.extensions import Extension from markdown.inlinepatterns import SimpleTagPattern DEL_RE = r'(--)(.*?)--' class MyExtension(Extension): def extendMarkdown(self, md, md_globals): del_tag = SimpleTagPattern(DEL_RE, 'del') md.inlinePatterns.add('del', del_tag, '>not_strong') import pprint pprint.pprint(md.inlinePatterns.items()) def makeExtension(*args, **kwargs): return MyExtension(*args, **kwargs)
import markdown from my_extention2 import MyExtension print markdown.markdown('foo bar', extensions=[MyExtension()])
inlinePatternsがどのような順番で登録されているかを確認すると
実行結果
[(u'backtick', <markdown.inlinepatterns.BacktickPattern object at 0x02A8D190>), (u'escape', <markdown.inlinepatterns.EscapePattern object at 0x02A8D1B0>), (u'reference', <markdown.inlinepatterns.ReferencePattern object at 0x02A8D1D0>), (u'link', <markdown.inlinepatterns.LinkPattern object at 0x02A8D1F0>), (u'image_link', <markdown.inlinepatterns.ImagePattern object at 0x02A8D210>), (u'image_reference', <markdown.inlinepatterns.ImageReferencePattern object at 0x02A8D230>), (u'short_reference', <markdown.inlinepatterns.ReferencePattern object at 0x02A8D250>), (u'autolink', <markdown.inlinepatterns.AutolinkPattern object at 0x02A8D270>), (u'automail', <markdown.inlinepatterns.AutomailPattern object at 0x02A8D290>), (u'linebreak', <markdown.inlinepatterns.SubstituteTagPattern object at 0x02A8D2B0>), (u'html', <markdown.inlinepatterns.HtmlPattern object at 0x02A8D2F0>), (u'entity', <markdown.inlinepatterns.HtmlPattern object at 0x02A8D310>), (u'not_strong', <markdown.inlinepatterns.SimpleTextPattern object at 0x02A8D330>), ('del', <markdown.inlinepatterns.SimpleTagPattern object at 0x02A8D4D0>), (u'em_strong', <markdown.inlinepatterns.DoubleTagPattern object at 0x02A8D350>), (u'strong_em', <markdown.inlinepatterns.DoubleTagPattern object at 0x02A8D370>), (u'strong', <markdown.inlinepatterns.SimpleTagPattern object at 0x02A8D390>), (u'emphasis', <markdown.inlinepatterns.SimpleTagPattern object at 0x02A8D3B0>), (u'emphasis2', <markdown.inlinepatterns.SimpleTagPattern object at 0x02A8D3D0>)] <p>foo bar</p>
delキーがnot_strongキーの後に挿入されているのがわかる。
つまり、marakdown記法がこの順に処理をされ、
marakdown記法の優先順位が決定されることになる。
OrderedDictはmarkdownパッケージに含まれているが
markdownパッケージだけでなくpython2.7や
python 3.6でも
使う事ができる。
Extensionを文字列として指定 - ファイルmdx_xxxについて
名前によるExtensionモジュールの呼び出しにおいて、Extensionモジュールが存在しない場合当然エラーになるのだが
my_extention_test_byname.py
import markdown print markdown.markdown('foo bar', extensions=["myextention"])
その時に表示されるエラーメッセージは
実行結果
ImportError: Failed loading extension 'myextention' from 'myextention', 'markdown.extensions.myextention' or 'mdx_myextention'
どうやら先頭にmdx_の付くExtensionモジュールを探しているようである。
試しに、mdx_myextention.pyモジュールにExtensionクラスを定義してみると正常に動作する。
試しに、mdx_myextention.py
from markdown.extensions import Extension class MyExtension(Extension): def extendMarkdown(self, md, md_globals): # ここにmarkdownの動作を実装 print "mdx_myextention.MyExtension" def makeExtension(*args, **kwargs): return MyExtension(*args, **kwargs)