18

I'm trying to convert python2 to python3,i have an error : use this command "telnet localhost 5005 "to connect the program, but when i try to login, one problem was reported.

ScreenShot:

Codes:

#!/usr/bin/env python3
__author__ = 'tcstory'
from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore

PORT=5005
NAME='TestChat'

class EndSession(Exception): pass

class CommandHandler:
    '''
    Simple command handler similar to cmd.Cmd from the standard library
    '''

    def unknown(self, session, cmd):
        'Respond to an unknown command'
        session.push('Unkonw command: {0}\r\n'.format(cmd).encode())

    def handle(self, session, line):
        'Handle a received line from a given session'
        if not line.strip():
            return
        #Split off the command
        parts = line.split(' ', 1)
        cmd = parts[0]
        try:
            line=parts[1].strip()
        except IndexError:
            line=''
        #Try to find a handler
        meth=getattr(self,'do_'+cmd,None)
        try:
            #Assume it's callable
            meth(session,line)
        except TypeError:
            #If it isn't ,respond to the unknown command
            self.unknown(session,cmd)

class Room(CommandHandler):
    '''
    A generic environment that may contain one or more users(sessions).it takes care of basic command handling and broadcasting.
    '''
    def __init__(self,server):
        self.server=server
        self.sessions=[]

    def add(self,session):
        'A session(user) has entered the room'
        self.sessions.append(session)

    def remove(self,session):
        'A session (user) has left the room'
        self.sessions.remove(session)

    def broadcast(self,line):
        'Send a line to all sessions in the room'
        for session in self.sessions:
            session.push(line.encode())

    def do_logout(self,session,line):
        'Respond to the logout command'
        raise EndSession

class LoginRoom(Room):
    '''
    A room meant for a single person who has just connected
    '''

    def add(self,session):
        Room.add(self,session)
        #When a user enters,greet him/her
        self.broadcast('Welcome to {0}\r\n'.format(self.server.name))

    def unknown(self, session, cmd):
        #All unknown commands (anything except login or logout)
        #results in a prodding
        session.push('Please log in\nUse "login <nick>"\r\n'.encode())

    def do_login(self,session,line):
        name=line.strip()
        #Make sure the user has entered a name
        if not name:
            session.push('Please enter a name\r\n'.encode())
        #Make sure that the name isn't in use
        elif name in self.server.users:
            session.push('The name {0} is taken.\r\n'.format(name).encode())
            session.push('Please try again.\r\n'.encode())
        else:
            #The name is OK,os it is stored in the session.and
            #the user is moved into the main room
            session.name=name
            session.enter(self.server.main_room)

class ChatRoom(Room):
    '''
    A room meant for multiple users who can chat with the others in the room
    '''

    def add(self,session):
        #Notify everyone that a new user has entered
        self.broadcast('{0} has entered the room.\r\n'.format(session.name))
        self.server.users[session.name]=session
        Room.add(self,session)

    def remove(self,session):
        Room.remove(self,session)
        #Notify everyone that a user has left
        self.broadcast('{0} has left the room.\r\n'.format(session.name))

    def do_say(self,session,line):
        self.broadcast('{0}: '+line+'\r\n'.format(session.name))

    def do_look(self,session,line):
        'Handles the look command,used to see who is in a room'
        session.push('The following are in this room:\r\n'.encode())
        for other in self.sessions:
            session.push('{0}\r\n'.format(other.name).encode())

    def do_who(self,session,line):
        'Handles the who command ,used to see who is logged in'
        session.push('The following are logged in:\r\n'.encode())
        for name in self.server.users:
            session.push('{0}\r\n'.format(name))

class LogoutRoom(Room):
    '''
    A simple room for a single user.Its sole purpose is to remove the user's name from the server
    '''

    def add(self,session):
        #When a session (user) enters the LogoutRoom it is deleted

        try:
            del self.server.users[session.name]
        except KeyError:
            pass

class ChatSession(async_chat):
    '''
    A single session,which takes care of the communication with a single user
    '''

    def __init__(self,server,sock):
        # async_chat.__init__(self,sock)
        super().__init__(sock)
        self.server=server
        self.set_terminator('\r\n')
        self.data=[]
        self.name=None
        #All sessions begin in a separate LoginRoom
        self.enter(LoginRoom(server))

    def enter(self,room):
        # Remove self from current room and add self to next room....
        try:
            cur=self.room
        except AttributeError:pass
        else:
            cur.remove(self)
        self.room=room
        room.add(self)

    def collect_incoming_data(self, data):
        self.data.append(data)

    def found_terminator(self):
        line=''.join(self.data)
        self.data=[]
        try:
            self.room.handle(self,line)
        except EndSession:
            self.handle_close()

    def handle_close(self):
        async_chat.handle_close(self)
        self.enter(LogoutRoom(self.server))

class ChatServer(dispatcher):
    '''
    A chat server with a single room
    '''

    def __init__(self,port,name):
        super().__init__()
        # dispatcher.__init__(self)
        self.create_socket(socket.AF_INET,socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind(('',port))
        self.listen(5)
        self.name=name
        self.users={}
        self.main_room=ChatRoom(self)

    def handle_accept(self):
        conn,addr=self.accept()
        ChatSession(self,conn)

if __name__=='__main__':
    s=ChatServer(PORT,NAME)
    try:
        asyncore.loop()
    except KeyboardInterrupt:
        print()

What is the problem that is giving me this error?

traceback

error: uncaptured python exception, closing channel <__main__.ChatSession connected 
127.0.0.1:39939 at 0x7f14b1f07d68> 
(<class 'TypeError'>:Type str doesn't support the buffer API 
[/usr/lib/python3.4/asyncore.py|read|83] [/usr/lib/python3.4/asyncore.py|handle_read_event|442]
[/usr/lib/python3.4/asynchat.py|handle_read|154])
3
  • forget to say,the version of python3 is 3.4.0 Commented Jul 24, 2014 at 11:24
  • Could you copy-paste that traceback into your question, for readability. Commented Jul 24, 2014 at 11:44
  • At a guess: you're using a Python 3 (unicode) string where you should be using a bytes object. Commented Jul 24, 2014 at 11:45

2 Answers 2

22

There are two issues in your code:

  1. Calling set_terminator() with str

    Reading asynchat.py you should feed this method a bytes object unless use_encoding is set to True (which is not recommended). In your case you should do:

            self.set_terminator(b'\r\n')
    
  2. Assuming data is str in collect_incoming_data()

    In Python 3, data received from socket objects are bytes. So if what you need is a str, you should decode first:

        def collect_incoming_data(self, data):
            self.data.append(data.decode('utf-8'))
    

See also the source code: http://hg.python.org/cpython/file/c0e311e010fc/Lib/asynchat.py

Sign up to request clarification or add additional context in comments.

3 Comments

Reading your link to source code I see on line 118 reading bytes(str, self.encoding). Should that be bytes(data, self.encoding) instead? Haven't gone through the whole logic yet.
@Ryan i think that is not a bug,because one who help me modify the code had found the issue,he had tested,even change bytes(str,self.encoding) to bytes(data,self.encoding), the problem is still here.so i think that is not a bug
@tcstory I saw your discussions over. It may be not directly relevant (or it may be) but it doesn't look right at all.
1
from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore

PORT = 5005
NAME = 'TestChat'

class EndSession(Exception): pass

class CommandHandler:
    """
    Simple command handler similar to cmd.Cmd from the standard library.
    """

    def unknown(self, session, cmd):
        'Respond to an unknown command'
        session.push('Unknown command: {}s\r\n'.format(cmd).encode('utf-8'))

    def handle(self, session, line):
        'Handle a received line from a given session'
        if not line.strip(): return
        # Split off the command:
        parts = line.split(' ', 1)
        cmd = parts[0]
        try: line = parts[1].strip()
        except IndexError: line = ''
        # Try to find a handler:
        meth = getattr(self, 'do_' + cmd, None)
        try:
            # Assume it's callable:
            meth(session, line)
        except TypeError:
            # If it isn't, respond to the unknown command:
            self.unknown(session, cmd)

class Room(CommandHandler):
    """
    A generic environment that may contain one or more users (sessions).
    It takes care of basic command handling and broadcasting.
    """

    def __init__(self, server):
        self.server = server
        self.sessions = []

    def add(self, session):
        'A session (user) has entered the room'
        self.sessions.append(session)

    def remove(self, session):
        'A session (user) has left the room'
        self.sessions.remove(session)

    def broadcast(self, line):
        'Send a line to all sessions in the room'
        for session in self.sessions:
            session.push(line)

    def do_logout(self, session, line):
        'Respond to the logout command'
        raise EndSession

class LoginRoom(Room):
    """
    A room meant for a single person who has just connected.
    """

    def add(self, session):
        Room.add(self, session)
        # When a user enters, greet him/her:
        self.broadcast('Welcome to {}\r\n'.format(self.server.name).encode('utf-8'))

    def unknown(self, session, cmd):
        # All unknown commands (anything except login or logout)
        # results in a prodding:
        session.push('Please log in\nUse "login <nick>"\r\n'.encode('utf-8'))

    def do_login(self, session, line):
        name = line.strip()
        # Make sure the user has entered a name:
        if not name:
            session.push('Please enter a name\r\n'.encode('utf-8'))
        # Make sure that the name isn't in use:
        elif name in self.server.users:
            session.push('The name "{}" is taken.\r\n'.format(name).encode('utf-8'))
            session.push('Please try again.\r\n'.encode('utf-8'))
        else:
            # The name is OK, so it is stored in the session, and
            # the user is moved into the main room.
            session.name = name
            session.enter(self.server.main_room)

class ChatRoom(Room):
    """
    A room meant for multiple users who can chat with the others in the room.
    """

    def add(self, session):
        # Notify everyone that a new user has entered:
        self.broadcast((session.name + ' has entered the room.\r`enter code here`\n').encode('utf-8'))
        self.server.users[session.name] = session
        super().add(session)

    def remove(self, session):
        Room.remove(self, session)
        # Notify everyone that a user has left:
        self.broadcast((session.name + ' has left the room.\r\n').encode('utf-8'))

    def do_say(self, session, line):
        self.broadcast((session.name + ': ' + line + '\r\n').encode('utf-8'))

    def do_look(self, session, line):
        'Handles the look command, used to see who is in a room'
        session.push('The following are in this room:\r\n'.encode('utf-8'))
        for other in self.sessions:
            session.push((other.name + '\r\n').encode('utf-8'))

    def do_who(self, session, line):
        'Handles the who command, used to see who is logged in'
        session.push('The following are logged in:\r\n'.encode('utf-8'))
        for name in self.server.users:
            session.push((name + '\r\n').encode('utf-8'))

class LogoutRoom(Room):
    """
    A simple room for a single user. Its sole purpose is to remove the
    user's name from the server.
    """

    def add(self, session):
        # When a session (user) enters the LogoutRoom it is deleted
        try: del self.server.users[session.name]
        except KeyError: pass

class ChatSession(async_chat):
    """
    A single session, which takes care of the communication with a single user.
    """

    def __init__(self, server, sock):
        super().__init__(sock)
        self.server = server
        self.set_terminator(b"\r\n")
        self.data = []
        self.name = None
        # All sessions begin in a separate LoginRoom:
        self.enter(LoginRoom(server))

    def enter(self, room):
        # Remove self from current room and add self to
        # next room...
        try: cur = self.room
        except AttributeError: pass
        else: cur.remove(self)
        self.room = room
        room.add(self)

    def collect_incoming_data(self, data):
        self.data.append(data.decode('utf-8'))

    def found_terminator(self):
        line = ''.join(self.data)
        self.data = []
        try: self.room.handle(self, line)
        except EndSession: self.handle_close()

    def handle_close(self):
        async_chat.handle_close(self)
        self.enter(LogoutRoom(self.server))

class ChatServer(dispatcher):
    """
    A chat server with a single room.
    """

    def __init__(self, port, name):
        super().__init__()
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind(('', port))
        self.listen(5)
        self.name = name
        self.users = {}
        self.main_room = ChatRoom(self)

    def handle_accept(self):
        conn, addr = self.accept()
        ChatSession(self, conn)

if __name__ == '__main__':
    s = ChatServer(PORT, NAME)
    try: asyncore.loop()
    except KeyboardInterrupt: print()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.