tags:

views:

206

answers:

3

I have a class that inherits from httplib.HTTPSConnection.

class MyConnection(httplib.HTTPSConnection):
  def __init__(self, *args, **kw):
    httplib.HTTPSConnection.__init__(self,*args, **kw)
    ...

Is it possible to turn off the SSL layer when the class is instantiatied so I can also use it to communicate with non-secure servers?

I my case it is known before initialisation if SSL should be used so another solution would be to try to switch the inheritance from httplib.HTTPSConnection to httplib.HTTPConnection, but I am also not sure how to do this in a sensible way?

+2  A: 

Per your last paragraph, in Python you can use something like a factory pattern:

class Foo:
    def doit(self):
        print "I'm a foo"
class Bar:
    def doit(self):
        print "I'm a bar"

def MakeClass(isSecure):
    if isSecure:
        base = Foo
    else:
        base = Bar

    class Quux(base):
        def __init__(self):
            print "I am derived from", base

    return Quux()

MakeClass(True).doit()
MakeClass(False).doit()

outputs:

I am derived from __main__.Foo
I'm a foo
I am derived from __main__.Bar
I'm a bar
Mark Rushakoff
Thanks for this insight. Still I wonder if this is the only way to handle both cases when inheriting from HTTPSConnection?
pkit
@pkit, a factory approach is not the ONLY way -- it's just BY FAR the BEST way -- simple, clean, direct, flawless. I wouldn't do it exactly as Mark did, in detail (this generates many separate but identical classes) but I'm still +1 on general grounds.
Alex Martelli
@Alex, thanks for your response. Looking at the httplib code it seems that only the connect method is re-implemented. Would monkey patching the HTTPSConnection instance with the HTTPConnection.connect method make sense? Or is this just plain ugly?
pkit
@pkit, I dislike monkeypatching on general principles!-) Sometimes it's nevertheless the only viable solution, but that's not the case here.
Alex Martelli
A: 

Apparently, you want to use MyConnection for multiple subsequent connections to different hosts. If so, you shouldn't be inheriting from HTTP(S)Connection at all - that class isn't really meant to be used for multiple connections. Instead, just make MyConnection have a HTTP(S)Connection.

Martin v. Löwis
In my case I actually use it just for one connection to one host. That's why I know before the instatiation if it should be with SLL or wihtout.
pkit
+1  A: 

As per my comment on @Mark's answer, I like the factory approach he's advocating. However, I wouldn't do it exactly his way, because he makes a new class afresh every time. Rather, this is a nice use case for mixin MI and super, as follows:

class MyConnectionPlugin(object):
  def __init__(self, *args, **kw):
    super(MyConnectionPlugin, self).__init__(*args, **kw)
    # etc etc -- rest of initiatizations, other methods

class SecureConnection(MyConnectionPlugin,
                       httplib.HTTPSConnection, object):
  pass

class PlainConnection(MyConnectionPlugin,
                      httplib.HTTPConnection, object):
  pass

def ConnectionClass(secure):
  if secure:
    return SecureConnection
  else:
    return PlainConnection

conn = ConnectionClass(whatever_expression())()

etc.

Now, alternatives ARE possible, since a Python object can change its own __class__, and so forth. However, like shooting flies with a rhino rifle, using excessive force (extremely powerful, deep, and near-magical language features) to solve problems that can be nicely solved with reasonable restraint (the equivalent of a flyswatter), is NOT recommended;-).

Edit: the extra injection of object in the bases is needed only to compensate for the sad fact that in Python 2.* the HTTPConnection class is old-style and therefore doesn't play well with others -- witness...:

>>> import httplib
>>> class Z(object): pass
... 
>>> class Y(Z, httplib.HTTPConnection): pass
... 
>>> Y.mro()
[<class '__main__.Y'>, <class '__main__.Z'>, <type 'object'>, <class httplib.HTTPConnection at 0x264ae0>]
>>> class X(Z, httplib.HTTPConnection, object): pass
... 
>>> X.mro()
[<class '__main__.X'>, <class '__main__.Z'>, <class httplib.HTTPConnection at 0x264ae0>, <type 'object'>]
>>>

The method-resolution order (aka MRO) in class Y (without the further injection of an object base) has object before the class from httplib (so super doesn't do the right thing), but the extra injection jiggles the MRO to compensate. Alas, such care is needed in Python 2.* when dealing with bad old legacy-style classes; fortunately, in Python 3, the legacy style has disappeared and every class "plays well with others" as it should!-)

Alex Martelli
Thanks. Again something I did not know 'super'. This saves me from indenting my whole file to enclose it in the factory code. Will use this.
pkit
@pkit, always glad to help! `super` always needs to be used with care, cfr http://fuhm.net/super-harmful/ , but for this unusual case of mixins needing specialized `__init__`s it's truly a great fit.
Alex Martelli
When I use this it seems that HTTP(S)Connection.__init__ are not called. Could it be that I am missing something in this class setup?
pkit
@pkit, that `__init__` would called by the `super...__init__` idiom... IF, as I said, HTTPConnection was new-style, but, alas, it's a bad old legacy style class. Let me edit the answer accordingly.
Alex Martelli
There, I've edited to show a workaround and, I hope, explain the root of the problem.
Alex Martelli
Thank you for the edit. With this info and after also reading James Knight's article I have a better understanding of the 'super' powers.
pkit