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
This commit is contained in:
Goffi 2019-08-13 19:08:41 +02:00
commit fbfe96c505

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# SAT plugin for Pubsub Schemas
@ -20,7 +20,7 @@
from collections import Iterable
import copy
import itertools
from zope.interface import implements
from zope.interface import implementer
from twisted.words.protocols.jabber import jid
from twisted.words.protocols.jabber.xmlstream import XMPPHandler
from twisted.internet import defer
@ -38,23 +38,23 @@ from sat.core.log import getLogger
log = getLogger(__name__)
NS_SCHEMA = u"https://salut-a-toi/protocol/schema:0"
NS_SCHEMA = "https://salut-a-toi/protocol/schema:0"
PLUGIN_INFO = {
C.PI_NAME: u"PubSub Schema",
C.PI_IMPORT_NAME: u"PUBSUB_SCHEMA",
C.PI_TYPE: u"EXP",
C.PI_NAME: "PubSub Schema",
C.PI_IMPORT_NAME: "PUBSUB_SCHEMA",
C.PI_TYPE: "EXP",
C.PI_PROTOCOLS: [],
C.PI_DEPENDENCIES: [u"XEP-0060", u"IDENTITY"],
C.PI_MAIN: u"PubsubSchema",
C.PI_HANDLER: u"yes",
C.PI_DESCRIPTION: _(u"""Handle Pubsub data schemas"""),
C.PI_DEPENDENCIES: ["XEP-0060", "IDENTITY"],
C.PI_MAIN: "PubsubSchema",
C.PI_HANDLER: "yes",
C.PI_DESCRIPTION: _("""Handle Pubsub data schemas"""),
}
class PubsubSchema(object):
def __init__(self, host):
log.info(_(u"PubSub Schema initialization"))
log.info(_("PubSub Schema initialization"))
self.host = host
self._p = self.host.plugins["XEP-0060"]
self._i = self.host.plugins["IDENTITY"]
@ -64,7 +64,7 @@ class PubsubSchema(object):
in_sign="sss",
out_sign="s",
method=self._getSchema,
async=True,
async_=True,
)
host.bridge.addMethod(
"psSchemaSet",
@ -72,15 +72,16 @@ class PubsubSchema(object):
in_sign="ssss",
out_sign="",
method=self._setSchema,
async=True,
async_=True,
)
host.bridge.addMethod(
"psSchemaUIGet",
".plugin",
in_sign="sss",
out_sign="s",
method=utils.partial(self._getUISchema, default_node=None),
async=True,
method=lambda service, nodeIdentifier, profile_key: self._getUISchema(
service, nodeIdentifier, default_node=None, profile_key=profile_key),
async_=True,
)
host.bridge.addMethod(
"psItemsFormGet",
@ -88,7 +89,7 @@ class PubsubSchema(object):
in_sign="ssssiassa{ss}s",
out_sign="(asa{ss})",
method=self._getDataFormItems,
async=True,
async_=True,
)
host.bridge.addMethod(
"psItemFormSend",
@ -96,7 +97,7 @@ class PubsubSchema(object):
in_sign="ssa{sas}ssa{ss}s",
out_sign="s",
method=self._sendDataFormItem,
async=True,
async_=True,
)
def getHandler(self, client):
@ -104,7 +105,7 @@ class PubsubSchema(object):
def _getSchemaBridgeCb(self, schema_elt):
if schema_elt is None:
return u""
return ""
return schema_elt.toXml()
def _getSchema(self, service, nodeIdentifier, profile_key=C.PROF_KEY_NONE):
@ -135,7 +136,7 @@ class PubsubSchema(object):
@return (domish.Element, None): schema (<x> element)
None if not schema has been set on this node
"""
iq_elt = client.IQ(u"get")
iq_elt = client.IQ("get")
if service is not None:
iq_elt["to"] = service.full()
pubsub_elt = iq_elt.addElement((NS_SCHEMA, "pubsub"))
@ -163,12 +164,12 @@ class PubsubSchema(object):
the form should not be modified if copy_form is not set
"""
if schema is None:
log.debug(_(u"unspecified schema, we need to request it"))
log.debug(_("unspecified schema, we need to request it"))
schema = yield self.getSchema(client, service, nodeIdentifier)
if schema is None:
raise exceptions.DataError(
_(
u"no schema specified, and this node has no schema either, we can't construct the data form"
"no schema specified, and this node has no schema either, we can't construct the data form"
)
)
elif isinstance(schema, data_form.Form):
@ -179,7 +180,7 @@ class PubsubSchema(object):
try:
form = data_form.Form.fromElement(schema)
except data_form.Error as e:
raise exceptions.DataError(_(u"Invalid Schema: {msg}").format(msg=e))
raise exceptions.DataError(_("Invalid Schema: {msg}").format(msg=e))
form.formType = form_type
defer.returnValue(form)
@ -192,7 +193,7 @@ class PubsubSchema(object):
profile_key=C.PROF_KEY_NONE):
if not nodeIdentifier:
if not default_node:
raise ValueError(_(u"nodeIndentifier needs to be set"))
raise ValueError(_("nodeIndentifier needs to be set"))
nodeIdentifier = default_node
client = self.host.getClient(profile_key)
service = None if not service else jid.JID(service)
@ -233,7 +234,7 @@ class PubsubSchema(object):
client = self.host.getClient(profile_key)
service = jid.JID(service) if service else None
if not node:
raise exceptions.DataError(_(u"empty node is not allowed"))
raise exceptions.DataError(_("empty node is not allowed"))
if schema:
schema = generic.parseXml(schema.encode("utf-8"))
else:
@ -276,7 +277,7 @@ class PubsubSchema(object):
if not nodeIdentifier:
if not default_node:
raise ValueError(
_(u"default_node must be set if nodeIdentifier is not set")
_("default_node must be set if nodeIdentifier is not set")
)
nodeIdentifier = default_node
# we need the initial form to get options of fields when suitable
@ -296,7 +297,7 @@ class PubsubSchema(object):
items, metadata = items_data
items_xmlui = []
for item_elt in items:
for x_elt in item_elt.elements((data_form.NS_X_DATA, u"x")):
for x_elt in item_elt.elements((data_form.NS_X_DATA, "x")):
form = data_form.Form.fromElement(x_elt)
if form_ns and form.formNamespace != form_ns:
continue
@ -307,9 +308,9 @@ class PubsubSchema(object):
# are not checked
prepend=(
("label", "id"),
("text", item_elt["id"], u"id"),
("text", item_elt["id"], "id"),
("label", "publisher"),
("text", item_elt.getAttribute("publisher", ""), u"publisher"),
("text", item_elt.getAttribute("publisher", ""), "publisher"),
),
filters=filters,
read_only=False,
@ -336,7 +337,7 @@ class PubsubSchema(object):
extra,
deserialise=True,
)
d.addCallback(lambda ret: ret or u"")
d.addCallback(lambda ret: ret or "")
return d
@defer.inlineCallbacks
@ -361,22 +362,22 @@ class PubsubSchema(object):
client, service, nodeIdentifier, schema, form_type="submit"
)
for name, values_list in values.iteritems():
for name, values_list in values.items():
try:
field = form.fields[name]
except KeyError:
log.warning(
_(u"field {name} doesn't exist, ignoring it").format(name=name)
_("field {name} doesn't exist, ignoring it").format(name=name)
)
continue
if isinstance(values_list, basestring) or not isinstance(
if isinstance(values_list, str) or not isinstance(
values_list, Iterable
):
values_list = [values_list]
if deserialise:
if field.fieldType == u"boolean":
if field.fieldType == "boolean":
values_list = [C.bool(v) for v in values_list]
elif field.fieldType == u"text-multi":
elif field.fieldType == "text-multi":
# for text-multi, lines must be put on separate values
values_list = list(
itertools.chain(*[v.splitlines() for v in values_list])
@ -384,9 +385,9 @@ class PubsubSchema(object):
elif xml_tools.isXHTMLField(field):
values_list = [generic.parseXml(v.encode("utf-8"))
for v in values_list]
elif u"jid" in (field.fieldType or u""):
elif "jid" in (field.fieldType or ""):
values_list = [jid.JID(v) for v in values_list]
if u"list" in (field.fieldType or u""):
if "list" in (field.fieldType or ""):
# for lists, we check that given values are allowed in form
allowed_values = [o.value for o in field.options]
values_list = [v for v in values_list if v in allowed_values]
@ -419,26 +420,26 @@ class PubsubSchema(object):
main use case is using a textbox for labels
"""
if widget_type != u"textbox":
if widget_type != "textbox":
return widget_type, args, kwargs
widget_type = u"list"
options = [o for o in args.pop(0).split(u"\n") if o]
widget_type = "list"
options = [o for o in args.pop(0).split("\n") if o]
kwargs = {
"options": options,
"name": kwargs.get("name"),
"styles": (u"noselect", u"extensible", u"reducible"),
"styles": ("noselect", "extensible", "reducible"),
}
return widget_type, args, kwargs
def dateFilter(self, form_xmlui, widget_type, args, kwargs):
"""Convert a string with a date to a unix timestamp"""
if widget_type != u"string" or not args[0]:
if widget_type != "string" or not args[0]:
return widget_type, args, kwargs
# we convert XMPP date to timestamp
try:
args[0] = unicode(date_utils.date_parse(args[0]))
args[0] = str(date_utils.date_parse(args[0]))
except Exception as e:
log.warning(_(u"Can't parse date field: {msg}").format(msg=e))
log.warning(_("Can't parse date field: {msg}").format(msg=e))
return widget_type, args, kwargs
## Helper methods ##
@ -479,7 +480,7 @@ class PubsubSchema(object):
# have to modify them
if C.bool(extra.get("labels_as_list", C.BOOL_FALSE)):
filters = filters.copy()
filters[u"labels"] = self.textbox2ListFilter
filters["labels"] = self.textbox2ListFilter
client, service, node, max_items, extra, sub_id = self.prepareBridgeGet(
service, node, max_items, sub_id, extra, profile_key
)
@ -536,7 +537,7 @@ class PubsubSchema(object):
item_elt = items_data[0][0]
except Exception as e:
log.warning(
_(u"Can't get previous item, update ignored: {reason}").format(
_("Can't get previous item, update ignored: {reason}").format(
reason=e
)
)
@ -546,13 +547,13 @@ class PubsubSchema(object):
if form is None:
log.warning(
_(
u"Can't parse previous item, update ignored: data form not found"
"Can't parse previous item, update ignored: data form not found"
).format(reason=e)
)
else:
for name, field in form.fields.iteritems():
for name, field in form.fields.items():
if name not in values:
values[name] = u"\n".join(unicode(v) for v in field.values)
values[name] = "\n".join(str(v) for v in field.values)
def _set(self, service, node, values, schema=None, item_id=None, extra=None,
default_node=None, form_ns=None, fill_author=True,
@ -578,7 +579,7 @@ class PubsubSchema(object):
default_node=default_node,
fill_author=fill_author,
)
d.addCallback(lambda ret: ret or u"")
d.addCallback(lambda ret: ret or "")
return d
@defer.inlineCallbacks
@ -605,15 +606,15 @@ class PubsubSchema(object):
extra = {}
if not node:
if default_node is None:
raise ValueError(_(u"default_node must be set if node is not set"))
raise ValueError(_("default_node must be set if node is not set"))
node = default_node
now = utils.xmpp_date()
if not item_id:
values["created"] = now
elif extra.get(u"update", False):
elif extra.get("update", False):
if item_id is None:
raise exceptions.DataError(
_(u'if extra["update"] is set, item_id must be set too')
_('if extra["update"] is set, item_id must be set too')
)
yield self.copyMissingValues(client, service, node, item_id, form_ns, values)
@ -630,8 +631,8 @@ class PubsubSchema(object):
defer.returnValue(item_id)
@implementer(iwokkel.IDisco)
class SchemaHandler(XMPPHandler):
implements(iwokkel.IDisco)
def getDiscoInfo(self, requestor, service, nodeIdentifier=""):
return [disco.DiscoFeature(NS_SCHEMA)]