[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