【问题标题】:python3 Type str doesn't support the buffer APIpython3 类型 str 不支持缓冲区 API
【发布时间】:2014-07-24 08:38:07
【问题描述】:

我正在尝试将python2转换为python3,我有一个错误:使用此命令“telnet localhost 5005”连接程序,但是当我尝试登录时,报告了一个问题。

截屏:

代码:

#!/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()

是什么问题导致我出现此错误?

追溯

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])

【问题讨论】:

  • 忘了说,python3的版本是3.4.0
  • 为了便于阅读,您能否将该回溯复制粘贴到您的问题中。
  • 猜测:您使用的是 Python 3 (unicode) 字符串,而您应该使用 bytes 对象。

标签: python-3.x


【解决方案1】:

您的代码中有两个问题:

  1. str呼叫set_terminator()

    阅读asynchat.py,你应该给这个方法一个bytes对象,除非use_encoding设置为True(不推荐)。在您的情况下,您应该这样做:

            self.set_terminator(b'\r\n')
    
  2. 假设datacollect_incoming_data() 中是str

    在 Python 3 中,从套接字对象接收的数据是 bytes。所以如果你需要的是str,你应该先解码:

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

另见源代码:http://hg.python.org/cpython/file/c0e311e010fc/Lib/asynchat.py

【讨论】:

  • 阅读您的源代码链接,我在第 118 行看到bytes(str, self.encoding)。那应该是bytes(data, self.encoding) 吗?还没有走完整个逻辑。
  • @Ryan 我认为这不是错误,因为帮助我修改代码的人发现了问题,他已经测试过,甚至将字节(str,self.encoding)更改为字节(数据, self.encoding),问题仍然存在。所以我认为这不是一个错误
  • @tcstory 我看到你的讨论结束了。它可能不直接相关(或者可能是),但看起来完全不正确。
【解决方案2】:
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()

【讨论】:

    猜你喜欢
    • 2015-03-26
    • 2015-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-06
    • 2015-01-12
    • 2016-05-08
    • 2013-03-26
    相关资源
    最近更新 更多