root/masterdriverz/snakeoil-formatters/snakeoil/obj.py @ masterdriverz%2540gentoo.org-20070316195713-evqx0kx7szx3ben5

Revision masterdriverz%2540gentoo.org-20070316195713-evqx0kx7szx3ben5, 6.8 kB (checked in by Charlie Shepherd <masterdriverz@…>, 22 months ago)

Pull from pkgcore

Line 
1# Copyright: 2006 Brian Harring <ferringb@gmail.com>
2# License: GPL2
3
4from operator import attrgetter
5from snakeoil.currying import pre_curry
6from snakeoil.mappings import DictMixin
7
8def alias_method(getter, self, *a, **kwd):
9    return getter(self.__obj__)(*a, **kwd)
10
11def instantiate(inst):
12    delayed = object.__getattribute__(inst, "__delayed__")
13    obj = delayed[1](*delayed[2], **delayed[3])
14    object.__setattr__(inst, "__obj__", obj)
15    object.__delattr__(inst, "__delayed__")
16    return obj
17
18
19# we exempt __getattribute__ since we cover it already, same
20# for __new__ and __init__
21base_kls_descriptors = frozenset(
22    ('__delattr__', '__doc__', '__hash__', '__reduce__',
23        '__reduce_ex__', '__repr__', '__setattr__', '__str__'))
24
25class BaseDelayedObject(object):
26    """
27    delay actual instantiation
28    """
29
30    def __new__(cls, desired_kls, func, *a, **kwd):
31        o = object.__new__(cls)
32        object.__setattr__(o, "__delayed__", (desired_kls, func, a, kwd))
33        object.__setattr__(o, "__obj__", None)
34        return o
35
36    def __getattribute__(self, attr):
37        obj = object.__getattribute__(self, "__obj__")
38        if obj is None:
39            if attr == "__class__":
40                return object.__getattribute__(self, "__delayed__")[0]
41
42            obj = instantiate(self)
43            # now we grow some attributes.
44
45        if attr == "__obj__":
46            # special casing for alias_method
47            return obj
48        return getattr(obj, attr)
49
50    # special case the normal descriptors
51    for x in base_kls_descriptors:
52        locals()[x] = pre_curry(alias_method, attrgetter(x))
53    del x
54
55
56# note that we ignore __getattribute__; we already handle it.
57kls_descriptors = frozenset([
58        # simple comparison protocol...
59        '__cmp__',
60        # rich comparison protocol...
61        '__le__', '__lt__', '__eq__', '__ne__', '__gt__', '__ge__',
62        # unicode conversion
63        '__unicode__',
64        # truth...
65        '__nonzero__',
66        # container protocol...
67        '__len__', '__getitem__', '__setitem__', '__delitem__',
68        '__iter__', '__contains__',
69        # deprecated sequence protocol bits...
70        '__getslice__', '__setslice__', '__delslice__',
71        # numeric...
72        '__add__', '__sub__', '__mul__', '__floordiv__', '__mod__',
73        '__divmod__', '__pow__', '__lshift__', '__rshift__',
74        '__and__', '__xor__', '__or__', '__div__', '__truediv__',
75        '__rad__', '__rsub__', '__rmul__', '__rdiv__', '__rtruediv__',
76        '__rfloordiv__', '__rmod__', '__rdivmod__', '__rpow__',
77        '__rlshift__', '__rrshift__', '__rand__', '__rxor__', '__ror__',
78        '__iadd__', '__isub__', '__imul__', '__idiv__', '__itruediv__',
79        '__ifloordiv__', '__imod__', '__ipow__', '__ilshift__',
80        '__irshift__', '__iand__', '__ixor__', '__ior__',
81        '__neg__', '__pos__', '__abs__', '__invert__', '__complex__',
82        '__int__', '__long__', '__float__', '__oct__', '__hex__',
83        '__coerce__',
84        # remaining...
85        '__call__'])
86
87descriptor_overrides = dict((k, pre_curry(alias_method, attrgetter(k)))
88    for k in kls_descriptors)
89
90method_cache = {}
91def make_kls(kls):
92    special_descriptors = tuple(sorted(kls_descriptors.intersection(dir(kls))))
93    if not special_descriptors:
94        return BaseDelayedObject
95    o = method_cache.get(special_descriptors, None)
96    if o is None:
97        class CustomDelayedObject(BaseDelayedObject):
98            locals().update((k, descriptor_overrides[k])
99                for k in special_descriptors)
100
101        o = CustomDelayedObject
102        method_cache[special_descriptors] = o
103    return o
104
105def DelayedInstantiation_kls(kls, *a, **kwd):
106    return DelayedInstantiation(kls, kls, *a, **kwd)
107
108class_cache = {}
109def DelayedInstantiation(resultant_kls, func, *a, **kwd):
110    """Generate an objects that does not get initialized before it is used.
111
112    The returned object can be passed around without triggering
113    initialization. The first time it is actually used (an attribute
114    is accessed) it is initialized once.
115
116    The returned "fake" object cannot completely reliably mimic a
117    builtin type. It will usually work but some corner cases may fail
118    in confusing ways. Make sure to test if DelayedInstantiation has
119    no unwanted side effects.
120
121    @param resultant_kls: type object to fake an instance of.
122    @param func: callable, the return value is used as initialized object.
123    """
124    o = class_cache.get(resultant_kls, None)
125    if o is None:
126        o = make_kls(resultant_kls)
127        class_cache[resultant_kls] = o
128    return o(resultant_kls, func, *a, **kwd)
129
130
131slotted_dict_cache = {}
132def make_SlottedDict_kls(keys):
133    new_keys = tuple(sorted(keys))
134    o = slotted_dict_cache.get(new_keys, None)
135    if o is None:
136        class SlottedDict(DictMixin):
137            __slots__ = new_keys
138            __externally_mutable__ = True
139
140            def __init__(self, iterables=()):
141                if iterables:
142                    self.update(iterables)
143
144            __setitem__ = object.__setattr__
145
146            def __getitem__(self, key):
147                try:
148                    return getattr(self, key)
149                except AttributeError:
150                    raise KeyError(key)
151
152            def __delitem__(self, key):
153                # Python does not raise anything if you delattr an
154                # unset slot (works ok if __slots__ is not involved).
155                try:
156                    getattr(self, key)
157                except AttributeError:
158                    raise KeyError(key)
159                delattr(self, key)
160
161            def __iter__(self):
162                for k in self.__slots__:
163                    if hasattr(self, k):
164                        yield k
165
166            def iterkeys(self):
167                return iter(self)
168
169            def itervalues(self):
170                for k in self:
171                    yield self[k]
172
173            def get(self, key, default=None):
174                return getattr(self, key, default)
175
176            def pop(self, key, *a):
177                # faster then the exception form...
178                l = len(a)
179                if l > 1:
180                    raise TypeError("pop accepts 1 or 2 args only")
181                if hasattr(self, key):
182                    o = getattr(self, key)
183                    object.__delattr__(self, key)
184                elif l:
185                    o = a[0]
186                else:
187                    raise KeyError(key)
188                return o
189
190            def clear(self):
191                for k in self:
192                    del self[k]
193
194            def update(self, iterable):
195                for k, v in iterable:
196                    setattr(self, k, v)
197
198            def __len__(self):
199                return len(self.keys())
200
201            def __contains__(self, key):
202                return hasattr(self, key)
203
204        o = SlottedDict
205        slotted_dict_cache[new_keys] = o
206    return o
Note: See TracBrowser for help on using the browser.