[Python-modules-commits] [python-textile] 03/11: Import python-textile_2.2.2.orig.tar.gz

Dmitry Shachnev mitya57 at moszumanska.debian.org
Mon Dec 14 16:30:29 UTC 2015


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

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

commit 1f8e042a0f9ef31cb7a65ada1c7298699cc8e3cd
Author: Dmitry Shachnev <mitya57 at gmail.com>
Date:   Mon Dec 14 18:37:27 2015 +0300

    Import python-textile_2.2.2.orig.tar.gz
---
 PKG-INFO                       |   12 +-
 setup.cfg                      |    3 +
 setup.py                       |   35 +-
 textile.egg-info/PKG-INFO      |   12 +-
 textile.egg-info/SOURCES.txt   |    5 +-
 textile.egg-info/requires.txt  |    3 +
 textile/__init__.py            |    7 +-
 textile/core.py                | 1691 ++++++++++++++++++++++++++++++++++++++++
 textile/functions.py           | 1003 ------------------------
 textile/tests/__init__.py      |  343 ++++++--
 textile/textilefactory.py      |   20 +-
 textile/tools/doctest_utils.py |   92 +++
 textile/tools/imagesize.py     |   23 +-
 textile/tools/sanitizer.py     |   29 +-
 textile/version.py             |    1 +
 15 files changed, 2128 insertions(+), 1151 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index 7b86215..dbf3933 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,15 +1,15 @@
-Metadata-Version: 1.0
+Metadata-Version: 1.1
 Name: textile
-Version: 2.1.5
+Version: 2.2.2
 Summary: Textile processing for python.
-Home-page: http://github.com/chrisdrackett/python-textile
-Author: Chris Drackett
-Author-email: chris at chrisdrackett.com
+Home-page: http://github.com/textile/python-textile
+Author: UNKNOWN
+Author-email: UNKNOWN
 License: UNKNOWN
 Description: UNKNOWN
 Keywords: textile,text
 Platform: UNKNOWN
-Classifier: Development Status :: 3 - Alpha
+Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Web Environment
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: BSD License
diff --git a/setup.cfg b/setup.cfg
index 336606e..2829b29 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,6 +6,9 @@ cover-erase = 1
 with-doctest = 1
 with-id = 1
 
+[bdist_wheel]
+universal = 1
+
 [egg_info]
 tag_build = 
 tag_date = 0
diff --git a/setup.py b/setup.py
index e29b15f..386c774 100644
--- a/setup.py
+++ b/setup.py
@@ -1,17 +1,31 @@
 from setuptools import setup, find_packages
+import os
+import sys
 
-version = '2.1.5'
+install_requires = []
+
+
+if 'develop' in sys.argv:
+    install_requires.extend([
+        'tox',
+    ])
+
+def get_version():
+    basedir = os.path.dirname(__file__)
+    with open(os.path.join(basedir, 'textile/version.py')) as f:
+        variables = {}
+        exec(f.read(), variables)
+        return variables.get('VERSION')
+    raise RuntimeError('No version info found.')
 
 setup(
     name='textile',
-    version=version,
+    version=get_version(),
     description='Textile processing for python.',
-    author='Chris Drackett',
-    author_email='chris at chrisdrackett.com',
-    url='http://github.com/chrisdrackett/python-textile',
+    url='http://github.com/textile/python-textile',
     packages=find_packages(),
     classifiers=[
-        'Development Status :: 3 - Alpha',
+        'Development Status :: 5 - Production/Stable',
         'Environment :: Web Environment',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: BSD License',
@@ -20,8 +34,13 @@ setup(
         'Topic :: Software Development :: Libraries :: Python Modules',
     ],
     keywords='textile,text',
-    test_suite = 'nose.collector',
-    tests_require = ['nose'],
+    install_requires=install_requires,
+    extras_require={
+        ':python_version=="2.6"': ['ordereddict>=1.1'],
+    },
+    test_suite='nose.collector',
+    tests_require=['nose'],
     include_package_data=True,
     zip_safe=False,
 )
+
diff --git a/textile.egg-info/PKG-INFO b/textile.egg-info/PKG-INFO
index 7b86215..dbf3933 100644
--- a/textile.egg-info/PKG-INFO
+++ b/textile.egg-info/PKG-INFO
@@ -1,15 +1,15 @@
-Metadata-Version: 1.0
+Metadata-Version: 1.1
 Name: textile
-Version: 2.1.5
+Version: 2.2.2
 Summary: Textile processing for python.
-Home-page: http://github.com/chrisdrackett/python-textile
-Author: Chris Drackett
-Author-email: chris at chrisdrackett.com
+Home-page: http://github.com/textile/python-textile
+Author: UNKNOWN
+Author-email: UNKNOWN
 License: UNKNOWN
 Description: UNKNOWN
 Keywords: textile,text
 Platform: UNKNOWN
-Classifier: Development Status :: 3 - Alpha
+Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Web Environment
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: BSD License
diff --git a/textile.egg-info/SOURCES.txt b/textile.egg-info/SOURCES.txt
index ab397f5..ce8cd49 100644
--- a/textile.egg-info/SOURCES.txt
+++ b/textile.egg-info/SOURCES.txt
@@ -1,14 +1,17 @@
 setup.cfg
 setup.py
 textile/__init__.py
-textile/functions.py
+textile/core.py
 textile/textilefactory.py
+textile/version.py
 textile.egg-info/PKG-INFO
 textile.egg-info/SOURCES.txt
 textile.egg-info/dependency_links.txt
 textile.egg-info/not-zip-safe
+textile.egg-info/requires.txt
 textile.egg-info/top_level.txt
 textile/tests/__init__.py
 textile/tools/__init__.py
+textile/tools/doctest_utils.py
 textile/tools/imagesize.py
 textile/tools/sanitizer.py
\ No newline at end of file
diff --git a/textile.egg-info/requires.txt b/textile.egg-info/requires.txt
new file mode 100644
index 0000000..5e99290
--- /dev/null
+++ b/textile.egg-info/requires.txt
@@ -0,0 +1,3 @@
+
+[:python_version=="2.6"]
+ordereddict>=1.1
diff --git a/textile/__init__.py b/textile/__init__.py
index eeaeb33..e7ea665 100644
--- a/textile/__init__.py
+++ b/textile/__init__.py
@@ -1,3 +1,8 @@
-from functions import textile, textile_restricted, Textile
+from __future__ import unicode_literals
+
+from .core import textile, textile_restricted, Textile
+from .version import VERSION
 
 __all__ = ['textile', 'textile_restricted']
+
+__version__ = VERSION
diff --git a/textile/core.py b/textile/core.py
new file mode 100644
index 0000000..88da32b
--- /dev/null
+++ b/textile/core.py
@@ -0,0 +1,1691 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+__copyright__ = """
+Copyright (c) 2009, Jason Samsa, http://jsamsa.com/
+Copyright (c) 2010, Kurt Raschke <kurt at kurtraschke.com>
+Copyright (c) 2004, Roberto A. F. De Almeida, http://dealmeida.net/
+Copyright (c) 2003, Mark Pilgrim, http://diveintomark.org/
+
+Original PHP Version:
+Copyright (c) 2003-2004, Dean Allen <dean at textism.com>
+All rights reserved.
+
+Thanks to Carlo Zottmann <carlo at g-blog.net> for refactoring
+Textile's procedural code into a class framework
+
+Additions and fixes Copyright (c) 2006 Alex Shiels http://thresholdstate.com/
+
+"""
+
+import uuid
+
+from textile.tools import sanitizer, imagesize
+
+
+# We're going to use the Python 2.7+ OrderedDict data type. Import it if it's
+# available, otherwise, use the included tool.
+try:
+    from collections import OrderedDict
+except ImportError:
+    from ordereddict import OrderedDict
+
+
+try:
+    # Python 3
+    from urllib.parse import urlparse, urlsplit, urlunsplit, quote, unquote
+    from html.parser import HTMLParser
+    xrange = range
+    unichr = chr
+    unicode = str
+except (ImportError):
+    # Python 2
+    from urllib import quote, unquote
+    from urlparse import urlparse, urlsplit, urlunsplit
+    from HTMLParser import HTMLParser
+
+
+try:
+    # Use regex module for matching uppercase characters if installed,
+    # otherwise fall back to finding all the uppercase chars in a loop.
+    import regex as re
+    upper_re_s = r'\p{Lu}'
+except ImportError:
+    import re
+    from sys import maxunicode
+    upper_re_s = "".join([unichr(c) for c in
+        xrange(maxunicode) if unichr(c).isupper()])
+
+
+def _normalize_newlines(string):
+    out = string.strip()
+    out = re.sub(r'\r\n', '\n', out)
+    out = re.sub(r'\n{3,}', '\n\n', out)
+    out = re.sub(r'\n\s*\n', '\n\n', out)
+    out = re.sub(r'"$', '" ', out)
+    return out
+
+
+class Textile(object):
+    halign_re_s = r'(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+(?! ))'
+    valign_re_s = r'[\-^~]'
+    class_re_s = r'(?:\([^)\n]+\))'       # Don't allow classes/ids,
+    language_re_s = r'(?:\[[^\]\n]+\])'   # languages,
+    style_re_s = r'(?:\{[^}\n]+\})'       # or styles to span across newlines
+    colspan_re_s = r'(?:\\\d+)'
+    rowspan_re_s = r'(?:\/\d+)'
+    align_re_s = r'(?:%s|%s)*' % (halign_re_s, valign_re_s)
+    table_span_re_s = r'(?:%s|%s)*' % (colspan_re_s, rowspan_re_s)
+    # regex string to match class, style, language and horizontal alignment
+    # attributes
+    cslh_re_s = r'(?:%s)*' % '|'.join([class_re_s, style_re_s, language_re_s,
+        halign_re_s])
+    # regex string to match class, style and language attributes
+    csl_re_s = r'(?:%s)*' % '|'.join([class_re_s, style_re_s, language_re_s])
+
+    pnct_re_s = r'[-!"#$%&()*+,/:;<=>?@\'\[\\\]\.^_`{|}~]'
+    urlchar_re_s = r'[\w"$\-_.+!*\'(),";\/?:@=&%#{}|\\^~\[\]`]'
+    syms_re_s = '¤§µ¶†‡•∗∴◊♠♣♥♦'
+
+    restricted_url_schemes = ('http', 'https', 'ftp', 'mailto')
+    unrestricted_url_schemes = restricted_url_schemes + ('file', 'tel',
+            'callto', 'sftp')
+
+    btag = ('bq', 'bc', 'notextile', 'pre', 'h[1-6]', 'fn\d+', 'p', '###')
+    btag_lite = ('bq', 'bc', 'p')
+
+    iAlign = {'<': 'float: left;',
+              '>': 'float: right;',
+              '=': 'display: block; margin: 0 auto;'}
+    vAlign = {'^': 'top', '-': 'middle', '~': 'bottom'}
+    hAlign = {'<': 'left', '=': 'center', '>': 'right', '<>': 'justify'}
+
+    note_index = 1
+
+    doctype_whitelist = ['xhtml', 'html5']
+
+    glyph_definitions = {
+        'quote_single_open':  '‘',
+        'quote_single_close': '’',
+        'quote_double_open':  '“',
+        'quote_double_close': '”',
+        'apostrophe':         '’',
+        'prime':              '′',
+        'prime_double':       '″',
+        'ellipsis':           '…',
+        'ampersand':          '&',
+        'emdash':             '—',
+        'endash':             '–',
+        'dimension':          '×',
+        'trademark':          '™',
+        'registered':         '®',
+        'copyright':          '©',
+        'half':               '½',
+        'quarter':            '¼',
+        'threequarters':      '¾',
+        'degrees':            '°',
+        'plusminus':          '±',
+        'fn_ref_pattern':     '<sup%(atts)s>%(marker)s</sup>',
+        'fn_foot_pattern':    '<sup%(atts)s>%(marker)s</sup>',
+        'nl_ref_pattern':     '<sup%(atts)s>%(marker)s</sup>',
+    }
+
+    def __init__(self, restricted=False, lite=False, noimage=False,
+                 auto_link=False, get_sizes=False, html_type='xhtml'):
+        """Textile properties that are common to regular textile and
+        textile_restricted"""
+        self.restricted = restricted
+        self.lite = lite
+        self.noimage = noimage
+        self.get_sizes = get_sizes
+        self.auto_link = auto_link
+        self.fn = {}
+        self.urlrefs = {}
+        self.shelf = {}
+        self.rel = ''
+        self.html_type = html_type
+        self.max_span_depth = 5
+
+        # We'll be searching for characters that need to be HTML-encoded to
+        # produce properly valid html.  These are the defaults that work in
+        # most cases.  Below, we'll copy this and modify the necessary pieces
+        # to make it work for characters at the beginning of the string.
+        self.glyph_search = [
+            # apostrophe's
+            re.compile(r"(^|\w)'(\w)", re.U),
+            # back in '88
+            re.compile(r"(\s)'(\d+\w?)\b(?!')", re.U),
+            # single closing
+            re.compile(r"(^|\S)'(?=\s|%s|$)" % self.pnct_re_s, re.U),
+            # single opening
+            re.compile(r"'", re.U),
+            # double closing
+            re.compile(r'(^|\S)"(?=\s|%s|$)' % self.pnct_re_s, re.U),
+            # double opening
+            re.compile(r'"'),
+            # ellipsis
+            re.compile(r'([^.]?)\.{3}', re.U),
+            # ampersand
+            re.compile(r'(\s)&(\s)', re.U),
+            # em dash
+            re.compile(r'(\s?)--(\s?)', re.U),
+            # en dash
+            re.compile(r'\s-(?:\s|$)', re.U),
+            # dimension sign
+            re.compile(r'(\d+)( ?)x( ?)(?=\d+)', re.U),
+            # trademark
+            re.compile(r'\b ?[([]TM[])]', re.I | re.U),
+            # registered
+            re.compile(r'\b ?[([]R[])]', re.I | re.U),
+            # copyright
+            re.compile(r'\b ?[([]C[])]', re.I | re.U),
+            # 1/2
+            re.compile(r'[([]1\/2[])]', re.I | re.U),
+            # 1/4
+            re.compile(r'[([]1\/4[])]', re.I | re.U),
+            # 3/4
+            re.compile(r'[([]3\/4[])]', re.I | re.U),
+            # degrees
+            re.compile(r'[([]o[])]', re.I | re.U),
+            # plus/minus
+            re.compile(r'[([]\+\/-[])]', re.I | re.U),
+            # 3+ uppercase acronym
+            re.compile(r'\b([%s][%s0-9]{2,})\b(?:[(]([^)]*)[)])' % (upper_re_s, upper_re_s)),
+            # 3+ uppercase
+            re.compile(r"""(?:(?<=^)|(?<=\s)|(?<=[>\(;-]))([%s]{3,})(\w*)(?=\s|%s|$)(?=[^">]*?(<|$))""" %
+                (upper_re_s, self.pnct_re_s)),
+        ]
+
+        # These are the changes that need to be made for characters that occur
+        # at the beginning of the string.
+        self.glyph_search_initial = list(self.glyph_search)
+        # apostrophe's
+        self.glyph_search_initial[0] = re.compile(r"(\w)'(\w)", re.U)
+        # single closing
+        self.glyph_search_initial[2] = re.compile(r"(\S)'(?=\s|%s|$)" %
+                self.pnct_re_s, re.U)
+        # double closing
+        self.glyph_search_initial[4] = re.compile(r'(\S)"(?=\s|%s|$)' %
+                self.pnct_re_s, re.U)
+
+        self.glyph_replace = [x % self.glyph_definitions for x in (
+            r'\1%(apostrophe)s\2',                # apostrophe's
+            r'\1%(apostrophe)s\2',                # back in '88
+            r'\1%(quote_single_close)s',          # single closing
+            r'%(quote_single_open)s',             # single opening
+            r'\1%(quote_double_close)s',          # double closing
+            r'%(quote_double_open)s',             # double opening
+            r'\1%(ellipsis)s',                    # ellipsis
+            r'\1%(ampersand)s\2',                 # ampersand
+            r'\1%(emdash)s\2',                    # em dash
+            r' %(endash)s ',                      # en dash
+            r'\1\2%(dimension)s\3',               # dimension sign
+            r'%(trademark)s',                     # trademark
+            r'%(registered)s',                    # registered
+            r'%(copyright)s',                     # copyright
+            r'%(half)s',                          # 1/2
+            r'%(quarter)s',                       # 1/4
+            r'%(threequarters)s',                 # 3/4
+            r'%(degrees)s',                       # degrees
+            r'%(plusminus)s',                     # plus/minus
+            r'<acronym title="\2">\1</acronym>',  # 3+ uppercase acronym
+            r'<span class="caps">\1</span>\2',    # 3+ uppercase
+        )]
+
+        if self.html_type == 'html5':
+            self.glyph_replace[19] = r'<abbr title="\2">\1</abbr>'
+
+        if self.restricted is True:
+            self.url_schemes = self.restricted_url_schemes
+        else:
+            self.url_schemes = self.unrestricted_url_schemes
+
+
+    def parse(self, text, rel=None, head_offset=0, sanitize=False):
+        """
+        >>> import textile
+        >>> Py3 << textile.textile('some textile')
+        '\\t<p>some textile</p>'
+        """
+        self.notes = OrderedDict()
+        self.unreferencedNotes = OrderedDict()
+        self.notelist_cache = OrderedDict()
+
+        text = _normalize_newlines(text)
+
+        if self.restricted:
+            text = self.encode_html(text, quotes=False)
+
+        if rel:
+            self.rel = ' rel="%s"' % rel
+
+        text = self.getRefs(text)
+
+        # The original php puts the below within an if not self.lite, but our
+        # block function handles self.lite itself.
+        text = self.block(text, int(head_offset))
+
+        if not self.lite:
+            text = self.placeNoteLists(text)
+
+        text = self.retrieve(text)
+
+        if sanitize:
+            text = sanitizer.sanitize(text)
+
+        # if the text contains a break tag (<br> or <br />) not followed by
+        # a newline, replace it with a new style break tag and a newline.
+        text = re.sub(r'<br( /)?>(?!\n)', '<br />\n', text)
+
+        return text
+
+    def pba(self, block_attributes, element=None):
+        """
+        Parse block attributes.
+
+        >>> t = Textile()
+        >>> Py3 << t.pba(r'\3')
+        ''
+        >>> Py3 << t.pba(r'\\3', element='td')
+        ' colspan="3"'
+        >>> Py3 << t.pba(r'/4', element='td')
+        ' rowspan="4"'
+        >>> Py3 << t.pba(r'\\3/4', element='td')
+        ' colspan="3" rowspan="4"'
+
+        >>> Py3 << t.pba('^', element='td')
+        ' style="vertical-align:top;"'
+
+        >>> Py3 << t.pba('{line-height:18px}')
+        ' style="line-height:18px;"'
+
+        >>> Py3 << t.pba('(foo-bar)')
+        ' class="foo-bar"'
+
+        >>> Py3 << t.pba('(#myid)')
+        ' id="myid"'
+
+        >>> Py3 << t.pba('(foo-bar#myid)')
+        ' class="foo-bar" id="myid"'
+
+        >>> Py3 << t.pba('((((')
+        ' style="padding-left:4em;"'
+
+        >>> Py3 << t.pba(')))')
+        ' style="padding-right:3em;"'
+
+        >>> Py3 << t.pba('[fr]')
+        ' lang="fr"'
+
+        >>> Py3 << t.pba(r'\\5 80', 'col')
+        ' span="5" width="80"'
+
+        >>> rt = Textile()
+        >>> rt.restricted = True
+        >>> Py3 << rt.pba('[en]')
+        ' lang="en"'
+
+        >>> Py3 << rt.pba('(#id)')
+        ''
+
+        """
+        style = []
+        aclass = ''
+        lang = ''
+        colspan = ''
+        rowspan = ''
+        block_id = ''
+        span = ''
+        width = ''
+
+        if not block_attributes:
+            return ''
+
+        matched = block_attributes
+        if element == 'td':
+            m = re.search(r'\\(\d+)', matched)
+            if m:
+                colspan = m.group(1)
+
+            m = re.search(r'/(\d+)', matched)
+            if m:
+                rowspan = m.group(1)
+
+        if element == 'td' or element == 'tr':
+            m = re.search(r'(%s)' % self.valign_re_s, matched)
+            if m:
+                style.append("vertical-align:%s" % self.vAlign[m.group(1)])
+
+        m = re.search(r'\{([^}]*)\}', matched)
+        if m:
+            style += m.group(1).rstrip(';').split(';')
+            matched = matched.replace(m.group(0), '')
+
+        m = re.search(r'\[([^\]]+)\]', matched, re.U)
+        if m:
+            lang = m.group(1)
+            matched = matched.replace(m.group(0), '')
+
+        m = re.search(r'\(([^()]+)\)', matched, re.U)
+        if m:
+            aclass = m.group(1)
+            matched = matched.replace(m.group(0), '')
+
+        m = re.search(r'([(]+)', matched)
+        if m:
+            style.append("padding-left:%sem" % len(m.group(1)))
+            matched = matched.replace(m.group(0), '')
+
+        m = re.search(r'([)]+)', matched)
+        if m:
+            style.append("padding-right:%sem" % len(m.group(1)))
+            matched = matched.replace(m.group(0), '')
+
+        m = re.search(r'(%s)' % self.halign_re_s, matched)
+        if m:
+            style.append("text-align:%s" % self.hAlign[m.group(1)])
+
+        m = re.search(r'^(.*)#(.*)$', aclass)
+        if m:
+            block_id = m.group(2)
+            aclass = m.group(1)
+
+        if element == 'col':
+            pattern = r'(?:\\(\d+))?\s*(\d+)?'
+            csp = re.match(pattern, matched)
+            span, width = csp.groups()
+
+        if self.restricted:
+            if lang:
+                return ' lang="%s"' % lang
+            else:
+                return ''
+
+        result = []
+        if style:
+            # Previous splits that created style may have introduced extra
+            # whitespace into the list elements.  Clean it up.
+            style = [x.strip() for x in style]
+            result.append(' style="%s;"' % "; ".join(style))
+        if aclass:
+            result.append(' class="%s"' % aclass)
+        if block_id:
+            result.append(' id="%s"' % block_id)
+        if lang:
+            result.append(' lang="%s"' % lang)
+        if colspan:
+            result.append(' colspan="%s"' % colspan)
+        if rowspan:
+            result.append(' rowspan="%s"' % rowspan)
+        if span:
+            result.append(' span="%s"' % span)
+        if width:
+            result.append(' width="%s"' % width)
+        return ''.join(result)
+
+    def hasRawText(self, text):
+        """
+        checks whether the text has text not already enclosed by a block tag
+
+        >>> t = Textile()
+        >>> t.hasRawText('<p>foo bar biz baz</p>')
+        False
+
+        >>> t.hasRawText(' why yes, yes it does')
+        True
+
+        """
+        # The php version has orders the below list of tags differently.  The
+        # important thing to note here is that the pre must occur before the
+        # p or else the regex module doesn't properly match pre-s. It only
+        # matches the p in pre.
+        r = re.compile(r'<(pre|p|blockquote|div|form|table|ul|ol|dl|h[1-6])[^>]*?>.*</\1>',
+                       re.S).sub('', text.strip()).strip()
+        r = re.compile(r'<(hr|br)[^>]*?/>').sub('', r)
+        return '' != r
+
+    def table(self, text):
+        r"""
+        >>> t = Textile()
+        >>> Py3 << t.table('(rowclass). |one|two|three|\n|a|b|c|')
+        '\t<table>\n\t\t<tr class="rowclass">\n\t\t\t<td>one</td>\n\t\t\t<td>two</td>\n\t\t\t<td>three</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>a</td>\n\t\t\t<td>b</td>\n\t\t\t<td>c</td>\n\t\t</tr>\n\t</table>\n\n'
+        """
+        text = text + "\n\n"
+        pattern = re.compile(r'^(?:table(_?%(s)s%(a)s%(c)s)\.(.*?)\n)?^(%(a)s%(c)s\.? ?\|.*\|)[\s]*\n\n' %
+                {'s': self.table_span_re_s, 'a': self.align_re_s, 'c':
+                    self.cslh_re_s}, re.S | re.M | re.U)
+        return pattern.sub(self.fTable, text)
+
+    def fTable(self, match):
+        tatts = self.pba(match.group(1), 'table')
+
+        summary = (' summary="%s"' % match.group(2).strip() if match.group(2)
+                   else '')
+        cap = ''
+        colgrp, last_rgrp = '', ''
+        c_row = 1
+        rows = []
+        try:
+            split = re.split(r'\|\s*?$', match.group(3), flags=re.M)
+        except TypeError:
+            split = re.compile(r'\|\s*?$', re.M).split(match.group(3))
+        for row in [x for x in split if x]:
+            row = row.lstrip()
+
+            # Caption -- only occurs on row 1, otherwise treat '|=. foo |...'
+            # as a normal center-aligned cell.
+            captionpattern = r"^\|\=(%(s)s%(a)s%(c)s)\. ([^\n]*)(.*)" % {'s':
+                    self.table_span_re_s, 'a': self.align_re_s, 'c':
+                    self.cslh_re_s}
+            caption_re = re.compile(captionpattern, re.S)
+            cmtch = caption_re.match(row)
+            if c_row == 1 and cmtch:
+                capatts = self.pba(cmtch.group(1))
+                cap = "\t<caption%s>%s</caption>\n" % (capatts,
+                                                       cmtch.group(2).strip())
+                row = cmtch.group(3).lstrip()
+                if row == '':
+                    continue
+
+            c_row += 1
+
+            # Colgroup
+            grppattern = r"^\|:(%(s)s%(a)s%(c)s\. .*)" % {'s':
+                    self.table_span_re_s, 'a': self.align_re_s, 'c':
+                    self.cslh_re_s}
+            grp_re = re.compile(grppattern, re.M)
+            gmtch = grp_re.match(row.lstrip())
+            if gmtch:
+                has_newline = "\n" in row
+                idx = 0
+                for col in gmtch.group(1).replace('.', '').split("|"):
+                    gatts = self.pba(col.strip(), 'col')
+                    if idx == 0:
+                        gatts = "group%s>" % gatts
+                    else:
+                        gatts = gatts + " />"
+                    colgrp = colgrp + "\t<col%s\n" % gatts
+                    idx += 1
+                colgrp += "\t</colgroup>\n"
+
+                # If the row has a newline in it, account for the missing
+                # closing pipe and process the rest of the line
+                if not has_newline:
+                    continue
+                else:
+                    row = row[row.index('\n'):].lstrip()
+
+            grpmatchpattern = (r"(:?^\|(%(v)s)(%(s)s%(a)s%(c)s)\.\s*$\n)?^(.*)"
+                    % {'v': self.valign_re_s, 's': self.table_span_re_s, 'a':
+                        self.align_re_s, 'c': self.cslh_re_s})
+            grpmatch_re = re.compile(grpmatchpattern, re.S | re.M)
+            grpmatch = grpmatch_re.match(row.lstrip())
+
+            # Row group
+            rgrp = ''
+            rgrptypes = {'^': 'head', '~': 'foot', '-': 'body'}
+            if grpmatch.group(2):
+                rgrp = rgrptypes[grpmatch.group(2)]
+            rgrpatts = self.pba(grpmatch.group(3))
+            row = grpmatch.group(4)
+
+            rmtch = re.search(r'^(%s%s\. )(.*)' % (self.align_re_s,
+                self.cslh_re_s), row.lstrip())
+            if rmtch:
+                ratts = self.pba(rmtch.group(1), 'tr')
+                row = rmtch.group(2)
+            else:
+                ratts = ''
+
+            cells = []
+            cellctr = 0
+            for cell in row.split('|'):
+                ctyp = 'd'
+                if re.search(r'^_', cell):
+                    ctyp = "h"
+                cmtch = re.search(r'^(_?%s%s%s\. )(.*)' % (
+                    self.table_span_re_s, self.align_re_s, self.cslh_re_s),
+                    cell)
+                if cmtch:
+                    catts = self.pba(cmtch.group(1), 'td')
+                    cell = cmtch.group(2)
+                else:
+                    catts = ''
+
+                if not self.lite:
+                    cell = self.redcloth_list(cell)
+                    cell = self.lists(cell)
+
+                # row.split() gives us ['', 'cell 1 contents', '...']
+                # so we ignore the first cell.
+                if cellctr > 0:
+                    ctag = "t%s" % ctyp
+                    cline = ("\t\t\t<%(ctag)s%(catts)s>%(cell)s</%(ctag)s>" %
+                             {'ctag': ctag, 'catts': catts, 'cell': cell})
+                    cells.append(self.doTagBr(ctag, cline))
+
+                cellctr += 1
+
+            if rgrp and last_rgrp:
+                grp = "\t</t%s>\n" % last_rgrp
+            else:
+                grp = ''
+
+            if rgrp:
+                grp += "\t<t%s%s>\n" % (rgrp, rgrpatts)
+
+            last_rgrp = rgrp if rgrp else last_rgrp
+
+            rows.append("%s\t\t<tr%s>\n%s%s\t\t</tr>" % (grp, ratts,
+                        '\n'.join(cells), '\n' if cells else ''))
+            cells = []
+            catts = None
+
+        if last_rgrp:
+            last_rgrp = '\t</t%s>\n' % last_rgrp
+        tbl = ("\t<table%(tatts)s%(summary)s>\n%(cap)s%(colgrp)s%(rows)s\n%(last_rgrp)s\t</table>\n\n"
+               % {'tatts': tatts, 'summary': summary, 'cap': cap, 'colgrp':
+                  colgrp, 'last_rgrp': last_rgrp, 'rows': '\n'.join(rows)})
+        return tbl
+
+    def lists(self, text):
+        """
+        >>> t = Textile()
+        >>> Py3 << t.lists("* one\\n* two\\n* three")
+        '\\t<ul>\\n\\t\\t<li>one</li>\\n\\t\\t<li>two</li>\\n\\t\\t<li>three</li>\\n\\t</ul>'
+        """
+
+        #Replace line-initial bullets with asterisks
+        bullet_pattern = re.compile('^•', re.U | re.M)
+
+        pattern = re.compile(r'^((?:[*;:]+|[*;:#]*#(?:_|\d+)?)%s[ .].*)$(?![^#*;:])'
+                             % self.csl_re_s, re.U | re.M | re.S)
+        return pattern.sub(self.fList, bullet_pattern.sub('*', text))
+
+    def fList(self, match):
+        try:
+            text = re.split(r'\n(?=[*#;:])', match.group(), flags=re.M)
+        except TypeError:
+            text = re.compile(r'\n(?=[*#;:])', re.M).split(match.group())
+        pt = ''
+        result = []
+        ls = OrderedDict()
+        for i, line in enumerate(text):
+            try:
+                nextline = text[i + 1]
+            except IndexError:
+                nextline = ''
+
+            m = re.search(r"^([#*;:]+)(_|\d+)?(%s)[ .](.*)$" % self.csl_re_s,
+                    line, re.S)
+            if m:
+                tl, start, atts, content = m.groups()
+                content = content.strip()
+                nl = ''
+                ltype = self.listType(tl)
+                if ';' in tl:
+                    litem = 'dt'
+                elif ':' in tl:
+                    litem = 'dd'
+                else:
+                    litem = 'li'
+
+                showitem = len(content) > 0
+
+                # handle list continuation/start attribute on ordered lists
+                if ltype == 'o':
+                    if not hasattr(self, 'olstarts'):
+                        self.olstarts = {tl: 1}
+
+                    # does the first line of this ol have a start attribute
+                    if len(tl) > len(pt):
+                        # no, set it to 1
+                        if start is None:
+                            self.olstarts[tl] = 1
+                        # yes, set it to the given number
+                        elif start != '_':
+                            self.olstarts[tl] = int(start)
+                        # we won't need to handle the '_' case, we'll just
+                        # print out the number when it's needed
+
+                    # put together the start attribute if needed
+                    if len(tl) > len(pt) and start is not None:
+                        start = ' start="%s"' % self.olstarts[tl]
+
+                    # This will only increment the count for list items, not
+                    # definition items
+                    if showitem:
+                        self.olstarts[tl] += 1
+
+                nm = re.match("^([#\*;:]+)(_|[\d]+)?%s[ .].*" % self.csl_re_s,
+                              nextline)
+                if nm:
+                    nl = nm.group(1)
+
+                # We need to handle nested definition lists differently.  If
+                # the next tag is a dt (';') of a lower nested level than the
+                # current dd (':'),
+                if ';' in pt and ':' in tl:
+                    ls[tl] = 2
+
+                atts = self.pba(atts)
+                # If start is still None, set it to '', else leave the value
+                # that we've already formatted.
+                start = start or ''
+
+                # if this item tag isn't in the list, create a new list and
+                # item, else just create the item
+                if tl not in ls:
+                    ls[tl] = 1
+                    itemtag = ("\n\t\t<%s>%s" % (litem, content) if
+                               showitem else '')
+                    line = "\t<%sl%s%s>%s" % (ltype, atts, start, itemtag)
+                else:
+                    line = ("\t\t<%s%s>%s" % (litem, atts, content) if showitem
+                            else '')
+
+                if len(nl) <= len(tl):
+                    line = line + ("</%s>" % litem if showitem else '')
+                # work backward through the list closing nested lists/items
+                for k, v in reversed(list(ls.items())):
+                    if len(k) > len(nl):
+                        if v != 2:
+                            line = line + "\n\t</%sl>" % self.listType(k)
+                        if len(k) > 1 and v != 2:
+                            line = line + "</%s>" % litem
+                        del ls[k]
+
+                # Remember the current Textile tag
+                pt = tl
+
+            # This else exists in the original php version.  I'm not sure how
+            # to come up with a case where the line would not match.  I think
+            # it may have been necessary due to the way php returns matches.
+            #else:
+                #line = line + "\n"
+            result.append(line)
+        return self.doTagBr(litem, "\n".join(result))
+
+    def listType(self, list_string):
+        listtypes = {
+            list_string.startswith('*'): 'u',
+            list_string.startswith('#'): 'o',
+            (not list_string.startswith('*') and not
+                list_string.startswith('#')): 'd'
+        }
+        return listtypes[True]
+
+    def doTagBr(self, tag, input):
+        return re.compile(r'<(%s)([^>]*?)>(.*)(</\1>)' % re.escape(tag),
+                          re.S).sub(self.doBr, input)
+
+    def doPBr(self, in_):
+        return re.compile(r'<(p)([^>]*?)>(.*)(</\1>)', re.S).sub(self.doBr,
+                                                                 in_)
+
+    def doBr(self, match):
+        content = re.sub(r'(.+)(?:(?<!<br>)|(?<!<br />))\n(?![#*;:\s|])',
+                         r'\1<br />', match.group(3))
+        return '<%s%s>%s%s' % (match.group(1), match.group(2), content,
+                               match.group(4))
+
+    def block(self, text, head_offset=0):
+        """
+        >>> t = Textile()
+        >>> Py3 << t.block('h1. foobar baby')
+        '\\t<h1>foobar baby</h1>'
+        """
+        if not self.lite:
+            tre = '|'.join(self.btag)
+        else:
+            tre = '|'.join(self.btag_lite)
+        text = text.split('\n\n')
+
+        tag = 'p'
+        atts = cite = graf = ext = ''
+        c1 = ''
+
+        out = []
+
+        anon = False
+        for line in text:
+            pattern = r'^(%s)(%s%s)\.(\.?)(?::(\S+))? (.*)$' % (
+                tre, self.align_re_s, self.cslh_re_s
+            )
+            match = re.search(pattern, line, re.S)
+            if match:
+                if ext:
+                    out.append(out.pop() + c1)
+
+                tag, atts, ext, cite, graf = match.groups()
+                h_match = re.search(r'h([1-6])', tag)
+                if h_match:
+                    head_level, = h_match.groups()
+                    tag = 'h%i' % max(1, min(int(head_level) + head_offset, 6))
+                o1, o2, content, c2, c1, eat = self.fBlock(tag, atts, ext,
+                                                           cite, graf)
+                # leave off c1 if this block is extended,
+                # we'll close it at the start of the next block
+
+                if ext:
+                    line = "%s%s%s%s" % (o1, o2, content, c2)
+                else:
+                    line = "%s%s%s%s%s" % (o1, o2, content, c2, c1)
+
+            else:
+                anon = True
+                if ext or not re.search(r'^\s', line):
+                    o1, o2, content, c2, c1, eat = self.fBlock(tag, atts, ext,
+                                                               cite, line)
+                    # skip $o1/$c1 because this is part of a continuing
+                    # extended block
+                    if tag == 'p' and not self.hasRawText(content):
+                        line = content
+                    else:
+                        line = "%s%s%s" % (o2, content, c2)
+                else:
+                    line = self.graf(line)
+
+            line = self.doPBr(line)
+            line = re.sub(r'<br>', '<br />', line)
+
+            if ext and anon:
+                out.append(out.pop() + "\n" + line)
+            elif not eat:
+                out.append(line)
+
+            if not ext:
+                tag = 'p'
+                atts = ''
... 2708 lines suppressed ...

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



More information about the Python-modules-commits mailing list