11#!/usr/bin/env python
22# -*- coding: utf-8 -*-
3+ from __future__ import unicode_literals
34
45__copyright__ = """
56Copyright (c) 2009, Jason Samsa, http://jsamsa.com/
@@ -75,7 +76,7 @@ class Textile(object):
7576
7677 pnct_re_s = r'[-!"#$%&()*+,/:;<=>?@\'\[\\\]\.^_`{|}~]'
7778 urlchar_re_s = r'[\w"$\-_.+!*\'(),";\/?:@=&%#{}|\\^~\[\]`]'
78- syms_re_s = u '¤§µ¶†‡•∗∴◊♠♣♥♦'
79+ syms_re_s = '¤§µ¶†‡•∗∴◊♠♣♥♦'
7980
8081 restricted_url_schemes = ('http' , 'https' , 'ftp' , 'mailto' )
8182 unrestricted_url_schemes = restricted_url_schemes + ('file' , 'tel' ,
@@ -181,9 +182,9 @@ def __init__(self, restricted=False, lite=False, noimage=False,
181182 # plus/minus
182183 re .compile (r'[([]\+\/-[])]' , re .I | re .U ),
183184 # 3+ uppercase acronym
184- regex .compile (u' \\ b([\\ p{Lu}][\\ p{Lu}0-9]{2,})\ \ b(?:[(]([^)]*)[)])' ),
185+ regex .compile (r'\ b([\p{Lu}][\p{Lu}0-9]{2,})\b(?:[(]([^)]*)[)])' ),
185186 # 3+ uppercase
186- regex .compile (u """(?:(?<=^)|(?<=\\ s)|(?<=[>\\ (;-]))([\\ p{Lu}]{3,})(\\ w*)(?=\ \ s|%s|$)(?=[^">]*?(<|$))""" %
187+ regex .compile (r """(?:(?<=^)|(?<=\s)|(?<=[>\(;-]))([\p{Lu}]{3,})(\w*)(?=\s|%s|$)(?=[^">]*?(<|$))""" %
187188 self .pnct_re_s ),
188189 ]
189190
@@ -235,7 +236,7 @@ def __init__(self, restricted=False, lite=False, noimage=False,
235236 def parse (self , text , rel = None , head_offset = 0 , sanitize = False ):
236237 """
237238 >>> import textile
238- >>> textile.textile('some textile')
239+ >>> Py3 << textile.textile('some textile')
239240 '\\ t<p>some textile</p>'
240241 """
241242 self .notes = OrderedDict ()
@@ -275,48 +276,48 @@ def pba(self, block_attributes, element=None):
275276 Parse block attributes.
276277
277278 >>> t = Textile()
278- >>> t.pba(r'\3 ')
279+ >>> Py3 << t.pba(r'\3 ')
279280 ''
280- >>> t.pba(r'\\ 3', element='td')
281+ >>> Py3 << t.pba(r'\\ 3', element='td')
281282 ' colspan="3"'
282- >>> t.pba(r'/4', element='td')
283+ >>> Py3 << t.pba(r'/4', element='td')
283284 ' rowspan="4"'
284- >>> t.pba(r'\\ 3/4', element='td')
285+ >>> Py3 << t.pba(r'\\ 3/4', element='td')
285286 ' colspan="3" rowspan="4"'
286287
287- >>> t.pba('^', element='td')
288+ >>> Py3 << t.pba('^', element='td')
288289 ' style="vertical-align:top;"'
289290
290- >>> t.pba('{line-height:18px}')
291+ >>> Py3 << t.pba('{line-height:18px}')
291292 ' style="line-height:18px;"'
292293
293- >>> t.pba('(foo-bar)')
294+ >>> Py3 << t.pba('(foo-bar)')
294295 ' class="foo-bar"'
295296
296- >>> t.pba('(#myid)')
297+ >>> Py3 << t.pba('(#myid)')
297298 ' id="myid"'
298299
299- >>> t.pba('(foo-bar#myid)')
300+ >>> Py3 << t.pba('(foo-bar#myid)')
300301 ' class="foo-bar" id="myid"'
301302
302- >>> t.pba('((((')
303+ >>> Py3 << t.pba('((((')
303304 ' style="padding-left:4em;"'
304305
305- >>> t.pba(')))')
306+ >>> Py3 << t.pba(')))')
306307 ' style="padding-right:3em;"'
307308
308- >>> t.pba('[fr]')
309+ >>> Py3 << t.pba('[fr]')
309310 ' lang="fr"'
310311
311- >>> t.pba(r'\\ 5 80', 'col')
312+ >>> Py3 << t.pba(r'\\ 5 80', 'col')
312313 ' span="5" width="80"'
313314
314315 >>> rt = Textile()
315316 >>> rt.restricted = True
316- >>> rt.pba('[en]')
317+ >>> Py3 << rt.pba('[en]')
317318 ' lang="en"'
318319
319- >>> rt.pba('(#id)')
320+ >>> Py3 << rt.pba('(#id)')
320321 ''
321322
322323 """
@@ -434,7 +435,7 @@ def hasRawText(self, text):
434435 def table (self , text ):
435436 r"""
436437 >>> t = Textile()
437- >>> t.table('(rowclass). |one|two|three|\n|a|b|c|')
438+ >>> Py3 << t.table('(rowclass). |one|two|three|\n|a|b|c|')
438439 '\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'
439440 """
440441 text = text + "\n \n "
@@ -578,12 +579,12 @@ def fTable(self, match):
578579 def lists (self , text ):
579580 """
580581 >>> t = Textile()
581- >>> t.lists("* one\\ n* two\\ n* three")
582+ >>> Py3 << t.lists("* one\\ n* two\\ n* three")
582583 '\\ t<ul>\\ n\\ t\\ t<li>one</li>\\ n\\ t\\ t<li>two</li>\\ n\\ t\\ t<li>three</li>\\ n\\ t</ul>'
583584 """
584585
585586 #Replace line-initial bullets with asterisks
586- bullet_pattern = re .compile (u '^•' , re .U | re .M )
587+ bullet_pattern = re .compile ('^•' , re .U | re .M )
587588
588589 pattern = re .compile (r'^((?:[*;:]+|[*;:#]*#(?:_|\d+)?)%s[ .].*)$(?![^#*;:])'
589590 % self .csl_re_s , re .U | re .M | re .S )
@@ -719,7 +720,7 @@ def doBr(self, match):
719720 def block (self , text , head_offset = 0 ):
720721 """
721722 >>> t = Textile()
722- >>> t.block('h1. foobar baby')
723+ >>> Py3 << t.block('h1. foobar baby')
723724 '\\ t<h1>foobar baby</h1>'
724725 """
725726 if not self .lite :
@@ -794,16 +795,16 @@ def block(self, text, head_offset=0):
794795 def fBlock (self , tag , atts , ext , cite , content ):
795796 """
796797 >>> t = Textile()
797- >>> t.fBlock("bq", "", None, "", "Hello BlockQuote")
798+ >>> Py3 << t.fBlock("bq", "", None, "", "Hello BlockQuote")
798799 ('\\ t<blockquote>\\ n', '\\ t\\ t<p>', 'Hello BlockQuote', '</p>', '\\ n\\ t</blockquote>', False)
799800
800- >>> t.fBlock("bq", "", None, "http://google.com", "Hello BlockQuote")
801+ >>> Py3 << t.fBlock("bq", "", None, "http://google.com", "Hello BlockQuote")
801802 ('\\ t<blockquote cite="http://google.com">\\ n', '\\ t\\ t<p>', 'Hello BlockQuote', '</p>', '\\ n\\ t</blockquote>', False)
802803
803- >>> t.fBlock("bc", "", None, "", 'printf "Hello, World";') # doctest: +ELLIPSIS
804+ >>> Py3 << t.fBlock("bc", "", None, "", 'printf "Hello, World";') # doctest: +ELLIPSIS
804805 ('<pre>', '<code>', ..., '</code>', '</pre>', False)
805806
806- >>> t.fBlock("h1", "", None, "", "foobar")
807+ >>> Py3 << t.fBlock("h1", "", None, "", "foobar")
807808 ('', '\\ t<h1>', 'foobar', '</h1>', '', False)
808809 """
809810 att = atts
@@ -911,7 +912,7 @@ def formatFootnote(self, marker, atts='', anchor=True):
911912 def footnoteRef (self , text ):
912913 """
913914 >>> t = Textile()
914- >>> t.footnoteRef('foo[1] ') # doctest: +ELLIPSIS
915+ >>> Py3 << t.footnoteRef('foo[1] ') # doctest: +ELLIPSIS
915916 'foo<sup class="footnote" id="fnrev..."><a href="#fn...">1</a></sup> '
916917 """
917918 return re .compile (r'(?<=\S)\[(\d+)(!?)\](\s)?' , re .U ).sub (
@@ -954,22 +955,22 @@ def glyphs(self, text):
954955
955956 >>> t = Textile()
956957
957- >>> t.glyphs("apostrophe's")
958+ >>> Py3 << t.glyphs("apostrophe's")
958959 'apostrophe’s'
959960
960- >>> t.glyphs("back in '88")
961+ >>> Py3 << t.glyphs("back in '88")
961962 'back in ’88'
962963
963- >>> t.glyphs('foo ...')
964+ >>> Py3 << t.glyphs('foo ...')
964965 'foo …'
965966
966- >>> t.glyphs('--')
967+ >>> Py3 << t.glyphs('--')
967968 '—'
968969
969- >>> t.glyphs('FooBar[tm]')
970+ >>> Py3 << t.glyphs('FooBar[tm]')
970971 'FooBar™'
971972
972- >>> t.glyphs("<p><cite>Cat's Cradle</cite> by Vonnegut</p>")
973+ >>> Py3 << t.glyphs("<p><cite>Cat's Cradle</cite> by Vonnegut</p>")
973974 '<p><cite>Cat’s Cradle</cite> by Vonnegut</p>'
974975
975976 """
@@ -993,9 +994,9 @@ def getRefs(self, text):
993994 Capture and store URL references in self.urlrefs.
994995
995996 >>> t = Textile()
996- >>> t.getRefs("some text [Google]http://www.google.com")
997+ >>> Py3 << t.getRefs("some text [Google]http://www.google.com")
997998 'some text '
998- >>> t.urlrefs
999+ >>> Py3 << t.urlrefs
9991000 {'Google': 'http://www.google.com'}
10001001
10011002 """
@@ -1029,10 +1030,10 @@ def isRelURL(self, url):
10291030 def relURL (self , url ):
10301031 """
10311032 >>> t = Textile()
1032- >>> t.relURL("http://www.google.com/")
1033+ >>> Py3 << t.relURL("http://www.google.com/")
10331034 'http://www.google.com/'
10341035 >>> t.restricted = True
1035- >>> t.relURL("gopher://gopher.com/")
1036+ >>> Py3 << t.relURL("gopher://gopher.com/")
10361037 '#'
10371038
10381039 """
@@ -1050,7 +1051,7 @@ def retrieve(self, text):
10501051 """
10511052 >>> t = Textile()
10521053 >>> id = t.shelve("foobar")
1053- >>> t.retrieve(id)
1054+ >>> Py3 << t.retrieve(id)
10541055 'foobar'
10551056 """
10561057 while True :
@@ -1064,7 +1065,7 @@ def retrieve(self, text):
10641065 def encode_html (self , text , quotes = True ):
10651066 """Return text that's safe for an HTML attribute.
10661067 >>> t = Textile()
1067- >>> t.encode_html('this is a "test" of text that\\ \' s safe to put in an <html> attribute.')
1068+ >>> Py3 << t.encode_html('this is a "test" of text that\\ \' s safe to put in an <html> attribute.')
10681069 'this is a "test" of text that's safe to put in an <html> attribute.'
10691070 """
10701071 a = (
@@ -1111,7 +1112,7 @@ def graf(self, text):
11111112 def autoLink (self , text ):
11121113 """
11131114 >>> t = Textile()
1114- >>> t.autoLink("http://www.ya.ru")
1115+ >>> Py3 << t.autoLink("http://www.ya.ru")
11151116 '"$":http://www.ya.ru'
11161117 """
11171118
@@ -1122,7 +1123,7 @@ def autoLink(self, text):
11221123 def links (self , text ):
11231124 """
11241125 >>> t = Textile()
1125- >>> t.links('fooobar "Google":http://google.com/foobar/ and hello world "flickr":http://flickr.com/photos/jsamsa/ ') # doctest: +ELLIPSIS
1126+ >>> Py3 << t.links('fooobar "Google":http://google.com/foobar/ and hello world "flickr":http://flickr.com/photos/jsamsa/ ') # doctest: +ELLIPSIS
11261127 'fooobar ... and hello world ...'
11271128 """
11281129
@@ -1223,10 +1224,10 @@ def encode_url(self, url):
12231224 host = netloc_parsed ['host' ]
12241225 port = netloc_parsed ['port' ] and netloc_parsed ['port' ]
12251226 path = '/' .join ( # could be encoded slashes!
1226- quote (unquote (pce ).encode ('utf8' ), '' )
1227+ quote (unquote (pce ).encode ('utf8' ), b '' )
12271228 for pce in parsed .path .split ('/' )
12281229 )
1229- query = quote (unquote (parsed .query ), '=&?/' )
1230+ query = quote (unquote (parsed .query ), b '=&?/' )
12301231 fragment = quote (unquote (parsed .fragment ))
12311232
12321233 # put it back together
@@ -1244,7 +1245,7 @@ def encode_url(self, url):
12441245 def span (self , text ):
12451246 """
12461247 >>> t = Textile()
1247- >>> t.span(r"hello %(bob)span *strong* and **bold**% goodbye")
1248+ >>> Py3 << t.span(r"hello %(bob)span *strong* and **bold**% goodbye")
12481249 'hello <span class="bob">span <strong>strong</strong> and <b>bold</b></span> goodbye'
12491250 """
12501251 qtags = (r'\*\*' , r'\*' , r'\?\?' , r'\-' , r'__' ,
@@ -1295,9 +1296,9 @@ def fSpan(self, match):
12951296 def image (self , text ):
12961297 """
12971298 >>> t = Textile()
1298- >>> t.image('!/imgs/myphoto.jpg!:http://jsamsa.com')
1299+ >>> Py3 << t.image('!/imgs/myphoto.jpg!:http://jsamsa.com')
12991300 '<a href="http://jsamsa.com" class="img"><img alt="" src="/imgs/myphoto.jpg" /></a>'
1300- >>> t.image('!</imgs/myphoto.jpg!')
1301+ >>> Py3 << t.image('!</imgs/myphoto.jpg!')
13011302 '<img align="left" alt="" src="/imgs/myphoto.jpg" />'
13021303 """
13031304 pattern = re .compile (r"""
@@ -1521,7 +1522,7 @@ def fNoteLists(self, match):
15211522 content = info ['def' ]['content' ]
15221523 li = """\t <li%s>%s</li>""" % (atts , content )
15231524 o .append (li )
1524- self .notelist_cache [index ] = u "\n " .join (o )
1525+ self .notelist_cache [index ] = "\n " .join (o )
15251526 result = self .notelist_cache [index ]
15261527 if result :
15271528 list_atts = self .pba (att )
@@ -1665,3 +1666,13 @@ def textile_restricted(text, lite=True, noimage=True, html_type='xhtml',
16651666 return Textile (restricted = True , lite = lite , noimage = noimage ,
16661667 auto_link = auto_link , html_type = html_type ).parse ( text ,
16671668 rel = 'nofollow' )
1669+
1670+
1671+ def setup_module (mod ):
1672+ """Inject Py3 to builtins for doctests."""
1673+ try :
1674+ import builtins
1675+ except ImportError :
1676+ import __builtin__ as builtins
1677+ from textile .tools .doctest_utils import Py3
1678+ builtins .Py3 = Py3
0 commit comments