comparison sat/tools/xml_tools.py @ 2762:5a51c7fc74a5

core (XMLUI): small optimisation: introspection is done once at module loading instead of on each XMLUI instantiation
author Goffi <goffi@goffi.org>
date Fri, 11 Jan 2019 09:48:17 +0100
parents 1797671827b9
children 003b8b4b56a7
comparison
equal deleted inserted replaced
2761:4b693ea24d5f 2762:5a51c7fc74a5
108 if field.var: 108 if field.var:
109 widget_kwargs["name"] = field.var 109 widget_kwargs["name"] = field.var
110 110
111 return widget_type, widget_args, widget_kwargs 111 return widget_type, widget_args, widget_kwargs
112 112
113
114 def dataForm2Widgets(form_ui, form, read_only=False, prepend=None, filters=None): 113 def dataForm2Widgets(form_ui, form, read_only=False, prepend=None, filters=None):
115 """Complete an existing XMLUI with widget converted from XEP-0004 data forms. 114 """Complete an existing XMLUI with widget converted from XEP-0004 data forms.
116 115
117 @param form_ui (XMLUI): XMLUI instance 116 @param form_ui (XMLUI): XMLUI instance
118 @param form (data_form.Form): Wokkel's implementation of data form 117 @param form (data_form.Form): Wokkel's implementation of data form
278 @param session_id (unicode): session id to return with the data 277 @param session_id (unicode): session id to return with the data
279 @param prepend: same as for [dataForm2Widgets] 278 @param prepend: same as for [dataForm2Widgets]
280 @param filters: same as for [dataForm2Widgets] 279 @param filters: same as for [dataForm2Widgets]
281 @return: XMLUI instance 280 @return: XMLUI instance
282 """ 281 """
282 # we deepcopy the form because _dataFormField2XMLUIData can modify the value
283 # FIXME: check if it's really important, the only modified value seems to be
284 # the replacement of None by "" on fixed fields
283 form = deepcopy(result_form) 285 form = deepcopy(result_form)
286 form = result_form
284 for name, field in form.fields.iteritems(): 287 for name, field in form.fields.iteritems():
285 try: 288 try:
286 base_field = base_form.fields[name] 289 base_field = base_form.fields[name]
287 except KeyError: 290 except KeyError:
288 continue 291 continue
1298 dialog) 1301 dialog)
1299 @param session_id: use to keep a session attached to the dialog, must be 1302 @param session_id: use to keep a session attached to the dialog, must be
1300 returned by frontends 1303 returned by frontends
1301 @attribute named_widgets(dict): map from name to widget 1304 @attribute named_widgets(dict): map from name to widget
1302 """ 1305 """
1303 self._introspect() # FIXME: why doing that on each XMLUI ? should be done once
1304 if panel_type not in [ 1306 if panel_type not in [
1305 C.XMLUI_WINDOW, 1307 C.XMLUI_WINDOW,
1306 C.XMLUI_FORM, 1308 C.XMLUI_FORM,
1307 C.XMLUI_PARAM, 1309 C.XMLUI_PARAM,
1308 C.XMLUI_POPUP, 1310 C.XMLUI_POPUP,
1334 return 1336 return
1335 self.main_container = self._createContainer(container, TopElement(self)) 1337 self.main_container = self._createContainer(container, TopElement(self))
1336 self.current_container = self.main_container 1338 self.current_container = self.main_container
1337 self.named_widgets = {} 1339 self.named_widgets = {}
1338 1340
1339 def _introspect(self): 1341 @staticmethod
1340 """ Introspect module to find Widgets and Containers """ 1342 def creatorWrapper(widget_cls, is_input):
1341 self._containers = {} 1343 # TODO: once moved to Python 3, use functools.partialmethod and
1342 self._widgets = {} 1344 # remove the creatorWrapper
1345 def createWidget(self, *args, **kwargs):
1346 if self.type == C.XMLUI_DIALOG:
1347 raise exceptions.InternalError(_(
1348 "createWidget can't be used with dialogs"))
1349 if "parent" not in kwargs:
1350 kwargs["parent"] = self.current_container
1351 if "name" not in kwargs and is_input:
1352 # name can be given as first argument or in keyword
1353 # arguments for InputWidgets
1354 args = list(args)
1355 kwargs["name"] = args.pop(0)
1356 return widget_cls(self, *args, **kwargs)
1357 return createWidget
1358
1359 @classmethod
1360 def _introspect(cls):
1361 """ Introspect module to find Widgets and Containers, and create addXXX methods"""
1362 # FIXME: we can't log anything because this file is used
1363 # in bin/sat script then evaluated
1364 # bin/sat should be refactored
1365 # log.debug(u'introspecting XMLUI widgets and containers')
1366 cls._containers = {}
1367 cls._widgets = {}
1343 for obj in globals().values(): 1368 for obj in globals().values():
1344 try: 1369 try:
1345 if issubclass(obj, Widget): 1370 if issubclass(obj, Widget):
1346 if obj.__name__ == "Widget": 1371 if obj.__name__ == "Widget":
1347 continue 1372 continue
1348 self._widgets[obj.type] = obj 1373 cls._widgets[obj.type] = obj
1374 creator_name = u"add" + obj.__name__
1375 if creator_name.endswith('Widget'):
1376 creator_name = creator_name[:-6]
1377 is_input = issubclass(obj, InputWidget)
1378 # FIXME: cf. above comment
1379 # log.debug(u"Adding {creator_name} creator (is_input={is_input}))"
1380 # .format(creator_name=creator_name, is_input=is_input))
1381
1382 assert not hasattr(cls, creator_name)
1383 # XXX: we need to use creatorWrapper because we are in a loop
1384 # and Python 2 doesn't support default values in kwargs
1385 # when using *args, **kwargs
1386 setattr(cls, creator_name, cls.creatorWrapper(obj, is_input))
1387
1349 elif issubclass(obj, Container): 1388 elif issubclass(obj, Container):
1350 if obj.__name__ == "Container": 1389 if obj.__name__ == "Container":
1351 continue 1390 continue
1352 self._containers[obj.type] = obj 1391 cls._containers[obj.type] = obj
1353 except TypeError: 1392 except TypeError:
1354 pass 1393 pass
1355 1394
1356 def __del__(self): 1395 def __del__(self):
1357 self.doc.unlink() 1396 self.doc.unlink()
1358
1359 def __getattr__(self, name):
1360 if name.startswith("add") and name not in (
1361 "addWidget",
1362 ): # addWidgetName(...) create an instance of WidgetName
1363 if self.type == C.XMLUI_DIALOG:
1364 raise exceptions.InternalError(_("addXXX can't be used with dialogs"))
1365 class_name = name[3:] + "Widget"
1366 if class_name in globals():
1367 cls = globals()[class_name]
1368 if issubclass(cls, Widget):
1369
1370 def createWidget(*args, **kwargs):
1371 if "parent" not in kwargs:
1372 kwargs["parent"] = self.current_container
1373 if "name" not in kwargs and issubclass(
1374 cls, InputWidget
1375 ): # name can be given as first argument or in keyword arguments for InputWidgets
1376 args = list(args)
1377 kwargs["name"] = args.pop(0)
1378 return cls(self, *args, **kwargs)
1379
1380 return createWidget
1381 return object.__getattribute__(self, name)
1382 1397
1383 @property 1398 @property
1384 def submit_id(self): 1399 def submit_id(self):
1385 top_element = self.doc.documentElement 1400 top_element = self.doc.documentElement
1386 if not top_element.hasAttribute("submit"): 1401 if not top_element.hasAttribute("submit"):
1478 assert isinstance(self.current_container, Container) 1493 assert isinstance(self.current_container, Container)
1479 return self.current_container 1494 return self.current_container
1480 1495
1481 def addWidget(self, type_, *args, **kwargs): 1496 def addWidget(self, type_, *args, **kwargs):
1482 """Convenience method to add an element""" 1497 """Convenience method to add an element"""
1483 if type_ not in self._widgets:
1484 raise exceptions.DataError(_("Invalid type [%s]") % type_)
1485 if "parent" not in kwargs: 1498 if "parent" not in kwargs:
1486 kwargs["parent"] = self.current_container 1499 kwargs["parent"] = self.current_container
1487 cls = self._widgets[type_] 1500 try:
1501 cls = self._widgets[type_]
1502 except KeyError:
1503 raise exceptions.DataError(_("Invalid type [{type_}]").format(type_=type_))
1488 return cls(self, *args, **kwargs) 1504 return cls(self, *args, **kwargs)
1489 1505
1490 def toXml(self): 1506 def toXml(self):
1491 """return the XML representation of the panel""" 1507 """return the XML representation of the panel"""
1492 return self.doc.toxml() 1508 return self.doc.toxml()
1509
1510
1511 # we call this to have automatic discovery of containers and widgets
1512 XMLUI._introspect()
1493 1513
1494 1514
1495 # Some sugar for XMLUI dialogs 1515 # Some sugar for XMLUI dialogs
1496 1516
1497 1517