1ffe3c632Sopenharmony_ci#!/usr/bin/python2.4 2ffe3c632Sopenharmony_ci# 3ffe3c632Sopenharmony_ci# Copyright 2008 Google Inc. 4ffe3c632Sopenharmony_ci# 5ffe3c632Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 6ffe3c632Sopenharmony_ci# you may not use this file except in compliance with the License. 7ffe3c632Sopenharmony_ci# You may obtain a copy of the License at 8ffe3c632Sopenharmony_ci# 9ffe3c632Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 10ffe3c632Sopenharmony_ci# 11ffe3c632Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software 12ffe3c632Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, 13ffe3c632Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14ffe3c632Sopenharmony_ci# See the License for the specific language governing permissions and 15ffe3c632Sopenharmony_ci# limitations under the License. 16ffe3c632Sopenharmony_ci 17ffe3c632Sopenharmony_ci# This file is used for testing. The original is at: 18ffe3c632Sopenharmony_ci# http://code.google.com/p/pymox/ 19ffe3c632Sopenharmony_ci 20ffe3c632Sopenharmony_ciimport inspect 21ffe3c632Sopenharmony_ci 22ffe3c632Sopenharmony_ci 23ffe3c632Sopenharmony_ciclass StubOutForTesting: 24ffe3c632Sopenharmony_ci """Sample Usage: 25ffe3c632Sopenharmony_ci You want os.path.exists() to always return true during testing. 26ffe3c632Sopenharmony_ci 27ffe3c632Sopenharmony_ci stubs = StubOutForTesting() 28ffe3c632Sopenharmony_ci stubs.Set(os.path, 'exists', lambda x: 1) 29ffe3c632Sopenharmony_ci ... 30ffe3c632Sopenharmony_ci stubs.UnsetAll() 31ffe3c632Sopenharmony_ci 32ffe3c632Sopenharmony_ci The above changes os.path.exists into a lambda that returns 1. Once 33ffe3c632Sopenharmony_ci the ... part of the code finishes, the UnsetAll() looks up the old value 34ffe3c632Sopenharmony_ci of os.path.exists and restores it. 35ffe3c632Sopenharmony_ci 36ffe3c632Sopenharmony_ci """ 37ffe3c632Sopenharmony_ci def __init__(self): 38ffe3c632Sopenharmony_ci self.cache = [] 39ffe3c632Sopenharmony_ci self.stubs = [] 40ffe3c632Sopenharmony_ci 41ffe3c632Sopenharmony_ci def __del__(self): 42ffe3c632Sopenharmony_ci self.SmartUnsetAll() 43ffe3c632Sopenharmony_ci self.UnsetAll() 44ffe3c632Sopenharmony_ci 45ffe3c632Sopenharmony_ci def SmartSet(self, obj, attr_name, new_attr): 46ffe3c632Sopenharmony_ci """Replace obj.attr_name with new_attr. This method is smart and works 47ffe3c632Sopenharmony_ci at the module, class, and instance level while preserving proper 48ffe3c632Sopenharmony_ci inheritance. It will not stub out C types however unless that has been 49ffe3c632Sopenharmony_ci explicitly allowed by the type. 50ffe3c632Sopenharmony_ci 51ffe3c632Sopenharmony_ci This method supports the case where attr_name is a staticmethod or a 52ffe3c632Sopenharmony_ci classmethod of obj. 53ffe3c632Sopenharmony_ci 54ffe3c632Sopenharmony_ci Notes: 55ffe3c632Sopenharmony_ci - If obj is an instance, then it is its class that will actually be 56ffe3c632Sopenharmony_ci stubbed. Note that the method Set() does not do that: if obj is 57ffe3c632Sopenharmony_ci an instance, it (and not its class) will be stubbed. 58ffe3c632Sopenharmony_ci - The stubbing is using the builtin getattr and setattr. So, the __get__ 59ffe3c632Sopenharmony_ci and __set__ will be called when stubbing (TODO: A better idea would 60ffe3c632Sopenharmony_ci probably be to manipulate obj.__dict__ instead of getattr() and 61ffe3c632Sopenharmony_ci setattr()). 62ffe3c632Sopenharmony_ci 63ffe3c632Sopenharmony_ci Raises AttributeError if the attribute cannot be found. 64ffe3c632Sopenharmony_ci """ 65ffe3c632Sopenharmony_ci if (inspect.ismodule(obj) or 66ffe3c632Sopenharmony_ci (not inspect.isclass(obj) and obj.__dict__.has_key(attr_name))): 67ffe3c632Sopenharmony_ci orig_obj = obj 68ffe3c632Sopenharmony_ci orig_attr = getattr(obj, attr_name) 69ffe3c632Sopenharmony_ci 70ffe3c632Sopenharmony_ci else: 71ffe3c632Sopenharmony_ci if not inspect.isclass(obj): 72ffe3c632Sopenharmony_ci mro = list(inspect.getmro(obj.__class__)) 73ffe3c632Sopenharmony_ci else: 74ffe3c632Sopenharmony_ci mro = list(inspect.getmro(obj)) 75ffe3c632Sopenharmony_ci 76ffe3c632Sopenharmony_ci mro.reverse() 77ffe3c632Sopenharmony_ci 78ffe3c632Sopenharmony_ci orig_attr = None 79ffe3c632Sopenharmony_ci 80ffe3c632Sopenharmony_ci for cls in mro: 81ffe3c632Sopenharmony_ci try: 82ffe3c632Sopenharmony_ci orig_obj = cls 83ffe3c632Sopenharmony_ci orig_attr = getattr(obj, attr_name) 84ffe3c632Sopenharmony_ci except AttributeError: 85ffe3c632Sopenharmony_ci continue 86ffe3c632Sopenharmony_ci 87ffe3c632Sopenharmony_ci if orig_attr is None: 88ffe3c632Sopenharmony_ci raise AttributeError("Attribute not found.") 89ffe3c632Sopenharmony_ci 90ffe3c632Sopenharmony_ci # Calling getattr() on a staticmethod transforms it to a 'normal' function. 91ffe3c632Sopenharmony_ci # We need to ensure that we put it back as a staticmethod. 92ffe3c632Sopenharmony_ci old_attribute = obj.__dict__.get(attr_name) 93ffe3c632Sopenharmony_ci if old_attribute is not None and isinstance(old_attribute, staticmethod): 94ffe3c632Sopenharmony_ci orig_attr = staticmethod(orig_attr) 95ffe3c632Sopenharmony_ci 96ffe3c632Sopenharmony_ci self.stubs.append((orig_obj, attr_name, orig_attr)) 97ffe3c632Sopenharmony_ci setattr(orig_obj, attr_name, new_attr) 98ffe3c632Sopenharmony_ci 99ffe3c632Sopenharmony_ci def SmartUnsetAll(self): 100ffe3c632Sopenharmony_ci """Reverses all the SmartSet() calls, restoring things to their original 101ffe3c632Sopenharmony_ci definition. Its okay to call SmartUnsetAll() repeatedly, as later calls 102ffe3c632Sopenharmony_ci have no effect if no SmartSet() calls have been made. 103ffe3c632Sopenharmony_ci 104ffe3c632Sopenharmony_ci """ 105ffe3c632Sopenharmony_ci self.stubs.reverse() 106ffe3c632Sopenharmony_ci 107ffe3c632Sopenharmony_ci for args in self.stubs: 108ffe3c632Sopenharmony_ci setattr(*args) 109ffe3c632Sopenharmony_ci 110ffe3c632Sopenharmony_ci self.stubs = [] 111ffe3c632Sopenharmony_ci 112ffe3c632Sopenharmony_ci def Set(self, parent, child_name, new_child): 113ffe3c632Sopenharmony_ci """Replace child_name's old definition with new_child, in the context 114ffe3c632Sopenharmony_ci of the given parent. The parent could be a module when the child is a 115ffe3c632Sopenharmony_ci function at module scope. Or the parent could be a class when a class' 116ffe3c632Sopenharmony_ci method is being replaced. The named child is set to new_child, while 117ffe3c632Sopenharmony_ci the prior definition is saved away for later, when UnsetAll() is called. 118ffe3c632Sopenharmony_ci 119ffe3c632Sopenharmony_ci This method supports the case where child_name is a staticmethod or a 120ffe3c632Sopenharmony_ci classmethod of parent. 121ffe3c632Sopenharmony_ci """ 122ffe3c632Sopenharmony_ci old_child = getattr(parent, child_name) 123ffe3c632Sopenharmony_ci 124ffe3c632Sopenharmony_ci old_attribute = parent.__dict__.get(child_name) 125ffe3c632Sopenharmony_ci if old_attribute is not None and isinstance(old_attribute, staticmethod): 126ffe3c632Sopenharmony_ci old_child = staticmethod(old_child) 127ffe3c632Sopenharmony_ci 128ffe3c632Sopenharmony_ci self.cache.append((parent, old_child, child_name)) 129ffe3c632Sopenharmony_ci setattr(parent, child_name, new_child) 130ffe3c632Sopenharmony_ci 131ffe3c632Sopenharmony_ci def UnsetAll(self): 132ffe3c632Sopenharmony_ci """Reverses all the Set() calls, restoring things to their original 133ffe3c632Sopenharmony_ci definition. Its okay to call UnsetAll() repeatedly, as later calls have 134ffe3c632Sopenharmony_ci no effect if no Set() calls have been made. 135ffe3c632Sopenharmony_ci 136ffe3c632Sopenharmony_ci """ 137ffe3c632Sopenharmony_ci # Undo calls to Set() in reverse order, in case Set() was called on the 138ffe3c632Sopenharmony_ci # same arguments repeatedly (want the original call to be last one undone) 139ffe3c632Sopenharmony_ci self.cache.reverse() 140ffe3c632Sopenharmony_ci 141ffe3c632Sopenharmony_ci for (parent, old_child, child_name) in self.cache: 142ffe3c632Sopenharmony_ci setattr(parent, child_name, old_child) 143ffe3c632Sopenharmony_ci self.cache = [] 144