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