Python-Markdown extensionを記述する(つたない翻訳)
「Extension API — Python-Markdown 2.6.11 documentation」をつたない翻訳,勝手な解釈。
初回公開:2018/01/13
最終更新:2018/01/14
【 目次 】
Python-Markdownは独自のカスタム機能および/または構文解析をおこなうプラグインのための拡張機能extensionを記述するAPIが含まれています。
ソースがパーサに渡される前にソースを変更できるようにするプリプロセッサ(Preprocessors),インライン要素の構文を追加、削除、またはオーバーライドできるようにするインラインパターン(inline patterns),およびパーサーから返される前にパーサーの出力を取り消すことができるポストプロセッサー(Postprocessors)が含まれています。
真により深く内部に入り込んで処理をおこないたいなら、コアBlockParserの一部であるブロックプロセッサ(Blockprocessors)もあります。
パーサが後にUnicodeテキストとしてレンダリングされるElementTreeオブジェクトを構築する時に、ツリーの操作を容易にするヘルパーもいくつかあります。
APIの各部分については、以下のそれぞれのセクションで説明します。
さらに、いくつかの(利用可能なExtensions
)のソースを読む事も役立つかもしれません。
例えば、Footnotes extension
はここで説明している機能のほとんどを使用しています。
Preprocessors
プリプロセサはマークダウンのコアに渡される前にソーステキストを操作します。
これは、不正な構文を取り除き、そうでなければパーザの処理を妨げる可能性のあるものを抽出し、後で修正するためにそれを保存することさえできる優れた場所です。
プリプロセッサはmarkdown.preprocessors.Preprocessorを継承し、そして、一つの引数lines
をもったrun
メソッドを実装すべきです。
各プリプロセッサのrun
メソッドは、ソーステキスト全体をUnicode文字列のリストとして渡されます。
おのおのの文字列には1行分テキストが含まれます。
run
メソッドは、、そのリスト、もしくは変更されたUnicode文字列のリストのいずれかを返すべきです。
コードのイメージは以下のようになります:
from markdown.preprocessors import Preprocessor class MyPreprocessor(Preprocessor): def run(self, lines): new_lines = [] for line in lines: m = MYREGEX.match(line) if m: # do stuff else: new_lines.append(line) return new_lines
Inline Patterns
Inline Patterns
(インラインパターン)は、*emphasis*
または[links](http://example.com)
のようなMarkdownのためのインラインHTML要素の構文を実装します。
訳者による蛇足
インラインHTML要素とは
パターンオブジェクトはmarkdown.inlinepatterns.Patternまたはその子クラスを継承するクラスのインスタンスである必要があります。
おのおののパターンオブジェクトは、単一の正規表現を使用し、以下のメソッドを持っている必要があります:
-
getCompiledRegExp()
:コンパイル済みの正規表現を返す。
-
handleMatch(m)
:matchオブジェクトを受け入れ、プレーンなUnicode文字列のElementTreeの要素を返します。
getCompiledRegExp
によって返された任意の正規表現はブロック全体をキャプチャする必要がある事に注意してください。
したがって、それらはすべてr'^(.*?)'で始まり、
r'(.*?)!'`で終わるべきです。
Pattern
クラスで提供されているデフォルトのgetCompiledRegExp()
メソッドを使用すると、正規表現を渡すことができ、getCompiledRegExp
は式をラップしてre.DOTALL
とre.UNICODE
フラグを設定します。
これは、m.group(1)
がパターンの前のすべてと一致するため、一致の最初のグループはm.group(2)
になることを意味します。
たとえば、この単純化されたemphasis(強調)パターンを考えてみましょう。
from markdown.inlinepatterns import Pattern from markdown.util import etree class EmphasisPattern(Pattern): def handleMatch(self, m): el = etree.Element('em') el.text = m.group(3) return el
Markdownへのコードの統合で論じるように、このクラスのインスタンスをMarkdownに提供する必要があります。
そのインスタンスは次のように作成されます:
# あまりにも簡単な正規表現 MYPATTERN = r'\*([^*]+)\*' # patternをわたしてインスタンスを作成 emphasis = EmphasisPattern(MYPATTERN)
実際には、そのパターンを作成する必要はありません(単にMarkdownにもっと洗練された強調パターンが存在するという理由だけではなく)。
事実、例のパターンはあまりドライ(無味乾燥)ではないということです。
**strong**
テキストのパターンは、'strong'要素を作成することを除いて、ほぼ同じです。
したがって、Markdownにはいくつかの共通の機能を提供できる汎用パターンクラスがいくつか用意されています。
例えば、emphasisとstrongの両方が以下にリストされているSimpleTagPattern
の別のインスタンスで実装されています。
markdown.inlinepatterns
で見つかったパターンクラスを自由に使用したり拡張したりできます。
汎用のPatternクラス
-
SimpleTextPattern(pattern)
:pattern
のgroup(2)
の単純なテキストを返します。 -
SimpleTagPattern(pattern, tag)
:pattern
のgroup(3)
のtext属性を持つ"tag
"型の要素を返します。tag
はHTML要素の文字列('em')である必要があります。 -
SubstituteTagPattern(pattern, tag)
:子要素やテキストのないタイプの"
tag
"要素(例えばbr
のような)を返します。
Markdownソースには、拡張したり使う事ができる他のPatternクラスもあるでしょう。
ソースを読み、使用できるものがあるかどうかを確認してみてください。
あなたの特定の状況に対するさまざまなアプローチのためのいくつかのアイデアが得られるかもしれません。
訳者による蛇足
Patternクラスのソースは標準のインストール方法でインストールされたwindows,Python27ではC:\Python27\Lib\site-packages\markdown\inlinepatterns.py
にあり、そこにいろいろなPatternクラスが定義されている。
Inline Patterns
クラスのベースクラスとなるPatternクラスより重要とおもわれる部分を抜粋すると
class Pattern(object): ... def __init__(self, pattern, markdown_instance=None): ... self.compiled_re = re.compile("^(.*?)%s(.*?)$" % pattern, re.DOTALL | re.UNICODE) ... def getCompiledRegExp(self): return self.compiled_re def handleMatch(self, m): pass #pragma: no cover
Patternクラスはコンストラクタで渡された正規表現文字列の前後に任意の文字列とマッチする正規表現(.*?)
を追加している事がわかる。
そして、re.DOTALLとre.UNICODEフラグを設定して、正規表現のコンパイルをおこなっている。
getCompiledRegExpメソッドは、このコンパイルされた正規表現オブジェクトを返す。
re.compileについては
re.DOTALLフラグは
特殊文字 '.' を、改行を含む任意の文字と、とにかくマッチさせます;このフラグがなければ、 '.' は、改行 以外の 任意の文字とマッチします。
re.UNICODEフラグは
Make the \w, \W, \b, \B, \d, \D, \s and \S sequences dependent on the Unicode character properties database. Also enables non-ASCII matching for IGNORECASE.
基底クラスPatternのhandleMatchメソッドは、空のメソッドで何もしていないが、このメソッドの引数mには正規表現の検索結果のmatchオブジェクトが渡される。
EmphasisPatternのhandleMatchメソッドのコードの例をみてみよう。
matchオブジェクトのgroup属性は最初にマッチしたものはm.group(1)
に入るはずであるが、前述のように、「Patternクラスはコンストラクタで渡された正規表現文字列の前後に任意の文字列とマッチする正規表現(.*?)
を追加」されるため、m.group(1)
にはコンストラクタで渡された正規表現文字列の前のすべての文字列が入る事になる。
そして、m.group(2)
にはコンストラクタで渡された正規表現文字列の最初に一致した文字列*
が入る事になる。
m.group(3)
には*
の間に入っている文字列という事になる。
EmphasisPatternのhandleMatchメソッドではetreeクラスの新しいem
タグ要素を作成して、そのテキスト要素にgroup(3)
の*
に囲まれている文字列が入る事になる。
そして作成されたetreeオブジェクトを返している。
Treeprocessors
Treeprocessorsは、ElementTreeオブジェクトがコアのBlockParserを通過した後にそれを操作します。
ここでツリーの追加の操作が行われます。
さらに、InlineProcessorはツリーを辿り、ツリーの各要素のテキスト上のインラインパターンを実行するTreeprocessorです。
Treeprocessorはmarkdown.treeprocessors.Treeprocessor
から継承すべきです。
そして、run
メソッドをオーバーライドして1つの引数root
(ElementTreeオブジェクト)を受け取り、そのルート要素を変更してNone
を返すか、あるいは新しいElementTreeオブジェクトを返します。
コードのイメージは以下のようになります:
from markdown.treeprocessors import Treeprocessor class MyTreeprocessor(Treeprocessor): def run(self, root): root.text = 'modified content'
return
文が定義されていない場合、デフォルトではPythonクラスのメソッドはNone
を返すことに注意してください。
さらに、すべてのPython変数は参照によってオブジェクトを参照します。
したがって、上記のrunメソッドは、ルート要素を変更し、Noneを返します。
ルート要素とその子要素に対する変更は保持されます。
変更されたルート要素を返す傾向があります。
それが機能する間、Treeprocessorが実行されるたびにElementTree全体のコピーが起きるでしょう。
したがって、通常、run
メソッドはNone
または新しいElementTreeオブジェクトを返すことのみが予想されます。
ElementTreeの操作の詳細については、下記のElementTreeを扱うを参照してください。
Postprocessors
Postprocessorsは、ElementTreeが文字列にシリアライズされた後にドキュメントを操作します。
出力の直前にテキストを処理するには、ポストプロセッサを使用する必要があります。
Postprocessorはmarkdown.postprocessors.Postprocessor
から継承し、runメソッドをオーバーライドする必要があります。このメソッドは1つの引数textをとり、Unicode文字列を返します。
Postprocessorsは、ElementTreeがUnicodeテキストにシリアル化された後に実行されます。
たとえば、これはドキュメントに目次を追加するのに適した場所です:
from markdown.postprocessors import Postprocessor class TocPostprocessor(Postprocessor): def run(self, text): return MYMARKERRE.sub(MyToc, text)
訳者による蛇足
PostprocessorsクラスのはC:\Python27\Lib\site-packages\markdown\postprocessors.py
で実装されていて、
Postprocessorsの使用例については、Footnotes(脚注)のソースコード(C:\Python27\Lib\site-packages\markdown\extensions\footnotes.py)を参照。
BlockParser
時おり、Preprocessors, Treeprocessors, PostprocessorsおよびInline Patternsは、必要な処理を実行しないことがあります。
おそらく、コア解析に統合された新しいタイプのブロックタイプが必要に感じるでしょう。
このような状況において、コアBlockParserの機能を追加/変更/削除できます
BlockParserは、いくつかのBlockprocessorで構成されています。
BlockParserは、(空白行で分割された)テキストの各ブロックをステップ実行し、各ブロックを適切なブロックプロセッサに渡します。
Blockprocessorはブロックを解析し、それをElementTreeに追加します。
Definition Lists extensionは、ブロックプロセッサを追加/変更する拡張の良い例になります。
Blockprocessorはmarkdown.blockprocessors.BlockProcessor
を継承し、test
メソッドとrun
メソッドの両方を実装する必要があります。
test
メソッドは、BlockParserによってブロックのタイプを識別するために使用されます。
したがって、test
メソッドはブール値を返す必要があります。
テストでTrue
が返された場合、BlockParserはそのBlockprocessorのrun
メソッドを呼び出します。
False
を返すと、BlockParserは次のブロックプロセッサに移動します。
test
メソッドは2つの引数をとります:
-
parent
: ブロックの親ElementTree要素。
これは、ブロックがリスト内にある場合など、ブロックを別の方法で扱う必要がある場合などに便利です。 -
block
: テキストのカレントブロックの文字列。
testは単純な文字列メソッド(block.startswith(some_text)など)でも複雑な正規表現でもかまいません。
run
メソッドは2つの引数をとります:
-
parent
: ブロックの親ElementTree要素へのポインタ。
runメソッドは、おそらく追加のノードをこのparentに接続します。
メソッドによって何も返されないことに注意してください。
ElementTreeオブジェクトはここで変更されます。 -
blocks
: ドキュメントの残りすべてのブロックのリスト。
あなたのrunメソッドは、リストから最初のブロックを削除(ポップ)しなければなりません(変更される場所で - 戻れません)。そして、そのブロックを解析しなければなりません。
テキストブロックに正当な形で複数のブロックタイプが含まれていることがあります。
したがって、最初のタイプを処理した後で、残りのテキストを後で解析するためにブロックリストの先頭に挿入することができます。
1つのブロックが複数のテキストブロックにまたがる可能性があることに注意してください。
たとえば、公式のMarkdown構文規則では、空白行はコードブロックを終了しないと記述されています。
次のテキストブロックもインデントされている場合は、それは前のブロックの一部です。
したがって、BlockParserは、これらのタイプの状況に対処するために特別に設計されています。
したがって、最初のタイプを処理した後で、残りのテキストを後で解析するためにブロックリストの先頭に挿入することができます。
CoreBlockProcessorに気がついたら、コアの中のparentの最後の子をチェックすることに注意してください。
最後の子がコードブロック(<pre><code>...</code></pre>
)の場合、新しいブロックを作成するのではなく、そのブロックを前のコードブロックに追加します。
各ブロックプロセッサには、次のユーティリティメソッドが使用可能です。
-
lastChild(parent)
:指定されたElementTree要素の最後の子を返します。もしくは子要素がない場合はNoneを返します。
-
detab(text)
:指定されたテキスト文字列の各行の先頭から1レベルのインデント(デフォルトでは4つのスペース)を削除します。
-
looseDetab(text, level)
:指定されたテキスト文字列の各行の先頭からインデントの"level"で指定されたレベル(デフォルトは1)を削除します。
けれども、このメソッドはMarkdown構文のいくつかの部分と同じように二次的な行をインデントしないようにすることができます。
各Blockprocessorには、パーサーの状態を確認または変更するために使用できる、self.parser
のBlockParserインスタンスへのポインタもあります。
BlockParserは、parser.state
のスタック内の状態を追跡します。
状態スタックは、Stateクラスのインスタンスです。
State
はlist
のサブクラスであり、追加のメソッドを持っています:
-
set(state)
:文字列
state
.に対して新しい状態を設定します。
新しい状態はスタックの末尾に追加されます。 -
reset()
:スタックの一つ前に戻ります。
最後に一番後のステートはスタックから削除されます。 -
isstate(state)
:スタックのトップ(カレントの)レベルが指定された文字列
state
であることをテストします。
状態スタックが破損しないようにするには、ブロックに状態が設定されるたびに、パーサーがそのブロックの解析を終了したときにその状態をリセットする必要があります。
BlockParser
のインスタンスはMarkdown.parser
にあります。
BlockParser
は以下のメソッドを持っています:
-
parseDocument(lines)
:行のリストをもとに、ElementTreeオブジェクトを返します。
これは、ドキュメント全体を渡す必要があり、Markdownクラスが直接呼び出す唯一のメソッドです。 -
parseChunk(parent, text)
:複数のブロックで構成されるマークダウンテキストの断片であるtextを解析し、それらのブロックをparent(親要素)に結びつけます。
parentはここで変更され、何も返されません。
Extensionsがブロック解析するためにこのメソッドが使われる可能性が最も高いでしょう。 -
parseBlocks(parent, blocks)
:テキストブロックのリストを解析し、それらのブロックをparent(親要素)に結びつけます。
parentはここで変更され、何も返されません。
このメソッドは、通常、ネストされたテキストブロックを再帰的に解析するために内部的に使われるだけです。
isは推奨されませんが、ExtensionはBlockParserをサブクラス化または完全に置き換えることができます。
新しいクラスは同じパブリックAPIを提供する必要があります。
ただし、他のExtensionではコアパーサーが提供されていると予想され、そのような大幅に異なるパーサーでは機能しないことに注意してください。
ElementTreeを扱う
前述のように、Markdownパーサーは、ソースドキュメントをElementTreeオブジェクトに変換してからUnicodeテキストにシリアル化します。
Markdownは、Markdownモジュールのコンテキスト内での操作を簡単にするためにいくつかのヘルパーを提供しています。
まず、ElementTreeモジュールへのアクセスを取得するには、それを直接インポートするのではなく、ElementTreeをmarkdown
からインポートします。
これにより、markdownとして同じバージョンのElementTreeを使用していることが保証されます。
モジュールはMarkdown内のmarkdown.util.etree
にあります。
from markdown.util import etree
markdown.util.etree
は、標準ライブラリモジュール(Python 2.5ではxml.etree
から)として、次にサードパーティパッケージ(ElementTree)として、既知の場所からElementTreeをインポートしようとします
それぞれのインスタンスでは、最初にcElementTree
のインポートを
試みるます、次にシステム上で高速なCの実装が利用できないならElementTreeのインポートを
試みます。
Inline Patternsによって解析された要素の中にテキストを挿入したいと望むでしょう。
このような状況では、通常どおりにテキストをシンプルに挿入します、そしてテキストは自動的にインラインパターンをとおして実行されます。
けれども、いくつかのテキストに対してインラインパターンでの解析を望まないなら、テキストをAtomicString
として挿入します。
from markdown.util import AtomicString some_element.text = AtomicString(some_text)
ここに、HTMLテーブルを作成する基本的な例を示します(2番目のセル(td2)の内容は、後でインラインパターンをとおして実行されます)。
table = etree.Element("table") table.set("cellpadding", "2") # Set cellpadding to 2 tr = etree.SubElement(table, "tr") # Add child tr to table td1 = etree.SubElement(tr, "td") # Add child td1 to tr td1.text = markdown.util.AtomicString("Cell content") # Add plain text content td2 = etree.SubElement(tr, "td") # Add second td to tr td2.text = "*text* with **inline** formatting." # Add markup text table.tail = "Text after table" # Add text after table
既存のツリーを操作することもできます。
<a>
要素に対してclass
属性を追加する次の例を考えてみましょう。
def set_link_class(self, element): for child in element: if child.tag == "a": child.set("class", "myclass") #set the class attribute set_link_class(child) # run recursively on children
ElementTreeの操作の詳細については、ElementTreeのドキュメント(Python Docsを参照してください。
Markdownへのコードの統合
extensionのさまざまな部分を作成したら、Markdownを使って、それらが適切な順序で実行されていることを確認する必要があります。
Markdownは、おのおののextensionのExtension
インスタンスを受け入れます。
したがって、markdown.extensions.Extension
を拡張し、extendMarkdown
メソッドをオーバーライドするクラスを定義する必要があります。
このクラスで、extensionのコンフィグレーション(設定)オプションを管理し、さまざまなプロセッサとパターンをMarkdownインスタンスに(アタッチ)
付け加えします。
さまざまなプロセッサとパターンの順序が重要であることに注意することが重要です。
たとえば、リンクhttp://...
を<a>
要素に置き換えて、インラインHTMLを処理しようとすると、混乱することになります。
したがって、さまざまな種類のプロセッサとパターンは、OrderedDictのMarkdownクラスのインスタンス内に格納されます。
Extension
クラスは、それらのOrderedDictを適切に操作する必要があります。
OrderedDictの適切な場所にプロセッサーとパターンのインスタンスを挿入したり、組み込みのインスタンスを削除したり、組み込みのインスタンスを独自のインスタンスに置き換えたりすることができます。
extendMarkdown
markdown.extensions.Extension
クラスのextendMarkdown
メソッドは、2つの引数を受け取ります:
-
md
:Markdownクラスのインスタンスへのポインタ。
プロセッサーとパターンのOrderedDictsにアクセスするためにこれを使います。
それらは次の属性のもとにあります。md.preprocessors
md.inlinePatterns
md.parser.blockprocessors
md.treeprocessors
md.postprocessors
markdownインスタンスでアクセスしたいいくつかのものがあります:
md.htmlStash
md.output_formats
md.set_output_format()
md.output_format
md.serializer
md.registerExtension()
md.html_replacement_text
md.tab_length
md.enable_attributes
md.smart_emphasis
-
md_globals
:markdownモジュール内のさまざまなグローバル変数をすべて含みます。
警告
上記の項目にアクセスすると、理論的には、さまざまなモンキーパッチテクニックによって変更を加えるオプションがあります。
しかし、markdownの様々なドキュメント化されていない部分が予告なしに変更され、あなたのモンキーパッチが新しいリリースによって突然使えなくなる恐れがあるあることに注意してください。
したがって、本当にすべきことは、プロセッサとパターンをマークダウンパイプラインに挿入することです。
よく考え、あなた自身に言い聞かせて下さい。
簡単な例:
from markdown.extensions import Extension class MyExtension(Extension): def extendMarkdown(self, md, md_globals): # Insert instance of 'mypattern' before 'references' pattern md.inlinePatterns.add('mypattern', MyPattern(md), '<references')
OrderedDict
OrderedDictは、項目の順序を保持する辞書オブジェクトのようなものです。
項目はOrderedDictに追加された順に並べられます。
ただし、項目は、OrderedDictのなかに格納済みの項目に関連した特定の位置に挿入することもできます。
OrderedDictは、リストと辞書の組み合わせとして、両方に共通のメソッドを持っていると考えてください。
たとえば、od[key] = value
構文を使って項目を取得および設定する事ができて、そしてkeys()
, values()
,およびitems()
メソッドはキー,値,および項目が適切な順序で返されるよう期待どおりにはたらきます。
それに加えて、リストと同じように、insert()
, append()
,およびindex()
を使うことができます。
一般的に言えば、Markdown extensionsでは、既存のOrderedDictに項目を追加するために特別なヘルパーメソッドadd()
が使われます。
add()
メソッドは3つの引数を受け取ります:
-
key
: 文字列。 キーは、後で項目を参照するために使われます。 -
value
: この項目に格納されているオブジェクトインスタンス。 -
location
: オプション。 他の項目と関連した項目の位置。位置はいくつかの異なる値で指定できる事に注意してください:
-
特別な文字列
"_begin"
と"_end"
は、それぞれOrderedDictの先頭または末尾に項目を挿入します。 -
不等号
"<"の後ろに既存のキー(例えば
"<somekey"`)がある場合、項目は既存のキーの前に挿入されます。 -
不等号
">"の後ろに既存のキー(例えば
">somekey"`)がある場合、項目は既存のキーの後ろに挿入されます。
-
次の例を考えてみましょう:
>>> from markdown.odict import OrderedDict >>> od = OrderedDict() >>> od['one'] = 1 # The same as: od.add('one', 1, '_begin') >>> od['three'] = 3 # The same as: od.add('three', 3, '>one') >>> od['four'] = 4 # The same as: od.add('four', 4, '_end') >>> od.items() [("one", 1), ("three", 3), ("four", 4)]
OrderedDictに順番に項目を設定,追加するとき、add
メソッドの特別な機能は役に立ちません、そして必要ないことに注意してください。
しかし、既存のOrderedDictを操作するときには、add
は非常に役に立ちます。
そこで、別の項目をOrderedDictに挿入しましょう。
>>> od.add('two', 2, '>one') # Insert after 'one' >>> od.values() [1, 2, 3, 4]
今度は別の項目を挿入してみましょう。
>>> od.add('two-point-five', 2.5, '<three') # Insert before 'three' >>> od.keys() ["one", "two", "two-point-five", "three", "four"]
「two-point-five」の位置を「twoの後」(すなわち'>two'
)に設定できることに注意してください。
けれども、extensionがロードされる順序をコントロールすることは難しく、OrderedDictの最終ソート順に影響を及ぼす可能性があります。
例えば、上記の例の"two-point-five"を追加するextensionが、'two'を追加する別のextensionの前にロードされたとします。
さまざまなmarkdownのOrderedDictsにextensionコンポーネントを追加するときは、これを考慮する必要があります。
一度、OrderedDict を作成すると、項目は、キーを介して利用可能です。:
MyNode = od['somekey']
したがって、既存のアイテムを削除するには:
del od['somekey']
既存の項目の値を変更する(位置は変更しない):
od['somekey'] = MyNewObject()
既存の項目の位置を変更するには:
t.link('somekey', '<otherkey')
訳者による蛇足
上記のt.link
のコードが訳者にはわかりずらかったので、以下のコードで確認してみた。
from markdown.odict import OrderedDict od = OrderedDict() od['one'] = 1 # The same as: od.add('one', 1, '_begin') od['three'] = 3 # The same as: od.add('three', 3, '>one') od['four'] = 4 # The same as: od.add('four', 4, '_end') print od.items() od.link('one', '<four') print od.items()
実行結果
[('one', 1), ('three', 3), ('four', 4)] [('three', 3), ('one', 1), ('four', 4)]
なるほど、説明どうり、登録されている項目の順番を変更できるのネ!
ちなみに、python-markdownを使わずともcollectionsモジュールをimportするととOrderedDictオブジェクトを使う事ができる。
from collections import OrderedDict
- 8.3. collections — 高性能なコンテナ・データ型 — Python 2.7.14 ドキュメント
- 8.3. collections — コンテナデータ型 — Python 3.6.3 ドキュメント
しかし、linkメソッドはサポートされていないようだ。
registerExtension
一部のextensionsでは、Markdownクラスを複数回実行する間に状態をリセットする必要があります。
たとえば、次のFootnotesextensionの使用を考えてみましょう。
md = markdown.Markdown(extensions=['footnotes']) html1 = md.convert(text_with_footnote) md.reset() html2 = md.convert(text_without_footnote)
reset
を呼び出さないと、最初のドキュメントの脚注定義は、クラスインスタンス内にまだ格納されているため、2番目のドキュメントに挿入されます。
したがって、Extension
クラスは、extensionの状態をリセットするreset
メソッドを定義する必要があります(すなわちself.footnotes = {}
)。
ただし、多くのextensionはreset
が必要ないため、reset
は登録されているextensionでのみ呼び出されます。
エクステンションを登録するには、extendMarkdown
メソッドからmd.registerExtension
を呼び出します。
def extendMarkdown(self, md, md_globals): md.registerExtension(self) # insert processors and patterns here
次に、reset
がMarkdownインスタンスから呼び出されるたびに、登録された各extensionのreset
メソッドも呼び出されます。
最初に初期化された後、登録された各extensionでreset
が呼び出されることにも注意してください。
extensionのreset
メソッドをオーバーライドしているときは、そのことを覚えておいてください。
コンフィグレーション設定
もしextensionでユーザーが変更したいパラメータを使用しているなら、これらのパラメータはあなたのmarkdown.extensions.Extension
クラスのself.config
に次の形式で格納する必要があります:
class MyExtension(markdown.extensions.Extension): def __init__(self, **kwargs): self.config = {'option1' : ['value1', 'description1'], 'option2' : ['value2', 'description2'] } super(MyExtension, self).__init__(**kwargs)
このように実装すると、実行時にコンフィグレーションパラメータを上書きすることができます(つまり、superに対する呼び出し)。 例えば:
markdown.Markdown(extensions=[MyExtension(option1='other value'])
self.config
にまだ定義されていないキーワードが渡された場合、KeyError
が発生することに注意してください。
markdown.extensions.Extension
クラスおよびそのサブクラスには、コンフィグレーション設定の操作を支援するための次のメソッドがあります:
-
getConfig(key [, default])
:あたえられた
key
に対する値もしくはkey
が存在しない場合は、default
を返します。
default
が指定されていない場合は、空の文字列を返します。 -
getConfigs()
:辞書(dict)の中のすべてのキーと値のペアをかえします。
-
getConfigInfo()
:すべてのコンフィグレーションをタプルのリストとして返します。
-
setConfig(key, value)
:指定された
value
を持つkey
をコンフィグレーションに設定します。key
が未知の場合、KeyError
が発生します。
key
の前の値がブール値だった場合、value
はブール値に変換されます。
以前のkey
の値がNone
の場合、value
がNone
の場合を除いて、それはブール値に変換されます。
keyの前の値が文字列である場合、変換は行われません。 -
setConfigs(items)
:キーと値のペアからなる辞書を指定して、複数のコンフィグレーションを設定します。
makeExtension
ライブラリリファレンスに記載されているように、extensionのインスタンスはMarkdownに直接渡すことができます。
実際、サードパーティのextensionで使われている好ましい方法です。
例えば:
import markdown import myextension myext = myextension.MyExtension(option='value') md = markdown.Markdown(extensions=[myext])
(コマンドラインから、またはテンプレート内から)直接extensionをインポートすることが実際的でない時、その場合のために
Markdownはサードパーティのextensionを「名前付き」でもまた受け入れます。
extensionの"名前"は、Pythonのドット表記を使用してモジュールへのインポート可能なパスからなる文字列でなければなりません。
したがって、ユーザーにライブラリを提供していて、ライブラリ内にカスタムmarkdown extension を含める場合、そのextension は "mylib.mdext.myext"
という名前になります。ここで、mylib/mdext/myext.py
にはextensionとmylib
のデレクトリがPYTHONPATHに含まれています。
文字列には、コロンで区切られたクラスの名前を含めることもできます。したがって、このようなクラスをインポートするなら:
from path.to.module import SomeExtensionClass
名前付けされたextensionは次の文字列からなります:
"path.to.module:SomeExtensionClass"
この機能をサポートするために特別な操作を行う必要はありません。
extensionクラスをインポートすることができれば、ユーザーは上記の構文でextensionクラスを組み込むことができます。
上記の2つの方法は、モジュール内に複数のextensionが存在する場合に、多数のextensionを実装する必要がある場合に特に便利です。
ただし、ユーザーが文字列にクラス名を含める必要がない場合は、モジュールごとに1つのextensionのみを定義する必要があり、そのモジュールには、**kwargs
を受け入れ、extensionのインスタンスを返すmakeExtensionというモジュールレベルの関数を含める必要があります 。
例えば:
class MyExtension(markdown.extensions.Extension) # Define extension here... def makeExtension(**kwargs): return MyExtension(**kwargs)
Markdownにあなたのextensionの"名前"がドット表記文字列として渡されると、モジュールがインポートされ、makeExtension
関数が呼び出されてextensionが開始されます。