apply monkey-patches to stdlib modules in-place

    imports the relevant modules and simply overwrites attributes on the module
    objects themselves. those attributes may be functions, classes or other
    attributes.

    valid arguments are:

    - __builtin__
    - Queue
    - fcntl
    - os
    - select
    - signal
    - socket
    - ssl
    - sys
    - thread
    - threading
    - time
    - zmq

    with no arguments, patches everything it can in all of the above modules

    :raises: ``ValueError`` if an unknown module name is provided

    .. note::
        lots more standard library modules can be made non-blocking by virtue
        of patching some combination of the the above (because they only block
        by using blocking functions defined elsewhere). a few examples:

        - ``subprocess`` works cooperatively with ``os`` and ``select`` patched
        - ``httplib``, ``urllib`` and ``urllib2`` will all operate
          cooperatively with ``socket`` and ``ssl`` patched

def patch(*module_names):
    """apply monkey-patches to stdlib modules in-place

    imports the relevant modules and simply overwrites attributes on the module
    objects themselves. those attributes may be functions, classes or other
    attributes.

    valid arguments are:

    - __builtin__
    - Queue
    - fcntl
    - os
    - select
    - signal
    - socket
    - ssl
    - sys
    - thread
    - threading
    - time
    - zmq

    with no arguments, patches everything it can in all of the above modules

    :raises: ``ValueError`` if an unknown module name is provided

    .. note::
        lots more standard library modules can be made non-blocking by virtue
        of patching some combination of the the above (because they only block
        by using blocking functions defined elsewhere). a few examples:

        - ``subprocess`` works cooperatively with ``os`` and ``select`` patched
        - ``httplib``, ``urllib`` and ``urllib2`` will all operate
          cooperatively with ``socket`` and ``ssl`` patched
    """
    if not module_names:
        module_names = _patchers.keys()

    log.info("monkey-patching in-place (%d modules)" % len(module_names))

    for module_name in module_names:
        if module_name not in _patchers:
            raise ValueError("'%s' is not greenhouse-patchable" % module_name)

    for module_name in module_names:
        if module_name in sys.modules:
            module = sys.modules[module_name]
        else:
            module = __import__(
                module_name, {}, {}, module_name.rsplit(".", 1)[0])
        for attr, patch in _patchers[module_name].items():
            setattr(module, attr, patch)