Fixed #28160 -- Prevented hiding GDAL exceptions when it's not installed.

This commit is contained in:
Tim Graham 2017-05-02 21:27:11 -04:00
parent 890537253c
commit 2dc3280254
18 changed files with 118 additions and 182 deletions

View File

@ -25,32 +25,23 @@
by setting `GDAL_LIBRARY_PATH` in your settings with the path to the GDAL C by setting `GDAL_LIBRARY_PATH` in your settings with the path to the GDAL C
library on your system. library on your system.
""" """
from django.contrib.gis.gdal.datasource import DataSource
from django.contrib.gis.gdal.driver import Driver
from django.contrib.gis.gdal.envelope import Envelope from django.contrib.gis.gdal.envelope import Envelope
from django.contrib.gis.gdal.error import ( # NOQA from django.contrib.gis.gdal.error import (
GDALException, OGRException, OGRIndexError, SRSException, check_err, GDALException, OGRException, OGRIndexError, SRSException, check_err,
) )
from django.contrib.gis.gdal.geomtype import OGRGeomType # NOQA from django.contrib.gis.gdal.geometries import OGRGeometry
from django.contrib.gis.gdal.geomtype import OGRGeomType
from django.contrib.gis.gdal.libgdal import (
GDAL_VERSION, gdal_full_version, gdal_version,
)
from django.contrib.gis.gdal.raster.source import GDALRaster
from django.contrib.gis.gdal.srs import CoordTransform, SpatialReference
__all__ = [ __all__ = (
'check_err', 'Envelope', 'GDALException', 'OGRException', 'OGRIndexError', 'Driver', 'DataSource', 'CoordTransform', 'Envelope', 'GDALException',
'SRSException', 'OGRGeomType', 'HAS_GDAL', 'GDALRaster', 'GDAL_VERSION', 'OGRException', 'OGRGeometry', 'OGRGeomType',
] 'OGRIndexError', 'SpatialReference', 'SRSException',
'check_err', 'gdal_version', 'gdal_full_version',
# Attempting to import objects that depend on the GDAL library. The )
# HAS_GDAL flag will be set to True if the library is present on
# the system.
try:
from django.contrib.gis.gdal.driver import Driver # NOQA
from django.contrib.gis.gdal.datasource import DataSource # NOQA
from django.contrib.gis.gdal.libgdal import gdal_version, gdal_full_version, GDAL_VERSION # NOQA
from django.contrib.gis.gdal.raster.source import GDALRaster # NOQA
from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform # NOQA
from django.contrib.gis.gdal.geometries import OGRGeometry # NOQA
HAS_GDAL = True
__all__ += [
'Driver', 'DataSource', 'gdal_version', 'gdal_full_version',
'GDALRaster', 'GDAL_VERSION', 'SpatialReference', 'CoordTransform',
'OGRGeometry',
]
except GDALException:
HAS_GDAL = False

View File

@ -1,10 +1,7 @@
from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.gdal import CoordTransform, SpatialReference
from django.core.serializers.base import SerializerDoesNotExist from django.core.serializers.base import SerializerDoesNotExist
from django.core.serializers.json import Serializer as JSONSerializer from django.core.serializers.json import Serializer as JSONSerializer
if HAS_GDAL:
from django.contrib.gis.gdal import CoordTransform, SpatialReference
class Serializer(JSONSerializer): class Serializer(JSONSerializer):
""" """

View File

@ -1,16 +1,14 @@
""" """
This module contains useful utilities for GeoDjango. This module contains useful utilities for GeoDjango.
""" """
from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.utils.ogrinfo import ogrinfo # NOQA
from django.contrib.gis.utils.ogrinspect import mapping, ogrinspect # NOQA
from django.contrib.gis.utils.srs import add_srs_entry # NOQA
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
if HAS_GDAL: try:
from django.contrib.gis.utils.ogrinfo import ogrinfo # NOQA
from django.contrib.gis.utils.ogrinspect import mapping, ogrinspect # NOQA
from django.contrib.gis.utils.srs import add_srs_entry # NOQA
try:
# LayerMapping requires DJANGO_SETTINGS_MODULE to be set, # LayerMapping requires DJANGO_SETTINGS_MODULE to be set,
# so this needs to be in try/except. # so this needs to be in try/except.
from django.contrib.gis.utils.layermapping import LayerMapping, LayerMapError # NOQA from django.contrib.gis.utils.layermapping import LayerMapping, LayerMapError # NOQA
except ImproperlyConfigured: except ImproperlyConfigured:
pass pass

View File

@ -225,17 +225,8 @@ Troubleshooting
Can't find GDAL library Can't find GDAL library
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
When GeoDjango can't find the GDAL library, the ``HAS_GDAL`` flag When GeoDjango can't find the GDAL library, configure your :ref:`libsettings`
will be false: *or* set :ref:`gdallibrarypath` in your settings.
.. code-block:: pycon
>>> from django.contrib.gis import gdal
>>> gdal.HAS_GDAL
False
The solution is to properly configure your :ref:`libsettings` *or* set
:ref:`gdallibrarypath` in your settings.
.. _gdallibrarypath: .. _gdallibrarypath:

View File

@ -94,3 +94,6 @@ Bugfixes
* Fixed ``QuerySet.prefetch_related()`` crash when fetching relations in nested * Fixed ``QuerySet.prefetch_related()`` crash when fetching relations in nested
``Prefetch`` objects (:ticket:`27554`). ``Prefetch`` objects (:ticket:`27554`).
* Prevented hiding GDAL errors if it's not installed when using ``contrib.gis``
(:ticket:`28160`). (It's a required dependency as of Django 1.11.)

View File

@ -1,11 +1,7 @@
import unittest import unittest
from unittest import mock from unittest import mock
from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.gdal import Driver, GDALException
if HAS_GDAL:
from django.contrib.gis.gdal import Driver, GDALException
valid_drivers = ( valid_drivers = (
# vector # vector
@ -29,7 +25,6 @@ aliases = {
} }
@unittest.skipUnless(HAS_GDAL, "GDAL is required")
class DriverTest(unittest.TestCase): class DriverTest(unittest.TestCase):
def test01_valid_driver(self): def test01_valid_driver(self):

View File

@ -1,17 +1,16 @@
import os import os
import unittest import unittest
from unittest import skipUnless
from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.gdal import (
GDAL_VERSION, DataSource, Envelope, GDALException, OGRGeometry,
OGRIndexError,
)
from django.contrib.gis.gdal.field import OFTInteger, OFTReal, OFTString
from ..test_data import TEST_DATA, TestDS, get_ds_file from ..test_data import TEST_DATA, TestDS, get_ds_file
if HAS_GDAL: # List of acceptable data sources.
from django.contrib.gis.gdal import DataSource, Envelope, OGRGeometry, GDALException, OGRIndexError, GDAL_VERSION ds_list = (
from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString
# List of acceptable data sources.
ds_list = (
TestDS( TestDS(
'test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='ESRI Shapefile', 'test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, driver='ESRI Shapefile',
fields={'dbl': OFTReal, 'int': OFTInteger, 'str': OFTString}, fields={'dbl': OFTReal, 'int': OFTInteger, 'str': OFTString},
@ -55,12 +54,11 @@ if HAS_GDAL:
'0.017453292519943295]]' '0.017453292519943295]]'
), ),
) )
) )
bad_ds = (TestDS('foo'),) bad_ds = (TestDS('foo'),)
@skipUnless(HAS_GDAL, "GDAL is required")
class DataSourceTest(unittest.TestCase): class DataSourceTest(unittest.TestCase):
def test01_valid_shp(self): def test01_valid_shp(self):

View File

@ -1,10 +1,6 @@
import unittest import unittest
from unittest import skipUnless
from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.gdal import Envelope, GDALException
if HAS_GDAL:
from django.contrib.gis.gdal import Envelope, GDALException
class TestPoint: class TestPoint:
@ -13,7 +9,6 @@ class TestPoint:
self.y = y self.y = y
@skipUnless(HAS_GDAL, "GDAL is required")
class EnvelopeTest(unittest.TestCase): class EnvelopeTest(unittest.TestCase):
def setUp(self): def setUp(self):

View File

@ -2,20 +2,15 @@ import json
import pickle import pickle
import unittest import unittest
from binascii import b2a_hex from binascii import b2a_hex
from unittest import skipUnless
from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.gdal import (
CoordTransform, GDALException, OGRGeometry, OGRGeomType, OGRIndexError,
SpatialReference,
)
from ..test_data import TestDataMixin from ..test_data import TestDataMixin
if HAS_GDAL:
from django.contrib.gis.gdal import (
CoordTransform, GDALException, OGRGeometry, OGRGeomType, OGRIndexError,
SpatialReference,
)
@skipUnless(HAS_GDAL, "GDAL is required")
class OGRGeomTest(unittest.TestCase, TestDataMixin): class OGRGeomTest(unittest.TestCase, TestDataMixin):
"This tests the OGR Geometry." "This tests the OGR Geometry."

View File

@ -43,21 +43,16 @@ Band 1 Block=163x50 Type=Byte, ColorInterp=Gray
import os import os
import struct import struct
import tempfile import tempfile
import unittest
from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.gdal import GDAL_VERSION, GDALRaster
from django.contrib.gis.gdal.error import GDALException from django.contrib.gis.gdal.error import GDALException
from django.contrib.gis.gdal.raster.band import GDALBand
from django.contrib.gis.shortcuts import numpy from django.contrib.gis.shortcuts import numpy
from django.test import SimpleTestCase from django.test import SimpleTestCase
from ..data.rasters.textrasters import JSON_RASTER from ..data.rasters.textrasters import JSON_RASTER
if HAS_GDAL:
from django.contrib.gis.gdal import GDALRaster, GDAL_VERSION
from django.contrib.gis.gdal.raster.band import GDALBand
@unittest.skipUnless(HAS_GDAL, "GDAL is required")
class GDALRasterTests(SimpleTestCase): class GDALRasterTests(SimpleTestCase):
""" """
Test a GDALRaster instance created from a file (GeoTiff). Test a GDALRaster instance created from a file (GeoTiff).
@ -383,7 +378,6 @@ class GDALRasterTests(SimpleTestCase):
) )
@unittest.skipUnless(HAS_GDAL, "GDAL is required")
class GDALBandTests(SimpleTestCase): class GDALBandTests(SimpleTestCase):
def setUp(self): def setUp(self):
self.rs_path = os.path.join(os.path.dirname(__file__), '../data/rasters/raster.tif') self.rs_path = os.path.join(os.path.dirname(__file__), '../data/rasters/raster.tif')

View File

@ -1,10 +1,8 @@
import unittest import unittest
from unittest import skipUnless
from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.gdal import (
CoordTransform, GDALException, SpatialReference, SRSException,
if HAS_GDAL: )
from django.contrib.gis.gdal import SpatialReference, CoordTransform, GDALException, SRSException
class TestSRS: class TestSRS:
@ -148,7 +146,6 @@ bad_srlist = (
) )
@skipUnless(HAS_GDAL, "GDAL is required")
class SpatialRefTest(unittest.TestCase): class SpatialRefTest(unittest.TestCase):
def test01_wkt(self): def test01_wkt(self):

View File

@ -79,7 +79,6 @@ class GeoModelTest(TestCase):
self.assertEqual(ply, ns.poly) self.assertEqual(ply, ns.poly)
# Testing the `ogr` and `srs` lazy-geometry properties. # Testing the `ogr` and `srs` lazy-geometry properties.
if gdal.HAS_GDAL:
self.assertIsInstance(ns.poly.ogr, gdal.OGRGeometry) self.assertIsInstance(ns.poly.ogr, gdal.OGRGeometry)
self.assertEqual(ns.poly.wkb, ns.poly.ogr.wkb) self.assertEqual(ns.poly.wkb, ns.poly.ogr.wkb)
self.assertIsInstance(ns.poly.srs, gdal.SpatialReference) self.assertIsInstance(ns.poly.srs, gdal.SpatialReference)

View File

@ -7,7 +7,6 @@ from io import BytesIO
from unittest import mock, skipUnless from unittest import mock, skipUnless
from django.contrib.gis import gdal from django.contrib.gis import gdal
from django.contrib.gis.gdal import HAS_GDAL
from django.contrib.gis.geos import ( from django.contrib.gis.geos import (
HAS_GEOS, GeometryCollection, GEOSException, GEOSGeometry, LinearRing, HAS_GEOS, GeometryCollection, GEOSException, GEOSGeometry, LinearRing,
LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
@ -134,7 +133,6 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertEqual(srid, poly.shell.srid) self.assertEqual(srid, poly.shell.srid)
self.assertEqual(srid, fromstr(poly.ewkt).srid) # Checking export self.assertEqual(srid, fromstr(poly.ewkt).srid) # Checking export
@skipUnless(HAS_GDAL, "GDAL is required.")
def test_json(self): def test_json(self):
"Testing GeoJSON input/output (via GDAL)." "Testing GeoJSON input/output (via GDAL)."
for g in self.geometries.json_geoms: for g in self.geometries.json_geoms:
@ -145,7 +143,6 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertEqual(json.loads(g.json), json.loads(geom.geojson)) self.assertEqual(json.loads(g.json), json.loads(geom.geojson))
self.assertEqual(GEOSGeometry(g.wkt, 4326), GEOSGeometry(geom.json)) self.assertEqual(GEOSGeometry(g.wkt, 4326), GEOSGeometry(geom.json))
@skipUnless(HAS_GDAL, "GDAL is required.")
def test_json_srid(self): def test_json_srid(self):
geojson_data = { geojson_data = {
"type": "Point", "type": "Point",
@ -730,7 +727,6 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
with self.assertRaisesMessage(ValueError, 'Input geometry already has SRID: %d.' % pnt.srid): with self.assertRaisesMessage(ValueError, 'Input geometry already has SRID: %d.' % pnt.srid):
GEOSGeometry(pnt.ewkb, srid=1) GEOSGeometry(pnt.ewkb, srid=1)
@skipUnless(HAS_GDAL, "GDAL is required.")
def test_custom_srid(self): def test_custom_srid(self):
"""Test with a null srid and a srid unknown to GDAL.""" """Test with a null srid and a srid unknown to GDAL."""
for srid in [None, 999999]: for srid in [None, 999999]:
@ -1016,7 +1012,6 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
# And, they should be equal. # And, they should be equal.
self.assertEqual(gc1, gc2) self.assertEqual(gc1, gc2)
@skipUnless(HAS_GDAL, "GDAL is required.")
def test_gdal(self): def test_gdal(self):
"Testing `ogr` and `srs` properties." "Testing `ogr` and `srs` properties."
g1 = fromstr('POINT(5 23)') g1 = fromstr('POINT(5 23)')
@ -1042,7 +1037,6 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertNotEqual(poly._ptr, cpy1._ptr) self.assertNotEqual(poly._ptr, cpy1._ptr)
self.assertNotEqual(poly._ptr, cpy2._ptr) self.assertNotEqual(poly._ptr, cpy2._ptr)
@skipUnless(HAS_GDAL, "GDAL is required to transform geometries")
def test_transform(self): def test_transform(self):
"Testing `transform` method." "Testing `transform` method."
orig = GEOSGeometry('POINT (-104.609 38.255)', 4326) orig = GEOSGeometry('POINT (-104.609 38.255)', 4326)
@ -1067,13 +1061,11 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertAlmostEqual(trans.x, p.x, prec) self.assertAlmostEqual(trans.x, p.x, prec)
self.assertAlmostEqual(trans.y, p.y, prec) self.assertAlmostEqual(trans.y, p.y, prec)
@skipUnless(HAS_GDAL, "GDAL is required to transform geometries")
def test_transform_3d(self): def test_transform_3d(self):
p3d = GEOSGeometry('POINT (5 23 100)', 4326) p3d = GEOSGeometry('POINT (5 23 100)', 4326)
p3d.transform(2774) p3d.transform(2774)
self.assertEqual(p3d.z, 100) self.assertEqual(p3d.z, 100)
@skipUnless(HAS_GDAL, "GDAL is required.")
def test_transform_noop(self): def test_transform_noop(self):
""" Testing `transform` method (SRID match) """ """ Testing `transform` method (SRID match) """
# transform() should no-op if source & dest SRIDs match, # transform() should no-op if source & dest SRIDs match,
@ -1090,7 +1082,6 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertEqual(g1.srid, 4326) self.assertEqual(g1.srid, 4326)
self.assertIsNot(g1, g, "Clone didn't happen") self.assertIsNot(g1, g, "Clone didn't happen")
@skipUnless(HAS_GDAL, "GDAL is required.")
def test_transform_nosrid(self): def test_transform_nosrid(self):
""" Testing `transform` method (no SRID or negative SRID) """ """ Testing `transform` method (no SRID or negative SRID) """

View File

@ -2,7 +2,8 @@ import os
import re import re
from io import StringIO from io import StringIO
from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.gdal import GDAL_VERSION, Driver, GDALException
from django.contrib.gis.utils.ogrinspect import ogrinspect
from django.core.management import call_command from django.core.management import call_command
from django.db import connection, connections from django.db import connection, connections
from django.test import TestCase, skipUnlessDBFeature from django.test import TestCase, skipUnlessDBFeature
@ -10,12 +11,7 @@ from django.test.utils import modify_settings
from ..test_data import TEST_DATA from ..test_data import TEST_DATA
from ..utils import postgis from ..utils import postgis
from .models import AllOGRFields
if HAS_GDAL:
from django.contrib.gis.gdal import Driver, GDALException, GDAL_VERSION
from django.contrib.gis.utils.ogrinspect import ogrinspect
from .models import AllOGRFields
class InspectDbTests(TestCase): class InspectDbTests(TestCase):

View File

@ -4,12 +4,11 @@ from copy import copy
from decimal import Decimal from decimal import Decimal
from django.conf import settings from django.conf import settings
from django.contrib.gis.gdal import HAS_GDAL
from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.geos import HAS_GEOS
from django.db import connection from django.db import connection
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
if HAS_GEOS and HAS_GDAL: if HAS_GEOS:
from django.contrib.gis.utils.layermapping import ( from django.contrib.gis.utils.layermapping import (
LayerMapping, LayerMapError, InvalidDecimal, InvalidString, LayerMapping, LayerMapError, InvalidDecimal, InvalidString,
MissingForeignKey, MissingForeignKey,

View File

@ -1,8 +1,7 @@
from django.contrib.gis.db import models from django.contrib.gis.db import models
from django.contrib.gis.gdal import HAS_GDAL
if HAS_GDAL:
class RasterModel(models.Model): class RasterModel(models.Model):
rast = models.RasterField('A Verbose Raster Name', null=True, srid=4326, spatial_index=True, blank=True) rast = models.RasterField('A Verbose Raster Name', null=True, srid=4326, spatial_index=True, blank=True)
rastprojected = models.RasterField('A Projected Raster Table', srid=3086, null=True) rastprojected = models.RasterField('A Projected Raster Table', srid=3086, null=True)
geom = models.PointField(null=True) geom = models.PointField(null=True)
@ -13,7 +12,8 @@ if HAS_GDAL:
def __str__(self): def __str__(self):
return str(self.id) return str(self.id)
class RasterRelatedModel(models.Model):
class RasterRelatedModel(models.Model):
rastermodel = models.ForeignKey(RasterModel, models.CASCADE) rastermodel = models.ForeignKey(RasterModel, models.CASCADE)
class Meta: class Meta:

View File

@ -3,7 +3,7 @@ import json
from django.contrib.gis.db.models.fields import BaseSpatialField from django.contrib.gis.db.models.fields import BaseSpatialField
from django.contrib.gis.db.models.functions import Distance from django.contrib.gis.db.models.functions import Distance
from django.contrib.gis.db.models.lookups import DistanceLookupBase, GISLookup from django.contrib.gis.db.models.lookups import DistanceLookupBase, GISLookup
from django.contrib.gis.gdal import HAS_GDAL from django.contrib.gis.gdal import GDALRaster
from django.contrib.gis.geos import GEOSGeometry from django.contrib.gis.geos import GEOSGeometry
from django.contrib.gis.measure import D from django.contrib.gis.measure import D
from django.contrib.gis.shortcuts import numpy from django.contrib.gis.shortcuts import numpy
@ -11,10 +11,7 @@ from django.db.models import Q
from django.test import TransactionTestCase, skipUnlessDBFeature from django.test import TransactionTestCase, skipUnlessDBFeature
from ..data.rasters.textrasters import JSON_RASTER from ..data.rasters.textrasters import JSON_RASTER
from .models import RasterModel, RasterRelatedModel
if HAS_GDAL:
from django.contrib.gis.gdal import GDALRaster
from .models import RasterModel, RasterRelatedModel
@skipUnlessDBFeature('supports_raster') @skipUnlessDBFeature('supports_raster')

View File

@ -135,8 +135,8 @@ class DiscoverRunnerTest(TestCase):
""" """
Tests shouldn't be discovered twice when discovering on overlapping paths. Tests shouldn't be discovered twice when discovering on overlapping paths.
""" """
base_app = 'gis_tests' base_app = 'forms_tests'
sub_app = 'gis_tests.geo3d' sub_app = 'forms_tests.field_tests'
with self.modify_settings(INSTALLED_APPS={'append': sub_app}): with self.modify_settings(INSTALLED_APPS={'append': sub_app}):
single = DiscoverRunner().build_suite([base_app]).countTestCases() single = DiscoverRunner().build_suite([base_app]).countTestCases()
dups = DiscoverRunner().build_suite([base_app, sub_app]).countTestCases() dups = DiscoverRunner().build_suite([base_app, sub_app]).countTestCases()