class UserExt(xso.XSO): TAG = (namespaces.xep0045_muc_user, "x") status_codes = xso.ChildValueList(StatusCodeList(), container_type=set) destroy = xso.Child([DestroyNotification]) decline = xso.Child([Decline]) invites = xso.ChildList([Invite]) items = xso.ChildList([UserItem]) password = xso.ChildText((namespaces.xep0045_muc_user, "password"), default=None) def __init__(self, status_codes=[], destroy=None, decline=None, invites=[], items=[], password=None): super().__init__() self.status_codes.update(status_codes) self.destroy = destroy self.decline = decline self.invites.extend(invites) self.items.extend(items) self.password = password
class HashesUsedParent(xso.XSO): """ Mix-in class for XSOs which use :class:`HashUsed` children. .. attribute:: algos A list of hash algorithms. """ algos = xso.ChildValueList(type_=HashUsedType(), )
class Data(AbstractItem): """ A :xep:`4` ``x`` element, that is, a Data Form. :param type_: Initial value for the :attr:`type_` attribute. .. attribute:: type_ The ``type`` attribute of the form, represented by one of the members of the :class:`DataType` enumeration. .. attribute:: title The (optional) title of the form. Either a :class:`str` or :data:`None`. .. attribute:: instructions A sequence of strings which represent the instructions elements on the form. .. attribute:: fields If the :class:`Data` is a form, this is a sequence of :class:`Field` elements which represent the fields to be filled in. This does not make sense on :attr:`.DataType.RESULT` typed objects. .. attribute:: items If the :class:`Data` is a table, this is a sequence of :class:`Item` instances which represent the table rows. This only makes sense on :attr:`.DataType.RESULT` typed objects. .. attribute:: reported If the :class:`Data` is a table, this is a :class:`Reported` object representing the table header. This only makes sense on :attr:`.DataType.RESULT` typed objects. .. automethod:: get_form_type """ TAG = (namespaces.xep0004_data, "x") type_ = xso.Attr("type", type_=xso.EnumCDataType(DataType)) title = xso.ChildText( (namespaces.xep0004_data, "title"), default=None, ) instructions = xso.ChildValueList(type_=InstructionsElement()) items = xso.ChildList([Item]) reported = xso.Child([Reported], required=False) def __init__(self, type_): super().__init__() self.type_ = type_ def _validate_result(self): if self.fields: raise ValueError("field in report result") fieldvars = {field.var for field in self.reported.fields} if not fieldvars: raise ValueError("empty report header") for item in self.items: itemvars = {field.var for field in item.fields} if itemvars != fieldvars: raise ValueError("field mismatch between row and header") def validate(self): super().validate() if (self.type_ != DataType.RESULT and (self.reported is not None or self.items)): raise ValueError("report in non-result") if (self.type_ == DataType.RESULT and (self.reported is not None or self.items)): self._validate_result() def get_form_type(self): """ Extract the ``FORM_TYPE`` from the fields. :return: ``FORM_TYPE`` value or :data:`None` :rtype: :class:`str` or :data:`None` Return :data:`None` if no well-formed ``FORM_TYPE`` field is found in the list of fields. .. versionadded:: 0.8 """ for field in self.fields: if field.var == "FORM_TYPE" and field.type_ == FieldType.HIDDEN: if len(field.values) != 1: return None return field.values[0]
class Field(xso.XSO): """ Represent a single field in a Data Form. :param type_: Field type, must be one of the valid field types specified in :xep:`4`. :type type_: :class:`FieldType` :param options: A mapping of values to labels defining the options in a ``list-*`` field. :type options: :class:`dict` mapping :class:`str` to :class:`str` :param values: A sequence of values currently given for the field. Having more than one value is only valid in ``*-multi`` fields. :type values: :class:`list` of :class:`str` :param desc: Description which can be shown in a tool-tip or similar, without newlines. :type desc: :class:`str` or :data:`None`s :param label: Human-readable label to be shown next to the field input :type label: :class:`str` or :data:`None` :param required: Flag to indicate that the field is required :type required: :class:`bool` :param var: "ID" identifying the field uniquely inside the form. Only required for fields carrying a meaning (thus, not for ``fixed``). :type var: :class:`str` or :data:`None` The semantics of a :class:`Field` are different depending on where it occurs: in a :class:`Data`, it is a form field to be filled in, in a :class:`Item` it is a cell of a row and in a :class:`Reported` it represents a column header. .. attribute:: required A boolean flag indicating whether the field is required. If true, the XML serialisation will contain the corresponding ``<required/>`` tag. .. attribute:: desc Single line of description for the field. This attribute represents the ``<desc/>`` element from :xep:`4`. .. attribute:: values A sequence of strings representing the ``<value/>`` elements of the field, one string for each value. .. note:: Since the requirements on the sequence of strings in :attr:`values` change depending on the :attr:`type_` attribute, validation and type conversion on assignment is very lax. The attribute accepts all sequences of strings, even if the field is for example a :attr:`FieldType.BOOLEAN` field, which allows for at most one string of a well-defined format (see the documentation there for the details). This makes it easy to inadvertendly generate invalid forms, which is why you should be using :class:`Form` subclasses when accessing forms from within normal code and some other, generic mechanism taking care of these details when showing forms in a UI framework to users. Note that devising such a mechanism is out of scope for :mod:`aioxmpp`, as every UI framework has different requirements. .. attribute:: options A dictionary mapping values to human-readable labels, representing the ``<option/>`` elements of the field. .. attribute:: var The uniquely identifying string of the (valued, that is, non-:attr:`FieldType.FIXED` field). Represents the ``var`` attribute of the field. .. attribute:: type_ The type of the field. The :attr:`type_` must be a :class:`FieldType` enumeration value and determines restrictions and constraints on other attributes. See the :class:`FieldType` enumeration and :xep:`4` for details. .. attribute:: label The human-readable label for the field, representing the ``label`` attribute of the field. May be :data:`None` if the label is omitted. """ TAG = (namespaces.xep0004_data, "field") required = xso.ChildFlag((namespaces.xep0004_data, "required"), ) desc = xso.ChildText((namespaces.xep0004_data, "desc"), default=None) values = xso.ChildValueList(type_=ValueElement()) options = xso.ChildValueMap( type_=OptionElement(), mapping_type=collections.OrderedDict, ) var = xso.Attr((None, "var"), default=None) type_ = xso.Attr( (None, "type"), type_=xso.EnumCDataType(FieldType, ), default=FieldType.TEXT_SINGLE, ) label = xso.Attr((None, "label"), default=None) def __init__(self, *, type_=FieldType.TEXT_SINGLE, options={}, values=[], desc=None, label=None, required=False, var=None): super().__init__() self.type_ = type_ self.options.update(options) self.values[:] = values self.desc = desc self.label = label self.required = required self.var = var def validate(self): super().validate() if self.type_ != FieldType.FIXED and not self.var: raise ValueError("missing attribute var") if not self.type_.has_options and self.options: raise ValueError("unexpected option on non-list field") if not self.type_.is_multivalued and len(self.values) > 1: raise ValueError("too many values on non-multi field") values_list = [opt for opt in self.options.values()] values_set = set(values_list) if len(values_list) != len(values_set): raise ValueError("duplicate option value")
class InfoQuery(xso.CapturingXSO): """ A query for features and identities of an entity. The keyword arguments to the constructor can be used to initialize the attributes. Note that `identities` and `features` must be iterables of :class:`Identity` and :class:`Feature`, respectively; these iterables are evaluated and the items are stored in the respective attributes. .. attribute:: node The node at which the query is directed. .. attribute:: identities The identities of the entity, as :class:`Identity` instances. Each entity has at least one identity. .. attribute:: features The features of the entity, as a set of strings. Each string represents a :class:`Feature` instance with the corresponding :attr:`~.Feature.var` attribute. .. attribute:: captured_events If the object was created by parsing an XML stream, this attribute holds a list of events which were used when parsing it. Otherwise, this is :data:`None`. .. versionadded:: 0.5 .. automethod:: to_dict """ __slots__ = ("captured_events", ) TAG = (namespaces.xep0030_info, "query") node = xso.Attr(tag="node", default=None) identities = xso.ChildList([Identity]) features = xso.ChildValueList(FeatureSet(), container_type=set) exts = xso.ChildList([forms_xso.Data]) def __init__(self, *, identities=(), features=(), node=None): super().__init__() self.captured_events = None self.identities.extend(identities) self.features.update(features) if node is not None: self.node = node def to_dict(self): """ Convert the query result to a normalized JSON-like representation. The format is a subset of the format used by the `capsdb`__. Obviously, the node name and hash type are not included; otherwise, the format is identical. __ https://github.com/xnyhps/capsdb """ identities = [] for identity in self.identities: identity_dict = { "category": identity.category, "type": identity.type_, } if identity.lang is not None: identity_dict["lang"] = identity.lang.match_str if identity.name is not None: identity_dict["name"] = identity.name identities.append(identity_dict) features = sorted(self.features) forms = [] for form in self.exts: forms.append({ field.var: list(field.values) for field in form.fields if field.var is not None }) result = { "identities": identities, "features": features, "forms": forms } return result def _set_captured_events(self, events): self.captured_events = events
class Field(xso.XSO): TAG = (namespaces.xep0004_data, "field") required = xso.ChildTag( tags=[ (namespaces.xep0004_data, "required"), ], allow_none=True) desc = xso.ChildText( (namespaces.xep0004_data, "desc"), default=None ) values = xso.ChildValueList( type_=ValueElement() ) options = xso.ChildValueMap( type_=OptionElement() ) var = xso.Attr( (None, "var"), default=None ) type_ = xso.Attr( (None, "type"), validator=xso.RestrictToSet([ "boolean", "fixed", "hidden", "jid-multi", "jid-single", "list-multi", "list-single", "text-multi", "text-private", "text-single", ]), default="text-single" ) label = xso.Attr( (None, "label"), default=None ) def __init__(self, *, type_="text-single", options={}, values=[], desc=None, label=None, required=False, var=None): super().__init__() self.type_ = type_ self.options.update(options) self.values[:] = values self.desc = desc self.label = label if required: self.required = (namespaces.xep0004_data, "required") self.var = var def validate(self): super().validate() if self.type_ != "fixed" and not self.var: raise ValueError("missing attribute var") if not self.type_.startswith("list-") and self.options: raise ValueError("unexpected option on non-list field") if not self.type_.endswith("-multi") and len(self.values) > 1: raise ValueError("too many values on non-multi field") values_list = [opt for opt in self.options.values()] values_set = set(values_list) if len(values_list) != len(values_set): raise ValueError("duplicate option value")