diff --git a/doc/changelog/1.X.X-changelog.rst b/doc/changelog/1.X.X-changelog.rst
index 03caebaa27..00695e64f1 100644
--- a/doc/changelog/1.X.X-changelog.rst
+++ b/doc/changelog/1.X.X-changelog.rst
@@ -1,3 +1,13 @@
+1.8.1 (May 16, 2022)
+====================
+
+Bug-fix release in the 1.8.x series.
+
+The previous release vendored ``distutils.version.LooseVersion``, and the vendored objects did not
+preserve compatiblity with the ``distutils`` objects. This release switches to the
+``looseversion`` package that ensures compatiblity.
+
+
1.8.0 (May 10, 2022)
====================
diff --git a/doc/interfaces.rst b/doc/interfaces.rst
index 7f8bbf1e35..c5edd658f9 100644
--- a/doc/interfaces.rst
+++ b/doc/interfaces.rst
@@ -8,7 +8,7 @@ Interfaces and Workflows
:Release: |version|
:Date: |today|
-Previous versions: `1.7.1 `_ `1.7.0 `_
+Previous versions: `1.8.0 `_ `1.7.1 `_
Workflows
---------
diff --git a/nipype/__init__.py b/nipype/__init__.py
index bfc1e16a5b..bf6968a95a 100644
--- a/nipype/__init__.py
+++ b/nipype/__init__.py
@@ -14,7 +14,7 @@
import os
# XXX Deprecate this import
-from .external.version import LooseVersion
+from looseversion import LooseVersion
from .info import URL as __url__, STATUS as __status__, __version__
from .utils.config import NipypeConfig
diff --git a/nipype/external/version.py b/nipype/external/version.py
deleted file mode 100644
index 0a2fbf167e..0000000000
--- a/nipype/external/version.py
+++ /dev/null
@@ -1,224 +0,0 @@
-# This module has been vendored from CPython distutils/version.py
-# last updated in 662db125cddbca1db68116c547c290eb3943d98e
-#
-# It is licensed according to the Python Software Foundation License Version 2
-# which may be found in full in the following (hopefully persistent) locations:
-#
-# https://github.com/python/cpython/blob/main/LICENSE
-# https://spdx.org/licenses/Python-2.0.html
-#
-# The following changes have been made:
-#
-# 2022.04.27 - Minor changes are made to the comments,
-# - The StrictVersion class was removed
-# - Black styling was applied
-#
-
-# distutils/version.py
-#
-# Implements multiple version numbering conventions for the
-# Python Module Distribution Utilities.
-
-"""Provides classes to represent module version numbers (one class for
-each style of version numbering). There are currently two such classes
-implemented: StrictVersion and LooseVersion.
-
-Every version number class implements the following interface:
- * the 'parse' method takes a string and parses it to some internal
- representation; if the string is an invalid version number,
- 'parse' raises a ValueError exception
- * the class constructor takes an optional string argument which,
- if supplied, is passed to 'parse'
- * __str__ reconstructs the string that was passed to 'parse' (or
- an equivalent string -- ie. one that will generate an equivalent
- version number instance)
- * __repr__ generates Python code to recreate the version number instance
- * _cmp compares the current instance with either another instance
- of the same class or a string (which will be parsed to an instance
- of the same class, thus must follow the same rules)
-"""
-
-import re
-
-
-class Version:
- """Abstract base class for version numbering classes. Just provides
- constructor (__init__) and reproducer (__repr__), because those
- seem to be the same for all version numbering classes; and route
- rich comparisons to _cmp.
- """
-
- def __init__(self, vstring=None):
- if vstring:
- self.parse(vstring)
-
- def __repr__(self):
- return "%s ('%s')" % (self.__class__.__name__, str(self))
-
- def __eq__(self, other):
- c = self._cmp(other)
- if c is NotImplemented:
- return c
- return c == 0
-
- def __lt__(self, other):
- c = self._cmp(other)
- if c is NotImplemented:
- return c
- return c < 0
-
- def __le__(self, other):
- c = self._cmp(other)
- if c is NotImplemented:
- return c
- return c <= 0
-
- def __gt__(self, other):
- c = self._cmp(other)
- if c is NotImplemented:
- return c
- return c > 0
-
- def __ge__(self, other):
- c = self._cmp(other)
- if c is NotImplemented:
- return c
- return c >= 0
-
-
-# The rules according to Greg Stein:
-# 1) a version number has 1 or more numbers separated by a period or by
-# sequences of letters. If only periods, then these are compared
-# left-to-right to determine an ordering.
-# 2) sequences of letters are part of the tuple for comparison and are
-# compared lexicographically
-# 3) recognize the numeric components may have leading zeroes
-#
-# The LooseVersion class below implements these rules: a version number
-# string is split up into a tuple of integer and string components, and
-# comparison is a simple tuple comparison. This means that version
-# numbers behave in a predictable and obvious way, but a way that might
-# not necessarily be how people *want* version numbers to behave. There
-# wouldn't be a problem if people could stick to purely numeric version
-# numbers: just split on period and compare the numbers as tuples.
-# However, people insist on putting letters into their version numbers;
-# the most common purpose seems to be:
-# - indicating a "pre-release" version
-# ('alpha', 'beta', 'a', 'b', 'pre', 'p')
-# - indicating a post-release patch ('p', 'pl', 'patch')
-# but of course this can't cover all version number schemes, and there's
-# no way to know what a programmer means without asking him.
-#
-# The problem is what to do with letters (and other non-numeric
-# characters) in a version number. The current implementation does the
-# obvious and predictable thing: keep them as strings and compare
-# lexically within a tuple comparison. This has the desired effect if
-# an appended letter sequence implies something "post-release":
-# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
-#
-# However, if letters in a version number imply a pre-release version,
-# the "obvious" thing isn't correct. Eg. you would expect that
-# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
-# implemented here, this just isn't so.
-#
-# Two possible solutions come to mind. The first is to tie the
-# comparison algorithm to a particular set of semantic rules, as has
-# been done in the StrictVersion class above. This works great as long
-# as everyone can go along with bondage and discipline. Hopefully a
-# (large) subset of Python module programmers will agree that the
-# particular flavour of bondage and discipline provided by StrictVersion
-# provides enough benefit to be worth using, and will submit their
-# version numbering scheme to its domination. The free-thinking
-# anarchists in the lot will never give in, though, and something needs
-# to be done to accommodate them.
-#
-# Perhaps a "moderately strict" version class could be implemented that
-# lets almost anything slide (syntactically), and makes some heuristic
-# assumptions about non-digits in version number strings. This could
-# sink into special-case-hell, though; if I was as talented and
-# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
-# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
-# just as happy dealing with things like "2g6" and "1.13++". I don't
-# think I'm smart enough to do it right though.
-#
-# In any case, I've coded the test suite for this module (see
-# ../test/test_version.py) specifically to fail on things like comparing
-# "1.2a2" and "1.2". That's not because the *code* is doing anything
-# wrong, it's because the simple, obvious design doesn't match my
-# complicated, hairy expectations for real-world version numbers. It
-# would be a snap to fix the test suite to say, "Yep, LooseVersion does
-# the Right Thing" (ie. the code matches the conception). But I'd rather
-# have a conception that matches common notions about version numbers.
-
-
-class LooseVersion(Version):
-
- """Version numbering for anarchists and software realists.
- Implements the standard interface for version number classes as
- described above. A version number consists of a series of numbers,
- separated by either periods or strings of letters. When comparing
- version numbers, the numeric components will be compared
- numerically, and the alphabetic components lexically. The following
- are all valid version numbers, in no particular order:
-
- 1.5.1
- 1.5.2b2
- 161
- 3.10a
- 8.02
- 3.4j
- 1996.07.12
- 3.2.pl0
- 3.1.1.6
- 2g6
- 11g
- 0.960923
- 2.2beta29
- 1.13++
- 5.5.kw
- 2.0b1pl0
-
- In fact, there is no such thing as an invalid version number under
- this scheme; the rules for comparison are simple and predictable,
- but may not always give the results you want (for some definition
- of "want").
- """
-
- component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
-
- def __init__(self, vstring=None):
- if vstring:
- self.parse(vstring)
-
- def parse(self, vstring):
- # I've given up on thinking I can reconstruct the version string
- # from the parsed tuple -- so I just store the string here for
- # use by __str__
- self.vstring = vstring
- components = [x for x in self.component_re.split(vstring) if x and x != '.']
- for i, obj in enumerate(components):
- try:
- components[i] = int(obj)
- except ValueError:
- pass
-
- self.version = components
-
- def __str__(self):
- return self.vstring
-
- def __repr__(self):
- return "LooseVersion ('%s')" % str(self)
-
- def _cmp(self, other):
- if isinstance(other, str):
- other = LooseVersion(other)
- elif not isinstance(other, LooseVersion):
- return NotImplemented
-
- if self.version == other.version:
- return 0
- if self.version < other.version:
- return -1
- if self.version > other.version:
- return 1
diff --git a/nipype/info.py b/nipype/info.py
index 4fe067b9b6..f60f774e4a 100644
--- a/nipype/info.py
+++ b/nipype/info.py
@@ -5,7 +5,7 @@
# nipype version information
# Remove -dev for release
-__version__ = "1.8.0"
+__version__ = "1.8.1"
def get_nipype_gitversion():
@@ -146,6 +146,7 @@ def get_nipype_gitversion():
"traits>=%s,!=5.0" % TRAITS_MIN_VERSION,
"filelock>=3.0.0",
"etelemetry>=0.2.0",
+ "looseversion",
]
TESTS_REQUIRES = [
diff --git a/nipype/interfaces/dipy/preprocess.py b/nipype/interfaces/dipy/preprocess.py
index d4271b6159..867ba79d81 100644
--- a/nipype/interfaces/dipy/preprocess.py
+++ b/nipype/interfaces/dipy/preprocess.py
@@ -4,7 +4,7 @@
import nibabel as nb
import numpy as np
-from nipype.external.version import LooseVersion
+from looseversion import LooseVersion
from ... import logging
from ..base import traits, TraitedSpec, File, isdefined
from .base import (
diff --git a/nipype/interfaces/dipy/reconstruction.py b/nipype/interfaces/dipy/reconstruction.py
index cef7579772..14a2dff462 100644
--- a/nipype/interfaces/dipy/reconstruction.py
+++ b/nipype/interfaces/dipy/reconstruction.py
@@ -7,7 +7,7 @@
import numpy as np
import nibabel as nb
-from nipype.external.version import LooseVersion
+from looseversion import LooseVersion
from ... import logging
from ..base import TraitedSpec, File, traits, isdefined
diff --git a/nipype/interfaces/dipy/registration.py b/nipype/interfaces/dipy/registration.py
index e07859560d..b9b818a66a 100644
--- a/nipype/interfaces/dipy/registration.py
+++ b/nipype/interfaces/dipy/registration.py
@@ -1,4 +1,4 @@
-from nipype.external.version import LooseVersion
+from looseversion import LooseVersion
from ... import logging
from .base import HAVE_DIPY, dipy_version, dipy_to_nipype_interface, get_dipy_workflows
diff --git a/nipype/interfaces/dipy/stats.py b/nipype/interfaces/dipy/stats.py
index 971857b64e..f2de24ca33 100644
--- a/nipype/interfaces/dipy/stats.py
+++ b/nipype/interfaces/dipy/stats.py
@@ -1,4 +1,4 @@
-from nipype.external.version import LooseVersion
+from looseversion import LooseVersion
from ... import logging
from .base import HAVE_DIPY, dipy_version, dipy_to_nipype_interface, get_dipy_workflows
diff --git a/nipype/interfaces/dipy/tracks.py b/nipype/interfaces/dipy/tracks.py
index 6b1da93a95..e97250dd26 100644
--- a/nipype/interfaces/dipy/tracks.py
+++ b/nipype/interfaces/dipy/tracks.py
@@ -3,7 +3,7 @@
import os.path as op
import numpy as np
import nibabel as nb
-from nipype.external.version import LooseVersion
+from looseversion import LooseVersion
from ... import logging
from ..base import TraitedSpec, BaseInterfaceInputSpec, File, isdefined, traits
diff --git a/nipype/utils/config.py b/nipype/utils/config.py
index 3106bd4c8c..9c7505455d 100644
--- a/nipype/utils/config.py
+++ b/nipype/utils/config.py
@@ -14,7 +14,7 @@
import errno
import atexit
from warnings import warn
-from nipype.external.version import LooseVersion
+from looseversion import LooseVersion
import configparser
import numpy as np
diff --git a/nipype/utils/misc.py b/nipype/utils/misc.py
index ba8687110c..11aa9ea859 100644
--- a/nipype/utils/misc.py
+++ b/nipype/utils/misc.py
@@ -9,7 +9,7 @@
from collections.abc import Iterator
from warnings import warn
-from nipype.external.version import LooseVersion
+from looseversion import LooseVersion
import numpy as np