class ElementClass:
    def __subclasshook__(cls, other):
def element_to_unicode(element): class XMLStreamHandler(object):
    """Base class for stream handler, used as a target for XMPP parser
    or XMPP transports.
    """
    # pylint: disable-msg=R0201
    def stream_start(self, element):
        """Called when the start tag of root element is encountered
        in the stream.

        :Parameters:
            - `element`: the root element
        :Types:
            - `element`: :etree:`ElementTree.Element`"""
        logger.error("Unhandled stream start: {0!r}".format(element))

    def stream_end(self):
        """Called when the end tag of root element is encountered
        in the stream.
        """
        logger.error("Unhandled stream end")

    def stream_element(self, element):
        """Called when the end tag of a direct child of the root
        element is encountered in the stream.

        :Parameters:
            - `element`: the (complete) element being processed
        :Types:
            - `element`: :etree:`ElementTree.Element`"""
        logger.error("Unhandled stanza: {0!r}".format(element))

    def stream_parse_error(self, descr):
        """Called when an error is encountered in the stream.

        :Parameters:
            - `descr`: description of the error
        :Types:
            - `descr`: `unicode`"""
        raise StreamParseError(descr)

    def stream_eof(self):
        """Called when stream input ends (EOF, socket closed by peer)
        which could happen before actual stream end tag was received,
        but usually will happen just after that.

        This event is not generated by the XMPP parser, but may be generated
        by transports.
        """
        pass class StreamErrorElement(ErrorElement):
    """Stream error element."""
    error_qname = STREAM_QNP + "error"
    text_qname = STREAM_QNP + "text"
    cond_qname_prefix = STREAM_ERROR_QNP
    def __init__(self, element_or_cond, text = None, language = None):
        """Initialize an StreamErrorElement object.

        :Parameters:
            - `element_or_cond`: XML <error/> element to decode or an error
              condition name or element.
            - `text`: optional description to override the default one
            - `language`: RFC 3066 language tag for the description
        :Types:
            - `element_or_cond`: :etree:`ElementTree.Element` or `unicode`
            - `text`: `unicode`
            - `language`: `unicode`
        """
        if isinstance(element_or_cond, unicode):
            if element_or_cond not in STREAM_ERRORS:
                raise ValueError("Bad error condition")
        ErrorElement.__init__(self, element_or_cond, text, language)

    def get_message(self):
        """Get the standard English message for the error.

        :return: the error message.
        :returntype: `unicode`"""
        cond = self.condition_name
        if cond in STREAM_ERRORS:
            return STREAM_ERRORS[cond][0]
        else:
            return None class JID(object):
    """JID.

    :Ivariables:
        - `local`: localpart of the JID
        - `domain`: domainpart of the JID
        - `resource`: resourcepart of the JID

    JID objects are immutable. They are also cached for better performance.
    """
    cache = weakref.WeakValueDictionary()
    __slots__ = ("local", "domain", "resource", "__weakref__",)
    def __new__(cls, local_or_jid = None, domain = None, resource = None,
                                                                check = True):
        """Create a new JID object or take one from the cache.

        :Parameters:
            - `local_or_jid`: localpart of the JID, JID object to copy, or
              Unicode representation of the JID.
            - `domain`: domain part of the JID
            - `resource`: resource part of the JID
            - `check`: if `False` then JID is not checked for specification
              compliance.
        """

        if isinstance(local_or_jid, JID):
            return local_or_jid

        if domain is None and resource is None:
            obj = cls.cache.get(unicode(local_or_jid))
            if obj:
                return obj

        obj = object.__new__(cls)

        if local_or_jid:
            local_or_jid = unicode(local_or_jid)
        if (local_or_jid and not domain and not resource):
            local, domain, resource = cls.__from_unicode(local_or_jid)
            cls.cache[local_or_jid] = obj
        else:
            if domain is None and resource is None:
                raise JIDError("At least domain must be given")
            if check:
                local = cls.__prepare_local(local_or_jid)
                domain = cls.__prepare_domain(domain)
                resource = cls.__prepare_resource(resource)
            else:
                local = local_or_jid
        object.__setattr__(obj, "local", local)
        object.__setattr__(obj, "domain", domain)
        object.__setattr__(obj, "resource", resource)
        return obj

    def __setattr__(self, name, value):
        raise RuntimeError("JID objects are immutable!")

    def __attribute_declarations__(self):
        # to make pylint happy
        self.local = u""
        self.domain = u""
        self.resource = u""

    @classmethod
    def __from_unicode(cls, data, check = True):
        """Return jid tuple from an Unicode string.

        :Parameters:
            - `data`: the JID string
            - `check`: when `False` then the JID is not checked for
              specification compliance.

        :Return: (localpart, domainpart, resourcepart) tuple"""
        parts1 = data.split(u"/", 1)
        parts2 = parts1[0].split(u"@", 1)
        if len(parts2) == 2:
            local = parts2[0]
            domain = parts2[1]
            if check:
                local = cls.__prepare_local(local)
                domain = cls.__prepare_domain(domain)
        else:
            local = None
            domain = parts2[0]
            if check:
                domain = cls.__prepare_domain(domain)
        if len(parts1) == 2:
            resource = parts1[1]
            if check:
                resource = cls.__prepare_resource(parts1[1])
        else:
            resource = None
        if not domain:
            raise JIDError("Domain is required in JID.")
        return (local, domain, resource)

    @staticmethod
    def __prepare_local(data):
        """Prepare localpart of the JID

        :Parameters:
            - `data`: localpart of the JID
        :Types:
            - `data`: `unicode`

        :raise JIDError: if the local name is too long.
        :raise pyxmpp.xmppstringprep.StringprepError: if the
            local name fails Nodeprep preparation."""
        if not data:
            return None
        data = unicode(data)
        try:
            local = NODEPREP.prepare(data)
        except StringprepError, err:
            raise JIDError(u"Local part invalid: {0}".format(err))
        if len(local.encode("utf-8")) > 1023:
            raise JIDError(u"Local part too long")
        return local

    @staticmethod
    def __prepare_domain(data):
        """Prepare domainpart of the JID.

        :Parameters:
            - `data`: Domain part of the JID
        :Types:
            - `data`: `unicode`

        :raise JIDError: if the domain name is too long.
        """
        # pylint: disable=R0912
        if not data:
            raise JIDError("Domain must be given")
        data = unicode(data)
        if not data:
            raise JIDError("Domain must be given")
        if u'[' in data:
            if data[0] == u'[' and data[-1] == u']':
                try:
                    addr = _validate_ip_address(socket.AF_INET6, data[1:-1])
                    return "[{0}]".format(addr)
                except ValueError, err:
                    logger.debug("ValueError: {0}".format(err))
                    raise JIDError(u"Invalid IPv6 literal in JID domainpart")
            else:
                raise JIDError(u"Invalid use of '[' or ']' in JID domainpart")
        elif data[0].isdigit() and data[-1].isdigit():
            try:
                addr = _validate_ip_address(socket.AF_INET, data)
            except ValueError, err:
                logger.debug("ValueError: {0}".format(err))
        data = UNICODE_DOT_RE.sub(u".", data)
        data = data.rstrip(u".")
        labels = data.split(u".")
        try:
            labels = [idna.nameprep(label) for label in labels]
        except UnicodeError:
            raise JIDError(u"Domain name invalid")
        for label in labels:
            if not STD3_LABEL_RE.match(label):
                raise JIDError(u"Domain name invalid")
            try:
                idna.ToASCII(label)
            except UnicodeError:
                raise JIDError(u"Domain name invalid")
        domain = u".".join(labels)
        if len(domain.encode("utf-8")) > 1023:
            raise JIDError(u"Domain name too long")
        return domain

    @staticmethod
    def __prepare_resource(data):
        """Prepare the resourcepart of the JID.

        :Parameters:
            - `data`: Resourcepart of the JID

        :raise JIDError: if the resource name is too long.
        :raise pyxmpp.xmppstringprep.StringprepError: if the
            resourcepart fails Resourceprep preparation."""
        if not data:
            return None
        data = unicode(data)
        try:
            resource = RESOURCEPREP.prepare(data)
        except StringprepError, err:
            raise JIDError(u"Local part invalid: {0}".format(err))
        if len(resource.encode("utf-8")) > 1023:
            raise JIDError("Resource name too long")
        return resource

    def __unicode__(self):
        return self.as_unicode()

    def __repr__(self):
        return "JID(%r)" % (self.as_unicode())

    def as_utf8(self):
        """UTF-8 encoded JID representation.

        :return: UTF-8 encoded JID."""
        return self.as_unicode().encode("utf-8")

    def as_string(self):
        """UTF-8 encoded JID representation.

        *Deprecated* Always use Unicode objects, or `as_utf8` if you really want.

        :return: UTF-8 encoded JID."""
        warnings.warn("JID.as_string() is deprecated. Use unicode()"
                " or `as_utf8` instead.", DeprecationWarning, stacklevel=1)
        return self.as_utf8()

    def as_unicode(self):
        """Unicode string JID representation.

        :return: JID as Unicode string."""
        result = self.domain
        if self.local:
            result = self.local + u'@' + result
        if self.resource:
            result = result + u'/' + self.resource
        if not JID.cache.has_key(result):
            JID.cache[result] = self
        return result

    def bare(self):
        """Make bare JID made by removing resource from current `self`.

        :return: new JID object without resource part."""
        return JID(self.local, self.domain, check = False)

    def __eq__(self, other):
        if other is None:
            return False
        elif type(other) in (str, unicode):
            try:
                other = JID(other)
            except StandardError:
                return False
        elif not isinstance(other, JID):
            return False

        return (self.local == other.local
            and are_domains_equal(self.domain, other.domain)
            and self.resource == other.resource)

    def __ne__(self, other):
        return not self == other

    def __lt__(self, other):
        if other is None:
            return False
        return unicode(self) < unicode(other)

    def __gt__(self, other):
        if other is None:
            return True
        return unicode(self) > unicode(other)

    def __le__(self, other):
        if other is None:
            return False
        return unicode(self) <= unicode(other)

    def __ge__(self, other):
        if other is None:
            return True
        return unicode(self) >= unicode(other)

    def __hash__(self):
        return hash(self.local) ^ hash(self.domain) ^ hash(self.resource) class StreamError(Error):
    """Base class for all stream errors."""
    pass class FatalStreamError(StreamError):
    """Base class for all fatal Stream exceptions.

    When `FatalStreamError` is raised the stream is no longer usable."""
    pass class StreamParseError(FatalStreamError):
    """Raised when invalid XML is received in an XMPP stream."""
    pass STREAM_QNP = "{{{0}}}".format(STREAM_NS) XML_LANG_QNAME = XML_QNP + "lang" STREAM_ROOT_TAG = STREAM_QNP + "stream" class XMPPSettings(MutableMapping):
    """Container for various parameters used all over PyXMPP.

    It can be used like a regular dictionary, but will provide reasonable
    defaults for PyXMPP for parameters which are not explicitly set.

    All known PyXMPP settings are included in the :r:`settings list`.

    :CVariables:
        - `_defaults`: defaults for registered parameters.
        - `_defaults_factories`: factory functions providing default values
          which cannot be hard-coded.
    :Ivariables:
        - `_settings`: current values of the parameters explicitly set.
    """
    _defs = {}
    def __init__(self, data = None):
        """Create settings, optionally initialized with `data`.

        :Parameters:
            - `data`: initial data
        :Types:
            - `data`: any mapping, including `XMPPSettings`
        """
        if data is None:
            self._settings = {}
        else:
            self._settings = dict(data)
    def __len__(self):
        """Number of parameters set."""
        return len(self._settings)
    def __iter__(self):
        """Iterate over the parameter names."""
        for key in self._settings.iterkeys():
            return self[key]
    def __contains__(self, key):
        """Check if a parameter is set.

        :Parameters:
            - `key`: the parameter name
        :Types:
            - `key`: `unicode`
        """
        return key in self._settings
    def __getitem__(self, key):
        """Get a parameter value. Return the default if no value is set
        and the default is provided by PyXMPP.

        :Parameters:
            - `key`: the parameter name
        :Types:
            - `key`: `unicode`
        """
        return self.get(key, required = True)
    def __setitem__(self, key, value):
        """Set a parameter value.

        :Parameters:
            - `key`: the parameter name
            - `value`: the new value
        :Types:
            - `key`: `unicode`
        """
        self._settings[unicode(key)] = value
    def __delitem__(self, key):
        """Unset a parameter value.

        :Parameters:
            - `key`: the parameter name
        :Types:
            - `key`: `unicode`
        """
        del self._settings[key]
    def get(self, key, local_default = None, required = False):
        """Get a parameter value.

        If parameter is not set, return `local_default` if it is not `None`
        or the PyXMPP global default otherwise.

        :Raise `KeyError`: if parameter has no value and no global default

        :Return: parameter value
        """
        # pylint: disable-msg=W0221
        if key in self._settings:
            return self._settings[key]
        if local_default is not None:
            return local_default
        if key in self._defs:
            setting_def = self._defs[key]
            if setting_def.default is not None:
                return setting_def.default
            factory = setting_def.factory
            if factory is None:
                return None
            value = factory(self)
            if setting_def.cache is True:
                setting_def.default = value
            return value
        if required:
            raise KeyError(key)
        return local_default
    def keys(self):
        """Return names of parameters set.

        :Returntype: - `list` of `unicode`
        """
        return self._settings.keys()
    def items(self):
        """Return names and values of parameters set.

        :Returntype: - `list` of tuples
        """
        return self._settings.items()

    def load_arguments(self, args):
        """Load settings from :std:`ArgumentParser` output.

        :Parameters:
            - `args`: output of argument parsed based on the one
              returned by `get_arg_parser()`
        """
        for name, setting in self._defs.items():
            if sys.version_info.major < 3:
                # pylint: disable-msg=W0404
                from locale import getpreferredencoding
                encoding = getpreferredencoding()
                name = name.encode(encoding, "replace")
            attr = "pyxmpp2_" + name
            try:
                self[setting.name] = getattr(args, attr)
            except AttributeError:
                pass

    @classmethod
    def add_setting(cls, name, type = unicode, default = None, factory = None,
                        cache = False, default_d = None, doc = None,
                        cmdline_help = None, validator = None, basic = False):
        """Add a new setting definition.

        :Parameters:
            - `name`: setting name
            - `type`: setting type object or type description
            - `default`: default value
            - `factory`: default value factory
            - `cache`: if `True` the `factory` will be called only once
              and its value stored as a constant default.
            - `default_d`: description of the default value
            - `doc`: setting documentation
            - `cmdline_help`: command line argument description. When not
              provided then the setting won't be available as a command-line
              option
            - `basic`: when `True` the option is considered a basic option -
              one of those which should usually stay configurable in
              an application.
            - `validator`: function validating command-line option value string
              and returning proper value for the settings objects. Defaults
              to `type`.
        :Types:
            - `name`: `unicode`
            - `type`: type or `unicode`
            - `factory`: a callable
            - `cache`: `bool`
            - `default_d`: `unicode`
            - `doc`: `unicode`
            - `cmdline_help`: `unicode`
            - `basic`: `bool`
            - `validator`: a callable
        """
        # pylint: disable-msg=W0622,R0913
        setting_def = _SettingDefinition(name, type, default, factory,
                                            cache, default_d, doc,
                                            cmdline_help, validator, basic)
        if name not in cls._defs:
            cls._defs[name] = setting_def
            return
        duplicate = cls._defs[name]
        if duplicate.type != setting_def.type:
            raise ValueError("Setting duplicate, with a different type")
        if duplicate.default != setting_def.default:
            raise ValueError("Setting duplicate, with a different default")
        if duplicate.factory != setting_def.factory:
            raise ValueError("Setting duplicate, with a different factory")

    @staticmethod
    def validate_string_list(value):
        """Validator for string lists to be used with `add_setting`."""
        try:
            if sys.version_info.major < 3:
                # pylint: disable-msg=W0404
                from locale import getpreferredencoding
                encoding = getpreferredencoding()
                value = value.decode(encoding)
            return [x.strip() for x in value.split(u",")]
        except (AttributeError, TypeError, UnicodeError):
            raise ValueError("Bad string list")

    @staticmethod
    def validate_positive_int(value):
        """Positive integer validator to be used with `add_setting`."""
        value = int(value)
        if value <= 0:
            raise ValueError("Positive number required")
        return value

    @staticmethod
    def validate_positive_float(value):
        """Positive float validator to be used with `add_setting`."""
        value = float(value)
        if value <= 0:
            raise ValueError("Positive number required")
        return value

    @staticmethod
    def get_int_range_validator(start, stop):
        """Return an integer range validator to be used with `add_setting`.

        :Parameters:
            - `start`: minimum value for the integer
            - `stop`: the upper bound (maximum value + 1)
        :Types:
            - `start`: `int`
            - `stop`: `int`

        :return: a validator function
        """
        def validate_int_range(value):
            """Integer range validator."""
            value = int(value)
            if value >= start and value < stop:
                return value
            raise ValueError("Not in <{0},{1}) range".format(start, stop))
        return validate_int_range

    @classmethod
    def list_all(cls, basic = None):
        """List known settings.

        :Parameters:
            - `basic`: When `True` then limit output to the basic settings,
              when `False` list only the extra settings.
        """
        if basic is None:
            return [s for s in cls._defs]
        else:
            return [s.name for s in cls._defs.values() if s.basic == basic]

    @classmethod
    def get_arg_parser(cls, settings = None, option_prefix = u'--',
                                                            add_help = False):
        """Make a command-line option parser.

        The returned parser may be used as a parent parser for application
        argument parser.

        :Parameters:
            - `settings`: list of PyXMPP2 settings to consider. By default
              all 'basic' settings are provided.
            - `option_prefix`: custom prefix for PyXMPP2 options. E.g.
              ``'--xmpp'`` to differentiate them from not xmpp-related
              application options.
            - `add_help`: when `True` a '--help' option will be included
              (probably already added in the application parser object)
        :Types:
            - `settings`: list of `unicode`
            - `option_prefix`: `str`
            - `add_help`:

        :return: an argument parser object.
        :returntype: :std:`argparse.ArgumentParser`
        """
        # pylint: disable-msg=R0914,R0912
        parser = argparse.ArgumentParser(add_help = add_help,
                                            prefix_chars = option_prefix[0])
        if settings is None:
            settings = cls.list_all(basic = True)

        if sys.version_info.major < 3:
            # pylint: disable-msg=W0404
            from locale import getpreferredencoding
            encoding = getpreferredencoding()
            def decode_string_option(value):
                """Decode a string option."""
                return value.decode(encoding)

        for name in settings:
            if name not in cls._defs:
                logger.debug("get_arg_parser: ignoring unknown option {0}"
                                                                .format(name))
                return
            setting = cls._defs[name]
            if not setting.cmdline_help:
                logger.debug("get_arg_parser: option {0} has no cmdline"
                                                                .format(name))
                return
            if sys.version_info.major < 3:
                name = name.encode(encoding, "replace")
            option = option_prefix + name.replace("_", "-")
            dest = "pyxmpp2_" + name
            if setting.validator:
                opt_type = setting.validator
            elif setting.type is unicode and sys.version_info.major < 3:
                opt_type = decode_string_option
            else:
                opt_type = setting.type
            if setting.default_d:
                default_s = setting.default_d
                if sys.version_info.major < 3:
                    default_s = default_s.encode(encoding, "replace")
            elif setting.default is not None:
                default_s = repr(setting.default)
            else:
                default_s = None
            opt_help = setting.cmdline_help
            if sys.version_info.major < 3:
                opt_help = opt_help.encode(encoding, "replace")
            if default_s:
                opt_help += " (Default: {0})".format(default_s)
            if opt_type is bool:
                opt_action = _YesNoAction
            else:
                opt_action = "store"
            parser.add_argument(option,
                                action = opt_action,
                                default = setting.default,
                                type = opt_type,
                                help = opt_help,
                                metavar = name.upper(),
                                dest = dest)
        return parser def serialize(element):
    """Serialize an XMPP element.

    Utility function for debugging or logging.

        :Parameters:
            - `element`: the element to serialize
        :Types:
            - `element`: :etree:`ElementTree.Element`

        :Return: serialized element
        :Returntype: `unicode`
    """
    if getattr(_THREAD, "serializer", None) is None:
        _THREAD.serializer = XMPPSerializer("jabber:client")
        _THREAD.serializer.emit_head(None, None)
    return _THREAD.serializer.emit_stanza(element) class StreamConnectedEvent(StreamEvent):
    """Emitted when the initial stream handshake (<stream:stream> tag exchange)
    is completed, before any authentication.

    :Ivariables:
        - `peer`: peer name
    :Types:
        - `peer`: `pyxmpp2.jid.JID`
    """
    def __init__(self, peer):
        self.peer = peer
    def __unicode__(self):
        return u"Connected to {0}".format(self.peer) class GotFeaturesEvent(StreamEvent):
    """Emitted when the stream features are received.

    Default action (skipped if the handler returns `True`) may be, depending
    on the features available, one of:

    - StartTLS initiation
    - SASL authentication
    - Resource binding

    :Ivariables:
        - `features`: the <stream:features/> element
    :Types:
        - `features`: :etree:`ElementTree.Element`
    """
    def __init__(self, features):
        self.features = features
    def __unicode__(self):
        return u"Got stream features" class AuthenticatedEvent(StreamEvent):
    """Event raised after stream authentication is complete.
    Usually it happens after SASL authentication and before XMPP resource
    binding.

    Default action: none

    :Ivariables:
        - `authenticated_jid`: JID (bare) just authenticated
    :Types:
        - `authenticated_jid`: `pyxmpp2.jid.JID`
    """
    def __init__(self, authenticated_jid):
        self.authenticated_jid = authenticated_jid
    def __unicode__(self):
        return u"Authenticated: {0}".format(self.authenticated_jid) class StreamRestartedEvent(StreamEvent):
    """Emitted after stream is restarted (<stream:stream> tag exchange)
    e.g. after SASL.

    :Ivariables:
        - `peer`: peer name
    :Types:
        - `peer`: `pyxmpp2.jid.JID`
    """
    def __init__(self, peer):
        self.peer = peer
    def __unicode__(self):
        return u"Connected to {0}".format(self.peer) def stanza_factory(element, return_path = None, language = None):
    """Creates Iq, Message or Presence object for XML stanza `element`

    :Parameters:
        - `element`: the stanza XML element
        - `return_path`: object through which responses to this stanza should
          be sent (will be weakly referenced by the stanza object).
        - `language`: default language for the stanza
    :Types:
        - `element`: :etree:`ElementTree.Element`
        - `return_path`: `StanzaRoute`
        - `language`: `unicode`
    """
    tag = element.tag
    if tag.endswith("}iq") or tag == "iq":
        return Iq(element, return_path = return_path, language = language)
    if tag.endswith("}message") or tag == "message":
        return Message(element, return_path = return_path, language = language)
    if tag.endswith("}presence") or tag == "presence":
        return Presence(element, return_path = return_path, language = language)
    else:
        return Stanza(element, return_path = return_path, language = language) class StreamFeatureHandler:
    """Base class for stream feature handlers.

    The `handle_stream_features` and `make_stream_features` should
    process and populate the ``<stream::features/>`` element as needed.

    Other methods, decorated with the `stream_element_handler` decorated,
    will be called to handle matching stream element.
    """
    # pylint: disable-msg=W0232
    __metaclass__ = ABCMeta
    def handle_stream_features(self, stream, features):
        """Handle features announced by the stream peer.

        [initiator only]

        :Parameters:
            - `stream`: the stream
            - `features`: the features element just received
        :Types:
            - `stream`: `StreamBase`
            - `features`: :etree:`ElementTree.Element`

        :Return:
            - `StreamFeatureHandled` instance if a feature was recognized and
              handled
            - `StreamFeatureNotHandled` instance if a feature was recognized
              but not handled
            - `None` if no feature was recognized
        """
        # pylint: disable-msg=W0613,R0201
        return False

    def make_stream_features(self, stream, features):
        """Update the features element announced by the stream.

        [receiver only]

        :Parameters:
            - `stream`: the stream
            - `features`: the features element about to be sent
        :Types:
            - `stream`: `StreamBase`
            - `features`: :etree:`ElementTree.Element`
        """
        # pylint: disable-msg=W0613,R0201
        return False class StreamFeatureHandled(object):
    """Object returned by a stream feature handler for recognized and handled
    features.
    """
    # pylint: disable-msg=R0903
    def __init__(self, feature_name, mandatory = False):
        self.feature_name = feature_name
        self.mandatory = mandatory
    def __repr__(self):
        if self.mandatory:
            return "StreamFeatureHandled({0!r}, mandatory = True)".format(
                                                            self.feature_name)
        else:
            return "StreamFeatureHandled({0!r})".format(self.feature_name)
    def __str__(self):
        return self.feature_name class StreamFeatureNotHandled(object):
    """Object returned by a stream feature handler for recognized,
    but unhandled features.
    """
    # pylint: disable-msg=R0903
    def __init__(self, feature_name, mandatory = False):
        self.feature_name = feature_name
        self.mandatory = mandatory
    def __repr__(self):
        if self.mandatory:
            return "StreamFeatureNotHandled({0!r}, mandatory = True)".format(
                                                            self.feature_name)
        else:
            return "StreamFeatureNotHandled({0!r})".format(self.feature_name)
    def __str__(self):
        return self.feature_name
import inspect
import logging
import uuid
import re
import threading
from .etree import ElementTree, element_to_unicode
from .xmppparser import XMLStreamHandler
from .error import StreamErrorElement
from .jid import JID
from .exceptions import StreamError
from .exceptions import FatalStreamError, StreamParseError
from .constants import STREAM_QNP, XML_LANG_QNAME, STREAM_ROOT_TAG
from .settings import XMPPSettings
from .xmppserializer import serialize
from .streamevents import StreamConnectedEvent, GotFeaturesEvent
from .streamevents import AuthenticatedEvent, StreamRestartedEvent
from .stanzaprocessor import stanza_factory
from .interfaces import StreamFeatureHandler
from .interfaces import StreamFeatureHandled, StreamFeatureNotHandled
# just to distinguish those from a domain name
IP_RE = re.compile(r"^((\d+.){3}\d+)|([0-9a-f]*:[0-9a-f:]*:[0-9a-f]*)$")

class StreamBase(XMLStreamHandler):
    """Base class for a generic XMPP stream.

    Responsible for establishing connection, parsing the stream, handling
    stream elements and passing stanzas receiver to other object.
    This doesn't provide any authentication or encryption (both required by
    the XMPP specification) and is not usable on its own.

    Whenever we say "stream" here we actually mean two streams
    (incoming and outgoing) of one connections, as defined by the XMPP
    specification.

    :Ivariables:
        - `authenticated`: `True` if local entity has authenticated to peer
        - `features`: stream features as annouced by the receiver.
        - `handlers`: handlers for stream elements
        - `initiator`: `True` if local stream endpoint is the initiating entity.
        - `lock`: RLock object used to synchronize access to Stream object.
        - `me`: local stream endpoint JID.
        - `peer_authenticated`: `True` if the peer has authenticated to us
        - `peer_language`: language of human-readable stream content selected
          by the peer
        - `peer`: remote stream endpoint JID.
        - `settings`: stream settings
        - `stanza_namespace`: default namespace of the stream
        - `tls_established`: `True` when the stream is protected by TLS
        - `transport`: transport used by this stream
        - `version`: Negotiated version of the XMPP protocol. (0,9) for the
          legacy (pre-XMPP) Jabber protocol.
        - `_element_handlers`: mapping from stream element names to lists of
          methods handling them
        - `_input_state`: `None`, "open" (<stream:stream> has been received)
          "restart" or "closed" (</stream:stream> or EOF has been received)
        - `_output_state`: `None`, "open" (<stream:stream> has been received)
          "restart" or "closed" (</stream:stream> or EOF has been received)
        - `_stanza_namespace_p`: qname prefix of the stanza namespace
        - `_stream_feature_handlers`: stream features handlers
    :Types:
        - `authenticated`: `bool`
        - `features`: :etree:`ElementTree.Element`
        - `handlers`: `list`
        - `initiator`: `bool`
        - `lock`: :std:`threading.RLock`
        - `me`: `JID`
        - `peer_authenticated`: `bool`
        - `peer_language`: `unicode`
        - `peer`: `JID`
        - `settings`: XMPPSettings
        - `stanza_namespace`: `unicode`
        - `tls_established`: `bool`
        - `transport`: `transport.XMPPTransport`
        - `version`: (`int`, `int`) tuple
        - `_element_handlers`: `dict`
        - `_input_state`: `unicode`
        - `_output_state`: `unicode`
        - `_stanza_namespace_p`: `unicode`
        - `_stream_feature_handlers`: `list` of `StreamFeatureHandler`
    """
    # pylint: disable-msg=R0902,R0904
    def __init__(self, stanza_namespace, stanza_route, handlers,
                                                            settings = None):
        """Initialize StreamBase object

        :Parameters:
          - `stanza_namespace`: stream's default namespace URI ("jabber:client"
            for client, "jabber:server" for server, etc.)
          - `stanza_route`: object to handle received stanzas
          - `handlers`: objects to handle the stream events and elements
          - `settings`: extra settings
        :Types:
          - `stanza_namespace`: `unicode`
          - `stanza_route`: `StanzaRoute`
          - `settings`: XMPPSettings
          - `handlers`: `list` of objects
        """
        XMLStreamHandler.__init__(self)
        self.lock = threading.RLock()
        if settings is None:
            settings = XMPPSettings()
        self.settings = settings
        self.stanza_namespace = stanza_namespace
        self._stanza_namespace_p = "{{{0}}}".format(stanza_namespace)
        self.stanza_route = stanza_route
        self.handlers = handlers
        self._stream_feature_handlers = []
        for handler in handlers:
            if isinstance(handler, StreamFeatureHandler):
                self._stream_feature_handlers.append(handler)
        self.me = None
        self.peer = None
        self.stream_id = None
        self.initiator = None
        self.features = None
        self.authenticated = False
        self.peer_authenticated = False
        self.tls_established = False
        self.auth_method_used = None
        self.version = None
        self.language = None
        self.peer_language = None
        self.transport = None
        self._input_state = None
        self._output_state = None
        self._element_handlers = {}

    def initiate(self, transport, to = None):
        """Initiate an XMPP connection over the `transport`.

        :Parameters:
            - `transport`: an XMPP transport instance
            - `to`: peer name
        """
        with self.lock:
            self.initiator = True
            self.transport = transport
            transport.set_target(self)
            if to:

                self.peer = JID(to)