From 8cdc84726e13da4c796db614765658835d4786a1 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sun, 23 Sep 2012 19:55:52 +0200 Subject: [PATCH] [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. --- django/contrib/gis/__init__.py | 6 +++++ django/contrib/gis/db/models/proxy.py | 3 ++- django/contrib/gis/db/models/query.py | 3 ++- django/contrib/gis/gdal/geometries.py | 8 ++++--- django/contrib/gis/geos/factory.py | 3 ++- django/contrib/gis/geos/geometry.py | 7 +++--- django/contrib/gis/geos/prototypes/io.py | 7 +++--- django/contrib/gis/geos/tests/test_geos.py | 9 ++++---- django/contrib/gis/geos/tests/test_io.py | 26 ++++++++++++---------- 9 files changed, 44 insertions(+), 28 deletions(-) diff --git a/django/contrib/gis/__init__.py b/django/contrib/gis/__init__.py index e69de29bb2..c996fdfdc2 100644 --- a/django/contrib/gis/__init__.py +++ b/django/contrib/gis/__init__.py @@ -0,0 +1,6 @@ +from django.utils import six + +if six.PY3: + memoryview = memoryview +else: + memoryview = buffer diff --git a/django/contrib/gis/db/models/proxy.py b/django/contrib/gis/db/models/proxy.py index 413610fc5c..1fdc5036ba 100644 --- a/django/contrib/gis/db/models/proxy.py +++ b/django/contrib/gis/db/models/proxy.py @@ -5,6 +5,7 @@ corresponding to geographic model fields. Thanks to Robert Coup for providing this functionality (see #4322). """ +from django.contrib.gis import memoryview from django.utils import six 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'): # Assigning the SRID to the geometry. 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 pass else: diff --git a/django/contrib/gis/db/models/query.py b/django/contrib/gis/db/models/query.py index d87e151aea..b994252002 100644 --- a/django/contrib/gis/db/models/query.py +++ b/django/contrib/gis/db/models/query.py @@ -1,6 +1,7 @@ from django.db import connections 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.fields import get_srid_info, PointField, LineStringField 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 isinstance(geo_field, PointField): 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') # The `function` procedure argument needs to be set differently for # geodetic distance calculations. diff --git a/django/contrib/gis/gdal/geometries.py b/django/contrib/gis/gdal/geometries.py index 373ece777d..1985062c56 100644 --- a/django/contrib/gis/gdal/geometries.py +++ b/django/contrib/gis/gdal/geometries.py @@ -43,6 +43,8 @@ import sys from binascii import a2b_hex, b2a_hex 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 from django.contrib.gis.gdal.base import GDALBase 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 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 # Constructing the geometry, @@ -106,7 +108,7 @@ class OGRGeometry(GDALBase): # (e.g., 'Point', 'POLYGON'). ogr_t = OGRGeomType(geom_input) g = capi.create_geom(OGRGeomType(geom_input).num) - elif isinstance(geom_input, buffer): + elif isinstance(geom_input, memoryview): # WKB was passed in g = capi.from_wkb(str(geom_input), None, byref(c_void_p()), len(geom_input)) elif isinstance(geom_input, OGRGeomType): @@ -354,7 +356,7 @@ class OGRGeometry(GDALBase): buf = (c_ubyte * sz)() wkb = capi.to_wkb(self.ptr, byteorder, byref(buf)) # Returning a buffer of the string at the pointer. - return buffer(string_at(buf, sz)) + return memoryview(string_at(buf, sz)) @property def wkt(self): diff --git a/django/contrib/gis/geos/factory.py b/django/contrib/gis/geos/factory.py index fbd7d5a3e9..42b0310f0a 100644 --- a/django/contrib/gis/geos/factory.py +++ b/django/contrib/gis/geos/factory.py @@ -1,3 +1,4 @@ +from django.contrib.gis import memoryview from django.contrib.gis.geos.geometry import GEOSGeometry, wkt_regex, hex_regex from django.utils import six @@ -18,7 +19,7 @@ def fromfile(file_h): if wkt_regex.match(buf) or hex_regex.match(buf): return GEOSGeometry(buf) else: - return GEOSGeometry(buffer(buf)) + return GEOSGeometry(memoryview(buf)) def fromstr(string, **kwargs): "Given a string value, returns a GEOSGeometry object." diff --git a/django/contrib/gis/geos/geometry.py b/django/contrib/gis/geos/geometry.py index 4e5409de1d..41bf4dfa1f 100644 --- a/django/contrib/gis/geos/geometry.py +++ b/django/contrib/gis/geos/geometry.py @@ -5,6 +5,7 @@ # Python, ctypes and types dependencies. from ctypes import addressof, byref, c_double +from django.contrib.gis import memoryview # super-class for mutable list behavior from django.contrib.gis.geos.mutable_list import ListMixin @@ -75,7 +76,7 @@ class GEOSGeometry(GEOSBase, ListMixin): elif isinstance(geo_input, GEOM_PTR): # When the input is a pointer to a geomtry (GEOM_PTR). g = geo_input - elif isinstance(geo_input, buffer): + elif isinstance(geo_input, memoryview): # When the input is a buffer (WKB). g = wkb_r().read(geo_input) elif isinstance(geo_input, GEOSGeometry): @@ -139,12 +140,12 @@ class GEOSGeometry(GEOSBase, ListMixin): def __getstate__(self): # The pickled state is simply a tuple of the WKB (in string form) # and the SRID. - return str(self.wkb), self.srid + return bytes(self.wkb), self.srid def __setstate__(self, state): # Instantiating from the tuple state that was pickled. 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.') self.ptr = ptr self._post_init(srid) diff --git a/django/contrib/gis/geos/prototypes/io.py b/django/contrib/gis/geos/prototypes/io.py index 053b9948a2..4a774f9589 100644 --- a/django/contrib/gis/geos/prototypes/io.py +++ b/django/contrib/gis/geos/prototypes/io.py @@ -1,5 +1,6 @@ import threading 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.libgeos import GEOM_PTR 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): "Returns a _pointer_ to C GEOS Geometry object from the given WKB." - if isinstance(wkb, buffer): - wkb_s = str(wkb) + if isinstance(wkb, memoryview): + wkb_s = bytes(wkb) return wkb_reader_read(self.ptr, wkb_s, len(wkb_s)) elif isinstance(wkb, six.string_types): return wkb_reader_read_hex(self.ptr, wkb, len(wkb)) @@ -155,7 +156,7 @@ class WKBWriter(IOBase): def write(self, geom): "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): "Returns the HEXEWKB representation of the given geometry." diff --git a/django/contrib/gis/geos/tests/test_geos.py b/django/contrib/gis/geos/tests/test_geos.py index 7300ab9c63..2e873b4cd9 100644 --- a/django/contrib/gis/geos/tests/test_geos.py +++ b/django/contrib/gis/geos/tests/test_geos.py @@ -2,6 +2,7 @@ import ctypes import json import random +from django.contrib.gis import memoryview from django.contrib.gis.geos import (GEOSException, GEOSIndexError, GEOSGeometry, GeometryCollection, Point, MultiPoint, Polygon, MultiPolygon, LinearRing, LineString, MultiLineString, fromfile, fromstr, geos_version_info) @@ -118,9 +119,9 @@ class GEOSTest(unittest.TestCase, TestDataMixin): self.fail('Should have raised GEOSException.') # 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: - self.assertEqual(buffer(a2b_hex(hexewkb_3d)), pnt_3d.ewkb) + self.assertEqual(memoryview(a2b_hex(hexewkb_3d)), pnt_3d.ewkb) else: try: ewkb = pnt_3d.ewkb @@ -150,7 +151,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin): pass # Bad WKB - self.assertRaises(GEOSException, GEOSGeometry, buffer('0')) + self.assertRaises(GEOSException, GEOSGeometry, memoryview(b'0')) print("\nEND - expecting GEOS_ERROR; safe to ignore.\n") @@ -182,7 +183,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin): "Testing creation from WKB." from binascii import a2b_hex for g in self.geometries.hex_wkt: - wkb = buffer(a2b_hex(g.hex)) + wkb = memoryview(a2b_hex(g.hex)) geom_h = GEOSGeometry(wkb) # we need to do this so decimal places get normalised geom_t = fromstr(g.wkt) diff --git a/django/contrib/gis/geos/tests/test_io.py b/django/contrib/gis/geos/tests/test_io.py index ebf178a807..1e604e506b 100644 --- a/django/contrib/gis/geos/tests/test_io.py +++ b/django/contrib/gis/geos/tests/test_io.py @@ -1,5 +1,7 @@ import binascii import unittest + +from django.contrib.gis import memoryview from django.contrib.gis.geos import GEOSGeometry, WKTReader, WKTWriter, WKBReader, WKBWriter, geos_version_info from django.utils import six @@ -20,7 +22,7 @@ class GEOSIOTest(unittest.TestCase): # Should only accept six.string_types objects. 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): # Creating a WKTWriter instance, testing its ptr property. @@ -29,14 +31,14 @@ class GEOSIOTest(unittest.TestCase): ref = GEOSGeometry('POINT (5 23)') 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): # Creating a WKBReader instance wkb_r = WKBReader() - hex = '000000000140140000000000004037000000000000' - wkb = buffer(binascii.a2b_hex(hex)) + hex = b'000000000140140000000000004037000000000000' + wkb = memoryview(binascii.a2b_hex(hex)) ref = GEOSGeometry(hex) # 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 # the other with the byte order changed. g = GEOSGeometry('POINT (5 23)') - hex1 = '010100000000000000000014400000000000003740' - wkb1 = buffer(binascii.a2b_hex(hex1)) - hex2 = '000000000140140000000000004037000000000000' - wkb2 = buffer(binascii.a2b_hex(hex2)) + hex1 = b'010100000000000000000014400000000000003740' + wkb1 = memoryview(binascii.a2b_hex(hex1)) + hex2 = b'000000000140140000000000004037000000000000' + wkb2 = memoryview(binascii.a2b_hex(hex2)) self.assertEqual(hex1, wkb_w.write_hex(g)) self.assertEqual(wkb1, wkb_w.write(g)) @@ -81,10 +83,10 @@ class GEOSIOTest(unittest.TestCase): g = GEOSGeometry('POINT (5 23 17)') g.srid = 4326 - hex3d = '0101000080000000000000144000000000000037400000000000003140' - wkb3d = buffer(binascii.a2b_hex(hex3d)) - hex3d_srid = '01010000A0E6100000000000000000144000000000000037400000000000003140' - wkb3d_srid = buffer(binascii.a2b_hex(hex3d_srid)) + hex3d = b'0101000080000000000000144000000000000037400000000000003140' + wkb3d = memoryview(binascii.a2b_hex(hex3d)) + hex3d_srid = b'01010000A0E6100000000000000000144000000000000037400000000000003140' + wkb3d_srid = memoryview(binascii.a2b_hex(hex3d_srid)) # Ensuring bad output dimensions are not accepted for bad_outdim in (-1, 0, 1, 4, 423, 'foo', None):