XEP-0030 stanza: FIX PEP8, types

This commit is contained in:
mathieui 2021-03-01 20:55:27 +01:00
parent 10611525a0
commit e3027dabb2
2 changed files with 86 additions and 62 deletions

View File

@ -1,10 +1,19 @@
# Slixmpp: The Slick XMPP Library # Slixmpp: The Slick XMPP Library
# Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout # Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
# This file is part of Slixmpp. # This file is part of Slixmpp.
# See the file LICENSE for copying permission. # See the file LICENSE for copying permission.
from typing import (
Iterable,
List,
Optional,
Set,
Tuple,
Union,
)
from slixmpp.xmlstream import ElementBase, ET from slixmpp.xmlstream import ElementBase, ET
IdentityType = Tuple[str, str, Optional[str], Optional[str]]
class DiscoInfo(ElementBase): class DiscoInfo(ElementBase):
@ -18,21 +27,23 @@ class DiscoInfo(ElementBase):
category with a type of 'pc' to indicate the agent is a human operated category with a type of 'pc' to indicate the agent is a human operated
client with a GUI, or a category of 'gateway' with a type of 'aim' to client with a GUI, or a category of 'gateway' with a type of 'aim' to
identify the agent as a gateway for the legacy AIM protocol. See identify the agent as a gateway for the legacy AIM protocol. See
<http://xmpp.org/registrar/disco-categories.html> for a full list of `XMPP Registrar Disco Categories`_ for a full list of
accepted category and type combinations. accepted category and type combinations.
.. _XMPP Registrar Disco Categories: <http://xmpp.org/registrar/disco-categories.html>
Features are simply a set of the namespaces that identify the supported Features are simply a set of the namespaces that identify the supported
features. For example, a client that supports service discovery will features. For example, a client that supports service discovery will
include the feature 'http://jabber.org/protocol/disco#info'. include the feature ``http://jabber.org/protocol/disco#info``.
Since clients and components may operate in several roles at once, identity Since clients and components may operate in several roles at once, identity
and feature information may be grouped into "nodes". If one were to write and feature information may be grouped into "nodes". If one were to write
all of the identities and features used by a client, then node names would all of the identities and features used by a client, then node names would
be like section headings. be like section headings.
Example disco#info stanzas: Example disco#info stanza:
:: .. code-block:: xml
<iq type="get"> <iq type="get">
<query xmlns="http://jabber.org/protocol/disco#info" /> <query xmlns="http://jabber.org/protocol/disco#info" />
@ -46,30 +57,26 @@ class DiscoInfo(ElementBase):
<feature var="urn:xmpp:ping" /> <feature var="urn:xmpp:ping" />
</query> </query>
</iq> </iq>
Stanza Interface:
::
node -- The name of the node to either
query or return info from.
identities -- A set of 4-tuples, where each tuple contains
the category, type, xml:lang, and name
of an identity.
features -- A set of namespaces for features.
""" """
name = 'query' name = 'query'
namespace = 'http://jabber.org/protocol/disco#info' namespace = 'http://jabber.org/protocol/disco#info'
plugin_attrib = 'disco_info' plugin_attrib = 'disco_info'
#: Stanza interfaces:
#:
#: - ``node``: The name of the node to either query or return the info from
#: - ``identities``: A set of 4-tuples, where each tuple contains the
#: category, type, xml:lang and name of an identity
#: - ``features``: A set of namespaces for features
#:
interfaces = {'node', 'features', 'identities'} interfaces = {'node', 'features', 'identities'}
lang_interfaces = {'identities'} lang_interfaces = {'identities'}
# Cache identities and features # Cache identities and features
_identities = set() _identities: Set[Tuple[str, str, Optional[str]]]
_features = set() _features: Set[str]
def setup(self, xml=None): def setup(self, xml: Optional[ET.ElementTree] = None):
""" """
Populate the stanza object using an optional XML object. Populate the stanza object using an optional XML object.
@ -84,7 +91,9 @@ class DiscoInfo(ElementBase):
self._identities = {id[0:3] for id in self['identities']} self._identities = {id[0:3] for id in self['identities']}
self._features = self['features'] self._features = self['features']
def add_identity(self, category, itype, name=None, lang=None): def add_identity(self, category: str, itype: str,
name: Optional[str] = None, lang: Optional[str] = None
) -> bool:
""" """
Add a new identity element. Each identity must be unique Add a new identity element. Each identity must be unique
in terms of all four identity components. in terms of all four identity components.
@ -113,7 +122,8 @@ class DiscoInfo(ElementBase):
return True return True
return False return False
def del_identity(self, category, itype, name=None, lang=None): def del_identity(self, category: str, itype: str, name=None,
lang: Optional[str] = None) -> bool:
""" """
Remove a given identity. Remove a given identity.
@ -134,7 +144,8 @@ class DiscoInfo(ElementBase):
return True return True
return False return False
def get_identities(self, lang=None, dedupe=True): def get_identities(self, lang: Optional[str] = None, dedupe: bool = True
) -> Iterable[IdentityType]:
""" """
Return a set of all identities in tuple form as so: Return a set of all identities in tuple form as so:
@ -147,6 +158,7 @@ class DiscoInfo(ElementBase):
:param dedupe: If True, de-duplicate identities, otherwise :param dedupe: If True, de-duplicate identities, otherwise
return a list of all identities. return a list of all identities.
""" """
identities: Union[List[IdentityType], Set[IdentityType]]
if dedupe: if dedupe:
identities = set() identities = set()
else: else:
@ -158,13 +170,14 @@ class DiscoInfo(ElementBase):
id_xml.attrib['type'], id_xml.attrib['type'],
id_xml.attrib.get('{%s}lang' % self.xml_ns, None), id_xml.attrib.get('{%s}lang' % self.xml_ns, None),
id_xml.attrib.get('name', None)) id_xml.attrib.get('name', None))
if dedupe: if isinstance(identities, set):
identities.add(id) identities.add(id)
else: else:
identities.append(id) identities.append(id)
return identities return identities
def set_identities(self, identities, lang=None): def set_identities(self, identities: Iterable[IdentityType],
lang: Optional[str] = None):
""" """
Add or replace all identities. The identities must be a in set Add or replace all identities. The identities must be a in set
where each identity is a tuple of the form: where each identity is a tuple of the form:
@ -187,7 +200,7 @@ class DiscoInfo(ElementBase):
category, itype, lang, name = identity category, itype, lang, name = identity
self.add_identity(category, itype, name, lang) self.add_identity(category, itype, name, lang)
def del_identities(self, lang=None): def del_identities(self, lang: Optional[str] = None):
""" """
Remove all identities. If a language was specified, only Remove all identities. If a language was specified, only
remove identities using that language. remove identities using that language.
@ -204,7 +217,7 @@ class DiscoInfo(ElementBase):
id_xml.attrib.get('{%s}lang' % self.xml_ns, None))) id_xml.attrib.get('{%s}lang' % self.xml_ns, None)))
self.xml.remove(id_xml) self.xml.remove(id_xml)
def add_feature(self, feature): def add_feature(self, feature: str) -> bool:
""" """
Add a single, new feature. Add a single, new feature.
@ -218,7 +231,7 @@ class DiscoInfo(ElementBase):
return True return True
return False return False
def del_feature(self, feature): def del_feature(self, feature: str) -> bool:
""" """
Remove a single feature. Remove a single feature.
@ -232,20 +245,21 @@ class DiscoInfo(ElementBase):
return True return True
return False return False
def get_features(self, dedupe=True): def get_features(self, dedupe: bool = True) -> Iterable[str]:
"""Return the set of all supported features.""" """Return the set of all supported features."""
features: Union[List[str], Set[str]]
if dedupe: if dedupe:
features = set() features = set()
else: else:
features = [] features = []
for feature_xml in self.xml.findall('{%s}feature' % self.namespace): for feature_xml in self.xml.findall('{%s}feature' % self.namespace):
if dedupe: if isinstance(features, set):
features.add(feature_xml.attrib['var']) features.add(feature_xml.attrib['var'])
else: else:
features.append(feature_xml.attrib['var']) features.append(feature_xml.attrib['var'])
return features return features
def set_features(self, features): def set_features(self, features: Iterable[str]):
""" """
Add or replace the set of supported features. Add or replace the set of supported features.

View File

@ -1,9 +1,34 @@
# Slixmpp: The Slick XMPP Library # Slixmpp: The Slick XMPP Library
# Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout # Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
# This file is part of Slixmpp. # This file is part of Slixmpp.
# See the file LICENSE for copying permission. # See the file LICENSE for copying permission.
from slixmpp.xmlstream import ElementBase, register_stanza_plugin from typing import (
Iterable,
Optional,
Set,
Tuple,
)
from slixmpp import JID
from slixmpp.xmlstream import (
ElementBase,
ET,
register_stanza_plugin,
)
class DiscoItem(ElementBase):
name = 'item'
namespace = 'http://jabber.org/protocol/disco#items'
plugin_attrib = name
interfaces = {'jid', 'node', 'name'}
def get_node(self) -> Optional[str]:
"""Return the item's node name or ``None``."""
return self._get_attr('node', None)
def get_name(self) -> Optional[str]:
"""Return the item's human readable name, or ``None``."""
return self._get_attr('name', None)
class DiscoItems(ElementBase): class DiscoItems(ElementBase):
@ -11,7 +36,7 @@ class DiscoItems(ElementBase):
""" """
Example disco#items stanzas: Example disco#items stanzas:
:: .. code-block:: xml
<iq type="get"> <iq type="get">
<query xmlns="http://jabber.org/protocol/disco#items" /> <query xmlns="http://jabber.org/protocol/disco#items" />
@ -28,25 +53,24 @@ class DiscoItems(ElementBase):
</query> </query>
</iq> </iq>
Stanza Interface:
::
node -- The name of the node to either
query or return info from.
items -- A list of 3-tuples, where each tuple contains
the JID, node, and name of an item.
""" """
name = 'query' name = 'query'
namespace = 'http://jabber.org/protocol/disco#items' namespace = 'http://jabber.org/protocol/disco#items'
plugin_attrib = 'disco_items' plugin_attrib = 'disco_items'
#: Stanza Interface:
#:
#: - ``node``: The name of the node to either
#: query or return info from.
#: - ``items``: A list of 3-tuples, where each tuple contains
#: the JID, node, and name of an item.
#:
interfaces = {'node', 'items'} interfaces = {'node', 'items'}
# Cache items # Cache items
_items = set() _items: Set[Tuple[JID, Optional[str]]]
def setup(self, xml=None): def setup(self, xml: Optional[ET.ElementTree] = None):
""" """
Populate the stanza object using an optional XML object. Populate the stanza object using an optional XML object.
@ -59,7 +83,8 @@ class DiscoItems(ElementBase):
ElementBase.setup(self, xml) ElementBase.setup(self, xml)
self._items = {item[0:2] for item in self['items']} self._items = {item[0:2] for item in self['items']}
def add_item(self, jid, node=None, name=None): def add_item(self, jid: JID, node: Optional[str] = None,
name: Optional[str] = None):
""" """
Add a new item element. Each item is required to have a Add a new item element. Each item is required to have a
JID, but may also specify a node value to reference JID, but may also specify a node value to reference
@ -80,7 +105,7 @@ class DiscoItems(ElementBase):
return True return True
return False return False
def del_item(self, jid, node=None): def del_item(self, jid: JID, node: Optional[str] = None) -> bool:
""" """
Remove a single item. Remove a single item.
@ -96,7 +121,7 @@ class DiscoItems(ElementBase):
return True return True
return False return False
def get_items(self): def get_items(self) -> Set[DiscoItem]:
"""Return all items.""" """Return all items."""
items = set() items = set()
for item in self['substanzas']: for item in self['substanzas']:
@ -104,7 +129,7 @@ class DiscoItems(ElementBase):
items.add((item['jid'], item['node'], item['name'])) items.add((item['jid'], item['node'], item['name']))
return items return items
def set_items(self, items): def set_items(self, items: Iterable[DiscoItem]):
""" """
Set or replace all items. The given items must be in a Set or replace all items. The given items must be in a
list or set where each item is a tuple of the form: list or set where each item is a tuple of the form:
@ -127,19 +152,4 @@ class DiscoItems(ElementBase):
self.iterables.remove(item) self.iterables.remove(item)
class DiscoItem(ElementBase):
name = 'item'
namespace = 'http://jabber.org/protocol/disco#items'
plugin_attrib = name
interfaces = {'jid', 'node', 'name'}
def get_node(self):
"""Return the item's node name or ``None``."""
return self._get_attr('node', None)
def get_name(self):
"""Return the item's human readable name, or ``None``."""
return self._get_attr('name', None)
register_stanza_plugin(DiscoItems, DiscoItem, iterable=True) register_stanza_plugin(DiscoItems, DiscoItem, iterable=True)