Subclassing and built-in methods in Python - python

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?

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)

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

Related

Selective Python Serialization

import json
import socket
class node:
def __init__(self,name,hostname,port,connected = False) -> None:
self.nodeName = name
self.hostname = hostname
self.port = port
self.connectStatus = connected
# self.outgoingSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
class NodeEncoder(json.JSONEncoder):
def default(self, o):
return o.__dict__
I am using this code to serialize this object. However, I want to also attach a socket to each object and still be able to print it.
However, when uncommenting the line with the outgoing socket I get this
AttributeError: 'socket' object has no attribute '__dict__'. Did you mean: '__dir__'?
I understand this is because a socket cannot be turned into a dictionary. Is there any way I can make it so everything stays the same but the socket just prints or whatever the default is?

How to modify or extend standard socket class in Python?

I am beginner in programming and trying to develop simple console messenger on Python. I have an idea to extend standard socket.socket object and add to it additional attribute "account_name". I created new class "NamedSoket" based on standard socket class. Here is my code:
class NamedSocket(socket):
def __init__(self, family=-1, type=-1, proto=-1, fileno=None, name=None):
super().__init__(family=-1, type=-1, proto=-1, fileno=None)
self.name = name
def accept(self):
fd, addr = self._accept()
sock = NamedSocket(self.family, self.type, self.proto, fileno=fd, name=self.name)
if getdefaulttimeout() is None and self.gettimeout():
sock.setblocking(True)
return sock, addr
server = NamedSocket()
server.bind(('', 8000))
server.listen()
client = NamedSocket('Bob')
client.connect(('localhost', 8000))
new_client, address = server.accept()
Although new socket objects are created successfully, they do not work properly.. Methods 'recv' and 'send' do not work.. Could you please explain to me, where is the problem?
P.S.: I understand, that my 'idea', maybe, is not good 'at all', but now I became very interested in 'inheritance issue'. From first look, it should work, but it does not...
I've replicated described actions as accurate as possible. This code is working without any error.
import socket as sk
class NamedSocket(sk.socket):
def __init__(self, family=sk.AF_INET, type=sk.SOCK_STREAM, proto=0, fileno=None, name=None):
super().__init__(family, type, proto, fileno)
self.name = name
def accept(self):
fd, addr = self._accept()
sock = NamedSocket(self.family, self.type, self.proto, fileno=fd, name=self.name)
if sk.getdefaulttimeout() is None and self.gettimeout():
sock.setblocking(True)
return sock, addr
server = NamedSocket()
server.bind(('', 8000))
server.listen()
client = NamedSocket(name='Bob')
client.connect(('localhost', 8000))
new_client, address = server.accept()
client.send('hello'.encode())
new_client.recv(1024)
I replaced default parameters in __init()__ method of NamedSocket class to AF_INET, SOCK_STREAM and 0 for the first three arguments. Running such script does not imply any error. You could try do the same changes or edit something related to IP address binded to socket being established on the server side, according to error message.
As you may see, your constructor method takes a bunch of optional parameters:
def __init__(self, family=-1, type=-1, proto=-1, fileno=None, name=None):
super().__init__(family=-1, type=-1, proto=-1, fileno=None)
self.name = name
When you try to create an object of class NamedSocket for client, you pass the only 'Bob' parameter, which, by precedence of function argument, will be passed to family argument, but not to name. For doing things in the key you want, you may write:
client = NamedSocket(name='Bob')

How to use an parent instance to replace child object's parent in python

Here is my code:
import socket
import select
class My_socket(socket.socket):
def __init__(self, soc = None):
if soc == None:
socket.socket.__init__(self, socket.AF_INET, socket.SOCK_STREAM)
....
else:
# how to use soc to initialize or replace parent part
....
rs, ws, es = select.select([my_socket_instance],[],[])
....
I want to create a my_socket class which has more functions, and when u pass no argument to its constructor, it will initialize its parent part automatically, but when u pass a socket instance as argument, it uses it as its parent so that my_socket can still work fine like just a socket ( for instance as an input of select function)
More specifically I met this problem when I get the (conn, addr) as a return of socket.accept(), and conn is socket type, I want to turn it to my_socket type, how to achieve that
thanks

How can I pass parameters to a RequestHandler?

The Python documentation includes an example of creating an HTTP server:
def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
server_address = ('', 8000)
httpd = server_class(server_address, handler_class)
httpd.serve_forever()
A RequestHandler class is provided to the Server, which then takes care of instantiating the handler automatically.
Let's say I want to pass in custom parameters to the request handler when it's created. How can and should I do that?
More specifically, I want to pass in parameters from the command line, and having to access sys.argv inside the request handler class seems unnecessarily clunky.
It seems like this should be possible by overriding parts of the Server class, but I feel like I'm overlooking a simpler and better solution.
I solved this in my code using "partial application".
Example is written using Python 3, but partial application works the same way in Python 2:
from functools import partial
from http.server import HTTPServer, BaseHTTPRequestHandler
class ExampleHandler(BaseHTTPRequestHandler):
def __init__(self, foo, bar, qux, *args, **kwargs):
self.foo = foo
self.bar = bar
self.qux = qux
# BaseHTTPRequestHandler calls do_GET **inside** __init__ !!!
# So we have to call super().__init__ after setting attributes.
super().__init__(*args, **kwargs)
def do_HEAD(self):
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
def do_GET(self):
self.do_HEAD()
self.wfile.write('{!r} {!r} {!r}\n'
.format(self.foo, self.bar, self.qux)
.encode('utf8'))
# We "partially apply" the first three arguments to the ExampleHandler
handler = partial(ExampleHandler, sys.argv[1], sys.argv[2], sys.argv[3])
# .. then pass it to HTTPHandler as normal:
server = HTTPServer(('', 8000), handler)
server.serve_forever()
This is very similar to a class factory, but in my opinion it has a couple of subtle advantages:
partial objects are much easier to introspect for what's inside them than nested classes defined and returned by factory functions.
partial objects can be serialized with pickle in modern Python, whereas nested class definitions inside factory functions cannot (at least not without going out of your way to code a __reduce__ method on the class to make it possible).
In my limited experience explicit "pre-attaching" of arguments with partial to an otherwise Pythonic and normal class definition is easier (less cognitive load) to read, understand, and verify for correctness than a nested class definition with the parameters of the wrapping function buried somewhere inside it.
The only real disadvantage is that many people are unfamiliar with partial - but in my experience it is better for everyone to become familiar with partial anyway, because partial has a way of popping up as an easy and composable solution in many places, sometimes unexpectedly, like here.
Use a class factory:
def MakeHandlerClassFromArgv(init_args):
class CustomHandler(BaseHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super(CustomHandler, self).__init__(*args, **kwargs)
do_stuff_with(self, init_args)
return CustomHandler
if __name__ == "__main__":
server_address = ('', 8000)
HandlerClass = MakeHandlerClassFromArgv(sys.argv)
httpd = HTTPServer(server_address, HandlerClass)
httpd.serve_forever()
At the time of this writing all the answers here essentially stick to the (very awkward) intention that the author of the socketserver module seemed to have that the handler passed in be a class (i.e. constructor). Really the only thing that's required of the handler is that it's callable, so we can work around the socketserver API by making instances of our handler class callable and having them run the superclass's __init__ code when called. In Python 3:
class MyHandler(http.server.BaseHTTPRequestHandler):
def __init__(self, message):
self.message = message
def __call__(self, *args, **kwargs):
"""Handle a request."""
super().__init__(*args, **kwargs)
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(self.message.encode("utf-8"))
This keeps the superclass "constructor" call out of __init__ which eliminates the possibility of dispatching a request (from the superclass's constructor) before the subclass's constructor is finished. Note that the __init__ override must be present to divert execution even if it's not needed for initialization; an empty implementation using pass would work.
With this design the weird interface is hidden and using the API looks more natural:
handler = MyHandler("Hello world")
server = http.server.HTTPServer(("localhost", 8000), handler)
server.serve_forever()
I would just comment on Thomas Orozco's answer but since I can't..
Perhaps this will help others who also run into this problem. Before Python3, Python has "old-style" classes, and BaseHTTPRequestHandler seems to be one of them. So, the factory should look like
def MakeHandlerClassFromArgv(init_args):
class CustomHandler(BaseHTTPRequestHandler, object):
def __init__(self, *args, **kwargs):
do_stuff_with(self, init_args)
super(CustomHandler, self).__init__(*args, **kwargs)
return CustomHandler
to avoid errors like TypeError: must be type, not classobj.
Why not just subclass the RequestHandler ?
class RequestHandler(BaseHTTPRequestHandler):
a_variable = None
class Server(HTTPServer):
def serve_forever(self, variable):
self.RequestHandlerClass.a_variable = variable
HTTPServer.serve_forever(self)
def run(server_class=Server, handler_class=RequestHandler):
server_address = ('', 8000)
httpd = server_class(server_address, handler_class)
variable = sys.argv
httpd.serve_forever(variable)
Ref Subclassing the HTTPServer is another option. Variables on the server are accessible in the Request Handler methods via self.server.context. It basically works like this:
class MyHTTPServer(HTTPServer):
def __init__(self, *args, **kwargs):
HTTPServer.__init__(self, *args, **kwargs)
self.context = SomeContextObject()
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
context = self.server.context
...
# Drawback, Notice that you cannot actually pass the context parameter during constructor creation, but can do it within the __init__ of the MyHTTPServer
server = MyHTTPServer(('', port), MyHandler)
server.serve_forever()
If you do not need instance properties, but only class properties you could use this approach:
def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
server_address = ('', 8000)
httpd = server_class(server_address, handler_class)
httpd.RequestHandlerClass.my_custom_variable = "hello!"
httpd.serve_forever()
or maybe you could:
def run(server_class=HTTPServer, handler_class=BaseHTTPRequestHandler):
server_address = ('', 8000)
httpd = server_class(server_address, handler_class)
httpd.my_custom_variable = "hello!"
httpd.serve_forever()
and retrieve in your RequestHandler with:
self.server.my_custom_variable
Using a lambda is a pretty simple way to create a new function that takes the request handler args and creates your custom class.
Here I want to pass a variable that will be used in do_POST(), and set the directory used by SimpleHTTPRequestHandler, so setup calls
HTTPServer(('', 8001), lambda *_: _RequestHandler("[1, 2]", *_, directory=sys.path[0]))
Full program:
from http.server import HTTPServer, SimpleHTTPRequestHandler
import sys
class _RequestHandler(SimpleHTTPRequestHandler):
def __init__(self, x, *args, **kwargs):
self.x = x # NEEDS TO HAPPEN BEFORE super().__init__()
super().__init__(*args, **kwargs)
def _set_headers(self):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
def do_POST(self):
print("POST")
length = int(self.headers.get('content-length'))
message = self.rfile.read(length).decode('utf-8')
print(message)
self._set_headers()
self.wfile.write(self.x.encode('utf-8'))
def run_server():
server_address = ('', 8001)
httpd = HTTPServer(server_address, lambda *_: _RequestHandler("[1, 2]", *_, directory=sys.path[0]))
print('serving http://localhost:8001')
httpd.serve_forever()
if __name__ == '__main__':
run_server()
Never do it with a global. Use the factory described in other answers.
CONFIG = None
class MyHandler(BaseHTTPRequestHandler):
def __init__(self, ...
self.config = CONFIG # CONFIG is now 'stuff'
if __name__ == "__main__":
global CONFIG
CONFIG = 'stuff'
server_address = ('', 8000)
httpd = HTTPServer(server_address, MyHandler)
httpd.serve_forever()
(except maybe in the privacy of your own home)

Twisted Protocol Instance Variables?

CLIENT:
#!/usr/bin/env python
from twisted.internet import reactor, protocol
class EchoClient(protocol.Protocol):
def __init__(self, arg):
self.arg = arg
def connectionMade(self):
self.transport.write("hello, world!")
def dataReceived(self, data):
print "Server said:", data
self.transport.loseConnection()
def connectionLost(self, reason):
print "connection lost"
class EchoFactory(protocol.ClientFactory):
protocol = EchoClient
def buildProtocol(self, address):
proto = protocol.ClientFactory.buildProtocol(self, address, 12)
self.connectedProtocol = proto
return proto
def clientConnectionFailed(self, connector, reason):
print "Connection failed - goodbye!"
reactor.stop()
def clientConnectionLost(self, connector, reason):
print "Connection lost - goodbye!"
reactor.stop()
def main():
f = EchoFactory()
reactor.connectTCP("localhost", 8000, f)
reactor.run()
if __name__ == '__main__':
main()
SERVER:
#!/usr/bin/env python
from twisted.internet import reactor, protocol
from twisted.application import service, internet
class Echo(protocol.Protocol):
def dataReceived(self, data):
self.transport.write(data)
def main():
factory = protocol.ServerFactory()
factory.protocol = Echo
reactor.listenTCP(8000,factory)
reactor.run()
if __name__ == '__main__':
main()
ERROR:
exceptions.TypeError: buildProtocol() takes exactly 2 arguments (3 given)
QUESTION:
How can I get the EchoClient class in the CLIENT to accept parameters and assign instance variables ( such as arg in the EchoClient constructor above)? As noted below, it was previously suggested that I override the buildProtocol function, but my attempt at doing so has lead me to the above error. I am not really sure where to go from here. I suppose my question can be generalize to: how can I add instance variables to a protocol?
you wrote:
def buildProtocol(self, address):
proto = protocol.ClientFactory.buildProtocol(self, address, 12)
that is, you are overriding ClientFactory.buildProtocol and calling the parent class with a different signature than it knows how to handle.
Passing data from the factory to the client is only a little tricky. You can provide any __init__ you want to the factory, but twisted creates instances of IProtocol itself. Fortunately, most factories assign themselves to the factory attribute of the protocol, once it's ready to go:
class MyClientProtocol(protocol.Protocol):
def connectionMade(self):
# use self.factory here:
self.transport.write(self.factory.arg)
class MyClientFactory(protocol.ClientFactory):
protocol = MyClientProtocol
def __init__(self, arg):
self.arg = arg
In fact, the whole ProtocolFactory business is to support this kind of use; but be mindful; many instances of Protocol will share a single instance of their factory; use the factory for configuration but manage state in the protocol.
It's certainly possible that the way the standard family of Protocol/Factory implementations don't suit your needs, and that's also reasonable, so long as you fully implement the IProtocol and IProtocolFactory interfaces. The base classes exist because they handle most of the cases for you, not because they are the only possible implementation.
It's not clear from your question what exactly your tryed and what exactly the error was, but anyway you have to do two steps:
Make EchoClient's constructor take whatever arguments you need it to take and initialise whatever field you need it to initialise.
Override buildProtocol method in your factory to supply those arguments to your protocol.

Categories