From f6a62c66ed237274d63187468729278259d13bb4 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 14 Sep 2022 03:42:00 -0400 Subject: [PATCH 1/4] Use packaging.version instead of distutils Version classes Resolves the following warning: /usr/local/lib/python3.10/dist-packages/horovod/tensorflow/elastic.py:28: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead. Signed-off-by: Jinzhe Zeng --- horovod/tensorflow/elastic.py | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/horovod/tensorflow/elastic.py b/horovod/tensorflow/elastic.py index 7153216c4f..4bfd9f248a 100644 --- a/horovod/tensorflow/elastic.py +++ b/horovod/tensorflow/elastic.py @@ -13,7 +13,7 @@ # limitations under the License. # ============================================================================== -from distutils.version import LooseVersion +from packaging.version import parse import tensorflow as tf @@ -25,7 +25,7 @@ from horovod.tensorflow.mpi_ops import _executing_eagerly, init, rank, shutdown -_IS_TF2 = LooseVersion(tf.__version__) >= LooseVersion('2.0.0') +_IS_TF2 = parse(tf.__version__) >= parse('2.0.0') def run(func): diff --git a/setup.py b/setup.py index dca03780e0..24d3705f19 100644 --- a/setup.py +++ b/setup.py @@ -154,7 +154,7 @@ def build_extensions(self): # python packages required to use horovod in general -require_list = ['cloudpickle', 'psutil', 'pyyaml', 'dataclasses;python_version<"3.7"'] +require_list = ['cloudpickle', 'psutil', 'pyyaml', 'dataclasses;python_version<"3.7"', 'packaging'] # framework dependencies tensorflow_require_list = ['tensorflow'] From 8cd6452f5b5db38c12577f7423d216bc84285aa8 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 14 Sep 2022 05:50:49 -0400 Subject: [PATCH 2/4] import `version` instead of `parse` Signed-off-by: Jinzhe Zeng --- horovod/tensorflow/elastic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/horovod/tensorflow/elastic.py b/horovod/tensorflow/elastic.py index 4bfd9f248a..bebcb07501 100644 --- a/horovod/tensorflow/elastic.py +++ b/horovod/tensorflow/elastic.py @@ -13,7 +13,7 @@ # limitations under the License. # ============================================================================== -from packaging.version import parse +from packaging import version import tensorflow as tf @@ -25,7 +25,7 @@ from horovod.tensorflow.mpi_ops import _executing_eagerly, init, rank, shutdown -_IS_TF2 = parse(tf.__version__) >= parse('2.0.0') +_IS_TF2 = version.parse(tf.__version__) >= version.parse('2.0.0') def run(func): From 29ad6d06225bd71a2be49eb30af8fb64ffaedd7d Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 14 Sep 2022 06:23:10 -0400 Subject: [PATCH 3/4] conduct replacements for all of `distutils.version` Signed-off-by: Jinzhe Zeng --- docs/conf.py | 2 +- .../tensorflow2/tensorflow2_keras_mnist_elastic.py | 4 ++-- examples/pytorch/pytorch_mnist.py | 6 +++--- examples/spark/keras/keras_spark_mnist.py | 6 +++--- .../spark/keras/keras_spark_rossmann_estimator.py | 4 ++-- .../spark/pytorch/pytorch_lightning_spark_mnist.py | 10 +++++----- .../pytorch_lightning_spark_mnist_legacy.py | 6 +++--- examples/spark/pytorch/pytorch_spark_mnist.py | 4 ++-- horovod/_keras/__init__.py | 4 ++-- horovod/_keras/callbacks.py | 4 ++-- horovod/common/util.py | 4 ++-- horovod/spark/common/store.py | 8 ++++---- horovod/spark/keras/remote.py | 14 +++++++------- horovod/spark/keras/util.py | 2 +- horovod/spark/lightning/estimator.py | 4 ++-- horovod/spark/lightning/remote.py | 2 +- horovod/spark/task/task_service.py | 4 ++-- horovod/tensorflow/__init__.py | 4 ++-- horovod/tensorflow/gradient_aggregation_eager.py | 4 ++-- horovod/tensorflow/mpi_ops.py | 4 ++-- horovod/torch/sync_batch_norm.py | 10 +++++----- setup.py | 8 ++++---- test/integration/test_spark.py | 10 +++++----- test/integration/test_spark_keras.py | 2 +- test/integration/test_spark_lightning.py | 8 ++++---- test/parallel/base_test_mxnet.py | 4 ++-- test/parallel/test_compute_worker.py | 4 ++-- test/parallel/test_keras.py | 4 ++-- test/parallel/test_mxnet1.py | 6 +++--- test/parallel/test_mxnet2.py | 4 ++-- test/parallel/test_tensorflow.py | 10 +++++----- test/parallel/test_tensorflow2_keras.py | 10 +++++----- .../test_tensorflow2_keras_process_sets.py | 8 ++++---- test/parallel/test_tensorflow_keras.py | 6 +++--- test/parallel/test_tensorflow_process_sets.py | 4 ++-- test/parallel/test_torch.py | 6 +++--- test/parallel/test_xla.py | 4 ++-- test/parallel/test_xla_process_sets.py | 4 ++-- test/single/test_compute_service.py | 4 ++-- 39 files changed, 108 insertions(+), 108 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 228002aff2..b34ce61678 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,7 +62,7 @@ 'members': None, 'member-order': 'bysource', 'imported-members': None, - 'exclude-members': 'contextmanager, LooseVersion, tf, keras, torch, mx, pyspark', + 'exclude-members': 'contextmanager, version.parse, tf, keras, torch, mx, pyspark', } diff --git a/examples/elastic/tensorflow2/tensorflow2_keras_mnist_elastic.py b/examples/elastic/tensorflow2/tensorflow2_keras_mnist_elastic.py index a0ce8d1929..73403c08ba 100644 --- a/examples/elastic/tensorflow2/tensorflow2_keras_mnist_elastic.py +++ b/examples/elastic/tensorflow2/tensorflow2_keras_mnist_elastic.py @@ -16,7 +16,7 @@ import argparse import tensorflow as tf import horovod.tensorflow.keras as hvd -from distutils.version import LooseVersion +from packaging import version parser = argparse.ArgumentParser(description='Tensorflow 2.0 Keras MNIST Example') @@ -26,7 +26,7 @@ args = parser.parse_args() if args.use_mixed_precision: - if LooseVersion(tf.__version__) >= LooseVersion('2.4.0'): + if version.parse(tf.__version__) >= version.parse('2.4.0'): from tensorflow.keras import mixed_precision mixed_precision.set_global_policy('mixed_float16') else: diff --git a/examples/pytorch/pytorch_mnist.py b/examples/pytorch/pytorch_mnist.py index 9ee5a77fe5..7c3e04ee6c 100644 --- a/examples/pytorch/pytorch_mnist.py +++ b/examples/pytorch/pytorch_mnist.py @@ -1,6 +1,6 @@ import argparse import os -from distutils.version import LooseVersion +from packaging import version import torch.multiprocessing as mp import torch.nn as nn @@ -161,8 +161,8 @@ def test(): if args.use_mixed_precision: raise ValueError("Mixed precision is only supported with cuda enabled.") - if (args.use_mixed_precision and LooseVersion(torch.__version__) - < LooseVersion('1.6.0')): + if (args.use_mixed_precision and version.parse(torch.__version__) + < version.parse('1.6.0')): raise ValueError("""Mixed precision is using torch.cuda.amp.autocast(), which requires torch >= 1.6.0""") diff --git a/examples/spark/keras/keras_spark_mnist.py b/examples/spark/keras/keras_spark_mnist.py index 0b017364c9..a97e67ae1b 100644 --- a/examples/spark/keras/keras_spark_mnist.py +++ b/examples/spark/keras/keras_spark_mnist.py @@ -2,7 +2,7 @@ import os import subprocess import sys -from distutils.version import LooseVersion +from packaging import version import numpy as np @@ -10,7 +10,7 @@ import pyspark.sql.types as T from pyspark import SparkConf from pyspark.ml.evaluation import MulticlassClassificationEvaluator -if LooseVersion(pyspark.__version__) < LooseVersion('3.0.0'): +if version.parse(pyspark.__version__) < version.parse('3.0.0'): from pyspark.ml.feature import OneHotEncoderEstimator as OneHotEncoder else: from pyspark.ml.feature import OneHotEncoder @@ -78,7 +78,7 @@ train_df, test_df = train_df.randomSplit([0.9, 0.1]) # Disable GPUs when building the model to prevent memory leaks - if LooseVersion(tf.__version__) >= LooseVersion('2.0.0'): + if version.parse(tf.__version__) >= version.parse('2.0.0'): # See https://github.com/tensorflow/tensorflow/issues/33168 os.environ['CUDA_VISIBLE_DEVICES'] = '-1' else: diff --git a/examples/spark/keras/keras_spark_rossmann_estimator.py b/examples/spark/keras/keras_spark_rossmann_estimator.py index 48e1aa3915..b981167198 100644 --- a/examples/spark/keras/keras_spark_rossmann_estimator.py +++ b/examples/spark/keras/keras_spark_rossmann_estimator.py @@ -18,7 +18,7 @@ import datetime import os import sys -from distutils.version import LooseVersion +from packaging import version import pyspark.sql.types as T import pyspark.sql.functions as F @@ -338,7 +338,7 @@ def act_sigmoid_scaled(x): 'act_sigmoid_scaled': act_sigmoid_scaled} # Disable GPUs when building the model to prevent memory leaks - if LooseVersion(tf.__version__) >= LooseVersion('2.0.0'): + if version.parse(tf.__version__) >= version.parse('2.0.0'): # See https://github.com/tensorflow/tensorflow/issues/33168 os.environ['CUDA_VISIBLE_DEVICES'] = '-1' else: diff --git a/examples/spark/pytorch/pytorch_lightning_spark_mnist.py b/examples/spark/pytorch/pytorch_lightning_spark_mnist.py index 1d1e1a33c2..436df8e4c0 100644 --- a/examples/spark/pytorch/pytorch_lightning_spark_mnist.py +++ b/examples/spark/pytorch/pytorch_lightning_spark_mnist.py @@ -2,7 +2,7 @@ import os import subprocess import sys -from distutils.version import LooseVersion +from packaging import version import numpy as np @@ -10,7 +10,7 @@ import pyspark.sql.types as T from pyspark import SparkConf from pyspark.ml.evaluation import MulticlassClassificationEvaluator -if LooseVersion(pyspark.__version__) < LooseVersion('3.0.0'): +if version.parse(pyspark.__version__) < version.parse('3.0.0'): from pyspark.ml.feature import OneHotEncoderEstimator as OneHotEncoder else: from pyspark.ml.feature import OneHotEncoder @@ -21,8 +21,8 @@ try: # tensorflow has to be imported BEFORE pytorch_lightning, otherwise we see the segfault right away import tensorflow as tf - from distutils.version import LooseVersion - if LooseVersion('2.5.0') <= LooseVersion(tf.__version__) < LooseVersion('2.7.0'): + from packaging import version + if version.parse('2.5.0') <= version.parse(tf.__version__) < version.parse('2.7.0'): print('Skipping test as Pytorch Lightning conflicts with present Tensorflow 2.6.x', file=sys.stderr) sys.exit(0) except ImportError: @@ -61,7 +61,7 @@ def train_model(args): # do not run this test for pytorch lightning below min supported verson import pytorch_lightning as pl - if LooseVersion(pl.__version__) < LooseVersion(MIN_PL_VERSION): + if version.parse(pl.__version__) < version.parse(MIN_PL_VERSION): print("Skip test for pytorch_ligthning=={}, min support version is {}".format(pl.__version__, MIN_PL_VERSION)) return diff --git a/examples/spark/pytorch/pytorch_lightning_spark_mnist_legacy.py b/examples/spark/pytorch/pytorch_lightning_spark_mnist_legacy.py index a6811d1751..856e06ec07 100644 --- a/examples/spark/pytorch/pytorch_lightning_spark_mnist_legacy.py +++ b/examples/spark/pytorch/pytorch_lightning_spark_mnist_legacy.py @@ -2,7 +2,7 @@ import os import subprocess import sys -from distutils.version import LooseVersion +from packaging import version import numpy as np @@ -10,7 +10,7 @@ import pyspark.sql.types as T from pyspark import SparkConf from pyspark.ml.evaluation import MulticlassClassificationEvaluator -if LooseVersion(pyspark.__version__) < LooseVersion('3.0.0'): +if version.parse(pyspark.__version__) < version.parse('3.0.0'): from pyspark.ml.feature import OneHotEncoderEstimator as OneHotEncoder else: from pyspark.ml.feature import OneHotEncoder @@ -45,7 +45,7 @@ def train_model(args): # do not run this test for pytorch lightning below min supported verson import pytorch_lightning as pl - if LooseVersion(pl.__version__) < LooseVersion(MIN_PL_VERSION): + if version.parse(pl.__version__) < version.parse(MIN_PL_VERSION): print("Skip test for pytorch_ligthning=={}, min support version is {}".format(pl.__version__, MIN_PL_VERSION)) return diff --git a/examples/spark/pytorch/pytorch_spark_mnist.py b/examples/spark/pytorch/pytorch_spark_mnist.py index 7791144dbf..a1fe48cb66 100644 --- a/examples/spark/pytorch/pytorch_spark_mnist.py +++ b/examples/spark/pytorch/pytorch_spark_mnist.py @@ -2,7 +2,7 @@ import os import subprocess import sys -from distutils.version import LooseVersion +from packaging import version import numpy as np @@ -10,7 +10,7 @@ import pyspark.sql.types as T from pyspark import SparkConf from pyspark.ml.evaluation import MulticlassClassificationEvaluator -if LooseVersion(pyspark.__version__) < LooseVersion('3.0.0'): +if version.parse(pyspark.__version__) < version.parse('3.0.0'): from pyspark.ml.feature import OneHotEncoderEstimator as OneHotEncoder else: from pyspark.ml.feature import OneHotEncoder diff --git a/horovod/_keras/__init__.py b/horovod/_keras/__init__.py index 1d3c1f3e54..75de16b36a 100644 --- a/horovod/_keras/__init__.py +++ b/horovod/_keras/__init__.py @@ -13,7 +13,7 @@ # limitations under the License. # ============================================================================== -from distutils.version import LooseVersion +from packaging import version import horovod.tensorflow as hvd import tensorflow as tf @@ -22,7 +22,7 @@ from horovod.tensorflow.mpi_ops import rank -_PRE_TF_2_4_0 = LooseVersion(tf.__version__) < LooseVersion('2.4.0') +_PRE_TF_2_4_0 = version.parse(tf.__version__) < version.parse('2.4.0') def create_distributed_optimizer(keras, optimizer, name, device_dense, device_sparse, diff --git a/horovod/_keras/callbacks.py b/horovod/_keras/callbacks.py index 839ea6ef0c..bcd34aa849 100644 --- a/horovod/_keras/callbacks.py +++ b/horovod/_keras/callbacks.py @@ -13,7 +13,7 @@ # limitations under the License. # ============================================================================== -from distutils.version import LooseVersion +from packaging import version import warnings import horovod.tensorflow as hvd @@ -54,7 +54,7 @@ def __init__(self, backend, device='', *args): self.allreduce_ops = {} self.device = device - if LooseVersion("2.3") <= LooseVersion(tf.__version__) < LooseVersion("2.5"): + if version.parse("2.3") <= version.parse(tf.__version__) < version.parse("2.5"): warnings.warn( "Some callbacks may not have access to the averaged metrics, " "see https://github.com/horovod/horovod/issues/2440") diff --git a/horovod/common/util.py b/horovod/common/util.py index a337fdb223..3c5057f244 100644 --- a/horovod/common/util.py +++ b/horovod/common/util.py @@ -269,7 +269,7 @@ def is_iterable(x): @_cache def is_version_greater_equal_than(ver, target): - from distutils.version import LooseVersion + from packaging import version if any([not isinstance(_str, str) for _str in (ver, target)]): raise ValueError("This function only accepts string arguments. \n" "Received:\n" @@ -285,4 +285,4 @@ def is_version_greater_equal_than(ver, target): raise ValueError("We only accepts target version values in the form " "of: major.minor.patch. Received: {}".format(target)) - return LooseVersion(ver) >= LooseVersion(target) + return version.parse(ver) >= version.parse(target) diff --git a/horovod/spark/common/store.py b/horovod/spark/common/store.py index f1c7abb298..f8c2aaa790 100644 --- a/horovod/spark/common/store.py +++ b/horovod/spark/common/store.py @@ -22,7 +22,7 @@ import tempfile import warnings -from distutils.version import LooseVersion +from packaging import version import pyarrow as pa import pyarrow.parquet as pq @@ -202,7 +202,7 @@ def read_serialized_keras_model(self, ckpt_path, model, custom_objects): from tensorflow import keras from horovod.spark.keras.util import TFKerasUtil - if LooseVersion(tensorflow.__version__) < LooseVersion("2.0.0"): + if version.parse(tensorflow.__version__) < version.parse("2.0.0"): model_bytes = self.read(ckpt_path) return codec.dumps_base64(model_bytes) else: @@ -429,7 +429,7 @@ def __init__(self, prefix_path, user=user, kerb_ticket=kerb_ticket, extra_conf=extra_conf) - if LooseVersion(pa.__version__) < LooseVersion('0.17.0'): + if version.parse(pa.__version__) < version.parse('0.17.0'): self._hdfs_kwargs['driver'] = driver self._hdfs = self._get_filesystem_fn()() @@ -609,7 +609,7 @@ def read_serialized_keras_model(self, ckpt_path, model, custom_objects): from tensorflow import keras from horovod.spark.keras.util import TFKerasUtil - if LooseVersion(tensorflow.__version__) < LooseVersion("2.0.0"): + if version.parse(tensorflow.__version__) < version.parse("2.0.0"): model.load_weights(ckpt_path) else: with keras.utils.custom_object_scope(custom_objects): diff --git a/horovod/spark/keras/remote.py b/horovod/spark/keras/remote.py index 5d0b848598..89e173ebe7 100644 --- a/horovod/spark/keras/remote.py +++ b/horovod/spark/keras/remote.py @@ -22,7 +22,7 @@ import h5py import tensorflow as tf -from distutils.version import LooseVersion +from packaging import version from horovod.spark.common import constants from horovod.spark.common.store import DBFSLocalStore from horovod.spark.common.util import _get_assigned_gpu_or_default, _set_mp_start_method @@ -126,7 +126,7 @@ def train(serialized_model, train_rows, val_rows, avg_row_size): print("Skip pinning current process to the GPU.") if random_seed is not None: - if LooseVersion(tf.__version__) < LooseVersion('2.0.0'): + if version.parse(tf.__version__) < version.parse('2.0.0'): tf.random.set_random_seed(random_seed) else: tf.random.set_seed(random_seed) @@ -176,7 +176,7 @@ def train(serialized_model, train_rows, val_rows, avg_row_size): if _checkpoint_callback: _checkpoint_callback.filepath = ckpt_file else: - if is_dbfs and LooseVersion(tf.__version__) < LooseVersion("2.0.0"): + if is_dbfs and version.parse(tf.__version__) < version.parse("2.0.0"): # Because DBFS local file APIs does not support random write which is # required by h5 format, save_weights_only=True is needed for switching # to the TensorFlow SavedModel format. @@ -270,7 +270,7 @@ def train(serialized_model, train_rows, val_rows, avg_row_size): if hvd.rank() == 0: if is_dbfs: - if LooseVersion(tf.__version__) < LooseVersion("2.0.0"): + if version.parse(tf.__version__) < version.parse("2.0.0"): model.load_weights(ckpt_file) else: # needs to be deserialized in the with scope @@ -278,7 +278,7 @@ def train(serialized_model, train_rows, val_rows, avg_row_size): model = k.models.load_model(ckpt_file) serialized_model = keras_utils.serialize_model(model) else: - if LooseVersion(tf.__version__) >= LooseVersion("2.0.0"): + if version.parse(tf.__version__) >= version.parse("2.0.0"): with k.utils.custom_object_scope(custom_objects): model = k.models.load_model(ckpt_file) serialized_model = keras_utils.serialize_model(model) @@ -302,7 +302,7 @@ def deserialize_keras_model(model_bytes, load_model_fn): def _pin_gpu_fn(): # Horovod: pin GPU to be used to process local rank (one GPU per process) - return _pin_gpu_tensorflow2_fn() if LooseVersion(tf.__version__) >= LooseVersion('2.0.0') \ + return _pin_gpu_tensorflow2_fn() if version.parse(tf.__version__) >= version.parse('2.0.0') \ else _pin_gpu_tensorflow1_fn() @@ -328,7 +328,7 @@ def fn(hvd, tf, keras): def _pin_cpu_fn(): - return _pin_cpu_tensorflow2_fn() if LooseVersion(tf.__version__) >= LooseVersion('2.0.0') \ + return _pin_cpu_tensorflow2_fn() if version.parse(tf.__version__) >= version.parse('2.0.0') \ else _pin_cpu_tensorflow1_fn() diff --git a/horovod/spark/keras/util.py b/horovod/spark/keras/util.py index 837dde2a16..e8796c1a69 100644 --- a/horovod/spark/keras/util.py +++ b/horovod/spark/keras/util.py @@ -16,7 +16,7 @@ import io -from distutils.version import LooseVersion +from packaging import version import h5py import numpy as np diff --git a/horovod/spark/lightning/estimator.py b/horovod/spark/lightning/estimator.py index fcfd912c61..1b4994db04 100644 --- a/horovod/spark/lightning/estimator.py +++ b/horovod/spark/lightning/estimator.py @@ -21,7 +21,7 @@ import os import time import warnings -from distutils.version import LooseVersion +from packaging import version from pyspark import keyword_only from pyspark.ml.param.shared import Param, Params, TypeConverters @@ -298,7 +298,7 @@ def __init__(self, kwargs = self._input_kwargs # pl version check - if LooseVersion(pl.__version__) < LooseVersion(MIN_PL_VERSION): + if version.parse(pl.__version__) < version.parse(MIN_PL_VERSION): raise RuntimeError("Only support pytorch_lightning version > {}, found version {}".format(MIN_PL_VERSION, pl.__version__)) if EstimatorParams.loss.name in kwargs and TorchEstimator.loss_constructors.name in kwargs: diff --git a/horovod/spark/lightning/remote.py b/horovod/spark/lightning/remote.py index f242e6a7af..def18b6ea4 100644 --- a/horovod/spark/lightning/remote.py +++ b/horovod/spark/lightning/remote.py @@ -19,7 +19,7 @@ import tempfile import math import warnings -from distutils.version import LooseVersion +from packaging import version import torch import pytorch_lightning as pl diff --git a/horovod/spark/task/task_service.py b/horovod/spark/task/task_service.py index 4988ed9164..321392a3b1 100644 --- a/horovod/spark/task/task_service.py +++ b/horovod/spark/task/task_service.py @@ -13,7 +13,7 @@ # limitations under the License. # ============================================================================== -from distutils.version import LooseVersion +from packaging import version import os import pyspark @@ -96,7 +96,7 @@ def _handle(self, req, client_address): return super(SparkTaskService, self)._handle(req, client_address) def _get_resources(self): - if LooseVersion(pyspark.__version__) >= LooseVersion('3.0.0'): + if version.parse(pyspark.__version__) >= version.parse('3.0.0'): task_context = pyspark.TaskContext.get() if task_context: return task_context.resources() diff --git a/horovod/tensorflow/__init__.py b/horovod/tensorflow/__init__.py index fb06ee9a3f..1db47b18ac 100644 --- a/horovod/tensorflow/__init__.py +++ b/horovod/tensorflow/__init__.py @@ -16,7 +16,7 @@ # ============================================================================== # pylint: disable=g-short-docstring-punctuation -from distutils.version import LooseVersion +from packaging import version import os import warnings @@ -45,7 +45,7 @@ from horovod.tensorflow.gradient_aggregation import LocalGradientAggregationHelper import tensorflow as tf -_IS_TF2 = LooseVersion(tf.__version__) >= LooseVersion('2.0.0') +_IS_TF2 = version.parse(tf.__version__) >= version.parse('2.0.0') # @DEKHTIARJonathan: Do not remove, this fixes issues: # - https://github.com/tensorflow/tensorflow/issues/38516 diff --git a/horovod/tensorflow/gradient_aggregation_eager.py b/horovod/tensorflow/gradient_aggregation_eager.py index b0ad760d73..c6397d8ef3 100644 --- a/horovod/tensorflow/gradient_aggregation_eager.py +++ b/horovod/tensorflow/gradient_aggregation_eager.py @@ -1,8 +1,8 @@ -from distutils.version import LooseVersion +from packaging import version import tensorflow as tf -_POST_TF_2_4_0 = LooseVersion(tf.__version__) >= LooseVersion('2.4.0') +_POST_TF_2_4_0 = version.parse(tf.__version__) >= version.parse('2.4.0') class LocalGradientAggregationHelperEager: diff --git a/horovod/tensorflow/mpi_ops.py b/horovod/tensorflow/mpi_ops.py index 511f0ddb76..ddbc0e9928 100644 --- a/horovod/tensorflow/mpi_ops.py +++ b/horovod/tensorflow/mpi_ops.py @@ -369,8 +369,8 @@ def broadcast_(variables, root_rank, name=None, process_set=global_process_set): Returns: The tensor values of the updated `variables` as broadcasted from root rank. """ - from distutils.version import LooseVersion - if LooseVersion(tf.__version__) < LooseVersion('2.6.0'): + from packaging import version + if version.parse(tf.__version__) < version.parse('2.6.0'): raise NotImplementedError("In-place broadcasts are only supported with TensorFlow 2.6 or later") from tensorflow.python.ops import resource_variable_ops diff --git a/horovod/torch/sync_batch_norm.py b/horovod/torch/sync_batch_norm.py index 74678023d4..b538fcc1af 100644 --- a/horovod/torch/sync_batch_norm.py +++ b/horovod/torch/sync_batch_norm.py @@ -16,7 +16,7 @@ from horovod.torch.mpi_ops import allgather_async, allreduce_async, Sum, size, synchronize -from distutils.version import LooseVersion +from packaging import version import torch from torch.autograd.function import Function @@ -30,11 +30,11 @@ _SYNC_BN_V2 = ( - LooseVersion(torch.__version__) >= LooseVersion('1.5.0') and - LooseVersion(torch.__version__) <= LooseVersion('1.6.0') + version.parse(torch.__version__) >= version.parse('1.5.0') and + version.parse(torch.__version__) <= version.parse('1.6.0') ) -_SYNC_BN_V3 = LooseVersion(torch.__version__) >= LooseVersion('1.6.0') -_SYNC_BN_V4 = LooseVersion(torch.__version__) >= LooseVersion('1.9.0') +_SYNC_BN_V3 = version.parse(torch.__version__) >= version.parse('1.6.0') +_SYNC_BN_V4 = version.parse(torch.__version__) >= version.parse('1.9.0') class SyncBatchNorm(_BatchNorm): diff --git a/setup.py b/setup.py index 24d3705f19..55fcf86493 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ from setuptools import setup, Extension, find_packages from setuptools.command.build_ext import build_ext -from distutils.version import LooseVersion +from packaging import version from horovod import __version__ @@ -72,11 +72,11 @@ def get_cmake_bin(): try: out = subprocess.check_output([cmake_bin, '--version']) except OSError: - cmake_installed_version = LooseVersion("0.0") + cmake_installed_version = version.parse("0.0") else: - cmake_installed_version = LooseVersion(re.search(r'version\s*([\d.]+)', out.decode()).group(1)) + cmake_installed_version = version.parse(re.search(r'version\s*([\d.]+)', out.decode()).group(1)) - if cmake_installed_version < LooseVersion("3.13.0"): + if cmake_installed_version < version.parse("3.13.0"): print("Could not find a recent CMake to build Horovod. " "Attempting to install CMake 3.13 to a temporary location via pip.", flush=True) cmake_temp_dir = tempfile.TemporaryDirectory(prefix="horovod-cmake-tmp") diff --git a/test/integration/test_spark.py b/test/integration/test_spark.py index 2607cb3845..4a57f0730a 100644 --- a/test/integration/test_spark.py +++ b/test/integration/test_spark.py @@ -30,7 +30,7 @@ import time import unittest import warnings -from distutils.version import LooseVersion +from packaging import version import mock import psutil @@ -1692,7 +1692,7 @@ def test_spark_task_service_abort_no_command(self): client.abort_command() self.do_test_spark_task_service_executes_command(client, file) - @pytest.mark.skipif(LooseVersion(pyspark.__version__) < LooseVersion('3.0.0'), + @pytest.mark.skipif(version.parse(pyspark.__version__) < version.parse('3.0.0'), reason='get_available_devices only supported in Spark 3.0 and above') def test_get_available_devices(self): res = run_get_available_devices() @@ -1774,14 +1774,14 @@ def deserialize_keras_model(serialized_model): get_dbfs_output_dir = dbfs_store.get_local_output_dir_fn("0") with get_dbfs_output_dir() as run_output_dir: dbfs_ckpt_path = run_output_dir + "/" + local_store.get_checkpoint_filename() - if LooseVersion(tensorflow.__version__) < LooseVersion("2.0.0"): + if version.parse(tensorflow.__version__) < version.parse("2.0.0"): model.save_weights(dbfs_ckpt_path) else: model.save(dbfs_ckpt_path) serialized_model_dbfs = dbfs_store.read_serialized_keras_model(dbfs_ckpt_path, model, custom_objects={}) reconstructed_model_dbfs = deserialize_keras_model(serialized_model_dbfs) - if LooseVersion(tensorflow.__version__) >= LooseVersion("2.3.0"): + if version.parse(tensorflow.__version__) >= version.parse("2.3.0"): assert reconstructed_model_dbfs.get_config() == model.get_config() # test local_store.read_serialized_keras_model @@ -1801,7 +1801,7 @@ def deserialize_keras_model(serialized_model): custom_objects={}) reconstructed_model_local = deserialize_keras_model(serialized_model_local) - if LooseVersion(tensorflow.__version__) >= LooseVersion("2.3.0"): + if version.parse(tensorflow.__version__) >= version.parse("2.3.0"): assert reconstructed_model_local.get_config() == model.get_config() finally: shutil.rmtree(actual_dbfs_dir, ignore_errors=True) diff --git a/test/integration/test_spark_keras.py b/test/integration/test_spark_keras.py index 749f11d349..aec2bd54d1 100644 --- a/test/integration/test_spark_keras.py +++ b/test/integration/test_spark_keras.py @@ -20,7 +20,7 @@ import sys import warnings -from distutils.version import LooseVersion +from packaging import version import mock import numpy as np diff --git a/test/integration/test_spark_lightning.py b/test/integration/test_spark_lightning.py index 9a422d2367..afad416110 100644 --- a/test/integration/test_spark_lightning.py +++ b/test/integration/test_spark_lightning.py @@ -23,7 +23,7 @@ from unittest.mock import Mock import pytest import numpy as np -from distutils.version import LooseVersion +from packaging import version import torch import torch.nn as nn @@ -37,8 +37,8 @@ try: # tensorflow has to be imported BEFORE pytorch_lightning, otherwise we see the segfault right away import tensorflow as tf - from distutils.version import LooseVersion - if LooseVersion('2.5.0') <= LooseVersion(tf.__version__) < LooseVersion('2.7.0'): + from packaging import version + if version.parse('2.5.0') <= version.parse(tf.__version__) < version.parse('2.7.0'): skip_lightning_tests = True except ImportError: pass @@ -125,7 +125,7 @@ def create_legacy_xor_model(input_dim=2, output_dim=1): return LegacyXOR(input_dim, output_dim) -@pytest.mark.skipif(LooseVersion(pl.__version__) < LooseVersion(MIN_PL_VERSION), reason='Pytorch lightning version is not supported.') +@pytest.mark.skipif(version.parse(pl.__version__) < version.parse(MIN_PL_VERSION), reason='Pytorch lightning version is not supported.') class SparkLightningTests(unittest.TestCase): def __init__(self, *args, **kwargs): diff --git a/test/parallel/base_test_mxnet.py b/test/parallel/base_test_mxnet.py index 54849c805c..92d8d97478 100644 --- a/test/parallel/base_test_mxnet.py +++ b/test/parallel/base_test_mxnet.py @@ -19,7 +19,7 @@ import sys import itertools import unittest -from distutils.version import LooseVersion +from packaging import version import pytest import numpy as np @@ -1607,7 +1607,7 @@ def test_optimizer_process_sets(self): g = mx.random.uniform(shape=shape, ctx=ctx, dtype=np.float32) # Update that is only averaged over even_set - if LooseVersion(mx.__version__) >= LooseVersion('2.0.0'): + if version.parse(mx.__version__) >= version.parse('2.0.0'): opt.update([0], [w], [g], [opt.create_state(0, w)]) else: opt.update(0, w, g, opt.create_state(0, w)) diff --git a/test/parallel/test_compute_worker.py b/test/parallel/test_compute_worker.py index 82ae4e7f42..4050db9b0c 100644 --- a/test/parallel/test_compute_worker.py +++ b/test/parallel/test_compute_worker.py @@ -15,7 +15,7 @@ import logging import os import unittest -from distutils.version import LooseVersion +from packaging import version from itertools import islice import tensorflow as tf @@ -25,7 +25,7 @@ from horovod.tensorflow.data.compute_service import TfDataServiceConfig, tf_data_service from horovod.tensorflow.data.compute_worker import main -_PRE_TF_2_0_0 = LooseVersion(tf.__version__) < LooseVersion("2.0.0") +_PRE_TF_2_0_0 = version.parse(tf.__version__) < version.parse("2.0.0") # this test is to be run via horovodrun -np 2, all processes have to run on the same machine diff --git a/test/parallel/test_keras.py b/test/parallel/test_keras.py index 8f3b4b86d3..bd55ff0f6e 100644 --- a/test/parallel/test_keras.py +++ b/test/parallel/test_keras.py @@ -15,7 +15,7 @@ """Tests for horovod.keras.""" -from distutils.version import LooseVersion +from packaging import version import os import sys @@ -38,7 +38,7 @@ @pytest.mark.skipif(not _HAS_KERAS, reason='Keras not available') -@pytest.mark.skipif(LooseVersion(tf.__version__) >= LooseVersion('2.0.0'), reason='TensorFlow v1 tests') +@pytest.mark.skipif(version.parse(tf.__version__) >= version.parse('2.0.0'), reason='TensorFlow v1 tests') class KerasTests(tf.test.TestCase): """ Tests for ops in horovod.keras. diff --git a/test/parallel/test_mxnet1.py b/test/parallel/test_mxnet1.py index 206020b0f4..856dba18c1 100644 --- a/test/parallel/test_mxnet1.py +++ b/test/parallel/test_mxnet1.py @@ -18,7 +18,7 @@ import sys import itertools import unittest -from distutils.version import LooseVersion +from packaging import version import pytest import numpy as np @@ -29,13 +29,13 @@ if HAS_MXNET: # MXNet 1.4.x will kill test MPI process if error occurs during operation enqueue. Skip # those tests for versions earlier than 1.5.0. - _skip_enqueue_errors = LooseVersion(mx.__version__) < LooseVersion('1.5.0') + _skip_enqueue_errors = version.parse(mx.__version__) < version.parse('1.5.0') else: _skip_enqueue_errors = False @pytest.mark.skipif(not HAS_MXNET, reason='MXNet unavailable') -@pytest.mark.skipif(LooseVersion(mx.__version__) >= LooseVersion('2.0.0'), reason='MXNet v1.x tests') +@pytest.mark.skipif(version.parse(mx.__version__) >= version.parse('2.0.0'), reason='MXNet v1.x tests') class MX1Tests(MXTests, unittest.TestCase): """ Tests for ops in horovod.mxnet. This tests MXNet 1.x specifically. diff --git a/test/parallel/test_mxnet2.py b/test/parallel/test_mxnet2.py index b89e269fe0..a7697c9c0f 100644 --- a/test/parallel/test_mxnet2.py +++ b/test/parallel/test_mxnet2.py @@ -18,7 +18,7 @@ import sys import itertools import unittest -from distutils.version import LooseVersion +from packaging import version import pytest import numpy as np @@ -28,7 +28,7 @@ from base_test_mxnet import * -@pytest.mark.skipif(LooseVersion(mx.__version__) < LooseVersion('2.0.0'), reason='MXNet v2 tests') +@pytest.mark.skipif(version.parse(mx.__version__) < version.parse('2.0.0'), reason='MXNet v2 tests') class MX2Tests(MXTests, unittest.TestCase): """ Tests for ops in horovod.mxnet. This tests MXNet 2.x specifically. diff --git a/test/parallel/test_tensorflow.py b/test/parallel/test_tensorflow.py index 1d7db5e39c..cf00e160ef 100644 --- a/test/parallel/test_tensorflow.py +++ b/test/parallel/test_tensorflow.py @@ -18,7 +18,7 @@ """Tests for horovod.tensorflow.mpi_ops.""" -from distutils.version import LooseVersion +from packaging import version import itertools import numpy as np @@ -40,7 +40,7 @@ from base_test_tensorflow import * -_IS_TF2 = LooseVersion(tf.__version__) >= LooseVersion('2.0.0') +_IS_TF2 = version.parse(tf.__version__) >= version.parse('2.0.0') _is_mac = platform.system() == 'Darwin' class TensorFlowTests(BaseTensorFlowTests): @@ -1612,7 +1612,7 @@ def test_horovod_broadcast_gpu(self): def test_horovod_broadcast_inplace_cpu(self): """Test that the inplace broadcast correctly broadcasts 1D, 2D, 3D variables on CPU.""" - if LooseVersion(tf.__version__) < LooseVersion('2.6.0'): + if version.parse(tf.__version__) < version.parse('2.6.0'): self.skipTest("Custom Ops using resource variables only work with TF 2.6+") hvd.init() @@ -1662,7 +1662,7 @@ def test_horovod_broadcast_inplace_cpu(self): def test_horovod_broadcast_inplace_gpu(self): """Test that the inplace broadcast correctly broadcasts 1D, 2D, 3D variables on GPU.""" - if LooseVersion(tf.__version__) < LooseVersion('2.6.0'): + if version.parse(tf.__version__) < version.parse('2.6.0'): self.skipTest("Custom Ops using resource variables only work with TF 2.6+") if not tf.test.is_gpu_available(cuda_only=True): @@ -1719,7 +1719,7 @@ def test_horovod_broadcast_inplace_gpu(self): def test_horovod_broadcast_inplace_multiple_cpu(self): """Test that the inplace broadcast correctly broadcasts multiple variables on CPU.""" - if LooseVersion(tf.__version__) < LooseVersion('2.6.0'): + if version.parse(tf.__version__) < version.parse('2.6.0'): self.skipTest("Custom Ops using resource variables only work with TF 2.6+") hvd.init() diff --git a/test/parallel/test_tensorflow2_keras.py b/test/parallel/test_tensorflow2_keras.py index 197ebdbdc7..0c0e587c45 100644 --- a/test/parallel/test_tensorflow2_keras.py +++ b/test/parallel/test_tensorflow2_keras.py @@ -19,7 +19,7 @@ import numpy as np import warnings -from distutils.version import LooseVersion +from packaging import version from parameterized import parameterized import pytest @@ -29,7 +29,7 @@ from horovod.common.util import is_version_greater_equal_than if is_version_greater_equal_than(tf.__version__, "2.6.0"): - if LooseVersion(keras.__version__) < LooseVersion("2.9.0"): + if version.parse(keras.__version__) < version.parse("2.9.0"): from keras.optimizer_v2 import optimizer_v2 else: from keras.optimizers.optimizer_v2 import optimizer_v2 @@ -39,11 +39,11 @@ import horovod.tensorflow.keras as hvd -_PRE_TF_2_2_0 = LooseVersion(tf.__version__) < LooseVersion("2.2.0") +_PRE_TF_2_2_0 = version.parse(tf.__version__) < version.parse("2.2.0") -@pytest.mark.skipif(LooseVersion(tf.__version__) < - LooseVersion('2.0.0'), reason='TensorFlow v2 tests') +@pytest.mark.skipif(version.parse(tf.__version__) < + version.parse('2.0.0'), reason='TensorFlow v2 tests') class Tf2KerasTests(tf.test.TestCase): """ Tests for ops in horovod.tensorflow.keras. diff --git a/test/parallel/test_tensorflow2_keras_process_sets.py b/test/parallel/test_tensorflow2_keras_process_sets.py index 16c670db07..43c83771cc 100644 --- a/test/parallel/test_tensorflow2_keras_process_sets.py +++ b/test/parallel/test_tensorflow2_keras_process_sets.py @@ -7,7 +7,7 @@ import tensorflow as tf import warnings -from distutils.version import LooseVersion +from packaging import version import pytest from tensorflow import keras @@ -15,10 +15,10 @@ from horovod.runner.common.util.env import get_env_rank_and_size -_PRE_TF_2_2_0 = LooseVersion(tf.__version__) < LooseVersion("2.2.0") +_PRE_TF_2_2_0 = version.parse(tf.__version__) < version.parse("2.2.0") -@pytest.mark.skipif(LooseVersion(tf.__version__) < - LooseVersion('2.0.0'), reason='TensorFlow v2 tests') +@pytest.mark.skipif(version.parse(tf.__version__) < + version.parse('2.0.0'), reason='TensorFlow v2 tests') class Tf2KerasProcessSetsTests(tf.test.TestCase): """ Tests for ops in horovod.tensorflow.keras using multiple process sets. diff --git a/test/parallel/test_tensorflow_keras.py b/test/parallel/test_tensorflow_keras.py index 061084ed70..9c36e83a11 100644 --- a/test/parallel/test_tensorflow_keras.py +++ b/test/parallel/test_tensorflow_keras.py @@ -23,14 +23,14 @@ import tensorflow as tf import warnings -from distutils.version import LooseVersion +from packaging import version from tensorflow import keras from horovod.common.util import is_version_greater_equal_than if is_version_greater_equal_than(tf.__version__, "2.6.0"): from keras import backend as K - if LooseVersion(keras.__version__) < LooseVersion("2.9.0"): + if version.parse(keras.__version__) < version.parse("2.9.0"): from keras.optimizer_v2 import optimizer_v2 else: from keras.optimizers.optimizer_v2 import optimizer_v2 @@ -45,7 +45,7 @@ from common import temppath -@pytest.mark.skipif(LooseVersion(tf.__version__) >= LooseVersion('2.0.0'), reason='TensorFlow v1 tests') +@pytest.mark.skipif(version.parse(tf.__version__) >= version.parse('2.0.0'), reason='TensorFlow v1 tests') class TfKerasTests(tf.test.TestCase): """ Tests for ops in horovod.tensorflow.keras. diff --git a/test/parallel/test_tensorflow_process_sets.py b/test/parallel/test_tensorflow_process_sets.py index 5bc74f206b..3833e474c0 100644 --- a/test/parallel/test_tensorflow_process_sets.py +++ b/test/parallel/test_tensorflow_process_sets.py @@ -5,7 +5,7 @@ tests for multiple process sets in this script that initializes Horovod with static process sets. """ -from distutils.version import LooseVersion +from packaging import version import itertools import numpy as np @@ -19,7 +19,7 @@ from horovod.runner.common.util.env import get_env_rank_and_size -_IS_TF2 = LooseVersion(tf.__version__) >= LooseVersion('2.0.0') +_IS_TF2 = version.parse(tf.__version__) >= version.parse('2.0.0') _is_mac = platform.system() == 'Darwin' diff --git a/test/parallel/test_torch.py b/test/parallel/test_torch.py index d4de610c89..2591e7a9b4 100644 --- a/test/parallel/test_torch.py +++ b/test/parallel/test_torch.py @@ -15,7 +15,7 @@ # limitations under the License. # ============================================================================== -from distutils.version import LooseVersion +from packaging import version import inspect import itertools @@ -42,8 +42,8 @@ from common import mpi_env_rank_and_size, skip_or_fail_gpu_test, temppath -_1_12_api = LooseVersion(torch.__version__) >= LooseVersion('1.12.0') -_1_5_api = LooseVersion(torch.__version__) >= LooseVersion('1.5.0') +_1_12_api = version.parse(torch.__version__) >= version.parse('1.12.0') +_1_5_api = version.parse(torch.__version__) >= version.parse('1.5.0') _is_mac = platform.system() == 'Darwin' ccl_supported_types = set([torch.ByteTensor, torch.CharTensor, torch.ShortTensor, diff --git a/test/parallel/test_xla.py b/test/parallel/test_xla.py index eefba48d97..b42d0e203e 100644 --- a/test/parallel/test_xla.py +++ b/test/parallel/test_xla.py @@ -24,7 +24,7 @@ import math import numpy as np import itertools -from distutils.version import LooseVersion +from packaging import version import warnings # Enable HVD XLA ops so that tf.function(jit_compile=True) works. This @@ -59,7 +59,7 @@ ccl_supported_types = set([tf.uint8, tf.int8, tf.uint16, tf.int16, tf.int32, tf.int64, tf.float32]) -_IS_TF26 = LooseVersion(tf.__version__) >= LooseVersion('2.6.0') +_IS_TF26 = version.parse(tf.__version__) >= version.parse('2.6.0') @pytest.mark.skipif(not _IS_TF26, reason='TF2.6+ is required') diff --git a/test/parallel/test_xla_process_sets.py b/test/parallel/test_xla_process_sets.py index d45d6dadb4..e85ecf4d8d 100644 --- a/test/parallel/test_xla_process_sets.py +++ b/test/parallel/test_xla_process_sets.py @@ -23,7 +23,7 @@ out tests that depend on that setting to this script. """ -from distutils.version import LooseVersion +from packaging import version import itertools import numpy as np @@ -45,7 +45,7 @@ from horovod.runner.common.util.env import get_env_rank_and_size -_IS_TF26 = LooseVersion(tf.__version__) >= LooseVersion('2.6.0') +_IS_TF26 = version.parse(tf.__version__) >= version.parse('2.6.0') @pytest.mark.skipif(not _IS_TF26, reason='TF2.6+ is required') diff --git a/test/single/test_compute_service.py b/test/single/test_compute_service.py index 69c6caa5c4..12698690cd 100644 --- a/test/single/test_compute_service.py +++ b/test/single/test_compute_service.py @@ -13,7 +13,7 @@ # limitations under the License. # ============================================================================== import unittest -from distutils.version import LooseVersion +from packaging import version from queue import Queue import tensorflow as tf @@ -23,7 +23,7 @@ from horovod.runner.common.util.timeout import TimeoutException from horovod.runner.util.threads import in_thread -_PRE_TF_2_0_0 = LooseVersion(tf.__version__) < LooseVersion("2.0.0") +_PRE_TF_2_0_0 = version.parse(tf.__version__) < version.parse("2.0.0") class ComputeServiceTest(unittest.TestCase): From 09d554b9a6a00adad86b25ebd1b77828d16a334b Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 14 Sep 2022 06:27:35 -0400 Subject: [PATCH 4/4] fix exclude-members Signed-off-by: Jinzhe Zeng --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index b34ce61678..b7c7116d4f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -62,7 +62,7 @@ 'members': None, 'member-order': 'bysource', 'imported-members': None, - 'exclude-members': 'contextmanager, version.parse, tf, keras, torch, mx, pyspark', + 'exclude-members': 'contextmanager, version, tf, keras, torch, mx, pyspark', }