Fixed #27830 -- Used distutils.version.LooseVersion for version parsing.

This commit is contained in:
chillaranand 2017-02-15 21:44:21 +05:30 committed by Tim Graham
parent e7dc39fb65
commit 08bda82c23
10 changed files with 52 additions and 45 deletions

View File

@ -11,6 +11,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.db.backends.postgresql.operations import DatabaseOperations
from django.db.utils import ProgrammingError
from django.utils.functional import cached_property
from django.utils.version import get_version_tuple
from .adapter import PostGISAdapter
from .models import PostGISGeometryColumns, PostGISSpatialRefSys
@ -109,7 +110,6 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations):
postgis = True
geography = True
geom_func_prefix = 'ST_'
version_regex = re.compile(r'^(?P<major>\d)\.(?P<minor1>\d)\.(?P<minor2>\d+)')
Adapter = PostGISAdapter
@ -353,18 +353,8 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations):
Return the PostGIS version as a tuple (version string, major,
minor, subminor).
"""
# Getting the PostGIS version
version = self.postgis_lib_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 PostGIS version string: %s' % version)
return (version, major, minor1, minor2)
return (version,) + get_version_tuple(version)
def proj_version_tuple(self):
"""

View File

@ -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://www.gaia-gis.it/gaia-sins/spatialite-sql-4.2.1.html
"""
import re
from django.contrib.gis.db.backends.base.operations import (
BaseSpatialOperations,
)
@ -16,6 +14,7 @@ from django.contrib.gis.measure import Distance
from django.core.exceptions import ImproperlyConfigured
from django.db.backends.sqlite3.operations import DatabaseOperations
from django.utils.functional import cached_property
from django.utils.version import get_version_tuple
class SpatiaLiteDistanceOperator(SpatialOperator):
@ -35,7 +34,6 @@ class SpatiaLiteDistanceOperator(SpatialOperator):
class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
name = 'spatialite'
spatialite = True
version_regex = re.compile(r'^(?P<major>\d)\.(?P<minor1>\d)\.(?P<minor2>\d+)')
Adapter = SpatiaLiteAdapter
@ -189,16 +187,7 @@ class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
minor, subminor).
"""
version = self.spatialite_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)
return (version,) + get_version_tuple(version)
def spatial_aggregate_name(self, agg_name):
"""

View File

@ -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.error import GEOSException
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.point import Point
from django.contrib.gis.geos.polygon import Polygon
@ -115,7 +115,7 @@ class MultiLineString(LinearGeometryMixin, GeometryCollection):
@property
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.")
return super().closed

View File

@ -15,6 +15,7 @@ from ctypes.util import find_library
from django.contrib.gis.geos.error import GEOSException
from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import SimpleLazyObject
from django.utils.version import get_version_tuple
logger = logging.getLogger('django.contrib.gis')
@ -199,3 +200,8 @@ def geos_version_info():
raise GEOSException('Could not parse version info string "%s"' % ver)
return {key: m.group(key) for key in (
'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'])

View File

@ -14,6 +14,7 @@ from django.db.backends.base.base import BaseDatabaseWrapper
from django.db.utils import DatabaseError as WrappedDatabaseError
from django.utils.functional import cached_property
from django.utils.safestring import SafeText
from django.utils.version import get_version_tuple
try:
import psycopg2 as Database
@ -25,7 +26,7 @@ except ImportError as e:
def psycopg2_version():
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()

View File

@ -2,6 +2,7 @@ import datetime
import functools
import os
import subprocess
from distutils.version import LooseVersion
def get_version(version=None):
@ -77,3 +78,17 @@ def get_git_changeset():
except ValueError:
return None
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)

View File

@ -323,13 +323,12 @@ class PostgreSQLTests(TestCase):
def test_correct_extraction_psycopg2_version(self):
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'):
self.assertEqual(psycopg2_version(), (2, 6, 9))
with mock.patch('psycopg2.__version__', '4.2.1 (dt dec pq3 ext lo64)'):
self.assertEqual(psycopg2_version(), (4, 2, 1))
with mock.patch(version_path, '2.5.dev0'):
self.assertEqual(psycopg2_version(), (2, 5))
with mock.patch('psycopg2.__version__', '4.2b0.dev1 (dt dec pq3 ext lo64)'):
self.assertEqual(psycopg2_version(), (4, 2))
class DateQuotingTest(TestCase):

View File

@ -12,7 +12,7 @@ from django.contrib.gis.geos import (
MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, fromfile,
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.template import Context
from django.template.engine import Engine
@ -670,11 +670,11 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertFalse(ls_not_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.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."):
MultiLineString().closed
@ -1305,6 +1305,10 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
self.assertEqual(m.group('version'), v_geos)
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):
self.assertEqual(
GEOSGeometry('POINT(0 0)'),

View File

@ -68,6 +68,12 @@ class TestPostGISVersionCheck(unittest.TestCase):
actual = ops.postgis_version_tuple()
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):
versions = [
('1.3.0', 1, 3, 0),
@ -81,15 +87,6 @@ class TestPostGISVersionCheck(unittest.TestCase):
actual = ops.spatial_version
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):
ops = FakePostGISOperations()
with self.assertRaises(ImproperlyConfigured):

View File

@ -1,5 +1,6 @@
from django import get_version
from django.test import SimpleTestCase
from django.utils.version import get_version_tuple
class VersionTests(SimpleTestCase):
@ -22,3 +23,8 @@ class VersionTests(SimpleTestCase):
)
for ver_tuple, ver_string in tuples_to_strings:
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))