mirror of https://github.com/django/django.git
Fixed #27830 -- Used distutils.version.LooseVersion for version parsing.
This commit is contained in:
parent
e7dc39fb65
commit
08bda82c23
|
@ -11,6 +11,7 @@ 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.utils import ProgrammingError
|
from django.db.utils import ProgrammingError
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
from django.utils.version import get_version_tuple
|
||||||
|
|
||||||
from .adapter import PostGISAdapter
|
from .adapter import PostGISAdapter
|
||||||
from .models import PostGISGeometryColumns, PostGISSpatialRefSys
|
from .models import PostGISGeometryColumns, PostGISSpatialRefSys
|
||||||
|
@ -109,7 +110,6 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations):
|
||||||
postgis = True
|
postgis = True
|
||||||
geography = True
|
geography = True
|
||||||
geom_func_prefix = 'ST_'
|
geom_func_prefix = 'ST_'
|
||||||
version_regex = re.compile(r'^(?P<major>\d)\.(?P<minor1>\d)\.(?P<minor2>\d+)')
|
|
||||||
|
|
||||||
Adapter = PostGISAdapter
|
Adapter = PostGISAdapter
|
||||||
|
|
||||||
|
@ -353,18 +353,8 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations):
|
||||||
Return the PostGIS version as a tuple (version string, major,
|
Return the PostGIS version as a tuple (version string, major,
|
||||||
minor, subminor).
|
minor, subminor).
|
||||||
"""
|
"""
|
||||||
# Getting the PostGIS version
|
|
||||||
version = self.postgis_lib_version()
|
version = self.postgis_lib_version()
|
||||||
m = self.version_regex.match(version)
|
return (version,) + get_version_tuple(version)
|
||||||
|
|
||||||
if m:
|
|
||||||
major = int(m.group('major'))
|
|
||||||
minor1 = int(m.group('minor1'))
|
|
||||||
minor2 = int(m.group('minor2'))
|
|
||||||
else:
|
|
||||||
raise Exception('Could not parse PostGIS version string: %s' % version)
|
|
||||||
|
|
||||||
return (version, major, minor1, minor2)
|
|
||||||
|
|
||||||
def proj_version_tuple(self):
|
def proj_version_tuple(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -3,8 +3,6 @@ SQL functions reference lists:
|
||||||
https://web.archive.org/web/20130407175746/https://www.gaia-gis.it/gaia-sins/spatialite-sql-4.0.0.html
|
https://web.archive.org/web/20130407175746/https://www.gaia-gis.it/gaia-sins/spatialite-sql-4.0.0.html
|
||||||
https://www.gaia-gis.it/gaia-sins/spatialite-sql-4.2.1.html
|
https://www.gaia-gis.it/gaia-sins/spatialite-sql-4.2.1.html
|
||||||
"""
|
"""
|
||||||
import re
|
|
||||||
|
|
||||||
from django.contrib.gis.db.backends.base.operations import (
|
from django.contrib.gis.db.backends.base.operations import (
|
||||||
BaseSpatialOperations,
|
BaseSpatialOperations,
|
||||||
)
|
)
|
||||||
|
@ -16,6 +14,7 @@ from django.contrib.gis.measure import Distance
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.db.backends.sqlite3.operations import DatabaseOperations
|
from django.db.backends.sqlite3.operations import DatabaseOperations
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
from django.utils.version import get_version_tuple
|
||||||
|
|
||||||
|
|
||||||
class SpatiaLiteDistanceOperator(SpatialOperator):
|
class SpatiaLiteDistanceOperator(SpatialOperator):
|
||||||
|
@ -35,7 +34,6 @@ class SpatiaLiteDistanceOperator(SpatialOperator):
|
||||||
class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
|
class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
|
||||||
name = 'spatialite'
|
name = 'spatialite'
|
||||||
spatialite = True
|
spatialite = True
|
||||||
version_regex = re.compile(r'^(?P<major>\d)\.(?P<minor1>\d)\.(?P<minor2>\d+)')
|
|
||||||
|
|
||||||
Adapter = SpatiaLiteAdapter
|
Adapter = SpatiaLiteAdapter
|
||||||
|
|
||||||
|
@ -189,16 +187,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
|
||||||
minor, subminor).
|
minor, subminor).
|
||||||
"""
|
"""
|
||||||
version = self.spatialite_version()
|
version = self.spatialite_version()
|
||||||
|
return (version,) + get_version_tuple(version)
|
||||||
m = self.version_regex.match(version)
|
|
||||||
if m:
|
|
||||||
major = int(m.group('major'))
|
|
||||||
minor1 = int(m.group('minor1'))
|
|
||||||
minor2 = int(m.group('minor2'))
|
|
||||||
else:
|
|
||||||
raise Exception('Could not parse SpatiaLite version string: %s' % version)
|
|
||||||
|
|
||||||
return (version, major, minor1, minor2)
|
|
||||||
|
|
||||||
def spatial_aggregate_name(self, agg_name):
|
def spatial_aggregate_name(self, agg_name):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -8,7 +8,7 @@ from ctypes import byref, c_int, c_uint
|
||||||
from django.contrib.gis.geos import prototypes as capi
|
from django.contrib.gis.geos import prototypes as capi
|
||||||
from django.contrib.gis.geos.error import GEOSException
|
from django.contrib.gis.geos.error import GEOSException
|
||||||
from django.contrib.gis.geos.geometry import GEOSGeometry, LinearGeometryMixin
|
from django.contrib.gis.geos.geometry import GEOSGeometry, LinearGeometryMixin
|
||||||
from django.contrib.gis.geos.libgeos import geos_version_info, get_pointer_arr
|
from django.contrib.gis.geos.libgeos import geos_version_tuple, get_pointer_arr
|
||||||
from django.contrib.gis.geos.linestring import LinearRing, LineString
|
from django.contrib.gis.geos.linestring import LinearRing, LineString
|
||||||
from django.contrib.gis.geos.point import Point
|
from django.contrib.gis.geos.point import Point
|
||||||
from django.contrib.gis.geos.polygon import Polygon
|
from django.contrib.gis.geos.polygon import Polygon
|
||||||
|
@ -115,7 +115,7 @@ class MultiLineString(LinearGeometryMixin, GeometryCollection):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def closed(self):
|
def closed(self):
|
||||||
if geos_version_info()['version'] < '3.5':
|
if geos_version_tuple() < (3, 5):
|
||||||
raise GEOSException("MultiLineString.closed requires GEOS >= 3.5.0.")
|
raise GEOSException("MultiLineString.closed requires GEOS >= 3.5.0.")
|
||||||
return super().closed
|
return super().closed
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ from ctypes.util import find_library
|
||||||
from django.contrib.gis.geos.error import GEOSException
|
from django.contrib.gis.geos.error import GEOSException
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.utils.functional import SimpleLazyObject
|
from django.utils.functional import SimpleLazyObject
|
||||||
|
from django.utils.version import get_version_tuple
|
||||||
|
|
||||||
logger = logging.getLogger('django.contrib.gis')
|
logger = logging.getLogger('django.contrib.gis')
|
||||||
|
|
||||||
|
@ -199,3 +200,8 @@ def geos_version_info():
|
||||||
raise GEOSException('Could not parse version info string "%s"' % ver)
|
raise GEOSException('Could not parse version info string "%s"' % ver)
|
||||||
return {key: m.group(key) for key in (
|
return {key: m.group(key) for key in (
|
||||||
'version', 'release_candidate', 'capi_version', 'major', 'minor', 'subminor')}
|
'version', 'release_candidate', 'capi_version', 'major', 'minor', 'subminor')}
|
||||||
|
|
||||||
|
|
||||||
|
def geos_version_tuple():
|
||||||
|
"""Return the GEOS version as a tuple (major, minor, subminor)."""
|
||||||
|
return get_version_tuple(geos_version_info()['version'])
|
||||||
|
|
|
@ -14,6 +14,7 @@ from django.db.backends.base.base import BaseDatabaseWrapper
|
||||||
from django.db.utils import DatabaseError as WrappedDatabaseError
|
from django.db.utils import DatabaseError as WrappedDatabaseError
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.safestring import SafeText
|
from django.utils.safestring import SafeText
|
||||||
|
from django.utils.version import get_version_tuple
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import psycopg2 as Database
|
import psycopg2 as Database
|
||||||
|
@ -25,7 +26,7 @@ except ImportError as e:
|
||||||
|
|
||||||
def psycopg2_version():
|
def psycopg2_version():
|
||||||
version = psycopg2.__version__.split(' ', 1)[0]
|
version = psycopg2.__version__.split(' ', 1)[0]
|
||||||
return tuple(int(v) for v in version.split('.') if v.isdigit())
|
return get_version_tuple(version)
|
||||||
|
|
||||||
|
|
||||||
PSYCOPG2_VERSION = psycopg2_version()
|
PSYCOPG2_VERSION = psycopg2_version()
|
||||||
|
|
|
@ -2,6 +2,7 @@ import datetime
|
||||||
import functools
|
import functools
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from distutils.version import LooseVersion
|
||||||
|
|
||||||
|
|
||||||
def get_version(version=None):
|
def get_version(version=None):
|
||||||
|
@ -77,3 +78,17 @@ def get_git_changeset():
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
return timestamp.strftime('%Y%m%d%H%M%S')
|
return timestamp.strftime('%Y%m%d%H%M%S')
|
||||||
|
|
||||||
|
|
||||||
|
def get_version_tuple(version):
|
||||||
|
"""
|
||||||
|
Return a tuple of version numbers (e.g. (1, 2, 3)) from the version
|
||||||
|
string (e.g. '1.2.3').
|
||||||
|
"""
|
||||||
|
loose_version = LooseVersion(version)
|
||||||
|
version_numbers = []
|
||||||
|
for item in loose_version.version:
|
||||||
|
if not isinstance(item, int):
|
||||||
|
break
|
||||||
|
version_numbers.append(item)
|
||||||
|
return tuple(version_numbers)
|
||||||
|
|
|
@ -323,13 +323,12 @@ class PostgreSQLTests(TestCase):
|
||||||
|
|
||||||
def test_correct_extraction_psycopg2_version(self):
|
def test_correct_extraction_psycopg2_version(self):
|
||||||
from django.db.backends.postgresql.base import psycopg2_version
|
from django.db.backends.postgresql.base import psycopg2_version
|
||||||
version_path = 'django.db.backends.postgresql.base.Database.__version__'
|
|
||||||
|
|
||||||
with mock.patch(version_path, '2.6.9'):
|
with mock.patch('psycopg2.__version__', '4.2.1 (dt dec pq3 ext lo64)'):
|
||||||
self.assertEqual(psycopg2_version(), (2, 6, 9))
|
self.assertEqual(psycopg2_version(), (4, 2, 1))
|
||||||
|
|
||||||
with mock.patch(version_path, '2.5.dev0'):
|
with mock.patch('psycopg2.__version__', '4.2b0.dev1 (dt dec pq3 ext lo64)'):
|
||||||
self.assertEqual(psycopg2_version(), (2, 5))
|
self.assertEqual(psycopg2_version(), (4, 2))
|
||||||
|
|
||||||
|
|
||||||
class DateQuotingTest(TestCase):
|
class DateQuotingTest(TestCase):
|
||||||
|
|
|
@ -12,7 +12,7 @@ from django.contrib.gis.geos import (
|
||||||
MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, fromfile,
|
MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, fromfile,
|
||||||
fromstr,
|
fromstr,
|
||||||
)
|
)
|
||||||
from django.contrib.gis.geos.libgeos import geos_version_info
|
from django.contrib.gis.geos.libgeos import geos_version_tuple
|
||||||
from django.contrib.gis.shortcuts import numpy
|
from django.contrib.gis.shortcuts import numpy
|
||||||
from django.template import Context
|
from django.template import Context
|
||||||
from django.template.engine import Engine
|
from django.template.engine import Engine
|
||||||
|
@ -670,11 +670,11 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
|
||||||
self.assertFalse(ls_not_closed.closed)
|
self.assertFalse(ls_not_closed.closed)
|
||||||
self.assertTrue(ls_closed.closed)
|
self.assertTrue(ls_closed.closed)
|
||||||
|
|
||||||
if geos_version_info()['version'] >= '3.5':
|
if geos_version_tuple() >= (3, 5):
|
||||||
self.assertFalse(MultiLineString(ls_closed, ls_not_closed).closed)
|
self.assertFalse(MultiLineString(ls_closed, ls_not_closed).closed)
|
||||||
self.assertTrue(MultiLineString(ls_closed, ls_closed).closed)
|
self.assertTrue(MultiLineString(ls_closed, ls_closed).closed)
|
||||||
|
|
||||||
with mock.patch('django.contrib.gis.geos.collections.geos_version_info', lambda: {'version': '3.4.9'}):
|
with mock.patch('django.contrib.gis.geos.libgeos.geos_version_info', lambda: {'version': '3.4.9'}):
|
||||||
with self.assertRaisesMessage(GEOSException, "MultiLineString.closed requires GEOS >= 3.5.0."):
|
with self.assertRaisesMessage(GEOSException, "MultiLineString.closed requires GEOS >= 3.5.0."):
|
||||||
MultiLineString().closed
|
MultiLineString().closed
|
||||||
|
|
||||||
|
@ -1305,6 +1305,10 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
|
||||||
self.assertEqual(m.group('version'), v_geos)
|
self.assertEqual(m.group('version'), v_geos)
|
||||||
self.assertEqual(m.group('capi_version'), v_capi)
|
self.assertEqual(m.group('capi_version'), v_capi)
|
||||||
|
|
||||||
|
def test_geos_version_tuple(self):
|
||||||
|
with mock.patch('django.contrib.gis.geos.libgeos.geos_version_info', lambda: {'version': '3.4.9'}):
|
||||||
|
self.assertEqual(geos_version_tuple(), (3, 4, 9))
|
||||||
|
|
||||||
def test_from_gml(self):
|
def test_from_gml(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
GEOSGeometry('POINT(0 0)'),
|
GEOSGeometry('POINT(0 0)'),
|
||||||
|
|
|
@ -68,6 +68,12 @@ class TestPostGISVersionCheck(unittest.TestCase):
|
||||||
actual = ops.postgis_version_tuple()
|
actual = ops.postgis_version_tuple()
|
||||||
self.assertEqual(expect, actual)
|
self.assertEqual(expect, actual)
|
||||||
|
|
||||||
|
def test_version_loose_tuple(self):
|
||||||
|
expect = ('1.2.3b1.dev0', 1, 2, 3)
|
||||||
|
ops = FakePostGISOperations(expect[0])
|
||||||
|
actual = ops.postgis_version_tuple()
|
||||||
|
self.assertEqual(expect, actual)
|
||||||
|
|
||||||
def test_valid_version_numbers(self):
|
def test_valid_version_numbers(self):
|
||||||
versions = [
|
versions = [
|
||||||
('1.3.0', 1, 3, 0),
|
('1.3.0', 1, 3, 0),
|
||||||
|
@ -81,15 +87,6 @@ class TestPostGISVersionCheck(unittest.TestCase):
|
||||||
actual = ops.spatial_version
|
actual = ops.spatial_version
|
||||||
self.assertEqual(version[1:], actual)
|
self.assertEqual(version[1:], actual)
|
||||||
|
|
||||||
def test_invalid_version_numbers(self):
|
|
||||||
versions = ['nope', '123']
|
|
||||||
|
|
||||||
for version in versions:
|
|
||||||
with self.subTest(version=version):
|
|
||||||
ops = FakePostGISOperations(version)
|
|
||||||
with self.assertRaises(Exception):
|
|
||||||
ops.spatial_version
|
|
||||||
|
|
||||||
def test_no_version_number(self):
|
def test_no_version_number(self):
|
||||||
ops = FakePostGISOperations()
|
ops = FakePostGISOperations()
|
||||||
with self.assertRaises(ImproperlyConfigured):
|
with self.assertRaises(ImproperlyConfigured):
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django import get_version
|
from django import get_version
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
|
from django.utils.version import get_version_tuple
|
||||||
|
|
||||||
|
|
||||||
class VersionTests(SimpleTestCase):
|
class VersionTests(SimpleTestCase):
|
||||||
|
@ -22,3 +23,8 @@ class VersionTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
for ver_tuple, ver_string in tuples_to_strings:
|
for ver_tuple, ver_string in tuples_to_strings:
|
||||||
self.assertEqual(get_version(ver_tuple), ver_string)
|
self.assertEqual(get_version(ver_tuple), ver_string)
|
||||||
|
|
||||||
|
def test_get_version_tuple(self):
|
||||||
|
self.assertEqual(get_version_tuple('1.2.3'), (1, 2, 3))
|
||||||
|
self.assertEqual(get_version_tuple('1.2.3b2'), (1, 2, 3))
|
||||||
|
self.assertEqual(get_version_tuple('1.2.3b2.dev0'), (1, 2, 3))
|
||||||
|
|
Loading…
Reference in New Issue