Search code examples
pythonpython-3.xsocketsinheritancesuperclass

Initializing a subclass with an existing superclass instance in python3


I am writing my own version of the socket class in python. I was wondering if there is a way to not initialize the subclass using it's __init__ method, but rather initialize it using an existing instance of the socket class.

Basically, what I am trying to do, is inherit from the socket class, and overwrite the accept() method to return an object of my own class instead of the socket class.

To get around this problem I want to implement the possibility to set a base socket in my __init__ method in my custom socket class, which takes the instance of a socket as argument and creates a new instance of my custom socket class using an existing instance of the socket class, or do the same using a fromSocket() classmethod.

My question now is how do I set an existing instance of the socket class as superclass for my own socket without using an attribute to contain the socket object? (Is that even possible in python?)

All the other related questions I found were more on how to change a superclass at runtime. I may have overlooked the answer somewhere, but I couldn't find it.

My code looks something like this:

import socket
class MySocket(socket.socket):
    def __init__(self, sock:socket.socket=None):
        if sock != None:
            # initialize using existing instance contained in sock
            # How? (My question)
        else:
            super().__init__() # Default conf is enough for now
    def accept(self):
        s, addr = super().accept()
        return MySocket(sock=s) # <= This is what I want to do

and I want to call it like this:

s = MySocket()
# Call bind and listen, left out because it doesn't matter here
client, addr = s.accept() 
# Client is an instance of socket.socket, but it should be MySocket

Solution

  • Here's the source code for socket.accept().

    Honestly, this looks like a mistake on the socket class, they've hardcoded it to create a new instance of socket:

    sock = socket(self.family, self.type, self.proto, fileno=fd)
    

    When it probably should fetch the type of self instead:

    sock = type(self)(self.family, self.type, self.proto, fileno=fd)
    

    This seems to be done in multiple methods, it looks like your best chance is to copy all the attributes manually from the returned socket to your custom instance. The dup() method is hopefully correct, so we can create a classmethod that repeats its functionality:

    import socket
    
    class MySocket(socket.socket):
        @classmethod
        def from_socket(cls, sock):
            fd = socket.dup(sock.fileno())
            my_sock = cls(sock.family, sock.type, sock.proto, fileno=fd)
            my_sock.settimeout(sock.gettimeout())
            return my_sock