Merge remote-tracking branch 'django/master' into t3011
This commit is contained in:
commit
29d1abbe35
|
@ -0,0 +1,6 @@
|
||||||
|
from django.utils import six
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
memoryview = memoryview
|
||||||
|
else:
|
||||||
|
memoryview = buffer
|
|
@ -1,6 +1,7 @@
|
||||||
"""
|
"""
|
||||||
This object provides quoting for GEOS geometries into PostgreSQL/PostGIS.
|
This object provides quoting for GEOS geometries into PostgreSQL/PostGIS.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from psycopg2 import Binary
|
from psycopg2 import Binary
|
||||||
from psycopg2.extensions import ISQLQuote
|
from psycopg2.extensions import ISQLQuote
|
||||||
|
@ -10,7 +11,7 @@ class PostGISAdapter(object):
|
||||||
"Initializes on the geometry."
|
"Initializes on the geometry."
|
||||||
# Getting the WKB (in string form, to allow easy pickling of
|
# Getting the WKB (in string form, to allow easy pickling of
|
||||||
# the adaptor) and the SRID from the geometry.
|
# the adaptor) and the SRID from the geometry.
|
||||||
self.ewkb = str(geom.ewkb)
|
self.ewkb = bytes(geom.ewkb)
|
||||||
self.srid = geom.srid
|
self.srid = geom.srid
|
||||||
self._adapter = Binary(self.ewkb)
|
self._adapter = Binary(self.ewkb)
|
||||||
|
|
||||||
|
@ -39,7 +40,7 @@ class PostGISAdapter(object):
|
||||||
def getquoted(self):
|
def getquoted(self):
|
||||||
"Returns a properly quoted string for use in PostgreSQL/PostGIS."
|
"Returns a properly quoted string for use in PostgreSQL/PostGIS."
|
||||||
# psycopg will figure out whether to use E'\\000' or '\000'
|
# psycopg will figure out whether to use E'\\000' or '\000'
|
||||||
return 'ST_GeomFromEWKB(%s)' % self._adapter.getquoted()
|
return str('ST_GeomFromEWKB(%s)' % self._adapter.getquoted().decode())
|
||||||
|
|
||||||
def prepare_database_save(self, unused):
|
def prepare_database_save(self, unused):
|
||||||
return self
|
return self
|
||||||
|
|
|
@ -160,7 +160,7 @@ class GeometryField(Field):
|
||||||
# from the given string input.
|
# from the given string input.
|
||||||
if isinstance(geom, Geometry):
|
if isinstance(geom, Geometry):
|
||||||
pass
|
pass
|
||||||
elif isinstance(geom, six.string_types) or hasattr(geom, '__geo_interface__'):
|
elif isinstance(geom, (bytes, six.string_types)) or hasattr(geom, '__geo_interface__'):
|
||||||
try:
|
try:
|
||||||
geom = Geometry(geom)
|
geom = Geometry(geom)
|
||||||
except GeometryException:
|
except GeometryException:
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
try:
|
||||||
|
from itertools import zip_longest
|
||||||
|
except ImportError:
|
||||||
|
from itertools import izip_longest as zip_longest
|
||||||
|
|
||||||
from django.utils.six.moves import zip
|
from django.utils.six.moves import zip
|
||||||
|
|
||||||
from django.db.backends.util import truncate_name, typecast_timestamp
|
from django.db.backends.util import truncate_name, typecast_timestamp
|
||||||
|
@ -190,7 +195,7 @@ class GeoSQLCompiler(compiler.SQLCompiler):
|
||||||
if self.connection.ops.oracle or getattr(self.query, 'geo_values', False):
|
if self.connection.ops.oracle or getattr(self.query, 'geo_values', False):
|
||||||
# We resolve the rest of the columns if we're on Oracle or if
|
# We resolve the rest of the columns if we're on Oracle or if
|
||||||
# the `geo_values` attribute is defined.
|
# the `geo_values` attribute is defined.
|
||||||
for value, field in map(None, row[index_start:], fields):
|
for value, field in zip_longest(row[index_start:], fields):
|
||||||
values.append(self.query.convert_values(value, field, self.connection))
|
values.append(self.query.convert_values(value, field, self.connection))
|
||||||
else:
|
else:
|
||||||
values.extend(row[index_start:])
|
values.extend(row[index_start:])
|
||||||
|
|
|
@ -41,7 +41,7 @@ try:
|
||||||
from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
|
from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
|
||||||
from django.contrib.gis.gdal.geometries import OGRGeometry
|
from django.contrib.gis.gdal.geometries import OGRGeometry
|
||||||
HAS_GDAL = True
|
HAS_GDAL = True
|
||||||
except:
|
except ImportError:
|
||||||
HAS_GDAL = False
|
HAS_GDAL = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -45,6 +45,7 @@ from django.contrib.gis.gdal.layer import Layer
|
||||||
# Getting the ctypes prototypes for the DataSource.
|
# Getting the ctypes prototypes for the DataSource.
|
||||||
from django.contrib.gis.gdal.prototypes import ds as capi
|
from django.contrib.gis.gdal.prototypes import ds as capi
|
||||||
|
|
||||||
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.six.moves import xrange
|
from django.utils.six.moves import xrange
|
||||||
|
|
||||||
|
@ -73,7 +74,7 @@ class DataSource(GDALBase):
|
||||||
ds_driver = Driver.ptr_type()
|
ds_driver = Driver.ptr_type()
|
||||||
try:
|
try:
|
||||||
# OGROpen will auto-detect the data source type.
|
# OGROpen will auto-detect the data source type.
|
||||||
ds = capi.open_ds(ds_input, self._write, byref(ds_driver))
|
ds = capi.open_ds(force_bytes(ds_input), self._write, byref(ds_driver))
|
||||||
except OGRException:
|
except OGRException:
|
||||||
# Making the error message more clear rather than something
|
# Making the error message more clear rather than something
|
||||||
# like "Invalid pointer returned from OGROpen".
|
# like "Invalid pointer returned from OGROpen".
|
||||||
|
@ -102,7 +103,7 @@ class DataSource(GDALBase):
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
"Allows use of the index [] operator to get a layer at the index."
|
"Allows use of the index [] operator to get a layer at the index."
|
||||||
if isinstance(index, six.string_types):
|
if isinstance(index, six.string_types):
|
||||||
l = capi.get_layer_by_name(self.ptr, index)
|
l = capi.get_layer_by_name(self.ptr, force_bytes(index))
|
||||||
if not l: raise OGRIndexError('invalid OGR Layer name given: "%s"' % index)
|
if not l: raise OGRIndexError('invalid OGR Layer name given: "%s"' % index)
|
||||||
elif isinstance(index, int):
|
elif isinstance(index, int):
|
||||||
if index < 0 or index >= self.layer_count:
|
if index < 0 or index >= self.layer_count:
|
||||||
|
|
|
@ -5,6 +5,7 @@ from django.contrib.gis.gdal.error import OGRException
|
||||||
from django.contrib.gis.gdal.prototypes import ds as capi
|
from django.contrib.gis.gdal.prototypes import ds as capi
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.encoding import force_bytes
|
||||||
|
|
||||||
# For more information, see the OGR C API source code:
|
# For more information, see the OGR C API source code:
|
||||||
# http://www.gdal.org/ogr/ogr__api_8h.html
|
# http://www.gdal.org/ogr/ogr__api_8h.html
|
||||||
|
@ -36,7 +37,7 @@ class Driver(GDALBase):
|
||||||
name = dr_input
|
name = dr_input
|
||||||
|
|
||||||
# Attempting to get the OGR driver by the string name.
|
# Attempting to get the OGR driver by the string name.
|
||||||
dr = capi.get_driver_by_name(name)
|
dr = capi.get_driver_by_name(force_bytes(name))
|
||||||
elif isinstance(dr_input, int):
|
elif isinstance(dr_input, int):
|
||||||
self._register()
|
self._register()
|
||||||
dr = capi.get_driver(dr_input)
|
dr = capi.get_driver(dr_input)
|
||||||
|
|
|
@ -52,7 +52,7 @@ class Envelope(object):
|
||||||
elif len(args) == 4:
|
elif len(args) == 4:
|
||||||
# Individual parameters passed in.
|
# Individual parameters passed in.
|
||||||
# Thanks to ww for the help
|
# Thanks to ww for the help
|
||||||
self._from_sequence(map(float, args))
|
self._from_sequence([float(a) for a in args])
|
||||||
else:
|
else:
|
||||||
raise OGRException('Incorrect number (%d) of arguments.' % len(args))
|
raise OGRException('Incorrect number (%d) of arguments.' % len(args))
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.contrib.gis.gdal.geometries import OGRGeometry, OGRGeomType
|
||||||
# ctypes function prototypes
|
# ctypes function prototypes
|
||||||
from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api
|
from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api
|
||||||
|
|
||||||
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.six.moves import xrange
|
from django.utils.six.moves import xrange
|
||||||
|
|
||||||
|
@ -107,6 +108,7 @@ class Feature(GDALBase):
|
||||||
|
|
||||||
def index(self, field_name):
|
def index(self, field_name):
|
||||||
"Returns the index of the given field name."
|
"Returns the index of the given field name."
|
||||||
i = capi.get_field_index(self.ptr, field_name)
|
i = capi.get_field_index(self.ptr, force_bytes(field_name))
|
||||||
if i < 0: raise OGRIndexError('invalid OFT field name given: "%s"' % field_name)
|
if i < 0:
|
||||||
|
raise OGRIndexError('invalid OFT field name given: "%s"' % field_name)
|
||||||
return i
|
return i
|
||||||
|
|
|
@ -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,16 +78,11 @@ 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().encode()))
|
||||||
str_instance = False
|
str_instance = False
|
||||||
|
|
||||||
# Constructing the geometry,
|
# Constructing the geometry,
|
||||||
if str_instance:
|
if str_instance:
|
||||||
# Checking if unicode
|
|
||||||
if isinstance(geom_input, six.text_type):
|
|
||||||
# Encoding to ASCII, WKT or HEX doesn't need any more.
|
|
||||||
geom_input = geom_input.encode('ascii')
|
|
||||||
|
|
||||||
wkt_m = wkt_regex.match(geom_input)
|
wkt_m = wkt_regex.match(geom_input)
|
||||||
json_m = json_regex.match(geom_input)
|
json_m = json_regex.match(geom_input)
|
||||||
if wkt_m:
|
if wkt_m:
|
||||||
|
@ -96,19 +93,19 @@ class OGRGeometry(GDALBase):
|
||||||
# OGR_G_CreateFromWkt doesn't work with LINEARRING WKT.
|
# OGR_G_CreateFromWkt doesn't work with LINEARRING WKT.
|
||||||
# See http://trac.osgeo.org/gdal/ticket/1992.
|
# See http://trac.osgeo.org/gdal/ticket/1992.
|
||||||
g = capi.create_geom(OGRGeomType(wkt_m.group('type')).num)
|
g = capi.create_geom(OGRGeomType(wkt_m.group('type')).num)
|
||||||
capi.import_wkt(g, byref(c_char_p(wkt_m.group('wkt'))))
|
capi.import_wkt(g, byref(c_char_p(wkt_m.group('wkt').encode())))
|
||||||
else:
|
else:
|
||||||
g = capi.from_wkt(byref(c_char_p(wkt_m.group('wkt'))), None, byref(c_void_p()))
|
g = capi.from_wkt(byref(c_char_p(wkt_m.group('wkt').encode())), None, byref(c_void_p()))
|
||||||
elif json_m:
|
elif json_m:
|
||||||
g = capi.from_json(geom_input)
|
g = capi.from_json(geom_input.encode())
|
||||||
else:
|
else:
|
||||||
# Seeing if the input is a valid short-hand string
|
# Seeing if the input is a valid short-hand string
|
||||||
# (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(bytes(geom_input), None, byref(c_void_p()), len(geom_input))
|
||||||
elif isinstance(geom_input, OGRGeomType):
|
elif isinstance(geom_input, OGRGeomType):
|
||||||
# OGRGeomType was passed in, an empty geometry will be created.
|
# OGRGeomType was passed in, an empty geometry will be created.
|
||||||
g = capi.create_geom(geom_input.num)
|
g = capi.create_geom(geom_input.num)
|
||||||
|
@ -141,7 +138,7 @@ class OGRGeometry(GDALBase):
|
||||||
srs = srs.wkt
|
srs = srs.wkt
|
||||||
else:
|
else:
|
||||||
srs = None
|
srs = None
|
||||||
return str(self.wkb), srs
|
return bytes(self.wkb), srs
|
||||||
|
|
||||||
def __setstate__(self, state):
|
def __setstate__(self, state):
|
||||||
wkb, srs = state
|
wkb, srs = state
|
||||||
|
@ -354,7 +351,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):
|
||||||
|
|
|
@ -14,6 +14,7 @@ from django.contrib.gis.gdal.srs import SpatialReference
|
||||||
# GDAL ctypes function prototypes.
|
# GDAL ctypes function prototypes.
|
||||||
from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api, srs as srs_api
|
from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api, srs as srs_api
|
||||||
|
|
||||||
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.six.moves import xrange
|
from django.utils.six.moves import xrange
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ class Layer(GDALBase):
|
||||||
self._ds = ds
|
self._ds = ds
|
||||||
self._ldefn = capi.get_layer_defn(self._ptr)
|
self._ldefn = capi.get_layer_defn(self._ptr)
|
||||||
# Does the Layer support random reading?
|
# Does the Layer support random reading?
|
||||||
self._random_read = self.test_capability('RandomRead')
|
self._random_read = self.test_capability(b'RandomRead')
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
"Gets the Feature at the specified index."
|
"Gets the Feature at the specified index."
|
||||||
|
@ -212,4 +213,4 @@ class Layer(GDALBase):
|
||||||
'FastFeatureCount', 'FastGetExtent', 'CreateField', 'Transactions',
|
'FastFeatureCount', 'FastGetExtent', 'CreateField', 'Transactions',
|
||||||
'DeleteFeature', and 'FastSetNextByIndex'.
|
'DeleteFeature', and 'FastSetNextByIndex'.
|
||||||
"""
|
"""
|
||||||
return bool(capi.test_capability(self.ptr, capability))
|
return bool(capi.test_capability(self.ptr, force_bytes(capability)))
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from ctypes import c_char_p, CDLL
|
from ctypes import c_char_p, CDLL
|
||||||
|
@ -65,7 +67,7 @@ _version_info.restype = c_char_p
|
||||||
|
|
||||||
def gdal_version():
|
def gdal_version():
|
||||||
"Returns only the GDAL version number information."
|
"Returns only the GDAL version number information."
|
||||||
return _version_info('RELEASE_NAME')
|
return _version_info(b'RELEASE_NAME')
|
||||||
|
|
||||||
def gdal_full_version():
|
def gdal_full_version():
|
||||||
"Returns the full GDAL version information."
|
"Returns the full GDAL version information."
|
||||||
|
@ -86,7 +88,7 @@ def gdal_release_date(date=False):
|
||||||
|
|
||||||
version_regex = re.compile(r'^(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<subminor>\d+))?')
|
version_regex = re.compile(r'^(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<subminor>\d+))?')
|
||||||
def gdal_version_info():
|
def gdal_version_info():
|
||||||
ver = gdal_version()
|
ver = gdal_version().decode()
|
||||||
m = version_regex.match(ver)
|
m = version_regex.match(ver)
|
||||||
if not m: raise OGRException('Could not parse GDAL version string "%s"' % ver)
|
if not m: raise OGRException('Could not parse GDAL version string "%s"' % ver)
|
||||||
return dict([(key, m.group(key)) for key in ('major', 'minor', 'subminor')])
|
return dict([(key, m.group(key)) for key in ('major', 'minor', 'subminor')])
|
||||||
|
|
|
@ -30,9 +30,10 @@ def check_const_string(result, func, cargs, offset=None):
|
||||||
if offset:
|
if offset:
|
||||||
check_err(result)
|
check_err(result)
|
||||||
ptr = ptr_byref(cargs, offset)
|
ptr = ptr_byref(cargs, offset)
|
||||||
return ptr.value
|
return ptr.value.decode()
|
||||||
else:
|
else:
|
||||||
return result
|
if result is not None:
|
||||||
|
return result.decode()
|
||||||
|
|
||||||
def check_string(result, func, cargs, offset=-1, str_result=False):
|
def check_string(result, func, cargs, offset=-1, str_result=False):
|
||||||
"""
|
"""
|
||||||
|
@ -47,13 +48,13 @@ def check_string(result, func, cargs, offset=-1, str_result=False):
|
||||||
# For routines that return a string.
|
# For routines that return a string.
|
||||||
ptr = result
|
ptr = result
|
||||||
if not ptr: s = None
|
if not ptr: s = None
|
||||||
else: s = string_at(result)
|
else: s = string_at(result).decode()
|
||||||
else:
|
else:
|
||||||
# Error-code return specified.
|
# Error-code return specified.
|
||||||
check_err(result)
|
check_err(result)
|
||||||
ptr = ptr_byref(cargs, offset)
|
ptr = ptr_byref(cargs, offset)
|
||||||
# Getting the string value
|
# Getting the string value
|
||||||
s = ptr.value
|
s = ptr.value.decode()
|
||||||
# Correctly freeing the allocated memory beind GDAL pointer
|
# Correctly freeing the allocated memory beind GDAL pointer
|
||||||
# w/the VSIFree routine.
|
# w/the VSIFree routine.
|
||||||
if ptr: lgdal.VSIFree(ptr)
|
if ptr: lgdal.VSIFree(ptr)
|
||||||
|
@ -125,4 +126,4 @@ def check_str_arg(result, func, cargs):
|
||||||
"""
|
"""
|
||||||
dbl = result
|
dbl = result
|
||||||
ptr = cargs[-1]._obj
|
ptr = cargs[-1]._obj
|
||||||
return dbl, ptr.value
|
return dbl, ptr.value.decode()
|
||||||
|
|
|
@ -34,6 +34,8 @@ from django.contrib.gis.gdal.error import SRSException
|
||||||
from django.contrib.gis.gdal.prototypes import srs as capi
|
from django.contrib.gis.gdal.prototypes import srs as capi
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.encoding import force_bytes, force_text
|
||||||
|
|
||||||
|
|
||||||
#### Spatial Reference class. ####
|
#### Spatial Reference class. ####
|
||||||
class SpatialReference(GDALBase):
|
class SpatialReference(GDALBase):
|
||||||
|
@ -51,7 +53,6 @@ class SpatialReference(GDALBase):
|
||||||
EPSG code, a PROJ.4 string, and/or a projection "well known" shorthand
|
EPSG code, a PROJ.4 string, and/or a projection "well known" shorthand
|
||||||
string (one of 'WGS84', 'WGS72', 'NAD27', 'NAD83').
|
string (one of 'WGS84', 'WGS72', 'NAD27', 'NAD83').
|
||||||
"""
|
"""
|
||||||
buf = c_char_p('')
|
|
||||||
srs_type = 'user'
|
srs_type = 'user'
|
||||||
|
|
||||||
if isinstance(srs_input, six.string_types):
|
if isinstance(srs_input, six.string_types):
|
||||||
|
@ -79,6 +80,7 @@ class SpatialReference(GDALBase):
|
||||||
srs = srs_input
|
srs = srs_input
|
||||||
else:
|
else:
|
||||||
# Creating a new SRS pointer, using the string buffer.
|
# Creating a new SRS pointer, using the string buffer.
|
||||||
|
buf = c_char_p(b'')
|
||||||
srs = capi.new_srs(buf)
|
srs = capi.new_srs(buf)
|
||||||
|
|
||||||
# If the pointer is NULL, throw an exception.
|
# If the pointer is NULL, throw an exception.
|
||||||
|
@ -137,15 +139,16 @@ class SpatialReference(GDALBase):
|
||||||
"""
|
"""
|
||||||
if not isinstance(target, six.string_types) or not isinstance(index, int):
|
if not isinstance(target, six.string_types) or not isinstance(index, int):
|
||||||
raise TypeError
|
raise TypeError
|
||||||
return capi.get_attr_value(self.ptr, target, index)
|
value = capi.get_attr_value(self.ptr, force_bytes(target), index)
|
||||||
|
return force_text(value, 'ascii', strings_only=True)
|
||||||
|
|
||||||
def auth_name(self, target):
|
def auth_name(self, target):
|
||||||
"Returns the authority name for the given string target node."
|
"Returns the authority name for the given string target node."
|
||||||
return capi.get_auth_name(self.ptr, target)
|
return capi.get_auth_name(self.ptr, force_bytes(target))
|
||||||
|
|
||||||
def auth_code(self, target):
|
def auth_code(self, target):
|
||||||
"Returns the authority code for the given string target node."
|
"Returns the authority code for the given string target node."
|
||||||
return capi.get_auth_code(self.ptr, target)
|
return capi.get_auth_code(self.ptr, force_bytes(target))
|
||||||
|
|
||||||
def clone(self):
|
def clone(self):
|
||||||
"Returns a clone of this SpatialReference object."
|
"Returns a clone of this SpatialReference object."
|
||||||
|
@ -219,12 +222,14 @@ class SpatialReference(GDALBase):
|
||||||
and will automatically determines whether to return the linear
|
and will automatically determines whether to return the linear
|
||||||
or angular units.
|
or angular units.
|
||||||
"""
|
"""
|
||||||
|
units, name = None, None
|
||||||
if self.projected or self.local:
|
if self.projected or self.local:
|
||||||
return capi.linear_units(self.ptr, byref(c_char_p()))
|
units, name = capi.linear_units(self.ptr, byref(c_char_p()))
|
||||||
elif self.geographic:
|
elif self.geographic:
|
||||||
return capi.angular_units(self.ptr, byref(c_char_p()))
|
units, name = capi.angular_units(self.ptr, byref(c_char_p()))
|
||||||
else:
|
if name is not None:
|
||||||
return (None, None)
|
name.decode()
|
||||||
|
return (units, name)
|
||||||
|
|
||||||
#### Spheroid/Ellipsoid Properties ####
|
#### Spheroid/Ellipsoid Properties ####
|
||||||
@property
|
@property
|
||||||
|
@ -283,7 +288,7 @@ class SpatialReference(GDALBase):
|
||||||
|
|
||||||
def import_user_input(self, user_input):
|
def import_user_input(self, user_input):
|
||||||
"Imports the Spatial Reference from the given user input string."
|
"Imports the Spatial Reference from the given user input string."
|
||||||
capi.from_user_input(self.ptr, user_input)
|
capi.from_user_input(self.ptr, force_bytes(user_input))
|
||||||
|
|
||||||
def import_wkt(self, wkt):
|
def import_wkt(self, wkt):
|
||||||
"Imports the Spatial Reference from OGC WKT (string)"
|
"Imports the Spatial Reference from OGC WKT (string)"
|
||||||
|
|
|
@ -9,7 +9,7 @@ ds_list = (TestDS('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='
|
||||||
fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
|
fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
|
||||||
extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS
|
extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS
|
||||||
srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]',
|
srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]',
|
||||||
field_values={'dbl' : [float(i) for i in range(1, 6)], 'int' : range(1, 6), 'str' : [str(i) for i in range(1, 6)]},
|
field_values={'dbl' : [float(i) for i in range(1, 6)], 'int' : list(range(1, 6)), 'str' : [str(i) for i in range(1, 6)]},
|
||||||
fids=range(5)),
|
fids=range(5)),
|
||||||
TestDS('test_vrt', ext='vrt', nfeat=3, nfld=3, geom='POINT', gtype='Point25D', driver='VRT',
|
TestDS('test_vrt', ext='vrt', nfeat=3, nfld=3, geom='POINT', gtype='Point25D', driver='VRT',
|
||||||
fields={'POINT_X' : OFTString, 'POINT_Y' : OFTString, 'NUM' : OFTString}, # VRT uses CSV, which all types are OFTString.
|
fields={'POINT_X' : OFTString, 'POINT_Y' : OFTString, 'NUM' : OFTString}, # VRT uses CSV, which all types are OFTString.
|
||||||
|
@ -200,7 +200,7 @@ class DataSourceTest(unittest.TestCase):
|
||||||
|
|
||||||
# Setting the spatial filter with a tuple/list with the extent of
|
# Setting the spatial filter with a tuple/list with the extent of
|
||||||
# a buffer centering around Pueblo.
|
# a buffer centering around Pueblo.
|
||||||
self.assertRaises(ValueError, lyr._set_spatial_filter, range(5))
|
self.assertRaises(ValueError, lyr._set_spatial_filter, list(range(5)))
|
||||||
filter_extent = (-105.609252, 37.255001, -103.609252, 39.255001)
|
filter_extent = (-105.609252, 37.255001, -103.609252, 39.255001)
|
||||||
lyr.spatial_filter = (-105.609252, 37.255001, -103.609252, 39.255001)
|
lyr.spatial_filter = (-105.609252, 37.255001, -103.609252, 39.255001)
|
||||||
self.assertEqual(OGRGeometry.from_bbox(filter_extent), lyr.spatial_filter)
|
self.assertEqual(OGRGeometry.from_bbox(filter_extent), lyr.spatial_filter)
|
||||||
|
|
|
@ -92,7 +92,7 @@ class OGRGeomTest(unittest.TestCase, TestDataMixin):
|
||||||
"Testing HEX input/output."
|
"Testing HEX input/output."
|
||||||
for g in self.geometries.hex_wkt:
|
for g in self.geometries.hex_wkt:
|
||||||
geom1 = OGRGeometry(g.wkt)
|
geom1 = OGRGeometry(g.wkt)
|
||||||
self.assertEqual(g.hex, geom1.hex)
|
self.assertEqual(g.hex.encode(), geom1.hex)
|
||||||
# Constructing w/HEX
|
# Constructing w/HEX
|
||||||
geom2 = OGRGeometry(g.hex)
|
geom2 = OGRGeometry(g.hex)
|
||||||
self.assertEqual(geom1, geom2)
|
self.assertEqual(geom1, geom2)
|
||||||
|
@ -102,7 +102,7 @@ class OGRGeomTest(unittest.TestCase, TestDataMixin):
|
||||||
for g in self.geometries.hex_wkt:
|
for g in self.geometries.hex_wkt:
|
||||||
geom1 = OGRGeometry(g.wkt)
|
geom1 = OGRGeometry(g.wkt)
|
||||||
wkb = geom1.wkb
|
wkb = geom1.wkb
|
||||||
self.assertEqual(b2a_hex(wkb).upper(), g.hex)
|
self.assertEqual(b2a_hex(wkb).upper(), g.hex.encode())
|
||||||
# Constructing w/WKB.
|
# Constructing w/WKB.
|
||||||
geom2 = OGRGeometry(wkb)
|
geom2 = OGRGeometry(wkb)
|
||||||
self.assertEqual(geom1, geom2)
|
self.assertEqual(geom1, geom2)
|
||||||
|
|
|
@ -101,6 +101,6 @@ class TestDataMixin(object):
|
||||||
if GEOMETRIES is None:
|
if GEOMETRIES is None:
|
||||||
# Load up the test geometry data from fixture into global.
|
# Load up the test geometry data from fixture into global.
|
||||||
gzf = gzip.GzipFile(os.path.join(TEST_DATA, 'geometries.json.gz'))
|
gzf = gzip.GzipFile(os.path.join(TEST_DATA, 'geometries.json.gz'))
|
||||||
geometries = json.loads(gzf.read())
|
geometries = json.loads(gzf.read().decode())
|
||||||
GEOMETRIES = TestGeomSet(**strconvert(geometries))
|
GEOMETRIES = TestGeomSet(**strconvert(geometries))
|
||||||
return GEOMETRIES
|
return GEOMETRIES
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
def fromfile(file_h):
|
def fromfile(file_h):
|
||||||
"""
|
"""
|
||||||
Given a string file name, returns a GEOSGeometry. The file may contain WKB,
|
Given a string file name, returns a GEOSGeometry. The file may contain WKB,
|
||||||
|
@ -14,11 +16,19 @@ def fromfile(file_h):
|
||||||
else:
|
else:
|
||||||
buf = file_h.read()
|
buf = file_h.read()
|
||||||
|
|
||||||
# If we get WKB need to wrap in buffer(), so run through regexes.
|
# If we get WKB need to wrap in memoryview(), so run through regexes.
|
||||||
if wkt_regex.match(buf) or hex_regex.match(buf):
|
if isinstance(buf, bytes):
|
||||||
return GEOSGeometry(buf)
|
try:
|
||||||
|
decoded = buf.decode()
|
||||||
|
if wkt_regex.match(decoded) or hex_regex.match(decoded):
|
||||||
|
return GEOSGeometry(decoded)
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
return GEOSGeometry(buffer(buf))
|
return GEOSGeometry(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."
|
||||||
|
|
|
@ -2,9 +2,12 @@
|
||||||
This module contains the 'base' GEOSGeometry object -- all GEOS Geometries
|
This module contains the 'base' GEOSGeometry object -- all GEOS Geometries
|
||||||
inherit from this object.
|
inherit from this object.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
|
@ -28,6 +31,8 @@ from django.contrib.gis.geos.prototypes.io import wkt_r, wkt_w, wkb_r, wkb_w, ew
|
||||||
from django.contrib.gis.geometry.regex import hex_regex, wkt_regex, json_regex
|
from django.contrib.gis.geometry.regex import hex_regex, wkt_regex, json_regex
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.encoding import force_bytes, force_text
|
||||||
|
|
||||||
|
|
||||||
class GEOSGeometry(GEOSBase, ListMixin):
|
class GEOSGeometry(GEOSBase, ListMixin):
|
||||||
"A class that, generally, encapsulates a GEOS geometry."
|
"A class that, generally, encapsulates a GEOS geometry."
|
||||||
|
@ -54,19 +59,17 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||||
The `srid` keyword is used to specify the Source Reference Identifier
|
The `srid` keyword is used to specify the Source Reference Identifier
|
||||||
(SRID) number for this Geometry. If not set, the SRID will be None.
|
(SRID) number for this Geometry. If not set, the SRID will be None.
|
||||||
"""
|
"""
|
||||||
|
if isinstance(geo_input, bytes):
|
||||||
|
geo_input = force_text(geo_input)
|
||||||
if isinstance(geo_input, six.string_types):
|
if isinstance(geo_input, six.string_types):
|
||||||
if isinstance(geo_input, six.text_type):
|
|
||||||
# Encoding to ASCII, WKT or HEXEWKB doesn't need any more.
|
|
||||||
geo_input = geo_input.encode('ascii')
|
|
||||||
|
|
||||||
wkt_m = wkt_regex.match(geo_input)
|
wkt_m = wkt_regex.match(geo_input)
|
||||||
if wkt_m:
|
if wkt_m:
|
||||||
# Handling WKT input.
|
# Handling WKT input.
|
||||||
if wkt_m.group('srid'): srid = int(wkt_m.group('srid'))
|
if wkt_m.group('srid'): srid = int(wkt_m.group('srid'))
|
||||||
g = wkt_r().read(wkt_m.group('wkt'))
|
g = wkt_r().read(force_bytes(wkt_m.group('wkt')))
|
||||||
elif hex_regex.match(geo_input):
|
elif hex_regex.match(geo_input):
|
||||||
# Handling HEXEWKB input.
|
# Handling HEXEWKB input.
|
||||||
g = wkb_r().read(geo_input)
|
g = wkb_r().read(force_bytes(geo_input))
|
||||||
elif gdal.HAS_GDAL and json_regex.match(geo_input):
|
elif gdal.HAS_GDAL and json_regex.match(geo_input):
|
||||||
# Handling GeoJSON input.
|
# Handling GeoJSON input.
|
||||||
g = wkb_r().read(gdal.OGRGeometry(geo_input).wkb)
|
g = wkb_r().read(gdal.OGRGeometry(geo_input).wkb)
|
||||||
|
@ -75,7 +78,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 +142,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)
|
||||||
|
@ -216,7 +219,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||||
@property
|
@property
|
||||||
def geom_type(self):
|
def geom_type(self):
|
||||||
"Returns a string representing the Geometry type, e.g. 'Polygon'"
|
"Returns a string representing the Geometry type, e.g. 'Polygon'"
|
||||||
return capi.geos_type(self.ptr)
|
return capi.geos_type(self.ptr).decode()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def geom_typeid(self):
|
def geom_typeid(self):
|
||||||
|
@ -283,7 +286,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||||
"""
|
"""
|
||||||
if not GEOS_PREPARE:
|
if not GEOS_PREPARE:
|
||||||
raise GEOSException('Upgrade GEOS to 3.1 to get validity reason.')
|
raise GEOSException('Upgrade GEOS to 3.1 to get validity reason.')
|
||||||
return capi.geos_isvalidreason(self.ptr)
|
return capi.geos_isvalidreason(self.ptr).decode()
|
||||||
|
|
||||||
#### Binary predicates. ####
|
#### Binary predicates. ####
|
||||||
def contains(self, other):
|
def contains(self, other):
|
||||||
|
@ -337,7 +340,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||||
"""
|
"""
|
||||||
if not isinstance(pattern, six.string_types) or len(pattern) > 9:
|
if not isinstance(pattern, six.string_types) or len(pattern) > 9:
|
||||||
raise GEOSException('invalid intersection matrix pattern')
|
raise GEOSException('invalid intersection matrix pattern')
|
||||||
return capi.geos_relatepattern(self.ptr, other.ptr, pattern)
|
return capi.geos_relatepattern(self.ptr, other.ptr, force_bytes(pattern))
|
||||||
|
|
||||||
def touches(self, other):
|
def touches(self, other):
|
||||||
"""
|
"""
|
||||||
|
@ -379,7 +382,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||||
@property
|
@property
|
||||||
def wkt(self):
|
def wkt(self):
|
||||||
"Returns the WKT (Well-Known Text) representation of this Geometry."
|
"Returns the WKT (Well-Known Text) representation of this Geometry."
|
||||||
return wkt_w().write(self)
|
return wkt_w().write(self).decode()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hex(self):
|
def hex(self):
|
||||||
|
@ -589,7 +592,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
||||||
|
|
||||||
def relate(self, other):
|
def relate(self, other):
|
||||||
"Returns the DE-9IM intersection matrix for this Geometry and the other."
|
"Returns the DE-9IM intersection matrix for this Geometry and the other."
|
||||||
return capi.geos_relate(self.ptr, other.ptr)
|
return capi.geos_relate(self.ptr, other.ptr).decode()
|
||||||
|
|
||||||
def simplify(self, tolerance=0.0, preserve_topology=False):
|
def simplify(self, tolerance=0.0, preserve_topology=False):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -57,6 +57,7 @@ lgeos = CDLL(lib_path)
|
||||||
# typedef void (*GEOSMessageHandler)(const char *fmt, ...);
|
# typedef void (*GEOSMessageHandler)(const char *fmt, ...);
|
||||||
NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
|
NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
|
||||||
def notice_h(fmt, lst, output_h=sys.stdout):
|
def notice_h(fmt, lst, output_h=sys.stdout):
|
||||||
|
fmt, lst = fmt.decode(), lst.decode()
|
||||||
try:
|
try:
|
||||||
warn_msg = fmt % lst
|
warn_msg = fmt % lst
|
||||||
except:
|
except:
|
||||||
|
@ -66,6 +67,7 @@ notice_h = NOTICEFUNC(notice_h)
|
||||||
|
|
||||||
ERRORFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
|
ERRORFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
|
||||||
def error_h(fmt, lst, output_h=sys.stderr):
|
def error_h(fmt, lst, output_h=sys.stderr):
|
||||||
|
fmt, lst = fmt.decode(), lst.decode()
|
||||||
try:
|
try:
|
||||||
err_msg = fmt % lst
|
err_msg = fmt % lst
|
||||||
except:
|
except:
|
||||||
|
|
|
@ -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
|
||||||
|
@ -7,6 +8,7 @@ from django.contrib.gis.geos.prototypes.geom import c_uchar_p, geos_char_p
|
||||||
from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
|
from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.encoding import force_bytes
|
||||||
|
|
||||||
### The WKB/WKT Reader/Writer structures and pointers ###
|
### The WKB/WKT Reader/Writer structures and pointers ###
|
||||||
class WKTReader_st(Structure): pass
|
class WKTReader_st(Structure): pass
|
||||||
|
@ -120,8 +122,9 @@ class _WKTReader(IOBase):
|
||||||
ptr_type = WKT_READ_PTR
|
ptr_type = WKT_READ_PTR
|
||||||
|
|
||||||
def read(self, wkt):
|
def read(self, wkt):
|
||||||
if not isinstance(wkt, six.string_types): raise TypeError
|
if not isinstance(wkt, (bytes, six.string_types)):
|
||||||
return wkt_reader_read(self.ptr, wkt)
|
raise TypeError
|
||||||
|
return wkt_reader_read(self.ptr, force_bytes(wkt))
|
||||||
|
|
||||||
class _WKBReader(IOBase):
|
class _WKBReader(IOBase):
|
||||||
_constructor = wkb_reader_create
|
_constructor = wkb_reader_create
|
||||||
|
@ -130,10 +133,10 @@ 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, (bytes, six.string_types)):
|
||||||
return wkb_reader_read_hex(self.ptr, wkb, len(wkb))
|
return wkb_reader_read_hex(self.ptr, wkb, len(wkb))
|
||||||
else:
|
else:
|
||||||
raise TypeError
|
raise TypeError
|
||||||
|
@ -155,7 +158,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."
|
||||||
|
@ -188,8 +191,8 @@ class WKBWriter(IOBase):
|
||||||
return bool(ord(wkb_writer_get_include_srid(self.ptr)))
|
return bool(ord(wkb_writer_get_include_srid(self.ptr)))
|
||||||
|
|
||||||
def _set_include_srid(self, include):
|
def _set_include_srid(self, include):
|
||||||
if bool(include): flag = chr(1)
|
if bool(include): flag = b'\x01'
|
||||||
else: flag = chr(0)
|
else: flag = b'\x00'
|
||||||
wkb_writer_set_include_srid(self.ptr, flag)
|
wkb_writer_set_include_srid(self.ptr, flag)
|
||||||
|
|
||||||
srid = property(_get_include_srid, _set_include_srid)
|
srid = property(_get_include_srid, _set_include_srid)
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import ctypes
|
import ctypes
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
|
from binascii import a2b_hex, b2a_hex
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
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)
|
||||||
|
@ -9,6 +14,7 @@ from django.contrib.gis.geos.base import gdal, numpy, GEOSBase
|
||||||
from django.contrib.gis.geos.libgeos import GEOS_PREPARE
|
from django.contrib.gis.geos.libgeos import GEOS_PREPARE
|
||||||
from django.contrib.gis.geometry.test_data import TestDataMixin
|
from django.contrib.gis.geometry.test_data import TestDataMixin
|
||||||
|
|
||||||
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.six.moves import xrange
|
from django.utils.six.moves import xrange
|
||||||
from django.utils import unittest
|
from django.utils import unittest
|
||||||
|
@ -64,7 +70,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
||||||
# result in a TypeError when trying to assign it to the `ptr` property.
|
# result in a TypeError when trying to assign it to the `ptr` property.
|
||||||
# Thus, memmory addresses (integers) and pointers of the incorrect type
|
# Thus, memmory addresses (integers) and pointers of the incorrect type
|
||||||
# (in `bad_ptrs`) will not be allowed.
|
# (in `bad_ptrs`) will not be allowed.
|
||||||
bad_ptrs = (5, ctypes.c_char_p('foobar'))
|
bad_ptrs = (5, ctypes.c_char_p(b'foobar'))
|
||||||
for bad_ptr in bad_ptrs:
|
for bad_ptr in bad_ptrs:
|
||||||
# Equivalent to `fg.ptr = bad_ptr`
|
# Equivalent to `fg.ptr = bad_ptr`
|
||||||
self.assertRaises(TypeError, fg1._set_ptr, bad_ptr)
|
self.assertRaises(TypeError, fg1._set_ptr, bad_ptr)
|
||||||
|
@ -80,18 +86,16 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
||||||
"Testing HEX output."
|
"Testing HEX output."
|
||||||
for g in self.geometries.hex_wkt:
|
for g in self.geometries.hex_wkt:
|
||||||
geom = fromstr(g.wkt)
|
geom = fromstr(g.wkt)
|
||||||
self.assertEqual(g.hex, geom.hex)
|
self.assertEqual(g.hex, geom.hex.decode())
|
||||||
|
|
||||||
def test_hexewkb(self):
|
def test_hexewkb(self):
|
||||||
"Testing (HEX)EWKB output."
|
"Testing (HEX)EWKB output."
|
||||||
from binascii import a2b_hex
|
|
||||||
|
|
||||||
# For testing HEX(EWKB).
|
# For testing HEX(EWKB).
|
||||||
ogc_hex = '01010000000000000000000000000000000000F03F'
|
ogc_hex = b'01010000000000000000000000000000000000F03F'
|
||||||
# `SELECT ST_AsHEXEWKB(ST_GeomFromText('POINT(0 1)', 4326));`
|
# `SELECT ST_AsHEXEWKB(ST_GeomFromText('POINT(0 1)', 4326));`
|
||||||
hexewkb_2d = '0101000020E61000000000000000000000000000000000F03F'
|
hexewkb_2d = b'0101000020E61000000000000000000000000000000000F03F'
|
||||||
# `SELECT ST_AsHEXEWKB(ST_GeomFromEWKT('SRID=4326;POINT(0 1 2)'));`
|
# `SELECT ST_AsHEXEWKB(ST_GeomFromEWKT('SRID=4326;POINT(0 1 2)'));`
|
||||||
hexewkb_3d = '01010000A0E61000000000000000000000000000000000F03F0000000000000040'
|
hexewkb_3d = b'01010000A0E61000000000000000000000000000000000F03F0000000000000040'
|
||||||
|
|
||||||
pnt_2d = Point(0, 1, srid=4326)
|
pnt_2d = Point(0, 1, srid=4326)
|
||||||
pnt_3d = Point(0, 1, 2, srid=4326)
|
pnt_3d = Point(0, 1, 2, srid=4326)
|
||||||
|
@ -118,9 +122,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 +154,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")
|
||||||
|
|
||||||
|
@ -164,11 +168,10 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
||||||
|
|
||||||
def test_wkb(self):
|
def test_wkb(self):
|
||||||
"Testing WKB output."
|
"Testing WKB output."
|
||||||
from binascii import b2a_hex
|
|
||||||
for g in self.geometries.hex_wkt:
|
for g in self.geometries.hex_wkt:
|
||||||
geom = fromstr(g.wkt)
|
geom = fromstr(g.wkt)
|
||||||
wkb = geom.wkb
|
wkb = geom.wkb
|
||||||
self.assertEqual(b2a_hex(wkb).upper(), g.hex)
|
self.assertEqual(b2a_hex(wkb).decode().upper(), g.hex)
|
||||||
|
|
||||||
def test_create_hex(self):
|
def test_create_hex(self):
|
||||||
"Testing creation from HEX."
|
"Testing creation from HEX."
|
||||||
|
@ -180,9 +183,8 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
||||||
|
|
||||||
def test_create_wkb(self):
|
def test_create_wkb(self):
|
||||||
"Testing creation from WKB."
|
"Testing creation from WKB."
|
||||||
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.encode()))
|
||||||
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)
|
||||||
|
@ -212,13 +214,12 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
||||||
|
|
||||||
def test_fromfile(self):
|
def test_fromfile(self):
|
||||||
"Testing the fromfile() factory."
|
"Testing the fromfile() factory."
|
||||||
from io import BytesIO
|
|
||||||
ref_pnt = GEOSGeometry('POINT(5 23)')
|
ref_pnt = GEOSGeometry('POINT(5 23)')
|
||||||
|
|
||||||
wkt_f = BytesIO()
|
wkt_f = BytesIO()
|
||||||
wkt_f.write(ref_pnt.wkt)
|
wkt_f.write(force_bytes(ref_pnt.wkt))
|
||||||
wkb_f = BytesIO()
|
wkb_f = BytesIO()
|
||||||
wkb_f.write(str(ref_pnt.wkb))
|
wkb_f.write(bytes(ref_pnt.wkb))
|
||||||
|
|
||||||
# Other tests use `fromfile()` on string filenames so those
|
# Other tests use `fromfile()` on string filenames so those
|
||||||
# aren't tested here.
|
# aren't tested here.
|
||||||
|
@ -439,8 +440,8 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
||||||
self.assertEqual(r.geom_typeid, 2)
|
self.assertEqual(r.geom_typeid, 2)
|
||||||
|
|
||||||
# Testing polygon construction.
|
# Testing polygon construction.
|
||||||
self.assertRaises(TypeError, Polygon.__init__, 0, [1, 2, 3])
|
self.assertRaises(TypeError, Polygon, 0, [1, 2, 3])
|
||||||
self.assertRaises(TypeError, Polygon.__init__, 'foo')
|
self.assertRaises(TypeError, Polygon, 'foo')
|
||||||
|
|
||||||
# Polygon(shell, (hole1, ... holeN))
|
# Polygon(shell, (hole1, ... holeN))
|
||||||
rings = tuple(r for r in poly)
|
rings = tuple(r for r in poly)
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
class GEOSIOTest(unittest.TestCase):
|
class GEOSIOTest(unittest.TestCase):
|
||||||
|
|
||||||
def test01_wktreader(self):
|
def test01_wktreader(self):
|
||||||
|
@ -12,15 +17,15 @@ class GEOSIOTest(unittest.TestCase):
|
||||||
|
|
||||||
# read() should return a GEOSGeometry
|
# read() should return a GEOSGeometry
|
||||||
ref = GEOSGeometry(wkt)
|
ref = GEOSGeometry(wkt)
|
||||||
g1 = wkt_r.read(wkt)
|
g1 = wkt_r.read(wkt.encode())
|
||||||
g2 = wkt_r.read(six.text_type(wkt))
|
g2 = wkt_r.read(wkt)
|
||||||
|
|
||||||
for geom in (g1, g2):
|
for geom in (g1, g2):
|
||||||
self.assertEqual(ref, geom)
|
self.assertEqual(ref, geom)
|
||||||
|
|
||||||
# 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 +34,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 +61,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 +86,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):
|
||||||
|
@ -100,7 +105,7 @@ class GEOSIOTest(unittest.TestCase):
|
||||||
self.assertEqual(hex3d, wkb_w.write_hex(g))
|
self.assertEqual(hex3d, wkb_w.write_hex(g))
|
||||||
self.assertEqual(wkb3d, wkb_w.write(g))
|
self.assertEqual(wkb3d, wkb_w.write(g))
|
||||||
|
|
||||||
# Telling the WKBWriter to inlcude the srid in the representation.
|
# Telling the WKBWriter to include the srid in the representation.
|
||||||
wkb_w.srid = True
|
wkb_w.srid = True
|
||||||
self.assertEqual(hex3d_srid, wkb_w.write_hex(g))
|
self.assertEqual(hex3d_srid, wkb_w.write_hex(g))
|
||||||
self.assertEqual(wkb3d_srid, wkb_w.write(g))
|
self.assertEqual(wkb3d_srid, wkb_w.write(g))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from __future__ import absolute_import
|
# -*- encoding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ class GeoRegressionTests(TestCase):
|
||||||
|
|
||||||
def test_kmz(self):
|
def test_kmz(self):
|
||||||
"Testing `render_to_kmz` with non-ASCII data. See #11624."
|
"Testing `render_to_kmz` with non-ASCII data. See #11624."
|
||||||
name = '\xc3\x85land Islands'.decode('iso-8859-1')
|
name = "Åland Islands"
|
||||||
places = [{'name' : name,
|
places = [{'name' : name,
|
||||||
'description' : name,
|
'description' : name,
|
||||||
'kml' : '<Point><coordinates>5.0,23.0</coordinates></Point>'
|
'kml' : '<Point><coordinates>5.0,23.0</coordinates></Point>'
|
||||||
|
|
|
@ -341,7 +341,7 @@ class BaseTemporalField(Field):
|
||||||
for format in self.input_formats:
|
for format in self.input_formats:
|
||||||
try:
|
try:
|
||||||
return self.strptime(value, format)
|
return self.strptime(value, format)
|
||||||
except ValueError:
|
except (ValueError, TypeError):
|
||||||
continue
|
continue
|
||||||
raise ValidationError(self.error_messages['invalid'])
|
raise ValidationError(self.error_messages['invalid'])
|
||||||
|
|
||||||
|
|
|
@ -555,7 +555,7 @@ with the :ttag:`url` template tag:
|
||||||
|
|
||||||
If ``{% url 'polls.views.detail' poll.id %}`` (with quotes) doesn't work,
|
If ``{% url 'polls.views.detail' poll.id %}`` (with quotes) doesn't work,
|
||||||
but ``{% url polls.views.detail poll.id %}`` (without quotes) does, that
|
but ``{% url polls.views.detail poll.id %}`` (without quotes) does, that
|
||||||
means you're using a version of Django ≤ 1.4. In this case, add the
|
means you're using a version of Django < 1.5. In this case, add the
|
||||||
following declaration at the top of your template:
|
following declaration at the top of your template:
|
||||||
|
|
||||||
.. code-block:: html+django
|
.. code-block:: html+django
|
||||||
|
|
|
@ -356,6 +356,11 @@ class FieldsTests(SimpleTestCase):
|
||||||
self.assertEqual(datetime.date(2006, 10, 25), f.clean(' 25 October 2006 '))
|
self.assertEqual(datetime.date(2006, 10, 25), f.clean(' 25 October 2006 '))
|
||||||
self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, ' ')
|
self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, ' ')
|
||||||
|
|
||||||
|
def test_datefield_5(self):
|
||||||
|
# Test null bytes (#18982)
|
||||||
|
f = DateField()
|
||||||
|
self.assertRaisesMessage(ValidationError, "'Enter a valid date.'", f.clean, 'a\x00b')
|
||||||
|
|
||||||
# TimeField ###################################################################
|
# TimeField ###################################################################
|
||||||
|
|
||||||
def test_timefield_1(self):
|
def test_timefield_1(self):
|
||||||
|
|
Loading…
Reference in New Issue