15

I am working on a project that aims to augment the Python socket messages with partial ordering information. The library I'm building is written in Python, and needs to be interposed on an existing system's messages sent through the socket functions.

I have read some of the resources out there, namely the answer by @Omnifarious at this question python-importing-from-builtin-library-when-module-with-same-name-exist

There is an extremely ugly and horrible thing you can do that does not involve hooking the import mechanism. This is something you should probably not do, but it will likely work. It turns your calendar module into a hybrid of the system calendar module and your calendar module.

I have implemented the import mechanism solution, but we have decided this is not the direction we'd like to take, since it relies too much on the environment. The solution to merge classes into a hybrid, rather than relying on the import mechanisms, seems to be the best approach in my case.

Why has the hybrid been called an ugly and horrible solution? I'd like to start implementing it in my project but I am wary of the warnings. It does seem a bit hackish, but since it would be part of an installation script, wouldn't it be OK to run this once?

Here is a code snippet where the interposition needs to intercept the socket message before it's sent:

class vector_clock:

  def __init__(self):
   """
   Initiate the clock with the object
   """
   self.clock = [0,0]

  def sendMessage(self):
   """
   Send Message to the server
   """
   self.msg = "This is the test message to that will be interposed on"
   self.vector_clock.increment(0) # We are clock position 0

   # Some extraneous formatting details removed for brevity….
   # connectAndSend needs interpositioning to include the vector clock

   self.client.connectAndSend(totalMsg);
   self.client.s.close()
Community
  • 1
  • 1
jspacek
  • 1,895
  • 3
  • 13
  • 16
  • 2
    Please emphasize what the question is. I guess you received a downvote because it is not clear what you are asking. – kapa May 21 '14 at 09:25
  • @kapa Thanks so much for your suggestion! I will edit to make clearer. – jspacek May 21 '14 at 14:50
  • 2
    The title sounds like you're trolling, not quoting :/ –  May 21 '14 at 16:18
  • @Will - Oh no, really?! I am using the wording from the original answer, which is what Omnifarious called his solution. I will update it to make it less inflammatory, since it's not obvious why I referred to it in this way. – jspacek May 21 '14 at 16:28
  • 2
    Much better now, and the original problem is also solved: you can comment everywhere :). – kapa May 21 '14 at 19:22
  • 1
    Have you taken a look at monkey-patching socket, the way gevent (http://www.gevent.org/intro.html#monkey-patching) does? I'm not sure if this is what you mean by "merging" or not, but the gevent folks are using this approach quite successfully in a project used by lots of people. – dano May 21 '14 at 21:05
  • @kapa Yes, I can comment now! This will be much better, thanks for all of your help :) – jspacek May 22 '14 at 02:12
  • @dano This is a very interesting article. I've heard of dynamic functions, but only inasmuch as I learned in an interpreter course! I wasn't aware that they were called monkey-patching though. I also found this link http://blog.codinghorror.com/monkeypatching-for-humans/. It's generally not considered good practice because it's so difficult to debug, but I don't see why this would be an issue if I used it just to append to a message. I will look into it - thank you for bringing this option to my attention! – jspacek May 22 '14 at 02:15

1 Answers1

2

From my understanding of your post, you wish to modify the existing socket library to inject your own functionality into it.

Yes, this is completely doable, and possibly it is even the easiest solution to your problem, but you have to consider all of the implications of what you are doing.

The most important point is that you are not just modifying socket for yourself, but for anything that is run in any part of your process which uses the socket library unless it uses it's own class loader. I understand that there is probably some existing library you are using which uses socket and you want to inject this functionality into it, but this will affect EVERYTHING.

From this you have to consider the question: is your change 100% backwards compatible. Unless you can guarantee that you know every single use case of socket by any library used by your process (hint: you can't), then you need to make sure that it completely preserves all existing functionality or else somewhere down the road stuff in some core library is going to mysteriously break and you will have no idea why and no way to debug it. An example of something 100% backwards compatible (or as close as it is possible to get) is injecting a decorator which saves timing information to one of your own modules.

If you completely understand this and still think that your solution is a good one then I say "go for it". However, have you considered any alternatives?

If you just need to inject this functionality for a specific set of libraries that you use, then I would suggest doing something like patching: https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch

You could subclass whatever core library you want to modify and then patch the library to use your class instead. At it's core, what patch does is it modifies the global bindings used in the target module to use a different class/module than the one it had originally used.

PS. I don't think yours is a situation which calls for hooking the import mechanism.

Brendan F
  • 619
  • 3
  • 8
  • My __init__.py file in my vector timestamp library calls a static function with `patcher = mock.patch('__main__.socket.socket', SVSocket.SVSocket)`. One tricky issue was stopping the patch so that I could create a plain socket.socket variable inside of the decorator SVSocket class. For this, I used `mock.patch.stopall()`. A work in progress, but the full code is here if it's of some use https://bitbucket.org/bestchai/shivector/src/default/python/ – jspacek Jul 19 '14 at 20:53
  • `mock.patch.stopall()` has an issue http://bugs.python.org/issue21239. A fixed version of mock.py is included in the link that I've used with great success for Python versions < 3.5. – jspacek Aug 12 '14 at 14:38