diff sat/tools/xml_tools.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 e2cb04b381bb
children 9d0df638c8b4
line wrap: on
line diff
--- a/sat/tools/xml_tools.py	Wed Jul 31 11:31:22 2019 +0200
+++ b/sat/tools/xml_tools.py	Tue Aug 13 19:08:41 2019 +0200
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
 # SAT: a jabber client
@@ -30,8 +30,7 @@
 from twisted.internet import defer
 from sat.core import exceptions
 from collections import OrderedDict
-from copy import deepcopy
-import htmlentitydefs
+import html.entities
 import re
 
 """This library help manage XML used in SàT (parameters, registration, etc)"""
@@ -64,26 +63,26 @@
     widget_kwargs = {}
     if field.fieldType is None and field.ext_type is not None:
         # we have an extended field
-        if field.ext_type == u"xml":
+        if field.ext_type == "xml":
             element = field.value
             if element.uri == C.NS_XHTML:
                 widget_type = "xhtmlbox"
                 widget_args[0] = element.toXml()
                 widget_kwargs["read_only"] = read_only
             else:
-                log.warning(u"unknown XML element, falling back to textbox")
+                log.warning("unknown XML element, falling back to textbox")
                 widget_type = "textbox"
                 widget_args[0] = element.toXml()
                 widget_kwargs["read_only"] = read_only
         else:
-            raise exceptions.DataError(u"unknown extended type {ext_type}".format(
+            raise exceptions.DataError("unknown extended type {ext_type}".format(
                 ext_type = field.ext_type))
 
     elif field.fieldType == "fixed" or field.fieldType is None:
         widget_type = "text"
         if field.value is None:
             if field.label is None:
-                log.warning(_(u"Fixed field has neither value nor label, ignoring it"))
+                log.warning(_("Fixed field has neither value nor label, ignoring it"))
                 field.value = ""
             else:
                 field.value = field.label
@@ -97,7 +96,7 @@
         widget_kwargs["read_only"] = read_only
     elif field.fieldType == "text-multi":
         widget_type = "textbox"
-        widget_args[0] = u"\n".join(field.values)
+        widget_args[0] = "\n".join(field.values)
         widget_kwargs["read_only"] = read_only
     elif field.fieldType == "hidden":
         widget_type = "hidden"
@@ -121,7 +120,7 @@
         widget_args = []
     else:
         log.error(
-            u"FIXME FIXME FIXME: Type [%s] is not managed yet by SàT" % field.fieldType
+            "FIXME FIXME FIXME: Type [%s] is not managed yet by SàT" % field.fieldType
         )
         widget_type = "string"
         widget_kwargs["read_only"] = read_only
@@ -206,7 +205,7 @@
     """
     headers = OrderedDict()
     try:
-        reported_elt = form_xml.elements("jabber:x:data", "reported").next()
+        reported_elt = next(form_xml.elements("jabber:x:data", "reported"))
     except StopIteration:
         raise exceptions.DataError(
             "Couldn't find expected <reported> tag in %s" % form_xml.toXml()
@@ -229,7 +228,7 @@
     for item_elt in item_elts:
         for elt in item_elt.elements():
             if elt.name != "field":
-                log.warning(u"Unexpected tag (%s)" % elt.name)
+                log.warning("Unexpected tag (%s)" % elt.name)
                 continue
             field = data_form.Field.fromElement(elt)
 
@@ -303,9 +302,9 @@
     # 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 = deepcopy(result_form)
     form = result_form
-    for name, field in form.fields.iteritems():
+    for name, field in form.fields.items():
         try:
             base_field = base_form.fields[name]
         except KeyError:
@@ -324,8 +323,8 @@
     """
     # XXX: must be removed when DBus types will no cause problems anymore
     # FIXME: should be cleaned inside D-Bus bridge itself
-    if isinstance(value, basestring):
-        return unicode(value)
+    if isinstance(value, str):
+        return str(value)
     return value
 
 
@@ -336,12 +335,12 @@
     @return: dict of data usable by Wokkel's data form
     """
     ret = {}
-    for key, value in xmlui_data.iteritems():
+    for key, value in xmlui_data.items():
         if not key.startswith(SAT_FORM_PREFIX):
             continue
-        if isinstance(value, basestring) and u'\n' in value:
+        if isinstance(value, str) and '\n' in value:
             # data form expects multi-lines text to be in separated values
-            value = value.split(u'\n')
+            value = value.split('\n')
         ret[key[len(SAT_FORM_PREFIX) :]] = _cleanValue(value)
     return ret
 
@@ -352,12 +351,12 @@
     @param name (unicode): form name
     @return: unicode
     """
-    return u"%s%s" % (SAT_FORM_PREFIX, name)
+    return "%s%s" % (SAT_FORM_PREFIX, name)
 
 
 def isXMLUICancelled(raw_xmlui):
     """Tell if an XMLUI has been cancelled by checking raw XML"""
-    return C.bool(raw_xmlui.get(u'cancelled', C.BOOL_FALSE))
+    return C.bool(raw_xmlui.get('cancelled', C.BOOL_FALSE))
 
 
 def XMLUIResultToElt(xmlui_data):
@@ -618,7 +617,7 @@
         """
         assert isinstance(parent, ListWidget)
         super(OptionElement, self).__init__(parent.xmlui, parent)
-        if isinstance(option, basestring):
+        if isinstance(option, str):
             value, label = option, option
         elif isinstance(option, tuple):
             value, label = option
@@ -643,8 +642,8 @@
         super(JidElement, self).__init__(parent.xmlui, parent)
         if isinstance(jid_, jid.JID):
             value = jid_.full()
-        elif isinstance(jid_, basestring):
-            value = unicode(jid_)
+        elif isinstance(jid_, str):
+            value = str(jid_)
         else:
             raise NotImplementedError
         jid_txt = self.xmlui.doc.createTextNode(value)
@@ -879,7 +878,7 @@
             self.elem.setAttribute("name", name)
             if name in xmlui.named_widgets:
                 raise exceptions.ConflictError(
-                    _(u'A widget with the name "{name}" already exists.').format(
+                    _('A widget with the name "{name}" already exists.').format(
                         name=name
                     )
                 )
@@ -973,7 +972,7 @@
         try:
             self.elem.setAttribute("value", jid.full())
         except AttributeError:
-            self.elem.setAttribute("value", unicode(jid))
+            self.elem.setAttribute("value", str(jid))
 
 
 class DividerWidget(Widget):
@@ -1049,7 +1048,7 @@
         if clean:
             if cleanXHTML is None:
                 raise exceptions.NotFound(
-                    u"No cleaning method set, can't clean the XHTML")
+                    "No cleaning method set, can't clean the XHTML")
             value = cleanXHTML(value)
 
         super(XHTMLBoxWidget, self).__init__(
@@ -1114,7 +1113,7 @@
 
 class ListWidget(InputWidget):
     type = "list"
-    STYLES = (u"multi", u"noselect", u"extensible", u"reducible", u"inline")
+    STYLES = ("multi", "noselect", "extensible", "reducible", "inline")
 
     def __init__(
         self, xmlui, options, selected=None, styles=None, name=None, parent=None
@@ -1144,11 +1143,11 @@
             styles = set()
         else:
             styles = set(styles)
-        if u"noselect" in styles and (u"multi" in styles or selected):
+        if "noselect" in styles and ("multi" in styles or selected):
             raise exceptions.DataError(
                 _(
-                    u'"multi" flag and "selected" option are not compatible with '
-                    u'"noselect" flag'
+                    '"multi" flag and "selected" option are not compatible with '
+                    '"noselect" flag'
                 )
             )
         if not options:
@@ -1163,18 +1162,18 @@
     def addOptions(self, options, selected=None):
         """Add options to a multi-values element (e.g. list) """
         if selected:
-            if isinstance(selected, basestring):
+            if isinstance(selected, str):
                 selected = [selected]
         else:
             selected = []
         for option in options:
-            assert isinstance(option, basestring) or isinstance(option, tuple)
-            value = option if isinstance(option, basestring) else option[0]
+            assert isinstance(option, str) or isinstance(option, tuple)
+            value = option if isinstance(option, str) else option[0]
             OptionElement(self, option, value in selected)
 
     def setStyles(self, styles):
         if not styles.issubset(self.STYLES):
-            raise exceptions.DataError(_(u"invalid styles"))
+            raise exceptions.DataError(_("invalid styles"))
         for style in styles:
             self.elem.setAttribute(style, "yes")
         # TODO: check flags incompatibily (noselect and multi) like in __init__
@@ -1186,9 +1185,9 @@
     def value(self):
         """Return the value of first selected option"""
         for child in self.elem.childNodes:
-            if child.tagName == u"option" and child.getAttribute(u"selected") == u"true":
-                return child.getAttribute(u"value")
-        return u""
+            if child.tagName == "option" and child.getAttribute("selected") == "true":
+                return child.getAttribute("value")
+        return ""
 
 
 class JidsListWidget(InputWidget):
@@ -1358,7 +1357,7 @@
             raise exceptions.DataError(_("Unknown panel type [%s]") % panel_type)
         if panel_type == C.XMLUI_FORM and submit_id is None:
             raise exceptions.DataError(_("form XMLUI need a submit_id"))
-        if not isinstance(container, basestring):
+        if not isinstance(container, str):
             raise exceptions.DataError(_("container argument must be a string"))
         if dialog_opt is not None and panel_type != C.XMLUI_DIALOG:
             raise exceptions.DataError(
@@ -1410,13 +1409,13 @@
         # log.debug(u'introspecting XMLUI widgets and containers')
         cls._containers = {}
         cls._widgets = {}
-        for obj in globals().values():
+        for obj in list(globals().values()):
             try:
                 if issubclass(obj, Widget):
                     if obj.__name__ == "Widget":
                         continue
                     cls._widgets[obj.type] = obj
-                    creator_name = u"add" + obj.__name__
+                    creator_name = "add" + obj.__name__
                     if creator_name.endswith('Widget'):
                         creator_name = creator_name[:-6]
                     is_input = issubclass(obj, InputWidget)
@@ -1525,7 +1524,7 @@
 
         @param container: either container type (container it then created),
                           or an Container instance"""
-        if isinstance(container, basestring):
+        if isinstance(container, str):
             self.current_container = self._createContainer(
                 container,
                 self.current_container.getParentContainer() or self.main_container,
@@ -1633,7 +1632,7 @@
     return xmlui_d
 
 
-def deferDialog(host, message, title=u"Please confirm", type_=C.XMLUI_DIALOG_CONFIRM,
+def deferDialog(host, message, title="Please confirm", type_=C.XMLUI_DIALOG_CONFIRM,
     options=None, action_extra=None, security_limit=C.NO_SECURITY_LIMIT, chained=False,
     profile=C.PROF_KEY_NONE):
     """Create a submitable dialog and manage it with a deferred
@@ -1689,7 +1688,7 @@
 
 def isXHTMLField(field):
     """Check if a data_form.Field is an XHTML one"""
-    return (field.fieldType is None and field.ext_type == u"xml" and
+    return (field.fieldType is None and field.ext_type == "xml" and
             field.value.uri == C.NS_XHTML)
 
 
@@ -1705,10 +1704,10 @@
             return matchobj.group(0)
         else:
             try:
-                return unichr(htmlentitydefs.name2codepoint[entity])
+                return chr(html.entities.name2codepoint[entity])
             except KeyError:
-                log.warning(u"removing unknown entity {}".format(entity))
-                return u""
+                log.warning("removing unknown entity {}".format(entity))
+                return ""
 
     def __call__(self, raw_xml, force_spaces=False, namespace=None):
         """
@@ -1721,9 +1720,9 @@
         # we need to wrap element in case
         # there is not a unique one on the top
         if namespace is not None:
-            raw_xml = u"<div xmlns='{}'>{}</div>".format(namespace, raw_xml)
+            raw_xml = "<div xmlns='{}'>{}</div>".format(namespace, raw_xml)
         else:
-            raw_xml = u"<div>{}</div>".format(raw_xml)
+            raw_xml = "<div>{}</div>".format(raw_xml)
 
         # avoid ParserError on HTML escaped chars
         raw_xml = html_entity_re.sub(self._escapeHTML, raw_xml)
@@ -1767,7 +1766,7 @@
     for child in node.childNodes:
         if child.nodeType == child.TEXT_NODE:
             data.append(child.wholeText)
-    return u"".join(data)
+    return "".join(data)
 
 
 def findAll(elt, namespaces=None, names=None):
@@ -1780,9 +1779,9 @@
         None to accept every namespaces
     @return ((G)domish.Element): found elements
     """
-    if isinstance(namespaces, basestring):
+    if isinstance(namespaces, str):
         namespaces = tuple((namespaces,))
-    if isinstance(names, basestring):
+    if isinstance(names, str):
         names = tuple((names,))
 
     for child in elt.elements():