prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Python 3.12 distutils deprecation error
  ### System Info
  
  ```Shell
  accelerate 0.26.1, Windows 11, Python 3.12.1
  ```
  
  
  ### Information
  
  - [ ] The official example scripts
  - [x] My own modified scripts
  
  ### Tasks
  
  - [ ] One of the scripts in the examples/ folder of Accelerate or an officially supported `no_trainer` script in the `examples` folder of the `transformers` repo (such as `run_no_trainer_glue.py`)
  - [X] My own task or dataset (give details below)
  
  ### Reproduction
  
  To reproduce behaviour:
  1. In Python 3.12 install latest accelerate with
  ` pip3 install accelerate --upgrade `
  2. Try to initialise accelerate with:
  ```python
  from accelerate import Accelerator
  accelerator = Accelerator()
  ```
  
  This results in the error caused by https://github.com/huggingface/accelerate/blob/f88661b5d9f4977b5a1555a92e64ef3cd359cbc9/src/accelerate/utils/environment.py#L19
  
  ![image](https://github.com/huggingface/accelerate/assets/7803161/8b88d142-cf67-43b5-9b02-3f9346baa02a)
  The fundamental issue is deprecation of distutils as part of https://peps.python.org/pep-0632/
  
  ### Expected behavior
  
  Accelerate should not depend on deprecated package and be compatible with Python 3.12+
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/accelerate/utils/environment.py`
  Content:
  ```
  1 # Copyright 2022 The HuggingFace Team. All rights reserved.
  2 #
  3 # Licensed under the Apache License, Version 2.0 (the "License");
  4 # you may not use this file except in compliance with the License.
  5 # You may obtain a copy of the License at
  6 #
  7 #     http://www.apache.org/licenses/LICENSE-2.0
  8 #
  9 # Unless required by applicable law or agreed to in writing, software
  10 # distributed under the License is distributed on an "AS IS" BASIS,
  11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 # See the License for the specific language governing permissions and
  13 # limitations under the License.
  14 
  15 import os
  16 import platform
  17 import subprocess
  18 import sys
  19 from distutils import spawn
  20 from typing import Dict
  21 
  22 import torch
  23 
  24 
  25 def str_to_bool(value) -> int:
  26     """
  27     Converts a string representation of truth to `True` (1) or `False` (0).
  28 
  29     True values are `y`, `yes`, `t`, `true`, `on`, and `1`; False value are `n`, `no`, `f`, `false`, `off`, and `0`;
  30     """
  31     value = value.lower()
  32     if value in ("y", "yes", "t", "true", "on", "1"):
  33         return 1
  34     elif value in ("n", "no", "f", "false", "off", "0"):
  35         return 0
  36     else:
  37         raise ValueError(f"invalid truth value {value}")
  38 
  39 
  40 def get_int_from_env(env_keys, default):
  41     """Returns the first positive env value found in the `env_keys` list or the default."""
  42     for e in env_keys:
  43         val = int(os.environ.get(e, -1))
  44         if val >= 0:
  45             return val
  46     return default
  47 
  48 
  49 def parse_flag_from_env(key, default=False):
  50     """Returns truthy value for `key` from the env if available else the default."""
  51     value = os.environ.get(key, str(default))
  52     return str_to_bool(value) == 1  # As its name indicates `str_to_bool` actually returns an int...
  53 
  54 
  55 def parse_choice_from_env(key, default="no"):
  56     value = os.environ.get(key, str(default))
  57     return value
  58 
  59 
  60 def are_libraries_initialized(*library_names: str) -> Dict[str, bool]:
  61     """
  62     Checks if any of `library_names` are imported in the environment. Will return results as a `key:bool` pair.
  63     """
  64     return [lib_name for lib_name in library_names if lib_name in sys.modules.keys()]
  65 
  66 
  67 def get_gpu_info():
  68     """
  69     Gets GPU count and names using `nvidia-smi` instead of torch to not initialize CUDA.
  70 
  71     Largely based on the `gputil` library.
  72     """
  73     if platform.system() == "Windows":
  74         # If platform is Windows and nvidia-smi can't be found in path
  75         # try from systemd rive with default installation path
  76         command = spawn.find_executable("nvidia-smi")
  77         if command is None:
  78             command = "%s\\Program Files\\NVIDIA Corporation\\NVSMI\\nvidia-smi.exe" % os.environ["systemdrive"]
  79     else:
  80         command = "nvidia-smi"
  81     # Returns as list of `n` GPUs and their names
  82     output = subprocess.check_output(
  83         [command, "--query-gpu=count,name", "--format=csv,noheader"], universal_newlines=True
  84     )
  85     output = output.strip()
  86     gpus = output.split(os.linesep)
  87     # Get names from output
  88     gpu_count = len(gpus)
  89     gpu_names = [gpu.split(",")[1].strip() for gpu in gpus]
  90     return gpu_names, gpu_count
  91 
  92 
  93 def check_cuda_p2p_ib_support():
  94     """
  95     Checks if the devices being used have issues with P2P and IB communications, namely any consumer GPU hardware after
  96     the 3090.
  97 
  98     Noteably uses `nvidia-smi` instead of torch to not initialize CUDA.
  99     """
  100     try:
  101         device_names, device_count = get_gpu_info()
  102         # As new consumer GPUs get released, add them to `unsupported_devices``
  103         unsupported_devices = {"RTX 40"}
  104         if device_count > 1:
  105             if any(
  106                 unsupported_device in device_name
  107                 for device_name in device_names
  108                 for unsupported_device in unsupported_devices
  109             ):
  110                 return False
  111     except Exception:
  112         pass
  113     return True
  114 
  115 
  116 def check_fp8_capability():
  117     """
  118     Checks if all the current GPUs available support FP8.
  119 
  120     Notably must initialize `torch.cuda` to check.
  121     """
  122     cuda_device_capacity = torch.cuda.get_device_capability()
  123     return cuda_device_capacity >= (8, 9)
  124
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/src/accelerate/utils/environment.py b/src/accelerate/utils/environment.py
  --- a/src/accelerate/utils/environment.py
  +++ b/src/accelerate/utils/environment.py
  @@ -16,7 +16,7 @@
   import platform
   import subprocess
   import sys
  -from distutils import spawn
  +from shutil import which
   from typing import Dict
   
   import torch
  @@ -72,8 +72,8 @@
       """
       if platform.system() == "Windows":
           # If platform is Windows and nvidia-smi can't be found in path
  -        # try from systemd rive with default installation path
  -        command = spawn.find_executable("nvidia-smi")
  +        # try from systemd drive with default installation path
  +        command = which("nvidia-smi")
           if command is None:
               command = "%s\\Program Files\\NVIDIA Corporation\\NVSMI\\nvidia-smi.exe" % os.environ["systemdrive"]
       else:
  
verification_info: {"golden_diff": "diff --git a/src/accelerate/utils/environment.py b/src/accelerate/utils/environment.py\n--- a/src/accelerate/utils/environment.py\n+++ b/src/accelerate/utils/environment.py\n@@ -16,7 +16,7 @@\n import platform\n import subprocess\n import sys\n-from distutils import spawn\n+from shutil import which\n from typing import Dict\n \n import torch\n@@ -72,8 +72,8 @@\n     \"\"\"\n     if platform.system() == \"Windows\":\n         # If platform is Windows and nvidia-smi can't be found in path\n-        # try from systemd rive with default installation path\n-        command = spawn.find_executable(\"nvidia-smi\")\n+        # try from systemd drive with default installation path\n+        command = which(\"nvidia-smi\")\n         if command is None:\n             command = \"%s\\\\Program Files\\\\NVIDIA Corporation\\\\NVSMI\\\\nvidia-smi.exe\" % os.environ[\"systemdrive\"]\n     else:\n", "issue": "Python 3.12 distutils deprecation error\n### System Info\n\n```Shell\naccelerate 0.26.1, Windows 11, Python 3.12.1\n```\n\n\n### Information\n\n- [ ] The official example scripts\n- [x] My own modified scripts\n\n### Tasks\n\n- [ ] One of the scripts in the examples/ folder of Accelerate or an officially supported `no_trainer` script in the `examples` folder of the `transformers` repo (such as `run_no_trainer_glue.py`)\n- [X] My own task or dataset (give details below)\n\n### Reproduction\n\nTo reproduce behaviour:\r\n1. In Python 3.12 install latest accelerate with\r\n` pip3 install accelerate --upgrade `\r\n2. Try to initialise accelerate with:\r\n```python\r\nfrom accelerate import Accelerator\r\naccelerator = Accelerator()\r\n```\r\n\r\nThis results in the error caused by https://github.com/huggingface/accelerate/blob/f88661b5d9f4977b5a1555a92e64ef3cd359cbc9/src/accelerate/utils/environment.py#L19\r\n\r\n![image](https://github.com/huggingface/accelerate/assets/7803161/8b88d142-cf67-43b5-9b02-3f9346baa02a)\r\nThe fundamental issue is deprecation of distutils as part of https://peps.python.org/pep-0632/\n\n### Expected behavior\n\nAccelerate should not depend on deprecated package and be compatible with Python 3.12+\n", "before_files": [{"content": "# Copyright 2022 The HuggingFace Team. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport platform\nimport subprocess\nimport sys\nfrom distutils import spawn\nfrom typing import Dict\n\nimport torch\n\n\ndef str_to_bool(value) -> int:\n    \"\"\"\n    Converts a string representation of truth to `True` (1) or `False` (0).\n\n    True values are `y`, `yes`, `t`, `true`, `on`, and `1`; False value are `n`, `no`, `f`, `false`, `off`, and `0`;\n    \"\"\"\n    value = value.lower()\n    if value in (\"y\", \"yes\", \"t\", \"true\", \"on\", \"1\"):\n        return 1\n    elif value in (\"n\", \"no\", \"f\", \"false\", \"off\", \"0\"):\n        return 0\n    else:\n        raise ValueError(f\"invalid truth value {value}\")\n\n\ndef get_int_from_env(env_keys, default):\n    \"\"\"Returns the first positive env value found in the `env_keys` list or the default.\"\"\"\n    for e in env_keys:\n        val = int(os.environ.get(e, -1))\n        if val >= 0:\n            return val\n    return default\n\n\ndef parse_flag_from_env(key, default=False):\n    \"\"\"Returns truthy value for `key` from the env if available else the default.\"\"\"\n    value = os.environ.get(key, str(default))\n    return str_to_bool(value) == 1  # As its name indicates `str_to_bool` actually returns an int...\n\n\ndef parse_choice_from_env(key, default=\"no\"):\n    value = os.environ.get(key, str(default))\n    return value\n\n\ndef are_libraries_initialized(*library_names: str) -> Dict[str, bool]:\n    \"\"\"\n    Checks if any of `library_names` are imported in the environment. Will return results as a `key:bool` pair.\n    \"\"\"\n    return [lib_name for lib_name in library_names if lib_name in sys.modules.keys()]\n\n\ndef get_gpu_info():\n    \"\"\"\n    Gets GPU count and names using `nvidia-smi` instead of torch to not initialize CUDA.\n\n    Largely based on the `gputil` library.\n    \"\"\"\n    if platform.system() == \"Windows\":\n        # If platform is Windows and nvidia-smi can't be found in path\n        # try from systemd rive with default installation path\n        command = spawn.find_executable(\"nvidia-smi\")\n        if command is None:\n            command = \"%s\\\\Program Files\\\\NVIDIA Corporation\\\\NVSMI\\\\nvidia-smi.exe\" % os.environ[\"systemdrive\"]\n    else:\n        command = \"nvidia-smi\"\n    # Returns as list of `n` GPUs and their names\n    output = subprocess.check_output(\n        [command, \"--query-gpu=count,name\", \"--format=csv,noheader\"], universal_newlines=True\n    )\n    output = output.strip()\n    gpus = output.split(os.linesep)\n    # Get names from output\n    gpu_count = len(gpus)\n    gpu_names = [gpu.split(\",\")[1].strip() for gpu in gpus]\n    return gpu_names, gpu_count\n\n\ndef check_cuda_p2p_ib_support():\n    \"\"\"\n    Checks if the devices being used have issues with P2P and IB communications, namely any consumer GPU hardware after\n    the 3090.\n\n    Noteably uses `nvidia-smi` instead of torch to not initialize CUDA.\n    \"\"\"\n    try:\n        device_names, device_count = get_gpu_info()\n        # As new consumer GPUs get released, add them to `unsupported_devices``\n        unsupported_devices = {\"RTX 40\"}\n        if device_count > 1:\n            if any(\n                unsupported_device in device_name\n                for device_name in device_names\n                for unsupported_device in unsupported_devices\n            ):\n                return False\n    except Exception:\n        pass\n    return True\n\n\ndef check_fp8_capability():\n    \"\"\"\n    Checks if all the current GPUs available support FP8.\n\n    Notably must initialize `torch.cuda` to check.\n    \"\"\"\n    cuda_device_capacity = torch.cuda.get_device_capability()\n    return cuda_device_capacity >= (8, 9)\n", "path": "src/accelerate/utils/environment.py"}], "after_files": [{"content": "# Copyright 2022 The HuggingFace Team. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport platform\nimport subprocess\nimport sys\nfrom shutil import which\nfrom typing import Dict\n\nimport torch\n\n\ndef str_to_bool(value) -> int:\n    \"\"\"\n    Converts a string representation of truth to `True` (1) or `False` (0).\n\n    True values are `y`, `yes`, `t`, `true`, `on`, and `1`; False value are `n`, `no`, `f`, `false`, `off`, and `0`;\n    \"\"\"\n    value = value.lower()\n    if value in (\"y\", \"yes\", \"t\", \"true\", \"on\", \"1\"):\n        return 1\n    elif value in (\"n\", \"no\", \"f\", \"false\", \"off\", \"0\"):\n        return 0\n    else:\n        raise ValueError(f\"invalid truth value {value}\")\n\n\ndef get_int_from_env(env_keys, default):\n    \"\"\"Returns the first positive env value found in the `env_keys` list or the default.\"\"\"\n    for e in env_keys:\n        val = int(os.environ.get(e, -1))\n        if val >= 0:\n            return val\n    return default\n\n\ndef parse_flag_from_env(key, default=False):\n    \"\"\"Returns truthy value for `key` from the env if available else the default.\"\"\"\n    value = os.environ.get(key, str(default))\n    return str_to_bool(value) == 1  # As its name indicates `str_to_bool` actually returns an int...\n\n\ndef parse_choice_from_env(key, default=\"no\"):\n    value = os.environ.get(key, str(default))\n    return value\n\n\ndef are_libraries_initialized(*library_names: str) -> Dict[str, bool]:\n    \"\"\"\n    Checks if any of `library_names` are imported in the environment. Will return results as a `key:bool` pair.\n    \"\"\"\n    return [lib_name for lib_name in library_names if lib_name in sys.modules.keys()]\n\n\ndef get_gpu_info():\n    \"\"\"\n    Gets GPU count and names using `nvidia-smi` instead of torch to not initialize CUDA.\n\n    Largely based on the `gputil` library.\n    \"\"\"\n    if platform.system() == \"Windows\":\n        # If platform is Windows and nvidia-smi can't be found in path\n        # try from systemd drive with default installation path\n        command = which(\"nvidia-smi\")\n        if command is None:\n            command = \"%s\\\\Program Files\\\\NVIDIA Corporation\\\\NVSMI\\\\nvidia-smi.exe\" % os.environ[\"systemdrive\"]\n    else:\n        command = \"nvidia-smi\"\n    # Returns as list of `n` GPUs and their names\n    output = subprocess.check_output(\n        [command, \"--query-gpu=count,name\", \"--format=csv,noheader\"], universal_newlines=True\n    )\n    output = output.strip()\n    gpus = output.split(os.linesep)\n    # Get names from output\n    gpu_count = len(gpus)\n    gpu_names = [gpu.split(\",\")[1].strip() for gpu in gpus]\n    return gpu_names, gpu_count\n\n\ndef check_cuda_p2p_ib_support():\n    \"\"\"\n    Checks if the devices being used have issues with P2P and IB communications, namely any consumer GPU hardware after\n    the 3090.\n\n    Noteably uses `nvidia-smi` instead of torch to not initialize CUDA.\n    \"\"\"\n    try:\n        device_names, device_count = get_gpu_info()\n        # As new consumer GPUs get released, add them to `unsupported_devices``\n        unsupported_devices = {\"RTX 40\"}\n        if device_count > 1:\n            if any(\n                unsupported_device in device_name\n                for device_name in device_names\n                for unsupported_device in unsupported_devices\n            ):\n                return False\n    except Exception:\n        pass\n    return True\n\n\ndef check_fp8_capability():\n    \"\"\"\n    Checks if all the current GPUs available support FP8.\n\n    Notably must initialize `torch.cuda` to check.\n    \"\"\"\n    cuda_device_capacity = torch.cuda.get_device_capability()\n    return cuda_device_capacity >= (8, 9)\n", "path": "src/accelerate/utils/environment.py"}]}