comparison src/plugins/plugin_misc_text_syntaxes.py @ 2324:fe922e6fabd4

plugin text syntaxes: various improvments: - syntaxes and default_syntax are now class attribute instead of members of params_data - removed local exception UnknownSyntax in favor of exceptions.NotFound - syntaxes are now saved with a key which is name in lower case and stripped, unmodified name is put in syntax's data - new getSyntax (syntaxGet in bridge) method to get syntax key and raise an error if not found. Can be used by frontends to check that a syntax is known.
author Goffi <goffi@goffi.org>
date Thu, 13 Jul 2017 20:53:51 +0200
parents 33c8c4973743
children 8b37a62336c3
comparison
equal deleted inserted replaced
2323:2dae79990122 2324:fe922e6fabd4
61 C.PI_MAIN: "TextSyntaxes", 61 C.PI_MAIN: "TextSyntaxes",
62 C.PI_HANDLER: "no", 62 C.PI_HANDLER: "no",
63 C.PI_DESCRIPTION: _("""Management of various text syntaxes (XHTML-IM, Markdown, etc)""") 63 C.PI_DESCRIPTION: _("""Management of various text syntaxes (XHTML-IM, Markdown, etc)""")
64 } 64 }
65 65
66 class UnknownSyntax(Exception):
67 pass
68 66
69 class TextSyntaxes(object): 67 class TextSyntaxes(object):
70 """ Text conversion class 68 """ Text conversion class
71 XHTML utf-8 is used as intermediate language for conversions 69 XHTML utf-8 is used as intermediate language for conversions
72 """ 70 """
75 OPT_HIDDEN = "HIDDEN" 73 OPT_HIDDEN = "HIDDEN"
76 OPT_NO_THREAD = "NO_THREAD" 74 OPT_NO_THREAD = "NO_THREAD"
77 SYNTAX_XHTML = _SYNTAX_XHTML 75 SYNTAX_XHTML = _SYNTAX_XHTML
78 SYNTAX_MARKDOWN = "markdown" 76 SYNTAX_MARKDOWN = "markdown"
79 SYNTAX_TEXT = "text" 77 SYNTAX_TEXT = "text"
78 syntaxes = {}
79 default_syntax = SYNTAX_XHTML
80 80
81 params = """ 81 params = """
82 <params> 82 <params>
83 <individual> 83 <individual>
84 <category name="%(category_name)s" label="%(category_label)s"> 84 <category name="%(category_name)s" label="%(category_label)s">
93 params_data = { 93 params_data = {
94 'category_name': CATEGORY, 94 'category_name': CATEGORY,
95 'category_label': _(CATEGORY), 95 'category_label': _(CATEGORY),
96 'name': NAME, 96 'name': NAME,
97 'label': _(NAME), 97 'label': _(NAME),
98 'syntaxes': {}, 98 'syntaxes': syntaxes,
99 } 99 }
100 100
101 def __init__(self, host): 101 def __init__(self, host):
102 log.info(_("Text syntaxes plugin initialization")) 102 log.info(_("Text syntaxes plugin initialization"))
103 self.host = host 103 self.host = host
104 self.syntaxes = {}
105 self.addSyntax(self.SYNTAX_XHTML, lambda xhtml: defer.succeed(xhtml), lambda xhtml: defer.succeed(xhtml), 104 self.addSyntax(self.SYNTAX_XHTML, lambda xhtml: defer.succeed(xhtml), lambda xhtml: defer.succeed(xhtml),
106 TextSyntaxes.OPT_NO_THREAD) 105 TextSyntaxes.OPT_NO_THREAD)
107 # TODO: text => XHTML should add <a/> to url like in frontends 106 # TODO: text => XHTML should add <a/> to url like in frontends
108 # it's probably best to move sat_frontends.tools.strings to sat.tools.common or similar 107 # it's probably best to move sat_frontends.tools.strings to sat.tools.common or similar
109 self.addSyntax(self.SYNTAX_TEXT, lambda text: escape(text), lambda xhtml: self._removeMarkups(xhtml), [TextSyntaxes.OPT_HIDDEN]) 108 self.addSyntax(self.SYNTAX_TEXT, lambda text: escape(text), lambda xhtml: self._removeMarkups(xhtml), [TextSyntaxes.OPT_HIDDEN])
118 except ImportError: 117 except ImportError:
119 log.warning(u"markdown or html2text not found, can't use Markdown syntax") 118 log.warning(u"markdown or html2text not found, can't use Markdown syntax")
120 log.info(u"You can download/install them from https://pythonhosted.org/Markdown/ and https://github.com/Alir3z4/html2text/") 119 log.info(u"You can download/install them from https://pythonhosted.org/Markdown/ and https://github.com/Alir3z4/html2text/")
121 host.bridge.addMethod("syntaxConvert", ".plugin", in_sign='sssbs', out_sign='s', 120 host.bridge.addMethod("syntaxConvert", ".plugin", in_sign='sssbs', out_sign='s',
122 async=True, method=self.convert) 121 async=True, method=self.convert)
122 host.bridge.addMethod("syntaxGet", ".plugin", in_sign='s', out_sign='s',
123 method=self.getSyntax)
123 124
124 def _updateParamOptions(self): 125 def _updateParamOptions(self):
125 data_synt = TextSyntaxes.params_data['syntaxes'] 126 data_synt = TextSyntaxes.syntaxes
126 default_synt = TextSyntaxes.params_data.get('default', _SYNTAX_XHTML) 127 default_synt = TextSyntaxes.default_syntax
127 syntaxes = [] 128 syntaxes = []
128 129
129 for syntax in data_synt.keys(): 130 for syntax in data_synt.keys():
130 flags = data_synt[syntax]["flags"] 131 flags = data_synt[syntax]["flags"]
131 if TextSyntaxes.OPT_HIDDEN not in flags: 132 if TextSyntaxes.OPT_HIDDEN not in flags:
214 # when dealing with XHTML 215 # when dealing with XHTML
215 # TODO: a way for parser to return parsing errors/warnings 216 # TODO: a way for parser to return parsing errors/warnings
216 217
217 if syntax_from == _SYNTAX_CURRENT: 218 if syntax_from == _SYNTAX_CURRENT:
218 syntax_from = self.getCurrentSyntax(profile) 219 syntax_from = self.getCurrentSyntax(profile)
220 else:
221 syntax_from = syntax_from.lower().strip()
219 if syntax_to == _SYNTAX_CURRENT: 222 if syntax_to == _SYNTAX_CURRENT:
220 syntax_to = self.getCurrentSyntax(profile) 223 syntax_to = self.getCurrentSyntax(profile)
221 syntaxes = TextSyntaxes.params_data['syntaxes'] 224 else:
225 syntax_to = syntax_to.lower().strip()
226 syntaxes = TextSyntaxes.syntaxes
222 if syntax_from not in syntaxes: 227 if syntax_from not in syntaxes:
223 raise UnknownSyntax(syntax_from) 228 raise exceptions.NotFound(syntax_from)
224 if syntax_to not in syntaxes: 229 if syntax_to not in syntaxes:
225 raise UnknownSyntax(syntax_to) 230 raise exceptions.NotFound(syntax_to)
226 d = None 231 d = None
227 232
228 if TextSyntaxes.OPT_NO_THREAD in syntaxes[syntax_from]["flags"]: 233 if TextSyntaxes.OPT_NO_THREAD in syntaxes[syntax_from]["flags"]:
229 d = defer.maybeDeferred(syntaxes[syntax_from]["to"], text) 234 d = defer.maybeDeferred(syntaxes[syntax_from]["to"], text)
230 else: 235 else:
257 """ 262 """
258 flags = flags if flags is not None else [] 263 flags = flags if flags is not None else []
259 if TextSyntaxes.OPT_HIDDEN in flags and TextSyntaxes.OPT_DEFAULT in flags: 264 if TextSyntaxes.OPT_HIDDEN in flags and TextSyntaxes.OPT_DEFAULT in flags:
260 raise ValueError(u"{} and {} are mutually exclusive".format(TextSyntaxes.OPT_HIDDEN, TextSyntaxes.OPT_DEFAULT)) 265 raise ValueError(u"{} and {} are mutually exclusive".format(TextSyntaxes.OPT_HIDDEN, TextSyntaxes.OPT_DEFAULT))
261 266
262 syntaxes = TextSyntaxes.params_data['syntaxes'] 267 syntaxes = TextSyntaxes.syntaxes
263 if name in syntaxes: 268 key = name.lower().strip()
264 raise exceptions.ConflictError(u"This syntax name already exists: {}".format(name)) 269 if key in syntaxes:
265 syntaxes[name] = {"to": to_xhtml_cb, "from": from_xhtml_cb, "flags": flags} 270 raise exceptions.ConflictError(u"This syntax key already exists: {}".format(key))
271 syntaxes[key] = {"name": name, "to": to_xhtml_cb, "from": from_xhtml_cb, "flags": flags}
266 if TextSyntaxes.OPT_DEFAULT in flags: 272 if TextSyntaxes.OPT_DEFAULT in flags:
267 TextSyntaxes.params_data['default'] = name 273 TextSyntaxes.default_syntaxe = key
268 274
269 self._updateParamOptions() 275 self._updateParamOptions()
270 276
277 def getSyntax(self, name):
278 """get syntax key corresponding to a name
279
280 @raise exceptions.NotFound: syntax doesn't exist
281 """
282 key = name.lower().strip()
283 if key in self.syntaxes:
284 return key
285 raise exceptions.NotFound
286
271 def _removeMarkups(self, xhtml): 287 def _removeMarkups(self, xhtml):
272 """ 288 """Remove XHTML markups from the given string.
273 Remove XHTML markups from the given string. 289
274 @param xhtml: the XHTML string to be cleaned 290 @param xhtml: the XHTML string to be cleaned
275 @return: the cleaned string 291 @return: the cleaned string
276 """ 292 """
277 cleaner = clean.Cleaner(kill_tags=['style']) 293 cleaner = clean.Cleaner(kill_tags=['style'])
278 cleaned = cleaner.clean_html(html.fromstring(xhtml)) 294 cleaned = cleaner.clean_html(html.fromstring(xhtml))