3
\$\begingroup\$
import ssl
from socket import socket

class testsock(socket):
    def __init__(self, tls=True):
        super(testsock, self).__init__()
        self.connect(('192.168.0.1', 443))
        if tls:
            self = ssl.wrap_socket(self) # Ugly "handover"

testsock(tls=True)

This code works, it's the second ugliest bodge I've done as of today.
But it works with the behavior I desire, that .send() and .close() etc are "intact".

However, if I ever try to add any other values to my custom class testsock those will effectively be gone, since I replace self with whatever wrap_socket spits out.

I could use something that looks like this:

class testsock():
    def __init__(self, tls=True):
        self.s = socket()
        ...
        self.s = ssl.wrap_socket(self.s)

    def send(self, data):
        self.s.send(data)

But that goes against the elegance of inheriting the socket object and not having to create all the functions you'd expect out of a ordinary socket. (I know, lazy programming, but it looks cleaner without all the definitions).

I could probably iterate over all the .__dict__ or inspect.getmembers() functions and replace them with the matching available items from wrap_socket, but that would also cause for concerns later on, but it would look something like:

for name, obj in inspect.getmembers(self):
    print(name, obj)

It would yield something like:

close <bound method socket.close of <__main__.testsock fd=...>>

I'm not sure how I'd get socket from this, obj.__class__.__name__ doesn't yield this. If I could some how get socket from the object I would know that it's a socket() bound method, and replace the corresponding method from wrap_socket() return.

I've heard of metaclass programming, and I'm having a go at it.
But my question is, out of all the options - Which would be the best according to the community?

Would it be possible to inline-replace the inherited socket object with whatever ssl.wrap_socket returns?

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Aren't you just overriding the local variable self? \$\endgroup\$
    – Graipher
    Jan 19, 2017 at 12:45
  • \$\begingroup\$ @Graipher self is mutable and passed as a reference whenever a class function is called (self is just my name for that parameter, as you've already figured out). If you replace that you effectively replace the entire instance of the class with whatever you insert instead. If you do a print(class_handle) it will result in <ssl.SSLSocket fd=...> instead of <__main__.testsock fd=...>. \$\endgroup\$
    – Torxed
    Jan 19, 2017 at 12:49
  • \$\begingroup\$ You mention adding attributes to self but I see none in your code. Is that the code you are using then, or is this a draft where you want to add the desired behaviour of being able to add attributes? \$\endgroup\$ Jan 19, 2017 at 15:09
  • \$\begingroup\$ @MathiasEttinger It is, I simply stated that if i were to do self.moo = 'cow' either in __init__ or later on in a function, that would never be possible because anything in __init__ will be overwritten and any functions defined will also be gone. \$\endgroup\$
    – Torxed
    Jan 19, 2017 at 15:10
  • 1
    \$\begingroup\$ @Torxed: self may be mutable, but reassigning it replaces it, it doesn't mutate it. It won't change the object that you get from running sock = testsock(tls=True); sock will never be SSL. The only way that replacement would work is if literally all of your code using the testsock is run under __init__. Otherwise, when tls=True, no modifications to self after that line will be reflected in the final object you get from construction (and it will never be SSL enabled in any event, unless the wrap_socket function is actually mutating the socket it's passed in some way). \$\endgroup\$ Jan 19, 2017 at 17:06

1 Answer 1

4
\$\begingroup\$

You don't need a class here, a simple function will do:

import ssl
from socket import socket

def testsock(tls=True):
    sock = socket()
    sock.connect(('192.168.0.1', 443))
    if tls:
        sock = ssl.wrap_socket(sock)
    return sock

testsock(tls=True)

Returned objects are either plain sockets or ssl sockets but they share some common behaviour (like send or recv). If you need to add attributes, you can do that before the return statement.

In case you wish to be able to extend it better, you can still use the socket as an attribute on a custom class and expose the socket API through __getattr__:

class TestSock(object):
    def __init__(self, tls=True):
        sock = socket()
        sock.connect(('192.168.0.1', 443))
        if tls:
            sock = ssl.wrap_socket(sock)
        self.socket = sock
        # other attributes

    def __getattr__(self, attribute_name):
        """Defer unknown behaviour to the socket"""
        return getattr(self.socket, attribute_name)

    # other methods

But you don't need it for now.

\$\endgroup\$
3
  • \$\begingroup\$ I do wish to keep it as a class in order to expand this further in the future. That's one of my statements above. But this is the type of meta-programming I was beginning to explore, the use of hooking into the function calls and map it against either socket or wrap_socket functions. I'm willing to accept this as a reasonable answer even tho it's not quite what I had in mind. I was hoping for a way to replace the inherited structure of socket with that of wrap_socket. But I guess it's not really possible. So the closest I'll get is local variables and hook into __getattr__ etc. \$\endgroup\$
    – Torxed
    Jan 19, 2017 at 15:26
  • \$\begingroup\$ Took a while for me to find the word I was looking for, I want to re-inherit my class to match the wrap_socket object. Your approach would fully simulate this - absolutely. So might go with this instead of my bodge that Im afraid of posting here haha. \$\endgroup\$
    – Torxed
    Jan 19, 2017 at 15:32
  • \$\begingroup\$ I've found no better way to re-intialize a inheritance in a class, so this is probably the best option. \$\endgroup\$
    – Torxed
    Jan 23, 2017 at 19:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.