//  [python]
def run(self):
    # (use the exposed "run" method, because if we specify a thread target
    # of a private method, threading.Thread will keep a reference to it
    # indefinitely, creating a GC cycle and not letting Transport ever be
    # GC'd. it's a bug in Thread.)

    # Hold reference to 'sys' so we can test sys.modules to detect
    # interpreter shutdown.
    self.sys = sys

    # active=True occurs before the thread is launched, to avoid a race
    _active_threads.append(self)
    tid = hex(long(id(self)) & xffffffff)
    if self.server_mode:
        self._log(DEBUG, 'starting thread (server mode): %s' % tid)
    else:
        self._log(DEBUG, 'starting thread (client mode): %s' % tid)
    try:
        try:
            self.packetizer.write_all(b(self.local_version + '\r\n'))
            self._log(DEBUG, 'Local version/idstring: %s' % self.local_version) # noqa
            self._check_banner()
            # The above is actually very much part of the handshake, but
            # sometimes the banner can be read but the machine is not
            # responding, for example when the remote ssh daemon is loaded
            # in to memory but we can not read from the disk/spawn a new
            # shell.
            # Make sure we can specify a timeout for the initial handshake.
            # Re-use the banner timeout for now.
            self.packetizer.start_handshake(self.handshake_timeout)
            self._send_kex_init()
            self._expect_packet(MSG_KEXINIT)

            while self.active:
                if self.packetizer.need_rekey() and not self.in_kex:
                    self._send_kex_init()
                try:
                    ptype, m = self.packetizer.read_message()
                except NeedRekeyException:
                    continue
                if ptype == MSG_IGNORE:
                    continue
                elif ptype == MSG_DISCONNECT:
                    self._parse_disconnect(m)
                    self.active = False
                    self.packetizer.close()
                    break
                elif ptype == MSG_DEBUG:
                    self._parse_debug(m)
                    continue
                if len(self._expected_packet) > 0:
                    if ptype not in self._expected_packet:
                        raise SSHException('Expecting packet from %r, got %d' % (self._expected_packet, ptype)) # noqa
                    self._expected_packet = tuple()
                    if (ptype >= 30) and (ptype <= 41):
                        self.kex_engine.parse_next(ptype, m)
                        continue

                if ptype in self._handler_table:
                    self._handler_table[ptype](self, m)
                elif ptype in self._channel_handler_table:
                    chanid = m.get_int()
                    chan = self._channels.get(chanid)
                    if chan is not None:
                        self._channel_handler_table[ptype](chan, m)
                    elif chanid in self.channels_seen:
                        self._log(DEBUG, 'Ignoring message for dead channel %d' % chanid) # noqa
                    else:
                        self._log(ERROR, 'Channel request for unknown channel %d' % chanid) # noqa
                        self.active = False
                        self.packetizer.close()
                elif (
                    self.auth_handler is not None and
                    ptype in self.auth_handler._handler_table
                ):
                    handler = self.auth_handler._handler_table[ptype]
                    handler(self.auth_handler, m)
                    if len(self._expected_packet) > 0:
                        continue
                else:
                    self._log(WARNING, 'Oops, unhandled type %d' % ptype)
                    msg = Message()
                    msg.add_byte(cMSG_UNIMPLEMENTED)
                    msg.add_int(m.seqno)
                    self._send_message(msg)
                self.packetizer.complete_handshake()
        except SSHException as e:
            self._log(ERROR, 'Exception: ' + str(e))
            self._log(ERROR, util.tb_strings())
            self.saved_exception = e
        except EOFError as e:
            self._log(DEBUG, 'EOF in transport thread')
            self.saved_exception = e
        except socket.error as e:
            if type(e.args) is tuple:
                if e.args:
                    emsg = '%s (%d)' % (e.args[1], e.args[0])
                else:  # empty tuple, e.g. socket.timeout
                    emsg = str(e) or repr(e)
            else:
                emsg = e.args
            self._log(ERROR, 'Socket exception: ' + emsg)
            self.saved_exception = e
        except Exception as e:
            self._log(ERROR, 'Unknown exception: ' + str(e))
            self._log(ERROR, util.tb_strings())
            self.saved_exception = e
        _active_threads.remove(self)
        for chan in list(self._channels.values()):
            chan._unlink()
        if self.active:
            self.active = False
            self.packetizer.close()
            if self.completion_event is not None:
                self.completion_event.set()
            if self.auth_handler is not None:
                self.auth_handler.abort()
            for event in self.channel_events.values():
                event.set()
            try:
                self.lock.acquire()
                self.server_accept_cv.notify()
            finally:
                self.lock.release()
        self.sock.close()
    except:
        # Don't raise spurious 'NoneType has no attribute X' errors when we
        # wake up during interpreter shutdown. Or rather -- raise
        # everything *if* sys.modules (used as a convenient sentinel)
        # appears to still exist.
        if self.sys.modules is not None:
            raise