v1.3.5
This commit is contained in:
@@ -0,0 +1,785 @@
|
||||
########################################################################
|
||||
# File name: nonza.py
|
||||
# This file is part of: aioxmpp
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this program. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
########################################################################
|
||||
"""
|
||||
:mod:`~aioxmpp.nonza` --- Non-stanza stream-level XSOs (Nonzas)
|
||||
###############################################################
|
||||
|
||||
This module contains XSO models for stream-level elements which are not
|
||||
stanzas. Since :xep:`0360`, these are called "nonzas".
|
||||
|
||||
.. versionchanged:: 0.5
|
||||
|
||||
Before version 0.5, this module was called :mod:`aioxmpp.stream_xsos`.
|
||||
|
||||
General XSOs
|
||||
============
|
||||
|
||||
.. autoclass:: StreamError()
|
||||
|
||||
.. autoclass:: StreamFeatures()
|
||||
|
||||
StartTLS related XSOs
|
||||
=====================
|
||||
|
||||
.. autoclass:: StartTLSXSO()
|
||||
|
||||
.. autoclass:: StartTLSFeature()
|
||||
|
||||
.. autoclass:: StartTLS()
|
||||
|
||||
.. autoclass:: StartTLSProceed()
|
||||
|
||||
.. autoclass:: StartTLSFailure()
|
||||
|
||||
SASL related XSOs
|
||||
=================
|
||||
|
||||
.. autoclass:: SASLXSO
|
||||
|
||||
.. autoclass:: SASLAuth
|
||||
|
||||
.. autoclass:: SASLChallenge
|
||||
|
||||
.. autoclass:: SASLResponse
|
||||
|
||||
.. autoclass:: SASLFailure
|
||||
|
||||
.. autoclass:: SASLSuccess
|
||||
|
||||
.. autoclass:: SASLAbort
|
||||
|
||||
Stream management related XSOs
|
||||
==============================
|
||||
|
||||
.. autoclass:: SMXSO()
|
||||
|
||||
.. autoclass:: SMRequest()
|
||||
|
||||
.. autoclass:: SMAcknowledgement()
|
||||
|
||||
.. autoclass:: SMEnable()
|
||||
|
||||
.. autoclass:: SMEnabled()
|
||||
|
||||
.. autoclass:: SMResume()
|
||||
|
||||
.. autoclass:: SMResumed()
|
||||
|
||||
"""
|
||||
import itertools
|
||||
import warnings
|
||||
|
||||
from . import xso, errors
|
||||
|
||||
from .utils import namespaces
|
||||
|
||||
|
||||
class StreamError(xso.XSO):
|
||||
"""
|
||||
XSO representing a stream error.
|
||||
|
||||
.. attribute:: text
|
||||
|
||||
The text content of the stream error.
|
||||
|
||||
.. attribute:: condition
|
||||
|
||||
The RFC 6120 stream error condition.
|
||||
|
||||
"""
|
||||
|
||||
TAG = (namespaces.xmlstream, "error")
|
||||
|
||||
DECLARE_NS = {}
|
||||
|
||||
text = xso.ChildText(
|
||||
tag=(namespaces.streams, "text"),
|
||||
attr_policy=xso.UnknownAttrPolicy.DROP,
|
||||
default=None,
|
||||
declare_prefix=None
|
||||
)
|
||||
|
||||
condition_obj = xso.Child(
|
||||
[member.xso_class for member in errors.StreamErrorCondition],
|
||||
required=True,
|
||||
)
|
||||
|
||||
def __init__(self,
|
||||
condition=errors.StreamErrorCondition.UNDEFINED_CONDITION,
|
||||
text=None):
|
||||
super().__init__()
|
||||
if not isinstance(condition, errors.StreamErrorCondition):
|
||||
condition = errors.StreamErrorCondition(condition)
|
||||
warnings.warn(
|
||||
"as of aioxmpp 1.0, stream error conditions must be members "
|
||||
"of the aioxmpp.errors.StreamErrorCondition enumeration",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
self.condition_obj = condition.xso_class()
|
||||
self.text = text
|
||||
|
||||
@property
|
||||
def condition(self):
|
||||
return self.condition_obj.enum_member
|
||||
|
||||
@condition.setter
|
||||
def condition(self, value):
|
||||
if not isinstance(value, errors.StreamErrorCondition):
|
||||
value = errors.StreamErrorCondition(value)
|
||||
warnings.warn(
|
||||
"as of aioxmpp 1.0, stream error conditions must be members "
|
||||
"of the aioxmpp.errors.StreamErrorCondition enumeration",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if self.condition == value:
|
||||
return
|
||||
self.condition_obj = value.xso_class()
|
||||
|
||||
@classmethod
|
||||
def from_exception(cls, exc):
|
||||
instance = cls()
|
||||
instance.text = exc.text
|
||||
instance.condition = exc.condition
|
||||
return instance
|
||||
|
||||
def to_exception(self):
|
||||
return errors.StreamError(
|
||||
condition=self.condition,
|
||||
text=self.text
|
||||
)
|
||||
|
||||
|
||||
class StreamFeatures(xso.XSO):
|
||||
"""
|
||||
XSO for collecting the supported stream features the remote advertises.
|
||||
|
||||
To register a stream feature, use :meth:`register_child` with the
|
||||
:attr:`features` descriptor. A more fancy way to do the same thing is to
|
||||
use the :meth:`as_feature_class` classmethod as decorator for your feature
|
||||
XSO class.
|
||||
|
||||
Adding new feature classes:
|
||||
|
||||
.. automethod:: as_feature_class
|
||||
|
||||
Querying features:
|
||||
|
||||
.. method:: stream_features[FeatureClass]
|
||||
|
||||
Obtain the first feature XSO which matches the `FeatureClass`. If no
|
||||
such XSO is contained in the :class:`StreamFeatures` instance
|
||||
`stream_features`, :class:`KeyError` is raised.
|
||||
|
||||
.. method:: stream_features[FeatureClass] = feature
|
||||
|
||||
Replace the stream features belonging to the given `FeatureClass` with
|
||||
the `feature` XSO.
|
||||
|
||||
If the `FeatureClass` does not match the type of the `feature` XSO, a
|
||||
:class:`TypeError` is raised.
|
||||
|
||||
It is legal to leave the FeatureClass out by specifying ``...``
|
||||
instead. In that case, the class is auto-detected from the `feature`
|
||||
object assigned.
|
||||
|
||||
.. method:: del stream_features[FeatureClass]
|
||||
|
||||
If any feature of the given `FeatureClass` type is in the
|
||||
`stream_features`, they are all removed.
|
||||
|
||||
Otherwise, :class:`KeyError` is raised, to stay consistent with other
|
||||
mapping-like types.
|
||||
|
||||
.. automethod:: get_feature
|
||||
|
||||
.. automethod:: has_feature
|
||||
|
||||
"""
|
||||
|
||||
TAG = (namespaces.xmlstream, "features")
|
||||
DECLARE_NS = {
|
||||
None: namespaces.xmlstream
|
||||
}
|
||||
|
||||
# we drop unknown children
|
||||
UNKNOWN_CHILD_POLICY = xso.UnknownChildPolicy.DROP
|
||||
|
||||
features = xso.ChildMap([])
|
||||
cruft = xso.Collector()
|
||||
|
||||
@classmethod
|
||||
def as_feature_class(cls, other_cls):
|
||||
cls.register_child(cls.features, other_cls)
|
||||
return other_cls
|
||||
|
||||
@classmethod
|
||||
def is_feature(cls, other_cls):
|
||||
return (cls.CHILD_MAP.get(other_cls.TAG, None) is
|
||||
cls.features.xq_descriptor)
|
||||
|
||||
def __getitem__(self, feature_cls):
|
||||
try:
|
||||
return self.features[feature_cls.TAG][0]
|
||||
except IndexError:
|
||||
raise KeyError(feature_cls) from None
|
||||
|
||||
def __setitem__(self, feature_cls, feature):
|
||||
if feature_cls is Ellipsis:
|
||||
feature_cls = type(feature)
|
||||
if not isinstance(feature, feature_cls):
|
||||
raise ValueError("incorrect XSO class supplied")
|
||||
self.features[feature_cls.TAG][:] = [feature]
|
||||
|
||||
def __delitem__(self, feature_cls):
|
||||
items = self.features[feature_cls.TAG]
|
||||
if not items:
|
||||
raise KeyError(feature_cls)
|
||||
items.clear()
|
||||
|
||||
def __contains__(self, other):
|
||||
raise TypeError("membership test not supported")
|
||||
|
||||
def has_feature(self, feature_cls):
|
||||
"""
|
||||
Return :data:`True` if the stream features contain a feature of the
|
||||
given `feature_cls` type. :data:`False` is returned otherwise.
|
||||
"""
|
||||
return feature_cls.TAG in self.features
|
||||
|
||||
def get_feature(self, feature_cls, default=None):
|
||||
"""
|
||||
If a feature of the given `feature_cls` type is contained in the
|
||||
current stream features set, the first such instance is returned.
|
||||
|
||||
Otherwise, `default` is returned.
|
||||
"""
|
||||
try:
|
||||
return self[feature_cls]
|
||||
except KeyError:
|
||||
return default
|
||||
|
||||
def __iter__(self):
|
||||
return itertools.chain(*self.features.values())
|
||||
|
||||
|
||||
class StartTLSXSO(xso.XSO):
|
||||
"""
|
||||
Base class for starttls related XSOs.
|
||||
|
||||
This base class merely defines the namespaces to declare when serialising
|
||||
the derived XSOs
|
||||
"""
|
||||
|
||||
DECLARE_NS = {None: namespaces.starttls}
|
||||
|
||||
|
||||
@StreamFeatures.as_feature_class
|
||||
class StartTLSFeature(StartTLSXSO):
|
||||
"""
|
||||
Start TLS capability stream feature
|
||||
"""
|
||||
|
||||
TAG = (namespaces.starttls, "starttls")
|
||||
|
||||
class Required(xso.XSO):
|
||||
TAG = (namespaces.starttls, "required")
|
||||
|
||||
required = xso.Child([Required], required=False)
|
||||
|
||||
|
||||
class StartTLS(StartTLSXSO):
|
||||
"""
|
||||
XSO indicating that the client wants to start TLS now.
|
||||
"""
|
||||
|
||||
TAG = (namespaces.starttls, "starttls")
|
||||
|
||||
|
||||
class StartTLSFailure(StartTLSXSO):
|
||||
"""
|
||||
Server refusing to start TLS.
|
||||
"""
|
||||
TAG = (namespaces.starttls, "failure")
|
||||
|
||||
|
||||
class StartTLSProceed(StartTLSXSO):
|
||||
"""
|
||||
Server allows start TLS.
|
||||
"""
|
||||
TAG = (namespaces.starttls, "proceed")
|
||||
|
||||
|
||||
class SASLXSO(xso.XSO):
|
||||
DECLARE_NS = {
|
||||
None: namespaces.sasl
|
||||
}
|
||||
|
||||
|
||||
class SASLAuth(SASLXSO):
|
||||
"""
|
||||
Start SASL authentication.
|
||||
|
||||
.. attribute:: mechanism
|
||||
|
||||
The mechanism to authenticate with.
|
||||
|
||||
.. attribute:: payload
|
||||
|
||||
For mechanisms which use an initial client-supplied payload, this can be
|
||||
a string. It is automatically encoded as base64 according to the XMPP
|
||||
SASL specification.
|
||||
|
||||
"""
|
||||
|
||||
TAG = (namespaces.sasl, "auth")
|
||||
|
||||
mechanism = xso.Attr("mechanism")
|
||||
payload = xso.Text(
|
||||
type_=xso.Base64Binary(empty_as_equal=True),
|
||||
default=None
|
||||
)
|
||||
|
||||
def __init__(self, mechanism, payload=None):
|
||||
super().__init__()
|
||||
self.mechanism = mechanism
|
||||
self.payload = payload
|
||||
|
||||
|
||||
class SASLChallenge(SASLXSO):
|
||||
"""
|
||||
A SASL challenge.
|
||||
|
||||
.. attribute:: payload
|
||||
|
||||
The (decoded) SASL payload as :class:`bytes`. Base64 en/decoding is
|
||||
handled by the XSO stack.
|
||||
|
||||
"""
|
||||
|
||||
TAG = (namespaces.sasl, "challenge")
|
||||
|
||||
payload = xso.Text(
|
||||
type_=xso.Base64Binary(empty_as_equal=True),
|
||||
)
|
||||
|
||||
def __init__(self, payload):
|
||||
super().__init__()
|
||||
self.payload = payload
|
||||
|
||||
|
||||
class SASLResponse(SASLXSO):
|
||||
"""
|
||||
A SASL response.
|
||||
|
||||
.. attribute:: payload
|
||||
|
||||
The (decoded) SASL payload as :class:`bytes`. Base64 en/decoding is
|
||||
handled by the XSO stack.
|
||||
|
||||
"""
|
||||
|
||||
TAG = (namespaces.sasl, "response")
|
||||
|
||||
payload = xso.Text(
|
||||
type_=xso.Base64Binary(empty_as_equal=True)
|
||||
)
|
||||
|
||||
def __init__(self, payload):
|
||||
super().__init__()
|
||||
self.payload = payload
|
||||
|
||||
|
||||
class SASLFailure(SASLXSO):
|
||||
"""
|
||||
Indication of SASL failure.
|
||||
|
||||
.. attribute:: condition
|
||||
|
||||
The condition which caused the authentication to fail.
|
||||
|
||||
.. attribute:: text
|
||||
|
||||
Optional human-readable text.
|
||||
|
||||
"""
|
||||
|
||||
TAG = (namespaces.sasl, "failure")
|
||||
|
||||
condition = xso.ChildTag(
|
||||
tags=[
|
||||
"aborted",
|
||||
"account-disabled",
|
||||
"credentials-expired",
|
||||
"encryption-required",
|
||||
"incorrect-encoding",
|
||||
"invalid-authzid",
|
||||
"invalid-mechanism",
|
||||
"malformed-request",
|
||||
"mechanism-too-weak",
|
||||
"not-authorized",
|
||||
"temporary-auth-failure",
|
||||
],
|
||||
default_ns=namespaces.sasl,
|
||||
allow_none=False,
|
||||
declare_prefix=None,
|
||||
)
|
||||
|
||||
text = xso.ChildText(
|
||||
(namespaces.sasl, "text"),
|
||||
attr_policy=xso.UnknownAttrPolicy.DROP,
|
||||
default=None,
|
||||
declare_prefix=None
|
||||
)
|
||||
|
||||
def __init__(self, condition=(namespaces.sasl, "temporary-auth-failure")):
|
||||
super().__init__()
|
||||
self.condition = condition
|
||||
|
||||
|
||||
class SASLSuccess(SASLXSO):
|
||||
"""
|
||||
Indication of SASL success, with optional final payload supplied by the
|
||||
server.
|
||||
|
||||
.. attribute:: payload
|
||||
|
||||
The (decoded) SASL payload. Base64 en/decoding is handled by the XSO
|
||||
stack.
|
||||
|
||||
"""
|
||||
|
||||
TAG = (namespaces.sasl, "success")
|
||||
|
||||
payload = xso.Text(
|
||||
type_=xso.Base64Binary(empty_as_equal=True),
|
||||
default=None
|
||||
)
|
||||
|
||||
|
||||
class SASLAbort(SASLXSO):
|
||||
"""
|
||||
Request to abort the SASL authentication.
|
||||
"""
|
||||
TAG = (namespaces.sasl, "abort")
|
||||
|
||||
|
||||
class SMXSO(xso.XSO):
|
||||
"""
|
||||
Base class for stream-management related XSOs.
|
||||
|
||||
This base class merely defines the namespaces to declare when serializing
|
||||
the data.
|
||||
"""
|
||||
|
||||
DECLARE_NS = {
|
||||
None: namespaces.stream_management
|
||||
}
|
||||
|
||||
|
||||
@StreamFeatures.as_feature_class
|
||||
class StreamManagementFeature(SMXSO):
|
||||
"""
|
||||
Stream management stream feature
|
||||
"""
|
||||
TAG = (namespaces.stream_management, "sm")
|
||||
|
||||
class Required(xso.XSO):
|
||||
TAG = (namespaces.stream_management, "required")
|
||||
|
||||
class Optional(xso.XSO):
|
||||
TAG = (namespaces.stream_management, "optional")
|
||||
|
||||
required = xso.Child([Required])
|
||||
optional = xso.Child([Optional])
|
||||
|
||||
|
||||
class SMRequest(SMXSO):
|
||||
"""
|
||||
A request for an SM acknowledgement (see :class:`SMAcknowledgement`).
|
||||
"""
|
||||
TAG = (namespaces.stream_management, "r")
|
||||
|
||||
|
||||
class SMAcknowledgement(SMXSO):
|
||||
"""
|
||||
Response to a :class:`SMRequest`.
|
||||
|
||||
.. attribute:: counter
|
||||
|
||||
The counter as received by the remote side.
|
||||
|
||||
"""
|
||||
|
||||
TAG = (namespaces.stream_management, "a")
|
||||
|
||||
counter = xso.Attr(
|
||||
"h",
|
||||
type_=xso.Integer()
|
||||
)
|
||||
|
||||
def __init__(self, counter=0, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.counter = counter
|
||||
|
||||
def __repr__(self):
|
||||
return "<{}.{} counter={} at 0x{:x}>".format(
|
||||
type(self).__module__,
|
||||
type(self).__qualname__,
|
||||
self.counter,
|
||||
id(self),
|
||||
)
|
||||
|
||||
|
||||
class SMEnable(SMXSO):
|
||||
"""
|
||||
Request to enable stream management.
|
||||
|
||||
.. attribute:: resume
|
||||
|
||||
Set this to :data:`True` to request the capability of resuming the
|
||||
stream later.
|
||||
|
||||
.. attribute:: max_
|
||||
|
||||
Maximum time, as integer in seconds, for which the stream should be
|
||||
resumable after the connection dropped. Only relevant if
|
||||
:attr:`resume` is true and may be overridden by the server.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
|
||||
"""
|
||||
|
||||
TAG = (namespaces.stream_management, "enable")
|
||||
|
||||
resume = xso.Attr(
|
||||
"resume",
|
||||
type_=xso.Bool(),
|
||||
default=False
|
||||
)
|
||||
|
||||
max_ = xso.Attr(
|
||||
"max",
|
||||
type_=xso.Integer(),
|
||||
default=None,
|
||||
validate=xso.ValidateMode.ALWAYS,
|
||||
validator=xso.NumericRange(min_=0),
|
||||
)
|
||||
|
||||
def __init__(self, resume=False, max_=None):
|
||||
super().__init__()
|
||||
self.resume = resume
|
||||
self.max_ = max_
|
||||
|
||||
def __repr__(self):
|
||||
return "<{}.{} resume={} max={} at 0x{:x}>".format(
|
||||
type(self).__module__,
|
||||
type(self).__qualname__,
|
||||
self.resume,
|
||||
self.max_,
|
||||
id(self),
|
||||
)
|
||||
|
||||
|
||||
class SMEnabled(SMXSO):
|
||||
"""
|
||||
Response to a :class:`SMEnable` request.
|
||||
|
||||
.. attribute:: resume
|
||||
|
||||
If :data:`True`, the peer allows resumption of the stream.
|
||||
|
||||
.. attribute:: id_
|
||||
|
||||
The SM-ID of the stream. This is required to resume later.
|
||||
|
||||
.. attribute:: location
|
||||
|
||||
A hostname-port pair which defines to which host the client shall
|
||||
connect to resume the stream.
|
||||
|
||||
.. attribute:: max_
|
||||
|
||||
Maximum time, as integer in seconds, for which the stream will be
|
||||
resumable after the connection dropped. Only relevant if
|
||||
:attr:`resume` is true.
|
||||
|
||||
"""
|
||||
TAG = (namespaces.stream_management, "enabled")
|
||||
|
||||
resume = xso.Attr(
|
||||
"resume",
|
||||
type_=xso.Bool(),
|
||||
default=False
|
||||
)
|
||||
|
||||
id_ = xso.Attr("id", default=None)
|
||||
|
||||
location = xso.Attr(
|
||||
"location",
|
||||
type_=xso.ConnectionLocation(),
|
||||
default=None
|
||||
)
|
||||
|
||||
max_ = xso.Attr(
|
||||
"max",
|
||||
type_=xso.Integer(),
|
||||
default=None,
|
||||
validate=xso.ValidateMode.ALWAYS,
|
||||
validator=xso.NumericRange(min_=0),
|
||||
)
|
||||
|
||||
def __init__(self,
|
||||
resume=False,
|
||||
id_=None,
|
||||
location=None,
|
||||
max_=None):
|
||||
super().__init__()
|
||||
self.resume = resume
|
||||
self.id_ = id_
|
||||
self.location = location
|
||||
self.max_ = max_
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
"<{}.{} resume={} id={!r} location={!r} max={} at 0x{:x}>".format(
|
||||
type(self).__module__,
|
||||
type(self).__qualname__,
|
||||
self.resume,
|
||||
self.id_,
|
||||
self.location,
|
||||
self.max_,
|
||||
id(self),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class SMResume(SMXSO):
|
||||
"""
|
||||
Request resumption of a previously interrupted SM stream.
|
||||
|
||||
.. attribute:: counter
|
||||
|
||||
Set this to the value of the local incoming stanza counter.
|
||||
|
||||
.. attribute:: previd
|
||||
|
||||
Set this to the SM-ID, as received in :class:`SMEnabled`.
|
||||
|
||||
"""
|
||||
|
||||
TAG = (namespaces.stream_management, "resume")
|
||||
|
||||
counter = xso.Attr(
|
||||
"h",
|
||||
type_=xso.Integer()
|
||||
)
|
||||
previd = xso.Attr("previd")
|
||||
|
||||
def __init__(self, counter, previd):
|
||||
super().__init__()
|
||||
self.counter = counter
|
||||
self.previd = previd
|
||||
|
||||
def __repr__(self):
|
||||
return "<{}.{} counter={} previd={!r} at 0x{:x}>".format(
|
||||
type(self).__module__,
|
||||
type(self).__qualname__,
|
||||
self.counter,
|
||||
self.previd,
|
||||
id(self),
|
||||
)
|
||||
|
||||
|
||||
class SMResumed(SMXSO):
|
||||
"""
|
||||
Notification that SM resumption was successful, in response to
|
||||
:class:`SMResume`.
|
||||
|
||||
.. attribute:: counter
|
||||
|
||||
The stanza counter of the remote side.
|
||||
|
||||
.. attribute:: previd
|
||||
|
||||
The SM-ID of the stream.
|
||||
|
||||
"""
|
||||
|
||||
TAG = (namespaces.stream_management, "resumed")
|
||||
|
||||
counter = xso.Attr(
|
||||
"h",
|
||||
type_=xso.Integer())
|
||||
previd = xso.Attr("previd")
|
||||
|
||||
def __init__(self, counter, previd):
|
||||
super().__init__()
|
||||
self.counter = counter
|
||||
self.previd = previd
|
||||
|
||||
def __repr__(self):
|
||||
return "<{}.{} counter={} previd={!r} at 0x{:x}>".format(
|
||||
type(self).__module__,
|
||||
type(self).__qualname__,
|
||||
self.counter,
|
||||
self.previd,
|
||||
id(self),
|
||||
)
|
||||
|
||||
|
||||
class SMFailed(SMXSO):
|
||||
"""
|
||||
Server response to :class:`SMEnable` or :class:`SMResume` if stream
|
||||
management fails.
|
||||
"""
|
||||
TAG = (namespaces.stream_management, "failed")
|
||||
|
||||
condition = xso.Child(
|
||||
[member.xso_class for member in errors.ErrorCondition],
|
||||
required=True,
|
||||
)
|
||||
|
||||
counter = xso.Attr(
|
||||
"h",
|
||||
default=None,
|
||||
type_=xso.Integer(),
|
||||
)
|
||||
|
||||
def __init__(self,
|
||||
condition=errors.ErrorCondition.UNDEFINED_CONDITION,
|
||||
counter=None,
|
||||
**kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.condition = condition.to_xso()
|
||||
self.counter = counter
|
||||
|
||||
def __repr__(self):
|
||||
return "<{}.{} condition={!r} at 0x{:x}>".format(
|
||||
type(self).__module__,
|
||||
type(self).__qualname__,
|
||||
self.condition,
|
||||
id(self),
|
||||
)
|
||||
Reference in New Issue
Block a user