Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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
jbronn committed Apr 10, 2009
1 parent 29dc915 commit bdc8a59
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 121 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -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>
Expand Down
16 changes: 9 additions & 7 deletions django/contrib/gis/geos/collections.py
Expand Up @@ -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."
Expand Down
1 change: 0 additions & 1 deletion django/contrib/gis/geos/geometry.py
Expand Up @@ -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."
Expand Down
8 changes: 4 additions & 4 deletions django/contrib/gis/geos/linestring.py
Expand Up @@ -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

Expand Down
157 changes: 116 additions & 41 deletions django/contrib/gis/geos/mutable_list.py
Expand Up @@ -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]
Expand All @@ -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
Expand All @@ -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)

Expand All @@ -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]
Expand All @@ -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])
Expand Down Expand Up @@ -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())

Expand Down Expand Up @@ -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())
10 changes: 5 additions & 5 deletions django/contrib/gis/geos/point.py
Expand Up @@ -5,6 +5,7 @@

class Point(GEOSGeometry):
_minlength = 2
_maxlength = 3

def __init__(self, x, y=None, z=None, srid=None):
"""
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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."
Expand Down

0 comments on commit bdc8a59

Please sign in to comment.