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
#
from distutils.version import LooseVersion
import importlib
import os
import re
@ -12,6 +11,7 @@ import six
from ipaclient.frontend import ClientCommand, ClientMethod
from ipalib.frontend import Object
from ipapython.ipautil import APIVersion
if six.PY3:
unicode = str
@ -58,7 +58,7 @@ def get_package(server_info, client):
server_info['version'] = server_version
server_info.update_validity()
server_version = LooseVersion(server_version)
server_version = APIVersion(server_version)
package_names = {}
base_name = __name__.rpartition('.')[0]
@ -66,15 +66,14 @@ def get_package(server_info, client):
for name in os.listdir(base_dir):
package_dir = os.path.join(base_dir, name)
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_version = None
for version in sorted(package_names, key=LooseVersion):
if (package_version is None or
LooseVersion(package_version) < LooseVersion(version)):
for version in sorted(package_names):
if package_version is None or package_version < version:
package_version = version
if LooseVersion(version) >= server_version:
if version >= server_version:
break
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.
"""
from distutils import version
from ipapython.ipautil import APIVersion
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
"""
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.
"""
from distutils import version
import six
from ipapython.version import API_VERSION
from ipapython.ipautil import APIVersion
from ipapython.ipa_log_manager import root_logger
from ipalib.base import NameSpace
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
then let the request proceed.
"""
server_ver = version.LooseVersion(API_VERSION)
ver = version.LooseVersion(client_version)
if len(ver.version) < 2:
raise VersionError(cver=ver.version, sver=server_ver.version, server= self.env.xmlrpc_uri)
client_major = ver.version[0]
server_apiver = APIVersion(self.api_version)
try:
client_apiver = APIVersion(client_version)
except ValueError:
raise VersionError(cver=client_version,
sver=self.api_version,
server=self.env.xmlrpc_uri)
server_major = server_ver.version[0]
if server_major != client_major:
raise VersionError(cver=client_version, sver=API_VERSION, server=self.env.xmlrpc_uri)
if (client_apiver.major != server_apiver.major
or client_apiver > server_apiver):
raise VersionError(cver=client_version,
sver=self.api_version,
server=self.env.xmlrpc_uri)
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
http://docs.python.org/ref/sequence-types.html
"""
from distutils.version import LooseVersion
import operator
import sys
import threading
@ -725,8 +723,11 @@ class API(ReadOnly):
except KeyError:
pass
else:
version = LooseVersion(plugin.version)
default_version = LooseVersion(default_version)
# Technicall plugin.version is not an API version. The
# 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:
continue
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)
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 pwd
import socket
import dns.name
from ipaserver.install import service
@ -39,7 +40,6 @@ from ipaserver.install import replication
from ipaserver.install import ldapupdate
from ipaserver.install import certs
from distutils import version
from ipaplatform.constants import constants
from ipaplatform.tasks import tasks
from ipaplatform.paths import paths
@ -292,8 +292,8 @@ class KrbInstance(service.Service):
raiseonerr=False, capture_output=True)
if result.returncode == 0:
verstr = result.output.split()[-1]
ver = version.LooseVersion(verstr)
min = version.LooseVersion(MIN_KRB5KDC_WITH_WORKERS)
ver = tasks.parse_ipa_version(verstr)
min = tasks.parse_ipa_version(MIN_KRB5KDC_WITH_WORKERS)
if ver >= min:
workers = True
# Write down config file

View File

@ -4,7 +4,6 @@
from __future__ import print_function
from distutils.version import LooseVersion
import dns.exception as dnsexception
import dns.name as dnsname
import dns.resolver as dnsresolver
@ -15,6 +14,7 @@ import socket
import tempfile
import traceback
from pkg_resources import parse_version
import six
from ipapython import ipaldap, ipautil, sysrestore
@ -510,12 +510,12 @@ def check_remote_version(api):
finally:
client.disconnect()
remote_version = env['version']
version = api.env.version
if LooseVersion(remote_version) > LooseVersion(version):
remote_version = parse_version(env['version'])
api_version = parse_version(api.env.version)
if remote_version > api_version:
raise RuntimeError(
"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):