PageRenderTime 46ms CodeModel.GetById 32ms app.highlight 10ms RepoModel.GetById 2ms app.codeStats 0ms

/django/contrib/gis/geos/mutable_list.py

https://code.google.com/p/mango-py/
Python | 309 lines | 271 code | 16 blank | 22 comment | 29 complexity | 9c155a33376588661aecc4019121154d MD5 | raw file
  1# Copyright (c) 2008-2009 Aryeh Leib Taurog, all rights reserved.
  2# Released under the New BSD license.
  3"""
  4This module contains a base type which provides list-style mutations
  5without specific data storage methods.
  6
  7See also http://www.aryehleib.com/MutableLists.html
  8
  9Author: Aryeh Leib Taurog.
 10"""
 11class ListMixin(object):
 12    """
 13    A base class which provides complete list interface.
 14    Derived classes must call ListMixin's __init__() function
 15    and implement the following:
 16
 17    function _get_single_external(self, i):
 18        Return single item with index i for general use.
 19        The index i will always satisfy 0 <= i < len(self).
 20
 21    function _get_single_internal(self, i):
 22        Same as above, but for use within the class [Optional]
 23        Note that if _get_single_internal and _get_single_internal return
 24        different types of objects, _set_list must distinguish
 25        between the two and handle each appropriately.
 26
 27    function _set_list(self, length, items):
 28        Recreate the entire object.
 29
 30        NOTE: items may be a generator which calls _get_single_internal.
 31        Therefore, it is necessary to cache the values in a temporary:
 32            temp = list(items)
 33        before clobbering the original storage.
 34
 35    function _set_single(self, i, value):
 36        Set the single item at index i to value [Optional]
 37        If left undefined, all mutations will result in rebuilding
 38        the object using _set_list.
 39
 40    function __len__(self):
 41        Return the length
 42
 43    int _minlength:
 44        The minimum legal length [Optional]
 45
 46    int _maxlength:
 47        The maximum legal length [Optional]
 48
 49    type or tuple _allowed:
 50        A type or tuple of allowed item types [Optional]
 51
 52    class _IndexError:
 53        The type of exception to be raise on invalid index [Optional]
 54    """
 55
 56    _minlength = 0
 57    _maxlength = None
 58    _IndexError = IndexError
 59
 60    ### Python initialization and special list interface methods ###
 61
 62    def __init__(self, *args, **kwargs):
 63        if not hasattr(self, '_get_single_internal'):
 64            self._get_single_internal = self._get_single_external
 65
 66        if not hasattr(self, '_set_single'):
 67            self._set_single = self._set_single_rebuild
 68            self._assign_extended_slice = self._assign_extended_slice_rebuild
 69
 70        super(ListMixin, self).__init__(*args, **kwargs)
 71
 72    def __getitem__(self, index):
 73        "Get the item(s) at the specified index/slice."
 74        if isinstance(index, slice):
 75            return [self._get_single_external(i) for i in xrange(*index.indices(len(self)))]
 76        else:
 77            index = self._checkindex(index)
 78            return self._get_single_external(index)
 79
 80    def __delitem__(self, index):
 81        "Delete the item(s) at the specified index/slice."
 82        if not isinstance(index, (int, long, slice)):
 83            raise TypeError("%s is not a legal index" % index)
 84
 85        # calculate new length and dimensions
 86        origLen     = len(self)
 87        if isinstance(index, (int, long)):
 88            index = self._checkindex(index)
 89            indexRange  = [index]
 90        else:
 91            indexRange  = range(*index.indices(origLen))
 92
 93        newLen      = origLen - len(indexRange)
 94        newItems    = ( self._get_single_internal(i)
 95                        for i in xrange(origLen)
 96                        if i not in indexRange )
 97
 98        self._rebuild(newLen, newItems)
 99
100    def __setitem__(self, index, val):
101        "Set the item(s) at the specified index/slice."
102        if isinstance(index, slice):
103            self._set_slice(index, val)
104        else:
105            index = self._checkindex(index)
106            self._check_allowed((val,))
107            self._set_single(index, val)
108
109    def __iter__(self):
110        "Iterate over the items in the list"
111        for i in xrange(len(self)):
112            yield self[i]
113
114    ### Special methods for arithmetic operations ###
115    def __add__(self, other):
116        'add another list-like object'
117        return self.__class__(list(self) + list(other))
118
119    def __radd__(self, other):
120        'add to another list-like object'
121        return other.__class__(list(other) + list(self))
122
123    def __iadd__(self, other):
124        'add another list-like object to self'
125        self.extend(list(other))
126        return self
127
128    def __mul__(self, n):
129        'multiply'
130        return self.__class__(list(self) * n)
131
132    def __rmul__(self, n):
133        'multiply'
134        return self.__class__(list(self) * n)
135
136    def __imul__(self, n):
137        'multiply'
138        if n <= 0:
139            del self[:]
140        else:
141            cache = list(self)
142            for i in range(n-1):
143                self.extend(cache)
144        return self
145
146    def __cmp__(self, other):
147        'cmp'
148        slen = len(self)
149        for i in range(slen):
150            try:
151                c = cmp(self[i], other[i])
152            except IndexError:
153                # must be other is shorter
154                return 1
155            else:
156                # elements not equal
157                if c: return c
158
159        return cmp(slen, len(other))
160
161    ### Public list interface Methods ###
162    ## Non-mutating ##
163    def count(self, val):
164        "Standard list count method"
165        count = 0
166        for i in self:
167            if val == i: count += 1
168        return count
169
170    def index(self, val):
171        "Standard list index method"
172        for i in xrange(0, len(self)):
173            if self[i] == val: return i
174        raise ValueError('%s not found in object' % str(val))
175
176    ## Mutating ##
177    def append(self, val):
178        "Standard list append method"
179        self[len(self):] = [val]
180
181    def extend(self, vals):
182        "Standard list extend method"
183        self[len(self):] = vals
184
185    def insert(self, index, val):
186        "Standard list insert method"
187        if not isinstance(index, (int, long)):
188            raise TypeError("%s is not a legal index" % index)
189        self[index:index] = [val]
190
191    def pop(self, index=-1):
192        "Standard list pop method"
193        result = self[index]
194        del self[index]
195        return result
196
197    def remove(self, val):
198        "Standard list remove method"
199        del self[self.index(val)]
200
201    def reverse(self):
202        "Standard list reverse method"
203        self[:] = self[-1::-1]
204
205    def sort(self, cmp=cmp, key=None, reverse=False):
206        "Standard list sort method"
207        if key:
208            temp = [(key(v),v) for v in self]
209            temp.sort(cmp=cmp, key=lambda x: x[0], reverse=reverse)
210            self[:] = [v[1] for v in temp]
211        else:
212            temp = list(self)
213            temp.sort(cmp=cmp, reverse=reverse)
214            self[:] = temp
215
216    ### Private routines ###
217    def _rebuild(self, newLen, newItems):
218        if newLen < self._minlength:
219            raise ValueError('Must have at least %d items' % self._minlength)
220        if self._maxlength is not None and newLen > self._maxlength:
221            raise ValueError('Cannot have more than %d items' % self._maxlength)
222
223        self._set_list(newLen, newItems)
224
225    def _set_single_rebuild(self, index, value):
226        self._set_slice(slice(index, index + 1, 1), [value])
227
228    def _checkindex(self, index, correct=True):
229        length = len(self)
230        if 0 <= index < length:
231            return index
232        if correct and -length <= index < 0:
233            return index + length
234        raise self._IndexError('invalid index: %s' % str(index))
235
236    def _check_allowed(self, items):
237        if hasattr(self, '_allowed'):
238            if False in [isinstance(val, self._allowed) for val in items]:
239                raise TypeError('Invalid type encountered in the arguments.')
240
241    def _set_slice(self, index, values):
242        "Assign values to a slice of the object"
243        try:
244            iter(values)
245        except TypeError:
246            raise TypeError('can only assign an iterable to a slice')
247
248        self._check_allowed(values)
249
250        origLen     = len(self)
251        valueList   = list(values)
252        start, stop, step = index.indices(origLen)
253
254        # CAREFUL: index.step and step are not the same!
255        # step will never be None
256        if index.step is None:
257            self._assign_simple_slice(start, stop, valueList)
258        else:
259            self._assign_extended_slice(start, stop, step, valueList)
260
261    def _assign_extended_slice_rebuild(self, start, stop, step, valueList):
262        'Assign an extended slice by rebuilding entire list'
263        indexList   = range(start, stop, step)
264        # extended slice, only allow assigning slice of same size
265        if len(valueList) != len(indexList):
266            raise ValueError('attempt to assign sequence of size %d '
267                             'to extended slice of size %d'
268                             % (len(valueList), len(indexList)))
269
270        # we're not changing the length of the sequence
271        newLen  = len(self)
272        newVals = dict(zip(indexList, valueList))
273        def newItems():
274            for i in xrange(newLen):
275                if i in newVals:
276                    yield newVals[i]
277                else:
278                    yield self._get_single_internal(i)
279
280        self._rebuild(newLen, newItems())
281
282    def _assign_extended_slice(self, start, stop, step, valueList):
283        'Assign an extended slice by re-assigning individual items'
284        indexList   = range(start, stop, step)
285        # extended slice, only allow assigning slice of same size
286        if len(valueList) != len(indexList):
287            raise ValueError('attempt to assign sequence of size %d '
288                             'to extended slice of size %d'
289                             % (len(valueList), len(indexList)))
290
291        for i, val in zip(indexList, valueList):
292            self._set_single(i, val)
293
294    def _assign_simple_slice(self, start, stop, valueList):
295        'Assign a simple slice; Can assign slice of any length'
296        origLen = len(self)
297        stop = max(start, stop)
298        newLen  = origLen - stop + start + len(valueList)
299        def newItems():
300            for i in xrange(origLen + 1):
301                if i == start:
302                    for val in valueList:
303                        yield val
304
305                if i < origLen:
306                    if i < start or i >= stop:
307                        yield self._get_single_internal(i)
308
309        self._rebuild(newLen, newItems())