Repository: python/mypy

Issue:
Support FORCE_COLOR in addition to MYPY_FORCE_COLOR
**Feature**

Force colour when the `FORCE_COLOR` environment variable is set.

**Pitch**


Mypy automatically adds colour to output for TTYs, or if the env var `MYPY_FORCE_COLOR` is set.

For example, GitHub Actions is not a tty but it's good to add colour to help read the logs.

`FORCE_COLOR` is used by many CLI tools and libraries.

It would be good if mypy forced colour output if either `FORCE_COLOR` or `MYPY_FORCE_COLOR` is set.

This helps avoid adding lots of separate env vars for each tool/CLI, [for example](https://github.com/jazzband/pip-tools/pull/1691):

```yml
env:
  FORCE_COLOR: 1 # Request colored output from CLI tools supporting it
  MYPY_FORCE_COLOR: 1 # MyPy's color enforcement
  PIP_DISABLE_PIP_VERSION_CHECK: 1
  PIP_NO_PYTHON_VERSION_WARNING: 1
  PIP_NO_WARN_SCRIPT_LOCATION: 1
  PY_COLORS: 1 # Recognized by the `py` package, dependency of `pytest`
  TOX_PARALLEL_NO_SPINNER: 1
  TOX_TESTENV_PASSENV: >-
    FORCE_COLOR
    MYPY_FORCE_COLOR
    NO_COLOR
    PY_COLORS
    PYTEST_THEME
    PYTEST_THEME_MODE
```


For reference:

* pytest [issue](https://github.com/pytest-dev/pytest/issues/7464), [PR](https://github.com/pytest-dev/pytest/pull/7466) and [docs](https://pytest.org/en/7.0.x/reference/reference.html#environment-variables)
* nox [issue](https://github.com/theacodes/nox/issues/502) and [PR](https://github.com/theacodes/nox/pull/524)
* tox [PR](https://github.com/tox-dev/tox/pull/1630)
* rich [release](https://github.com/Textualize/rich/releases/tag/v12.6.0), [issue](https://github.com/Textualize/rich/issues/2425) and [PR](https://github.com/Textualize/rich/pull/2449)
* pypa/build [PR](https://github.com/pypa/build/pull/335)
* Node.js [docs](https://nodejs.org/api/tty.html#writestreamgetcolordepthenv)



Discussion:

Resolving patch:
diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py
--- a/mypy/dmypy/client.py
+++ b/mypy/dmypy/client.py
@@ -19,7 +19,7 @@
 from mypy.dmypy_os import alive, kill
 from mypy.dmypy_util import DEFAULT_STATUS_FILE, receive
 from mypy.ipc import IPCClient, IPCException
-from mypy.util import check_python_version, get_terminal_width
+from mypy.util import check_python_version, get_terminal_width, should_force_color
 from mypy.version import __version__
 
 # Argument parser.  Subparsers are tied to action functions by the
@@ -653,7 +653,7 @@ def request(
     args["command"] = command
     # Tell the server whether this request was initiated from a human-facing terminal,
     # so that it can format the type checking output accordingly.
-    args["is_tty"] = sys.stdout.isatty() or int(os.getenv("MYPY_FORCE_COLOR", "0")) > 0
+    args["is_tty"] = sys.stdout.isatty() or should_force_color()
     args["terminal_width"] = get_terminal_width()
     bdata = json.dumps(args).encode("utf8")
     _, name = get_status(status_file)
diff --git a/mypy/util.py b/mypy/util.py
--- a/mypy/util.py
+++ b/mypy/util.py
@@ -519,6 +519,10 @@ def parse_gray_color(cup: bytes) -> str:
     return gray
 
 
+def should_force_color() -> bool:
+    return bool(int(os.getenv("MYPY_FORCE_COLOR", os.getenv("FORCE_COLOR", "0"))))
+
+
 class FancyFormatter:
     """Apply color and bold font to terminal output.
 
@@ -531,8 +535,7 @@ def __init__(self, f_out: IO[str], f_err: IO[str], hide_error_codes: bool) -> No
         if sys.platform not in ("linux", "darwin", "win32", "emscripten"):
             self.dummy_term = True
             return
-        force_color = int(os.getenv("MYPY_FORCE_COLOR", "0"))
-        if not force_color and (not f_out.isatty() or not f_err.isatty()):
+        if not should_force_color() and (not f_out.isatty() or not f_err.isatty()):
             self.dummy_term = True
             return
         if sys.platform == "win32":


Test patch: