comparison sat/plugins/plugin_misc_text_syntaxes.py @ 3028:ab2696e34d29

Python 3 port: /!\ this is a huge commit /!\ starting from this commit, SàT is needs Python 3.6+ /!\ SàT maybe be instable or some feature may not work anymore, this will improve with time This patch port backend, bridge and frontends to Python 3. Roughly this has been done this way: - 2to3 tools has been applied (with python 3.7) - all references to python2 have been replaced with python3 (notably shebangs) - fixed files not handled by 2to3 (notably the shell script) - several manual fixes - fixed issues reported by Python 3 that where not handled in Python 2 - replaced "async" with "async_" when needed (it's a reserved word from Python 3.7) - replaced zope's "implements" with @implementer decorator - temporary hack to handle data pickled in database, as str or bytes may be returned, to be checked later - fixed hash comparison for password - removed some code which is not needed anymore with Python 3 - deactivated some code which needs to be checked (notably certificate validation) - tested with jp, fixed reported issues until some basic commands worked - ported Primitivus (after porting dependencies like urwid satext) - more manual fixes
author Goffi <goffi@goffi.org>
date Tue, 13 Aug 2019 19:08:41 +0200
parents a02ad4bc0a6d
children fee60f17ebac
comparison
equal deleted inserted replaced
3027:ff5bcb12ae60 3028:ab2696e34d29
1 #!/usr/bin/env python2 1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 3
4 # SAT plugin for managing various text syntaxes 4 # SAT plugin for managing various text syntaxes
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org)
6 6
33 from lxml import html 33 from lxml import html
34 from lxml.html import clean 34 from lxml.html import clean
35 from lxml import etree 35 from lxml import etree
36 except ImportError: 36 except ImportError:
37 raise exceptions.MissingModule( 37 raise exceptions.MissingModule(
38 u"Missing module lxml, please download/install it from http://lxml.de/" 38 "Missing module lxml, please download/install it from http://lxml.de/"
39 ) 39 )
40 40
41 log = getLogger(__name__) 41 log = getLogger(__name__)
42 42
43 CATEGORY = D_("Composition") 43 CATEGORY = D_("Composition")
216 self.addSyntax( 216 self.addSyntax(
217 self.SYNTAX_MARKDOWN, 217 self.SYNTAX_MARKDOWN,
218 partial(markdown.markdown, 218 partial(markdown.markdown,
219 extensions=[ 219 extensions=[
220 EscapeHTML(), 220 EscapeHTML(),
221 u'nl2br', 221 'nl2br',
222 u'codehilite', 222 'codehilite',
223 u'fenced_code', 223 'fenced_code',
224 u'sane_lists', 224 'sane_lists',
225 u'tables', 225 'tables',
226 ], 226 ],
227 extension_configs = { 227 extension_configs = {
228 u"codehilite": { 228 "codehilite": {
229 u"css_class": "highlight", 229 "css_class": "highlight",
230 } 230 }
231 }), 231 }),
232 _html2text, 232 _html2text,
233 [TextSyntaxes.OPT_DEFAULT], 233 [TextSyntaxes.OPT_DEFAULT],
234 ) 234 )
235 except ImportError: 235 except ImportError:
236 log.warning(u"markdown or html2text not found, can't use Markdown syntax") 236 log.warning("markdown or html2text not found, can't use Markdown syntax")
237 log.info( 237 log.info(
238 u"You can download/install them from https://pythonhosted.org/Markdown/ and https://github.com/Alir3z4/html2text/" 238 "You can download/install them from https://pythonhosted.org/Markdown/ and https://github.com/Alir3z4/html2text/"
239 ) 239 )
240 host.bridge.addMethod( 240 host.bridge.addMethod(
241 "syntaxConvert", 241 "syntaxConvert",
242 ".plugin", 242 ".plugin",
243 in_sign="sssbs", 243 in_sign="sssbs",
244 out_sign="s", 244 out_sign="s",
245 async=True, 245 async_=True,
246 method=self.convert, 246 method=self.convert,
247 ) 247 )
248 host.bridge.addMethod( 248 host.bridge.addMethod(
249 "syntaxGet", ".plugin", in_sign="s", out_sign="s", method=self.getSyntax 249 "syntaxGet", ".plugin", in_sign="s", out_sign="s", method=self.getSyntax
250 ) 250 )
251 if xml_tools.cleanXHTML is None: 251 if xml_tools.cleanXHTML is None:
252 log.debug(u"Installing cleaning method") 252 log.debug("Installing cleaning method")
253 xml_tools.cleanXHTML = self.cleanXHTML 253 xml_tools.cleanXHTML = self.cleanXHTML
254 254
255 def _updateParamOptions(self): 255 def _updateParamOptions(self):
256 data_synt = TextSyntaxes.syntaxes 256 data_synt = TextSyntaxes.syntaxes
257 default_synt = TextSyntaxes.default_syntax 257 default_synt = TextSyntaxes.default_syntax
258 syntaxes = [] 258 syntaxes = []
259 259
260 for syntax in data_synt.keys(): 260 for syntax in list(data_synt.keys()):
261 flags = data_synt[syntax]["flags"] 261 flags = data_synt[syntax]["flags"]
262 if TextSyntaxes.OPT_HIDDEN not in flags: 262 if TextSyntaxes.OPT_HIDDEN not in flags:
263 syntaxes.append(syntax) 263 syntaxes.append(syntax)
264 264
265 syntaxes.sort(key=lambda synt: synt.lower()) 265 syntaxes.sort(key=lambda synt: synt.lower())
266 options = [] 266 options = []
267 267
268 for syntax in syntaxes: 268 for syntax in syntaxes:
269 selected = 'selected="true"' if syntax == default_synt else "" 269 selected = 'selected="true"' if syntax == default_synt else ""
270 options.append(u'<option value="%s" %s/>' % (syntax, selected)) 270 options.append('<option value="%s" %s/>' % (syntax, selected))
271 271
272 TextSyntaxes.params_data["options"] = u"\n".join(options) 272 TextSyntaxes.params_data["options"] = "\n".join(options)
273 self.host.memory.updateParams(TextSyntaxes.params % TextSyntaxes.params_data) 273 self.host.memory.updateParams(TextSyntaxes.params % TextSyntaxes.params_data)
274 274
275 def getCurrentSyntax(self, profile): 275 def getCurrentSyntax(self, profile):
276 """ Return the selected syntax for the given profile 276 """ Return the selected syntax for the given profile
277 277
278 @param profile: %(doc_profile)s 278 @param profile: %(doc_profile)s
279 @return: profile selected syntax 279 @return: profile selected syntax
280 """ 280 """
281 return self.host.memory.getParamA(NAME, CATEGORY, profile_key=profile) 281 return self.host.memory.getParamA(NAME, CATEGORY, profile_key=profile)
282 282
283 def _logError(self, failure, action=u"converting syntax"): 283 def _logError(self, failure, action="converting syntax"):
284 log.error( 284 log.error(
285 u"Error while {action}: {failure}".format(action=action, failure=failure) 285 "Error while {action}: {failure}".format(action=action, failure=failure)
286 ) 286 )
287 return failure 287 return failure
288 288
289 def cleanStyle(self, styles): 289 def cleanStyle(self, styles):
290 """"Clean unsafe CSS styles 290 """"Clean unsafe CSS styles
318 318
319 @param xhtml(unicode, lxml.etree._Element): raw HTML/XHTML text to clean 319 @param xhtml(unicode, lxml.etree._Element): raw HTML/XHTML text to clean
320 @return (unicode): cleaned XHTML 320 @return (unicode): cleaned XHTML
321 """ 321 """
322 322
323 if isinstance(xhtml, basestring): 323 if isinstance(xhtml, str):
324 try: 324 try:
325 xhtml_elt = html.fromstring(xhtml) 325 xhtml_elt = html.fromstring(xhtml)
326 except etree.ParserError as e: 326 except etree.ParserError as e:
327 if not xhtml.strip(): 327 if not xhtml.strip():
328 return u"" 328 return ""
329 log.error(u"Can't clean XHTML: {xhtml}".format(xhtml=xhtml)) 329 log.error("Can't clean XHTML: {xhtml}".format(xhtml=xhtml))
330 raise e 330 raise e
331 elif isinstance(xhtml, html.HtmlElement): 331 elif isinstance(xhtml, html.HtmlElement):
332 xhtml_elt = xhtml 332 xhtml_elt = xhtml
333 else: 333 else:
334 log.error("Only strings and HtmlElements can be cleaned") 334 log.error("Only strings and HtmlElements can be cleaned")
343 for element in xhtml_elt.iter(tag=etree.Element): 343 for element in xhtml_elt.iter(tag=etree.Element):
344 if not element.text: 344 if not element.text:
345 if element.tag in VOID_ELEMENTS: 345 if element.tag in VOID_ELEMENTS:
346 element.text = None 346 element.text = None
347 else: 347 else:
348 element.text = u'' 348 element.text = ''
349 return html.tostring(xhtml_elt, encoding=unicode, method="xml") 349 return html.tostring(xhtml_elt, encoding=str, method="xml")
350 350
351 def convert( 351 def convert(
352 self, text, syntax_from, syntax_to=_SYNTAX_XHTML, safe=True, profile=None 352 self, text, syntax_from, syntax_to=_SYNTAX_XHTML, safe=True, profile=None
353 ): 353 ):
354 """Convert a text between two syntaxes 354 """Convert a text between two syntaxes
411 TextSyntaxes.OPT_NO_THREAD: do not defer to thread when converting (the callback may then return a deferred) 411 TextSyntaxes.OPT_NO_THREAD: do not defer to thread when converting (the callback may then return a deferred)
412 """ 412 """
413 flags = flags if flags is not None else [] 413 flags = flags if flags is not None else []
414 if TextSyntaxes.OPT_HIDDEN in flags and TextSyntaxes.OPT_DEFAULT in flags: 414 if TextSyntaxes.OPT_HIDDEN in flags and TextSyntaxes.OPT_DEFAULT in flags:
415 raise ValueError( 415 raise ValueError(
416 u"{} and {} are mutually exclusive".format( 416 "{} and {} are mutually exclusive".format(
417 TextSyntaxes.OPT_HIDDEN, TextSyntaxes.OPT_DEFAULT 417 TextSyntaxes.OPT_HIDDEN, TextSyntaxes.OPT_DEFAULT
418 ) 418 )
419 ) 419 )
420 420
421 syntaxes = TextSyntaxes.syntaxes 421 syntaxes = TextSyntaxes.syntaxes
422 key = name.lower().strip() 422 key = name.lower().strip()
423 if key in syntaxes: 423 if key in syntaxes:
424 raise exceptions.ConflictError( 424 raise exceptions.ConflictError(
425 u"This syntax key already exists: {}".format(key) 425 "This syntax key already exists: {}".format(key)
426 ) 426 )
427 syntaxes[key] = { 427 syntaxes[key] = {
428 "name": name, 428 "name": name,
429 "to": to_xhtml_cb, 429 "to": to_xhtml_cb,
430 "from": from_xhtml_cb, 430 "from": from_xhtml_cb,
451 @param xhtml: the XHTML string to be cleaned 451 @param xhtml: the XHTML string to be cleaned
452 @return: the cleaned string 452 @return: the cleaned string
453 """ 453 """
454 cleaner = clean.Cleaner(kill_tags=["style"]) 454 cleaner = clean.Cleaner(kill_tags=["style"])
455 cleaned = cleaner.clean_html(html.fromstring(xhtml)) 455 cleaned = cleaner.clean_html(html.fromstring(xhtml))
456 return html.tostring(cleaned, encoding=unicode, method="text") 456 return html.tostring(cleaned, encoding=str, method="text")