Permalink
Browse files

Applied latest changes to `ListMixin` from Aryeh Leib Taurog and adde…

…d him to AUTHORS; fixed memory leak introduced in r10174 -- no longer call `ListMixin.__init__` and set methods manually because it created references that prevented garbage collection; fixed several routines that had no need to be class methods.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10494 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent ae7cb57 commit 5e58810e2197f13dc8372bfeae66e95b908f601d @jbronn jbronn committed Apr 10, 2009
View
@@ -408,6 +408,7 @@ answer newbie questions, and generally made Django that much better:
Christian Tanzer <tanzer@swing.co.at>
Tyler Tarabula <tyler.tarabula@gmail.com>
Tyson Tate <tyson@fallingbullets.com>
+ Aryeh Leib Taurog <http://www.aryehleib.com/>
Frank Tegtmeyer <fte@fte.to>
Marcel Telka <marcel@telka.sk>
Terry Huang <terryh.tp@gmail.com>
@@ -49,33 +49,35 @@ def __len__(self):
return self.num_geom
### Methods for compatibility with ListMixin ###
- @classmethod
- def _create_collection(cls, length, items):
+ def _create_collection(self, length, items):
# Creating the geometry pointer array.
geoms = get_pointer_arr(length)
for i, g in enumerate(items):
# this is a little sloppy, but makes life easier
# allow GEOSGeometry types (python wrappers) or pointer types
geoms[i] = capi.geom_clone(getattr(g, 'ptr', g))
- return capi.create_collection(c_int(cls._typeid), byref(geoms), c_uint(length))
+ return capi.create_collection(c_int(self._typeid), byref(geoms), c_uint(length))
- def _getitem_internal(self, index):
+ def _get_single_internal(self, index):
return capi.get_geomn(self.ptr, index)
- def _getitem_external(self, index):
+ def _get_single_external(self, index):
"Returns the Geometry from this Collection at the given index (0-based)."
# Checking the index and returning the corresponding GEOS geometry.
- return GEOSGeometry(capi.geom_clone(self._getitem_internal(index)), srid=self.srid)
+ return GEOSGeometry(capi.geom_clone(self._get_single_internal(index)), srid=self.srid)
- def _set_collection(self, length, items):
+ def _set_list(self, length, items):
"Create a new collection, and destroy the contents of the previous pointer."
prev_ptr = self.ptr
srid = self.srid
self.ptr = self._create_collection(length, items)
if srid: self.srid = srid
capi.destroy_geom(prev_ptr)
+ _set_single = GEOSGeometry._set_single_rebuild
+ _assign_extended_slice = GEOSGeometry._assign_extended_slice_rebuild
+
@property
def kml(self):
"Returns the KML for this Geometry Collection."
@@ -90,7 +90,6 @@ def __init__(self, geo_input, srid=None):
# Post-initialization setup.
self._post_init(srid)
- super(GEOSGeometry, self).__init__()
def _post_init(self, srid):
"Helper routine for performing post-initialization setup."
@@ -74,12 +74,12 @@ def __len__(self):
"Returns the number of points in this LineString."
return len(self._cs)
- def _getitem_external(self, index):
- self._checkindex(index)
+ def _get_single_external(self, index):
return self._cs[index]
- _getitem_internal = _getitem_external
- def _set_collection(self, length, items):
+ _get_single_internal = _get_single_external
+
+ def _set_list(self, length, items):
ndim = self._cs.dims #
hasz = self._cs.hasz # I don't understand why these are different
@@ -2,45 +2,52 @@
# Released under the New BSD license.
"""
This module contains a base type which provides list-style mutations
-This is akin to UserList, but without specific data storage methods.
-Possible candidate for a more general position in the source tree,
-perhaps django.utils
+without specific data storage methods.
+
+See also http://www.aryehleib.com/MutableLists.html
Author: Aryeh Leib Taurog.
"""
class ListMixin(object):
"""
- A base class which provides complete list interface
- derived classes should implement the following:
+ A base class which provides complete list interface.
+ Derived classes must call ListMixin's __init__() function
+ and implement the following:
- function _getitem_external(self, i):
- Return single item with index i for general use
+ function _get_single_external(self, i):
+ Return single item with index i for general use.
+ The index i will always satisfy 0 <= i < len(self).
- function _getitem_internal(self, i):
+ function _get_single_internal(self, i):
Same as above, but for use within the class [Optional]
+ Note that if _get_single_internal and _get_single_internal return
+ different types of objects, _set_list must distinguish
+ between the two and handle each appropriately.
+
+ function _set_list(self, length, items):
+ Recreate the entire object.
- function _set_collection(self, length, items):
- Recreate the entire object
+ NOTE: items may be a generator which calls _get_single_internal.
+ Therefore, it is necessary to cache the values in a temporary:
+ temp = list(items)
+ before clobbering the original storage.
function _set_single(self, i, value):
Set the single item at index i to value [Optional]
If left undefined, all mutations will result in rebuilding
- the object using _set_collection.
+ the object using _set_list.
function __len__(self):
Return the length
- function __iter__(self):
- Return an iterator for the object
-
int _minlength:
The minimum legal length [Optional]
int _maxlength:
The maximum legal length [Optional]
- iterable _allowed:
- A list of allowed item types [Optional]
+ type or tuple _allowed:
+ A type or tuple of allowed item types [Optional]
class _IndexError:
The type of exception to be raise on invalid index [Optional]
@@ -50,11 +57,11 @@ class _IndexError:
_maxlength = None
_IndexError = IndexError
- ### Python initialization and list interface methods ###
+ ### Python initialization and special list interface methods ###
def __init__(self, *args, **kwargs):
- if not hasattr(self, '_getitem_internal'):
- self._getitem_internal = self._getitem_external
+ if not hasattr(self, '_get_single_internal'):
+ self._get_single_internal = self._get_single_external
if not hasattr(self, '_set_single'):
self._set_single = self._set_single_rebuild
@@ -63,15 +70,15 @@ def __init__(self, *args, **kwargs):
super(ListMixin, self).__init__(*args, **kwargs)
def __getitem__(self, index):
- "Gets the coordinates of the point(s) at the specified index/slice."
+ "Get the item(s) at the specified index/slice."
if isinstance(index, slice):
- return [self._getitem_external(i) for i in xrange(*index.indices(len(self)))]
+ return [self._get_single_external(i) for i in xrange(*index.indices(len(self)))]
else:
index = self._checkindex(index)
- return self._getitem_external(index)
+ return self._get_single_external(index)
def __delitem__(self, index):
- "Delete the point(s) at the specified index/slice."
+ "Delete the item(s) at the specified index/slice."
if not isinstance(index, (int, long, slice)):
raise TypeError("%s is not a legal index" % index)
@@ -84,22 +91,89 @@ def __delitem__(self, index):
indexRange = range(*index.indices(origLen))
newLen = origLen - len(indexRange)
- newItems = ( self._getitem_internal(i)
+ newItems = ( self._get_single_internal(i)
for i in xrange(origLen)
if i not in indexRange )
self._rebuild(newLen, newItems)
def __setitem__(self, index, val):
- "Sets the Geometry at the specified index."
+ "Set the item(s) at the specified index/slice."
if isinstance(index, slice):
self._set_slice(index, val)
else:
index = self._checkindex(index)
self._check_allowed((val,))
self._set_single(index, val)
+ def __iter__(self):
+ "Iterate over the items in the list"
+ for i in xrange(len(self)):
+ yield self[i]
+
+ ### Special methods for arithmetic operations ###
+ def __add__(self, other):
+ 'add another list-like object'
+ return self.__class__(list(self) + list(other))
+
+ def __radd__(self, other):
+ 'add to another list-like object'
+ return other.__class__(list(other) + list(self))
+
+ def __iadd__(self, other):
+ 'add another list-like object to self'
+ self.extend(list(other))
+ return self
+
+ def __mul__(self, n):
+ 'multiply'
+ return self.__class__(list(self) * n)
+
+ def __rmul__(self, n):
+ 'multiply'
+ return self.__class__(list(self) * n)
+
+ def __imul__(self, n):
+ 'multiply'
+ if n <= 0:
+ del self[:]
+ else:
+ cache = list(self)
+ for i in range(n-1):
+ self.extend(cache)
+ return self
+
+ def __cmp__(self, other):
+ 'cmp'
+ slen = len(self)
+ for i in range(slen):
+ try:
+ c = cmp(self[i], other[i])
+ except IndexError:
+ # must be other is shorter
+ return 1
+ else:
+ # elements not equal
+ if c: return c
+
+ return cmp(slen, len(other))
+
### Public list interface Methods ###
+ ## Non-mutating ##
+ def count(self, val):
+ "Standard list count method"
+ count = 0
+ for i in self:
+ if val == i: count += 1
+ return count
+
+ def index(self, val):
+ "Standard list index method"
+ for i in xrange(0, len(self)):
+ if self[i] == val: return i
+ raise ValueError('%s not found in object' % str(val))
+
+ ## Mutating ##
def append(self, val):
"Standard list append method"
self[len(self):] = [val]
@@ -120,32 +194,33 @@ def pop(self, index=-1):
del self[index]
return result
- def index(self, val):
- "Standard list index method"
- for i in xrange(0, len(self)):
- if self[i] == val: return i
- raise ValueError('%s not found in object' % str(val))
-
def remove(self, val):
"Standard list remove method"
del self[self.index(val)]
- def count(self, val):
- "Standard list count method"
- count = 0
- for i in self:
- if val == i: count += 1
- return count
+ def reverse(self):
+ "Standard list reverse method"
+ self[:] = self[-1::-1]
- ### Private API routines unique to ListMixin ###
+ def sort(self, cmp=cmp, key=None, reverse=False):
+ "Standard list sort method"
+ if key:
+ temp = [(key(v),v) for v in self]
+ temp.sort(cmp=cmp, key=lambda x: x[0], reverse=reverse)
+ self[:] = [v[1] for v in temp]
+ else:
+ temp = list(self)
+ temp.sort(cmp=cmp, reverse=reverse)
+ self[:] = temp
+ ### Private routines ###
def _rebuild(self, newLen, newItems):
if newLen < self._minlength:
raise ValueError('Must have at least %d items' % self._minlength)
if self._maxlength is not None and newLen > self._maxlength:
raise ValueError('Cannot have more than %d items' % self._maxlength)
- self._set_collection(newLen, newItems)
+ self._set_list(newLen, newItems)
def _set_single_rebuild(self, index, value):
self._set_slice(slice(index, index + 1, 1), [value])
@@ -200,7 +275,7 @@ def newItems():
if i in newVals:
yield newVals[i]
else:
- yield self._getitem_internal(i)
+ yield self._get_single_internal(i)
self._rebuild(newLen, newItems())
@@ -229,6 +304,6 @@ def newItems():
if i < origLen:
if i < start or i >= stop:
- yield self._getitem_internal(i)
+ yield self._get_single_internal(i)
self._rebuild(newLen, newItems())
@@ -5,6 +5,7 @@
class Point(GEOSGeometry):
_minlength = 2
+ _maxlength = 3
def __init__(self, x, y=None, z=None, srid=None):
"""
@@ -36,7 +37,6 @@ def __init__(self, x, y=None, z=None, srid=None):
# createPoint factory.
super(Point, self).__init__(point, srid=srid)
- @classmethod
def _create_point(self, ndim, coords):
"""
Create a coordinate sequence, set X, Y, [Z], and create point
@@ -52,7 +52,7 @@ def _create_point(self, ndim, coords):
return capi.create_point(cs)
- def _set_collection(self, length, items):
+ def _set_list(self, length, items):
ptr = self._create_point(length, items)
if ptr:
capi.destroy_geom(self.ptr)
@@ -76,15 +76,15 @@ def __len__(self):
if self.hasz: return 3
else: return 2
- def _getitem_external(self, index):
- self._checkindex(index)
+ def _get_single_external(self, index):
if index == 0:
return self.x
elif index == 1:
return self.y
elif index == 2:
return self.z
- _getitem_internal = _getitem_external
+
+ _get_single_internal = _get_single_external
def get_x(self):
"Returns the X component of the Point."
Oops, something went wrong.

0 comments on commit 5e58810

Please sign in to comment.