Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: cms/converters.py

Issue 29378736: NoIssue - Improves converters.py PEP8 compliance (Closed) Base URL: https://hg.adblockplus.org/cms
Patch Set: Created March 8, 2017, 6:06 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tox.ini » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # This file is part of the Adblock Plus web scripts, 1 # This file is part of the Adblock Plus web scripts,
2 # Copyright (C) 2006-2016 Eyeo GmbH 2 # Copyright (C) 2006-2016 Eyeo GmbH
3 # 3 #
4 # Adblock Plus is free software: you can redistribute it and/or modify 4 # Adblock Plus is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License version 3 as 5 # it under the terms of the GNU General Public License version 3 as
6 # published by the Free Software Foundation. 6 # published by the Free Software Foundation.
7 # 7 #
8 # Adblock Plus is distributed in the hope that it will be useful, 8 # Adblock Plus is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details. 11 # GNU General Public License for more details.
12 # 12 #
13 # You should have received a copy of the GNU General Public License 13 # You should have received a copy of the GNU General Public License
14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
15 15
16 import os 16 import os
17 import HTMLParser 17 import HTMLParser
18 import re 18 import re
19 19
20 import jinja2 20 import jinja2
21 import markdown 21 import markdown
22 22
23 23
24 # Monkey-patch Markdown's isBlockLevel function to ensure that no paragraphs are 24 # Monkey-patch Markdown's isBlockLevel function to ensure that no paragraphs
25 # inserted into the <head> tag 25 # are inserted into the <head> tag
26 orig_isBlockLevel = markdown.util.isBlockLevel 26 orig_isBlockLevel = markdown.util.isBlockLevel
27 27
28 28
29 def isBlockLevel(tag): 29 def isBlockLevel(tag):
30 if tag == 'head': 30 if tag == 'head':
31 return True 31 return True
32 else: 32 return orig_isBlockLevel(tag)
33 return orig_isBlockLevel(tag) 33
34 markdown.util.isBlockLevel = isBlockLevel 34 markdown.util.isBlockLevel = isBlockLevel
35 35
36 html_escapes = { 36 html_escapes = {
37 '<': '&lt;', 37 '<': '&lt;',
38 '>': '&gt;', 38 '>': '&gt;',
39 '&': '&amp;', 39 '&': '&amp;',
40 '"': '&quot;', 40 '"': '&quot;',
41 "'": '&#39;', 41 "'": '&#39;',
42 } 42 }
43 43
(...skipping 14 matching lines...) Expand all
58 self._inside_fixed = False 58 self._inside_fixed = False
59 self._attrs = {} 59 self._attrs = {}
60 self._pagename = pagename 60 self._pagename = pagename
61 61
62 # Force-escape ampersands, otherwise the parser will autocomplete bogus 62 # Force-escape ampersands, otherwise the parser will autocomplete bogus
63 # entities. 63 # entities.
64 text = re.sub(r'&(?!\S+;)', '&amp;', text) 64 text = re.sub(r'&(?!\S+;)', '&amp;', text)
65 65
66 try: 66 try:
67 self.feed(text) 67 self.feed(text)
68 return ''.join(self._string), self._attrs, [''.join(s) for s in self ._fixed_strings] 68 return (''.join(self._string),
69 self._attrs, [''.join(s) for s in self._fixed_strings])
69 finally: 70 finally:
70 self._string = None 71 self._string = None
71 self._attrs = None 72 self._attrs = None
72 self._pagename = None 73 self._pagename = None
73 self._inside_fixed = False 74 self._inside_fixed = False
74 self._fixed_strings = None 75 self._fixed_strings = None
75 76
76 def handle_starttag(self, tag, attrs): 77 def handle_starttag(self, tag, attrs):
77 if self._inside_fixed: 78 if self._inside_fixed:
78 raise Exception("Unexpected HTML tag '%s' inside a fixed string on p age %s" % (tag, self._pagename)) 79 raise Exception("Unexpected HTML tag '{}' inside a fixed string"
79 elif tag == 'fix': 80 'on page {}'.format(tag, self._pagename))
81 if tag == 'fix':
80 self._inside_fixed = True 82 self._inside_fixed = True
81 self._fixed_strings.append([]) 83 self._fixed_strings.append([])
82 elif tag in self._whitelist: 84 if tag in self._whitelist:
83 self._attrs.setdefault(tag, []).append(attrs) 85 self._attrs.setdefault(tag, []).append(attrs)
84 self._string.append('<%s>' % tag) 86 self._string.append('<{}>'.format(tag))
85 else: 87 else:
86 raise Exception("Unexpected HTML tag '%s' in localizable string on p age %s" % (tag, self._pagename)) 88 raise Exception("Unexpected HTML tag '{}' inside a fixed string"
89 'on page {}'.format(tag, self._pagename))
87 90
88 def handle_endtag(self, tag): 91 def handle_endtag(self, tag):
89 if tag == 'fix': 92 if tag == 'fix':
90 self._string.append('{%d}' % len(self._fixed_strings)) 93 self._string.append('{{{}}}'.format(self._fixed_strings))
91 self._inside_fixed = False 94 self._inside_fixed = False
92 else: 95 else:
93 self._string.append('</%s>' % tag) 96 self._string.append('</{}>'.format(tag))
94 97
95 def _append_text(self, s): 98 def _append_text(self, s):
96 if self._inside_fixed: 99 if self._inside_fixed:
97 self._fixed_strings[-1].append(s) 100 self._fixed_strings[-1].append(s)
98 else: 101 else:
99 self._string.append(s) 102 self._string.append(s)
100 103
101 def handle_data(self, data): 104 def handle_data(self, data):
102 # Note: lack of escaping here is intentional. The result is a locale str ing, 105 # Note: lack of escaping here is intentional. The result is a locale
103 # HTML escaping is applied when this string is inserted into the documen t. 106 # string, HTML escaping is applied when this string is inserted into
107 # the document.
104 self._append_text(data) 108 self._append_text(data)
105 109
106 def handle_entityref(self, name): 110 def handle_entityref(self, name):
107 self._append_text(self.unescape('&%s;' % name)) 111 self._append_text(self.unescape('&{};'.format(name)))
108 112
109 def handle_charref(self, name): 113 def handle_charref(self, name):
110 self._append_text(self.unescape('&#%s;' % name)) 114 self._append_text(self.unescape('&#{};'.format(name)))
111 115
112 116
113 class Converter: 117 class Converter:
114 whitelist = {'a', 'em', 'sup', 'strong', 'code', 'span'} 118 whitelist = {'a', 'em', 'sup', 'strong', 'code', 'span'}
115 missing_translations = 0 119 missing_translations = 0
116 total_translations = 0 120 total_translations = 0
117 121
118 def __init__(self, params, key='pagedata'): 122 def __init__(self, params, key='pagedata'):
119 self._params = params 123 self._params = params
120 self._key = key 124 self._key = key
121 self._attribute_parser = AttributeParser(self.whitelist) 125 self._attribute_parser = AttributeParser(self.whitelist)
122 self._seen_defaults = {} 126 self._seen_defaults = {}
123 127
124 # Read in any parameters specified at the beginning of the file 128 # Read in any parameters specified at the beginning of the file
125 data, filename = params[key] 129 data, filename = params[key]
126 lines = data.splitlines(True) 130 lines = data.splitlines(True)
127 for i, line in enumerate(lines): 131 for i, line in enumerate(lines):
128 if not re.search(r'^\s*[\w\-]+\s*=', line): 132 if not re.search(r'^\s*[\w\-]+\s*=', line):
129 break 133 break
130 name, value = line.split('=', 1) 134 name, value = line.split('=', 1)
131 params[name.strip()] = value.strip() 135 params[name.strip()] = value.strip()
132 lines[i] = '\n' 136 lines[i] = '\n'
133 params[key] = (''.join(lines), filename) 137 params[key] = (''.join(lines), filename)
134 138
135 def localize_string(self, page, name, default, comment, localedata, escapes) : 139 def localize_string(
140 self, page, name, default, comment, localedata, escapes):
141
136 def escape(s): 142 def escape(s):
137 return re.sub(r'.', 143 return re.sub(r'.',
138 lambda match: escapes.get(match.group(0), match.group( 0)), 144 lambda match: escapes.get(match.group(0),
145 match.group(0)),
139 s, flags=re.S) 146 s, flags=re.S)
140 147
141 def re_escape(s): 148 def re_escape(s):
142 return re.escape(escape(s)) 149 return re.escape(escape(s))
143 150
144 # Handle duplicated strings 151 # Handle duplicated strings
145 if default: 152 if default:
146 self._seen_defaults[(page, name)] = (default, comment) 153 self._seen_defaults[(page, name)] = (default, comment)
147 else: 154 else:
148 try: 155 try:
149 default, comment = self._seen_defaults[(page, name)] 156 default, comment = self._seen_defaults[(page, name)]
150 except KeyError: 157 except KeyError:
151 raise Exception('Text not yet defined for string %s on page %s' % 158 raise Exception('Text not yet defined for string {} on page'
152 (name, page)) 159 '{}'.format(name, page))
153 160
154 # Extract tag attributes from default string 161 # Extract tag attributes from default string
155 default, saved_attributes, fixed_strings = self._attribute_parser.parse( default, self._params['page']) 162 default, saved_attributes, fixed_strings = (
163 self._attribute_parser.parse(default, self._params['page']))
156 164
157 # Get translation 165 # Get translation
158 locale = self._params['locale'] 166 locale = self._params['locale']
159 if locale == self._params['defaultlocale']: 167 if locale == self._params['defaultlocale']:
160 result = default 168 result = default
161 elif name in localedata: 169 elif name in localedata:
162 result = localedata[name].strip() 170 result = localedata[name].strip()
163 else: 171 else:
164 result = default 172 result = default
165 self.missing_translations += 1 173 self.missing_translations += 1
166 self.total_translations += 1 174 self.total_translations += 1
167 175
168 # Perform callback with the string if required, e.g. for the translation s script 176 # Perform callback with the string if required, e.g. for the
177 # translations script
169 callback = self._params['localized_string_callback'] 178 callback = self._params['localized_string_callback']
170 if callback: 179 if callback:
171 callback(page, locale, name, result, comment, fixed_strings) 180 callback(page, locale, name, result, comment, fixed_strings)
172 181
173 # Insert fixed strings 182 # Insert fixed strings
174 for i, fixed_string in enumerate(fixed_strings, 1): 183 for i, fixed_string in enumerate(fixed_strings, 1):
175 result = result.replace('{%d}' % i, fixed_string) 184 result = result.replace('{{{%d}}}'.format(i), fixed_string)
176 185
177 # Insert attributes 186 # Insert attributes
178 result = escape(result) 187 result = escape(result)
179 188
180 def stringify_attribute((name, value)): 189 def stringify_attribute((name, value)):
181 return '%s="%s"' % ( 190 return '{}="{}"'.format(
182 escape(name), 191 escape(name),
183 escape(self.insert_localized_strings(value, {})) 192 escape(self.insert_localized_strings(value, {}))
184 ) 193 )
185 194
186 for tag in self.whitelist: 195 for tag in self.whitelist:
187 allowed_contents = '(?:[^<>]|%s)' % '|'.join(( 196 allowed_contents = '(?:[^<>]|{})'.format('|').join((
188 '<(?:%s[^<>]*?|/%s)>' % (t, t) 197 '<(?:{}[^<>]*?|/{})>'.format(t, t)
189 for t in map(re.escape, self.whitelist - {tag}) 198 for t in map(re.escape, self.whitelist - {tag})
190 )) 199 ))
191 saved = saved_attributes.get(tag, []) 200 saved = saved_attributes.get(tag, [])
192 for attrs in saved: 201 for attrs in saved:
193 attrs = map(stringify_attribute, attrs) 202 attrs = map(stringify_attribute, attrs)
194 result = re.sub( 203 result = re.sub(
195 r'%s(%s*?)%s' % (re_escape('<%s>' % tag), allowed_contents, 204 r'{}({}*?){}'.format(re_escape('<{}>'.format(tag)),
196 re_escape('</%s>' % tag)), 205 allowed_contents,
197 lambda match: r'<%s%s>%s</%s>' % ( 206 re_escape('</{}>'.format(tag))),
207 lambda match: r'<{}{}>{}</{}>'.format(
198 tag, 208 tag,
199 ' ' + ' '.join(attrs) if attrs else '', 209 ' ' + ' '.join(attrs) if attrs else '',
200 match.group(1), 210 match.group(1),
201 tag 211 tag
202 ), 212 ),
203 result, 1, flags=re.S 213 result, 1, flags=re.S
204 ) 214 )
205 result = re.sub( 215 result = re.sub(
206 r'%s(%s*?)%s' % (re_escape('<%s>' % tag), allowed_contents, 216 r'{}({}*?){}'.format(re_escape('<{}>'.format(tag)),
207 re_escape('</%s>' % tag)), 217 allowed_contents,
208 r'<%s>\1</%s>' % (tag, tag), 218 re_escape('</{}>'.format(tag))),
219 r'<{}>\1</{}>'.format(tag, tag),
209 result, flags=re.S 220 result, flags=re.S
210 ) 221 )
211 return result 222 return result
212 223
213 def insert_localized_strings(self, text, escapes, to_html=lambda s: s): 224 def insert_localized_strings(self, text, escapes, to_html=lambda s: s):
214 def lookup_string(match): 225 def lookup_string(match):
215 name, comment, default = match.groups() 226 name, comment, default = match.groups()
216 if default: 227 if default:
217 default = to_html(default).strip() 228 default = to_html(default).strip()
218 return self.localize_string(self._params['page'], name, default, 229 return self.localize_string(self._params['page'], name, default,
219 comment, self._params['localedata'], esc apes) 230 comment, self._params['localedata'],
231 escapes)
220 232
221 return re.sub( 233 return re.sub(
222 r'{{\s*' 234 r'{{\s*'
223 r'([\w\-]+)' # String ID 235 r'([\w\-]+)' # String ID
224 r'(?:(?:\[(.*?)\])?' # Optional comment 236 r'(?:(?:\[(.*?)\])?' # Optional comment
225 r'\s+' 237 r'\s+'
226 r'((?:(?!{{).|' # Translatable text 238 r'((?:(?!{{).|' # Translatable text
227 r'{{(?:(?!}}).)*}}' # Nested translation 239 r'{{(?:(?!}}).)*}}' # Nested translation
228 r')*?)' 240 r')*?)'
229 r')?' 241 r')?'
230 r'}}', 242 r'}}',
231 lookup_string, 243 lookup_string,
232 text, 244 text,
233 flags=re.S 245 flags=re.S
234 ) 246 )
235 247
236 def process_links(self, text): 248 def process_links(self, text):
237 def process_link(match): 249 def process_link(match):
238 pre, attr, url, post = match.groups() 250 pre, attr, url, post = match.groups()
239 url = jinja2.Markup(url).unescape() 251 url = jinja2.Markup(url).unescape()
240 252
241 locale, new_url = self._params['source'].resolve_link(url, self._par ams['locale']) 253 locale, new_url = (
242 if new_url != None: 254 self._params['source']
255 .resolve_link(url, self._params['locale']))
256
257 if new_url is not None:
243 url = new_url 258 url = new_url
244 if attr == 'href': 259 if attr == 'href':
245 post += ' hreflang="%s"' % jinja2.Markup.escape(locale) 260 post += ' hreflang="{}"'\
261 .format(jinja2.Markup.escape(locale))
246 262
247 return ''.join((pre, jinja2.Markup.escape(url), post)) 263 return ''.join((pre, jinja2.Markup.escape(url), post))
248 264
249 text = re.sub(r'(<a\s[^<>]*\b(href)=\")([^<>\"]+)(\")', process_link, te xt) 265 text = re.sub(r'(<a\s[^<>]*\b(href)=\")([^<>\"]+)(\")',
250 text = re.sub(r'(<img\s[^<>]*\b(src)=\")([^<>\"]+)(\")', process_link, t ext) 266 process_link, text)
267 text = re.sub(r'(<img\s[^<>]*\b(src)=\")([^<>\"]+)(\")',
268 process_link, text)
251 return text 269 return text
252 270
253 include_start_regex = '<' 271 include_start_regex = '<'
254 include_end_regex = '>' 272 include_end_regex = '>'
255 273
256 def resolve_includes(self, text): 274 def resolve_includes(self, text):
257 def resolve_include(match): 275 def resolve_include(match):
258 global converters
259 name = match.group(1) 276 name = match.group(1)
260 for format, converter_class in converters.iteritems(): 277 for format_, converter_class in converters.iteritems():
261 if self._params['source'].has_include(name, format): 278 if self._params['source'].has_include(name, format_):
262 self._params['includedata'] = self._params['source'].read_in clude(name, format) 279 self._params['includedata'] = (
263 converter = converter_class(self._params, key='includedata') 280 self._params['source'].read_include(name, format))
281
282 converter = converter_class(self._params, key='includedata'
Vasily Kuznetsov 2017/03/09 12:33:27 This looks kind of awkward with just the closing p
Jon Sonesen 2017/03/09 14:04:17 Yep, will do
283 )
264 result = converter() 284 result = converter()
265 self.missing_translations += converter.missing_translations 285 self.missing_translations += converter.missing_translations
266 self.total_translations += converter.total_translations 286 self.total_translations += converter.total_translations
267 return result 287 return result
268 raise Exception('Failed to resolve include %s on page %s' % (name, s elf._params['page'])) 288 raise Exception('Failed to resolve include {}'
289 'on page {}'.format(name, self._params['page']))
269 290
270 return re.sub( 291 return re.sub(
271 r'%s\?\s*include\s+([^\s<>"]+)\s*\?%s' % ( 292 r'{}\?\s*include\s+([^\s<>"]+)\s*\?{}'.format(
272 self.include_start_regex, 293 self.include_start_regex,
273 self.include_end_regex 294 self.include_end_regex
274 ), 295 ),
275 resolve_include, 296 resolve_include,
276 text 297 text
277 ) 298 )
278 299
279 def __call__(self): 300 def __call__(self):
280 result = self.get_html(*self._params[self._key]) 301 result = self.get_html(*self._params[self._key])
281 result = self.resolve_includes(result) 302 result = self.resolve_includes(result)
282 if self._key == 'pagedata': 303 if self._key == 'pagedata':
283 head = [] 304 head = []
284 305
285 def add_to_head(match): 306 def add_to_head(match):
286 head.append(match.group(1)) 307 head.append(match.group(1))
287 return '' 308 return ''
288 body = re.sub(r'<head>(.*?)</head>', add_to_head, result, flags=re.S ) 309 body = re.sub(r'<head>(.*?)</head>', add_to_head, result,
310 flags=re.S)
289 return ''.join(head), body 311 return ''.join(head), body
290 else: 312 return result
291 return result
292 313
293 314
294 class RawConverter(Converter): 315 class RawConverter(Converter):
295 def get_html(self, source, filename): 316 def get_html(self, source, filename):
296 result = self.insert_localized_strings(source, html_escapes) 317 result = self.insert_localized_strings(source, html_escapes)
297 result = self.process_links(result) 318 result = self.process_links(result)
298 return result 319 return result
299 320
300 321
301 class MarkdownConverter(Converter): 322 class MarkdownConverter(Converter):
302 include_start_regex = r'(?:%s|%s)' % ( 323 include_start_regex = r'(?:{}|{})'.format(
303 Converter.include_start_regex, 324 Converter.include_start_regex,
304 re.escape(jinja2.escape(Converter.include_start_regex)) 325 re.escape(jinja2.escape(Converter.include_start_regex))
305 ) 326 )
306 include_end_regex = r'(?:%s|%s)' % ( 327 include_end_regex = r'(?:{}|{})'.format(
307 Converter.include_end_regex, 328 Converter.include_end_regex,
308 re.escape(jinja2.escape(Converter.include_end_regex)) 329 re.escape(jinja2.escape(Converter.include_end_regex))
309 ) 330 )
310 331
311 def get_html(self, source, filename): 332 def get_html(self, source, filename):
312 def remove_unnecessary_entities(match): 333 def remove_unnecessary_entities(match):
313 char = unichr(int(match.group(1))) 334 char = unichr(int(match.group(1)))
314 if char in html_escapes: 335 if char in html_escapes:
315 return match.group(0) 336 return match.group(0)
316 else: 337 return char
317 return char
318 338
319 escapes = {} 339 escapes = {}
320 md = markdown.Markdown(output='html5', extensions=['extra']) 340 md = markdown.Markdown(output='html5', extensions=['extra'])
321 for char in md.ESCAPED_CHARS: 341 for char in md.ESCAPED_CHARS:
322 escapes[char] = '&#' + str(ord(char)) + ';' 342 escapes[char] = '&#{};'.format(str(ord(char)))
323 for key, value in html_escapes.iteritems(): 343 for key, value in html_escapes.iteritems():
324 escapes[key] = value 344 escapes[key] = value
325 345
326 md.preprocessors['html_block'].markdown_in_raw = True 346 md.preprocessors['html_block'].markdown_in_raw = True
327 347
328 def to_html(s): 348 def to_html(s):
329 return re.sub(r'</?p>', '', md.convert(s)) 349 return re.sub(r'</?p>', '', md.convert(s))
330 350
331 result = self.insert_localized_strings(source, escapes, to_html) 351 result = self.insert_localized_strings(source, escapes, to_html)
332 result = md.convert(result) 352 result = md.convert(result)
(...skipping 22 matching lines...) Expand all
355 'translate': self.translate, 375 'translate': self.translate,
356 'linkify': self.linkify, 376 'linkify': self.linkify,
357 'toclist': self.toclist, 377 'toclist': self.toclist,
358 } 378 }
359 379
360 globals = { 380 globals = {
361 'get_string': self.get_string, 381 'get_string': self.get_string,
362 'get_page_content': self.get_page_content, 382 'get_page_content': self.get_page_content,
363 } 383 }
364 384
365 for dirname, dictionary in [('filters', filters), ('globals', globals)]: 385 for dirname, dictionary in [('filters', filters),
386 ('globals', globals)]:
366 for filename in self._params['source'].list_files(dirname): 387 for filename in self._params['source'].list_files(dirname):
367 root, ext = os.path.splitext(filename) 388 root, ext = os.path.splitext(filename)
368 if ext.lower() != '.py': 389 if ext.lower() != '.py':
369 continue 390 continue
370 391
371 path = '%s/%s' % (dirname, filename) 392 path = os.path.join(dirname, filename)
372 namespace = self._params['source'].exec_file(path) 393 namespace = self._params['source'].exec_file(path)
373 394
374 name = os.path.basename(root) 395 name = os.path.basename(root)
375 try: 396 try:
376 dictionary[name] = namespace[name] 397 dictionary[name] = namespace[name]
377 except KeyError: 398 except KeyError:
378 raise Exception('Expected symbol %r not found in %r' % (name , path)) 399 raise Exception('Expected symbol {} not found'
400 'in {}'.format(name, path))
379 401
380 self._env = jinja2.Environment(loader=SourceTemplateLoader(self._params[ 'source']), autoescape=True) 402 self._env = jinja2.Environment(
403 loader=SourceTemplateLoader(self._params['source']),
404 autoescape=True)
381 self._env.filters.update(filters) 405 self._env.filters.update(filters)
382 self._env.globals.update(globals) 406 self._env.globals.update(globals)
383 407
384 def get_html(self, source, filename): 408 def get_html(self, source, filename):
385 env = self._env 409 env = self._env
386 code = env.compile(source, None, filename) 410 code = env.compile(source, None, filename)
387 template = jinja2.Template.from_code(env, code, env.globals) 411 template = jinja2.Template.from_code(env, code, env.globals)
388 412
389 try: 413 try:
390 module = template.make_module(self._params) 414 module = template.make_module(self._params)
(...skipping 11 matching lines...) Expand all
402 def translate(self, default, name, comment=None): 426 def translate(self, default, name, comment=None):
403 return jinja2.Markup(self.localize_string( 427 return jinja2.Markup(self.localize_string(
404 self._params['page'], name, default, comment, 428 self._params['page'], name, default, comment,
405 self._params['localedata'], html_escapes 429 self._params['localedata'], html_escapes
406 )) 430 ))
407 431
408 def get_string(self, name, page=None): 432 def get_string(self, name, page=None):
409 if page is None: 433 if page is None:
410 page = self._params['page'] 434 page = self._params['page']
411 435
412 localedata = self._params['source'].read_locale(self._params['locale'], page) 436 localedata = self._params['source'].read_locale(self._params['locale'],
437 page)
413 default = localedata[name] 438 default = localedata[name]
414 return jinja2.Markup(self.localize_string( 439 return jinja2.Markup(self.localize_string(
415 page, name, default, '', localedata, html_escapes 440 page, name, default, '', localedata, html_escapes
416 )) 441 ))
417 442
418 def get_page_content(self, page, locale=None): 443 def get_page_content(self, page, locale=None):
419 from cms.utils import get_page_params 444 from cms.utils import get_page_params
420 445
421 if locale is None: 446 if locale is None:
422 locale = self._params['locale'] 447 locale = self._params['locale']
423 return get_page_params(self._params['source'], locale, page) 448 return get_page_params(self._params['source'], locale, page)
424 449
425 def linkify(self, page, locale=None, **attrs): 450 def linkify(self, page, locale=None, **attrs):
426 if locale is None: 451 if locale is None:
427 locale = self._params['locale'] 452 locale = self._params['locale']
428 453
429 locale, url = self._params['source'].resolve_link(page, locale) 454 locale, url = self._params['source'].resolve_link(page, locale)
430 return jinja2.Markup('<a%s>' % ''.join( 455 return jinja2.Markup('<a{}>'.format(''.join(
431 ' %s="%s"' % (name, jinja2.escape(value)) for name, value in [ 456 ' {}="{}"'.format(name, jinja2.escape(value)) for name, value in [
432 ('href', url), 457 ('href', url),
433 ('hreflang', locale) 458 ('hreflang', locale)
434 ] + attrs.items() 459 ] + attrs.items()
435 )) 460 )))
436 461
437 def toclist(self, content): 462 def toclist(self, content):
463 toc_re = r'<h(\d)\s[^<>]*\bid="([^<>"]+)"[^<>]*>(.*?)</h\1>'
438 flat = [] 464 flat = []
439 for match in re.finditer(r'<h(\d)\s[^<>]*\bid="([^<>"]+)"[^<>]*>(.*?)</h \1>', content, re.S): 465 for match in re.finditer(toc_re, content, re.S):
440 flat.append({ 466 flat.append({
441 'level': int(match.group(1)), 467 'level': int(match.group(1)),
442 'anchor': jinja2.Markup(match.group(2)).unescape(), 468 'anchor': jinja2.Markup(match.group(2)).unescape(),
443 'title': jinja2.Markup(match.group(3)).unescape(), 469 'title': jinja2.Markup(match.group(3)).unescape(),
444 'subitems': [], 470 'subitems': [],
445 }) 471 })
446 472
447 structured = [] 473 structured = []
448 stack = [{'level': 0, 'subitems': structured}] 474 stack = [{'level': 0, 'subitems': structured}]
449 for item in flat: 475 for item in flat:
450 while stack[-1]['level'] >= item['level']: 476 while stack[-1]['level'] >= item['level']:
451 stack.pop() 477 stack.pop()
452 stack[-1]['subitems'].append(item) 478 stack[-1]['subitems'].append(item)
453 stack.append(item) 479 stack.append(item)
454 return structured 480 return structured
455 481
456 converters = { 482 converters = {
457 'html': RawConverter, 483 'html': RawConverter,
458 'md': MarkdownConverter, 484 'md': MarkdownConverter,
459 'tmpl': TemplateConverter, 485 'tmpl': TemplateConverter,
460 } 486 }
OLDNEW
« no previous file with comments | « no previous file | tox.ini » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld