prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  faster check for docker running
  currently this is how pre-commit checks if docker is available:
  
  https://github.com/pre-commit/pre-commit/blob/9a5461db24beeee9960423b853cfbfbaefb6cc7a/pre_commit/languages/docker.py#L29-L41
  
  there's two low-hanging-fruit problems with this:
  
  1. it is checked repeatedly, but can probably be checked just once per process (can use [`functools.lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache) as a "run once" decorator)
  2. `docker ps` is pretty slow, there's probably a faster way to see if docker is available -- on my machine it takes _more than a second_! even without containers
     ```console
     $ time docker ps
     CONTAINER ID  IMAGE   COMMAND  CREATED  STATUS  PORTS   NAMES
  
     real	0m1.169s
     user	0m0.088s
     sys	0m0.085s
     ```
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `pre_commit/languages/docker.py`
  Content:
  ```
  1 import hashlib
  2 import os
  3 from typing import Sequence
  4 from typing import Tuple
  5 
  6 import pre_commit.constants as C
  7 from pre_commit.hook import Hook
  8 from pre_commit.languages import helpers
  9 from pre_commit.prefix import Prefix
  10 from pre_commit.util import CalledProcessError
  11 from pre_commit.util import clean_path_on_failure
  12 from pre_commit.util import cmd_output_b
  13 
  14 ENVIRONMENT_DIR = 'docker'
  15 PRE_COMMIT_LABEL = 'PRE_COMMIT'
  16 get_default_version = helpers.basic_get_default_version
  17 healthy = helpers.basic_healthy
  18 
  19 
  20 def md5(s: str) -> str:  # pragma: win32 no cover
  21     return hashlib.md5(s.encode()).hexdigest()
  22 
  23 
  24 def docker_tag(prefix: Prefix) -> str:  # pragma: win32 no cover
  25     md5sum = md5(os.path.basename(prefix.prefix_dir)).lower()
  26     return f'pre-commit-{md5sum}'
  27 
  28 
  29 def docker_is_running() -> bool:  # pragma: win32 no cover
  30     try:
  31         cmd_output_b('docker', 'ps')
  32     except CalledProcessError:
  33         return False
  34     else:
  35         return True
  36 
  37 
  38 def assert_docker_available() -> None:  # pragma: win32 no cover
  39     assert docker_is_running(), (
  40         'Docker is either not running or not configured in this environment'
  41     )
  42 
  43 
  44 def build_docker_image(
  45         prefix: Prefix,
  46         *,
  47         pull: bool,
  48 ) -> None:  # pragma: win32 no cover
  49     cmd: Tuple[str, ...] = (
  50         'docker', 'build',
  51         '--tag', docker_tag(prefix),
  52         '--label', PRE_COMMIT_LABEL,
  53     )
  54     if pull:
  55         cmd += ('--pull',)
  56     # This must come last for old versions of docker.  See #477
  57     cmd += ('.',)
  58     helpers.run_setup_cmd(prefix, cmd)
  59 
  60 
  61 def install_environment(
  62         prefix: Prefix, version: str, additional_dependencies: Sequence[str],
  63 ) -> None:  # pragma: win32 no cover
  64     helpers.assert_version_default('docker', version)
  65     helpers.assert_no_additional_deps('docker', additional_dependencies)
  66     assert_docker_available()
  67 
  68     directory = prefix.path(
  69         helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT),
  70     )
  71 
  72     # Docker doesn't really have relevant disk environment, but pre-commit
  73     # still needs to cleanup its state files on failure
  74     with clean_path_on_failure(directory):
  75         build_docker_image(prefix, pull=True)
  76         os.mkdir(directory)
  77 
  78 
  79 def get_docker_user() -> Tuple[str, ...]:  # pragma: win32 no cover
  80     try:
  81         return ('-u', f'{os.getuid()}:{os.getgid()}')
  82     except AttributeError:
  83         return ()
  84 
  85 
  86 def docker_cmd() -> Tuple[str, ...]:  # pragma: win32 no cover
  87     return (
  88         'docker', 'run',
  89         '--rm',
  90         *get_docker_user(),
  91         # https://docs.docker.com/engine/reference/commandline/run/#mount-volumes-from-container-volumes-from
  92         # The `Z` option tells Docker to label the content with a private
  93         # unshared label. Only the current container can use a private volume.
  94         '-v', f'{os.getcwd()}:/src:rw,Z',
  95         '--workdir', '/src',
  96     )
  97 
  98 
  99 def run_hook(
  100         hook: Hook,
  101         file_args: Sequence[str],
  102         color: bool,
  103 ) -> Tuple[int, bytes]:  # pragma: win32 no cover
  104     assert_docker_available()
  105     # Rebuild the docker image in case it has gone missing, as many people do
  106     # automated cleanup of docker images.
  107     build_docker_image(hook.prefix, pull=False)
  108 
  109     hook_cmd = hook.cmd
  110     entry_exe, cmd_rest = hook.cmd[0], hook_cmd[1:]
  111 
  112     entry_tag = ('--entrypoint', entry_exe, docker_tag(hook.prefix))
  113     cmd = docker_cmd() + entry_tag + cmd_rest
  114     return helpers.run_xargs(hook, cmd, file_args, color=color)
  115
  ```
  
  Path: `pre_commit/languages/docker_image.py`
  Content:
  ```
  1 from typing import Sequence
  2 from typing import Tuple
  3 
  4 from pre_commit.hook import Hook
  5 from pre_commit.languages import helpers
  6 from pre_commit.languages.docker import assert_docker_available
  7 from pre_commit.languages.docker import docker_cmd
  8 
  9 ENVIRONMENT_DIR = None
  10 get_default_version = helpers.basic_get_default_version
  11 healthy = helpers.basic_healthy
  12 install_environment = helpers.no_install
  13 
  14 
  15 def run_hook(
  16         hook: Hook,
  17         file_args: Sequence[str],
  18         color: bool,
  19 ) -> Tuple[int, bytes]:  # pragma: win32 no cover
  20     assert_docker_available()
  21     cmd = docker_cmd() + hook.cmd
  22     return helpers.run_xargs(hook, cmd, file_args, color=color)
  23
  ```
  --- 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/pre_commit/languages/docker.py b/pre_commit/languages/docker.py
  --- a/pre_commit/languages/docker.py
  +++ b/pre_commit/languages/docker.py
  @@ -7,9 +7,7 @@
   from pre_commit.hook import Hook
   from pre_commit.languages import helpers
   from pre_commit.prefix import Prefix
  -from pre_commit.util import CalledProcessError
   from pre_commit.util import clean_path_on_failure
  -from pre_commit.util import cmd_output_b
   
   ENVIRONMENT_DIR = 'docker'
   PRE_COMMIT_LABEL = 'PRE_COMMIT'
  @@ -26,21 +24,6 @@
       return f'pre-commit-{md5sum}'
   
   
  -def docker_is_running() -> bool:  # pragma: win32 no cover
  -    try:
  -        cmd_output_b('docker', 'ps')
  -    except CalledProcessError:
  -        return False
  -    else:
  -        return True
  -
  -
  -def assert_docker_available() -> None:  # pragma: win32 no cover
  -    assert docker_is_running(), (
  -        'Docker is either not running or not configured in this environment'
  -    )
  -
  -
   def build_docker_image(
           prefix: Prefix,
           *,
  @@ -63,7 +46,6 @@
   ) -> None:  # pragma: win32 no cover
       helpers.assert_version_default('docker', version)
       helpers.assert_no_additional_deps('docker', additional_dependencies)
  -    assert_docker_available()
   
       directory = prefix.path(
           helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT),
  @@ -101,7 +83,6 @@
           file_args: Sequence[str],
           color: bool,
   ) -> Tuple[int, bytes]:  # pragma: win32 no cover
  -    assert_docker_available()
       # Rebuild the docker image in case it has gone missing, as many people do
       # automated cleanup of docker images.
       build_docker_image(hook.prefix, pull=False)
  diff --git a/pre_commit/languages/docker_image.py b/pre_commit/languages/docker_image.py
  --- a/pre_commit/languages/docker_image.py
  +++ b/pre_commit/languages/docker_image.py
  @@ -3,7 +3,6 @@
   
   from pre_commit.hook import Hook
   from pre_commit.languages import helpers
  -from pre_commit.languages.docker import assert_docker_available
   from pre_commit.languages.docker import docker_cmd
   
   ENVIRONMENT_DIR = None
  @@ -17,6 +16,5 @@
           file_args: Sequence[str],
           color: bool,
   ) -> Tuple[int, bytes]:  # pragma: win32 no cover
  -    assert_docker_available()
       cmd = docker_cmd() + hook.cmd
       return helpers.run_xargs(hook, cmd, file_args, color=color)
  
verification_info: {"golden_diff": "diff --git a/pre_commit/languages/docker.py b/pre_commit/languages/docker.py\n--- a/pre_commit/languages/docker.py\n+++ b/pre_commit/languages/docker.py\n@@ -7,9 +7,7 @@\n from pre_commit.hook import Hook\n from pre_commit.languages import helpers\n from pre_commit.prefix import Prefix\n-from pre_commit.util import CalledProcessError\n from pre_commit.util import clean_path_on_failure\n-from pre_commit.util import cmd_output_b\n \n ENVIRONMENT_DIR = 'docker'\n PRE_COMMIT_LABEL = 'PRE_COMMIT'\n@@ -26,21 +24,6 @@\n     return f'pre-commit-{md5sum}'\n \n \n-def docker_is_running() -> bool:  # pragma: win32 no cover\n-    try:\n-        cmd_output_b('docker', 'ps')\n-    except CalledProcessError:\n-        return False\n-    else:\n-        return True\n-\n-\n-def assert_docker_available() -> None:  # pragma: win32 no cover\n-    assert docker_is_running(), (\n-        'Docker is either not running or not configured in this environment'\n-    )\n-\n-\n def build_docker_image(\n         prefix: Prefix,\n         *,\n@@ -63,7 +46,6 @@\n ) -> None:  # pragma: win32 no cover\n     helpers.assert_version_default('docker', version)\n     helpers.assert_no_additional_deps('docker', additional_dependencies)\n-    assert_docker_available()\n \n     directory = prefix.path(\n         helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT),\n@@ -101,7 +83,6 @@\n         file_args: Sequence[str],\n         color: bool,\n ) -> Tuple[int, bytes]:  # pragma: win32 no cover\n-    assert_docker_available()\n     # Rebuild the docker image in case it has gone missing, as many people do\n     # automated cleanup of docker images.\n     build_docker_image(hook.prefix, pull=False)\ndiff --git a/pre_commit/languages/docker_image.py b/pre_commit/languages/docker_image.py\n--- a/pre_commit/languages/docker_image.py\n+++ b/pre_commit/languages/docker_image.py\n@@ -3,7 +3,6 @@\n \n from pre_commit.hook import Hook\n from pre_commit.languages import helpers\n-from pre_commit.languages.docker import assert_docker_available\n from pre_commit.languages.docker import docker_cmd\n \n ENVIRONMENT_DIR = None\n@@ -17,6 +16,5 @@\n         file_args: Sequence[str],\n         color: bool,\n ) -> Tuple[int, bytes]:  # pragma: win32 no cover\n-    assert_docker_available()\n     cmd = docker_cmd() + hook.cmd\n     return helpers.run_xargs(hook, cmd, file_args, color=color)\n", "issue": "faster check for docker running\ncurrently this is how pre-commit checks if docker is available:\r\n\r\nhttps://github.com/pre-commit/pre-commit/blob/9a5461db24beeee9960423b853cfbfbaefb6cc7a/pre_commit/languages/docker.py#L29-L41\r\n\r\nthere's two low-hanging-fruit problems with this:\r\n\r\n1. it is checked repeatedly, but can probably be checked just once per process (can use [`functools.lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache) as a \"run once\" decorator)\r\n2. `docker ps` is pretty slow, there's probably a faster way to see if docker is available -- on my machine it takes _more than a second_! even without containers\r\n   ```console\r\n   $ time docker ps\r\n   CONTAINER ID  IMAGE   COMMAND  CREATED  STATUS  PORTS   NAMES\r\n\r\n   real\t0m1.169s\r\n   user\t0m0.088s\r\n   sys\t0m0.085s\r\n   ```\n", "before_files": [{"content": "import hashlib\nimport os\nfrom typing import Sequence\nfrom typing import Tuple\n\nimport pre_commit.constants as C\nfrom pre_commit.hook import Hook\nfrom pre_commit.languages import helpers\nfrom pre_commit.prefix import Prefix\nfrom pre_commit.util import CalledProcessError\nfrom pre_commit.util import clean_path_on_failure\nfrom pre_commit.util import cmd_output_b\n\nENVIRONMENT_DIR = 'docker'\nPRE_COMMIT_LABEL = 'PRE_COMMIT'\nget_default_version = helpers.basic_get_default_version\nhealthy = helpers.basic_healthy\n\n\ndef md5(s: str) -> str:  # pragma: win32 no cover\n    return hashlib.md5(s.encode()).hexdigest()\n\n\ndef docker_tag(prefix: Prefix) -> str:  # pragma: win32 no cover\n    md5sum = md5(os.path.basename(prefix.prefix_dir)).lower()\n    return f'pre-commit-{md5sum}'\n\n\ndef docker_is_running() -> bool:  # pragma: win32 no cover\n    try:\n        cmd_output_b('docker', 'ps')\n    except CalledProcessError:\n        return False\n    else:\n        return True\n\n\ndef assert_docker_available() -> None:  # pragma: win32 no cover\n    assert docker_is_running(), (\n        'Docker is either not running or not configured in this environment'\n    )\n\n\ndef build_docker_image(\n        prefix: Prefix,\n        *,\n        pull: bool,\n) -> None:  # pragma: win32 no cover\n    cmd: Tuple[str, ...] = (\n        'docker', 'build',\n        '--tag', docker_tag(prefix),\n        '--label', PRE_COMMIT_LABEL,\n    )\n    if pull:\n        cmd += ('--pull',)\n    # This must come last for old versions of docker.  See #477\n    cmd += ('.',)\n    helpers.run_setup_cmd(prefix, cmd)\n\n\ndef install_environment(\n        prefix: Prefix, version: str, additional_dependencies: Sequence[str],\n) -> None:  # pragma: win32 no cover\n    helpers.assert_version_default('docker', version)\n    helpers.assert_no_additional_deps('docker', additional_dependencies)\n    assert_docker_available()\n\n    directory = prefix.path(\n        helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT),\n    )\n\n    # Docker doesn't really have relevant disk environment, but pre-commit\n    # still needs to cleanup its state files on failure\n    with clean_path_on_failure(directory):\n        build_docker_image(prefix, pull=True)\n        os.mkdir(directory)\n\n\ndef get_docker_user() -> Tuple[str, ...]:  # pragma: win32 no cover\n    try:\n        return ('-u', f'{os.getuid()}:{os.getgid()}')\n    except AttributeError:\n        return ()\n\n\ndef docker_cmd() -> Tuple[str, ...]:  # pragma: win32 no cover\n    return (\n        'docker', 'run',\n        '--rm',\n        *get_docker_user(),\n        # https://docs.docker.com/engine/reference/commandline/run/#mount-volumes-from-container-volumes-from\n        # The `Z` option tells Docker to label the content with a private\n        # unshared label. Only the current container can use a private volume.\n        '-v', f'{os.getcwd()}:/src:rw,Z',\n        '--workdir', '/src',\n    )\n\n\ndef run_hook(\n        hook: Hook,\n        file_args: Sequence[str],\n        color: bool,\n) -> Tuple[int, bytes]:  # pragma: win32 no cover\n    assert_docker_available()\n    # Rebuild the docker image in case it has gone missing, as many people do\n    # automated cleanup of docker images.\n    build_docker_image(hook.prefix, pull=False)\n\n    hook_cmd = hook.cmd\n    entry_exe, cmd_rest = hook.cmd[0], hook_cmd[1:]\n\n    entry_tag = ('--entrypoint', entry_exe, docker_tag(hook.prefix))\n    cmd = docker_cmd() + entry_tag + cmd_rest\n    return helpers.run_xargs(hook, cmd, file_args, color=color)\n", "path": "pre_commit/languages/docker.py"}, {"content": "from typing import Sequence\nfrom typing import Tuple\n\nfrom pre_commit.hook import Hook\nfrom pre_commit.languages import helpers\nfrom pre_commit.languages.docker import assert_docker_available\nfrom pre_commit.languages.docker import docker_cmd\n\nENVIRONMENT_DIR = None\nget_default_version = helpers.basic_get_default_version\nhealthy = helpers.basic_healthy\ninstall_environment = helpers.no_install\n\n\ndef run_hook(\n        hook: Hook,\n        file_args: Sequence[str],\n        color: bool,\n) -> Tuple[int, bytes]:  # pragma: win32 no cover\n    assert_docker_available()\n    cmd = docker_cmd() + hook.cmd\n    return helpers.run_xargs(hook, cmd, file_args, color=color)\n", "path": "pre_commit/languages/docker_image.py"}], "after_files": [{"content": "import hashlib\nimport os\nfrom typing import Sequence\nfrom typing import Tuple\n\nimport pre_commit.constants as C\nfrom pre_commit.hook import Hook\nfrom pre_commit.languages import helpers\nfrom pre_commit.prefix import Prefix\nfrom pre_commit.util import clean_path_on_failure\n\nENVIRONMENT_DIR = 'docker'\nPRE_COMMIT_LABEL = 'PRE_COMMIT'\nget_default_version = helpers.basic_get_default_version\nhealthy = helpers.basic_healthy\n\n\ndef md5(s: str) -> str:  # pragma: win32 no cover\n    return hashlib.md5(s.encode()).hexdigest()\n\n\ndef docker_tag(prefix: Prefix) -> str:  # pragma: win32 no cover\n    md5sum = md5(os.path.basename(prefix.prefix_dir)).lower()\n    return f'pre-commit-{md5sum}'\n\n\ndef build_docker_image(\n        prefix: Prefix,\n        *,\n        pull: bool,\n) -> None:  # pragma: win32 no cover\n    cmd: Tuple[str, ...] = (\n        'docker', 'build',\n        '--tag', docker_tag(prefix),\n        '--label', PRE_COMMIT_LABEL,\n    )\n    if pull:\n        cmd += ('--pull',)\n    # This must come last for old versions of docker.  See #477\n    cmd += ('.',)\n    helpers.run_setup_cmd(prefix, cmd)\n\n\ndef install_environment(\n        prefix: Prefix, version: str, additional_dependencies: Sequence[str],\n) -> None:  # pragma: win32 no cover\n    helpers.assert_version_default('docker', version)\n    helpers.assert_no_additional_deps('docker', additional_dependencies)\n\n    directory = prefix.path(\n        helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT),\n    )\n\n    # Docker doesn't really have relevant disk environment, but pre-commit\n    # still needs to cleanup its state files on failure\n    with clean_path_on_failure(directory):\n        build_docker_image(prefix, pull=True)\n        os.mkdir(directory)\n\n\ndef get_docker_user() -> Tuple[str, ...]:  # pragma: win32 no cover\n    try:\n        return ('-u', f'{os.getuid()}:{os.getgid()}')\n    except AttributeError:\n        return ()\n\n\ndef docker_cmd() -> Tuple[str, ...]:  # pragma: win32 no cover\n    return (\n        'docker', 'run',\n        '--rm',\n        *get_docker_user(),\n        # https://docs.docker.com/engine/reference/commandline/run/#mount-volumes-from-container-volumes-from\n        # The `Z` option tells Docker to label the content with a private\n        # unshared label. Only the current container can use a private volume.\n        '-v', f'{os.getcwd()}:/src:rw,Z',\n        '--workdir', '/src',\n    )\n\n\ndef run_hook(\n        hook: Hook,\n        file_args: Sequence[str],\n        color: bool,\n) -> Tuple[int, bytes]:  # pragma: win32 no cover\n    # Rebuild the docker image in case it has gone missing, as many people do\n    # automated cleanup of docker images.\n    build_docker_image(hook.prefix, pull=False)\n\n    hook_cmd = hook.cmd\n    entry_exe, cmd_rest = hook.cmd[0], hook_cmd[1:]\n\n    entry_tag = ('--entrypoint', entry_exe, docker_tag(hook.prefix))\n    cmd = docker_cmd() + entry_tag + cmd_rest\n    return helpers.run_xargs(hook, cmd, file_args, color=color)\n", "path": "pre_commit/languages/docker.py"}, {"content": "from typing import Sequence\nfrom typing import Tuple\n\nfrom pre_commit.hook import Hook\nfrom pre_commit.languages import helpers\nfrom pre_commit.languages.docker import docker_cmd\n\nENVIRONMENT_DIR = None\nget_default_version = helpers.basic_get_default_version\nhealthy = helpers.basic_healthy\ninstall_environment = helpers.no_install\n\n\ndef run_hook(\n        hook: Hook,\n        file_args: Sequence[str],\n        color: bool,\n) -> Tuple[int, bytes]:  # pragma: win32 no cover\n    cmd = docker_cmd() + hook.cmd\n    return helpers.run_xargs(hook, cmd, file_args, color=color)\n", "path": "pre_commit/languages/docker_image.py"}]}