Skip to content

Commit eea71e3

Browse files
authored
Merge pull request #67 from brondsem/restricted_styles
Restore restricted mode effect on inline styles. Fixes #65 Thanks, @brondsem ! And sorry for the delay.
2 parents f833df6 + 304d074 commit eea71e3

File tree

5 files changed

+78
-36
lines changed

5 files changed

+78
-36
lines changed

tests/test_textile.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,46 @@ def test_restricted():
5353

5454
assert result == expect
5555

56+
test = "p{color:blue}. is this blue?"
57+
result = textile.textile_restricted(test)
58+
expect = '\t<p>is this blue?</p>'
59+
60+
assert result == expect
61+
62+
test = """\
63+
table{border:1px solid black}.
64+
|={color:gray}. Your caption goes here
65+
|~.
66+
|{position:absolute}. A footer | foo |
67+
|-.
68+
|_{font-size:xxlarge}. header|_=. centered header|
69+
|~. bottom aligned|{background:red;width:200px}. asfd|"""
70+
result = textile.textile_restricted(test, lite=False)
71+
# styles from alignment hints like =. and ~. are ok
72+
expect = '''\
73+
\t<table>
74+
\t<caption>Your caption goes here</caption>
75+
\t
76+
\t<tfoot>
77+
\t\t<tr>
78+
\t\t\t<td>A footer </td>
79+
\t\t\t<td> foo </td>
80+
\t\t</tr>
81+
\t</tfoot>
82+
\t<tbody>
83+
\t\t<tr>
84+
\t\t\t<th>header</th>
85+
\t\t\t<th style="text-align:center;">centered header</th>
86+
\t\t</tr>
87+
\t\t<tr>
88+
\t\t\t<td style="vertical-align:bottom;">bottom aligned</td>
89+
\t\t\t<td>asfd</td>
90+
\t\t</tr>
91+
\t</tbody>
92+
\t</table>'''
93+
94+
assert result == expect
95+
5696
def test_unicode_footnote():
5797
html = textile.textile('текст[1]')
5898
assert re.compile(r'^\t<p>текст<sup class="footnote" id="fnrev([a-f0-9]{32})-1"><a href="#fn\1-1">1</a></sup></p>$', re.U).search(html) is not None

textile/core.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ def fTextileList(self, match):
363363
if ';' in pt and ':' in tl:
364364
ls[tl] = 2
365365

366-
atts = pba(atts)
366+
atts = pba(atts, restricted=self.restricted)
367367
tabs = '\t' * len(tl)
368368
# If start is still None, set it to '', else leave the value
369369
# that we've already formatted.
@@ -961,7 +961,7 @@ def _casesdefault(c, pop, popped, url_chars, counts, pre):
961961
text = self.span(text)
962962
text = self.glyphs(text)
963963
url = self.shelveURL(self.encode_url(urlunsplit(uri_parts)))
964-
attributes = parse_attributes(atts)
964+
attributes = parse_attributes(atts, restricted=self.restricted)
965965
if title:
966966
# if the title contains unicode data, it is annoying to get Python
967967
# 2.6 and all the latter versions working properly. But shelving
@@ -1078,7 +1078,7 @@ def fSpan(self, match):
10781078
}
10791079

10801080
tag = qtags[tag]
1081-
atts = pba(atts)
1081+
atts = pba(atts, restricted=self.restricted)
10821082
if cite:
10831083
atts = '{0} cite="{1}"'.format(atts, cite.rstrip())
10841084

@@ -1131,7 +1131,7 @@ def fImage(self, match):
11311131
atts.update(height=six.text_type(size[1]))
11321132
atts.update(src=url)
11331133
if attributes:
1134-
atts.update(parse_attributes(attributes))
1134+
atts.update(parse_attributes(attributes, restricted=self.restricted))
11351135
if title:
11361136
atts.update(title=title)
11371137
if size:
@@ -1220,7 +1220,7 @@ def fRCList(self, match):
12201220
atts, content = m.groups()
12211221
# cleanup
12221222
content = content.strip()
1223-
atts = pba(atts)
1223+
atts = pba(atts, restricted=self.restricted)
12241224

12251225
# split the content into the term and definition
12261226
xm = re.match(r'^(.*?)[\s]*:=(.*?)[\s]*(=:|:=)?[\s]*$', content,
@@ -1306,7 +1306,7 @@ def fNoteLists(self, match):
13061306
o.append(li)
13071307
self.notelist_cache[index] = "\n".join(o)
13081308
result = self.notelist_cache[index]
1309-
list_atts = pba(att)
1309+
list_atts = pba(att, restricted=self.restricted)
13101310
result = '<ol{0}>\n{1}\n\t</ol>'.format(list_atts, result)
13111311
return result
13121312

@@ -1351,7 +1351,7 @@ def fParseNoteDefs(self, m):
13511351

13521352
# Ignores subsequent defs using the same label
13531353
if 'def' not in self.notes[label]: # pragma: no branch
1354-
self.notes[label]['def'] = {'atts': pba(att), 'content':
1354+
self.notes[label]['def'] = {'atts': pba(att, restricted=self.restricted), 'content':
13551355
self.graf(content), 'link': link}
13561356
return ''
13571357

@@ -1373,7 +1373,7 @@ def fParseNoteRefs(self, match):
13731373
processed into the notes array. So now we can resolve the link numbers
13741374
in the order we process the refs..."""
13751375
atts, label, nolink = match.groups()
1376-
atts = pba(atts)
1376+
atts = pba(atts, restricted=self.restricted)
13771377
nolink = nolink == '!'
13781378

13791379
# Assign a sequence number to this reference if there isn't one already

textile/objects/block.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def __init__(self, textile, tag, atts, ext, cite, content):
2020
self.cite = cite
2121
self.content = content
2222

23-
self.attributes = parse_attributes(atts)
23+
self.attributes = parse_attributes(atts, restricted=self.textile.restricted)
2424
self.outer_tag = ''
2525
self.inner_tag = ''
2626
self.outer_atts = OrderedDict()
@@ -69,7 +69,7 @@ def process(self):
6969
if 'id' not in self.attributes:
7070
self.attributes.update({'id': 'fn{0}'.format(fnid)})
7171
else:
72-
supp_id = parse_attributes('(#fn{0})'.format(fnid))
72+
supp_id = parse_attributes('(#fn{0})'.format(fnid), restricted=self.textile.restricted)
7373

7474

7575
if '^' not in self.atts:

textile/objects/table.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
class Table(object):
1818
def __init__(self, textile, tatts, rows, summary):
1919
self.textile = textile
20-
self.attributes = parse_attributes(tatts, 'table')
20+
self.attributes = parse_attributes(tatts, 'table', restricted=self.textile.restricted)
2121
if summary:
2222
self.attributes.update(summary=summary.strip())
2323
self.input = rows
@@ -44,7 +44,7 @@ def process(self):
4444
caption_re = re.compile(captionpattern, re.S)
4545
cmtch = caption_re.match(row)
4646
if cmtch:
47-
caption = Caption(**cmtch.groupdict())
47+
caption = Caption(restricted=self.textile.restricted, **cmtch.groupdict())
4848
self.caption = '\n{0}'.format(caption.caption)
4949
row = cmtch.group('row').lstrip()
5050
if row == '':
@@ -60,7 +60,7 @@ def process(self):
6060
colgroup_atts, cols = colgroup_data, None
6161
if '|' in colgroup_data:
6262
colgroup_atts, cols = colgroup_data.split('|', 1)
63-
colgrp = Colgroup(cols, colgroup_atts)
63+
colgrp = Colgroup(cols, colgroup_atts, restricted=self.textile.restricted)
6464
self.colgroup = colgrp.process()
6565
if row == '':
6666
continue
@@ -79,13 +79,13 @@ def process(self):
7979
if rgrp:
8080
groups.append('\n\t{0}'.format(rgrp.process()))
8181
rgrp = grptypes[grpmatch.group('part')](grpmatch.group(
82-
'rgrpatts'))
82+
'rgrpatts'), restricted=self.textile.restricted)
8383
row = grpmatch.group('row')
8484

8585
rmtch = re.search(r'^(?P<ratts>{0}{1}\. )(?P<row>.*)'.format(
8686
align_re_s, cls_re_s), row.lstrip())
8787
if rmtch:
88-
row_atts = parse_attributes(rmtch.group('ratts'), 'tr')
88+
row_atts = parse_attributes(rmtch.group('ratts'), 'tr', restricted=self.textile.restricted)
8989
row = rmtch.group('row')
9090
else:
9191
row_atts = {}
@@ -102,7 +102,7 @@ def process(self):
102102
cls_re_s), cell, flags=re.S)
103103
if cmtch:
104104
catts = cmtch.group('catts')
105-
cell_atts = parse_attributes(catts, 'td')
105+
cell_atts = parse_attributes(catts, 'td', restricted=self.textile.restricted)
106106
cell = cmtch.group('cell')
107107
else:
108108
cell_atts = {}
@@ -139,8 +139,8 @@ def process(self):
139139

140140

141141
class Caption(object):
142-
def __init__(self, capts, cap, row):
143-
self.attributes = parse_attributes(capts)
142+
def __init__(self, capts, cap, row, restricted):
143+
self.attributes = parse_attributes(capts, restricted=restricted)
144144
self.caption = self.process(cap)
145145

146146
def process(self, cap):
@@ -149,17 +149,18 @@ def process(self, cap):
149149

150150

151151
class Colgroup(object):
152-
def __init__(self, cols, atts):
152+
def __init__(self, cols, atts, restricted):
153153
self.row = ''
154154
self.attributes = atts
155155
self.cols = cols
156+
self.restricted = restricted
156157

157158
def process(self):
158159
enc = 'unicode'
159160
if six.PY2: # pragma: no branch
160161
enc = 'UTF-8'
161162

162-
group_atts = parse_attributes(self.attributes, 'col')
163+
group_atts = parse_attributes(self.attributes, 'col', restricted=self.restricted)
163164
colgroup = ElementTree.Element('colgroup', attrib=group_atts)
164165
colgroup.text = '\n\t'
165166
if self.cols is not None:
@@ -168,7 +169,7 @@ def process(self):
168169
# colgroup is the first item in match_cols, the remaining items are
169170
# cols.
170171
for idx, col in enumerate(match_cols):
171-
col_atts = parse_attributes(col.strip(), 'col')
172+
col_atts = parse_attributes(col.strip(), 'col', restricted=self.restricted)
172173
ElementTree.SubElement(colgroup, 'col', col_atts)
173174
colgrp = ElementTree.tostring(colgroup, encoding=enc)
174175
# cleanup the extra xml declaration if it exists, (python versions
@@ -205,25 +206,25 @@ def process(self):
205206

206207

207208
class _TableSection(object):
208-
def __init__(self, tag, attributes):
209+
def __init__(self, tag, attributes, restricted):
209210
self.tag = tag
210-
self.attributes = parse_attributes(attributes)
211+
self.attributes = parse_attributes(attributes, restricted=restricted)
211212
self.rows = []
212213

213214
def process(self):
214215
return generate_tag(self.tag, '{0}\n\t'.format(''.join(self.rows)), self.attributes)
215216

216217

217218
class Thead(_TableSection):
218-
def __init__(self, attributes):
219-
super(Thead, self).__init__('thead', attributes)
219+
def __init__(self, attributes, restricted):
220+
super(Thead, self).__init__('thead', attributes, restricted)
220221

221222

222223
class Tbody(_TableSection):
223-
def __init__(self, attributes):
224-
super(Tbody, self).__init__('tbody', attributes)
224+
def __init__(self, attributes, restricted):
225+
super(Tbody, self).__init__('tbody', attributes, restricted)
225226

226227

227228
class Tfoot(_TableSection):
228-
def __init__(self, attributes):
229-
super(Tfoot, self).__init__('tfoot', attributes)
229+
def __init__(self, attributes, restricted):
230+
super(Tfoot, self).__init__('tfoot', attributes, restricted)

textile/utils.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def normalize_newlines(string):
105105
out = re.sub(r'"$', '" ', out)
106106
return out
107107

108-
def parse_attributes(block_attributes, element=None, include_id=True):
108+
def parse_attributes(block_attributes, element=None, include_id=True, restricted=False):
109109
vAlign = {'^': 'top', '-': 'middle', '~': 'bottom'}
110110
hAlign = {'<': 'left', '=': 'center', '>': 'right', '<>': 'justify'}
111111
style = []
@@ -136,10 +136,11 @@ def parse_attributes(block_attributes, element=None, include_id=True):
136136
if m:
137137
style.append("vertical-align:{0}".format(vAlign[m.group(1)]))
138138

139-
m = re.search(r'\{([^}]*)\}', matched)
140-
if m:
141-
style.extend(m.group(1).rstrip(';').split(';'))
142-
matched = matched.replace(m.group(0), '')
139+
if not restricted:
140+
m = re.search(r'\{([^}]*)\}', matched)
141+
if m:
142+
style.extend(m.group(1).rstrip(';').split(';'))
143+
matched = matched.replace(m.group(0), '')
143144

144145
m = re.search(r'\[([^\]]+)\]', matched, re.U)
145146
if m:
@@ -197,9 +198,9 @@ def parse_attributes(block_attributes, element=None, include_id=True):
197198
result['width'] = width
198199
return result
199200

200-
def pba(block_attributes, element=None, include_id=True):
201+
def pba(block_attributes, element=None, include_id=True, restricted=False):
201202
"""Parse block attributes."""
202-
attrs = parse_attributes(block_attributes, element, include_id)
203+
attrs = parse_attributes(block_attributes, element, include_id, restricted)
203204
if not attrs:
204205
return ''
205206
result = ' '.join(['{0}="{1}"'.format(k, v) for k, v in attrs.items()])

0 commit comments

Comments
 (0)