[py3] Added buffer/memoryview compatibility
Even if buffer and memoryview are not strictly identical, it should be safe to consider them equivalent for GIS support. Thanks Aymeric Augustin for the review.
This commit is contained in:
parent
3174b5f2f5
commit
8cdc84726e
|
@ -0,0 +1,6 @@
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
memoryview = memoryview
|
||||||
|
else:
|
||||||
|
memoryview = buffer
|
|
@ -5,6 +5,7 @@ corresponding to geographic model fields.
|
||||||
|
|
||||||
Thanks to Robert Coup for providing this functionality (see #4322).
|
Thanks to Robert Coup for providing this functionality (see #4322).
|
||||||
"""
|
"""
|
||||||
|
from django.contrib.gis import memoryview
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
class GeometryProxy(object):
|
class GeometryProxy(object):
|
||||||
|
@ -54,7 +55,7 @@ class GeometryProxy(object):
|
||||||
if isinstance(value, self._klass) and (str(value.geom_type).upper() == gtype or gtype == 'GEOMETRY'):
|
if isinstance(value, self._klass) and (str(value.geom_type).upper() == gtype or gtype == 'GEOMETRY'):
|
||||||
# Assigning the SRID to the geometry.
|
# Assigning the SRID to the geometry.
|
||||||
if value.srid is None: value.srid = self._field.srid
|
if value.srid is None: value.srid = self._field.srid
|
||||||
elif value is None or isinstance(value, six.string_types + (buffer,)):
|
elif value is None or isinstance(value, six.string_types + (memoryview,)):
|
||||||
# Set with None, WKT, HEX, or WKB
|
# Set with None, WKT, HEX, or WKB
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.db import connections
|
from django.db import connections
|
||||||
from django.db.models.query import QuerySet, ValuesQuerySet, ValuesListQuerySet
|
from django.db.models.query import QuerySet, ValuesQuerySet, ValuesListQuerySet
|
||||||
|
|
||||||
|
from django.contrib.gis import memoryview
|
||||||
from django.contrib.gis.db.models import aggregates
|
from django.contrib.gis.db.models import aggregates
|
||||||
from django.contrib.gis.db.models.fields import get_srid_info, PointField, LineStringField
|
from django.contrib.gis.db.models.fields import get_srid_info, PointField, LineStringField
|
||||||
from django.contrib.gis.db.models.sql import AreaField, DistanceField, GeomField, GeoQuery
|
from django.contrib.gis.db.models.sql import AreaField, DistanceField, GeomField, GeoQuery
|
||||||
|
@ -676,7 +677,7 @@ class GeoQuerySet(QuerySet):
|
||||||
if not backend.geography:
|
if not backend.geography:
|
||||||
if not isinstance(geo_field, PointField):
|
if not isinstance(geo_field, PointField):
|
||||||
raise ValueError('Spherical distance calculation only supported on PointFields.')
|
raise ValueError('Spherical distance calculation only supported on PointFields.')
|
||||||
if not str(Geometry(buffer(params[0].ewkb)).geom_type) == 'Point':
|
if not str(Geometry(memoryview(params[0].ewkb)).geom_type) == 'Point':
|
||||||
raise ValueError('Spherical distance calculation only supported with Point Geometry parameters')
|
raise ValueError('Spherical distance calculation only supported with Point Geometry parameters')
|
||||||
# The `function` procedure argument needs to be set differently for
|
# The `function` procedure argument needs to be set differently for
|
||||||
# geodetic distance calculations.
|
# geodetic distance calculations.
|
||||||
|
|
|
@ -43,6 +43,8 @@ import sys
|
||||||
from binascii import a2b_hex, b2a_hex
|
from binascii import a2b_hex, b2a_hex
|
||||||
from ctypes import byref, string_at, c_char_p, c_double, c_ubyte, c_void_p
|
from ctypes import byref, string_at, c_char_p, c_double, c_ubyte, c_void_p
|
||||||
|
|
||||||
|
from django.contrib.gis import memoryview
|
||||||
|
|
||||||
# Getting GDAL prerequisites
|
# Getting GDAL prerequisites
|
||||||
from django.contrib.gis.gdal.base import GDALBase
|
from django.contrib.gis.gdal.base import GDALBase
|
||||||
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
|
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
|
||||||
|
@ -76,7 +78,7 @@ class OGRGeometry(GDALBase):
|
||||||
|
|
||||||
# If HEX, unpack input to to a binary buffer.
|
# If HEX, unpack input to to a binary buffer.
|
||||||
if str_instance and hex_regex.match(geom_input):
|
if str_instance and hex_regex.match(geom_input):
|
||||||
geom_input = buffer(a2b_hex(geom_input.upper()))
|
geom_input = memoryview(a2b_hex(geom_input.upper()))
|
||||||
str_instance = False
|
str_instance = False
|
||||||
|
|
||||||
# Constructing the geometry,
|
# Constructing the geometry,
|
||||||
|
@ -106,7 +108,7 @@ class OGRGeometry(GDALBase):
|
||||||
# (e.g., 'Point', 'POLYGON').
|
# (e.g., 'Point', 'POLYGON').
|
||||||
ogr_t = OGRGeomType(geom_input)
|
ogr_t = OGRGeomType(geom_input)
|
||||||
g = capi.create_geom(OGRGeomType(geom_input).num)
|
g = capi.create_geom(OGRGeomType(geom_input).num)
|
||||||
elif isinstance(geom_input, buffer):
|
elif isinstance(geom_input, memoryview):
|
||||||
# WKB was passed in
|
# WKB was passed in
|
||||||
g = capi.from_wkb(str(geom_input), None, byref(c_void_p()), len(geom_input))
|
g = capi.from_wkb(str(geom_input), None, byref(c_void_p()), len(geom_input))
|
||||||
elif isinstance(geom_input, OGRGeomType):
|
elif isinstance(geom_input, OGRGeomType):
|
||||||
|
@ -354,7 +356,7 @@ class OGRGeometry(GDALBase):
|
||||||
buf = (c_ubyte * sz)()
|
buf = (c_ubyte * sz)()
|
||||||
wkb = capi.to_wkb(self.ptr, byteorder, byref(buf))
|
wkb = capi.to_wkb(self.ptr, byteorder, byref(buf))
|
||||||
# Returning a buffer of the string at the pointer.
|
# Returning a buffer of the string at the pointer.
|
||||||
return buffer(string_at(buf, sz))
|
return memoryview(string_at(buf, sz))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def wkt(self):
|
def wkt(self):
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from django.contrib.gis import memoryview
|
||||||
from django.contrib.gis.geos.geometry import GEOSGeometry, wkt_regex, hex_regex
|
from django.contrib.gis.geos.geometry import GEOSGeometry, wkt_regex, hex_regex
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
@ -18,7 +19,7 @@ def fromfile(file_h):
|
||||||
if wkt_regex.match(buf) or hex_regex.match(buf):
|
if wkt_regex.match(buf) or hex_regex.match(buf):
|
||||||
return GEOSGeometry(buf)
|
return GEOSGeometry(buf)
|
||||||
else:
|
else:
|
||||||
return GEOSGeometry(buffer(buf))
|
return GEOSGeometry(memoryview(buf))
|
||||||
|
|
||||||
def fromstr(string, **kwargs):
|
def fromstr(string, **kwargs):
|
||||||
"Given a string value, returns a GEOSGeometry object."
|
"Given a string value, returns a GEOSGeometry object."
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
# Python, ctypes and types dependencies.
|
# Python, ctypes and types dependencies.
|
||||||
from ctypes import addressof, byref, c_double
|
from ctypes import addressof, byref, c_double
|
||||||
|
|
||||||
|
from django.contrib.gis import memoryview
|
||||||
# super-class for mutable list behavior
|
# super-class for mutable list behavior
|
||||||
from django.contrib.gis.geos.mutable_list import ListMixin
|
from django.contrib.gis.geos.mutable_list import ListMixin
|
||||||
|
|
||||||
|
@ -75,7 +76,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||||
elif isinstance(geo_input, GEOM_PTR):
|
elif isinstance(geo_input, GEOM_PTR):
|
||||||
# When the input is a pointer to a geomtry (GEOM_PTR).
|
# When the input is a pointer to a geomtry (GEOM_PTR).
|
||||||
g = geo_input
|
g = geo_input
|
||||||
elif isinstance(geo_input, buffer):
|
elif isinstance(geo_input, memoryview):
|
||||||
# When the input is a buffer (WKB).
|
# When the input is a buffer (WKB).
|
||||||
g = wkb_r().read(geo_input)
|
g = wkb_r().read(geo_input)
|
||||||
elif isinstance(geo_input, GEOSGeometry):
|
elif isinstance(geo_input, GEOSGeometry):
|
||||||
|
@ -139,12 +140,12 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
# The pickled state is simply a tuple of the WKB (in string form)
|
# The pickled state is simply a tuple of the WKB (in string form)
|
||||||
# and the SRID.
|
# and the SRID.
|
||||||
return str(self.wkb), self.srid
|
return bytes(self.wkb), self.srid
|
||||||
|
|
||||||
def __setstate__(self, state):
|
def __setstate__(self, state):
|
||||||
# Instantiating from the tuple state that was pickled.
|
# Instantiating from the tuple state that was pickled.
|
||||||
wkb, srid = state
|
wkb, srid = state
|
||||||
ptr = wkb_r().read(buffer(wkb))
|
ptr = wkb_r().read(memoryview(wkb))
|
||||||
if not ptr: raise GEOSException('Invalid Geometry loaded from pickled state.')
|
if not ptr: raise GEOSException('Invalid Geometry loaded from pickled state.')
|
||||||
self.ptr = ptr
|
self.ptr = ptr
|
||||||
self._post_init(srid)
|
self._post_init(srid)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import threading
|
import threading
|
||||||
from ctypes import byref, c_char_p, c_int, c_char, c_size_t, Structure, POINTER
|
from ctypes import byref, c_char_p, c_int, c_char, c_size_t, Structure, POINTER
|
||||||
|
from django.contrib.gis import memoryview
|
||||||
from django.contrib.gis.geos.base import GEOSBase
|
from django.contrib.gis.geos.base import GEOSBase
|
||||||
from django.contrib.gis.geos.libgeos import GEOM_PTR
|
from django.contrib.gis.geos.libgeos import GEOM_PTR
|
||||||
from django.contrib.gis.geos.prototypes.errcheck import check_geom, check_string, check_sized_string
|
from django.contrib.gis.geos.prototypes.errcheck import check_geom, check_string, check_sized_string
|
||||||
|
@ -130,8 +131,8 @@ class _WKBReader(IOBase):
|
||||||
|
|
||||||
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."
|
||||||
if isinstance(wkb, buffer):
|
if isinstance(wkb, memoryview):
|
||||||
wkb_s = str(wkb)
|
wkb_s = bytes(wkb)
|
||||||
return wkb_reader_read(self.ptr, wkb_s, len(wkb_s))
|
return wkb_reader_read(self.ptr, wkb_s, len(wkb_s))
|
||||||
elif isinstance(wkb, six.string_types):
|
elif isinstance(wkb, six.string_types):
|
||||||
return wkb_reader_read_hex(self.ptr, wkb, len(wkb))
|
return wkb_reader_read_hex(self.ptr, wkb, len(wkb))
|
||||||
|
@ -155,7 +156,7 @@ class WKBWriter(IOBase):
|
||||||
|
|
||||||
def write(self, geom):
|
def write(self, geom):
|
||||||
"Returns the WKB representation of the given geometry."
|
"Returns the WKB representation of the given geometry."
|
||||||
return buffer(wkb_writer_write(self.ptr, geom.ptr, byref(c_size_t())))
|
return memoryview(wkb_writer_write(self.ptr, geom.ptr, byref(c_size_t())))
|
||||||
|
|
||||||
def write_hex(self, geom):
|
def write_hex(self, geom):
|
||||||
"Returns the HEXEWKB representation of the given geometry."
|
"Returns the HEXEWKB representation of the given geometry."
|
||||||
|
|
|
@ -2,6 +2,7 @@ import ctypes
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
from django.contrib.gis import memoryview
|
||||||
from django.contrib.gis.geos import (GEOSException, GEOSIndexError, GEOSGeometry,
|
from django.contrib.gis.geos import (GEOSException, GEOSIndexError, GEOSGeometry,
|
||||||
GeometryCollection, Point, MultiPoint, Polygon, MultiPolygon, LinearRing,
|
GeometryCollection, Point, MultiPoint, Polygon, MultiPolygon, LinearRing,
|
||||||
LineString, MultiLineString, fromfile, fromstr, geos_version_info)
|
LineString, MultiLineString, fromfile, fromstr, geos_version_info)
|
||||||
|
@ -118,9 +119,9 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
||||||
self.fail('Should have raised GEOSException.')
|
self.fail('Should have raised GEOSException.')
|
||||||
|
|
||||||
# Same for EWKB.
|
# Same for EWKB.
|
||||||
self.assertEqual(buffer(a2b_hex(hexewkb_2d)), pnt_2d.ewkb)
|
self.assertEqual(memoryview(a2b_hex(hexewkb_2d)), pnt_2d.ewkb)
|
||||||
if GEOS_PREPARE:
|
if GEOS_PREPARE:
|
||||||
self.assertEqual(buffer(a2b_hex(hexewkb_3d)), pnt_3d.ewkb)
|
self.assertEqual(memoryview(a2b_hex(hexewkb_3d)), pnt_3d.ewkb)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
ewkb = pnt_3d.ewkb
|
ewkb = pnt_3d.ewkb
|
||||||
|
@ -150,7 +151,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Bad WKB
|
# Bad WKB
|
||||||
self.assertRaises(GEOSException, GEOSGeometry, buffer('0'))
|
self.assertRaises(GEOSException, GEOSGeometry, memoryview(b'0'))
|
||||||
|
|
||||||
print("\nEND - expecting GEOS_ERROR; safe to ignore.\n")
|
print("\nEND - expecting GEOS_ERROR; safe to ignore.\n")
|
||||||
|
|
||||||
|
@ -182,7 +183,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
||||||
"Testing creation from WKB."
|
"Testing creation from WKB."
|
||||||
from binascii import a2b_hex
|
from binascii import a2b_hex
|
||||||
for g in self.geometries.hex_wkt:
|
for g in self.geometries.hex_wkt:
|
||||||
wkb = buffer(a2b_hex(g.hex))
|
wkb = memoryview(a2b_hex(g.hex))
|
||||||
geom_h = GEOSGeometry(wkb)
|
geom_h = GEOSGeometry(wkb)
|
||||||
# we need to do this so decimal places get normalised
|
# we need to do this so decimal places get normalised
|
||||||
geom_t = fromstr(g.wkt)
|
geom_t = fromstr(g.wkt)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import binascii
|
import binascii
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from django.contrib.gis import memoryview
|
||||||
from django.contrib.gis.geos import GEOSGeometry, WKTReader, WKTWriter, WKBReader, WKBWriter, geos_version_info
|
from django.contrib.gis.geos import GEOSGeometry, WKTReader, WKTWriter, WKBReader, WKBWriter, geos_version_info
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
|
@ -20,7 +22,7 @@ class GEOSIOTest(unittest.TestCase):
|
||||||
|
|
||||||
# Should only accept six.string_types objects.
|
# Should only accept six.string_types objects.
|
||||||
self.assertRaises(TypeError, wkt_r.read, 1)
|
self.assertRaises(TypeError, wkt_r.read, 1)
|
||||||
self.assertRaises(TypeError, wkt_r.read, buffer('foo'))
|
self.assertRaises(TypeError, wkt_r.read, memoryview(b'foo'))
|
||||||
|
|
||||||
def test02_wktwriter(self):
|
def test02_wktwriter(self):
|
||||||
# Creating a WKTWriter instance, testing its ptr property.
|
# Creating a WKTWriter instance, testing its ptr property.
|
||||||
|
@ -29,14 +31,14 @@ class GEOSIOTest(unittest.TestCase):
|
||||||
|
|
||||||
ref = GEOSGeometry('POINT (5 23)')
|
ref = GEOSGeometry('POINT (5 23)')
|
||||||
ref_wkt = 'POINT (5.0000000000000000 23.0000000000000000)'
|
ref_wkt = 'POINT (5.0000000000000000 23.0000000000000000)'
|
||||||
self.assertEqual(ref_wkt, wkt_w.write(ref))
|
self.assertEqual(ref_wkt, wkt_w.write(ref).decode())
|
||||||
|
|
||||||
def test03_wkbreader(self):
|
def test03_wkbreader(self):
|
||||||
# Creating a WKBReader instance
|
# Creating a WKBReader instance
|
||||||
wkb_r = WKBReader()
|
wkb_r = WKBReader()
|
||||||
|
|
||||||
hex = '000000000140140000000000004037000000000000'
|
hex = b'000000000140140000000000004037000000000000'
|
||||||
wkb = buffer(binascii.a2b_hex(hex))
|
wkb = memoryview(binascii.a2b_hex(hex))
|
||||||
ref = GEOSGeometry(hex)
|
ref = GEOSGeometry(hex)
|
||||||
|
|
||||||
# read() should return a GEOSGeometry on either a hex string or
|
# read() should return a GEOSGeometry on either a hex string or
|
||||||
|
@ -56,10 +58,10 @@ class GEOSIOTest(unittest.TestCase):
|
||||||
# Representations of 'POINT (5 23)' in hex -- one normal and
|
# Representations of 'POINT (5 23)' in hex -- one normal and
|
||||||
# the other with the byte order changed.
|
# the other with the byte order changed.
|
||||||
g = GEOSGeometry('POINT (5 23)')
|
g = GEOSGeometry('POINT (5 23)')
|
||||||
hex1 = '010100000000000000000014400000000000003740'
|
hex1 = b'010100000000000000000014400000000000003740'
|
||||||
wkb1 = buffer(binascii.a2b_hex(hex1))
|
wkb1 = memoryview(binascii.a2b_hex(hex1))
|
||||||
hex2 = '000000000140140000000000004037000000000000'
|
hex2 = b'000000000140140000000000004037000000000000'
|
||||||
wkb2 = buffer(binascii.a2b_hex(hex2))
|
wkb2 = memoryview(binascii.a2b_hex(hex2))
|
||||||
|
|
||||||
self.assertEqual(hex1, wkb_w.write_hex(g))
|
self.assertEqual(hex1, wkb_w.write_hex(g))
|
||||||
self.assertEqual(wkb1, wkb_w.write(g))
|
self.assertEqual(wkb1, wkb_w.write(g))
|
||||||
|
@ -81,10 +83,10 @@ class GEOSIOTest(unittest.TestCase):
|
||||||
g = GEOSGeometry('POINT (5 23 17)')
|
g = GEOSGeometry('POINT (5 23 17)')
|
||||||
g.srid = 4326
|
g.srid = 4326
|
||||||
|
|
||||||
hex3d = '0101000080000000000000144000000000000037400000000000003140'
|
hex3d = b'0101000080000000000000144000000000000037400000000000003140'
|
||||||
wkb3d = buffer(binascii.a2b_hex(hex3d))
|
wkb3d = memoryview(binascii.a2b_hex(hex3d))
|
||||||
hex3d_srid = '01010000A0E6100000000000000000144000000000000037400000000000003140'
|
hex3d_srid = b'01010000A0E6100000000000000000144000000000000037400000000000003140'
|
||||||
wkb3d_srid = buffer(binascii.a2b_hex(hex3d_srid))
|
wkb3d_srid = memoryview(binascii.a2b_hex(hex3d_srid))
|
||||||
|
|
||||||
# Ensuring bad output dimensions are not accepted
|
# Ensuring bad output dimensions are not accepted
|
||||||
for bad_outdim in (-1, 0, 1, 4, 423, 'foo', None):
|
for bad_outdim in (-1, 0, 1, 4, 423, 'foo', None):
|
||||||
|
|
Loading…
Reference in New Issue