mirror of
https://gerrit.ovirt.org/vdsm
synced 2026-02-05 12:46:23 +01:00
Nir prefers it like that, I don't mind. perl -0777 -i.original -pe 's/#\nfrom __future__/#\n\nfrom __future__/' `git ls-files \*.py` is easy. Change-Id: Iad69872e6ae1de62e7f2081da44bb92963eb358c Signed-off-by: Dan Kenigsberg <danken@redhat.com>
204 lines
5.5 KiB
Python
204 lines
5.5 KiB
Python
#
|
|
# Copyright 2012-2017 Red Hat, Inc.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
#
|
|
# Refer to the README and COPYING files for full details of the license
|
|
#
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
|
|
from contextlib import contextmanager
|
|
from functools import wraps
|
|
import inspect
|
|
|
|
import six
|
|
|
|
|
|
#
|
|
# Monkey patch.
|
|
#
|
|
# Usage:
|
|
# ---
|
|
# import monkeypatch
|
|
#
|
|
# class TestSomething():
|
|
#
|
|
# def __init__(self):
|
|
# self.patch = monkeypatch.Patch([
|
|
# (subprocess, 'Popen', lambda x: None),
|
|
# (os, 'chown', lambda *x: 0)
|
|
# ])
|
|
#
|
|
# def setUp(self):
|
|
# self.patch.apply()
|
|
#
|
|
# def tearDown(self):
|
|
# self.patch.revert()
|
|
#
|
|
# def testThis(self):
|
|
# # using patched functions
|
|
#
|
|
# def testThat(self):
|
|
# # using patched functions
|
|
# ---
|
|
#
|
|
class Patch(object):
|
|
|
|
def __init__(self, what):
|
|
self.what = what
|
|
self.old = []
|
|
|
|
@staticmethod
|
|
def _is_static_method(cls, method_name, method):
|
|
is_static_py2 = six.PY2 and inspect.isfunction(method)
|
|
# In Python 3, static methods are returned as 'function' and lose
|
|
# 'staticmethod' class relationship when returned by 'getattr'
|
|
# so we have to reach to __dict__ directly. Calling 'inspect.ismethod'
|
|
# to differentiate between a regular method and a static method won't
|
|
# work without referring to *bound* method and thus, creating
|
|
# an instance of a class.
|
|
is_static_py3 = six.PY3 and isinstance(cls.__dict__[method_name],
|
|
staticmethod)
|
|
return is_static_py2 or is_static_py3
|
|
|
|
@staticmethod
|
|
def _is_class_method(method):
|
|
return (inspect.ismethod(method) and
|
|
getattr(method, '__self__', None) is not None)
|
|
|
|
def apply(self):
|
|
assert self.old == []
|
|
for module, name, that in self.what:
|
|
old = getattr(module, name)
|
|
self.old.append((module, name, old))
|
|
# The following block is done so that if it is a method we are
|
|
# patching in, that it will have the same type as the method it
|
|
# replaced.
|
|
if inspect.isclass(module):
|
|
if self._is_static_method(module, name, old):
|
|
that = staticmethod(that)
|
|
elif self._is_class_method(old):
|
|
that = classmethod(that)
|
|
setattr(module, name, that)
|
|
|
|
def revert(self):
|
|
assert self.old != []
|
|
while self.old:
|
|
module, name, that = self.old.pop()
|
|
# Python 2 wrongly sets the function `that' as an instancemethod
|
|
# instead of keeping it as staticmethod.
|
|
if inspect.isclass(module) and self._is_static_method(module,
|
|
name, that):
|
|
that = staticmethod(that)
|
|
|
|
setattr(module, name, that)
|
|
|
|
|
|
#
|
|
# Monkey patch scope.
|
|
#
|
|
# Usage:
|
|
# ---
|
|
# from monkeypatch import MonkeyPatchScope
|
|
#
|
|
# def test():
|
|
# with MonkeyPatchScope([
|
|
# (subprocess, 'Popen', lambda x: None),
|
|
# (os, 'chown', lambda *x: 0)
|
|
# ])
|
|
# logic
|
|
# ---
|
|
#
|
|
@contextmanager
|
|
def MonkeyPatchScope(what):
|
|
patch = Patch(what)
|
|
patch.apply()
|
|
try:
|
|
yield {}
|
|
finally:
|
|
patch.revert()
|
|
|
|
|
|
#
|
|
# Monkey patch function decorator.
|
|
#
|
|
# Usage:
|
|
# ---
|
|
# from monkeypatch import MonkeyPatch
|
|
#
|
|
# @MonkeyPatch(subprocess, 'Popen', lambda x: None)
|
|
# @MonkeyPatch(os, 'chown', lambda *x: 0)
|
|
# def test():
|
|
# logic
|
|
# ---
|
|
#
|
|
def MonkeyPatch(module, name, that):
|
|
def decorator(f):
|
|
@wraps(f)
|
|
def wrapper(*args, **kw):
|
|
with MonkeyPatchScope([(module, name, that)]):
|
|
return f(*args, **kw)
|
|
return wrapper
|
|
return decorator
|
|
|
|
|
|
#
|
|
# Monkey patch class decorator.
|
|
#
|
|
# Usage:
|
|
# ---
|
|
# from monkeypatch import MonkeyClass
|
|
#
|
|
# @MonkeyClass(subprocess, 'Popen', lambda x: None)
|
|
# @MonkeyClass(os, 'chown', lambda *x: 0)
|
|
# class TestSomething():
|
|
#
|
|
# def testThis(self):
|
|
# # using patched functions
|
|
#
|
|
# def testThat(self):
|
|
# # using patched functions
|
|
# ---
|
|
#
|
|
def MonkeyClass(module, name, that):
|
|
|
|
def setup_decorator(func):
|
|
@wraps(func)
|
|
def setup(self, *a, **kw):
|
|
if not hasattr(self, '__monkeystack__'):
|
|
self.__monkeystack__ = []
|
|
patch = Patch([(module, name, that)])
|
|
self.__monkeystack__.append(patch)
|
|
patch.apply()
|
|
return func(self, *a, **kw)
|
|
return setup
|
|
|
|
def teardown_decorator(func):
|
|
@wraps(func)
|
|
def teardown(self, *a, **kw):
|
|
patch = self.__monkeystack__.pop()
|
|
patch.revert()
|
|
return func(self, *a, **kw)
|
|
return teardown
|
|
|
|
def wrapper(cls):
|
|
cls.setUp = setup_decorator(cls.setUp)
|
|
cls.tearDown = teardown_decorator(cls.tearDown)
|
|
return cls
|
|
|
|
return wrapper
|