Skip to content

Instantly share code, notes, and snippets.

@yosshy
Created April 27, 2015 06:19
Show Gist options
  • Save yosshy/5ae19e9bf965746b47c1 to your computer and use it in GitHub Desktop.
Save yosshy/5ae19e9bf965746b47c1 to your computer and use it in GitHub Desktop.
monkeypatch utils
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2011 Justin Santa Barbara
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Monkey Patching Helper."""
import inspect
import pyclbr
import sys
import eventlet
from oslo.config import cfg
from nova.openstack.common import importutils
monkey_patch_opts = [
cfg.BoolOpt('enabled',
default=False,
help='Whether to log monkey patching'),
cfg.ListOpt('target_modules',
default=[],
help='List of modules/decorators to monkey patch'),
cfg.ListOpt('exclude_methods',
default=[],
help='List of methods not to monkey patch'),
]
CONF = cfg.CONF
CONF.register_opts(monkey_patch_opts, 'monkey_patch')
def monkey_patch():
"""If the CONF.monkey_patch set as True,
this function patches a decorator
for all functions in specified modules.
You can set decorators for each modules
using CONF.monkey_patch_modules.
The format is "Module path:Decorator function".
Example:
'nova.api.ec2.cloud:nova.notifications.notify_decorator'
Parameters of the decorator is as follows.
(See nova.notifications.notify_decorator)
name - name of the function
function - object of the function
"""
# If CONF.monkey_patch is not True, this function do nothing.
if not CONF.monkey_patch.enabled:
return
# Get list of modules and decorators
for module_and_decorator in CONF.monkey_patch.target_modules:
module, decorator_name = module_and_decorator.split(':')
if module not in sys.modules:
continue
print("monkey-patching to module %s" % module)
# import decorator function
decorator = importutils.import_class(decorator_name)
__import__(module)
# Retrieve module information using pyclbr
module_data = pyclbr.readmodule_ex(module)
for key in module_data.keys():
# set the decorator for the class methods
if isinstance(module_data[key], pyclbr.Class):
clz = importutils.import_class("%s.%s" % (module, key))
for method, func in inspect.getmembers(clz, inspect.ismethod):
# check exclude_methods
if method in CONF.monkey_patch.exclude_methods:
continue
# avoid applying patch to methods with @classmethod
m = getattr(clz, method)
if m.__self__ is clz:
continue
# apply it
setattr(clz, method,
decorator("%s.%s.%s" % (module, key, method), func))
# set the decorator for the function
if isinstance(module_data[key], pyclbr.Function):
# check exclude_methods
if key in CONF.monkey_patch.exclude_methods:
continue
# apply it
func = importutils.import_class("%s.%s" % (module, key))
setattr(sys.modules[module], key,
decorator("%s.%s" % (module, key), func))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment