Skip to content

Commit 66b84e4

Browse files
committed
html_attribute: markdown.searialiers の内部変数を一時的に変更して実体参照を適切に処理
これまでは self._markdown.serializer が使われていたが、serializer は何 故か実体参照のパターンに一致する & を &amp; に変換せずにそのまま出力す る仕様になっている [1]。これを用いてHTMLに戻すと例えば元々のHTMLソース で &amp;var; となっていたものが処理後のHTMLソースで不正な実体参照 &var; になってしまい、ブラウザで正しく表示されなくなる可能性がある。例 えば reference/memory/addressof.html でこれが起こっている [2]。さらに、 後段でHTMLを解釈して処理しようとしたときにクラッシュする。 xml.etree.ElementTree が etree.tostring(method="xml") で正当なHTMLへの 変換に対応しているが、これを用いると <td></td> などが <td /> になって しまう。代わりに method="html" を使うと <br /> などが <br> になってし まう。また、どちらの method の場合でも HTML 属性の順序が変わってしまう ので好ましくない。 この実装では代わりに markdown.searializers の内部変数 markdown.searializers.RE_AMP を書き換えることによって期待する動作を得 ている。上流の markdown.searializers で実装の変更があると動かなくなる 可能性があることに注意する。 [1] https://github.com/Python-Markdown/markdown/blob/a11431539d08e14b0bd821ceb101fa59d6a74c8a/markdown/serializers.py#L69-L70 [2] https://github.com/cpprefjp/cpprefjp.github.io/blob/335489b522dcbb648c19a49e39cc880e187911a5/reference/memory/addressof.html#L263
1 parent 2ce6b71 commit 66b84e4

File tree

1 file changed

+28
-1
lines changed

1 file changed

+28
-1
lines changed

html_attribute.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import markdown
1111
from markdown import postprocessors
12+
from markdown import serializers
1213

1314
import xml.etree.ElementTree as etree
1415

@@ -312,6 +313,32 @@ def _add_meta(self, element):
312313
element.remove(e)
313314
element.append(body)
314315

316+
def _tohtml(self, element):
317+
# Note: 以下の様に etree.tostring(method="xml") を用いると
318+
# <span></span> や <td></td> が <span /> や <td /> になってしまう。また、
319+
# HTML 属性の順序が保持されない。
320+
#
321+
# return etree.tostring(element, encoding="unicode", method="xml")
322+
323+
# Note: 代わりに etree.tostring(method="html") を用いると、今度は <img
324+
# /> や <br /> が <img> や <br> になってしまい好ましくない。またこの時
325+
# も HTML 属性の順序が保持されない。
326+
#
327+
# return etree.tostring(element, encoding="unicode", method="html")
328+
329+
# 今は代わりに以下のようにして markdown.serializers の内部変数
330+
# markdown.serializers.RE_AMP を一時的に書き換えることによって期待する
331+
# 動作を得ている。これは markdown.serializers の内部実装に依存している
332+
# ので、markdown.serializers の上流で内部実装に変更があると動かなくなる
333+
# 可能性があることに注意する。
334+
old_RE_AMP = serializers.RE_AMP
335+
try:
336+
serializers.RE_AMP = re.compile(r'&')
337+
output = self._markdown.serializer(element)
338+
finally:
339+
serializers.RE_AMP = old_RE_AMP
340+
return output
341+
315342
def run(self, text):
316343
text = '<{tag}>{text}</{tag}>'.format(tag=self._markdown.doc_tag, text=text)
317344
try:
@@ -328,7 +355,7 @@ def run(self, text):
328355
self._iterate(root, self._adjust_url)
329356
self._add_meta(root)
330357

331-
output = self._markdown.serializer(root)
358+
output = self._tohtml(root)
332359
if self._markdown.stripTopLevelTags:
333360
try:
334361
start = output.index('<%s>' % self._markdown.doc_tag) + len(self._markdown.doc_tag) + 2

0 commit comments

Comments
 (0)