/usr/lib/python3/dist-packages/testfixtures/replace.py is in python3-testfixtures 4.14.3-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | # Copyright (c) 2008-2013 Simplistix Ltd, 2016 Chris Withers
# See license.txt for license details.
from functools import partial
from testfixtures.compat import ClassType
from testfixtures.resolve import resolve, not_there
from testfixtures.utils import wrap, extend_docstring
import warnings
def not_same_descriptor(x, y, descriptor):
return isinstance(x, descriptor) and not isinstance(y, descriptor)
class Replacer:
"""
These are used to manage the mocking out of objects so that units
of code can be tested without having to rely on their normal
dependencies.
"""
def __init__(self):
self.originals = {}
def _replace(self, container, name, method, value, strict=True):
if value is not_there:
if method == 'a':
delattr(container, name)
if method == 'i':
del container[name]
else:
if method == 'a':
setattr(container, name, value)
if method == 'i':
container[name] = value
def __call__(self, target, replacement, strict=True):
"""
Replace the specified target with the supplied replacement.
"""
container, method, attribute, t_obj = resolve(target)
if method is None:
raise ValueError('target must contain at least one dot!')
if t_obj is not_there and strict:
raise AttributeError('Original %r not found' % attribute)
if t_obj is not_there and replacement is not_there:
return not_there
replacement_to_use = replacement
if isinstance(container, (type, ClassType)):
if not_same_descriptor(t_obj, replacement, classmethod):
replacement_to_use = classmethod(replacement)
elif not_same_descriptor(t_obj, replacement, staticmethod):
replacement_to_use = staticmethod(replacement)
self._replace(container, attribute, method, replacement_to_use, strict)
if target not in self.originals:
self.originals[target] = t_obj
return replacement
def replace(self, target, replacement, strict=True):
"""
Replace the specified target with the supplied replacement.
"""
self(target, replacement, strict)
def restore(self):
"""
Restore all the original objects that have been replaced by
calls to the :meth:`replace` method of this :class:`Replacer`.
"""
for target, original in tuple(self.originals.items()):
container, method, attribute, found = resolve(target)
self._replace(container, attribute, method, original, strict=False)
del self.originals[target]
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.restore()
def __del__(self):
if self.originals:
# no idea why coverage misses the following statement
# it's covered by test_replace.TestReplace.test_replacer_del
warnings.warn( # pragma: no cover
'Replacer deleted without being restored, '
'originals left: %r' % self.originals
)
def replace(target, replacement, strict=True):
"""
A decorator to replace a target object for the duration of a test
function.
"""
r = Replacer()
return wrap(partial(r.__call__, target, replacement, strict), r.restore)
class Replace(object):
"""
A context manager that uses a :class:`Replacer` to replace a single target.
"""
def __init__(self, target, replacement, strict=True):
self.target = target
self.replacement = replacement
self.strict = strict
self._replacer = Replacer()
def __enter__(self):
return self._replacer(self.target, self.replacement, self.strict)
def __exit__(self, exc_type, exc_val, exc_tb):
self._replacer.restore()
replace_params_doc = """
:param target: A string containing the dotted-path to the
object to be replaced. This path may specify a
module in a package, an attribute of a module,
or any attribute of something contained within
a module.
:param replacement: The object to use as a replacement.
:param strict: When `True`, an exception will be raised if an
attempt is made to replace an object that does
not exist.
"""
# add the param docs, so we only have one copy of them!
extend_docstring(replace_params_doc,
[Replacer.__call__, Replacer.replace, replace, Replace])
|