Fixed #28665 -- Change some database exceptions to NotImplementedError per PEP 249.

This commit is contained in:
Simon Charette 2017-10-06 12:47:08 -04:00 committed by Tim Graham
parent 7d8d630e37
commit 9d93dff333
15 changed files with 37 additions and 32 deletions

View File

@ -3,6 +3,7 @@ from django.contrib.gis.db.models.functions import Distance
from django.contrib.gis.measure import ( from django.contrib.gis.measure import (
Area as AreaMeasure, Distance as DistanceMeasure, Area as AreaMeasure, Distance as DistanceMeasure,
) )
from django.db.utils import NotSupportedError
from django.utils.functional import cached_property from django.utils.functional import cached_property
@ -105,7 +106,7 @@ class BaseSpatialOperations:
def check_expression_support(self, expression): def check_expression_support(self, expression):
if isinstance(expression, self.disallowed_aggregates): if isinstance(expression, self.disallowed_aggregates):
raise NotImplementedError( raise NotSupportedError(
"%s spatial aggregation is not supported by this database backend." % expression.name "%s spatial aggregation is not supported by this database backend." % expression.name
) )
super().check_expression_support(expression) super().check_expression_support(expression)
@ -115,7 +116,7 @@ class BaseSpatialOperations:
def spatial_function_name(self, func_name): def spatial_function_name(self, func_name):
if func_name in self.unsupported_functions: if func_name in self.unsupported_functions:
raise NotImplementedError("This backend doesn't support the %s function." % func_name) raise NotSupportedError("This backend doesn't support the %s function." % func_name)
return self.function_names.get(func_name, self.geom_func_prefix + func_name) return self.function_names.get(func_name, self.geom_func_prefix + func_name)
# Routines for getting the OGC-compliant models. # Routines for getting the OGC-compliant models.

View File

@ -13,7 +13,7 @@ from django.contrib.gis.measure import Distance
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.db.backends.postgresql.operations import DatabaseOperations from django.db.backends.postgresql.operations import DatabaseOperations
from django.db.models import Func, Value from django.db.models import Func, Value
from django.db.utils import ProgrammingError from django.db.utils import NotSupportedError, ProgrammingError
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.version import get_version_tuple from django.utils.version import get_version_tuple
@ -231,7 +231,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations):
geom_type = f.geom_type geom_type = f.geom_type
if f.geography: if f.geography:
if f.srid != 4326: if f.srid != 4326:
raise NotImplementedError('PostGIS only supports geography columns with an SRID of 4326.') raise NotSupportedError('PostGIS only supports geography columns with an SRID of 4326.')
return 'geography(%s,%d)' % (geom_type, f.srid) return 'geography(%s,%d)' % (geom_type, f.srid)
else: else:

View File

@ -9,6 +9,7 @@ from django.db.models import (
) )
from django.db.models.expressions import Func, Value from django.db.models.expressions import Func, Value
from django.db.models.functions import Cast from django.db.models.functions import Cast
from django.db.utils import NotSupportedError
from django.utils.functional import cached_property from django.utils.functional import cached_property
NUMERIC_TYPES = (int, float, Decimal) NUMERIC_TYPES = (int, float, Decimal)
@ -123,7 +124,7 @@ class Area(OracleToleranceMixin, GeoFunc):
def as_sql(self, compiler, connection, **extra_context): def as_sql(self, compiler, connection, **extra_context):
if not connection.features.supports_area_geodetic and self.geo_field.geodetic(connection): if not connection.features.supports_area_geodetic and self.geo_field.geodetic(connection):
raise NotImplementedError('Area on geodetic coordinate systems not supported.') raise NotSupportedError('Area on geodetic coordinate systems not supported.')
return super().as_sql(compiler, connection, **extra_context) return super().as_sql(compiler, connection, **extra_context)
def as_sqlite(self, compiler, connection, **extra_context): def as_sqlite(self, compiler, connection, **extra_context):
@ -316,7 +317,7 @@ class Length(DistanceResultMixin, OracleToleranceMixin, GeoFunc):
def as_sql(self, compiler, connection, **extra_context): def as_sql(self, compiler, connection, **extra_context):
if self.geo_field.geodetic(connection) and not connection.features.supports_length_geodetic: if self.geo_field.geodetic(connection) and not connection.features.supports_length_geodetic:
raise NotImplementedError("This backend doesn't support Length on geodetic fields") raise NotSupportedError("This backend doesn't support Length on geodetic fields")
return super().as_sql(compiler, connection, **extra_context) return super().as_sql(compiler, connection, **extra_context)
def as_postgresql(self, compiler, connection): def as_postgresql(self, compiler, connection):
@ -372,7 +373,7 @@ class Perimeter(DistanceResultMixin, OracleToleranceMixin, GeoFunc):
def as_postgresql(self, compiler, connection): def as_postgresql(self, compiler, connection):
function = None function = None
if self.geo_field.geodetic(connection) and not self.source_is_geography(): if self.geo_field.geodetic(connection) and not self.source_is_geography():
raise NotImplementedError("ST_Perimeter cannot use a non-projected non-geography field.") raise NotSupportedError("ST_Perimeter cannot use a non-projected non-geography field.")
dim = min(f.dim for f in self.get_source_fields()) dim = min(f.dim for f in self.get_source_fields())
if dim > 2: if dim > 2:
function = connection.ops.perimeter3d function = connection.ops.perimeter3d
@ -380,7 +381,7 @@ class Perimeter(DistanceResultMixin, OracleToleranceMixin, GeoFunc):
def as_sqlite(self, compiler, connection): def as_sqlite(self, compiler, connection):
if self.geo_field.geodetic(connection): if self.geo_field.geodetic(connection):
raise NotImplementedError("Perimeter cannot use a non-projected field.") raise NotSupportedError("Perimeter cannot use a non-projected field.")
return super().as_sql(compiler, connection) return super().as_sql(compiler, connection)

View File

@ -1,5 +1,5 @@
from django.db.models.aggregates import StdDev from django.db.models.aggregates import StdDev
from django.db.utils import ProgrammingError from django.db.utils import NotSupportedError, ProgrammingError
from django.utils.functional import cached_property from django.utils.functional import cached_property
@ -269,9 +269,9 @@ class BaseDatabaseFeatures:
"""Confirm support for STDDEV and related stats functions.""" """Confirm support for STDDEV and related stats functions."""
try: try:
self.connection.ops.check_expression_support(StdDev(1)) self.connection.ops.check_expression_support(StdDev(1))
return True except NotSupportedError:
except NotImplementedError:
return False return False
return True
def introspected_boolean_field_type(self, field=None): def introspected_boolean_field_type(self, field=None):
""" """

View File

@ -579,7 +579,7 @@ class BaseDatabaseOperations:
This is used on specific backends to rule out known expressions This is used on specific backends to rule out known expressions
that have problematic or nonexistent implementations. If the that have problematic or nonexistent implementations. If the
expression has a known problem, the backend should raise expression has a known problem, the backend should raise
NotImplementedError. NotSupportedError.
""" """
pass pass

View File

@ -43,7 +43,7 @@ class DatabaseOperations(BaseDatabaseOperations):
pass pass
else: else:
if isinstance(output_field, bad_fields): if isinstance(output_field, bad_fields):
raise NotImplementedError( raise utils.NotSupportedError(
'You cannot use Sum, Avg, StdDev, and Variance ' 'You cannot use Sum, Avg, StdDev, and Variance '
'aggregations on date/time fields in sqlite3 ' 'aggregations on date/time fields in sqlite3 '
'since date/time is saved as text.' 'since date/time is saved as text.'

View File

@ -25,7 +25,7 @@ class Lookup:
# a bilateral transformation on a nested QuerySet: that won't work. # a bilateral transformation on a nested QuerySet: that won't work.
from django.db.models.sql.query import Query # avoid circular import from django.db.models.sql.query import Query # avoid circular import
if isinstance(rhs, Query): if isinstance(rhs, Query):
raise NotImplementedError("Bilateral transformations on nested querysets are not supported.") raise NotImplementedError("Bilateral transformations on nested querysets are not implemented.")
self.bilateral_transforms = bilateral_transforms self.bilateral_transforms = bilateral_transforms
def apply_bilateral_transforms(self, value): def apply_bilateral_transforms(self, value):

View File

@ -198,7 +198,9 @@ Backwards incompatible changes in 2.1
Database backend API Database backend API
-------------------- --------------------
* ... * To adhere to :pep:`249`, exceptions where a database doesn't support a
feature are changed from :exc:`NotImplementedError` to
:exc:`django.db.NotSupportedError`.
:mod:`django.contrib.gis` :mod:`django.contrib.gis`
------------------------- -------------------------

View File

@ -4,6 +4,7 @@ import unittest
from django.db import connection from django.db import connection
from django.db.models import Avg, StdDev, Sum, Variance from django.db.models import Avg, StdDev, Sum, Variance
from django.db.utils import NotSupportedError
from django.test import TestCase, TransactionTestCase, override_settings from django.test import TestCase, TransactionTestCase, override_settings
from ..models import Item, Object, Square from ..models import Item, Object, Square
@ -34,13 +35,13 @@ class Tests(TestCase):
Raise NotImplementedError when aggregating on date/time fields (#19360). Raise NotImplementedError when aggregating on date/time fields (#19360).
""" """
for aggregate in (Sum, Avg, Variance, StdDev): for aggregate in (Sum, Avg, Variance, StdDev):
with self.assertRaises(NotImplementedError): with self.assertRaises(NotSupportedError):
Item.objects.all().aggregate(aggregate('time')) Item.objects.all().aggregate(aggregate('time'))
with self.assertRaises(NotImplementedError): with self.assertRaises(NotSupportedError):
Item.objects.all().aggregate(aggregate('date')) Item.objects.all().aggregate(aggregate('date'))
with self.assertRaises(NotImplementedError): with self.assertRaises(NotSupportedError):
Item.objects.all().aggregate(aggregate('last_modified')) Item.objects.all().aggregate(aggregate('last_modified'))
with self.assertRaises(NotImplementedError): with self.assertRaises(NotSupportedError):
Item.objects.all().aggregate( Item.objects.all().aggregate(
**{'complex': aggregate('last_modified') + aggregate('last_modified')} **{'complex': aggregate('last_modified') + aggregate('last_modified')}
) )

View File

@ -319,7 +319,7 @@ class BilateralTransformTests(TestCase):
def test_bilateral_inner_qs(self): def test_bilateral_inner_qs(self):
with register_lookup(models.CharField, UpperBilateralTransform): with register_lookup(models.CharField, UpperBilateralTransform):
msg = 'Bilateral transformations on nested querysets are not supported.' msg = 'Bilateral transformations on nested querysets are not implemented.'
with self.assertRaisesMessage(NotImplementedError, msg): with self.assertRaisesMessage(NotImplementedError, msg):
Author.objects.filter(name__upper__in=Author.objects.values_list('name')) Author.objects.filter(name__upper__in=Author.objects.values_list('name'))

View File

@ -5,7 +5,7 @@ from django.contrib.gis.db.models.functions import (
) )
from django.contrib.gis.geos import GEOSGeometry, LineString, Point from django.contrib.gis.geos import GEOSGeometry, LineString, Point
from django.contrib.gis.measure import D # alias for Distance from django.contrib.gis.measure import D # alias for Distance
from django.db import connection from django.db import NotSupportedError, connection
from django.db.models import F, Q from django.db.models import F, Q
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
@ -474,7 +474,7 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase):
# TODO: test with spheroid argument (True and False) # TODO: test with spheroid argument (True and False)
else: else:
# Does not support geodetic coordinate systems. # Does not support geodetic coordinate systems.
with self.assertRaises(NotImplementedError): with self.assertRaises(NotSupportedError):
list(Interstate.objects.annotate(length=Length('path'))) list(Interstate.objects.annotate(length=Length('path')))
# Now doing length on a projected coordinate system. # Now doing length on a projected coordinate system.
@ -513,7 +513,7 @@ class DistanceFunctionsTests(FuncTestMixin, TestCase):
if connection.features.supports_perimeter_geodetic: if connection.features.supports_perimeter_geodetic:
self.assertAlmostEqual(qs1[0].perim.m, 18406.3818954314, 3) self.assertAlmostEqual(qs1[0].perim.m, 18406.3818954314, 3)
else: else:
with self.assertRaises(NotImplementedError): with self.assertRaises(NotSupportedError):
list(qs1) list(qs1)
# But should work fine when transformed to projected coordinates # But should work fine when transformed to projected coordinates
qs2 = CensusZipcode.objects.annotate(perim=Perimeter(Transform('poly', 32140))).filter(name='77002') qs2 = CensusZipcode.objects.annotate(perim=Perimeter(Transform('poly', 32140))).filter(name='77002')

View File

@ -8,7 +8,7 @@ from django.contrib.gis.geos import (
GEOSGeometry, LineString, Point, Polygon, fromstr, GEOSGeometry, LineString, Point, Polygon, fromstr,
) )
from django.contrib.gis.measure import Area from django.contrib.gis.measure import Area
from django.db import connection from django.db import NotSupportedError, connection
from django.db.models import Sum from django.db.models import Sum
from django.test import TestCase, skipUnlessDBFeature from django.test import TestCase, skipUnlessDBFeature
@ -28,7 +28,7 @@ class GISFunctionsTests(FuncTestMixin, TestCase):
def test_asgeojson(self): def test_asgeojson(self):
# Only PostGIS and SpatiaLite support GeoJSON. # Only PostGIS and SpatiaLite support GeoJSON.
if not connection.features.has_AsGeoJSON_function: if not connection.features.has_AsGeoJSON_function:
with self.assertRaises(NotImplementedError): with self.assertRaises(NotSupportedError):
list(Country.objects.annotate(json=functions.AsGeoJSON('mpoly'))) list(Country.objects.annotate(json=functions.AsGeoJSON('mpoly')))
return return

View File

@ -8,7 +8,7 @@ from django.contrib.gis.geos import (
MultiPoint, MultiPolygon, Point, Polygon, fromstr, MultiPoint, MultiPolygon, Point, Polygon, fromstr,
) )
from django.core.management import call_command from django.core.management import call_command
from django.db import connection from django.db import NotSupportedError, connection
from django.test import TestCase, skipUnlessDBFeature from django.test import TestCase, skipUnlessDBFeature
from ..utils import ( from ..utils import (
@ -516,7 +516,7 @@ class GeoQuerySetTest(TestCase):
Testing the `MakeLine` aggregate. Testing the `MakeLine` aggregate.
""" """
if not connection.features.supports_make_line_aggr: if not connection.features.supports_make_line_aggr:
with self.assertRaises(NotImplementedError): with self.assertRaises(NotSupportedError):
City.objects.all().aggregate(MakeLine('point')) City.objects.all().aggregate(MakeLine('point'))
return return

View File

@ -7,7 +7,7 @@ from unittest import skipIf, skipUnless
from django.contrib.gis.db import models from django.contrib.gis.db import models
from django.contrib.gis.db.models.functions import Area, Distance from django.contrib.gis.db.models.functions import Area, Distance
from django.contrib.gis.measure import D from django.contrib.gis.measure import D
from django.db import connection from django.db import NotSupportedError, connection
from django.db.models.functions import Cast from django.db.models.functions import Cast
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
@ -152,5 +152,5 @@ class GeographyFunctionTests(FuncTestMixin, TestCase):
@skipUnlessDBFeature("has_Area_function") @skipUnlessDBFeature("has_Area_function")
@skipIfDBFeature("supports_area_geodetic") @skipIfDBFeature("supports_area_geodetic")
def test_geodetic_area_raises_if_not_supported(self): def test_geodetic_area_raises_if_not_supported(self):
with self.assertRaisesMessage(NotImplementedError, 'Area on geodetic coordinate systems not supported.'): with self.assertRaisesMessage(NotSupportedError, 'Area on geodetic coordinate systems not supported.'):
Zipcode.objects.annotate(area=Area('poly')).get(code='77002') Zipcode.objects.annotate(area=Area('poly')).get(code='77002')

View File

@ -1,6 +1,6 @@
from django.contrib.gis.db.models import Collect, Count, Extent, F, Union from django.contrib.gis.db.models import Collect, Count, Extent, F, Union
from django.contrib.gis.geos import GEOSGeometry, MultiPoint, Point from django.contrib.gis.geos import GEOSGeometry, MultiPoint, Point
from django.db import connection from django.db import NotSupportedError, connection
from django.test import TestCase, skipUnlessDBFeature from django.test import TestCase, skipUnlessDBFeature
from django.test.utils import override_settings from django.test.utils import override_settings
from django.utils import timezone from django.utils import timezone
@ -147,7 +147,7 @@ class RelatedGeoModelTest(TestCase):
self.assertEqual('P2', qs.get().name) self.assertEqual('P2', qs.get().name)
else: else:
msg = "This backend doesn't support the Transform function." msg = "This backend doesn't support the Transform function."
with self.assertRaisesMessage(NotImplementedError, msg): with self.assertRaisesMessage(NotSupportedError, msg):
list(qs) list(qs)
# Should return the first Parcel, which has the center point equal # Should return the first Parcel, which has the center point equal
@ -162,7 +162,7 @@ class RelatedGeoModelTest(TestCase):
self.assertEqual('P1', qs.get().name) self.assertEqual('P1', qs.get().name)
else: else:
msg = "This backend doesn't support the Transform function." msg = "This backend doesn't support the Transform function."
with self.assertRaisesMessage(NotImplementedError, msg): with self.assertRaisesMessage(NotSupportedError, msg):
list(qs) list(qs)
def test07_values(self): def test07_values(self):