Replace LooseVersion

pylint is having a hard time with distutils.version in tox's virtual
envs. virtualenv uses some tricks to provide a virtual distutils
package, pylint can't cope with.

https://github.com/PyCQA/pylint/issues/73 suggests to use pkg_resources
instead. pkg_resources' version parser has some more benefits, e.g. PEP
440 conformity. But pkg_resources.parse_version() is a heavy weight solution
with reduced functionality, e.g. no access to major version.

For API_VERSION and plugin version we can use a much simpler and faster
approach.

https://fedorahosted.org/freeipa/ticket/6468

Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
Reviewed-By: Martin Babinsky <mbabinsk@redhat.com>
This commit is contained in:
Christian Heimes
2016-11-21 10:24:17 +01:00
committed by Martin Babinsky
parent 526bcea705
commit 2cbaf15604
7 changed files with 67 additions and 34 deletions

View File

@@ -2,7 +2,6 @@
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license # Copyright (C) 2016 FreeIPA Contributors see COPYING for license
# #
from distutils.version import LooseVersion
import importlib import importlib
import os import os
import re import re
@@ -12,6 +11,7 @@ import six
from ipaclient.frontend import ClientCommand, ClientMethod from ipaclient.frontend import ClientCommand, ClientMethod
from ipalib.frontend import Object from ipalib.frontend import Object
from ipapython.ipautil import APIVersion
if six.PY3: if six.PY3:
unicode = str unicode = str
@@ -58,7 +58,7 @@ def get_package(server_info, client):
server_info['version'] = server_version server_info['version'] = server_version
server_info.update_validity() server_info.update_validity()
server_version = LooseVersion(server_version) server_version = APIVersion(server_version)
package_names = {} package_names = {}
base_name = __name__.rpartition('.')[0] base_name = __name__.rpartition('.')[0]
@@ -66,15 +66,14 @@ def get_package(server_info, client):
for name in os.listdir(base_dir): for name in os.listdir(base_dir):
package_dir = os.path.join(base_dir, name) package_dir = os.path.join(base_dir, name)
if name.startswith('2_') and os.path.isdir(package_dir): if name.startswith('2_') and os.path.isdir(package_dir):
package_version = name.replace('_', '.') package_version = APIVersion(name.replace('_', '.'))
package_names[package_version] = '{}.{}'.format(base_name, name) package_names[package_version] = '{}.{}'.format(base_name, name)
package_version = None package_version = None
for version in sorted(package_names, key=LooseVersion): for version in sorted(package_names):
if (package_version is None or if package_version is None or package_version < version:
LooseVersion(package_version) < LooseVersion(version)):
package_version = version package_version = version
if LooseVersion(version) >= server_version: if version >= server_version:
break break
package_name = package_names[package_version] package_name = package_names[package_version]

View File

@@ -25,7 +25,7 @@ This module defines a dict, ``capabilities``, that maps feature names to API
versions they were introduced in. versions they were introduced in.
""" """
from distutils import version from ipapython.ipautil import APIVersion
VERSION_WITHOUT_CAPABILITIES = u'2.51' VERSION_WITHOUT_CAPABILITIES = u'2.51'
@@ -64,6 +64,6 @@ def client_has_capability(client_version, capability):
:param client_version: The API version string reported by the client :param client_version: The API version string reported by the client
""" """
version_tuple = version.LooseVersion(client_version) version = APIVersion(client_version)
return version_tuple >= version.LooseVersion(capabilities[capability]) return version >= APIVersion(capabilities[capability])

View File

@@ -20,12 +20,10 @@
""" """
Base classes for all front-end plugins. Base classes for all front-end plugins.
""" """
from distutils import version
import six import six
from ipapython.version import API_VERSION from ipapython.version import API_VERSION
from ipapython.ipautil import APIVersion
from ipapython.ipa_log_manager import root_logger from ipapython.ipa_log_manager import root_logger
from ipalib.base import NameSpace from ipalib.base import NameSpace
from ipalib.plugable import Plugin, APINameSpace from ipalib.plugable import Plugin, APINameSpace
@@ -770,16 +768,19 @@ class Command(HasParam):
If the client minor version is less than or equal to the server If the client minor version is less than or equal to the server
then let the request proceed. then let the request proceed.
""" """
server_ver = version.LooseVersion(API_VERSION) server_apiver = APIVersion(self.api_version)
ver = version.LooseVersion(client_version) try:
if len(ver.version) < 2: client_apiver = APIVersion(client_version)
raise VersionError(cver=ver.version, sver=server_ver.version, server= self.env.xmlrpc_uri) except ValueError:
client_major = ver.version[0] raise VersionError(cver=client_version,
sver=self.api_version,
server=self.env.xmlrpc_uri)
server_major = server_ver.version[0] if (client_apiver.major != server_apiver.major
or client_apiver > server_apiver):
if server_major != client_major: raise VersionError(cver=client_version,
raise VersionError(cver=client_version, sver=API_VERSION, server=self.env.xmlrpc_uri) sver=self.api_version,
server=self.env.xmlrpc_uri)
def run(self, *args, **options): def run(self, *args, **options):
""" """

View File

@@ -24,8 +24,6 @@ The classes in this module make heavy use of Python container emulation. If
you are unfamiliar with this Python feature, see you are unfamiliar with this Python feature, see
http://docs.python.org/ref/sequence-types.html http://docs.python.org/ref/sequence-types.html
""" """
from distutils.version import LooseVersion
import operator import operator
import sys import sys
import threading import threading
@@ -725,8 +723,11 @@ class API(ReadOnly):
except KeyError: except KeyError:
pass pass
else: else:
version = LooseVersion(plugin.version) # Technicall plugin.version is not an API version. The
default_version = LooseVersion(default_version) # APIVersion class can handle plugin versions. It's more
# lean than pkg_resource.parse_version().
version = ipautil.APIVersion(plugin.version)
default_version = ipautil.APIVersion(default_version)
if version < default_version: if version < default_version:
continue continue
self.__default_map[plugin.name] = plugin.version self.__default_map[plugin.name] = plugin.version

View File

@@ -1533,3 +1533,35 @@ def escape_seq(seq, *args):
""" """
return tuple(a.replace(seq, u'\\{}'.format(seq)) for a in args) return tuple(a.replace(seq, u'\\{}'.format(seq)) for a in args)
class APIVersion(tuple):
"""API version parser and handler
The class is used to parse ipapython.version.API_VERSION and plugin
versions.
"""
__slots__ = ()
def __new__(cls, version):
major, dot, minor = version.partition(u'.')
major = int(major)
minor = int(minor) if dot else 0
return tuple.__new__(cls, (major, minor))
def __str__(self):
return '{}.{}'.format(*self)
def __repr__(self):
return "<APIVersion('{}.{}')>".format(*self)
def __getnewargs__(self):
return str(self)
@property
def major(self):
return self[0]
@property
def minor(self):
return self[1]

View File

@@ -24,6 +24,7 @@ import shutil
import os import os
import pwd import pwd
import socket import socket
import dns.name import dns.name
from ipaserver.install import service from ipaserver.install import service
@@ -39,7 +40,6 @@ from ipaserver.install import replication
from ipaserver.install import ldapupdate from ipaserver.install import ldapupdate
from ipaserver.install import certs from ipaserver.install import certs
from distutils import version
from ipaplatform.constants import constants from ipaplatform.constants import constants
from ipaplatform.tasks import tasks from ipaplatform.tasks import tasks
from ipaplatform.paths import paths from ipaplatform.paths import paths
@@ -292,8 +292,8 @@ class KrbInstance(service.Service):
raiseonerr=False, capture_output=True) raiseonerr=False, capture_output=True)
if result.returncode == 0: if result.returncode == 0:
verstr = result.output.split()[-1] verstr = result.output.split()[-1]
ver = version.LooseVersion(verstr) ver = tasks.parse_ipa_version(verstr)
min = version.LooseVersion(MIN_KRB5KDC_WITH_WORKERS) min = tasks.parse_ipa_version(MIN_KRB5KDC_WITH_WORKERS)
if ver >= min: if ver >= min:
workers = True workers = True
# Write down config file # Write down config file

View File

@@ -4,7 +4,6 @@
from __future__ import print_function from __future__ import print_function
from distutils.version import LooseVersion
import dns.exception as dnsexception import dns.exception as dnsexception
import dns.name as dnsname import dns.name as dnsname
import dns.resolver as dnsresolver import dns.resolver as dnsresolver
@@ -15,6 +14,7 @@ import socket
import tempfile import tempfile
import traceback import traceback
from pkg_resources import parse_version
import six import six
from ipapython import ipaldap, ipautil, sysrestore from ipapython import ipaldap, ipautil, sysrestore
@@ -510,12 +510,12 @@ def check_remote_version(api):
finally: finally:
client.disconnect() client.disconnect()
remote_version = env['version'] remote_version = parse_version(env['version'])
version = api.env.version api_version = parse_version(api.env.version)
if LooseVersion(remote_version) > LooseVersion(version): if remote_version > api_version:
raise RuntimeError( raise RuntimeError(
"Cannot install replica of a server of higher version ({}) than" "Cannot install replica of a server of higher version ({}) than"
"the local version ({})".format(remote_version, version)) "the local version ({})".format(remote_version, api_version))
def common_check(no_ntp): def common_check(no_ntp):