textile-2.2.2/ 0000755 0000767 0000024 00000000000 12462765643 013617 5 ustar dennis staff 0000000 0000000 textile-2.2.2/PKG-INFO 0000644 0000767 0000024 00000001151 12462765643 014712 0 ustar dennis staff 0000000 0000000 Metadata-Version: 1.1
Name: textile
Version: 2.2.2
Summary: Textile processing for python.
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 :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
textile-2.2.2/setup.cfg 0000644 0000767 0000024 00000000320 12462765643 015433 0 ustar dennis staff 0000000 0000000 [nosetests]
detailed-errors = 1
with-coverage = 1
cover-package = textile
cover-erase = 1
with-doctest = 1
with-id = 1
[bdist_wheel]
universal = 1
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
textile-2.2.2/setup.py 0000644 0000767 0000024 00000002364 12462764257 015336 0 ustar dennis staff 0000000 0000000 from setuptools import setup, find_packages
import os
import sys
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=get_version(),
description='Textile processing for python.',
url='http://github.com/textile/python-textile',
packages=find_packages(),
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules',
],
keywords='textile,text',
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,
)
textile-2.2.2/textile/ 0000755 0000767 0000024 00000000000 12462765643 015275 5 ustar dennis staff 0000000 0000000 textile-2.2.2/textile/__init__.py 0000644 0000767 0000024 00000000301 12462764257 017400 0 ustar dennis staff 0000000 0000000 from __future__ import unicode_literals
from .core import textile, textile_restricted, Textile
from .version import VERSION
__all__ = ['textile', 'textile_restricted']
__version__ = VERSION
textile-2.2.2/textile/core.py 0000644 0000767 0000024 00000166343 12462764512 016606 0 ustar dennis staff 0000000 0000000 #!/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 some textile foo bar biz baz ', 'Hello BlockQuote', ' ', 'Hello BlockQuote', ' " % atts
c2 = " Cat's Cradle by Vonnegut Cat’s Cradle by Vonnegut %s notelist(%s)(?:\:([\w|%s]))?([\^!]?)(\+?)\.?[\s]* >>> import textile hello, world A single paragraph. Followed by another. I am very serious. I spoke. “Observe!” Observe — very nice! Observe – tiny and brief. Observe… Observe … Observe: 2 × 2. one™, two®, three©. An old text A block quotation. Any old text I believe every word. And then? She fell! I know. Cat’s Cradle by Vonnegut Convert with I’m You are a pleasant child. a 2 + b 2 = c 2 log 2 x I’m unaware of most soft drinks. I’m unaware An example Red here Red here Spacey blue rouge I seriously blushed align left align right centered justified left ident 1em left ident 2em right ident 3em
or
) not followed by
# a newline, replace it with a new style break tag and a newline.
text = re.sub(r'
(?!\n)', '
\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('\n\t\t
\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\n\t\t\t \n\t\tone \n\t\t\ttwo \n\t\t\tthree \n\t\t\n\t\t\t \n\ta \n\t\t\tb \n\t\t\tc \n\t\t\n%s%s\t\t " % (grp, ratts,
'\n'.join(cells), '\n' if cells else ''))
cells = []
catts = None
if last_rgrp:
last_rgrp = '\t\n%(cap)s%(colgrp)s%(rows)s\n%(last_rgrp)s\t
\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\\n\\t\\t
'
"""
#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'(.+)(?:(?)|(?))\n(?![#*;:\s|])',
r'\1
', 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')
'\\tfoobar baby
'
"""
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'
', '
', line)
if ext and anon:
out.append(out.pop() + "\n" + line)
elif not eat:
out.append(line)
if not ext:
tag = 'p'
atts = ''
cite = ''
graf = ''
if ext:
out.append(out.pop() + c1)
return '\n\n'.join(out)
def fBlock(self, tag, atts, ext, cite, content):
"""
>>> t = Textile()
>>> Py3 << t.fBlock("bq", "", None, "", "Hello BlockQuote")
('\\t\\n', '\\t\\t
', False)
>>> Py3 << t.fBlock("bq", "", None, "http://google.com", "Hello BlockQuote")
('\\t\\n', '\\t\\t
', False)
>>> Py3 << t.fBlock("bc", "", None, "", 'printf "Hello, World";') # doctest: +ELLIPSIS
('', '
', False)
>>> Py3 << t.fBlock("h1", "", None, "", "foobar")
('', '\\t', ..., '
', '', 'foobar', '
', '', False)
"""
att = atts
atts = self.pba(atts)
o1 = o2 = c2 = c1 = ''
eat = False
if tag == 'p':
# is this an anonymous block with a note definition?
notedef_re = re.compile(r"""
^note\# # start of note def marker
([^%%<*!@#^([{ \s.]+) # !label
([*!^]?) # !link
(%s) # !att
\.? # optional period.
[\s]+ # whitespace ends def marker
(.*)$ # !content""" % (self.cslh_re_s), re.X)
notedef = notedef_re.sub(self.fParseNoteDefs, content)
# It will be empty if the regex matched and ate it.
if '' == notedef:
return o1, o2, notedef, c2, c1, True
m = re.search(r'fn(\d+)', tag)
if m:
tag = 'p'
if m.group(1) in self.fn:
fnid = self.fn[m.group(1)]
else:
fnid = m.group(1)
# If there is an author-specified ID goes on the wrapper & the
# auto-id gets pushed to the
supp_id = ''
# if class has not been previously specified, set it to "footnote"
if atts.find('class=') < 0:
atts = atts + ' class="footnote"'
# if there's no specified id, use the generated one.
if atts.find('id=') < 0:
atts = atts + ' id="fn%s"' % fnid
else:
supp_id = ' id="fn%s"' % fnid
if att.find('^') < 0:
sup = self.formatFootnote(m.group(1), supp_id)
else:
fnrev = '%s' % (fnid, m.group(1))
sup = self.formatFootnote(fnrev, supp_id)
content = sup + ' ' + content
if tag == 'bq':
cite = self.checkRefs(cite)
if cite:
cite = ' cite="%s"' % cite
else:
cite = ''
o1 = "\t\n" % (cite, atts)
o2 = "\t\t
"
elif tag == 'bc':
o1 = "" % atts
o2 = "
"
content = self.shelve(self.encode_html(content.rstrip("\n") +
"\n"))
elif tag == 'notextile':
content = self.shelve(content)
o1 = o2 = ''
c1 = c2 = ''
elif tag == 'pre':
content = self.shelve(self.encode_html(content.rstrip("\n") +
"\n"))
o1 = "" % atts
c2 = "
"
c1 = "" % atts
o2 = c2 = ''
c1 = '
'
elif tag == '###':
eat = True
else:
o2 = "\t<%s%s>" % (tag, atts)
c2 = "%s>" % tag
if not eat:
content = self.graf(content)
else:
content = ''
return o1, o2, content, c2, c1, eat
def formatFootnote(self, marker, atts='', anchor=True):
if anchor:
pattern = self.glyph_definitions['fn_foot_pattern']
else:
pattern = self.glyph_definitions['fn_ref_pattern']
return pattern % {'atts': atts, 'marker': marker}
def footnoteRef(self, text):
"""
>>> t = Textile()
>>> Py3 << t.footnoteRef('foo[1] ') # doctest: +ELLIPSIS
'foo1 '
"""
return re.compile(r'(?<=\S)\[(\d+)(!?)\](\s)?', re.U).sub(
self.footnoteID, text
)
def footnoteID(self, match):
footnoteNum, nolink, space = match.groups()
if not space:
space = ''
backref = ' class="footnote"'
if footnoteNum not in self.fn:
a = uuid.uuid4().hex
self.fn[footnoteNum] = a
backref = '%s id="fnrev%s"' % (backref, a)
footnoteID = self.fn[footnoteNum]
footref = '!' == nolink and footnoteNum or '%s' % (
footnoteID, footnoteNum
)
footref = self.formatFootnote(footref, backref, False)
return footref + space
def glyphs(self, text):
"""
Because of the split command, the regular expressions are different for
when the text at the beginning and the rest of the text.
for example:
let's say the raw text provided is "*Here*'s some textile"
before it gets to this glyphs method, the text has been converted to
"Here's some textile"
When run through the split, we end up with ["", "Here",
"", "'s some textile"]. The re.search that follows tells it
not to ignore html tags.
If the single quote is the first character on the line, it's an open
single quote. If it's the first character of one of those splits, it's
an apostrophe or closed single quote, but the regex will bear that out.
A similar situation occurs for double quotes as well.
So, for the first pass, we use the glyph_search_initial set of
regexes. For all remaining passes, we use glyph_search
>>> t = Textile()
>>> Py3 << t.glyphs("apostrophe's")
'apostrophe’s'
>>> Py3 << t.glyphs("back in '88")
'back in ’88'
>>> Py3 << t.glyphs('foo ...')
'foo …'
>>> Py3 << t.glyphs('--')
'—'
>>> Py3 << t.glyphs('FooBar[tm]')
'FooBar™'
>>> Py3 << t.glyphs("^|(?<=[\s>.\(\|])|[{[])? # leading text
" # opening quote
(?P
'
>>> Py3 << t.image('!'
"""
pattern = re.compile(r"""
(?:[\[{])? # pre
\! # opening !
(\<|\=|\>)? # optional alignment atts
(%s) # optional style,class atts
(?:\. )? # optional dot-space
([^\s(!]+) # presume this is the src
\s? # optional space
(?:\(([^\)]+)\))? # optional title
\! # closing
(?::(\S+))? # optional href
(?:[\]}]|(?=\s|$)) # lookahead: space or end of string
""" % self.cslh_re_s, re.U | re.X)
return pattern.sub(self.fImage, text)
def fImage(self, match):
# (None, '', '/imgs/myphoto.jpg', None, None)
align, atts, url, title, href = match.groups()
atts = self.pba(atts)
size = None
alignments = {'<': 'left', '=': 'center', '>': 'right'}
if not title:
title = ''
if not self.isRelURL(url) and self.get_sizes:
size = imagesize.getimagesize(url)
if href:
href = self.checkRefs(href)
url = self.checkRefs(url)
url = self.relURL(url)
out = []
if href:
out.append('' % href)
out.append('
')
if href:
out.append('')
return ''.join(out)
def code(self, text):
text = self.doSpecial(text, '
', '
', self.fCode)
text = self.doSpecial(text, '@', '@', self.fCode)
text = self.doSpecial(text, '', '
', self.fPre)
return text
def fCode(self, match):
before, text, after = match.groups()
if after is None:
after = ''
# text needs to be escaped
if not self.restricted:
text = self.encode_html(text, quotes=False)
return ''.join([before, self.shelve('%s
' % text), after])
def fPre(self, match):
before, text, after = match.groups()
if after is None:
after = ''
# text needs to be escaped
if not self.restricted:
text = self.encode_html(text)
return ''.join([before, '', self.shelve(text), '
', after])
def doSpecial(self, text, start, end, method):
pattern = re.compile(r'(^|\s|[\[({>|])%s(.*?)%s($|[\])}])?'
% (re.escape(start), re.escape(end)), re.M | re.S)
return pattern.sub(method, text)
def noTextile(self, text):
text = self.doSpecial(text, '" % atts
else:
dltag = "
"
out.append(dltag)
if definition != '' and term != '':
if definition.startswith('\n'):
definition = '
')
out = '\n'.join(out)
return out
def placeNoteLists(self, text):
"""Parse the text for endnotes."""
if self.notes:
o = OrderedDict()
for label, info in self.notes.items():
if 'seq' in info:
i = info['seq']
info['seq'] = label
o[i] = info
else:
self.unreferencedNotes[label] = info
if o:
# sort o by key
o = OrderedDict(sorted(o.items(), key=lambda t: t[0]))
self.notes = o
text_re = re.compile('
').strip()
term = self.graf(term)
definition = self.graf(definition)
out.extend(['\t\n%s\n
""" % (list_atts, result)
return result
def makeBackrefLink(self, info, g_links, i):
"""Given the pieces of a back reference link, create an tag."""
atts, content, infoid, link = '', '', '', ''
if 'def' in info:
link = info['def']['link']
backlink_type = link or g_links
i_ = self.encode_high(i)
allow_inc = i not in self.syms_re_s
i_ = int(i_)
if backlink_type == "!":
return ''
elif backlink_type == '^':
return """%s""" % (
info['refids'][0], i)
else:
result = []
for refid in info['refids']:
i_entity = self.decode_high(i_)
sup = """%s""" % (refid,
i_entity)
if allow_inc:
i_ += 1
result.append(sup)
result = ' '.join(result)
return result
def fParseNoteDefs(self, m):
"""Parse the note definitions and format them as HTML"""
label, link, att, content = m.groups()
# Assign an id if the note reference parse hasn't found the label yet.
if label not in self.notes:
self.notes[label] = {'id': uuid.uuid4().hex}
# Ignores subsequent defs using the same label
if 'def' not in self.notes[label]:
self.notes[label]['def'] = {'atts': self.pba(att), 'content':
self.graf(content), 'link': link}
return ''
def noteRef(self, text):
"""Search the text looking for note references."""
text_re = re.compile(r"""
\[ # start
(%s) # !atts
\#
([^\]!]+) # !label
([!]?) # !nolink
\]""" % self.cslh_re_s, re.X)
text = text_re.sub(self.fParseNoteRefs, text)
return text
def fParseNoteRefs(self, match):
"""Parse and format the matched text into note references.
By the time this function is called, all the defs will have been
processed into the notes array. So now we can resolve the link numbers
in the order we process the refs..."""
atts, label, nolink = match.groups()
atts = self.pba(atts)
nolink = nolink == '!'
# Assign a sequence number to this reference if there isn't one already
if label in self.notes:
num = self.notes[label]['seq']
else:
self.notes[label] = {
'seq': self.note_index, 'refids': [], 'id': ''
}
num = self.note_index
self.note_index += 1
# Make our anchor point and stash it for possible use in backlinks when
# the note list is generated later...
refid = uuid.uuid4().hex
self.notes[label]['refids'].append(refid)
# If we are referencing a note that hasn't had the definition parsed
# yet, then assign it an ID...
if not self.notes[label]['id']:
self.notes[label]['id'] = uuid.uuid4().hex
labelid = self.notes[label]['id']
# Build the link (if any)...
result = '%s' % (refid, num)
if not nolink:
result = """%s""" % (labelid, result)
# Build the reference...
result = '%s' % (atts, result)
return result
def encode_high(self, text):
"""Encode the text so that it is an appropriate HTML entity."""
return ord(text)
def decode_high(self, text):
"""Decode encoded HTML entities."""
h = HTMLParser()
text = '%s;' % text
return h.unescape(text)
def textile(text, head_offset=0, html_type='xhtml', auto_link=False,
encoding=None, output=None):
"""
Apply Textile to a block of text.
This function takes the following additional parameters:
auto_link - enable automatic linking of URLs (default: False)
head_offset - offset to apply to heading levels (default: 0)
html_type - 'xhtml' or 'html5' style tags (default: 'xhtml')
"""
return Textile(auto_link=auto_link, html_type=html_type).parse(text,
head_offset=head_offset)
def textile_restricted(text, lite=True, noimage=True, html_type='xhtml',
auto_link=False):
"""
Apply Textile to a block of text, with restrictions designed for weblog
comments and other untrusted input. Raw HTML is escaped, style attributes
are disabled, and rel='nofollow' is added to external links.
This function takes the following additional parameters:
auto_link - enable automatic linking of URLs (default: False)
html_type - 'xhtml' or 'html5' style tags (default: 'xhtml')
lite - restrict block tags to p, bq, and bc, disable tables (default: True)
noimage - disable image tags (default: True)
"""
return Textile(restricted=True, lite=lite, noimage=noimage,
auto_link=auto_link, html_type=html_type).parse( text,
rel='nofollow')
def setup_module(mod):
"""Inject Py3 to builtins for doctests."""
try:
import builtins
except ImportError:
import __builtin__ as builtins
from textile.tools.doctest_utils import Py3
builtins.Py3 = Py3
textile-2.2.2/textile/tests/ 0000755 0000767 0000024 00000000000 12462765643 016437 5 ustar dennis staff 0000000 0000000 textile-2.2.2/textile/tests/__init__.py 0000644 0000767 0000024 00000152105 12462764257 020554 0 ustar dennis staff 0000000 0000000 # -*- coding: utf-8 -*-
from __future__ import unicode_literals
import textile
import re
from nose.tools import eq_, assert_true
from nose.plugins.skip import SkipTest
"""
('>>> import textile')
'\nI am very serious.\n
',
'\t\nI am <b>very</b> serious.\n
'),
('I spoke.\nAnd none replied.', '\t
\nAnd none replied.Header 1
'),
('h2. Header 2', '\tHeader 2
'),
('h3. Header 3', '\tHeader 3
'),
('An old text\n\nbq. A block quotation.\n\nAny old text''',
'\t\n\t\t
\n\n\t
\nI really know.str(foo)
sure not sure.
\nof most soft drinks.
\nwhen I sprouted'
' that
\ncorn stalk from my
\ncabeza.Bingo.
'),
('h3()>[no]{color:red}. Bingo', '\tBingo
'),
('\n
',
'\na.gsub!( /, "" )\n
\n\n
'),
('\na.gsub!( /</, "" )\n
\n
The main text of the
\n'
'page goes here and will
\nstay to the left of the
\nsidebar.
I searched Google.
'), ('I searched "a search engine (Google)":http://google.com.', '\tI searched a search engine.
'), ('I am crazy about "Hobix":hobix\nand "it\'s":hobix "all":hobix I ever\n"link to":hobix!\n\n[hobix]http://hobix.com', '\tI am crazy about Hobix
\nand it’s '
'all I ever
\nlink to!
And others sat all round the small
\nmachine and paid it to sing to them.
We use CSS.
'), ('|one|two|three|\n|a|b|c|', '\tone | \n\t\t\ttwo | \n\t\t\tthree | \n\t\t
a | \n\t\t\tb | \n\t\t\tc | \n\t\t
name | \n\t\t\tage | \n\t\t\tsex | \n\t\t
joan | \n\t\t\t24 | \n\t\t\tf | \n\t\t
archie | \n\t\t\t29 | \n\t\t\tm | \n\t\t
bella | \n\t\t\t45 | \n\t\t\tf | \n\t\t
name | \n\t\t\tage | \n\t\t\tsex | \n\t\t
---|---|---|
joan | \n\t\t\t24 | \n\t\t\tf | \n\t\t
archie | \n\t\t\t29 | \n\t\t\tm | \n\t\t
bella | \n\t\t\t45 | \n\t\t\tf | \n\t\t
Hello\n\nHello Again\n\n\n\t
normal text
'), ('this is in a pre tag', '
this is in a pre tag'), ('"test1":http://foo.com/bar--baz\n\n"test2":http://foo.com/bar---baz\n\n"test3":http://foo.com/bar-17-18-baz', '\t\n\n\t' '\n\n\t' ''), ('"foo ==(bar)==":#foobar', '\t'), ('!http://render.mathim.com/A%5EtAx%20%3D%20A%5Et%28Ax%29.!', '\t
array[4] = 8
Links (like this), are now mangled in 2.1.0, whereas 2.0 parsed them correctly.
'), ('@monospaced text@, followed by text', '\tmonospaced text
, followed by text
some text
'), ('*:(foo)foo bar baz*', '\tfoo bar baz
'), ('pre.. foo bar baz\nquux', 'foo bar baz\nquux\n'), ('line of text\n\n leading spaces', '\t
line of text
\n\n leading spaces'), ('"some text":http://www.example.com/?q=foo%20bar and more text', '\tsome text and more text
'), ('(??some text??)', '\t(some text)
'), ('(*bold text*)', '\t(bold text)
'), ('H[~2~]O', '\tH2O
'), ("p=. Où est l'école, l'église s'il vous plaît?", """\tOù est l’école, l’église s’il vous plaît?
"""), ("p=. *_The_* _*Prisoner*_", """\tThe Prisoner
"""), ("""p=. "An emphasised _word._" & "*A spanned phrase.*" """, """\t“An emphasised word.” & “A spanned phrase.”
"""), ("""p=. "*Here*'s a word!" """, """\t“Here’s a word!”
"""), ("""p=. "Please visit our "Textile Test Page":http://textile.sitemonks.com" """, """\t“Please visit our Textile Test Page”
"""), ("""| Foreign EXPÓŅÉNTIAL |""", """\tForeign EXPÓŅÉNTIAL | \n\t\t
Piękne ŹDŹBŁO
"""), ("""p=. Tell me, what is AJAX(Asynchronous Javascript and XML), please?""", """\tTell me, what is AJAX, please?
"""), ('p{font-size:0.8em}. *TxStyle* is a documentation project of Textile 2.4 for "Textpattern CMS":http://texpattern.com.', '\tTxStyle is a documentation project of Textile 2.4 for Textpattern CMS.
'), (""""Übermensch":http://de/wikipedia.org/wiki/Übermensch""", """\t"""), ("""Here is some text with a block.\n\n\n\n\n\nbc. """, """\tHere is some text with a block.
\n\n\t\n\n\t\n\n<!-- Here is a comment block in a code block. -->\n
"""),
(""""Textile(c)" is a registered(r) 'trademark' of Textpattern(tm) -- or TXP(That's textpattern!) -- at least it was - back in '88 when 2x4 was (+/-)5(o)C ... QED!\n\np{font-size: 200%;}. 2(1/4) 3(1/2) 4(3/4)""",
"""\t“Textile©” is a registered® ‘trademark’ of Textpattern™ — or TXP — at least it was – back in ’88 when 2×4 was ±5°C … QED!
\n\n\t2¼ 3½ 4¾
"""), ("""|=. Testing colgroup and col syntax\n|:\\5. 80\n|a|b|c|d|e|\n\n|=. Testing colgroup and col syntax|\n|:\\5. 80|\n|a|b|c|d|e|""", """\ta | \n\t\t\tb | \n\t\t\tc | \n\t\t\td | \n\t\t\te | \n\t\t
a | \n\t\t\tb | \n\t\t\tc | \n\t\t\td | \n\t\t\te | \n\t\t
Title | \n\t\t\tStarring | \n\t\t\tDirector | \n\t\t\tWriter | \n\t\t\tNotes | \n\t\t
---|---|---|---|---|
This is the tfoot, centred | \n\t\t||||
The Usual Suspects | \n\t\t\tBenicio Del Toro, Gabriel Byrne, Stephen Baldwin, Kevin Spacey | \n\t\t\tBryan Singer | \n\t\t\tChris McQaurrie | \n\t\t\tOne of the finest films ever made | \n\t\t
Se7en | \n\t\t\tMorgan Freeman, Brad Pitt, Kevin Spacey | \n\t\t\tDavid Fincher | \n\t\t\tAndrew Kevin Walker | \n\t\t\tGreat psychological thriller | \n\t\t
Primer | \n\t\t\tDavid Sullivan, Shane Carruth | \n\t\t\tShane Carruth | \n\t\t\tShane Carruth | \n\t\t\t Amazing insight into trust and human psychology \nrather than science fiction. Terrific! | \n\t\t
District 9 | \n\t\t\tSharlto Copley, Jason Cope | \n\t\t\tNeill Blomkamp | \n\t\t\tNeill Blomkamp, Terri Tatchell | \n\t\t\t Social commentary layered on thick, \nbut boy is it done well | \n\t\t
Arlington Road | \n\t\t\tTim Robbins, Jeff Bridges | \n\t\t\tMark Pellington | \n\t\t\tEhren Kruger | \n\t\t\tAwesome study in neighbourly relations | \n\t\t
Phone Booth | \n\t\t\tColin Farrell, Kiefer Sutherland, Forest Whitaker | \n\t\t\tJoel Schumacher | \n\t\t\tLarry Cohen | \n\t\t\t Edge-of-the-seat stuff in this \nshort but brilliantly executed thriller | \n\t\t
Nourishing beverage for baby cows.
\nCold drink that goes great with cookies.
Here is a comment
\n\n\tHere is a comment
\n\n\tHere is a class that is a little extended and is
\nfollowed by a strong word!
; Content-type: text/javascript\n; Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\n; Expires: Sat, 24 Jul 2003 05:00:00 GMT\n; Last-Modified: Wed, 1 Jan 2025 05:00:00 GMT\n; Pragma: no-cache\n
\n\n\t123 test
\n\n\ttest 123
\n\n\t123 test
\n\n\ttest 123
"""), ("""#_(first#list) one\n# two\n# three\n\ntest\n\n#(ordered#list2).\n# one\n# two\n# three\n\ntest\n\n#_(class_4).\n# four\n# five\n# six\n\ntest\n\n#_ seven\n# eight\n# nine\n\ntest\n\n# one\n# two\n# three\n\ntest\n\n#22 22\n# 23\n# 24""", """\ttest
\n\n\ttest
\n\n\ttest
\n\n\ttest
\n\n\ttest
\n\n\ttest
\n\n\ttest
\n\n\t\t
| \n\t\t
\t
| \n\t\t
\n | \n\t\t
table | \n\t\t\tmore | \n\t\t\tbadass | \n\t\t
---|---|---|
Horizontal span of 3 | \n\t\t||
first | \n\t\t\tHAL | \n\t\t\t1 | \n\t\t
some | \n\t\t\tstyled | \n\t\t\tcontent | \n\t\t
spans 2 rows | \n\t\t\tthis is | \n\t\t\tquite a | \n\t\t
deep test | \n\t\t\tdon’t you think? | \n\t\t|
fifth | \n\t\t\tI’m a lumberjack | \n\t\t\t5 | \n\t\t
sixth | \n\t\t\tbold italics | \n\t\t\t6 | \n\t\t
strong | \n\t\t
em | \n\t\t
Inter-word | \n\t\t\tZIP-codes are 5- or 9-digit codes | \n\t\t
attribute list | \n\t\t
---|
align left | \n\t\t
align right | \n\t\t
center | \n\t\t
justify me | \n\t\t
valign top | \n\t\t
bottom | \n\t\t
Goodbye.
"""), ("""h2. A Definition list which covers the instance where a new definition list is created with a term without a definition\n\n- term :=\n- term2 := def""", """\tI spoke.
\nAnd none replied.
I know.
\nI really know.
I’m unaware
\nof most soft drinks.
I seriously blushed
\nwhen I sprouted'
' that
\ncorn stalk from my
\ncabeza.
\n\na.gsub!( /, "" )\n
\n
',
'\n\na.gsub!( /</, "" )\n
\n
'),
('The main text of the
\n'
'page goes here and will
\nstay to the left of the
\nsidebar.
I am crazy about Hobix
\nand it’s '
'all I ever
\nlink to!
And others sat all round the small
\nmachine and paid it to sing to them.
quux
') ) def testKnownValuesXHTML(self): # XHTML for t, h in self.xhtml_known_values: yield self.check_textile, t, h, 'xhtml' def testKnownValuesHTML(self): # HTML5 for t, h in self.html_known_values: yield self.check_textile, t, h, 'html5' def check_textile(self, input, expected_output, html_type): output = textile.textile(input, html_type=html_type) eq_(output, expected_output) class Tests(): def testFootnoteReference(self): html = textile.textile('YACC[1]') assert_true(re.search(r'^\tYACC1
', html)) def testFootnote(self): html = textile.textile('This is covered elsewhere[1].\n\nfn1. Down here, in fact.\n\nfn2. Here is another footnote.') assert_true(re.search(r'^\tThis is covered elsewhere1.
\n\n\t1 Down here, in fact.
\n\n\t2 Here is another footnote.
$', html)) html = textile.textile('''See[1] for details -- or perhaps[100] at a push.\n\nfn1. Here are the details.\n\nfn100(footy#otherid). A totally unrelated footnote.''') assert_true(re.search(r'^\tSee1 for details — or perhaps100 at a push.
\n\n\t1 Here are the details.
\n\n\t100 A totally unrelated footnote.
$', html)) html = textile.textile('''See[2] for details, and later, reference it again[2].\n\nfn2^(footy#otherid)[en]. Here are the details.''') assert_true(re.search(r'^\tSee2 for details, and later, reference it again2.
\n\n\t2 Here are the details.
$', html)) html = textile.textile('''See[3!] for details.\n\nfn3. Here are the details.''') assert_true(re.search(r'^\tSee3 for details.
\n\n\t3 Here are the details.
$', html)) html = textile.textile('''See[4!] for details.\n\nfn4^. Here are the details.''') assert_true(re.search(r'^\tSee4 for details.
\n\n\t4 Here are the details.
$', html)) def testURLWithHyphens(self): eq_(textile.textile('"foo":http://google.com/one--two'), '\t') def testIssue024TableColspan(self): eq_(textile.textile('|\\2. spans two cols |\n| col 1 | col 2 |'), '\tspans two cols | \n\t\t|
col 1 | \n\t\t\tcol 2 | \n\t\t
Hello\n\nAgain\n\n\n\t
normal text
') def testURLWithParens(self): text = '"python":http://en.wikipedia.org/wiki/Python_(programming_language)' expect = '\t' result = textile.textile(text) eq_(result, expect) def testTableWithHyphenStyles(self): text = 'table(linkblog-thumbnail).\n|(linkblog-thumbnail-cell). apple|bear|' expect = '\tapple | \n\t\t\tbear | \n\t\t
“z”
' eq_(result, expect) result = textile.textile('" z"') expect = '\t“ z”
' eq_(result, expect) def testIssue032(self): text = "|thing|||otherthing|" result = textile.textile(text) expect = "\tthing | \n\t\t\t\n\t\t\t | \n\t\t\t | otherthing | \n\t\t
test text
" eq_(result, expect) test = "_*test text*_" result = textile.textile(test) expect = "\ttest text
" eq_(result, expect) def testRestricted(self): test = "this is \"some\" *bold text*." result = textile.textile_restricted(test) expect = "\tthis is “some” bold text.
" eq_(result, expect) #Note that the HTML is escaped, thus rendering #the " result = textile.textile_restricted(test) expect = "\tHere is some text.
\n<script>alert(‘hello world’)</script>
Here’s some <!— commented out —> text.
" eq_(result, expect) def testQuotesInCode(self): test = "'quoted string'
"
result = textile.textile(test)
expect = "\t'quoted string'
текст1
', re.U).search(html)) def testAutoLinking(self): test = """some text "test":http://www.google.com http://www.google.com "$":http://www.google.com""" result = """\tsome text test www.google.com www.google.com
""" expect = textile.textile(test, auto_link=True) eq_(result, expect) def testPre(self): test = "some preformatted textother text" result = "\t
some preformatted textother text" expect = textile.textile(test) eq_(result, expect) def testSanitize(self): try: __import__('html5lib') except ImportError: raise SkipTest() test = "a paragraph of benign text" result = "\t
a paragraph of benign text
" expect = textile.Textile().parse(test, sanitize=True) eq_(result, expect) test = """a paragraph of evil text
""" result = 'a paragraph of evil text
' expect = textile.Textile().parse(test, sanitize=True) eq_(result, expect) test = """a paragraph of benign text
and more text
a paragraph of benign text
\nand more text
<A1> | \n\t\t\t<A2> <A3> | \n\t\t
*B1* | \n\t\t\t*B2* *B3* | \n\t\t
Scientists say the moon is slowly shrinking1.
\n\n\tTim Berners-Lee is one of the pioneer voices in favour of Net Neutrality1 and has expressed the view that ISPs should supply “connectivity with no strings attached”1 2
\n\n\tBerners-Lee admitted that the forward slashes \(”//”\) in a web address were actually unnecessary. He told the newspaper that he could easily have designed URLs not to have the forward slashes. “… it seemed like a good idea at the time,”3
\n\n\tScientists say1 the moon is quite small. But I, for one, don’t believe them. Others claim it to be made of cheese2. If this proves true I suspect we are in for troubled times3 as people argue over their “share” of the moon’s cheese. In the end, its limited size1 may prove problematic.
\n\n\tScientists say1 the moon is quite small. But I, for one, don’t believe them. Others claim it to be made of cheese2. If this proves true I suspect we are in for troubled times3 as people argue over their “share” of the moon’s cheese. In the end, its limited size1 may prove problematic.
\n\n\tScientists say the moon is slowly shrinking1.
\n\n\tSee2 for details, and later, reference it again2.
\n\n\t2 Here are the details.
$' assert_true(re.compile(searchstring).search(html)) def testFootnoteWithoutReflink(self): html = textile.textile('''See[3!] for details.\n\nfn3. Here are the details.''') searchstring = r'^\tSee3 for details.
\n\n\t3 Here are the details.
$' assert_true(re.compile(searchstring).search(html)) def testSquareBrackets(self): html = textile.textile("""1[^st^], 2[^nd^], 3[^rd^]. 2 log[~n~]\n\nA close[!http://textpattern.com/favicon.ico!]image.\nA tight["text":http://textpattern.com/]link.\nA ["footnoted link":http://textpattern.com/][182].""") searchstring = r'^\t1st, 2nd, 3rd. 2 logn
\n\n\tA closeimage.
\nA tighttextlink.
\nA footnoted link182.
We use CSS.
' expect = textile.textile(test, html_type="html5") eq_(result, expect) class TestSubclassing(): """Test Textile subclassing ability.""" def testChangeGlyphs(self): class TextilePL(textile.Textile): glyph_definitions = dict(textile.Textile.glyph_definitions, quote_double_open = '„' ) test = 'Test "quotes".' expect = '\tTest „quotes”.
' result = TextilePL().parse(test) eq_(expect, result) # Base Textile is unchanged. expect = '\tTest “quotes”.
' result = textile.textile(test) eq_(expect, result) textile-2.2.2/textile/textilefactory.py 0000644 0000767 0000024 00000004451 12462764257 020721 0 ustar dennis staff 0000000 0000000 from __future__ import unicode_literals from .core import Textile class TextileFactory(object): """ Use TextileFactory to create a Textile object which can be re-used to process multiple strings with the same settings. >>> from .tools.doctest_utils import Py3 >>> f = TextileFactory() >>> Py3 << f.process("some text here") '\\tsome text here
' >>> f = TextileFactory(restricted=True) >>> Py3 << f.process("more text here") '\\tmore text here
' Certain parameter values are not permitted because they are illogical: >>> f = TextileFactory(lite=True) Traceback (most recent call last): ... ValueError: lite can only be enabled in restricted mode >>> f = TextileFactory(head_offset=7) Traceback (most recent call last): ... ValueError: head_offset must be 0-6 >>> f = TextileFactory(html_type='invalid') Traceback (most recent call last): ... ValueError: html_type must be 'xhtml' or 'html5' """ def __init__(self, restricted=False, lite=False, sanitize=False, noimage=None, auto_link=False, get_sizes=False, head_offset=0, html_type='xhtml'): self.class_parms = {} self.method_parms = {} if lite and not restricted: raise ValueError("lite can only be enabled in restricted mode") if restricted: self.class_parms['restricted'] = True self.class_parms['lite'] = lite self.method_parms['rel'] = 'nofollow' if noimage is None: if restricted: noimage = True else: noimage = False self.class_parms['noimage'] = noimage self.method_parms['sanitize'] = sanitize self.class_parms['auto_link'] = auto_link self.class_parms['get_sizes'] = get_sizes if int(head_offset) not in range(0, 6): raise ValueError("head_offset must be 0-6") else: self.method_parms['head_offset'] = head_offset if html_type not in ['xhtml', 'html5']: raise ValueError("html_type must be 'xhtml' or 'html5'") else: self.class_parms['html_type'] = html_type def process(self, text): return Textile(**self.class_parms).parse(text, **self.method_parms) textile-2.2.2/textile/tools/ 0000755 0000767 0000024 00000000000 12462765643 016435 5 ustar dennis staff 0000000 0000000 textile-2.2.2/textile/tools/__init__.py 0000644 0000767 0000024 00000000000 12462764257 020534 0 ustar dennis staff 0000000 0000000 textile-2.2.2/textile/tools/doctest_utils.py 0000644 0000767 0000024 00000006612 12462764257 021701 0 ustar dennis staff 0000000 0000000 """ Utilities needed for making doctests compatible with both Py2 and Py3. Author: Radek Czajka