Skip to content

Commit

Permalink
Add enable argument to wrap and patch functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
GrahamDumpleton committed Jul 25, 2023
1 parent 3112bbd commit 54fc35d
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 8 deletions.
9 changes: 9 additions & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ Version 1.16.0
Note that version 1.16.0 drops support for Python 2.7 and 3.5. Python version
3.6 or later is required.

**New Features**

* The ``wrap_function_wrapper()`` and ``patch_function_wrapper()`` functions now
accept an ``enabled`` argument, which can be a literal boolean value, object
that evaluates as boolean, or a callable object which returns a boolean. In
the case of a callable, determination of whether the wrapper is invoked will
be left until the point of the call. In the other cases, the wrapper will not
be applied if the value evaluates false at the point of applying the wrapper.

Version 1.15.0
--------------

Expand Down
16 changes: 8 additions & 8 deletions src/wrapt/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -894,15 +894,15 @@ def _wrapper(wrapped, instance, args, kwargs):
return FunctionWrapper(target_wrapped, target_wrapper)
return FunctionWrapper(wrapper, _wrapper)

def wrap_function_wrapper(module, name, wrapper):
return wrap_object(module, name, FunctionWrapper, (wrapper,))
def wrap_function_wrapper(module, name, wrapper, enabled=None):
return wrap_object(module, name, FunctionWrapper, (wrapper, enabled))

def patch_function_wrapper(module, name):
def patch_function_wrapper(module, name, enabled=None):
def _wrapper(wrapper):
return wrap_object(module, name, FunctionWrapper, (wrapper,))
return wrap_object(module, name, FunctionWrapper, (wrapper, enabled))
return _wrapper

def transient_function_wrapper(module, name):
def transient_function_wrapper(module, name, enabled=None):
def _decorator(wrapper):
def _wrapper(wrapped, instance, args, kwargs):
target_wrapped = args[0]
Expand All @@ -914,14 +914,14 @@ def _wrapper(wrapped, instance, args, kwargs):
target_wrapper = wrapper.__get__(instance, type(instance))
def _execute(wrapped, instance, args, kwargs):
(parent, attribute, original) = resolve_path(module, name)
replacement = FunctionWrapper(original, target_wrapper)
replacement = FunctionWrapper(original, target_wrapper, enabled)
setattr(parent, attribute, replacement)
try:
return wrapped(*args, **kwargs)
finally:
setattr(parent, attribute, original)
return FunctionWrapper(target_wrapped, _execute)
return FunctionWrapper(wrapper, _wrapper)
return FunctionWrapper(target_wrapped, _execute, enabled)
return FunctionWrapper(wrapper, _wrapper, enabled)
return _decorator

# A weak function proxy. This will work on instance methods, class
Expand Down
171 changes: 171 additions & 0 deletions tests/test_monkey_patching.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,27 @@ def global_function_1(*args, **kwargs):
def global_function_2(*args, **kwargs):
return args, kwargs

def global_function_2_enabled_literal_false(*args, **kwargs):
return args, kwargs

def global_function_2_enabled_literal_true(*args, **kwargs):
return args, kwargs

def global_function_2_enabled_callable(*args, **kwargs):
return args, kwargs

def global_function_3(*args, **kwargs):
return args, kwargs

def global_function_3_enabled_literal_false(*args, **kwargs):
return args, kwargs

def global_function_3_enabled_literal_true(*args, **kwargs):
return args, kwargs

def global_function_3_enabled_callable(*args, **kwargs):
return args, kwargs

def global_function_4(*args, **kwargs):
return args, kwargs

Expand Down Expand Up @@ -162,6 +180,87 @@ def wrapper(wrapped, instance, args, kwargs):
self.assertEqual(result, (_args, _kwargs))
self.assertEqual(called[0], (_args, _kwargs))

def test_wrap_function_module_enabled_literal_false(self):

_args = (1, 2)
_kwargs = {'one': 1, 'two': 2}

called = []

def wrapper(wrapped, instance, args, kwargs):
called.append((args, kwargs))
self.assertEqual(instance, None)
self.assertEqual(args, _args)
self.assertEqual(kwargs, _kwargs)
return wrapped(*args, **kwargs)

module = sys.modules[__name__]

wrapt.wrap_function_wrapper(module, 'global_function_2_enabled_literal_false', wrapper, enabled=False)

result = global_function_2_enabled_literal_false(*_args, **_kwargs)

self.assertEqual(result, (_args, _kwargs))
self.assertEqual(called, [])

def test_wrap_function_module_enabled_literal_true(self):

_args = (1, 2)
_kwargs = {'one': 1, 'two': 2}

called = []

def wrapper(wrapped, instance, args, kwargs):
called.append((args, kwargs))
self.assertEqual(instance, None)
self.assertEqual(args, _args)
self.assertEqual(kwargs, _kwargs)
return wrapped(*args, **kwargs)

module = sys.modules[__name__]

wrapt.wrap_function_wrapper(module, 'global_function_2_enabled_literal_true', wrapper, enabled=True)

result = global_function_2_enabled_literal_true(*_args, **_kwargs)

self.assertEqual(result, (_args, _kwargs))
self.assertEqual(called[0], (_args, _kwargs))

def test_wrap_function_module_enabled_callable(self):

_args = (1, 2)
_kwargs = {'one': 1, 'two': 2}

called = []

def wrapper(wrapped, instance, args, kwargs):
called.append((args, kwargs))
self.assertEqual(instance, None)
self.assertEqual(args, _args)
self.assertEqual(kwargs, _kwargs)
return wrapped(*args, **kwargs)

module = sys.modules[__name__]

enable = False

def enabled():
return enable

wrapt.wrap_function_wrapper(module, 'global_function_2_enabled_callable', wrapper, enabled=enabled)

result = global_function_2_enabled_callable(*_args, **_kwargs)

self.assertEqual(result, (_args, _kwargs))
self.assertEqual(called, [])

enable = True

result = global_function_2_enabled_callable(*_args, **_kwargs)

self.assertEqual(result, (_args, _kwargs))
self.assertEqual(called[0], (_args, _kwargs))

def test_wrap_instance_method_module_name(self):

_args = (1, 2)
Expand Down Expand Up @@ -275,6 +374,78 @@ def wrapper(wrapped, instance, args, kwargs):
self.assertEqual(result, (_args, _kwargs))
self.assertEqual(called[0], (_args, _kwargs))

def test_patch_function_module_name_enabled_literal_false(self):

_args = (1, 2)
_kwargs = {'one': 1, 'two': 2}

called = []

@wrapt.patch_function_wrapper(__name__, 'global_function_3_enabled_literal_false', enabled=False)
def wrapper(wrapped, instance, args, kwargs):
called.append((args, kwargs))
self.assertEqual(instance, None)
self.assertEqual(args, _args)
self.assertEqual(kwargs, _kwargs)
return wrapped(*args, **kwargs)

result = global_function_3_enabled_literal_false(*_args, **_kwargs)

self.assertEqual(result, (_args, _kwargs))
self.assertEqual(called, [])

def test_patch_function_module_name_enabled_literal_true(self):

_args = (1, 2)
_kwargs = {'one': 1, 'two': 2}

called = []

@wrapt.patch_function_wrapper(__name__, 'global_function_3_enabled_literal_true', enabled=True)
def wrapper(wrapped, instance, args, kwargs):
called.append((args, kwargs))
self.assertEqual(instance, None)
self.assertEqual(args, _args)
self.assertEqual(kwargs, _kwargs)
return wrapped(*args, **kwargs)

result = global_function_3_enabled_literal_true(*_args, **_kwargs)

self.assertEqual(result, (_args, _kwargs))
self.assertEqual(called[0], (_args, _kwargs))

def test_patch_function_module_name_enabled_callable(self):

_args = (1, 2)
_kwargs = {'one': 1, 'two': 2}

called = []

enable = False

def enabled():
return enable

@wrapt.patch_function_wrapper(__name__, 'global_function_3_enabled_callable', enabled=enabled)
def wrapper(wrapped, instance, args, kwargs):
called.append((args, kwargs))
self.assertEqual(instance, None)
self.assertEqual(args, _args)
self.assertEqual(kwargs, _kwargs)
return wrapped(*args, **kwargs)

result = global_function_3_enabled_callable(*_args, **_kwargs)

enable = True

self.assertEqual(result, (_args, _kwargs))
self.assertEqual(called, [])

result = global_function_3_enabled_callable(*_args, **_kwargs)

self.assertEqual(result, (_args, _kwargs))
self.assertEqual(called[0], (_args, _kwargs))

def test_patch_function_module(self):

_args = (1, 2)
Expand Down

0 comments on commit 54fc35d

Please sign in to comment.