Fixed #25938 -- Factored out CPointerBase base class for GEOSBase/GDALBase.
This commit is contained in:
parent
4884472447
commit
b01ceae843
|
@ -1,38 +1,6 @@
|
||||||
from ctypes import c_void_p
|
|
||||||
|
|
||||||
from django.contrib.gis.gdal.error import GDALException
|
from django.contrib.gis.gdal.error import GDALException
|
||||||
from django.utils import six
|
from django.contrib.gis.ptr import CPointerBase
|
||||||
|
|
||||||
|
|
||||||
class GDALBase(object):
|
class GDALBase(CPointerBase):
|
||||||
"""
|
null_ptr_exception_class = GDALException
|
||||||
Base object for GDAL objects that has a pointer access property
|
|
||||||
that controls access to the underlying C pointer.
|
|
||||||
"""
|
|
||||||
# Initially the pointer is NULL.
|
|
||||||
_ptr = None
|
|
||||||
|
|
||||||
# Default allowed pointer type.
|
|
||||||
ptr_type = c_void_p
|
|
||||||
|
|
||||||
# Pointer access property.
|
|
||||||
def _get_ptr(self):
|
|
||||||
# Raise an exception if the pointer isn't valid don't
|
|
||||||
# want to be passing NULL pointers to routines --
|
|
||||||
# that's very bad.
|
|
||||||
if self._ptr:
|
|
||||||
return self._ptr
|
|
||||||
else:
|
|
||||||
raise GDALException('GDAL %s pointer no longer valid.' % self.__class__.__name__)
|
|
||||||
|
|
||||||
def _set_ptr(self, ptr):
|
|
||||||
# Only allow the pointer to be set with pointers of the
|
|
||||||
# compatible type or None (NULL).
|
|
||||||
if isinstance(ptr, six.integer_types):
|
|
||||||
self._ptr = self.ptr_type(ptr)
|
|
||||||
elif ptr is None or isinstance(ptr, self.ptr_type):
|
|
||||||
self._ptr = ptr
|
|
||||||
else:
|
|
||||||
raise TypeError('Incompatible pointer type')
|
|
||||||
|
|
||||||
ptr = property(_get_ptr, _set_ptr)
|
|
||||||
|
|
|
@ -51,6 +51,7 @@ from django.utils.six.moves import range
|
||||||
# The OGR_DS_* routines are relevant here.
|
# The OGR_DS_* routines are relevant here.
|
||||||
class DataSource(GDALBase):
|
class DataSource(GDALBase):
|
||||||
"Wraps an OGR Data Source object."
|
"Wraps an OGR Data Source object."
|
||||||
|
destructor = capi.destroy_ds
|
||||||
|
|
||||||
def __init__(self, ds_input, ds_driver=False, write=False, encoding='utf-8'):
|
def __init__(self, ds_input, ds_driver=False, write=False, encoding='utf-8'):
|
||||||
# The write flag.
|
# The write flag.
|
||||||
|
@ -85,13 +86,6 @@ class DataSource(GDALBase):
|
||||||
# Raise an exception if the returned pointer is NULL
|
# Raise an exception if the returned pointer is NULL
|
||||||
raise GDALException('Invalid data source file "%s"' % ds_input)
|
raise GDALException('Invalid data source file "%s"' % ds_input)
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
"Destroys this DataStructure object."
|
|
||||||
try:
|
|
||||||
capi.destroy_ds(self._ptr)
|
|
||||||
except (AttributeError, TypeError):
|
|
||||||
pass # Some part might already have been garbage collected
|
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
"Allows for iteration over the layers in a data source."
|
"Allows for iteration over the layers in a data source."
|
||||||
for i in range(self.layer_count):
|
for i in range(self.layer_count):
|
||||||
|
|
|
@ -49,7 +49,7 @@ class Driver(GDALBase):
|
||||||
|
|
||||||
# Attempting to get the GDAL/OGR driver by the string name.
|
# Attempting to get the GDAL/OGR driver by the string name.
|
||||||
for iface in (vcapi, rcapi):
|
for iface in (vcapi, rcapi):
|
||||||
driver = iface.get_driver_by_name(force_bytes(name))
|
driver = c_void_p(iface.get_driver_by_name(force_bytes(name)))
|
||||||
if driver:
|
if driver:
|
||||||
break
|
break
|
||||||
elif isinstance(dr_input, int):
|
elif isinstance(dr_input, int):
|
||||||
|
|
|
@ -17,6 +17,7 @@ class Feature(GDALBase):
|
||||||
This class that wraps an OGR Feature, needs to be instantiated
|
This class that wraps an OGR Feature, needs to be instantiated
|
||||||
from a Layer object.
|
from a Layer object.
|
||||||
"""
|
"""
|
||||||
|
destructor = capi.destroy_feature
|
||||||
|
|
||||||
def __init__(self, feat, layer):
|
def __init__(self, feat, layer):
|
||||||
"""
|
"""
|
||||||
|
@ -27,13 +28,6 @@ class Feature(GDALBase):
|
||||||
self.ptr = feat
|
self.ptr = feat
|
||||||
self._layer = layer
|
self._layer = layer
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
"Releases a reference to this object."
|
|
||||||
try:
|
|
||||||
capi.destroy_feature(self._ptr)
|
|
||||||
except (AttributeError, TypeError):
|
|
||||||
pass # Some part might already have been garbage collected
|
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
"""
|
"""
|
||||||
Gets the Field object at the specified index, which may be either
|
Gets the Field object at the specified index, which may be either
|
||||||
|
|
|
@ -62,6 +62,7 @@ from django.utils.six.moves import range
|
||||||
# The OGR_G_* routines are relevant here.
|
# The OGR_G_* routines are relevant here.
|
||||||
class OGRGeometry(GDALBase):
|
class OGRGeometry(GDALBase):
|
||||||
"Generally encapsulates an OGR geometry."
|
"Generally encapsulates an OGR geometry."
|
||||||
|
destructor = capi.destroy_geom
|
||||||
|
|
||||||
def __init__(self, geom_input, srs=None):
|
def __init__(self, geom_input, srs=None):
|
||||||
"Initializes Geometry on either WKT or an OGR pointer as input."
|
"Initializes Geometry on either WKT or an OGR pointer as input."
|
||||||
|
@ -120,13 +121,6 @@ class OGRGeometry(GDALBase):
|
||||||
# Setting the class depending upon the OGR Geometry Type
|
# Setting the class depending upon the OGR Geometry Type
|
||||||
self.__class__ = GEO_CLASSES[self.geom_type.num]
|
self.__class__ = GEO_CLASSES[self.geom_type.num]
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
"Deletes this Geometry."
|
|
||||||
try:
|
|
||||||
capi.destroy_geom(self._ptr)
|
|
||||||
except (AttributeError, TypeError):
|
|
||||||
pass # Some part might already have been garbage collected
|
|
||||||
|
|
||||||
# Pickle routines
|
# Pickle routines
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
srs = self.srs
|
srs = self.srs
|
||||||
|
|
|
@ -57,6 +57,8 @@ class GDALRaster(GDALBase):
|
||||||
"""
|
"""
|
||||||
Wraps a raster GDAL Data Source object.
|
Wraps a raster GDAL Data Source object.
|
||||||
"""
|
"""
|
||||||
|
destructor = capi.close_ds
|
||||||
|
|
||||||
def __init__(self, ds_input, write=False):
|
def __init__(self, ds_input, write=False):
|
||||||
self._write = 1 if write else 0
|
self._write = 1 if write else 0
|
||||||
Driver.ensure_registered()
|
Driver.ensure_registered()
|
||||||
|
@ -143,12 +145,6 @@ class GDALRaster(GDALBase):
|
||||||
else:
|
else:
|
||||||
raise GDALException('Invalid data source input type: "{}".'.format(type(ds_input)))
|
raise GDALException('Invalid data source input type: "{}".'.format(type(ds_input)))
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
try:
|
|
||||||
capi.close_ds(self._ptr)
|
|
||||||
except (AttributeError, TypeError):
|
|
||||||
pass # Some part might already have been garbage collected
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ class SpatialReference(GDALBase):
|
||||||
the SpatialReference object "provide[s] services to represent coordinate
|
the SpatialReference object "provide[s] services to represent coordinate
|
||||||
systems (projections and datums) and to transform between them."
|
systems (projections and datums) and to transform between them."
|
||||||
"""
|
"""
|
||||||
|
destructor = capi.release_srs
|
||||||
|
|
||||||
def __init__(self, srs_input='', srs_type='user'):
|
def __init__(self, srs_input='', srs_type='user'):
|
||||||
"""
|
"""
|
||||||
|
@ -91,13 +92,6 @@ class SpatialReference(GDALBase):
|
||||||
elif srs_type == 'epsg':
|
elif srs_type == 'epsg':
|
||||||
self.import_epsg(srs_input)
|
self.import_epsg(srs_input)
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
"Destroys this spatial reference."
|
|
||||||
try:
|
|
||||||
capi.release_srs(self._ptr)
|
|
||||||
except (AttributeError, TypeError):
|
|
||||||
pass # Some part might already have been garbage collected
|
|
||||||
|
|
||||||
def __getitem__(self, target):
|
def __getitem__(self, target):
|
||||||
"""
|
"""
|
||||||
Returns the value of the given string attribute node, None if the node
|
Returns the value of the given string attribute node, None if the node
|
||||||
|
@ -329,6 +323,7 @@ class SpatialReference(GDALBase):
|
||||||
|
|
||||||
class CoordTransform(GDALBase):
|
class CoordTransform(GDALBase):
|
||||||
"The coordinate system transformation object."
|
"The coordinate system transformation object."
|
||||||
|
destructor = capi.destroy_ct
|
||||||
|
|
||||||
def __init__(self, source, target):
|
def __init__(self, source, target):
|
||||||
"Initializes on a source and target SpatialReference objects."
|
"Initializes on a source and target SpatialReference objects."
|
||||||
|
@ -338,12 +333,5 @@ class CoordTransform(GDALBase):
|
||||||
self._srs1_name = source.name
|
self._srs1_name = source.name
|
||||||
self._srs2_name = target.name
|
self._srs2_name = target.name
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
"Deletes this Coordinate Transformation object."
|
|
||||||
try:
|
|
||||||
capi.destroy_ct(self._ptr)
|
|
||||||
except (AttributeError, TypeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Transform from "%s" to "%s"' % (self._srs1_name, self._srs2_name)
|
return 'Transform from "%s" to "%s"' % (self._srs1_name, self._srs2_name)
|
||||||
|
|
|
@ -1,38 +1,6 @@
|
||||||
from ctypes import c_void_p
|
|
||||||
|
|
||||||
from django.contrib.gis.geos.error import GEOSException
|
from django.contrib.gis.geos.error import GEOSException
|
||||||
|
from django.contrib.gis.ptr import CPointerBase
|
||||||
|
|
||||||
|
|
||||||
class GEOSBase(object):
|
class GEOSBase(CPointerBase):
|
||||||
"""
|
null_ptr_exception_class = GEOSException
|
||||||
Base object for GEOS objects that has a pointer access property
|
|
||||||
that controls access to the underlying C pointer.
|
|
||||||
"""
|
|
||||||
# Initially the pointer is NULL.
|
|
||||||
_ptr = None
|
|
||||||
|
|
||||||
# Default allowed pointer type.
|
|
||||||
ptr_type = c_void_p
|
|
||||||
|
|
||||||
# Pointer access property.
|
|
||||||
def _get_ptr(self):
|
|
||||||
# Raise an exception if the pointer isn't valid don't
|
|
||||||
# want to be passing NULL pointers to routines --
|
|
||||||
# that's very bad.
|
|
||||||
if self._ptr:
|
|
||||||
return self._ptr
|
|
||||||
else:
|
|
||||||
raise GEOSException('NULL GEOS %s pointer encountered.' % self.__class__.__name__)
|
|
||||||
|
|
||||||
def _set_ptr(self, ptr):
|
|
||||||
# Only allow the pointer to be set with pointers of the
|
|
||||||
# compatible type or None (NULL).
|
|
||||||
if ptr is None or isinstance(ptr, self.ptr_type):
|
|
||||||
self._ptr = ptr
|
|
||||||
else:
|
|
||||||
raise TypeError('Incompatible pointer type')
|
|
||||||
|
|
||||||
# Property for controlling access to the GEOS object pointers. Using
|
|
||||||
# this raises an exception when the pointer is NULL, thus preventing
|
|
||||||
# the C library from attempting to access an invalid memory location.
|
|
||||||
ptr = property(_get_ptr, _set_ptr)
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||||
_GEOS_CLASSES = None
|
_GEOS_CLASSES = None
|
||||||
|
|
||||||
ptr_type = GEOM_PTR
|
ptr_type = GEOM_PTR
|
||||||
|
destructor = capi.destroy_geom
|
||||||
has_cs = False # Only Point, LineString, LinearRing have coordinate sequences
|
has_cs = False # Only Point, LineString, LinearRing have coordinate sequences
|
||||||
|
|
||||||
def __init__(self, geo_input, srid=None):
|
def __init__(self, geo_input, srid=None):
|
||||||
|
@ -120,16 +121,6 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||||
# geometries that do not have coordinate sequences)
|
# geometries that do not have coordinate sequences)
|
||||||
self._set_cs()
|
self._set_cs()
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
"""
|
|
||||||
Destroys this Geometry; in other words, frees the memory used by the
|
|
||||||
GEOS C++ object.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
capi.destroy_geom(self._ptr)
|
|
||||||
except (AttributeError, TypeError):
|
|
||||||
pass # Some part might already have been garbage collected
|
|
||||||
|
|
||||||
def __copy__(self):
|
def __copy__(self):
|
||||||
"""
|
"""
|
||||||
Returns a clone because the copy of a GEOSGeometry may contain an
|
Returns a clone because the copy of a GEOSGeometry may contain an
|
||||||
|
|
|
@ -9,6 +9,7 @@ class PreparedGeometry(GEOSBase):
|
||||||
operations.
|
operations.
|
||||||
"""
|
"""
|
||||||
ptr_type = capi.PREPGEOM_PTR
|
ptr_type = capi.PREPGEOM_PTR
|
||||||
|
destructor = capi.prepared_destroy
|
||||||
|
|
||||||
def __init__(self, geom):
|
def __init__(self, geom):
|
||||||
# Keeping a reference to the original geometry object to prevent it
|
# Keeping a reference to the original geometry object to prevent it
|
||||||
|
@ -20,12 +21,6 @@ class PreparedGeometry(GEOSBase):
|
||||||
raise TypeError
|
raise TypeError
|
||||||
self.ptr = capi.geos_prepare(geom.ptr)
|
self.ptr = capi.geos_prepare(geom.ptr)
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
try:
|
|
||||||
capi.prepared_destroy(self._ptr)
|
|
||||||
except (AttributeError, TypeError):
|
|
||||||
pass # Some part might already have been garbage collected
|
|
||||||
|
|
||||||
def contains(self, other):
|
def contains(self, other):
|
||||||
return capi.prepared_contains(self.ptr, other.ptr)
|
return capi.prepared_contains(self.ptr, other.ptr)
|
||||||
|
|
||||||
|
|
|
@ -119,17 +119,10 @@ class IOBase(GEOSBase):
|
||||||
self.ptr = self._constructor()
|
self.ptr = self._constructor()
|
||||||
# Loading the real destructor function at this point as doing it in
|
# Loading the real destructor function at this point as doing it in
|
||||||
# __del__ is too late (import error).
|
# __del__ is too late (import error).
|
||||||
self._destructor.func = self._destructor.get_func(
|
self.destructor.func = self.destructor.get_func(
|
||||||
*self._destructor.args, **self._destructor.kwargs
|
*self.destructor.args, **self.destructor.kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
# Cleaning up with the appropriate destructor.
|
|
||||||
try:
|
|
||||||
self._destructor(self._ptr)
|
|
||||||
except (AttributeError, TypeError):
|
|
||||||
pass # Some part might already have been garbage collected
|
|
||||||
|
|
||||||
# ### Base WKB/WKT Reading and Writing objects ###
|
# ### Base WKB/WKT Reading and Writing objects ###
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,8 +131,8 @@ class IOBase(GEOSBase):
|
||||||
# objects.
|
# objects.
|
||||||
class _WKTReader(IOBase):
|
class _WKTReader(IOBase):
|
||||||
_constructor = wkt_reader_create
|
_constructor = wkt_reader_create
|
||||||
_destructor = wkt_reader_destroy
|
|
||||||
ptr_type = WKT_READ_PTR
|
ptr_type = WKT_READ_PTR
|
||||||
|
destructor = wkt_reader_destroy
|
||||||
|
|
||||||
def read(self, wkt):
|
def read(self, wkt):
|
||||||
if not isinstance(wkt, (bytes, six.string_types)):
|
if not isinstance(wkt, (bytes, six.string_types)):
|
||||||
|
@ -149,8 +142,8 @@ class _WKTReader(IOBase):
|
||||||
|
|
||||||
class _WKBReader(IOBase):
|
class _WKBReader(IOBase):
|
||||||
_constructor = wkb_reader_create
|
_constructor = wkb_reader_create
|
||||||
_destructor = wkb_reader_destroy
|
|
||||||
ptr_type = WKB_READ_PTR
|
ptr_type = WKB_READ_PTR
|
||||||
|
destructor = wkb_reader_destroy
|
||||||
|
|
||||||
def read(self, wkb):
|
def read(self, wkb):
|
||||||
"Returns a _pointer_ to C GEOS Geometry object from the given WKB."
|
"Returns a _pointer_ to C GEOS Geometry object from the given WKB."
|
||||||
|
@ -166,8 +159,8 @@ class _WKBReader(IOBase):
|
||||||
# ### WKB/WKT Writer Classes ###
|
# ### WKB/WKT Writer Classes ###
|
||||||
class WKTWriter(IOBase):
|
class WKTWriter(IOBase):
|
||||||
_constructor = wkt_writer_create
|
_constructor = wkt_writer_create
|
||||||
_destructor = wkt_writer_destroy
|
|
||||||
ptr_type = WKT_WRITE_PTR
|
ptr_type = WKT_WRITE_PTR
|
||||||
|
destructor = wkt_writer_destroy
|
||||||
|
|
||||||
_trim = False
|
_trim = False
|
||||||
_precision = None
|
_precision = None
|
||||||
|
@ -219,8 +212,8 @@ class WKTWriter(IOBase):
|
||||||
|
|
||||||
class WKBWriter(IOBase):
|
class WKBWriter(IOBase):
|
||||||
_constructor = wkb_writer_create
|
_constructor = wkb_writer_create
|
||||||
_destructor = wkb_writer_destroy
|
|
||||||
ptr_type = WKB_WRITE_PTR
|
ptr_type = WKB_WRITE_PTR
|
||||||
|
destructor = wkb_writer_destroy
|
||||||
|
|
||||||
def __init__(self, dim=2):
|
def __init__(self, dim=2):
|
||||||
super(WKBWriter, self).__init__()
|
super(WKBWriter, self).__init__()
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from django.contrib.gis.geos.base import GEOSBase
|
||||||
from django.contrib.gis.geos.libgeos import (
|
from django.contrib.gis.geos.libgeos import (
|
||||||
CONTEXT_PTR, error_h, lgeos, notice_h,
|
CONTEXT_PTR, error_h, lgeos, notice_h,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class GEOSContextHandle(object):
|
class GEOSContextHandle(GEOSBase):
|
||||||
"""
|
"""
|
||||||
Python object representing a GEOS context handle.
|
Python object representing a GEOS context handle.
|
||||||
"""
|
"""
|
||||||
|
ptr_type = CONTEXT_PTR
|
||||||
|
destructor = lgeos.finishGEOS_r
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# Initializing the context handler for this thread with
|
# Initializing the context handler for this thread with
|
||||||
# the notice and error handler.
|
# the notice and error handler.
|
||||||
self.ptr = lgeos.initGEOS_r(notice_h, error_h)
|
self.ptr = lgeos.initGEOS_r(notice_h, error_h)
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
if self.ptr and lgeos:
|
|
||||||
lgeos.finishGEOS_r(self.ptr)
|
|
||||||
|
|
||||||
|
|
||||||
# Defining a thread-local object and creating an instance
|
# Defining a thread-local object and creating an instance
|
||||||
# to hold a reference to GEOSContextHandle for this thread.
|
# to hold a reference to GEOSContextHandle for this thread.
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
from ctypes import c_void_p
|
||||||
|
|
||||||
|
|
||||||
|
class CPointerBase(object):
|
||||||
|
"""
|
||||||
|
Base class for objects that have a pointer access property
|
||||||
|
that controls access to the underlying C pointer.
|
||||||
|
"""
|
||||||
|
_ptr = None # Initially the pointer is NULL.
|
||||||
|
ptr_type = c_void_p
|
||||||
|
destructor = None
|
||||||
|
null_ptr_exception_class = AttributeError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ptr(self):
|
||||||
|
# Raise an exception if the pointer isn't valid so that NULL pointers
|
||||||
|
# aren't passed to routines -- that's very bad.
|
||||||
|
if self._ptr:
|
||||||
|
return self._ptr
|
||||||
|
raise self.null_ptr_exception_class('NULL %s pointer encountered.' % self.__class__.__name__)
|
||||||
|
|
||||||
|
@ptr.setter
|
||||||
|
def ptr(self, ptr):
|
||||||
|
# Only allow the pointer to be set with pointers of the compatible
|
||||||
|
# type or None (NULL).
|
||||||
|
if not (ptr is None or isinstance(ptr, self.ptr_type)):
|
||||||
|
raise TypeError('Incompatible pointer type: %s.' % type(ptr))
|
||||||
|
self._ptr = ptr
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
"""
|
||||||
|
Free the memory used by the C++ object.
|
||||||
|
"""
|
||||||
|
if self.destructor and self._ptr:
|
||||||
|
try:
|
||||||
|
self.destructor(self.ptr)
|
||||||
|
except (AttributeError, TypeError):
|
||||||
|
pass # Some part might already have been garbage collected
|
|
@ -14,7 +14,6 @@ from django.contrib.gis.geos import (
|
||||||
LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
|
LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
|
||||||
fromfile, fromstr,
|
fromfile, fromstr,
|
||||||
)
|
)
|
||||||
from django.contrib.gis.geos.base import GEOSBase
|
|
||||||
from django.contrib.gis.geos.libgeos import geos_version_info
|
from django.contrib.gis.geos.libgeos import geos_version_info
|
||||||
from django.contrib.gis.shortcuts import numpy
|
from django.contrib.gis.shortcuts import numpy
|
||||||
from django.template import Context
|
from django.template import Context
|
||||||
|
@ -31,52 +30,6 @@ from ..test_data import TestDataMixin
|
||||||
@skipUnless(HAS_GEOS, "Geos is required.")
|
@skipUnless(HAS_GEOS, "Geos is required.")
|
||||||
class GEOSTest(SimpleTestCase, TestDataMixin):
|
class GEOSTest(SimpleTestCase, TestDataMixin):
|
||||||
|
|
||||||
def test_base(self):
|
|
||||||
"Tests out the GEOSBase class."
|
|
||||||
# Testing out GEOSBase class, which provides a `ptr` property
|
|
||||||
# that abstracts out access to underlying C pointers.
|
|
||||||
class FakeGeom1(GEOSBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# This one only accepts pointers to floats
|
|
||||||
c_float_p = ctypes.POINTER(ctypes.c_float)
|
|
||||||
|
|
||||||
class FakeGeom2(GEOSBase):
|
|
||||||
ptr_type = c_float_p
|
|
||||||
|
|
||||||
# Default ptr_type is `c_void_p`.
|
|
||||||
fg1 = FakeGeom1()
|
|
||||||
# Default ptr_type is C float pointer
|
|
||||||
fg2 = FakeGeom2()
|
|
||||||
|
|
||||||
# These assignments are OK -- None is allowed because
|
|
||||||
# it's equivalent to the NULL pointer.
|
|
||||||
fg1.ptr = ctypes.c_void_p()
|
|
||||||
fg1.ptr = None
|
|
||||||
fg2.ptr = c_float_p(ctypes.c_float(5.23))
|
|
||||||
fg2.ptr = None
|
|
||||||
|
|
||||||
# Because pointers have been set to NULL, an exception should be
|
|
||||||
# raised when we try to access it. Raising an exception is
|
|
||||||
# preferable to a segmentation fault that commonly occurs when
|
|
||||||
# a C method is given a NULL memory reference.
|
|
||||||
for fg in (fg1, fg2):
|
|
||||||
# Equivalent to `fg.ptr`
|
|
||||||
with self.assertRaises(GEOSException):
|
|
||||||
fg._get_ptr()
|
|
||||||
|
|
||||||
# Anything that is either not None or the acceptable pointer type will
|
|
||||||
# result in a TypeError when trying to assign it to the `ptr` property.
|
|
||||||
# Thus, memory addresses (integers) and pointers of the incorrect type
|
|
||||||
# (in `bad_ptrs`) will not be allowed.
|
|
||||||
bad_ptrs = (5, ctypes.c_char_p(b'foobar'))
|
|
||||||
for bad_ptr in bad_ptrs:
|
|
||||||
# Equivalent to `fg.ptr = bad_ptr`
|
|
||||||
with self.assertRaises(TypeError):
|
|
||||||
fg1._set_ptr(bad_ptr)
|
|
||||||
with self.assertRaises(TypeError):
|
|
||||||
fg2._set_ptr(bad_ptr)
|
|
||||||
|
|
||||||
def test_wkt(self):
|
def test_wkt(self):
|
||||||
"Testing WKT output."
|
"Testing WKT output."
|
||||||
for g in self.geometries.wkt_out:
|
for g in self.geometries.wkt_out:
|
||||||
|
|
|
@ -37,7 +37,7 @@ class GEOSIOTest(SimpleTestCase):
|
||||||
# Creating a WKTWriter instance, testing its ptr property.
|
# Creating a WKTWriter instance, testing its ptr property.
|
||||||
wkt_w = WKTWriter()
|
wkt_w = WKTWriter()
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
wkt_w._set_ptr(WKTReader.ptr_type())
|
wkt_w.ptr = WKTReader.ptr_type()
|
||||||
|
|
||||||
ref = GEOSGeometry('POINT (5 23)')
|
ref = GEOSGeometry('POINT (5 23)')
|
||||||
ref_wkt = 'POINT (5.0000000000000000 23.0000000000000000)'
|
ref_wkt = 'POINT (5.0000000000000000 23.0000000000000000)'
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
from django.contrib.gis.ptr import CPointerBase
|
||||||
|
from django.test import SimpleTestCase, mock
|
||||||
|
|
||||||
|
|
||||||
|
class CPointerBaseTests(SimpleTestCase):
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
destructor_mock = mock.Mock()
|
||||||
|
|
||||||
|
class NullPointerException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class FakeGeom1(CPointerBase):
|
||||||
|
null_ptr_exception_class = NullPointerException
|
||||||
|
|
||||||
|
class FakeGeom2(FakeGeom1):
|
||||||
|
ptr_type = ctypes.POINTER(ctypes.c_float)
|
||||||
|
destructor = destructor_mock
|
||||||
|
|
||||||
|
fg1 = FakeGeom1()
|
||||||
|
fg2 = FakeGeom2()
|
||||||
|
|
||||||
|
# These assignments are OK. None is allowed because it's equivalent
|
||||||
|
# to the NULL pointer.
|
||||||
|
fg1.ptr = fg1.ptr_type()
|
||||||
|
fg1.ptr = None
|
||||||
|
fg2.ptr = fg2.ptr_type(ctypes.c_float(5.23))
|
||||||
|
fg2.ptr = None
|
||||||
|
|
||||||
|
# Because pointers have been set to NULL, an exception is raised on
|
||||||
|
# access. Raising an exception is preferable to a segmentation fault
|
||||||
|
# that commonly occurs when a C method is given a NULL reference.
|
||||||
|
for fg in (fg1, fg2):
|
||||||
|
with self.assertRaises(NullPointerException):
|
||||||
|
fg.ptr
|
||||||
|
|
||||||
|
# Anything that's either not None or the acceptable pointer type
|
||||||
|
# results in a TypeError when trying to assign it to the `ptr` property.
|
||||||
|
# Thus, memory addresses (integers) and pointers of the incorrect type
|
||||||
|
# (in `bad_ptrs`) aren't allowed.
|
||||||
|
bad_ptrs = (5, ctypes.c_char_p(b'foobar'))
|
||||||
|
for bad_ptr in bad_ptrs:
|
||||||
|
for fg in (fg1, fg2):
|
||||||
|
with self.assertRaisesMessage(TypeError, 'Incompatible pointer type'):
|
||||||
|
fg.ptr = bad_ptr
|
||||||
|
|
||||||
|
# Object can be deleted without a destructor set.
|
||||||
|
fg = FakeGeom1()
|
||||||
|
fg.ptr = fg.ptr_type(1)
|
||||||
|
del fg
|
||||||
|
|
||||||
|
# A NULL pointer isn't passed to the destructor.
|
||||||
|
fg = FakeGeom2()
|
||||||
|
fg.ptr = None
|
||||||
|
del fg
|
||||||
|
self.assertFalse(destructor_mock.called)
|
||||||
|
|
||||||
|
# The destructor is called if set.
|
||||||
|
fg = FakeGeom2()
|
||||||
|
ptr = fg.ptr_type(ctypes.c_float(1.))
|
||||||
|
fg.ptr = ptr
|
||||||
|
del fg
|
||||||
|
destructor_mock.assert_called_with(ptr)
|
Loading…
Reference in New Issue