1from weakref import ref
2
3from blinker._saferef import BoundMethodWeakref
4
5
6try:
7    callable
8except NameError:
9    def callable(object):
10        return hasattr(object, '__call__')
11
12
13try:
14    from collections import defaultdict
15except:
16    class defaultdict(dict):
17
18        def __init__(self, default_factory=None, *a, **kw):
19            if (default_factory is not None and
20                not hasattr(default_factory, '__call__')):
21                raise TypeError('first argument must be callable')
22            dict.__init__(self, *a, **kw)
23            self.default_factory = default_factory
24
25        def __getitem__(self, key):
26            try:
27                return dict.__getitem__(self, key)
28            except KeyError:
29                return self.__missing__(key)
30
31        def __missing__(self, key):
32            if self.default_factory is None:
33                raise KeyError(key)
34            self[key] = value = self.default_factory()
35            return value
36
37        def __reduce__(self):
38            if self.default_factory is None:
39                args = tuple()
40            else:
41                args = self.default_factory,
42            return type(self), args, None, None, self.items()
43
44        def copy(self):
45            return self.__copy__()
46
47        def __copy__(self):
48            return type(self)(self.default_factory, self)
49
50        def __deepcopy__(self, memo):
51            import copy
52            return type(self)(self.default_factory,
53                              copy.deepcopy(self.items()))
54
55        def __repr__(self):
56            return 'defaultdict(%s, %s)' % (self.default_factory,
57                                            dict.__repr__(self))
58
59
60try:
61    from contextlib import contextmanager
62except ImportError:
63    def contextmanager(fn):
64        def oops(*args, **kw):
65            raise RuntimeError("Python 2.5 or above is required to use "
66                               "context managers.")
67        oops.__name__ = fn.__name__
68        return oops
69
70class _symbol(object):
71
72    def __init__(self, name):
73        """Construct a new named symbol."""
74        self.__name__ = self.name = name
75
76    def __reduce__(self):
77        return symbol, (self.name,)
78
79    def __repr__(self):
80        return self.name
81_symbol.__name__ = 'symbol'
82
83
84class symbol(object):
85    """A constant symbol.
86
87    >>> symbol('foo') is symbol('foo')
88    True
89    >>> symbol('foo')
90    foo
91
92    A slight refinement of the MAGICCOOKIE=object() pattern.  The primary
93    advantage of symbol() is its repr().  They are also singletons.
94
95    Repeated calls of symbol('name') will all return the same instance.
96
97    """
98    symbols = {}
99
100    def __new__(cls, name):
101        try:
102            return cls.symbols[name]
103        except KeyError:
104            return cls.symbols.setdefault(name, _symbol(name))
105
106
107try:
108    text = (str, unicode)
109except NameError:
110    text = str
111
112
113def hashable_identity(obj):
114    if hasattr(obj, '__func__'):
115        return (id(obj.__func__), id(obj.__self__))
116    elif hasattr(obj, 'im_func'):
117        return (id(obj.im_func), id(obj.im_self))
118    elif isinstance(obj, text):
119        return obj
120    else:
121        return id(obj)
122
123
124WeakTypes = (ref, BoundMethodWeakref)
125
126
127class annotatable_weakref(ref):
128    """A weakref.ref that supports custom instance attributes."""
129
130
131def reference(object, callback=None, **annotations):
132    """Return an annotated weak ref."""
133    if callable(object):
134        weak = callable_reference(object, callback)
135    else:
136        weak = annotatable_weakref(object, callback)
137    for key, value in annotations.items():
138        setattr(weak, key, value)
139    return weak
140
141
142def callable_reference(object, callback=None):
143    """Return an annotated weak ref, supporting bound instance methods."""
144    if hasattr(object, 'im_self') and object.im_self is not None:
145        return BoundMethodWeakref(target=object, on_delete=callback)
146    elif hasattr(object, '__self__') and object.__self__ is not None:
147        return BoundMethodWeakref(target=object, on_delete=callback)
148    return annotatable_weakref(object, callback)
149
150
151class lazy_property(object):
152    """A @property that is only evaluated once."""
153
154    def __init__(self, deferred):
155        self._deferred = deferred
156        self.__doc__ = deferred.__doc__
157
158    def __get__(self, obj, cls):
159        if obj is None:
160            return self
161        value = self._deferred(obj)
162        setattr(obj, self._deferred.__name__, value)
163        return value
164