This file is indexed.

/usr/share/pyshared/fudge/patcher.py is in python-fudge 1.0.3-3.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
"""Patching utilities for working with fake objects.

See :ref:`using-fudge` for common scenarios.
"""

__all__ = ['patch_object', 'with_patched_object', 'PatchHandler',
           'patched_context', 'patch']

import sys

import fudge
from fudge.util import wraps


class patch(object):
    """A test decorator that patches importable names with :class:`fakes <Fake>`
    
    Each fake is exposed as an argument to the test:
    
    .. doctest::
        :hide:
        
        >>> import fudge

    .. doctest::
        
        >>> @fudge.patch('os.remove')
        ... def test(fake_remove):
        ...     fake_remove.expects_call()
        ...     # do stuff...
        ... 
        >>> test()
        Traceback (most recent call last):
        ...
        AssertionError: fake:os.remove() was not called
    
    .. doctest::
        :hide:
        
        >>> fudge.clear_expectations()
    
    Many paths can be patched at once:

    .. doctest::
        
        >>> @fudge.patch('os.remove',
        ...              'shutil.rmtree')
        ... def test(fake_remove, fake_rmtree):
        ...     fake_remove.is_callable()
        ...     # do stuff...
        ... 
        >>> test()
    
    For convenience, the patch method calls
    :func:`fudge.clear_calls`, :func:`fudge.verify`, and :func:`fudge.clear_expectations`.  For that reason, you must manage all your fake objects within the test function itself.

    .. note::
    
        If you are using a unittest class, you cannot declare fakes
        within ``setUp()`` unless you manually clear calls and clear 
        expectations.  If you do that, you'll want to use the 
        :func:`fudge.with_fakes` decorator instead of ``@patch``.
    
    """
    
    def __init__(self, *obj_paths):
        self.obj_paths = obj_paths
    
    def __call__(self, fn):
        @wraps(fn)
        def caller(*args, **kw):
            fakes = self.__enter__()
            if not isinstance(fakes, (tuple, list)):
                fakes = [fakes]
            args += tuple(fakes)
            value = None
            try:
                value = fn(*args, **kw)
            except:
                etype, val, tb = sys.exc_info()
                self.__exit__(etype, val, tb)
                raise etype, val, tb
            else:
                self.__exit__(None, None, None)
            return value
        return caller
    
    def __enter__(self):
        fudge.clear_expectations()
        fudge.clear_calls()
        self.patches = []
        all_fakes = []
        for path in self.obj_paths:
            try:
                target, attr = path.rsplit('.', 1)    
            except (TypeError, ValueError):
                raise TypeError(
                    "Need a valid target to patch. You supplied: %r"
                    % path)
            fake = fudge.Fake(path)
            all_fakes.append(fake)
            self.patches.append(patch_object(target, attr, fake))
        if len(all_fakes) == 1:
            return all_fakes[0]
        else:
            return all_fakes
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        try:
            if not exc_type:
                fudge.verify()
        finally:
            for p in self.patches:
                p.restore()
            fudge.clear_expectations()


def with_patched_object(obj, attr_name, patched_value):
    """Decorator that patches an object before the decorated method 
    is called and restores it afterwards.
    
    This is a wrapper around :func:`fudge.patcher.patch_object`
    
    Example::
        
        >>> from fudge import with_patched_object
        >>> class Session:
        ...     state = 'clean'
        ... 
        >>> @with_patched_object(Session, "state", "dirty")
        ... def test():
        ...     print Session.state
        ... 
        >>> test()
        dirty
        >>> print Session.state
        clean
        
    """
    def patcher(method):
        @wraps(method)
        def method_call(*m_args, **m_kw):
            patched_obj = patch_object(obj, attr_name, patched_value)
            try:
                return method(*m_args, **m_kw)
            finally:
                patched_obj.restore()
        return method_call
    return patcher

class patched_context(object):
    """A context manager to patch an object temporarily during a `with statement`_ block.
        
    This is a wrapper around :func:`fudge.patcher.patch_object`
    
    .. lame, lame, cannot figure out how to apply __future__ to doctest 
       so this output is currently skipped
    
    .. doctest:: python25
        :options: +SKIP
        
        >>> from fudge import patched_context
        >>> class Session:
        ...     state = 'clean'
        ... 
        >>> with patched_context(Session, "state", "dirty"): # doctest: +SKIP
        ...     print Session.state
        ... 
        dirty
        >>> print Session.state
        clean
    
    .. _with statement: http://www.python.org/dev/peps/pep-0343/
        
    """
    def __init__(self, obj, attr_name, patched_value):
        
        # note that a @contextmanager decorator would be simpler 
        # but it can't be used since a value cannot be yielded within a 
        # try/finally block which is needed to restore the object on finally.
        
        self.patched_object = patch_object(obj, attr_name, patched_value)
    
    def __enter__(self):
        return self.patched_object
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.patched_object.restore()

def patch_object(obj, attr_name, patched_value):
    """Patches an object and returns an instance of :class:`fudge.patcher.PatchHandler` for later restoration.
    
    Note that if *obj* is not an object but a path to a module then it will be imported.
    
    You may want to use a more convenient wrapper :func:`with_patched_object` or :func:`patched_context`
    
    Example::
        
        >>> from fudge import patch_object
        >>> class Session:
        ...     state = 'clean'
        ... 
        >>> patched_session = patch_object(Session, "state", "dirty")
        >>> Session.state
        'dirty'
        >>> patched_session.restore()
        >>> Session.state
        'clean'
    
    Here is another example showing how to patch multiple objects at once::
        
        >>> class Session:
        ...     state = 'clean'
        ... 
        >>> class config:
        ...     session_strategy = 'database'
        ... 
        >>> patches = [
        ...     patch_object(config, "session_strategy", "filesystem"),
        ...     patch_object(Session, "state", "dirty")
        ... ]
        >>> try:
        ...     # your app under test would run here ...
        ...     print "(while patched)"
        ...     print "config.session_strategy=%r" % config.session_strategy
        ...     print "Session.state=%r" % Session.state
        ... finally:
        ...     for p in patches:
        ...         p.restore()
        ...     print "(patches restored)"
        (while patched)
        config.session_strategy='filesystem'
        Session.state='dirty'
        (patches restored)
        >>> config.session_strategy
        'database'
        >>> Session.state
        'clean'
        
    """
    if isinstance(obj, (str, unicode)):
        obj_path = adjusted_path = obj
        done = False
        exc = None
        at_top_level = False
        while not done:
            try:
                obj = __import__(adjusted_path)
                done = True
            except ImportError:
                # Handle paths that traveerse object attributes.
                # Such as: smtplib.SMTP.connect
                #          smtplib <- module to import 
                adjusted_path = adjusted_path.rsplit('.', 1)[0]
                if not exc:
                    exc = sys.exc_info()
                if at_top_level:
                    # We're at the top level module and it doesn't exist.
                    # Raise the first exception since it will make more sense:
                    etype, val, tb = exc
                    raise etype, val, tb
                if not adjusted_path.count('.'):
                    at_top_level = True
        for part in obj_path.split('.')[1:]:
            obj = getattr(obj, part)

    handle = PatchHandler(obj, attr_name)
    handle.patch(patched_value)
    return handle


class NonExistant(object):
    """Represents a non-existant value."""


class PatchHandler(object):
    """Low level patch handler that memorizes a patch so you can restore it later.
    
    You can use more convenient wrappers :func:`with_patched_object` and :func:`patched_context`
        
    """
    def __init__(self, orig_object, attr_name):
        self.orig_object = orig_object
        self.attr_name = attr_name
        self.proxy_object = None
        self.orig_value, self.is_local = self._get_original(self.orig_object,
                                                            self.attr_name)
        self.getter_class, self.getter = self._handle_getter(self.orig_object,
                                                             self.attr_name)
    
    def patch(self, patched_value):
        """Set a new value for the attribute of the object."""
        try:
            if self.getter:
                setattr(self.getter_class, self.attr_name, patched_value)
            else:
                setattr(self.orig_object, self.attr_name, patched_value)
        except TypeError:
            # Workaround for patching builtin objects:
            proxy_name = 'fudge_proxy_%s_%s_%s' % (
                                self.orig_object.__module__,
                                self.orig_object.__name__,
                                patched_value.__class__.__name__
            )
            self.proxy_object = type(proxy_name, (self.orig_object,),
                                     {self.attr_name: patched_value})
            mod = sys.modules[self.orig_object.__module__]
            setattr(mod, self.orig_object.__name__, self.proxy_object)
        
    def restore(self):
        """Restore the saved value for the attribute of the object."""
        if self.proxy_object is None:
            if self.getter:
                setattr(self.getter_class, self.attr_name, self.getter)
            elif self.is_local:
                setattr(self.orig_object, self.attr_name, self.orig_value)
            else:
                # Was not a local, safe to delete:
                delattr(self.orig_object, self.attr_name)
        else:
            setattr(sys.modules[self.orig_object.__module__],
                    self.orig_object.__name__,
                    self.orig_object)

    def _find_class_for_attr(self, cls, attr):
        if attr in cls.__dict__:
            return cls
        else:
            for base in cls.__bases__:
                if self._find_class_for_attr(base, attr) is not NonExistant:
                    return base
            return NonExistant

    def _get_original(self, orig_object, name):
        try:
            value = orig_object.__dict__[name]
            is_local = True
        except (AttributeError, KeyError):
            value = getattr(orig_object, name, NonExistant)
            is_local = False
        if value is NonExistant:
            raise AttributeError(
                    "%s does not have the attribute %r" % (orig_object, name))
        return value, is_local

    def _get_exact_original(self, orig_object, name):
        if hasattr(orig_object, '__dict__'):
            if name not in orig_object.__dict__:
                # TODO: handle class objects, not just instance objects?
                # This is only here for Class.property.__get__
                if hasattr(orig_object, '__class__'):
                    cls = orig_object.__class__
                    orig_object = self._find_class_for_attr(cls, name)
        return orig_object

    def _handle_getter(self, orig_object, name):
        getter_class, getter = None, None
        exact_orig = self._get_exact_original(orig_object, name)
        try:
            ob = exact_orig.__dict__[name]
        except (AttributeError, KeyError):
            pass
        else:
            if hasattr(ob, '__get__'):
                getter_class = exact_orig
                getter = ob
        return getter_class, getter