6

For convenience, I wanted to subclass socket to create an ICMP socket:

class ICMPSocket(socket.socket):
    def __init__(self):
        socket.socket.__init__(
            self, 
            socket.AF_INET,
            socket.SOCK_RAW,
            socket.getprotobyname("icmp"))

    def sendto(self, data, host):
        socket.socket.sendto(self, data, (host, 1))

However, I can't override socket.sendto:

>>> s = icmp.ICMPSocket()
>>> s.sendto
<built-in method sendto of _socket.socket object at 0x100587f00>

This is because sendto is a "built-in method". According to the data model reference, this is "really a different disguise of a built-in function, this time containing an object passed to the C function as an implicit extra argument."

My question: is there anyway to override built-in methods when subclassing?

[Edit] Second question: if not, why not?

grifaton
  • 3,986
  • 4
  • 30
  • 42
  • 2
    If subclassing does not work then how about writing a class that exposes the same behavior(same interface) and internally holds an object of the desired class? – Nobody moving away from SE Aug 07 '11 at 22:13
  • The socket class is a little different. Your question could be updated to subclassing socket class. The Python socket class monkey-patches the internal (C) implementation methods in the constructor, which clobbers your inherited method. So really the best way to deal with is to wrap it in your own class. – Keith Aug 08 '11 at 00:15
  • @Keith -- yes, I got that impression from reading the source. But why? Must things be this way? – grifaton Aug 08 '11 at 00:36
  • I'm not sure why, but probably for legacy/cross-platform reasons. Legacy because a while ago internal C types could not be subclassed, so this wraps it in a Python source level class that can be (can be used to add other methods, such as makefile, but not override). It could probably be cleaned up. – Keith Aug 08 '11 at 01:22

2 Answers2

6

I know this doesn't answer your question, but you could put the socket into an instance variable. This is what Nobody also suggested in the comments.

class ICMPSocket():
    def __init__(self):
        self.s = socket.socket(
            socket.AF_INET,
            socket.SOCK_RAW,
            socket.getprotobyname("icmp"))
    def sendto(self, data, host):
        self.s.sendto(data, (host, 1))
    def __getattr__(self, attr):
        return getattr(self.s, attr)
Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
  • This would certainly work, although it's somewhat unsatisfactory. – grifaton Aug 07 '11 at 23:36
  • @grifaton Why is it unsatisfactory if it works? Delegation is a somewhat common pattern. – Keith Aug 07 '11 at 23:58
  • Well for one, it makes introspection difficult -- not the end of the world, but given an object it's nice to know what you can do with it (and it gives me tab-completion!). – grifaton Aug 08 '11 at 00:36
3

Re-edit : My first solution wasn't working, and after straggling with this for sometime , i can conclude that in the case of python socket when you can say that aggregation is much better than inheriting but in case you want to know how you can do it using inheritance check this code:

import socket


class ICMPSocket(socket.socket):
    def __init__(self):

        self._sock = socket.socket(
                        socket.AF_INET,
                        socket.SOCK_RAW,
                        socket.getprotobyname("icmp"))

        # Delete the methods overrited by the socket initializer to make
        # possible defining our own.
        for attr in socket._delegate_methods:
            try:
                delattr(self, attr)
            except AttributeError:
                pass

    def sendto(self, data, flags, addr):
        return self._sock.sendto(data, flags, (addr, 1))

icmp = ICMPSocket()

print icmp.sendto('PING', 0, '127.0.0.1')
mouad
  • 67,571
  • 18
  • 114
  • 106
  • Interesting... Which version of Python? – schlamar Aug 07 '11 at 22:44
  • @ms4py : the snippet in my answer work for 2.6 and 2.7, for python 3 the `sendto` argument must be changed to `sendto(self, data, flags_or_addr, addr=None)` and the call should be something like `sendto(b'', 0, ('127.0.0.1', icmp.port))` and i tested this with python 3.1 and 3.3. – mouad Aug 07 '11 at 23:00
  • 1) What does your ICMPSocket().sendto return? – grifaton Aug 07 '11 at 23:27
  • 2) Your ICMPSocket().sendto takes different arguments to mine -- mine deliberately does not take a port, since ICMP packets (at least pings) are not sent to a particular port. – grifaton Aug 07 '11 at 23:29
  • @grifaton : i redited my answer totally , my first code wasn't working at all, the code above is tested with 2.6 only. hope this will be helpful . – mouad Aug 08 '11 at 00:27
  • delattr raises exceptions, also for sendto, so this answer is illogic – Massimo Jan 04 '21 at 22:35