From: Ralph Bean <rbean@redhat.com>
Date: Mon, 20 Nov 2023 08:25:21 -0500
Subject: Update for python-3.12

Also, drop testing for some older versions and adopt testing against
newer taskwarrior.
---
 requirements.txt                   |  1 +
 taskw/fields/commaseparateduuid.py |  6 +++---
 taskw/utils.py                     |  4 ++--
 taskw/warrior.py                   | 16 ++++++++--------
 4 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/requirements.txt b/requirements.txt
index b6f1dac..9a5ed8d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
 python-dateutil
 pytz
 kitchen
+packaging
diff --git a/taskw/fields/commaseparateduuid.py b/taskw/fields/commaseparateduuid.py
index 92838c5..d7ec4c0 100644
--- a/taskw/fields/commaseparateduuid.py
+++ b/taskw/fields/commaseparateduuid.py
@@ -1,4 +1,4 @@
-from distutils.version import LooseVersion
+from packaging.version import Version
 
 import uuid
 
@@ -6,7 +6,7 @@ from .base import DirtyableList, Field
 
 
 class CommaSeparatedUUIDField(Field):
-    version = LooseVersion('2.4')
+    version = Version('2.4')
 
     def deserialize(self, value):
         if not value:
@@ -27,7 +27,7 @@ class CommaSeparatedUUIDField(Field):
         if not hasattr(value, '__iter__'):
             raise ValueError("Value must be list or tuple, not %r." % value)
 
-        if self.version < LooseVersion('2.5'):
+        if self.version < Version('2.5'):
             return ','.join([str(v) for v in value])
         else:
             # We never hit this second code branch now.  taskwarrior changed
diff --git a/taskw/utils.py b/taskw/utils.py
index 73b3f8c..8837d2b 100644
--- a/taskw/utils.py
+++ b/taskw/utils.py
@@ -8,7 +8,7 @@ from operator import itemgetter
 import dateutil.tz
 import pytz
 
-from distutils.version import LooseVersion
+from packaging.version import Version
 
 
 DATE_FORMAT = '%Y%m%dT%H%M%SZ'
@@ -91,7 +91,7 @@ def encode_query(value, version, query=True):
                 ])
             )
         else:
-            if k.endswith(".is") and version >= LooseVersion('2.4'):
+            if k.endswith(".is") and version >= Version('2.4'):
                 args.append(
                     '%s == "%s"' % (
                         k[:-3],
diff --git a/taskw/warrior.py b/taskw/warrior.py
index 915e5e3..99eb2eb 100644
--- a/taskw/warrior.py
+++ b/taskw/warrior.py
@@ -11,7 +11,7 @@ fall back to the older TaskWarriorDirect implementation.
 """
 import abc
 import copy
-from distutils.version import LooseVersion
+from packaging.version import Version
 import logging
 import os
 import re
@@ -432,13 +432,13 @@ class TaskWarriorShellout(TaskWarriorBase):
         self._marshal = marshal
         self.config = TaskRc(config_filename, overrides=config_overrides)
 
-        if self.get_version() >= LooseVersion('2.4'):
+        if self.get_version() >= Version('2.4'):
             self.DEFAULT_CONFIG_OVERRIDES['verbose'] = 'new-uuid'
         # Combination of
         # https://github.com/GothenburgBitFactory/taskwarrior/issues/1953
         # and dictionaries random order may cause task add failures in
         # Python versions before 3.7
-        if (self.get_version() >= LooseVersion('2.5.3') and
+        if (self.get_version() >= Version('2.5.3') and
                 sys.hexversion < 0x03070000):
             warnings.once(
                 "Python < 3.7 with TaskWarrior => 2.5.3 is not suppoprted. "
@@ -553,7 +553,7 @@ class TaskWarriorShellout(TaskWarriorBase):
         """ Returns true if runtime requirements of experimental mode are met
         """
         try:
-            return cls.get_version() > LooseVersion('2')
+            return cls.get_version() > Version('2')
         except FileNotFoundError:
             # FileNotFound is raised if subprocess.Popen fails to find
             # the executable.
@@ -570,10 +570,10 @@ class TaskWarriorShellout(TaskWarriorBase):
             raise FileNotFoundError(
                 "Unable to find the 'task' command-line tool."
             )
-        return LooseVersion(taskwarrior_version.decode())
+        return Version(taskwarrior_version.decode())
 
     def sync(self, init=False):
-        if self.get_version() < LooseVersion('2.3'):
+        if self.get_version() < Version('2.3'):
             raise UnsupportedVersionException(
                 "'sync' requires version 2.3 of taskwarrior or later."
             )
@@ -682,7 +682,7 @@ class TaskWarriorShellout(TaskWarriorBase):
 
         # With older versions of taskwarrior, you can specify whatever uuid you
         # want when adding a task.
-        if self.get_version() < LooseVersion('2.4'):
+        if self.get_version() < Version('2.4'):
             task['uuid'] = str(uuid.uuid4())
         elif 'uuid' in task:
             del task['uuid']
@@ -697,7 +697,7 @@ class TaskWarriorShellout(TaskWarriorBase):
         # However, in 2.4 and later, you cannot specify whatever uuid you want
         # when adding a task.  Instead, you have to specify rc.verbose=new-uuid
         # and then parse the assigned uuid out from stdout.
-        if self.get_version() >= LooseVersion('2.4'):
+        if self.get_version() >= Version('2.4'):
             task['uuid'] = re.search(UUID_REGEX, stdout).group(0)
 
         id, added_task = self.get_task(uuid=task['uuid'])
