PageRenderTime 128ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 1ms

/compat/markdown2.py

https://bitbucket.org/piranha/byteflow/
Python | 1887 lines | 1666 code | 65 blank | 156 comment | 85 complexity | 3cdc2e02ecf0881a477716610409ec2b MD5 | raw file

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

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