comparison src/tools/xml_tools.py @ 1444:8ce9924fa92c

tools (xml_tools): better PEP-8 compliance
author souliane <souliane@mailoo.org>
date Tue, 21 Jul 2015 15:36:40 +0200
parents 3265a2639182
children a13be5c22334
comparison
equal deleted inserted replaced
1443:32d1089df687 1444:8ce9924fa92c
19 19
20 from sat.core.i18n import _ 20 from sat.core.i18n import _
21 from sat.core.constants import Const as C 21 from sat.core.constants import Const as C
22 from sat.core.log import getLogger 22 from sat.core.log import getLogger
23 log = getLogger(__name__) 23 log = getLogger(__name__)
24
24 from xml.dom import minidom, NotFoundErr 25 from xml.dom import minidom, NotFoundErr
25 from wokkel import data_form 26 from wokkel import data_form
26 from twisted.words.xish import domish 27 from twisted.words.xish import domish
27 from sat.core import exceptions 28 from sat.core import exceptions
28 29
29 30
30 """This library help manage XML used in SàT (parameters, registration, etc) """ 31 """This library help manage XML used in SàT (parameters, registration, etc)"""
31 32
32 SAT_FORM_PREFIX = "SAT_FORM_" 33 SAT_FORM_PREFIX = "SAT_FORM_"
33 SAT_PARAM_SEPARATOR = "_XMLUI_PARAM_" # used to have unique elements names 34 SAT_PARAM_SEPARATOR = "_XMLUI_PARAM_" # used to have unique elements names
34 35
35 36
36 # Helper functions 37 # Helper functions
37 38
38 def _dataFormField2XMLUIData(field, read_only=False): 39 def _dataFormField2XMLUIData(field, read_only=False):
39 """ Get data needed to create an XMLUI's Widget from Wokkel's data_form's Field 40 """Get data needed to create an XMLUI's Widget from Wokkel's data_form's Field.
40 field can be modified (if it's fixed and it has no value) 41
41 @param field: data_form.Field (it uses field.value, field.fieldType, field.label and field.var) 42 The attribute field can be modified (if it's fixed and it has no value).
42 @param read_only: if True and it make sens, create a read only input widget 43
43 @return: widget_type, widget_args, widget_kwargs 44 @param field (data_form.Field): a field with attributes "value", "fieldType", "label" and "var"
44 45 @param read_only (bool): if True and it makes sense, create a read only input widget
46 @return: a tuple (widget_type, widget_args, widget_kwargs)
45 """ 47 """
46 widget_args = [field.value] 48 widget_args = [field.value]
47 widget_kwargs = {} 49 widget_kwargs = {}
48 if field.fieldType == 'fixed' or field.fieldType is None: 50 if field.fieldType == 'fixed' or field.fieldType is None:
49 widget_type = 'text' 51 widget_type = 'text'
86 if field.var: 88 if field.var:
87 widget_kwargs["name"] = field.var 89 widget_kwargs["name"] = field.var
88 90
89 return widget_type, widget_args, widget_kwargs 91 return widget_type, widget_args, widget_kwargs
90 92
93
91 def dataForm2Widgets(form_ui, form, read_only=False): 94 def dataForm2Widgets(form_ui, form, read_only=False):
92 """Complete an existing XMLUI with widget converted frot XEP-0004 data forms 95 """Complete an existing XMLUI with widget converted from XEP-0004 data forms.
93 96
94 @param form_ui: XMLUI instance 97 @param form_ui (XMLUI): XMLUI instance
95 @param form: Wokkel's implementation of data form 98 @param form (data_form.Form): Wokkel's implementation of data form
96 @return: completed xml_ui 99 @param read_only (bool): if True and it makes sense, create a read only input widget
100 @return: the completed XMLUI instance
97 """ 101 """
98 if form.instructions: 102 if form.instructions:
99 form_ui.addText('\n'.join(form.instructions), 'instructions') 103 form_ui.addText('\n'.join(form.instructions), 'instructions')
100 104
101 form_ui.changeContainer("pairs") 105 form_ui.changeContainer("pairs")
110 114
111 form_ui.addWidget(widget_type, *widget_args, **widget_kwargs) 115 form_ui.addWidget(widget_type, *widget_args, **widget_kwargs)
112 116
113 return form_ui 117 return form_ui
114 118
119
115 def dataForm2XMLUI(form, submit_id, session_id=None, read_only=False): 120 def dataForm2XMLUI(form, submit_id, session_id=None, read_only=False):
116 """Take a data form (xep-0004, Wokkel's implementation) and convert it to a SàT XML 121 """Take a data form (XEP-0004, Wokkel's implementation) and convert it to a SàT XMLUI.
117 122
118 @param submit_id: callback id to call when submitting form 123 @param form (data_form.Form): a Form instance
119 @param session_id: id to return with the data 124 @param submit_id (unicode): callback id to call when submitting form
125 @param session_id (unicode): session id to return with the data
126 @param read_only (bool): if True and it makes sense, create a read only input widget
127 @return: XMLUI instance
120 """ 128 """
121 form_ui = XMLUI("form", "vertical", submit_id=submit_id, session_id=session_id) 129 form_ui = XMLUI("form", "vertical", submit_id=submit_id, session_id=session_id)
122 return dataForm2Widgets(form_ui, form, read_only=read_only) 130 return dataForm2Widgets(form_ui, form, read_only=read_only)
123 131
132
124 def dataFormResult2AdvancedList(xmlui, form_xml): 133 def dataFormResult2AdvancedList(xmlui, form_xml):
125 """Take a raw data form (not parsed by XEP-0004) and convert it to an advanced list 134 """Take a raw data form (not parsed by XEP-0004) and convert it to an advanced list.
126 raw data form is used because Wokkel doesn't manage result items parsing yet 135
127 @param xmlui: the XMLUI where the AdvancedList will be added 136 The raw data form is used because Wokkel doesn't manage result items parsing yet.
128 @param form_xml: domish.Element of the data form 137 @param xmlui (XMLUI): the XMLUI where the AdvancedList will be added
138 @param form_xml (domish.Element): element of the data form
129 @return: AdvancedList element 139 @return: AdvancedList element
130 """ 140 """
131 headers = {} 141 headers = {}
132 try: 142 try:
133 reported_elt = form_xml.elements('jabber:x:data', 'reported').next() 143 reported_elt = form_xml.elements('jabber:x:data', 'reported').next()
136 146
137 for elt in reported_elt.elements(): 147 for elt in reported_elt.elements():
138 if elt.name != "field": 148 if elt.name != "field":
139 raise exceptions.DataError("Unexpected tag") 149 raise exceptions.DataError("Unexpected tag")
140 name = elt["var"] 150 name = elt["var"]
141 label = elt.attributes.get('label','') 151 label = elt.attributes.get('label', '')
142 type_ = elt.attributes.get('type') 152 type_ = elt.attributes.get('type')
143 headers[name] = (label, type_) 153 headers[name] = (label, type_)
144 154
145 if not headers: 155 if not headers:
146 raise exceptions.DataError("No reported fields (see XEP-0004 §3.4)") 156 raise exceptions.DataError("No reported fields (see XEP-0004 §3.4)")
160 widget_type, widget_args, widget_kwargs = _dataFormField2XMLUIData(field) 170 widget_type, widget_args, widget_kwargs = _dataFormField2XMLUIData(field)
161 xmlui.addWidget(widget_type, *widget_args, **widget_kwargs) 171 xmlui.addWidget(widget_type, *widget_args, **widget_kwargs)
162 172
163 return xmlui 173 return xmlui
164 174
175
165 def dataFormResult2XMLUI(form_elt, session_id=None): 176 def dataFormResult2XMLUI(form_elt, session_id=None):
166 """Take a raw data form (not parsed by XEP-0004) and convert it to a SàT XMLUI 177 """Take a raw data form (not parsed by XEP-0004) and convert it to a SàT XMLUI.
167 raw data form is used because Wokkel doesn't manage result items parsing yet 178
168 @param form_elt: domish.Element of the data form 179 The raw data form is used because Wokkel doesn't manage result items parsing yet.
169 @return: XMLUI interface 180 @param form_elt (domish.Element): element of the data form
170 """ 181 @param session_id (unicode): session id to return with the data
171 182 @return: XMLUI instance
183 """
172 xml_ui = XMLUI("window", "vertical", session_id=session_id) 184 xml_ui = XMLUI("window", "vertical", session_id=session_id)
173 try: 185 try:
174 dataFormResult2AdvancedList(xml_ui, form_elt) 186 dataFormResult2AdvancedList(xml_ui, form_elt)
175 except exceptions.DataError: 187 except exceptions.DataError:
176 parsed_form = data_form.Form.fromElement(form_elt) 188 parsed_form = data_form.Form.fromElement(form_elt)
177 dataForm2Widgets(xml_ui, parsed_form, read_only=True) 189 dataForm2Widgets(xml_ui, parsed_form, read_only=True)
178 return xml_ui 190 return xml_ui
179 191
192
180 def _cleanValue(value): 193 def _cleanValue(value):
181 """Workaround method to avoid DBus types with D-Bus bridge 194 """Workaround method to avoid DBus types with D-Bus bridge.
182 195
183 @param value: value to clean 196 @param value: value to clean
184 @return: value in a non DBus type (only clean string yet) 197 @return: value in a non DBus type (only clean string yet)
185 """ 198 """
186 # XXX: must be removed when DBus types will no cause problems anymore 199 # XXX: must be removed when DBus types will no cause problems anymore
187 # FIXME: should be cleaned inside D-Bus bridge itself 200 # FIXME: should be cleaned inside D-Bus bridge itself
188 if isinstance(value, basestring): 201 if isinstance(value, basestring):
189 return unicode(value) 202 return unicode(value)
190 return value 203 return value
191 204
205
192 def XMLUIResult2DataFormResult(xmlui_data): 206 def XMLUIResult2DataFormResult(xmlui_data):
193 """ Extract form data from a XMLUI return 207 """ Extract form data from a XMLUI return.
194 @xmlui_data: data returned by frontends for XMLUI form 208
195 @return: dict of data usable by Wokkel's dataform 209 @param xmlui_data (dict): data returned by frontends for XMLUI form
210 @return: dict of data usable by Wokkel's data form
196 """ 211 """
197 return {key[len(SAT_FORM_PREFIX):]: _cleanValue(value) for key, value in xmlui_data.iteritems() if key.startswith(SAT_FORM_PREFIX)} 212 return {key[len(SAT_FORM_PREFIX):]: _cleanValue(value) for key, value in xmlui_data.iteritems() if key.startswith(SAT_FORM_PREFIX)}
198 213
214
199 def formEscape(name): 215 def formEscape(name):
200 """ Return escaped name for forms """ 216 """Return escaped name for forms.
201 return u"%s%s" % (SAT_FORM_PREFIX, name) 217
218 @param name (unicode): form name
219 @return: unicode
220 """
221 return u"%s%s" % (SAT_FORM_PREFIX, name)
222
202 223
203 def XMLUIResultToElt(xmlui_data): 224 def XMLUIResultToElt(xmlui_data):
204 """ Construct result domish.Element from XMLUI result 225 """Construct result domish.Element from XMLUI result.
205 @xmlui_data: data returned by frontends for XMLUI form 226
227 @param xmlui_data (dict): data returned by frontends for XMLUI form
228 @return: domish.Element
206 """ 229 """
207 form = data_form.Form('submit') 230 form = data_form.Form('submit')
208 form.makeFields(XMLUIResult2DataFormResult(xmlui_data)) 231 form.makeFields(XMLUIResult2DataFormResult(xmlui_data))
209 return form.toElement() 232 return form.toElement()
210 233
234
211 def tupleList2dataForm(values): 235 def tupleList2dataForm(values):
212 """convert a list of tuples (name,value) to a wokkel submit data form""" 236 """Convert a list of tuples (name, value) to a wokkel submit data form.
237
238 @param values (list): list of tuples
239 @return: data_form.Form
240 """
213 form = data_form.Form('submit') 241 form = data_form.Form('submit')
214 for value in values: 242 for value in values:
215 field = data_form.Field(var=value[0], value=value[1]) 243 field = data_form.Field(var=value[0], value=value[1])
216 form.addField(field) 244 form.addField(field)
217 245
218 return form 246 return form
219 247
248
220 def paramsXML2XMLUI(xml): 249 def paramsXML2XMLUI(xml):
221 """Convert the xml for parameter to a SàT XML User Interface""" 250 """Convert the XML for parameter to a SàT XML User Interface.
251
252 @param xml (unicode)
253 @return: XMLUI
254 """
222 params_doc = minidom.parseString(xml.encode('utf-8')) 255 params_doc = minidom.parseString(xml.encode('utf-8'))
223 top = params_doc.documentElement 256 top = params_doc.documentElement
224 if top.nodeName != 'params': 257 if top.nodeName != 'params':
225 raise exceptions.DataError(_('INTERNAL ERROR: parameters xml not valid')) 258 raise exceptions.DataError(_('INTERNAL ERROR: parameters xml not valid'))
226 259
272 305
273 return param_ui.toXml() 306 return param_ui.toXml()
274 307
275 308
276 def _getParamListOptions(param): 309 def _getParamListOptions(param):
277 """Retrieve the options for list element. The <option/> tags 310 """Retrieve the options for list element.
278 must be direct children of <param/>.""" 311
312 The <option/> tags must be direct children of <param/>.
313 @param param (domish.Element): element
314 @return: a tuple (options, selected_value)
315 """
279 if len(param.getElementsByTagName("options")) > 0: 316 if len(param.getElementsByTagName("options")) > 0:
280 raise exceptions.DataError(_("The 'options' tag is not allowed in parameter of type 'list'!")) 317 raise exceptions.DataError(_("The 'options' tag is not allowed in parameter of type 'list'!"))
281 elems = param.getElementsByTagName("option") 318 elems = param.getElementsByTagName("option")
282 if len(elems) == 0: 319 if len(elems) == 0:
283 return [] 320 return []
284 options = [elem.getAttribute("value") for elem in elems] 321 options = [elem.getAttribute("value") for elem in elems]
285 selected = [elem.getAttribute("value") for elem in elems if elem.getAttribute("selected") == 'true'] 322 selected = [elem.getAttribute("value") for elem in elems if elem.getAttribute("selected") == 'true']
286 return (options, selected) 323 return (options, selected)
287 324
288 325
289 ## XMLUI Elements 326 ## XMLUI Elements
290 327
291 328
293 """ Base XMLUI element """ 330 """ Base XMLUI element """
294 type = None 331 type = None
295 332
296 def __init__(self, xmlui, parent=None): 333 def __init__(self, xmlui, parent=None):
297 """Create a container element 334 """Create a container element
335
298 @param xmlui: XMLUI instance 336 @param xmlui: XMLUI instance
299 @parent: parent element 337 @parent: parent element
300 """ 338 """
301 assert(self.type) is not None 339 assert(self.type) is not None
302 if not hasattr(self, 'elem'): 340 if not hasattr(self, 'elem'):
319 self.elem = xmlui.doc.documentElement 357 self.elem = xmlui.doc.documentElement
320 super(TopElement, self).__init__(xmlui) 358 super(TopElement, self).__init__(xmlui)
321 359
322 360
323 class TabElement(Element): 361 class TabElement(Element):
324 """ Used by TabsContainer to give name and label to tabs """ 362 """ Used by TabsContainer to give name and label to tabs."""
325 type = 'tab' 363 type = 'tab'
326 364
327 def __init__(self, parent, name, label): 365 def __init__(self, parent, name, label):
328 if not isinstance(parent, TabsContainer): 366 if not isinstance(parent, TabsContainer):
329 raise exceptions.DataError(_("TabElement must be a child of TabsContainer")) 367 raise exceptions.DataError(_("TabElement must be a child of TabsContainer"))
445 while(not isinstance(current, (Container)) and 483 while(not isinstance(current, (Container)) and
446 current is not None): 484 current is not None):
447 current = current.parent 485 current = current.parent
448 return current 486 return current
449 487
488
450 class VerticalContainer(Container): 489 class VerticalContainer(Container):
451 type = "vertical" 490 type = "vertical"
452 491
453 492
454 class HorizontalContainer(Container): 493 class HorizontalContainer(Container):
480 519
481 520
482 class AdvancedListContainer(Container): 521 class AdvancedListContainer(Container):
483 type = "advanced_list" 522 type = "advanced_list"
484 523
485 def __init__(self, xmlui, callback_id=None, name=None, headers=None, items=None, columns=None, selectable = 'no', auto_index = False, parent=None): 524 def __init__(self, xmlui, callback_id=None, name=None, headers=None, items=None, columns=None, selectable='no', auto_index=False, parent=None):
486 """Create an advanced list 525 """Create an advanced list
487 @param headers: optional headers information 526 @param headers: optional headers information
488 @param callback_id: id of the method to call when selection is done 527 @param callback_id: id of the method to call when selection is done
489 @param items: list of widgets to add (just the first row) 528 @param items: list of widgets to add (just the first row)
490 @param columns: number of columns in this table, or None to autodetect 529 @param columns: number of columns in this table, or None to autodetect
525 def addHeaders(self, headers): 564 def addHeaders(self, headers):
526 for header in headers: 565 for header in headers:
527 self.addHeader(header) 566 self.addHeader(header)
528 567
529 def addHeader(self, header): 568 def addHeader(self, header):
530 pass # TODO 569 pass # TODO
531 570
532 def addItems(self, items): 571 def addItems(self, items):
533 for item in items: 572 for item in items:
534 self.append(item) 573 self.append(item)
535 574
621 660
622 class LabelWidget(Widget): 661 class LabelWidget(Widget):
623 """Used for one line blob of text, 662 """Used for one line blob of text,
624 most of time to display the desciption or name of the next widget 663 most of time to display the desciption or name of the next widget
625 """ 664 """
626 type='label' 665 type = 'label'
627 666
628 def __init__(self, xmlui, label, name=None, parent=None): 667 def __init__(self, xmlui, label, name=None, parent=None):
629 super(LabelWidget, self).__init__(xmlui, name, parent) 668 super(LabelWidget, self).__init__(xmlui, name, parent)
630 self.elem.setAttribute('value', label) 669 self.elem.setAttribute('value', label)
631 670
632 671
633 class JidWidget(Widget): 672 class JidWidget(Widget):
634 """Used to display a Jabber ID, some specific methods can be added""" 673 """Used to display a Jabber ID, some specific methods can be added"""
635 type='jid' 674 type = 'jid'
636 675
637 def __init__(self, xmlui, jid, name=None, parent=None): 676 def __init__(self, xmlui, jid, name=None, parent=None):
638 super(JidWidget, self).__init__(xmlui, name, parent) 677 super(JidWidget, self).__init__(xmlui, name, parent)
639 try: 678 try:
640 self.elem.setAttribute('value', jid.full()) 679 self.elem.setAttribute('value', jid.full())
903 if panel_type == C.XMLUI_DIALOG: 942 if panel_type == C.XMLUI_DIALOG:
904 if dialog_opt is None: 943 if dialog_opt is None:
905 dialog_opt = {} 944 dialog_opt = {}
906 self._createDialog(dialog_opt) 945 self._createDialog(dialog_opt)
907 return 946 return
908 self.main_container = self._createContainer(container, TopElement(self)) 947 self.main_container = self._createContainer(container, TopElement(self))
909 self.current_container = self.main_container 948 self.current_container = self.main_container
910 949
911 def _introspect(self): 950 def _introspect(self):
912 """ Introspect module to find Widgets and Containers """ 951 """ Introspect module to find Widgets and Containers """
913 self._containers = {} 952 self._containers = {}
927 966
928 def __del__(self): 967 def __del__(self):
929 self.doc.unlink() 968 self.doc.unlink()
930 969
931 def __getattr__(self, name): 970 def __getattr__(self, name):
932 if name.startswith("add") and name not in ('addWidget',): # addWidgetName(...) create an instance of WidgetName 971 if name.startswith("add") and name not in ('addWidget',): # addWidgetName(...) create an instance of WidgetName
933 if self.type == C.XMLUI_DIALOG: 972 if self.type == C.XMLUI_DIALOG:
934 raise exceptions.InternalError(_("addXXX can't be used with dialogs")) 973 raise exceptions.InternalError(_("addXXX can't be used with dialogs"))
935 class_name = name[3:]+"Widget" 974 class_name = name[3:] + "Widget"
936 if class_name in globals(): 975 if class_name in globals():
937 cls = globals()[class_name] 976 cls = globals()[class_name]
938 if issubclass(cls, Widget): 977 if issubclass(cls, Widget):
939 def createWidget(*args, **kwargs): 978 def createWidget(*args, **kwargs):
940 if "parent" not in kwargs: 979 if "parent" not in kwargs:
941 kwargs["parent"] = self.current_container 980 kwargs["parent"] = self.current_container
942 if "name" not in kwargs and issubclass(cls, InputWidget): # name can be given as first argument or in keyword arguments for InputWidgets 981 if "name" not in kwargs and issubclass(cls, InputWidget): # name can be given as first argument or in keyword arguments for InputWidgets
943 args = list(args) 982 args = list(args)
944 kwargs["name"] = args.pop(0) 983 kwargs["name"] = args.pop(0)
945 return cls(self, *args, **kwargs) 984 return cls(self, *args, **kwargs)
946 return createWidget 985 return createWidget
947 return object.__getattribute__(self, name) 986 return object.__getattribute__(self, name)
958 if value is None: 997 if value is None:
959 try: 998 try:
960 top_element.removeAttribute("submit") 999 top_element.removeAttribute("submit")
961 except NotFoundErr: 1000 except NotFoundErr:
962 pass 1001 pass
963 elif value: # submit_id can be the empty string to bypass form restriction 1002 elif value: # submit_id can be the empty string to bypass form restriction
964 top_element.setAttribute("submit", value) 1003 top_element.setAttribute("submit", value)
965 1004
966 @property 1005 @property
967 def session_id(self): 1006 def session_id(self):
968 top_element = self.doc.documentElement 1007 top_element = self.doc.documentElement
1048 1087
1049 @param message: body of the note 1088 @param message: body of the note
1050 @param title: title of the note 1089 @param title: title of the note
1051 @param level: one of C.XMLUI_DATA_LVL_* 1090 @param level: one of C.XMLUI_DATA_LVL_*
1052 """ 1091 """
1053 note_xmlui = XMLUI(C.XMLUI_DIALOG, dialog_opt = { 1092 note_xmlui = XMLUI(C.XMLUI_DIALOG, dialog_opt={
1054 C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE, 1093 C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE,
1055 C.XMLUI_DATA_MESS: message, 1094 C.XMLUI_DATA_MESS: message,
1056 C.XMLUI_DATA_LVL: level}, 1095 C.XMLUI_DATA_LVL: level},
1057 title=title 1096 title=title
1058 ) 1097 )
1059 return note_xmlui 1098 return note_xmlui
1060 1099
1061 # Misc other funtions 1100 # Misc other funtions
1062 1101
1063 1102