xref: /aosp_15_r20/external/cronet/third_party/protobuf/python/stubout.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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