Skip to content

Instantly share code, notes, and snippets.

@colinmarc
Created March 31, 2012 20:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save colinmarc/2268248 to your computer and use it in GitHub Desktop.
Save colinmarc/2268248 to your computer and use it in GitHub Desktop.
an approach to monkeypatching in gevent
__target__ = 'socket'
#TODO find a way to not require this line
_real = __import__(__target__)
__implements__ = [
'socket',
'getaddrinfo',
#'gethostname',
#'gethostbyname',
#'gethostbyaddr',
#'getservbyname',
#'getprotobyname' #omitted for brevity
]
#this is stuff that our patch requires
__before__ = [
'error',
'herror',
'gaierror',
'timeout',
'_GLOBAL_DEFAULT_TIMEOUT'
]
#this is stuff that uses our patch, within the same module
__after__ = [
'create_connection',
'getfqdn',
'fromfd',
'socketpair'
]
#whatever needs to be redefined, and nothing more, gets implemented here.
class socket(_real.socket):
def __init__(self, *args, **kwargs):
print('passthrough on socket')
_real.socket.__init__(self, *args, **kwargs)
def recv(self, *args, **kwargs):
#non-blocking code here
print('passthrough on read')
_real.socket.recv(self, *args, **kwargs)
#etc
# we would redefine getaddrinfo to be non-blocking (this is just a stub),
# but not create_connection, etc. create_connection would use our getaddrinfo and socket.
def getaddrinfo(*args, **kwargs):
#non-blocking code here
print('passthrough on getaddrinfo')
return _real.getaddrinfo(*args, **kwargs)
#this would be refactored out into monkey
def build_patch():
import imp
import builtins
import types
#create a blank module
mod = imp.new_module(__target__)
#add the real module as _real
mod._real = __import__(__target__)
#copy constants
for name in _real.__all__:
val = getattr(_real, name)
if isinstance(name, (int, str, bytes)):
setattr(mod, name, val)
#load requirements for patch
for name in __before__:
setattr(mod, name, getattr(_real, name))
#load patch
for name in __implements__:
setattr(mod, name, globals()[name])
#load parts of _real that require the patch
for name in __after__:
obj = getattr(_real, name)
#a little bit of magic is required for functions
if isinstance(obj, types.FunctionType):
ns = {'__builtins__': builtins}
ns.update(mod.__dict__)
obj = types.FunctionType(
obj.__code__,
ns,
obj.__name__,
obj.__defaults__,
obj.__closure__
)
setattr(mod, name, obj)
return mod
#usage:
# >>> import mysocket
# >>> socket = mysocket.build_patch()
# >>> socket.socket()
# passthrough on socket
# <mysocket.socket object, fd=3, family=2, type=1, proto=0>
# >>> socket.create_connection(('google.com', 80))
# passthrough on getaddrinfo
# passthrough on socket
# <mysocket.socket object, fd=4, family=2, type=1, proto=6>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment