PageRenderTime 62ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/bundled/markdown2/lib/markdown2.py

https://bitbucket.org/sjl/hg-review/
Python | 2055 lines | 1904 code | 47 blank | 104 comment | 71 complexity | fd79fc150134e73452a0aec78b31b94d MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. #!/usr/bin/env python
  2. # Copyright (c) 2007-2008 ActiveState Corp.
  3. # License: MIT (http://www.opensource.org/licenses/mit-license.php)
  4. r"""A fast and complete Python implementation of Markdown.
  5. [from http://daringfireball.net/projects/markdown/]
  6. > Markdown is a text-to-HTML filter; it translates an easy-to-read /
  7. > easy-to-write structured text format into HTML. Markdown's text
  8. > format is most similar to that of plain text email, and supports
  9. > features such as headers, *emphasis*, code blocks, blockquotes, and
  10. > links.
  11. >
  12. > Markdown's syntax is designed not as a generic markup language, but
  13. > specifically to serve as a front-end to (X)HTML. You can use span-level
  14. > HTML tags anywhere in a Markdown document, and you can use block level
  15. > HTML tags (like <div> and <table> as well).
  16. Module usage:
  17. >>> import markdown2
  18. >>> markdown2.markdown("*boo!*") # or use `html = markdown_path(PATH)`
  19. u'<p><em>boo!</em></p>\n'
  20. >>> markdowner = Markdown()
  21. >>> markdowner.convert("*boo!*")
  22. u'<p><em>boo!</em></p>\n'
  23. >>> markdowner.convert("**boom!**")
  24. u'<p><strong>boom!</strong></p>\n'
  25. This implementation of Markdown implements the full "core" syntax plus a
  26. number of extras (e.g., code syntax coloring, footnotes) as described on
  27. <http://code.google.com/p/python-markdown2/wiki/Extras>.
  28. """
  29. cmdln_desc = """A fast and complete Python implementation of Markdown, a
  30. text-to-HTML conversion tool for web writers.
  31. Supported extras (see -x|--extras option below):
  32. * code-friendly: Disable _ and __ for em and strong.
  33. * code-color: Pygments-based syntax coloring of <code> sections.
  34. * cuddled-lists: Allow lists to be cuddled to the preceding paragraph.
  35. * footnotes: Support footnotes as in use on daringfireball.net and
  36. implemented in other Markdown processors (tho not in Markdown.pl v1.0.1).
  37. * html-classes: Takes a dict mapping html tag names (lowercase) to a
  38. string to use for a "class" tag attribute. Currently only supports
  39. "pre" and "code" tags. Add an issue if you require this for other tags.
  40. * pyshell: Treats unindented Python interactive shell sessions as <code>
  41. blocks.
  42. * link-patterns: Auto-link given regex patterns in text (e.g. bug number
  43. references, revision number references).
  44. * xml: Passes one-liner processing instructions and namespaced XML tags.
  45. """
  46. # Dev Notes:
  47. # - There is already a Python markdown processor
  48. # (http://www.freewisdom.org/projects/python-markdown/).
  49. # - Python's regex syntax doesn't have '\z', so I'm using '\Z'. I'm
  50. # not yet sure if there implications with this. Compare 'pydoc sre'
  51. # and 'perldoc perlre'.
  52. __version_info__ = (1, 0, 1, 17) # first three nums match Markdown.pl
  53. __version__ = '1.0.1.17'
  54. __author__ = "Trent Mick"
  55. import os
  56. import sys
  57. from pprint import pprint
  58. import re
  59. import logging
  60. try:
  61. from hashlib import md5
  62. except ImportError:
  63. from md5 import md5
  64. import optparse
  65. from random import random, randint
  66. import codecs
  67. from urllib import quote
  68. #---- Python version compat
  69. if sys.version_info[:2] < (2,4):
  70. from sets import Set as set
  71. def reversed(sequence):
  72. for i in sequence[::-1]:
  73. yield i
  74. def _unicode_decode(s, encoding, errors='xmlcharrefreplace'):
  75. return unicode(s, encoding, errors)
  76. else:
  77. def _unicode_decode(s, encoding, errors='strict'):
  78. return s.decode(encoding, errors)
  79. #---- globals
  80. DEBUG = False
  81. log = logging.getLogger("markdown")
  82. DEFAULT_TAB_WIDTH = 4
  83. try:
  84. import uuid
  85. except ImportError:
  86. SECRET_SALT = str(randint(0, 1000000))
  87. else:
  88. SECRET_SALT = str(uuid.uuid4())
  89. def _hash_ascii(s):
  90. #return md5(s).hexdigest() # Markdown.pl effectively does this.
  91. return 'md5-' + md5(SECRET_SALT + s).hexdigest()
  92. def _hash_text(s):
  93. return 'md5-' + md5(SECRET_SALT + s.encode("utf-8")).hexdigest()
  94. # Table of hash values for escaped characters:
  95. g_escape_table = dict([(ch, _hash_ascii(ch))
  96. for ch in '\\`*_{}[]()>#+-.!'])
  97. #---- exceptions
  98. class MarkdownError(Exception):
  99. pass
  100. #---- public api
  101. def markdown_path(path, encoding="utf-8",
  102. html4tags=False, tab_width=DEFAULT_TAB_WIDTH,
  103. safe_mode=None, extras=None, link_patterns=None,
  104. use_file_vars=False):
  105. fp = codecs.open(path, 'r', encoding)
  106. text = fp.read()
  107. fp.close()
  108. return Markdown(html4tags=html4tags, tab_width=tab_width,
  109. safe_mode=safe_mode, extras=extras,
  110. link_patterns=link_patterns,
  111. use_file_vars=use_file_vars).convert(text)
  112. def markdown(text, html4tags=False, tab_width=DEFAULT_TAB_WIDTH,
  113. safe_mode=None, extras=None, link_patterns=None,
  114. use_file_vars=False):
  115. return Markdown(html4tags=html4tags, tab_width=tab_width,
  116. safe_mode=safe_mode, extras=extras,
  117. link_patterns=link_patterns,
  118. use_file_vars=use_file_vars).convert(text)
  119. class Markdown(object):
  120. # The dict of "extras" to enable in processing -- a mapping of
  121. # extra name to argument for the extra. Most extras do not have an
  122. # argument, in which case the value is None.
  123. #
  124. # This can be set via (a) subclassing and (b) the constructor
  125. # "extras" argument.
  126. extras = None
  127. urls = None
  128. titles = None
  129. html_blocks = None
  130. html_spans = None
  131. html_removed_text = "[HTML_REMOVED]" # for compat with markdown.py
  132. # Used to track when we're inside an ordered or unordered list
  133. # (see _ProcessListItems() for details):
  134. list_level = 0
  135. _ws_only_line_re = re.compile(r"^[ \t]+$", re.M)
  136. def __init__(self, html4tags=False, tab_width=4, safe_mode=None,
  137. extras=None, link_patterns=None, use_file_vars=False):
  138. if html4tags:
  139. self.empty_element_suffix = ">"
  140. else:
  141. self.empty_element_suffix = " />"
  142. self.tab_width = tab_width
  143. # For compatibility with earlier markdown2.py and with
  144. # markdown.py's safe_mode being a boolean,
  145. # safe_mode == True -> "replace"
  146. if safe_mode is True:
  147. self.safe_mode = "replace"
  148. else:
  149. self.safe_mode = safe_mode
  150. if self.extras is None:
  151. self.extras = {}
  152. elif not isinstance(self.extras, dict):
  153. self.extras = dict([(e, None) for e in self.extras])
  154. if extras:
  155. if not isinstance(extras, dict):
  156. extras = dict([(e, None) for e in extras])
  157. self.extras.update(extras)
  158. assert isinstance(self.extras, dict)
  159. if "toc" in self.extras and not "header-ids" in self.extras:
  160. self.extras["header-ids"] = None # "toc" implies "header-ids"
  161. self._instance_extras = self.extras.copy()
  162. self.link_patterns = link_patterns
  163. self.use_file_vars = use_file_vars
  164. self._outdent_re = re.compile(r'^(\t|[ ]{1,%d})' % tab_width, re.M)
  165. def reset(self):
  166. self.urls = {}
  167. self.titles = {}
  168. self.html_blocks = {}
  169. self.html_spans = {}
  170. self.list_level = 0
  171. self.extras = self._instance_extras.copy()
  172. if "footnotes" in self.extras:
  173. self.footnotes = {}
  174. self.footnote_ids = []
  175. if "header-ids" in self.extras:
  176. self._count_from_header_id = {} # no `defaultdict` in Python 2.4
  177. def convert(self, text):
  178. """Convert the given text."""
  179. # Main function. The order in which other subs are called here is
  180. # essential. Link and image substitutions need to happen before
  181. # _EscapeSpecialChars(), so that any *'s or _'s in the <a>
  182. # and <img> tags get encoded.
  183. # Clear the global hashes. If we don't clear these, you get conflicts
  184. # from other articles when generating a page which contains more than
  185. # one article (e.g. an index page that shows the N most recent
  186. # articles):
  187. self.reset()
  188. if not isinstance(text, unicode):
  189. #TODO: perhaps shouldn't presume UTF-8 for string input?
  190. text = unicode(text, 'utf-8')
  191. if self.use_file_vars:
  192. # Look for emacs-style file variable hints.
  193. emacs_vars = self._get_emacs_vars(text)
  194. if "markdown-extras" in emacs_vars:
  195. splitter = re.compile("[ ,]+")
  196. for e in splitter.split(emacs_vars["markdown-extras"]):
  197. if '=' in e:
  198. ename, earg = e.split('=', 1)
  199. try:
  200. earg = int(earg)
  201. except ValueError:
  202. pass
  203. else:
  204. ename, earg = e, None
  205. self.extras[ename] = earg
  206. # Standardize line endings:
  207. text = re.sub("\r\n|\r", "\n", text)
  208. # Make sure $text ends with a couple of newlines:
  209. text += "\n\n"
  210. # Convert all tabs to spaces.
  211. text = self._detab(text)
  212. # Strip any lines consisting only of spaces and tabs.
  213. # This makes subsequent regexen easier to write, because we can
  214. # match consecutive blank lines with /\n+/ instead of something
  215. # contorted like /[ \t]*\n+/ .
  216. text = self._ws_only_line_re.sub("", text)
  217. if self.safe_mode:
  218. text = self._hash_html_spans(text)
  219. # Turn block-level HTML blocks into hash entries
  220. text = self._hash_html_blocks(text, raw=True)
  221. # Strip link definitions, store in hashes.
  222. if "footnotes" in self.extras:
  223. # Must do footnotes first because an unlucky footnote defn
  224. # looks like a link defn:
  225. # [^4]: this "looks like a link defn"
  226. text = self._strip_footnote_definitions(text)
  227. text = self._strip_link_definitions(text)
  228. text = self._run_block_gamut(text)
  229. if "footnotes" in self.extras:
  230. text = self._add_footnotes(text)
  231. text = self._unescape_special_chars(text)
  232. if self.safe_mode:
  233. text = self._unhash_html_spans(text)
  234. text += "\n"
  235. rv = UnicodeWithAttrs(text)
  236. if "toc" in self.extras:
  237. rv._toc = self._toc
  238. return rv
  239. _emacs_oneliner_vars_pat = re.compile(r"-\*-\s*([^\r\n]*?)\s*-\*-", re.UNICODE)
  240. # This regular expression is intended to match blocks like this:
  241. # PREFIX Local Variables: SUFFIX
  242. # PREFIX mode: Tcl SUFFIX
  243. # PREFIX End: SUFFIX
  244. # Some notes:
  245. # - "[ \t]" is used instead of "\s" to specifically exclude newlines
  246. # - "(\r\n|\n|\r)" is used instead of "$" because the sre engine does
  247. # not like anything other than Unix-style line terminators.
  248. _emacs_local_vars_pat = re.compile(r"""^
  249. (?P<prefix>(?:[^\r\n|\n|\r])*?)
  250. [\ \t]*Local\ Variables:[\ \t]*
  251. (?P<suffix>.*?)(?:\r\n|\n|\r)
  252. (?P<content>.*?\1End:)
  253. """, re.IGNORECASE | re.MULTILINE | re.DOTALL | re.VERBOSE)
  254. def _get_emacs_vars(self, text):
  255. """Return a dictionary of emacs-style local variables.
  256. Parsing is done loosely according to this spec (and according to
  257. some in-practice deviations from this):
  258. http://www.gnu.org/software/emacs/manual/html_node/emacs/Specifying-File-Variables.html#Specifying-File-Variables
  259. """
  260. emacs_vars = {}
  261. SIZE = pow(2, 13) # 8kB
  262. # Search near the start for a '-*-'-style one-liner of variables.
  263. head = text[:SIZE]
  264. if "-*-" in head:
  265. match = self._emacs_oneliner_vars_pat.search(head)
  266. if match:
  267. emacs_vars_str = match.group(1)
  268. assert '\n' not in emacs_vars_str
  269. emacs_var_strs = [s.strip() for s in emacs_vars_str.split(';')
  270. if s.strip()]
  271. if len(emacs_var_strs) == 1 and ':' not in emacs_var_strs[0]:
  272. # While not in the spec, this form is allowed by emacs:
  273. # -*- Tcl -*-
  274. # where the implied "variable" is "mode". This form
  275. # is only allowed if there are no other variables.
  276. emacs_vars["mode"] = emacs_var_strs[0].strip()
  277. else:
  278. for emacs_var_str in emacs_var_strs:
  279. try:
  280. variable, value = emacs_var_str.strip().split(':', 1)
  281. except ValueError:
  282. log.debug("emacs variables error: malformed -*- "
  283. "line: %r", emacs_var_str)
  284. continue
  285. # Lowercase the variable name because Emacs allows "Mode"
  286. # or "mode" or "MoDe", etc.
  287. emacs_vars[variable.lower()] = value.strip()
  288. tail = text[-SIZE:]
  289. if "Local Variables" in tail:
  290. match = self._emacs_local_vars_pat.search(tail)
  291. if match:
  292. prefix = match.group("prefix")
  293. suffix = match.group("suffix")
  294. lines = match.group("content").splitlines(0)
  295. #print "prefix=%r, suffix=%r, content=%r, lines: %s"\
  296. # % (prefix, suffix, match.group("content"), lines)
  297. # Validate the Local Variables block: proper prefix and suffix
  298. # usage.
  299. for i, line in enumerate(lines):
  300. if not line.startswith(prefix):
  301. log.debug("emacs variables error: line '%s' "
  302. "does not use proper prefix '%s'"
  303. % (line, prefix))
  304. return {}
  305. # Don't validate suffix on last line. Emacs doesn't care,
  306. # neither should we.
  307. if i != len(lines)-1 and not line.endswith(suffix):
  308. log.debug("emacs variables error: line '%s' "
  309. "does not use proper suffix '%s'"
  310. % (line, suffix))
  311. return {}
  312. # Parse out one emacs var per line.
  313. continued_for = None
  314. for line in lines[:-1]: # no var on the last line ("PREFIX End:")
  315. if prefix: line = line[len(prefix):] # strip prefix
  316. if suffix: line = line[:-len(suffix)] # strip suffix
  317. line = line.strip()
  318. if continued_for:
  319. variable = continued_for
  320. if line.endswith('\\'):
  321. line = line[:-1].rstrip()
  322. else:
  323. continued_for = None
  324. emacs_vars[variable] += ' ' + line
  325. else:
  326. try:
  327. variable, value = line.split(':', 1)
  328. except ValueError:
  329. log.debug("local variables error: missing colon "
  330. "in local variables entry: '%s'" % line)
  331. continue
  332. # Do NOT lowercase the variable name, because Emacs only
  333. # allows "mode" (and not "Mode", "MoDe", etc.) in this block.
  334. value = value.strip()
  335. if value.endswith('\\'):
  336. value = value[:-1].rstrip()
  337. continued_for = variable
  338. else:
  339. continued_for = None
  340. emacs_vars[variable] = value
  341. # Unquote values.
  342. for var, val in emacs_vars.items():
  343. if len(val) > 1 and (val.startswith('"') and val.endswith('"')
  344. or val.startswith('"') and val.endswith('"')):
  345. emacs_vars[var] = val[1:-1]
  346. return emacs_vars
  347. # Cribbed from a post by Bart Lateur:
  348. # <http://www.nntp.perl.org/group/perl.macperl.anyperl/154>
  349. _detab_re = re.compile(r'(.*?)\t', re.M)
  350. def _detab_sub(self, match):
  351. g1 = match.group(1)
  352. return g1 + (' ' * (self.tab_width - len(g1) % self.tab_width))
  353. def _detab(self, text):
  354. r"""Remove (leading?) tabs from a file.
  355. >>> m = Markdown()
  356. >>> m._detab("\tfoo")
  357. ' foo'
  358. >>> m._detab(" \tfoo")
  359. ' foo'
  360. >>> m._detab("\t foo")
  361. ' foo'
  362. >>> m._detab(" foo")
  363. ' foo'
  364. >>> m._detab(" foo\n\tbar\tblam")
  365. ' foo\n bar blam'
  366. """
  367. if '\t' not in text:
  368. return text
  369. return self._detab_re.subn(self._detab_sub, text)[0]
  370. _block_tags_a = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del'
  371. _strict_tag_block_re = re.compile(r"""
  372. ( # save in \1
  373. ^ # start of line (with re.M)
  374. <(%s) # start tag = \2
  375. \b # word break
  376. (.*\n)*? # any number of lines, minimally matching
  377. </\2> # the matching end tag
  378. [ \t]* # trailing spaces/tabs
  379. (?=\n+|\Z) # followed by a newline or end of document
  380. )
  381. """ % _block_tags_a,
  382. re.X | re.M)
  383. _block_tags_b = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math'
  384. _liberal_tag_block_re = re.compile(r"""
  385. ( # save in \1
  386. ^ # start of line (with re.M)
  387. <(%s) # start tag = \2
  388. \b # word break
  389. (.*\n)*? # any number of lines, minimally matching
  390. .*</\2> # the matching end tag
  391. [ \t]* # trailing spaces/tabs
  392. (?=\n+|\Z) # followed by a newline or end of document
  393. )
  394. """ % _block_tags_b,
  395. re.X | re.M)
  396. def _hash_html_block_sub(self, match, raw=False):
  397. html = match.group(1)
  398. if raw and self.safe_mode:
  399. html = self._sanitize_html(html)
  400. key = _hash_text(html)
  401. self.html_blocks[key] = html
  402. return "\n\n" + key + "\n\n"
  403. def _hash_html_blocks(self, text, raw=False):
  404. """Hashify HTML blocks
  405. We only want to do this for block-level HTML tags, such as headers,
  406. lists, and tables. That's because we still want to wrap <p>s around
  407. "paragraphs" that are wrapped in non-block-level tags, such as anchors,
  408. phrase emphasis, and spans. The list of tags we're looking for is
  409. hard-coded.
  410. @param raw {boolean} indicates if these are raw HTML blocks in
  411. the original source. It makes a difference in "safe" mode.
  412. """
  413. if '<' not in text:
  414. return text
  415. # Pass `raw` value into our calls to self._hash_html_block_sub.
  416. hash_html_block_sub = _curry(self._hash_html_block_sub, raw=raw)
  417. # First, look for nested blocks, e.g.:
  418. # <div>
  419. # <div>
  420. # tags for inner block must be indented.
  421. # </div>
  422. # </div>
  423. #
  424. # The outermost tags must start at the left margin for this to match, and
  425. # the inner nested divs must be indented.
  426. # We need to do this before the next, more liberal match, because the next
  427. # match will start at the first `<div>` and stop at the first `</div>`.
  428. text = self._strict_tag_block_re.sub(hash_html_block_sub, text)
  429. # Now match more liberally, simply from `\n<tag>` to `</tag>\n`
  430. text = self._liberal_tag_block_re.sub(hash_html_block_sub, text)
  431. # Special case just for <hr />. It was easier to make a special
  432. # case than to make the other regex more complicated.
  433. if "<hr" in text:
  434. _hr_tag_re = _hr_tag_re_from_tab_width(self.tab_width)
  435. text = _hr_tag_re.sub(hash_html_block_sub, text)
  436. # Special case for standalone HTML comments:
  437. if "<!--" in text:
  438. start = 0
  439. while True:
  440. # Delimiters for next comment block.
  441. try:
  442. start_idx = text.index("<!--", start)
  443. except ValueError, ex:
  444. break
  445. try:
  446. end_idx = text.index("-->", start_idx) + 3
  447. except ValueError, ex:
  448. text = text[:start_idx] + '&lt;' + text[start_idx+1:]
  449. break
  450. # Start position for next comment block search.
  451. start = end_idx
  452. # Validate whitespace before comment.
  453. if start_idx:
  454. # - Up to `tab_width - 1` spaces before start_idx.
  455. for i in range(self.tab_width - 1):
  456. if text[start_idx - 1] != ' ':
  457. break
  458. start_idx -= 1
  459. if start_idx == 0:
  460. break
  461. # - Must be preceded by 2 newlines or hit the start of
  462. # the document.
  463. if start_idx == 0:
  464. pass
  465. elif start_idx == 1 and text[0] == '\n':
  466. start_idx = 0 # to match minute detail of Markdown.pl regex
  467. elif text[start_idx-2:start_idx] == '\n\n':
  468. pass
  469. else:
  470. break
  471. # Validate whitespace after comment.
  472. # - Any number of spaces and tabs.
  473. while end_idx < len(text):
  474. if text[end_idx] not in ' \t':
  475. break
  476. end_idx += 1
  477. # - Must be following by 2 newlines or hit end of text.
  478. if text[end_idx:end_idx+2] not in ('', '\n', '\n\n'):
  479. continue
  480. # Escape and hash (must match `_hash_html_block_sub`).
  481. html = text[start_idx:end_idx]
  482. if raw and self.safe_mode:
  483. html = self._sanitize_html(html)
  484. key = _hash_text(html)
  485. self.html_blocks[key] = html
  486. text = text[:start_idx] + "\n\n" + key + "\n\n" + text[end_idx:]
  487. if "xml" in self.extras:
  488. # Treat XML processing instructions and namespaced one-liner
  489. # tags as if they were block HTML tags. E.g., if standalone
  490. # (i.e. are their own paragraph), the following do not get
  491. # wrapped in a <p> tag:
  492. # <?foo bar?>
  493. #
  494. # <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="chapter_1.md"/>
  495. _xml_oneliner_re = _xml_oneliner_re_from_tab_width(self.tab_width)
  496. text = _xml_oneliner_re.sub(hash_html_block_sub, text)
  497. return text
  498. def _strip_link_definitions(self, text):
  499. # Strips link definitions from text, stores the URLs and titles in
  500. # hash references.
  501. less_than_tab = self.tab_width - 1
  502. # Link defs are in the form:
  503. # [id]: url "optional title"
  504. _link_def_re = re.compile(r"""
  505. ^[ ]{0,%d}\[(.+)\]: # id = \1
  506. [ \t]*
  507. \n? # maybe *one* newline
  508. [ \t]*
  509. <?(.+?)>? # url = \2
  510. [ \t]*
  511. (?:
  512. \n? # maybe one newline
  513. [ \t]*
  514. (?<=\s) # lookbehind for whitespace
  515. ['"(]
  516. ([^\n]*) # title = \3
  517. ['")]
  518. [ \t]*
  519. )? # title is optional
  520. (?:\n+|\Z)
  521. """ % less_than_tab, re.X | re.M | re.U)
  522. return _link_def_re.sub(self._extract_link_def_sub, text)
  523. def _extract_link_def_sub(self, match):
  524. id, url, title = match.groups()
  525. key = id.lower() # Link IDs are case-insensitive
  526. self.urls[key] = self._encode_amps_and_angles(url)
  527. if title:
  528. self.titles[key] = title.replace('"', '&quot;')
  529. return ""
  530. def _extract_footnote_def_sub(self, match):
  531. id, text = match.groups()
  532. text = _dedent(text, skip_first_line=not text.startswith('\n')).strip()
  533. normed_id = re.sub(r'\W', '-', id)
  534. # Ensure footnote text ends with a couple newlines (for some
  535. # block gamut matches).
  536. self.footnotes[normed_id] = text + "\n\n"
  537. return ""
  538. def _strip_footnote_definitions(self, text):
  539. """A footnote definition looks like this:
  540. [^note-id]: Text of the note.
  541. May include one or more indented paragraphs.
  542. Where,
  543. - The 'note-id' can be pretty much anything, though typically it
  544. is the number of the footnote.
  545. - The first paragraph may start on the next line, like so:
  546. [^note-id]:
  547. Text of the note.
  548. """
  549. less_than_tab = self.tab_width - 1
  550. footnote_def_re = re.compile(r'''
  551. ^[ ]{0,%d}\[\^(.+)\]: # id = \1
  552. [ \t]*
  553. ( # footnote text = \2
  554. # First line need not start with the spaces.
  555. (?:\s*.*\n+)
  556. (?:
  557. (?:[ ]{%d} | \t) # Subsequent lines must be indented.
  558. .*\n+
  559. )*
  560. )
  561. # Lookahead for non-space at line-start, or end of doc.
  562. (?:(?=^[ ]{0,%d}\S)|\Z)
  563. ''' % (less_than_tab, self.tab_width, self.tab_width),
  564. re.X | re.M)
  565. return footnote_def_re.sub(self._extract_footnote_def_sub, text)
  566. _hr_res = [
  567. re.compile(r"^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$", re.M),
  568. re.compile(r"^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$", re.M),
  569. re.compile(r"^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$", re.M),
  570. ]
  571. def _run_block_gamut(self, text):
  572. # These are all the transformations that form block-level
  573. # tags like paragraphs, headers, and list items.
  574. text = self._do_headers(text)
  575. # Do Horizontal Rules:
  576. hr = "\n<hr"+self.empty_element_suffix+"\n"
  577. for hr_re in self._hr_res:
  578. text = hr_re.sub(hr, text)
  579. text = self._do_lists(text)
  580. if "pyshell" in self.extras:
  581. text = self._prepare_pyshell_blocks(text)
  582. text = self._do_code_blocks(text)
  583. text = self._do_block_quotes(text)
  584. # We already ran _HashHTMLBlocks() before, in Markdown(), but that
  585. # was to escape raw HTML in the original Markdown source. This time,
  586. # we're escaping the markup we've just created, so that we don't wrap
  587. # <p> tags around block-level tags.
  588. text = self._hash_html_blocks(text)
  589. text = self._form_paragraphs(text)
  590. return text
  591. def _pyshell_block_sub(self, match):
  592. lines = match.group(0).splitlines(0)
  593. _dedentlines(lines)
  594. indent = ' ' * self.tab_width
  595. s = ('\n' # separate from possible cuddled paragraph
  596. + indent + ('\n'+indent).join(lines)
  597. + '\n\n')
  598. return s
  599. def _prepare_pyshell_blocks(self, text):
  600. """Ensure that Python interactive shell sessions are put in
  601. code blocks -- even if not properly indented.
  602. """
  603. if ">>>" not in text:
  604. return text
  605. less_than_tab = self.tab_width - 1
  606. _pyshell_block_re = re.compile(r"""
  607. ^([ ]{0,%d})>>>[ ].*\n # first line
  608. ^(\1.*\S+.*\n)* # any number of subsequent lines
  609. ^\n # ends with a blank line
  610. """ % less_than_tab, re.M | re.X)
  611. return _pyshell_block_re.sub(self._pyshell_block_sub, text)
  612. def _run_span_gamut(self, text):
  613. # These are all the transformations that occur *within* block-level
  614. # tags like paragraphs, headers, and list items.
  615. text = self._do_code_spans(text)
  616. text = self._escape_special_chars(text)
  617. # Process anchor and image tags.
  618. text = self._do_links(text)
  619. # Make links out of things like `<http://example.com/>`
  620. # Must come after _do_links(), because you can use < and >
  621. # delimiters in inline links like [this](<url>).
  622. text = self._do_auto_links(text)
  623. if "link-patterns" in self.extras:
  624. text = self._do_link_patterns(text)
  625. text = self._encode_amps_and_angles(text)
  626. text = self._do_italics_and_bold(text)
  627. # Do hard breaks:
  628. text = re.sub(r" {2,}\n", " <br%s\n" % self.empty_element_suffix, text)
  629. return text
  630. # "Sorta" because auto-links are identified as "tag" tokens.
  631. _sorta_html_tokenize_re = re.compile(r"""
  632. (
  633. # tag
  634. </?
  635. (?:\w+) # tag name
  636. (?:\s+(?:[\w-]+:)?[\w-]+=(?:".*?"|'.*?'))* # attributes
  637. \s*/?>
  638. |
  639. # auto-link (e.g., <http://www.activestate.com/>)
  640. <\w+[^>]*>
  641. |
  642. <!--.*?--> # comment
  643. |
  644. <\?.*?\?> # processing instruction
  645. )
  646. """, re.X)
  647. def _escape_special_chars(self, text):
  648. # Python markdown note: the HTML tokenization here differs from
  649. # that in Markdown.pl, hence the behaviour for subtle cases can
  650. # differ (I believe the tokenizer here does a better job because
  651. # it isn't susceptible to unmatched '<' and '>' in HTML tags).
  652. # Note, however, that '>' is not allowed in an auto-link URL
  653. # here.
  654. escaped = []
  655. is_html_markup = False
  656. for token in self._sorta_html_tokenize_re.split(text):
  657. if is_html_markup:
  658. # Within tags/HTML-comments/auto-links, encode * and _
  659. # so they don't conflict with their use in Markdown for
  660. # italics and strong. We're replacing each such
  661. # character with its corresponding MD5 checksum value;
  662. # this is likely overkill, but it should prevent us from
  663. # colliding with the escape values by accident.
  664. escaped.append(token.replace('*', g_escape_table['*'])
  665. .replace('_', g_escape_table['_']))
  666. else:
  667. escaped.append(self._encode_backslash_escapes(token))
  668. is_html_markup = not is_html_markup
  669. return ''.join(escaped)
  670. def _hash_html_spans(self, text):
  671. # Used for safe_mode.
  672. def _is_auto_link(s):
  673. if ':' in s and self._auto_link_re.match(s):
  674. return True
  675. elif '@' in s and self._auto_email_link_re.match(s):
  676. return True
  677. return False
  678. tokens = []
  679. is_html_markup = False
  680. for token in self._sorta_html_tokenize_re.split(text):
  681. if is_html_markup and not _is_auto_link(token):
  682. sanitized = self._sanitize_html(token)
  683. key = _hash_text(sanitized)
  684. self.html_spans[key] = sanitized
  685. tokens.append(key)
  686. else:
  687. tokens.append(token)
  688. is_html_markup = not is_html_markup
  689. return ''.join(tokens)
  690. def _unhash_html_spans(self, text):
  691. for key, sanitized in self.html_spans.items():
  692. text = text.replace(key, sanitized)
  693. return text
  694. def _sanitize_html(self, s):
  695. if self.safe_mode == "replace":
  696. return self.html_removed_text
  697. elif self.safe_mode == "escape":
  698. replacements = [
  699. ('&', '&amp;'),
  700. ('<', '&lt;'),
  701. ('>', '&gt;'),
  702. ]
  703. for before, after in replacements:
  704. s = s.replace(before, after)
  705. return s
  706. else:
  707. raise MarkdownError("invalid value for 'safe_mode': %r (must be "
  708. "'escape' or 'replace')" % self.safe_mode)
  709. _tail_of_inline_link_re = re.compile(r'''
  710. # Match tail of: [text](/url/) or [text](/url/ "title")
  711. \( # literal paren
  712. [ \t]*
  713. (?P<url> # \1
  714. <.*?>
  715. |
  716. .*?
  717. )
  718. [ \t]*
  719. ( # \2
  720. (['"]) # quote char = \3
  721. (?P<title>.*?)
  722. \3 # matching quote
  723. )? # title is optional
  724. \)
  725. ''', re.X | re.S)
  726. _tail_of_reference_link_re = re.compile(r'''
  727. # Match tail of: [text][id]
  728. [ ]? # one optional space
  729. (?:\n[ ]*)? # one optional newline followed by spaces
  730. \[
  731. (?P<id>.*?)
  732. \]
  733. ''', re.X | re.S)
  734. def _do_links(self, text):
  735. """Turn Markdown link shortcuts into XHTML <a> and <img> tags.
  736. This is a combination of Markdown.pl's _DoAnchors() and
  737. _DoImages(). They are done together because that simplified the
  738. approach. It was necessary to use a different approach than
  739. Markdown.pl because of the lack of atomic matching support in
  740. Python's regex engine used in $g_nested_brackets.
  741. """
  742. MAX_LINK_TEXT_SENTINEL = 3000 # markdown2 issue 24
  743. # `anchor_allowed_pos` is used to support img links inside
  744. # anchors, but not anchors inside anchors. An anchor's start
  745. # pos must be `>= anchor_allowed_pos`.
  746. anchor_allowed_pos = 0
  747. curr_pos = 0
  748. while True: # Handle the next link.
  749. # The next '[' is the start of:
  750. # - an inline anchor: [text](url "title")
  751. # - a reference anchor: [text][id]
  752. # - an inline img: ![text](url "title")
  753. # - a reference img: ![text][id]
  754. # - a footnote ref: [^id]
  755. # (Only if 'footnotes' extra enabled)
  756. # - a footnote defn: [^id]: ...
  757. # (Only if 'footnotes' extra enabled) These have already
  758. # been stripped in _strip_footnote_definitions() so no
  759. # need to watch for them.
  760. # - a link definition: [id]: url "title"
  761. # These have already been stripped in
  762. # _strip_link_definitions() so no need to watch for them.
  763. # - not markup: [...anything else...
  764. try:
  765. start_idx = text.index('[', curr_pos)
  766. except ValueError:
  767. break
  768. text_length = len(text)
  769. # Find the matching closing ']'.
  770. # Markdown.pl allows *matching* brackets in link text so we
  771. # will here too. Markdown.pl *doesn't* currently allow
  772. # matching brackets in img alt text -- we'll differ in that
  773. # regard.
  774. bracket_depth = 0
  775. for p in range(start_idx+1, min(start_idx+MAX_LINK_TEXT_SENTINEL,
  776. text_length)):
  777. ch = text[p]
  778. if ch == ']':
  779. bracket_depth -= 1
  780. if bracket_depth < 0:
  781. break
  782. elif ch == '[':
  783. bracket_depth += 1
  784. else:
  785. # Closing bracket not found within sentinel length.
  786. # This isn't markup.
  787. curr_pos = start_idx + 1
  788. continue
  789. link_text = text[start_idx+1:p]
  790. # Possibly a footnote ref?
  791. if "footnotes" in self.extras and link_text.startswith("^"):
  792. normed_id = re.sub(r'\W', '-', link_text[1:])
  793. if normed_id in self.footnotes:
  794. self.footnote_ids.append(normed_id)
  795. result = '<sup class="footnote-ref" id="fnref-%s">' \
  796. '<a href="#fn-%s">%s</a></sup>' \
  797. % (normed_id, normed_id, len(self.footnote_ids))
  798. text = text[:start_idx] + result + text[p+1:]
  799. else:
  800. # This id isn't defined, leave the markup alone.
  801. curr_pos = p+1
  802. continue
  803. # Now determine what this is by the remainder.
  804. p += 1
  805. if p == text_length:
  806. return text
  807. # Inline anchor or img?
  808. if text[p] == '(': # attempt at perf improvement
  809. match = self._tail_of_inline_link_re.match(text, p)
  810. if match:
  811. # Handle an inline anchor or img.
  812. is_img = start_idx > 0 and text[start_idx-1] == "!"
  813. if is_img:
  814. start_idx -= 1
  815. url, title = match.group("url"), match.group("title")
  816. if url and url[0] == '<':
  817. url = url[1:-1] # '<url>' -> 'url'
  818. # We've got to encode these to avoid conflicting
  819. # with italics/bold.
  820. url = url.replace('*', g_escape_table['*']) \
  821. .replace('_', g_escape_table['_']) \
  822. .replace('"', '&quo;')
  823. if url.startswith('javascript:'):
  824. url = url.replace('javascript:', '', 1)
  825. if title:
  826. title_str = ' title="%s"' \
  827. % title.replace('*', g_escape_table['*']) \
  828. .replace('_', g_escape_table['_']) \
  829. .replace('"', '&quot;')
  830. else:
  831. title_str = ''
  832. if is_img:
  833. if 'imgless' in self.extras:
  834. result = '[Image: <a href="%s" alt="a link to an image">%s</a> (%s)]' \
  835. % (url.replace('"', '&quot;'),
  836. title,
  837. link_text.replace('"', '&quot;'))
  838. else:
  839. result = '<img src="%s" alt="%s"%s%s' \
  840. % (url.replace('"', '&quot;'),
  841. link_text.replace('"', '&quot;'),
  842. title_str, self.empty_element_suffix)
  843. curr_pos = start_idx + len(result)
  844. text = text[:start_idx] + result + text[match.end():]
  845. elif start_idx >= anchor_allowed_pos:
  846. result_head = '<a href="%s"%s>' % (url, title_str)
  847. result = '%s%s</a>' % (result_head, link_text)
  848. # <img> allowed from curr_pos on, <a> from
  849. # anchor_allowed_pos on.
  850. curr_pos = start_idx + len(result_head)
  851. anchor_allowed_pos = start_idx + len(result)
  852. text = text[:start_idx] + result + text[match.end():]
  853. else:
  854. # Anchor not allowed here.
  855. curr_pos = start_idx + 1
  856. continue
  857. # Reference anchor or img?
  858. else:
  859. match = self._tail_of_reference_link_re.match(text, p)
  860. if match:
  861. # Handle a reference-style anchor or img.
  862. is_img = start_idx > 0 and text[start_idx-1] == "!"
  863. if is_img:
  864. start_idx -= 1
  865. link_id = match.group("id").lower()
  866. if not link_id:
  867. link_id = link_text.lower() # for links like [this][]
  868. if link_id in self.urls:
  869. url = self.urls[link_id]
  870. # We've got to encode these to avoid conflicting
  871. # with italics/bold.
  872. url = url.replace('*', g_escape_table['*']) \
  873. .replace('_', g_escape_table['_']) \
  874. .replace('"', '&quo;')
  875. if url.startswith('javascript:'):
  876. url = url.replace('javascript:', '', 1)
  877. title = self.titles.get(link_id)
  878. if title:
  879. title = title.replace('*', g_escape_table['*']) \
  880. .replace('_', g_escape_table['_'])
  881. title_str = ' title="%s"' % title
  882. else:
  883. title_str = ''
  884. if is_img:
  885. if 'imgless' in self.extras:
  886. result = '[Image: <a href="%s" alt="a link to an image">%s</a> (%s)]' \
  887. % (url.replace('"', '&quot;'),
  888. title,
  889. link_text.replace('"', '&quot;'))
  890. else:
  891. result = '<img src="%s" alt="%s"%s%s' \
  892. % (url.replace('"', '&quot;'),
  893. link_text.replace('"', '&quot;'),
  894. title_str, self.empty_element_suffix)
  895. curr_pos = start_idx + len(result)
  896. text = text[:start_idx] + result + text[match.end():]
  897. elif start_idx >= anchor_allowed_pos:
  898. result = '<a href="%s"%s>%s</a>' \
  899. % (url, title_str, link_text)
  900. result_head = '<a href="%s"%s>' % (url, title_str)
  901. result = '%s%s</a>' % (result_head, link_text)
  902. # <img> allowed from curr_pos on, <a> from
  903. # anchor_allowed_pos on.
  904. curr_pos = start_idx + len(result_head)
  905. anchor_allowed_pos = start_idx + len(result)
  906. text = text[:start_idx] + result + text[match.end():]
  907. else:
  908. # Anchor not allowed here.
  909. curr_pos = start_idx + 1
  910. else:
  911. # This id isn't defined, leave the markup alone.
  912. curr_pos = match.end()
  913. continue
  914. # Otherwise, it isn't markup.
  915. curr_pos = start_idx + 1
  916. return text
  917. def header_id_from_text(self, text, prefix):
  918. """Generate a header id attribute value from the given header
  919. HTML content.
  920. This is only called if the "header-ids" extra is enabled.
  921. Subclasses may override this for different header ids.
  922. """
  923. header_id = _slugify(text)
  924. if prefix:
  925. header_id = prefix + '-' + header_id
  926. if header_id in self._count_from_header_id:
  927. self._count_from_header_id[header_id] += 1
  928. header_id += '-%s' % self._count_from_header_id[header_id]
  929. else:
  930. self._count_from_header_id[header_id] = 1
  931. return header_id
  932. _toc = None
  933. def _toc_add_entry(self, level, id, name):
  934. if self._toc is None:
  935. self._toc = []
  936. self._toc.append((level, id, name))
  937. _setext_h_re = re.compile(r'^(.+)[ \t]*\n(=+|-+)[ \t]*\n+', re.M)
  938. def _setext_h_sub(self, match):
  939. n = {"=": 1, "-": 2}[match.group(2)[0]]
  940. demote_headers = self.extras.get("demote-headers")
  941. if demote_headers:
  942. n = min(n + demote_headers, 6)
  943. header_id_attr = ""
  944. if "header-ids" in self.extras:
  945. header_id = self.header_id_from_text(match.group(1),
  946. prefix=self.extras["header-ids"])
  947. header_id_attr = ' id="%s"' % header_id
  948. html = self._run_span_gamut(match.group(1))
  949. if "toc" in self.extras:
  950. self._toc_add_entry(n, header_id, html)
  951. return "<h%d%s>%s</h%d>\n\n" % (n, header_id_attr, html, n)
  952. _atx_h_re = re.compile(r'''
  953. ^(\#{1,6}) # \1 = string of #'s
  954. [ \t]*
  955. (.+?) # \2 = Header text
  956. [ \t]*
  957. (?<!\\) # ensure not an escaped trailing '#'
  958. \#* # optional closing #'s (not counted)
  959. \n+
  960. ''', re.X | re.M)
  961. def _atx_h_sub(self, match):
  962. n = len(match.group(1))
  963. demote_headers = self.extras.get("demote-headers")
  964. if demote_headers:
  965. n = min(n + demote_headers, 6)
  966. header_id_attr = ""
  967. if "header-ids" in self.extras:
  968. header_id = self.header_id_from_text(match.group(2),
  969. prefix=self.extras["header-ids"])
  970. header_id_attr = ' id="%s"' % header_id
  971. html = self._run_span_gamut(match.group(2))
  972. if "toc" in self.extras:
  973. self._toc_add_entry(n, header_id, html)
  974. return "<h%d%s>%s</h%d>\n\n" % (n, header_id_attr, html, n)
  975. def _do_headers(self, text):
  976. # Setext-style headers:
  977. # Header 1
  978. # ========
  979. #
  980. # Header 2
  981. # --------
  982. text = self._setext_h_re.sub(self._setext_h_sub, text)
  983. # atx-style headers:
  984. # # Header 1
  985. # ## Header 2
  986. # ## Header 2 with closing hashes ##
  987. # ...
  988. # ###### Header 6
  989. text = self._atx_h_re.sub(self._atx_h_sub, text)
  990. return text
  991. _marker_ul_chars = '*+-'
  992. _marker_any = r'(?:[%s]|\d+\.)' % _marker_ul_chars
  993. _marker_ul = '(?:[%s])' % _marker_ul_chars
  994. _marker_ol = r'(?:\d+\.)'
  995. def _list_sub(self, match):
  996. lst = match.group(1)
  997. lst_type = match.group(3) in self._marker_ul_chars and "ul" or "ol"
  998. result = self._process_list_items(lst)
  999. if self.list_level:
  1000. return "<%s>\n%s</%s>\n" % (lst_type, result, lst_type)
  1001. else:
  1002. return "<%s>\n%s</%s>\n\n" % (lst_type, result, lst_type)
  1003. def _do_lists(self, text):
  1004. # Form HTML ordered (numbered) and unordered (bulleted) lists.
  1005. for marker_pat in (self._marker_ul, self._marker_ol):
  1006. # Re-usable pattern to match any entire ul or ol list:
  1007. less_than_tab = self.tab_width - 1
  1008. whole_list = r'''
  1009. ( # \1 = whole list
  1010. ( # \2
  1011. [ ]{0,%d}
  1012. (%s) # \3 = first list item marker
  1013. [ \t]+
  1014. )
  1015. (?:.+?)
  1016. ( # \4
  1017. \Z
  1018. |
  1019. \n{2,}
  1020. (?=\S)
  1021. (?! # Negative lookahead for another list item marker
  1022. [ \t]*
  1023. %s[ \t]+
  1024. )
  1025. )
  1026. )
  1027. ''' % (less_than_tab, marker_pat, marker_pat)
  1028. # We use a different prefix before nested lists than top-level lists.
  1029. # See extended comment in _process_list_items().
  1030. #
  1031. # Note: There's a bit of duplication here. My original implementation
  1032. # created a scalar regex pattern as the conditional result of the test on
  1033. # $g_list_level, and then only ran the $text =~ s{...}{...}egmx
  1034. # substitution once, using the scalar as the pattern. This worked,
  1035. # everywhere except when running under MT on my hosting account at Pair
  1036. # Networks. There, this caused all rebuilds to be killed by the reaper (or
  1037. # perhaps they crashed, but that seems incredibly unlikely given that the
  1038. # same script on the same server ran fine *except* under MT. I've spent
  1039. # more time trying to figure out why this is happening than I'd like to
  1040. # admit. My only guess, backed up by the fact that this workaround works,
  1041. # is that Perl optimizes the substition when it can figure out that the
  1042. # pattern will never change, and when this optimization isn't on, we run
  1043. # afoul of the reaper. Thus, the slightly redundant code to that uses two
  1044. # static s/// patterns rather than one conditional pattern.
  1045. if self.list_level:
  1046. sub_list_re = re.compile("^"+whole_list, re.X | re.M | re.S)
  1047. text = sub_list_re.sub(self._list_sub, text)
  1048. else:
  1049. list_re = re.compile(r"(?:(?<=\n\n)|\A\n?)"+whole_list,
  1050. re.X | re.M | re.S)
  1051. text = list_re.sub(self._list_sub, text)
  1052. return text
  1053. _list_item_re = re.compile(r'''
  1054. (\n)? # leading line = \1
  1055. (^[ \t]*) # leading whitespace = \2
  1056. (?P<marker>%s) [ \t]+ # list marker = \3
  1057. ((?:.+?) # list item text = \4
  1058. (\n{1,2})) # eols = \5
  1059. (?= \n* (\Z | \2 (?P<next_marker>%s) [ \t]+))
  1060. ''' % (_marker_any, _marker_any),
  1061. re.M | re.X | re.S)
  1062. _last_li_endswith_two_eols = False
  1063. def _list_item_sub(self, match):
  1064. item = match.group(4)
  1065. leading_line = match.group(1)
  1066. leading_space = match.group(2)
  1067. if leading_line or "\n\n" in item or self._last_li_endswith_two_eols:
  1068. item = self._run_block_gamut(self._outdent(item))
  1069. else:
  1070. # Recursion for sub-lists:
  1071. item

Large files files are truncated, but you can click here to view the full file