[Python-modules-commits] [python-markdown] 01/05: Import python-markdown_2.6.8.orig.tar.gz

Dmitry Shachnev mitya57 at moszumanska.debian.org
Thu Jan 26 07:21:48 UTC 2017


This is an automated email from the git hooks/post-receive script.

mitya57 pushed a commit to branch master
in repository python-markdown.

commit c384fd162eb3bb556302711145cb694af01cf9f7
Author: Dmitry Shachnev <mitya57 at gmail.com>
Date:   Thu Jan 26 09:57:47 2017 +0300

    Import python-markdown_2.6.8.orig.tar.gz
---
 PKG-INFO                             |   4 +-
 docs/extensions/code_hilite.txt      |   4 +-
 docs/reference.txt                   |  10 +--
 markdown/__init__.py                 |   6 +-
 markdown/__version__.py              |   3 +-
 markdown/blockprocessors.py          |   5 +-
 markdown/extensions/attr_list.py     |   3 +-
 markdown/extensions/codehilite.py    |   2 +-
 markdown/extensions/fenced_code.py   |   2 +-
 markdown/extensions/footnotes.py     | 131 +++++++++++++++++++++++----
 markdown/extensions/tables.py        | 166 +++++++++++++++++++++++------------
 markdown/inlinepatterns.py           |  15 ++--
 markdown/preprocessors.py            |   8 +-
 markdown/treeprocessors.py           |   4 +-
 markdown/util.py                     |   2 +-
 setup.py                             |   2 +
 tests/extensions/extra/footnote.html |  38 ++++++++
 tests/extensions/extra/footnote.txt  |  42 +++++++++
 tests/extensions/extra/tables.html   | 125 ++++++++++++++++++++++++++
 tests/extensions/extra/tables.txt    |  57 +++++++++++-
 tests/misc/backtick-escape.html      |   5 +-
 tests/misc/backtick-escape.txt       |   5 +-
 tests/misc/blockquote-hr.html        |   9 ++
 tests/misc/blockquote-hr.txt         |   6 ++
 tests/misc/html.html                 |  11 +++
 tests/misc/html.txt                  |  11 +++
 tests/test_apis.py                   |  12 +++
 27 files changed, 577 insertions(+), 111 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index c5615b4..ba511e7 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,12 +1,12 @@
 Metadata-Version: 1.1
 Name: Markdown
-Version: 2.6.7
+Version: 2.6.8
 Summary: Python implementation of Markdown.
 Home-page: https://pythonhosted.org/Markdown/
 Author: Waylan Limberg
 Author-email: waylan.limberg [at] icloud.com
 License: BSD License
-Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-2.6.7.tar.gz
+Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-2.6.8.tar.gz
 Description: 
         This is a Python implementation of John Gruber's Markdown_.
         It is almost completely compliant with the reference implementation,
diff --git a/docs/extensions/code_hilite.txt b/docs/extensions/code_hilite.txt
index c17d688..52c101e 100644
--- a/docs/extensions/code_hilite.txt
+++ b/docs/extensions/code_hilite.txt
@@ -36,7 +36,7 @@ The CSS rules either need to be defined in or linked from the header of your
 HTML templates. Pygments can generate CSS rules for you. Just run the following
 command from the command line:
 
-    pygmentize -S default -f html -a codehilite > styles.css
+    pygmentize -S default -f html -a .codehilite > styles.css
 
 If you are using a different `css_class` (default: `codehilite`), then
 set the value of the `-a` option to that class name. The CSS rules will be
@@ -47,7 +47,7 @@ If you would like to use a different theme, swap out `default` for the desired
 theme. For a list of themes installed on your system (additional themes can be
 installed via Pygments plugins), run the following command:
 
-    pygmetize -L style
+    pygmentize -L style
 
 !!! see also
 
diff --git a/docs/reference.txt b/docs/reference.txt
index 7268cc6..b65f2dd 100644
--- a/docs/reference.txt
+++ b/docs/reference.txt
@@ -128,16 +128,12 @@ The following options are available on the `markdown.markdown` function:
     
     The dictionary of configuration settings must be in the following format:
 
-        extension_configs = 
-        {
-            'extension_name_1':
-            {
+        extension_configs = {
+            'extension_name_1': {
                 'option_1': 'value_1',
                 'option_2': 'value_2'
             },
-        {
-            'extension_name_2':
-            {
+            'extension_name_2': {
                 'option_1': 'value_1'
             }
         }
diff --git a/markdown/__init__.py b/markdown/__init__.py
index 78ea4cb..409f9cf 100644
--- a/markdown/__init__.py
+++ b/markdown/__init__.py
@@ -75,9 +75,6 @@ class Markdown(object):
         'xhtml5': to_xhtml_string,
     }
 
-    ESCAPED_CHARS = ['\\', '`', '*', '_', '{', '}', '[', ']',
-                     '(', ')', '>', '#', '+', '-', '.', '!']
-
     def __init__(self, *args, **kwargs):
         """
         Creates a new Markdown instance.
@@ -147,6 +144,9 @@ class Markdown(object):
                           'deprecated along with "safe_mode".',
                           DeprecationWarning)
 
+        self.ESCAPED_CHARS = ['\\', '`', '*', '_', '{', '}', '[', ']',
+                              '(', ')', '>', '#', '+', '-', '.', '!']
+
         self.registeredExtensions = []
         self.docType = ""
         self.stripTopLevelTags = True
diff --git a/markdown/__version__.py b/markdown/__version__.py
index 339f039..56b4260 100644
--- a/markdown/__version__.py
+++ b/markdown/__version__.py
@@ -5,7 +5,7 @@
 # (major, minor, micro, alpha/beta/rc/final, #)
 # (1, 1, 2, 'alpha', 0) => "1.1.2.dev"
 # (1, 2, 0, 'beta', 2) => "1.2b2"
-version_info = (2, 6, 7, 'final', 0)
+version_info = (2, 6, 8, 'final', 0)
 
 
 def _get_version():
@@ -26,4 +26,5 @@ def _get_version():
 
     return str(main + sub)
 
+
 version = _get_version()
diff --git a/markdown/blockprocessors.py b/markdown/blockprocessors.py
index 870151b..a3ed977 100644
--- a/markdown/blockprocessors.py
+++ b/markdown/blockprocessors.py
@@ -493,15 +493,16 @@ class HRProcessor(BlockProcessor):
 
     def run(self, parent, blocks):
         block = blocks.pop(0)
+        match = self.match
         # Check for lines in block before hr.
-        prelines = block[:self.match.start()].rstrip('\n')
+        prelines = block[:match.start()].rstrip('\n')
         if prelines:
             # Recursively parse lines before hr so they get parsed first.
             self.parser.parseBlocks(parent, [prelines])
         # create hr
         util.etree.SubElement(parent, 'hr')
         # check for lines in block after hr.
-        postlines = block[self.match.end():].lstrip('\n')
+        postlines = block[match.end():].lstrip('\n')
         if postlines:
             # Add lines after hr to master blocks for later parsing.
             blocks.insert(0, postlines)
diff --git a/markdown/extensions/attr_list.py b/markdown/extensions/attr_list.py
index c897a9c..a7f92b6 100644
--- a/markdown/extensions/attr_list.py
+++ b/markdown/extensions/attr_list.py
@@ -52,6 +52,7 @@ def _handle_word(s, t):
         return 'id', t[1:]
     return t, t
 
+
 _scanner = Scanner([
     (r'[^ =]+=".*?"', _handle_double_quote),
     (r"[^ =]+='.*?'", _handle_single_quote),
@@ -83,7 +84,7 @@ class AttrListTreeprocessor(Treeprocessor):
                          r'\:\-\.0-9\u00b7\u0300-\u036f\u203f-\u2040]+')
 
     def run(self, doc):
-        for elem in doc.getiterator():
+        for elem in doc.iter():
             if isBlockLevel(elem.tag):
                 # Block level: check for attrs on last line of text
                 RE = self.BLOCK_RE
diff --git a/markdown/extensions/codehilite.py b/markdown/extensions/codehilite.py
index 0657c37..20b889c 100644
--- a/markdown/extensions/codehilite.py
+++ b/markdown/extensions/codehilite.py
@@ -166,7 +166,7 @@ class CodeHilite(object):
         c = re.compile(r'''
             (?:(?:^::+)|(?P<shebang>^[#]!)) # Shebang or 2 or more colons
             (?P<path>(?:/\w+)*[/ ])?        # Zero or 1 path
-            (?P<lang>[\w+-]*)               # The language
+            (?P<lang>[\w#.+-]*)             # The language
             \s*                             # Arbitrary whitespace
             # Optional highlight lines, single- or double-quote-delimited
             (hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))?
diff --git a/markdown/extensions/fenced_code.py b/markdown/extensions/fenced_code.py
index 8c9935e..277bac4 100644
--- a/markdown/extensions/fenced_code.py
+++ b/markdown/extensions/fenced_code.py
@@ -37,7 +37,7 @@ class FencedCodeExtension(Extension):
 class FencedBlockPreprocessor(Preprocessor):
     FENCED_BLOCK_RE = re.compile(r'''
 (?P<fence>^(?:~{3,}|`{3,}))[ ]*         # Opening ``` or ~~~
-(\{?\.?(?P<lang>[a-zA-Z0-9_+-]*))?[ ]*  # Optional {, and lang
+(\{?\.?(?P<lang>[\w#.+-]*))?[ ]*        # Optional {, and lang
 # Optional highlight lines, single- or double-quote-delimited
 (hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))?[ ]*
 }?[ ]*\n                                # Optional closing }
diff --git a/markdown/extensions/footnotes.py b/markdown/extensions/footnotes.py
index b52815f..8bd8595 100644
--- a/markdown/extensions/footnotes.py
+++ b/markdown/extensions/footnotes.py
@@ -20,14 +20,16 @@ from ..preprocessors import Preprocessor
 from ..inlinepatterns import Pattern
 from ..treeprocessors import Treeprocessor
 from ..postprocessors import Postprocessor
-from ..util import etree, text_type
+from .. import util
 from ..odict import OrderedDict
 import re
+import copy
 
-FN_BACKLINK_TEXT = "zz1337820767766393qq"
-NBSP_PLACEHOLDER = "qq3936677670287331zz"
+FN_BACKLINK_TEXT = util.STX + "zz1337820767766393qq" + util.ETX
+NBSP_PLACEHOLDER = util.STX + "qq3936677670287331zz" + util.ETX
 DEF_RE = re.compile(r'[ ]{0,3}\[\^([^\]]*)\]:\s*(.*)')
 TABBED_RE = re.compile(r'((\t)|(    ))(.*)')
+RE_REF_ID = re.compile(r'(fnref)(\d+)')
 
 
 class FootnoteExtension(Extension):
@@ -53,6 +55,8 @@ class FootnoteExtension(Extension):
 
         # In multiple invocations, emit links that don't get tangled.
         self.unique_prefix = 0
+        self.found_refs = {}
+        self.used_refs = set()
 
         self.reset()
 
@@ -76,6 +80,15 @@ class FootnoteExtension(Extension):
         md.treeprocessors.add(
             "footnote", FootnoteTreeprocessor(self), "_begin"
         )
+
+        # Insert a tree-processor that will run after inline is done.
+        # In this tree-processor we want to check our duplicate footnote tracker
+        # And add additional backrefs to the footnote pointing back to the
+        # duplicated references.
+        md.treeprocessors.add(
+            "footnote-duplicate", FootnotePostTreeprocessor(self), '>inline'
+        )
+
         # Insert a postprocessor after amp_substitute oricessor
         md.postprocessors.add(
             "footnote", FootnotePostprocessor(self), ">amp_substitute"
@@ -85,6 +98,29 @@ class FootnoteExtension(Extension):
         """ Clear footnotes on reset, and prepare for distinct document. """
         self.footnotes = OrderedDict()
         self.unique_prefix += 1
+        self.found_refs = {}
+        self.used_refs = set()
+
+    def unique_ref(self, reference, found=False):
+        """ Get a unique reference if there are duplicates. """
+        if not found:
+            return reference
+
+        original_ref = reference
+        while reference in self.used_refs:
+            ref, rest = reference.split(self.get_separator(), 1)
+            m = RE_REF_ID.match(ref)
+            if m:
+                reference = '%s%d%s%s' % (m.group(1), int(m.group(2))+1, self.get_separator(), rest)
+            else:
+                reference = '%s%d%s%s' % (ref, 2, self.get_separator(), rest)
+
+        self.used_refs.add(reference)
+        if original_ref in self.found_refs:
+            self.found_refs[original_ref] += 1
+        else:
+            self.found_refs[original_ref] = 1
+        return reference
 
     def findFootnotesPlaceholder(self, root):
         """ Return ElementTree Element that contains Footnote placeholder. """
@@ -120,13 +156,12 @@ class FootnoteExtension(Extension):
         else:
             return 'fn%s%s' % (self.get_separator(), id)
 
-    def makeFootnoteRefId(self, id):
+    def makeFootnoteRefId(self, id, found=False):
         """ Return footnote back-link id. """
         if self.getConfig("UNIQUE_IDS"):
-            return 'fnref%s%d-%s' % (self.get_separator(),
-                                     self.unique_prefix, id)
+            return self.unique_ref('fnref%s%d-%s' % (self.get_separator(), self.unique_prefix, id), found)
         else:
-            return 'fnref%s%s' % (self.get_separator(), id)
+            return self.unique_ref('fnref%s%s' % (self.get_separator(), id), found)
 
     def makeFootnotesDiv(self, root):
         """ Return div of footnotes as et Element. """
@@ -134,16 +169,23 @@ class FootnoteExtension(Extension):
         if not list(self.footnotes.keys()):
             return None
 
-        div = etree.Element("div")
+        div = util.etree.Element("div")
         div.set('class', 'footnote')
-        etree.SubElement(div, "hr")
-        ol = etree.SubElement(div, "ol")
+        util.etree.SubElement(div, "hr")
+        ol = util.etree.SubElement(div, "ol")
+        surrogate_parent = util.etree.Element("div")
 
         for id in self.footnotes.keys():
-            li = etree.SubElement(ol, "li")
+            li = util.etree.SubElement(ol, "li")
             li.set("id", self.makeFootnoteId(id))
-            self.parser.parseChunk(li, self.footnotes[id])
-            backlink = etree.Element("a")
+            # Parse footnote with surrogate parent as li cannot be used.
+            # List block handlers have special logic to deal with li.
+            # When we are done parsing, we will copy everything over to li.
+            self.parser.parseChunk(surrogate_parent, self.footnotes[id])
+            for el in list(surrogate_parent):
+                li.append(el)
+                surrogate_parent.remove(el)
+            backlink = util.etree.Element("a")
             backlink.set("href", "#" + self.makeFootnoteRefId(id))
             if self.md.output_format not in ['html5', 'xhtml5']:
                 backlink.set("rev", "footnote")  # Invalid in HTML5
@@ -161,7 +203,7 @@ class FootnoteExtension(Extension):
                     node.text = node.text + NBSP_PLACEHOLDER
                     node.append(backlink)
                 else:
-                    p = etree.SubElement(li, "p")
+                    p = util.etree.SubElement(li, "p")
                     p.append(backlink)
         return div
 
@@ -268,19 +310,72 @@ class FootnotePattern(Pattern):
     def handleMatch(self, m):
         id = m.group(2)
         if id in self.footnotes.footnotes.keys():
-            sup = etree.Element("sup")
-            a = etree.SubElement(sup, "a")
-            sup.set('id', self.footnotes.makeFootnoteRefId(id))
+            sup = util.etree.Element("sup")
+            a = util.etree.SubElement(sup, "a")
+            sup.set('id', self.footnotes.makeFootnoteRefId(id, found=True))
             a.set('href', '#' + self.footnotes.makeFootnoteId(id))
             if self.footnotes.md.output_format not in ['html5', 'xhtml5']:
                 a.set('rel', 'footnote')  # invalid in HTML5
             a.set('class', 'footnote-ref')
-            a.text = text_type(self.footnotes.footnotes.index(id) + 1)
+            a.text = util.text_type(self.footnotes.footnotes.index(id) + 1)
             return sup
         else:
             return None
 
 
+class FootnotePostTreeprocessor(Treeprocessor):
+    """ Ammend footnote div with duplicates. """
+
+    def __init__(self, footnotes):
+        self.footnotes = footnotes
+
+    def add_duplicates(self, li, duplicates):
+        """ Adjust current li and add the duplicates: fnref2, fnref3, etc. """
+        for link in li.iter('a'):
+            # Find the link that needs to be duplicated.
+            if link.attrib.get('class', '') == 'footnote-backref':
+                ref, rest = link.attrib['href'].split(self.footnotes.get_separator(), 1)
+                # Duplicate link the number of times we need to
+                # and point the to the appropriate references.
+                links = []
+                for index in range(2, duplicates + 1):
+                    sib_link = copy.deepcopy(link)
+                    sib_link.attrib['href'] = '%s%d%s%s' % (ref, index, self.footnotes.get_separator(), rest)
+                    links.append(sib_link)
+                    self.offset += 1
+                # Add all the new duplicate links.
+                el = list(li)[-1]
+                for l in links:
+                    el.append(l)
+                break
+
+    def get_num_duplicates(self, li):
+        """ Get the number of duplicate refs of the footnote. """
+        fn, rest = li.attrib.get('id', '').split(self.footnotes.get_separator(), 1)
+        link_id = '%sref%s%s' % (fn, self.footnotes.get_separator(), rest)
+        return self.footnotes.found_refs.get(link_id, 0)
+
+    def handle_duplicates(self, parent):
+        """ Find duplicate footnotes and format and add the duplicates. """
+        for li in list(parent):
+            # Check number of duplicates footnotes and insert
+            # additional links if needed.
+            count = self.get_num_duplicates(li)
+            if count > 1:
+                self.add_duplicates(li, count)
+
+    def run(self, root):
+        """ Crawl the footnote div and add missing duplicate footnotes. """
+        self.offset = 0
+        for div in root.iter('div'):
+            if div.attrib.get('class', '') == 'footnote':
+                # Footnotes shoul be under the first orderd list under
+                # the footnote div.  So once we find it, quit.
+                for ol in div.iter('ol'):
+                    self.handle_duplicates(ol)
+                    break
+
+
 class FootnoteTreeprocessor(Treeprocessor):
     """ Build and append footnote div to end of document. """
 
diff --git a/markdown/extensions/tables.py b/markdown/extensions/tables.py
index 8c11739..ebe6ffa 100644
--- a/markdown/extensions/tables.py
+++ b/markdown/extensions/tables.py
@@ -19,32 +19,50 @@ from __future__ import absolute_import
 from __future__ import unicode_literals
 from . import Extension
 from ..blockprocessors import BlockProcessor
-from ..inlinepatterns import BacktickPattern, BACKTICK_RE
 from ..util import etree
+import re
 
 
 class TableProcessor(BlockProcessor):
     """ Process Tables. """
 
+    RE_CODE_PIPES = re.compile(r'(?:(\\\\)|(\\`+)|(`+)|(\\\|)|(\|))')
+    RE_END_BORDER = re.compile(r'(?<!\\)(?:\\\\)*\|$')
+
+    def __init__(self, parser):
+        self.border = False
+        self.separator = ''
+        super(TableProcessor, self).__init__(parser)
+
     def test(self, parent, block):
-        rows = block.split('\n')
-        return (len(rows) > 1 and '|' in rows[0] and
-                '|' in rows[1] and '-' in rows[1] and
-                rows[1].strip()[0] in ['|', ':', '-'])
+        """
+        Ensure first two rows (column header and separator row) are valid table rows.
+
+        Keep border check and separator row do avoid repeating the work.
+        """
+        is_table = False
+        header = [row.strip() for row in block.split('\n')[0:2]]
+        if len(header) == 2:
+            self.border = header[0].startswith('|')
+            row = self._split_row(header[0])
+            is_table = len(row) > 1
+
+            if is_table:
+                row = self._split_row(header[1])
+                is_table = len(row) > 1 and set(''.join(row)) <= set('|:- ')
+                if is_table:
+                    self.separator = row
+        return is_table
 
     def run(self, parent, blocks):
         """ Parse a table block and build table. """
         block = blocks.pop(0).split('\n')
         header = block[0].strip()
-        seperator = block[1].strip()
         rows = [] if len(block) < 3 else block[2:]
-        # Get format type (bordered by pipes or not)
-        border = False
-        if header.startswith('|'):
-            border = True
+
         # Get alignment of columns
         align = []
-        for c in self._split_row(seperator, border):
+        for c in self.separator:
             c = c.strip()
             if c.startswith(':') and c.endswith(':'):
                 align.append('center')
@@ -54,21 +72,22 @@ class TableProcessor(BlockProcessor):
                 align.append('right')
             else:
                 align.append(None)
+
         # Build table
         table = etree.SubElement(parent, 'table')
         thead = etree.SubElement(table, 'thead')
-        self._build_row(header, thead, align, border)
+        self._build_row(header, thead, align)
         tbody = etree.SubElement(table, 'tbody')
         for row in rows:
-            self._build_row(row.strip(), tbody, align, border)
+            self._build_row(row.strip(), tbody, align)
 
-    def _build_row(self, row, parent, align, border):
+    def _build_row(self, row, parent, align):
         """ Given a row of text, build table cells. """
         tr = etree.SubElement(parent, 'tr')
         tag = 'td'
         if parent.tag == 'thead':
             tag = 'th'
-        cells = self._split_row(row, border)
+        cells = self._split_row(row)
         # We use align here rather than cells to ensure every row
         # contains the same number of columns.
         for i, a in enumerate(align):
@@ -80,63 +99,94 @@ class TableProcessor(BlockProcessor):
             if a:
                 c.set('align', a)
 
-    def _split_row(self, row, border):
+    def _split_row(self, row):
         """ split a row of text into list of cells. """
-        if border:
+        if self.border:
             if row.startswith('|'):
                 row = row[1:]
-            if row.endswith('|'):
-                row = row[:-1]
-        return self._split(row, '|')
+            row = self.RE_END_BORDER.sub('', row)
+        return self._split(row)
 
-    def _split(self, row, marker):
+    def _split(self, row):
         """ split a row of text with some code into a list of cells. """
-        if self._row_has_unpaired_backticks(row):
-            # fallback on old behaviour
-            return row.split(marker)
-        # modify the backtick pattern to only match at the beginning of the search string
-        backtick_pattern = BacktickPattern('^' + BACKTICK_RE)
         elements = []
-        current = ''
-        i = 0
-        while i < len(row):
-            letter = row[i]
-            if letter == marker:
-                if current != '' or len(elements) == 0:
-                    # Don't append empty string unless it is the first element
-                    # The border is already removed when we get the row, then the line is strip()'d
-                    # If the first element is a marker, then we have an empty first cell
-                    elements.append(current)
-                current = ''
-            else:
-                match = backtick_pattern.getCompiledRegExp().match(row[i:])
-                if not match:
-                    current += letter
-                else:
-                    groups = match.groups()
-                    delim = groups[1]  # the code block delimeter (ie 1 or more backticks)
-                    row_contents = groups[2]  # the text contained inside the code block
-                    i += match.start(4) - 1  # jump pointer to the beginning of the rest of the text (group #4)
-                    element = delim + row_contents + delim  # reinstert backticks
-                    current += element
-            i += 1
-        elements.append(current)
+        pipes = []
+        tics = []
+        tic_points = []
+        tic_region = []
+        good_pipes = []
+
+        # Parse row
+        # Throw out \\, and \|
+        for m in self.RE_CODE_PIPES.finditer(row):
+            # Store ` data (len, start_pos, end_pos)
+            if m.group(2):
+                # \`+
+                # Store length of each tic group: subtract \
+                tics.append(len(m.group(2)) - 1)
+                # Store start of group, end of group, and escape length
+                tic_points.append((m.start(2), m.end(2) - 1, 1))
+            elif m.group(3):
+                # `+
+                # Store length of each tic group
+                tics.append(len(m.group(3)))
+                # Store start of group, end of group, and escape length
+                tic_points.append((m.start(3), m.end(3) - 1, 0))
+            # Store pipe location
+            elif m.group(5):
+                pipes.append(m.start(5))
+
+        # Pair up tics according to size if possible
+        # Subtract the escape length *only* from the opening.
+        # Walk through tic list and see if tic has a close.
+        # Store the tic region (start of region, end of region).
+        pos = 0
+        tic_len = len(tics)
+        while pos < tic_len:
+            try:
+                tic_size = tics[pos] - tic_points[pos][2]
+                if tic_size == 0:
+                    raise ValueError
+                index = tics[pos + 1:].index(tic_size) + 1
+                tic_region.append((tic_points[pos][0], tic_points[pos + index][1]))
+                pos += index + 1
+            except ValueError:
+                pos += 1
+
+        # Resolve pipes.  Check if they are within a tic pair region.
+        # Walk through pipes comparing them to each region.
+        #     - If pipe position is less that a region, it isn't in a region
+        #     - If it is within a region, we don't want it, so throw it out
+        #     - If we didn't throw it out, it must be a table pipe
+        for pipe in pipes:
+            throw_out = False
+            for region in tic_region:
+                if pipe < region[0]:
+                    # Pipe is not in a region
+                    break
+                elif region[0] <= pipe <= region[1]:
+                    # Pipe is within a code region.  Throw it out.
+                    throw_out = True
+                    break
+            if not throw_out:
+                good_pipes.append(pipe)
+
+        # Split row according to table delimeters.
+        pos = 0
+        for pipe in good_pipes:
+            elements.append(row[pos:pipe])
+            pos = pipe + 1
+        elements.append(row[pos:])
         return elements
 
-    def _row_has_unpaired_backticks(self, row):
-        count_total_backtick = row.count('`')
-        count_escaped_backtick = row.count('\`')
-        count_backtick = count_total_backtick - count_escaped_backtick
-        # odd number of backticks,
-        # we won't be able to build correct code blocks
-        return count_backtick & 1
-
 
 class TableExtension(Extension):
     """ Add tables to Markdown. """
 
     def extendMarkdown(self, md, md_globals):
         """ Add an instance of TableProcessor to BlockParser. """
+        if '|' not in md.ESCAPED_CHARS:
+            md.ESCAPED_CHARS.append('|')
         md.parser.blockprocessors.add('table',
                                       TableProcessor(md.parser),
                                       '<hashheader')
diff --git a/markdown/inlinepatterns.py b/markdown/inlinepatterns.py
index 6d4c969..37c9afa 100644
--- a/markdown/inlinepatterns.py
+++ b/markdown/inlinepatterns.py
@@ -87,6 +87,7 @@ def build_inlinepatterns(md_instance, **kwargs):
         inlinePatterns["emphasis2"] = SimpleTagPattern(EMPHASIS_2_RE, 'em')
     return inlinePatterns
 
+
 """
 The actual regular expressions for patterns
 -----------------------------------------------------------------------------
@@ -102,7 +103,7 @@ BRK = (
 NOIMG = r'(?<!\!)'
 
 # `e=f()` or ``e=f("`")``
-BACKTICK_RE = r'(?<!\\)(`+)(.+?)(?<!`)\2(?!`)'
+BACKTICK_RE = r'(?:(?<!\\)((?:\\{2})+)(?=`+)|(?<!\\)(`+)(.+?)(?<!`)\3(?!`))'
 
 # \<
 ESCAPE_RE = r'\\(.)'
@@ -301,12 +302,16 @@ class BacktickPattern(Pattern):
     """ Return a `<code>` element containing the matching text. """
     def __init__(self, pattern):
         Pattern.__init__(self, pattern)
-        self.tag = "code"
+        self.ESCAPED_BSLASH = '%s%s%s' % (util.STX, ord('\\'), util.ETX)
+        self.tag = 'code'
 
     def handleMatch(self, m):
-        el = util.etree.Element(self.tag)
-        el.text = util.AtomicString(m.group(3).strip())
-        return el
+        if m.group(4):
+            el = util.etree.Element(self.tag)
+            el.text = util.AtomicString(m.group(4).strip())
+            return el
+        else:
+            return m.group(2).replace('\\\\', self.ESCAPED_BSLASH)
 
 
 class DoubleTagPattern(SimpleTagPattern):
diff --git a/markdown/preprocessors.py b/markdown/preprocessors.py
index 7ea4fcf..94f9830 100644
--- a/markdown/preprocessors.py
+++ b/markdown/preprocessors.py
@@ -258,7 +258,13 @@ class HtmlBlockPreprocessor(Preprocessor):
             else:
                 items.append(block)
 
-                right_tag, data_index = self._get_right_tag(left_tag, 0, block)
+                # Need to evaluate all items so we can calculate relative to the left index.
+                right_tag, data_index = self._get_right_tag(left_tag, left_index, ''.join(items))
+                # Adjust data_index: relative to items -> relative to last block
+                prev_block_length = 0
+                for item in items[:-1]:
+                    prev_block_length += len(item)
+                data_index -= prev_block_length
 
                 if self._equal_tags(left_tag, right_tag):
                     # if find closing tag
diff --git a/markdown/treeprocessors.py b/markdown/treeprocessors.py
index d06f192..bb76572 100644
--- a/markdown/treeprocessors.py
+++ b/markdown/treeprocessors.py
@@ -358,14 +358,14 @@ class PrettifyTreeprocessor(Treeprocessor):
         self._prettifyETree(root)
         # Do <br />'s seperately as they are often in the middle of
         # inline content and missed by _prettifyETree.
-        brs = root.getiterator('br')
+        brs = root.iter('br')
         for br in brs:
             if not br.tail or not br.tail.strip():
                 br.tail = '\n'
             else:
                 br.tail = '\n%s' % br.tail
         # Clean up extra empty lines at end of code blocks.
-        pres = root.getiterator('pre')
+        pres = root.iter('pre')
         for pre in pres:
             if len(pre) and pre[0].tag == 'code':
                 pre[0].text = util.AtomicString(pre[0].text.rstrip() + '\n')
diff --git a/markdown/util.py b/markdown/util.py
index d3d48f0..b37e5ae 100644
--- a/markdown/util.py
+++ b/markdown/util.py
@@ -32,7 +32,7 @@ BLOCK_LEVEL_ELEMENTS = re.compile(
     "|hr|hr/|style|li|dt|dd|thead|tbody"
     "|tr|th|td|section|footer|header|group|figure"
     "|figcaption|aside|article|canvas|output"
-    "|progress|video|nav)$",
+    "|progress|video|nav|main)$",
     re.IGNORECASE
 )
 # Placeholders
diff --git a/setup.py b/setup.py
index fec2dcb..0699512 100755
--- a/setup.py
+++ b/setup.py
@@ -22,6 +22,7 @@ def get_version():
     finally:
         fp.close()
 
+
 version, version_info = get_version()
 
 # Get development Status for classifiers
@@ -210,6 +211,7 @@ class md_build(build):
 
     sub_commands = build.sub_commands + [('build_docs', has_docs)]
 
+
 long_description = '''
 This is a Python implementation of John Gruber's Markdown_.
 It is almost completely compliant with the reference implementation,
diff --git a/tests/extensions/extra/footnote.html b/tests/extensions/extra/footnote.html
index e5b41a7..4cca25c 100644
--- a/tests/extensions/extra/footnote.html
+++ b/tests/extensions/extra/footnote.html
@@ -1,5 +1,10 @@
 <p>This is the body with a footnote<sup id="fnref:1"><a class="footnote-ref" href="#fn:1" rel="footnote">1</a></sup> or two<sup id="fnref:2"><a class="footnote-ref" href="#fn:2" rel="footnote">2</a></sup> or more<sup id="fnref:3"><a class="footnote-ref" href="#fn:3" rel="footnote">3</a></sup> <sup id="fnref:4"><a class="footnote-ref" href="#fn:4" rel="footnote">4</a></sup> <sup id="fnref:5"><a class="footnote-ref" href="#fn:5" rel="footnote">5</a></sup>.</p>
 <p>Also a reference that does not exist[^6].</p>
+<p>Duplicate<sup id="fnref:a"><a class="footnote-ref" href="#fn:a" rel="footnote">6</a></sup> footnotes<sup id="fnref2:a"><a class="footnote-ref" href="#fn:a" rel="footnote">6</a></sup> test<sup id="fnref3:a"><a class="footnote-ref" href="#fn:a" rel="footnote">6</a></sup>.</p>
+<p>Duplicate<sup id="fnref:b"><a class="footnote-ref" href="#fn:b" rel="footnote">7</a></sup> footnotes<sup id="fnref2:b"><a class="footnote-ref" href="#fn:b" rel="footnote">7</a></sup> test<sup id="fnref3:b"><a class="footnote-ref" href="#fn:b" rel="footnote">7</a></sup>.</p>
+<p>Single after duplicates<sup id="fnref:c"><a class="footnote-ref" href="#fn:c" rel="footnote">8</a></sup>.</p>
+<p>Test emphasis at end of footnote<sup id="fnref:d"><a class="footnote-ref" href="#fn:d" rel="footnote">9</a></sup></p>
+<p>Complex footnote content<sup id="fnref:e"><a class="footnote-ref" href="#fn:e" rel="footnote">10</a></sup></p>
 <div class="footnote">
 <hr />
 <ol>
@@ -29,5 +34,38 @@
 Second line of first paragraph is not intended.
 Nor is third... <a class="footnote-backref" href="#fnref:5" rev="footnote" title="Jump back to footnote 5 in the text">↩</a></p>
 </li>
+<li id="fn:a">
+<p>1 <a class="footnote-backref" href="#fnref:a" rev="footnote" title="Jump back to footnote 6 in the text">↩</a><a class="footnote-backref" href="#fnref2:a" rev="footnote" title="Jump back to footnote 6 in the text">↩</a><a class="footnote-backref" href="#fnref3:a" rev="footnote" title="Jump back to footnote 6 in the text">↩</a></p>
+</li>
+<li id="fn:b">
+<p>2 <a class="footnote-backref" href="#fnref:b" rev="footnote" title="Jump back to footnote 7 in the text">↩</a><a class="footnote-backref" href="#fnref2:b" rev="footnote" title="Jump back to footnote 7 in the text">↩</a><a class="footnote-backref" href="#fnref3:b" rev="footnote" title="Jump back to footnote 7 in the text">↩</a></p>
+</li>
+<li id="fn:c">
+<p>3 <a class="footnote-backref" href="#fnref:c" rev="footnote" title="Jump back to footnote 8 in the text">↩</a></p>
+</li>
+<li id="fn:d">
+<p><em>emphasis works</em></p>
+<p><em>emphasis still works</em> <a class="footnote-backref" href="#fnref:d" rev="footnote" title="Jump back to footnote 9 in the text">↩</a></p>
+</li>
+<li id="fn:e">
+<ol>
+<li>
+<p>The top couple half figure, contrary sides and hands across with bottom couple,</p>
+<p>Half figure back on your own sides, and turn partner to places,</p>
+<p>Swing partners with right hands into straight line long-ways, as in a reel, and</p>
+<p>Set,</p>
+<p>Hey and return to places,</p>
+<p>The other three couples do the same.</p>
+</li>
+<li>
+<p>Top and bottom couples meet and set,</p>
+<p>Then each gentleman leas the opposite lady to the couple on his left, and set,</p>
+<p>Aach four right and left,</p>
+<p>Swing side couples to places, and turn partners all eight,</p>
+<p>The other two couple o the same.</p>
+</li>
+</ol>
+<p><a class="footnote-backref" href="#fnref:e" rev="footnote" title="Jump back to footnote 10 in the text">↩</a></p>
+</li>
 </ol>
 </div>
\ No newline at end of file
diff --git a/tests/extensions/extra/footnote.txt b/tests/extensions/extra/footnote.txt
index c5c1c92..93b5869 100644
--- a/tests/extensions/extra/footnote.txt
+++ b/tests/extensions/extra/footnote.txt
@@ -2,6 +2,16 @@ This is the body with a footnote[^1] or two[^2] or more[^3] [^4] [^5].
 
 Also a reference that does not exist[^6].
 
+Duplicate[^a] footnotes[^a] test[^a].
+
+Duplicate[^b] footnotes[^b] test[^b].
+
+Single after duplicates[^c].
+
+Test emphasis at end of footnote[^d]
+
+Complex footnote content[^e]
+
 [^1]: Footnote that ends with a list:
 
     * item 1
@@ -18,3 +28,35 @@ Also a reference that does not exist[^6].
 [^5]: First line of first paragraph.
 Second line of first paragraph is not intended.
 Nor is third...
+
+[^a]: 1
+[^b]: 2
+[^c]: 3
+
+[^d]:
+    _emphasis works_
+
+    _emphasis still works_
+
+[^e]: 
+    1.  The top couple half figure, contrary sides and hands across with bottom couple,
+
+        Half figure back on your own sides, and turn partner to places,
+
+        Swing partners with right hands into straight line long-ways, as in a reel, and
+
+        Set,
+
+        Hey and return to places,
+
+        The other three couples do the same.
+
+    2.  Top and bottom couples meet and set,
+
+        Then each gentleman leas the opposite lady to the couple on his left, and set,
+
+        Aach four right and left,
+
+        Swing side couples to places, and turn partners all eight,
+
+        The other two couple o the same.
diff --git a/tests/extensions/extra/tables.html b/tests/extensions/extra/tables.html
index b5c08be..2418c98 100644
--- a/tests/extensions/extra/tables.html
+++ b/tests/extensions/extra/tables.html
@@ -250,4 +250,129 @@ Content Cell | Content Cell
 <td>(<code>bar</code>) and <code>baz</code>.</td>
 </tr>
 </tbody>
+</table>
+<p>Lists are not tables</p>
+<ul>
+<li>this | should | not</li>
+<li>be | a | table</li>
+</ul>
+<p>Add tests for issue #449</p>
+<table>
+<thead>
+<tr>
+<th>Odd backticks</th>
+<th>Even backticks</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>[!\"\#$%&'()*+,\-./:;<=>?@\[\\\]^_`{|}~]</code></td>
+<td><code>[!\"\#$%&'()*+,\-./:;<=>?@\[\\\]^`_`{|}~]</code></td>
+</tr>
+</tbody>
+</table>
+<table>
+<thead>
+<tr>
+<th>Escapes</th>
+<th>More Escapes</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>`\</code></td>
+<td><code>\</code></td>
+</tr>
+</tbody>
+</table>
+<p>Only the first backtick can be escaped</p>
+<table>
+<thead>
+<tr>
+<th>Escaped</th>
+<th>Bacticks</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>`<code>\</code></td>
+<td>``</td>
+</tr>
+</tbody>
+</table>
+<p>Test escaped pipes</p>
+<table>
+<thead>
+<tr>
+<th>Column 1</th>
+<th>Column 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>|</code> |</td>
+<td>Pipes are okay in code and escaped. |</td>
+</tr>
+</tbody>
+</table>
+<table>
+<thead>
+<tr>
+<th>Column 1</th>
+<th>Column 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>row1</td>
+<td>row1    |</td>
+</tr>
+<tr>
+<td>row2</td>
+<td>row2</td>
+</tr>
+</tbody>
+</table>
+<p>Test header escapes</p>
+<table>
+<thead>
+<tr>
+<th><code>`\</code> |</th>
+<th><code>\</code> |</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>row1</td>
+<td>row1</td>
+</tr>
+<tr>
+<td>row2</td>
+<td>row2</td>
+</tr>
+</tbody>
+</table>
+<p>Escaped pipes in format row should not be a table</p>
+<p>| Column1   | Column2 |
+| ------- || ------- |
+| row1      | row1    |
+| row2      | row2    |</p>
+<p>Test escaped code in Table</p>
+<table>
... 211 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-markdown.git



More information about the Python-modules-commits mailing list