pythonsocketsoopinheritancecomposition

Extending socket.socket with a new attribute


I wish to extend Python socket.socket with a new attribute (in particular, a queue.Queue, but could be anything else). I am trying to decide whether I should use inheritance or composition, but at some point both pose a problem I am not sure how to solve:

A) If I use inheritance, with something like:

class MySocket(socket.socket):
    def __init__(self, *args, **kwargs):
        socket.socket.__init__(self, *args, **kwargs)
        self.queue = queue.Queue()

then I have problems when I use operations like

connection, client_address = s.accept()

because the accept method of sockets returns objects of type socket.socket, and not of type MySocket, and I am not sure how to convert one into the other. If there is is a trivial OOP way to do this, I do not know it.

B) The problem before is trivially solved if I used composition instead:

class MySocket(socket.socket):
    def __init__(self, true_socket):
        self.true_socket = true_socket
        self.queue = queue.Queue()

I would simply implement something like

def accept(self, *args, **kwargs):
    con, cli = socket.socket.accept(self, *args, **kwargs)
    return self.__class__(con), cli

But then I have another problem. When I need to do

readable, writable, exceptional = select.select(inputs, outputs, inputs)

select works for socket.socket. With the A) version I would expect this to work just as is, but with composition, now that MySockets are not instances of socket.socket, select is broken.

So, what is the best, pythonistic approach for this?

EDIT: I forgot to say that the first thing I tried was to add the attribute directly to instances of `socket.socket':

s = socket.socket(...)
s.queue = queue.Queue()

but I got an exception saying the attribute is unknown for 'socket.socket'.


Solution

  • You can use the following, based on socket.socket.dup() in Lib/socket.py:

        import _socket
    
        class MySocket(socket.socket):
            def __init__(self, *args, **kwargs):
                super(MySocket, self).__init__(*args, **kwargs)
                self.queue = queue.Queue
    
            @classmethod
            def copy(cls, sock):
                fd = _socket.dup(sock.fileno())
                copy = cls(sock.family, sock.type, sock.proto, fileno=fd)
                copy.settimeout(sock.gettimeout())
                return copy
    

    You can now use the constructor to create new MySockets, or use MySocket.copy() to create a MySocket from an existing socket.socket. Note that in most cases you should close the original socket after creating the copy.