Repository: dask/distributed

Issue:
importing distributed runs 4 `git` subprocesses in CI (when installed with -e)
I noticed that tests that run a dask subprocess are often flakey on CI (especially so on low performance macos runners)

https://github.com/dask/distributed/runs/4922796526?check_suite_focus=true#step:12:1849
This is an example of a process taking more than 5 seconds to boot on a mac in `test_dask_worker::test_memory_limit`:

```pytb
Traceback (most recent call last):
  File "/Users/runner/miniconda3/envs/dask-distributed/bin/dask-worker", line 33, in <module>
    sys.exit(load_entry_point('distributed', 'console_scripts', 'dask-worker')())
  File "/Users/runner/miniconda3/envs/dask-distributed/bin/dask-worker", line 25, in importlib_load_entry_point
    return next(matches).load()
  File "/Users/runner/miniconda3/envs/dask-distributed/lib/python3.9/importlib/metadata.py", line 77, in load
    module = import_module(match.group('module'))
  File "/Users/runner/miniconda3/envs/dask-distributed/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 972, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 972, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "/Users/runner/work/distributed/distributed/distributed/__init__.py", line 49, in <module>
    versions = get_versions()
  File "/Users/runner/work/distributed/distributed/distributed/_version.py", line 534, in get_versions
    pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
  File "/Users/runner/work/distributed/distributed/distributed/_version.py", line 265, in git_pieces_from_vcs
    full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
  File "/Users/runner/work/distributed/distributed/distributed/_version.py", line 78, in run_command
    p = subprocess.Popen(
  File "/Users/runner/miniconda3/envs/dask-distributed/lib/python3.9/subprocess.py", line 951, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/Users/runner/miniconda3/envs/dask-distributed/lib/python3.9/subprocess.py", line 1777, in _execute_child
    part = os.read(errpipe_read, 50000)
KeyboardInterrupt
```


Discussion:
git_pieces_from_vcs is here:
https://github.com/dask/distributed/blob/0f6a4dfe6fe985779285ed6d0a03ac4b1c06ceee/distributed/_version.py#L228-L326
cc @crusaderky 
The title suggests this is happening when installing with `-e .`. Is this not the case for non-editable installs?
for `test_dask_worker::test_memory_limit` that's `8` git subprocess calls that need to complete in 5 seconds
> The title suggests this is happening when installing with `-e .`. Is this not the case for non-editable installs?

for sdists the "_version.py" is transparently replaced with a stub:

```python
# This file was generated by 'versioneer.py' (0.17) from
# revision-control system data, or from the parent directory name of an
# unpacked source archive. Distribution tarballs contain a pre-generated copy
# of this file.

import json

version_json = '''
{
 "date": "2022-01-14T14:37:43-0600",
 "dirty": false,
 "error": null,
 "full-revisionid": "467c531f1bc5ec038a3c7f8d3ed2f2eb47c9e71a",
 "version": "2022.01.0"
}
'''  # END VERSION_JSON


def get_versions():
    return json.loads(version_json)
```

https://github.com/dask/distributed/blob/0f6a4dfe6fe985779285ed6d0a03ac4b1c06ceee/versioneer.py#L1619-L1639

Resolving patch:
diff --git a/distributed/__init__.py b/distributed/__init__.py
--- a/distributed/__init__.py
+++ b/distributed/__init__.py
@@ -1,10 +1,12 @@
 from . import config  # isort:skip; load distributed configuration first
 from . import widgets  # isort:skip; load distributed widgets second
+
+
 import dask
 from dask.config import config  # type: ignore
 
 from ._version import get_versions
-from .actor import Actor, BaseActorFuture
+from .actor import Actor, ActorFuture, BaseActorFuture
 from .client import (
     Client,
     CompatibleExecutor,
@@ -46,7 +48,20 @@
 from .worker import Reschedule, Worker, get_client, get_worker, print, secede, warn
 from .worker_client import local_client, worker_client
 
-versions = get_versions()
-__version__ = versions["version"]
-__git_revision__ = versions["full-revisionid"]
-del get_versions, versions
+
+def __getattr__(name):
+    global __version__, __git_revision__
+
+    if name == "__version__":
+        from importlib.metadata import version
+
+        __version__ = version("distributed")
+        return __version__
+
+    if name == "__git_revision__":
+        from ._version import get_versions
+
+        __git_revision__ = get_versions()["full-revisionid"]
+        return __git_revision__
+
+    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")


Test patch:
diff --git a/distributed/tests/test_init.py b/distributed/tests/test_init.py
new file mode 100644
--- /dev/null
+++ b/distributed/tests/test_init.py
@@ -0,0 +1,13 @@
+from __future__ import annotations
+
+import importlib.metadata
+
+import distributed
+
+
+def test_version() -> None:
+    assert distributed.__version__ == importlib.metadata.version("distributed")
+
+
+def test_git_revision() -> None:
+    assert isinstance(distributed.__git_revision__, str)