3 # Copyright 2008 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
19 class StubOutForTesting
:
21 You want os.path.exists() to always return true during testing.
23 stubs = StubOutForTesting()
24 stubs.Set(os.path, 'exists', lambda x: 1)
28 The above changes os.path.exists into a lambda that returns 1. Once
29 the ... part of the code finishes, the UnsetAll() looks up the old value
30 of os.path.exists and restores it.
41 def SmartSet(self
, obj
, attr_name
, new_attr
):
42 """Replace obj.attr_name with new_attr. This method is smart and works
43 at the module, class, and instance level while preserving proper
44 inheritance. It will not stub out C types however unless that has been
45 explicitly allowed by the type.
47 This method supports the case where attr_name is a staticmethod or a
51 - If obj is an instance, then it is its class that will actually be
52 stubbed. Note that the method Set() does not do that: if obj is
53 an instance, it (and not its class) will be stubbed.
54 - The stubbing is using the builtin getattr and setattr. So, the __get__
55 and __set__ will be called when stubbing (TODO: A better idea would
56 probably be to manipulate obj.__dict__ instead of getattr() and
59 Raises AttributeError if the attribute cannot be found.
61 if (inspect
.ismodule(obj
) or
62 (not inspect
.isclass(obj
) and obj
.__dict
__.has_key(attr_name
))):
64 orig_attr
= getattr(obj
, attr_name
)
67 if not inspect
.isclass(obj
):
68 mro
= list(inspect
.getmro(obj
.__class
__))
70 mro
= list(inspect
.getmro(obj
))
79 orig_attr
= getattr(obj
, attr_name
)
80 except AttributeError:
84 raise AttributeError("Attribute not found.")
86 # Calling getattr() on a staticmethod transforms it to a 'normal' function.
87 # We need to ensure that we put it back as a staticmethod.
88 old_attribute
= obj
.__dict
__.get(attr_name
)
89 if old_attribute
is not None and isinstance(old_attribute
, staticmethod):
90 orig_attr
= staticmethod(orig_attr
)
92 self
.stubs
.append((orig_obj
, attr_name
, orig_attr
))
93 setattr(orig_obj
, attr_name
, new_attr
)
95 def SmartUnsetAll(self
):
96 """Reverses all the SmartSet() calls, restoring things to their original
97 definition. Its okay to call SmartUnsetAll() repeatedly, as later calls
98 have no effect if no SmartSet() calls have been made.
103 for args
in self
.stubs
:
108 def Set(self
, parent
, child_name
, new_child
):
109 """Replace child_name's old definition with new_child, in the context
110 of the given parent. The parent could be a module when the child is a
111 function at module scope. Or the parent could be a class when a class'
112 method is being replaced. The named child is set to new_child, while
113 the prior definition is saved away for later, when UnsetAll() is called.
115 This method supports the case where child_name is a staticmethod or a
116 classmethod of parent.
118 old_child
= getattr(parent
, child_name
)
120 old_attribute
= parent
.__dict
__.get(child_name
)
121 if old_attribute
is not None and isinstance(old_attribute
, staticmethod):
122 old_child
= staticmethod(old_child
)
124 self
.cache
.append((parent
, old_child
, child_name
))
125 setattr(parent
, child_name
, new_child
)
128 """Reverses all the Set() calls, restoring things to their original
129 definition. Its okay to call UnsetAll() repeatedly, as later calls have
130 no effect if no Set() calls have been made.
133 # Undo calls to Set() in reverse order, in case Set() was called on the
134 # same arguments repeatedly (want the original call to be last one undone)
137 for (parent
, old_child
, child_name
) in self
.cache
:
138 setattr(parent
, child_name
, old_child
)