practical considerations about backporting a feature from python 3.3 to 2.7 versus monkeypatching

StackOverflow https://stackoverflow.com/questions/10746736

  •  10-06-2021
  •  | 
  •  

Question

I'm interested in a new smtplib feature introduced in python3.3: the ability to bind to an specific IP address in a multihomed machine (or a machine with multiple IP addresses).

Many building blocks I would like to use are not ported to 3.3 or have very unstable ports, so the idea of using 3.3 just because of this feature is not practical.

In order to backport this feature, I can patch or monkeypatch. I'm inclined to subclass smtplib.SMTP and monkeypatch the underlying socket, because it simplifies deployment, seems to be unlikely to affect the base class and is easier than a politically correct backport.

In the ruby world, monkeypatching are more tolerated, but in most python circles this dangerous-yet-frequently-useful technique is frowned upon.

My question is: have you ever faced such decision and/or would like to share some advice?

(I'm interested in the pros and cons of each approach)

[update]

pps actually, thinking some more, i always assumed monkey patching meant somehow modifying existing classes in-place so that the new code is invoked when loading from the standard location (i must admit, now that i think about it, i have no idea how you could do this). that is not what i am suggesting here - this subclass would be a new class, in my own module, used only by my own code. [andrew cooke]

Andrew, thanks for taking time to answer. This way I would be forking some SMTP.connect code, and it is also frowned upon because when the original library is updated, my forked code will not incorporate the change. I think of mokeypatching as something more surgical, but such updates also have potential to break the code if there is any refactoring around the monkeypatched code. Either forking or monkeypatching my jedi masters like not, to the dark side they lead. :-)

[update]

In the end I just wrote an SMTP proxy wich accepts an extended EHLO syntax allowing to choose the outgoing IP address:

s = SMTP('localhost', 8025)
# will use 173.22.213.16 as the outgoing IP address
s.ehlo('example.com 173.22.213.16') 

Using twisted it was under 40 SLOC, twisted is amazing for network code, and I can do everything in 2.7, at the expense of running another process.

Was it helpful?

Solution

what i would do is subclass SMTP and replace the SMTP.connect() method with one that was almost identical, but which called self.sock.connect() with the source_address (and an extra argument to set that).

i am not completely sure what monkey-patching means (i have never used ruby), but i think the above is completely normal for python. if you weren't meant to be able to overwrite that method then it would be named with a leading underscore. i haven't used SMTP myself, but i have used the HTTP lib and have done similar things to add authentication to an HTTP server. this was paid, professional work, and i had no worries whatsoever about doing so (the simple HTTP server isn't intended for heavy use, but it's useful in certain cases).

(i can't really provide any discussion of pros and cons because i don't see what the alternative is - do you mean dig out the patch and apply it? i guess you could do that, but it might contain many more changes - if i knew a patch existed i would probably give it a quick read to make sure it was consistent with what i was doing, but that's all. creating a new, patched, "official" smtplib seems like a lot more work for no real gain - this isn't rocket-science code, it's just a bind parameter.)

ps i would provide any extra argument with a useful default value so that if called with old code, the call would still work.

source of SMTP, although i am unsure what version; socket docs; smtplib docs

pps actually, thinking some more, i always assumed monkey patching meant somehow modifying existing classes in-place so that the new code is invoked when loading from the standard location (i must admit, now that i think about it, i have no idea how you could do this). that is not what i am suggesting here - this subclass would be a new class, in my own module, used only by my own code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top