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 141 142 143 144 145 146 147 148 149 150 151 152 153 154
|
# Copyright 2008 Google Inc.
#
# 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.
"""
This is a fork of the pymox library intended to work with Python 3.
The file was modified by quermit@gmail.com and dawid.fatyga@gmail.com
Previously, pyfakefs used just this file from the mox3 library.
However, mox3 will soon be decommissioned, yet standard mock cannot
be used because of the problem described in pyfakefs #182 and
mock issue 250 (https://github.com/testing-cabal/mock/issues/250).
Therefore just this file was forked from mox3 and incorporated
into pyfakefs.
"""
import inspect
class StubOutForTesting:
"""Sample Usage:
You want os.path.exists() to always return true during testing.
stubs = StubOutForTesting()
stubs.Set(os.path, 'exists', lambda x: 1)
...
stubs.UnsetAll()
The above changes os.path.exists into a lambda that returns 1. Once
the ... part of the code finishes, the UnsetAll() looks up the old value
of os.path.exists and restores it.
"""
def __init__(self):
self.cache = []
self.stubs = []
def __del__(self):
self.smart_unset_all()
self.unset_all()
def smart_set(self, obj, attr_name, new_attr):
"""Replace obj.attr_name with new_attr.
This method is smart and works at the module, class, and instance level
while preserving proper inheritance. It will not stub out C types
however unless that has been explicitly allowed by the type.
This method supports the case where attr_name is a staticmethod or a
classmethod of obj.
If obj is an instance, then it is its class that will actually be
stubbed. Note that the method Set() does not do that: if obj is an
instance, it (and not its class) will be stubbed.
Raises AttributeError if the attribute cannot be found.
"""
if inspect.ismodule(obj) or (
not inspect.isclass(obj) and attr_name in obj.__dict__
):
orig_obj = obj
if attr_name in obj.__dict__:
orig_attr = obj.__dict__[attr_name]
else:
orig_attr = None
else:
if not inspect.isclass(obj):
mro = list(inspect.getmro(obj.__class__))
else:
mro = list(inspect.getmro(obj))
mro.reverse()
orig_attr = None
for cls in mro:
try:
orig_obj = cls
orig_attr = obj.__dict__[attr_name]
except KeyError:
continue
if orig_attr is None:
raise AttributeError("Attribute not found.")
self.stubs.append((orig_obj, attr_name, orig_attr))
orig_obj.__dict__[attr_name] = new_attr
def smart_unset_all(self):
"""Reverses all the SmartSet() calls.
Restores things to their original definition. Its okay to call
SmartUnsetAll() repeatedly, as later calls have no effect if no
SmartSet() calls have been made.
"""
self.stubs.reverse()
for obj, attr_name, old_attr in self.stubs:
obj.__dict__[attr_name] = old_attr
self.stubs = []
def set(self, parent, child_name, new_child):
"""Replace child_name's old definition with new_child.
Replace definition in the context of the given parent. The parent could
be a module when the child is a function at module scope. Or the parent
could be a class when a class' method is being replaced. The named
child is set to new_child, while the prior definition is saved away
for later, when unset_all() is called.
This method supports the case where child_name is a staticmethod or a
classmethod of parent.
"""
old_child = getattr(parent, child_name)
old_attribute = parent.__dict__.get(child_name)
if old_attribute is not None:
if isinstance(old_attribute, staticmethod):
old_child = staticmethod(old_child)
elif isinstance(old_attribute, classmethod):
old_child = classmethod(old_child.__func__)
self.cache.append((parent, old_child, child_name))
parent.__dict__[child_name] = new_child
def unset_all(self):
"""Reverses all the Set() calls.
Restores things to their original definition. Its okay to call
unset_all() repeatedly, as later calls have no effect if no Set()
calls have been made.
"""
# Undo calls to set() in reverse order, in case set() was called on the
# same arguments repeatedly (want the original call to be last one
# undone)
self.cache.reverse()
for parent, old_child, child_name in self.cache:
parent.__dict__[child_name] = old_child
self.cache = []
|