# HG changeset patch # User Goffi # Date 1547196497 -3600 # Node ID 5a51c7fc74a53d00fcf77835b69b2ef4841ddb26 # Parent 4b693ea24d5fb418c7a92575e58ddc54644727d1 core (XMLUI): small optimisation: introspection is done once at module loading instead of on each XMLUI instantiation diff -r 4b693ea24d5f -r 5a51c7fc74a5 sat/tools/xml_tools.py --- a/sat/tools/xml_tools.py Sun Jan 06 17:39:57 2019 +0100 +++ b/sat/tools/xml_tools.py Fri Jan 11 09:48:17 2019 +0100 @@ -110,7 +110,6 @@ return widget_type, widget_args, widget_kwargs - def dataForm2Widgets(form_ui, form, read_only=False, prepend=None, filters=None): """Complete an existing XMLUI with widget converted from XEP-0004 data forms. @@ -280,7 +279,11 @@ @param filters: same as for [dataForm2Widgets] @return: XMLUI instance """ + # we deepcopy the form because _dataFormField2XMLUIData can modify the value + # FIXME: check if it's really important, the only modified value seems to be + # the replacement of None by "" on fixed fields form = deepcopy(result_form) + form = result_form for name, field in form.fields.iteritems(): try: base_field = base_form.fields[name] @@ -1300,7 +1303,6 @@ returned by frontends @attribute named_widgets(dict): map from name to widget """ - self._introspect() # FIXME: why doing that on each XMLUI ? should be done once if panel_type not in [ C.XMLUI_WINDOW, C.XMLUI_FORM, @@ -1336,50 +1338,63 @@ self.current_container = self.main_container self.named_widgets = {} - def _introspect(self): - """ Introspect module to find Widgets and Containers """ - self._containers = {} - self._widgets = {} + @staticmethod + def creatorWrapper(widget_cls, is_input): + # TODO: once moved to Python 3, use functools.partialmethod and + # remove the creatorWrapper + def createWidget(self, *args, **kwargs): + if self.type == C.XMLUI_DIALOG: + raise exceptions.InternalError(_( + "createWidget can't be used with dialogs")) + if "parent" not in kwargs: + kwargs["parent"] = self.current_container + if "name" not in kwargs and is_input: + # name can be given as first argument or in keyword + # arguments for InputWidgets + args = list(args) + kwargs["name"] = args.pop(0) + return widget_cls(self, *args, **kwargs) + return createWidget + + @classmethod + def _introspect(cls): + """ Introspect module to find Widgets and Containers, and create addXXX methods""" + # FIXME: we can't log anything because this file is used + # in bin/sat script then evaluated + # bin/sat should be refactored + # log.debug(u'introspecting XMLUI widgets and containers') + cls._containers = {} + cls._widgets = {} for obj in globals().values(): try: if issubclass(obj, Widget): if obj.__name__ == "Widget": continue - self._widgets[obj.type] = obj + cls._widgets[obj.type] = obj + creator_name = u"add" + obj.__name__ + if creator_name.endswith('Widget'): + creator_name = creator_name[:-6] + is_input = issubclass(obj, InputWidget) + # FIXME: cf. above comment + # log.debug(u"Adding {creator_name} creator (is_input={is_input}))" + # .format(creator_name=creator_name, is_input=is_input)) + + assert not hasattr(cls, creator_name) + # XXX: we need to use creatorWrapper because we are in a loop + # and Python 2 doesn't support default values in kwargs + # when using *args, **kwargs + setattr(cls, creator_name, cls.creatorWrapper(obj, is_input)) + elif issubclass(obj, Container): if obj.__name__ == "Container": continue - self._containers[obj.type] = obj + cls._containers[obj.type] = obj except TypeError: pass def __del__(self): self.doc.unlink() - def __getattr__(self, name): - if name.startswith("add") and name not in ( - "addWidget", - ): # addWidgetName(...) create an instance of WidgetName - if self.type == C.XMLUI_DIALOG: - raise exceptions.InternalError(_("addXXX can't be used with dialogs")) - class_name = name[3:] + "Widget" - if class_name in globals(): - cls = globals()[class_name] - if issubclass(cls, Widget): - - def createWidget(*args, **kwargs): - if "parent" not in kwargs: - kwargs["parent"] = self.current_container - if "name" not in kwargs and issubclass( - cls, InputWidget - ): # name can be given as first argument or in keyword arguments for InputWidgets - args = list(args) - kwargs["name"] = args.pop(0) - return cls(self, *args, **kwargs) - - return createWidget - return object.__getattribute__(self, name) - @property def submit_id(self): top_element = self.doc.documentElement @@ -1480,11 +1495,12 @@ def addWidget(self, type_, *args, **kwargs): """Convenience method to add an element""" - if type_ not in self._widgets: - raise exceptions.DataError(_("Invalid type [%s]") % type_) if "parent" not in kwargs: kwargs["parent"] = self.current_container - cls = self._widgets[type_] + try: + cls = self._widgets[type_] + except KeyError: + raise exceptions.DataError(_("Invalid type [{type_}]").format(type_=type_)) return cls(self, *args, **kwargs) def toXml(self): @@ -1492,6 +1508,10 @@ return self.doc.toxml() +# we call this to have automatic discovery of containers and widgets +XMLUI._introspect() + + # Some sugar for XMLUI dialogs