We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 9: invalid start byte
Hi, there! Get such error on commit.

Probably, because of cyrillic symbols in user name: 'C:\\Users\\Администратор\...'.

Is there a way to avoid this problem exept renaming user?

Thanks for your cool product!

### version information

```
pre-commit version: 2.4.0
sys.version:
    3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)]
sys.executable: c:\program files\git\dev\core\venv\scripts\python.exe
os.name: nt
sys.platform: win32
```

### error information

```
An unexpected error has occurred: UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 9: invalid start byte
```

```
Traceback (most recent call last):
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\error_handler.py", line 56, in error_handler
    yield
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\main.py", line 372, in main
    args=args.rest[1:],
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\commands\hook_impl.py", line 217, in hook_impl
    return retv | run(config, store, ns)
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\commands\run.py", line 357, in run
    for hook in all_hooks(config, store)
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\repository.py", line 206, in all_hooks
    for repo in root_config['repos']
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\repository.py", line 207, in <genexpr>
    for hook in _repository_hooks(repo, store, root_config)
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\repository.py", line 182, in _repository_hooks
    return _cloned_repository_hooks(repo_config, store, root_config)
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\repository.py", line 162, in _cloned_repository_hooks
    for hook in repo_config['hooks']
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\repository.py", line 162, in <listcomp>
    for hook in repo_config['hooks']
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\repository.py", line 110, in _hook
    ret['language_version'] = languages[lang].get_default_version()
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\languages\python.py", line 113, in get_default_version
    if _find_by_py_launcher(exe):
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\languages\python.py", line 72, in _find_by_py_launcher
    return cmd_output(*cmd)[1].strip()
  File "c:\program files\git\dev\core\venv\lib\site-packages\pre_commit\util.py", line 164, in cmd_output
    stdout = stdout_b.decode() if stdout_b is not None else None
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 9: invalid start byte

```

--- 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/python.py`
Content:
```
1 import contextlib
2 import functools
3 import os
4 import sys
5 from typing import Dict
6 from typing import Generator
7 from typing import Optional
8 from typing import Sequence
9 from typing import Tuple
10 
11 import pre_commit.constants as C
12 from pre_commit.envcontext import envcontext
13 from pre_commit.envcontext import PatchesT
14 from pre_commit.envcontext import UNSET
15 from pre_commit.envcontext import Var
16 from pre_commit.hook import Hook
17 from pre_commit.languages import helpers
18 from pre_commit.parse_shebang import find_executable
19 from pre_commit.prefix import Prefix
20 from pre_commit.util import CalledProcessError
21 from pre_commit.util import clean_path_on_failure
22 from pre_commit.util import cmd_output
23 from pre_commit.util import cmd_output_b
24 
25 ENVIRONMENT_DIR = 'py_env'
26 
27 
28 @functools.lru_cache(maxsize=None)
29 def _version_info(exe: str) -> str:
30     prog = 'import sys;print(".".join(str(p) for p in sys.version_info))'
31     try:
32         return cmd_output(exe, '-S', '-c', prog)[1].strip()
33     except CalledProcessError:
34         return f'<<error retrieving version from {exe}>>'
35 
36 
37 def _read_pyvenv_cfg(filename: str) -> Dict[str, str]:
38     ret = {}
39     with open(filename) as f:
40         for line in f:
41             try:
42                 k, v = line.split('=')
43             except ValueError:  # blank line / comment / etc.
44                 continue
45             else:
46                 ret[k.strip()] = v.strip()
47     return ret
48 
49 
50 def bin_dir(venv: str) -> str:
51     """On windows there's a different directory for the virtualenv"""
52     bin_part = 'Scripts' if os.name == 'nt' else 'bin'
53     return os.path.join(venv, bin_part)
54 
55 
56 def get_env_patch(venv: str) -> PatchesT:
57     return (
58         ('PIP_DISABLE_PIP_VERSION_CHECK', '1'),
59         ('PYTHONHOME', UNSET),
60         ('VIRTUAL_ENV', venv),
61         ('PATH', (bin_dir(venv), os.pathsep, Var('PATH'))),
62     )
63 
64 
65 def _find_by_py_launcher(
66         version: str,
67 ) -> Optional[str]:  # pragma: no cover (windows only)
68     if version.startswith('python'):
69         num = version[len('python'):]
70         try:
71             cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')
72             return cmd_output(*cmd)[1].strip()
73         except CalledProcessError:
74             pass
75     return None
76 
77 
78 def _find_by_sys_executable() -> Optional[str]:
79     def _norm(path: str) -> Optional[str]:
80         _, exe = os.path.split(path.lower())
81         exe, _, _ = exe.partition('.exe')
82         if exe not in {'python', 'pythonw'} and find_executable(exe):
83             return exe
84         return None
85 
86     # On linux, I see these common sys.executables:
87     #
88     # system `python`: /usr/bin/python -> python2.7
89     # system `python2`: /usr/bin/python2 -> python2.7
90     # virtualenv v: v/bin/python (will not return from this loop)
91     # virtualenv v -ppython2: v/bin/python -> python2
92     # virtualenv v -ppython2.7: v/bin/python -> python2.7
93     # virtualenv v -ppypy: v/bin/python -> v/bin/pypy
94     for path in (sys.executable, os.path.realpath(sys.executable)):
95         exe = _norm(path)
96         if exe:
97             return exe
98     return None
99 
100 
101 @functools.lru_cache(maxsize=1)
102 def get_default_version() -> str:  # pragma: no cover (platform dependent)
103     # First attempt from `sys.executable` (or the realpath)
104     exe = _find_by_sys_executable()
105     if exe:
106         return exe
107 
108     # Next try the `pythonX.X` executable
109     exe = f'python{sys.version_info[0]}.{sys.version_info[1]}'
110     if find_executable(exe):
111         return exe
112 
113     if _find_by_py_launcher(exe):
114         return exe
115 
116     # Give a best-effort try for windows
117     default_folder_name = exe.replace('.', '')
118     if os.path.exists(fr'C:\{default_folder_name}\python.exe'):
119         return exe
120 
121     # We tried!
122     return C.DEFAULT
123 
124 
125 def _sys_executable_matches(version: str) -> bool:
126     if version == 'python':
127         return True
128     elif not version.startswith('python'):
129         return False
130 
131     try:
132         info = tuple(int(p) for p in version[len('python'):].split('.'))
133     except ValueError:
134         return False
135 
136     return sys.version_info[:len(info)] == info
137 
138 
139 def norm_version(version: str) -> str:
140     if version == C.DEFAULT:
141         return os.path.realpath(sys.executable)
142 
143     # first see if our current executable is appropriate
144     if _sys_executable_matches(version):
145         return sys.executable
146 
147     if os.name == 'nt':  # pragma: no cover (windows)
148         version_exec = _find_by_py_launcher(version)
149         if version_exec:
150             return version_exec
151 
152         # Try looking up by name
153         version_exec = find_executable(version)
154         if version_exec and version_exec != version:
155             return version_exec
156 
157         # If it is in the form pythonx.x search in the default
158         # place on windows
159         if version.startswith('python'):
160             default_folder_name = version.replace('.', '')
161             return fr'C:\{default_folder_name}\python.exe'
162 
163     # Otherwise assume it is a path
164     return os.path.expanduser(version)
165 
166 
167 @contextlib.contextmanager
168 def in_env(
169         prefix: Prefix,
170         language_version: str,
171 ) -> Generator[None, None, None]:
172     directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)
173     envdir = prefix.path(directory)
174     with envcontext(get_env_patch(envdir)):
175         yield
176 
177 
178 def healthy(prefix: Prefix, language_version: str) -> bool:
179     directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)
180     envdir = prefix.path(directory)
181     pyvenv_cfg = os.path.join(envdir, 'pyvenv.cfg')
182 
183     # created with "old" virtualenv
184     if not os.path.exists(pyvenv_cfg):
185         return False
186 
187     exe_name = 'python.exe' if sys.platform == 'win32' else 'python'
188     py_exe = prefix.path(bin_dir(envdir), exe_name)
189     cfg = _read_pyvenv_cfg(pyvenv_cfg)
190 
191     return (
192         'version_info' in cfg and
193         _version_info(py_exe) == cfg['version_info'] and (
194             'base-executable' not in cfg or
195             _version_info(cfg['base-executable']) == cfg['version_info']
196         )
197     )
198 
199 
200 def install_environment(
201         prefix: Prefix,
202         version: str,
203         additional_dependencies: Sequence[str],
204 ) -> None:
205     envdir = prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))
206     python = norm_version(version)
207     venv_cmd = (sys.executable, '-mvirtualenv', envdir, '-p', python)
208     install_cmd = ('python', '-mpip', 'install', '.', *additional_dependencies)
209 
210     with clean_path_on_failure(envdir):
211         cmd_output_b(*venv_cmd, cwd='/')
212         with in_env(prefix, version):
213             helpers.run_setup_cmd(prefix, install_cmd)
214 
215 
216 def run_hook(
217         hook: Hook,
218         file_args: Sequence[str],
219         color: bool,
220 ) -> Tuple[int, bytes]:
221     with in_env(hook.prefix, hook.language_version):
222         return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
223
```
--- 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()

```

diff --git a/pre_commit/languages/python.py b/pre_commit/languages/python.py
--- a/pre_commit/languages/python.py
+++ b/pre_commit/languages/python.py
@@ -67,9 +67,10 @@
 ) -> Optional[str]:  # pragma: no cover (windows only)
     if version.startswith('python'):
         num = version[len('python'):]
+        cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')
+        env = dict(os.environ, PYTHONIOENCODING='UTF-8')
         try:
-            cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')
-            return cmd_output(*cmd)[1].strip()
+            return cmd_output(*cmd, env=env)[1].strip()
         except CalledProcessError:
             pass
     return None

{"golden_diff": "diff --git a/pre_commit/languages/python.py b/pre_commit/languages/python.py\n--- a/pre_commit/languages/python.py\n+++ b/pre_commit/languages/python.py\n@@ -67,9 +67,10 @@\n ) -> Optional[str]:  # pragma: no cover (windows only)\n     if version.startswith('python'):\n         num = version[len('python'):]\n+        cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')\n+        env = dict(os.environ, PYTHONIOENCODING='UTF-8')\n         try:\n-            cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')\n-            return cmd_output(*cmd)[1].strip()\n+            return cmd_output(*cmd, env=env)[1].strip()\n         except CalledProcessError:\n             pass\n     return None\n", "issue": "UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 9: invalid start byte\nHi, there! Get such error on commit.\r\n\r\nProbably, because of cyrillic symbols in user name: 'C:\\\\Users\\\\\u0410\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\\...'.\r\n\r\nIs there a way to avoid this problem exept renaming user?\r\n\r\nThanks for your cool product!\r\n\r\n### version information\r\n\r\n```\r\npre-commit version: 2.4.0\r\nsys.version:\r\n    3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)]\r\nsys.executable: c:\\program files\\git\\dev\\core\\venv\\scripts\\python.exe\r\nos.name: nt\r\nsys.platform: win32\r\n```\r\n\r\n### error information\r\n\r\n```\r\nAn unexpected error has occurred: UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 9: invalid start byte\r\n```\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\error_handler.py\", line 56, in error_handler\r\n    yield\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\main.py\", line 372, in main\r\n    args=args.rest[1:],\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\commands\\hook_impl.py\", line 217, in hook_impl\r\n    return retv | run(config, store, ns)\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\commands\\run.py\", line 357, in run\r\n    for hook in all_hooks(config, store)\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\repository.py\", line 206, in all_hooks\r\n    for repo in root_config['repos']\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\repository.py\", line 207, in <genexpr>\r\n    for hook in _repository_hooks(repo, store, root_config)\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\repository.py\", line 182, in _repository_hooks\r\n    return _cloned_repository_hooks(repo_config, store, root_config)\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\repository.py\", line 162, in _cloned_repository_hooks\r\n    for hook in repo_config['hooks']\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\repository.py\", line 162, in <listcomp>\r\n    for hook in repo_config['hooks']\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\repository.py\", line 110, in _hook\r\n    ret['language_version'] = languages[lang].get_default_version()\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\languages\\python.py\", line 113, in get_default_version\r\n    if _find_by_py_launcher(exe):\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\languages\\python.py\", line 72, in _find_by_py_launcher\r\n    return cmd_output(*cmd)[1].strip()\r\n  File \"c:\\program files\\git\\dev\\core\\venv\\lib\\site-packages\\pre_commit\\util.py\", line 164, in cmd_output\r\n    stdout = stdout_b.decode() if stdout_b is not None else None\r\nUnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 9: invalid start byte\r\n\r\n```\n", "before_files": [{"content": "import contextlib\nimport functools\nimport os\nimport sys\nfrom typing import Dict\nfrom typing import Generator\nfrom typing import Optional\nfrom typing import Sequence\nfrom typing import Tuple\n\nimport pre_commit.constants as C\nfrom pre_commit.envcontext import envcontext\nfrom pre_commit.envcontext import PatchesT\nfrom pre_commit.envcontext import UNSET\nfrom pre_commit.envcontext import Var\nfrom pre_commit.hook import Hook\nfrom pre_commit.languages import helpers\nfrom pre_commit.parse_shebang import find_executable\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\nfrom pre_commit.util import cmd_output_b\n\nENVIRONMENT_DIR = 'py_env'\n\n\n@functools.lru_cache(maxsize=None)\ndef _version_info(exe: str) -> str:\n    prog = 'import sys;print(\".\".join(str(p) for p in sys.version_info))'\n    try:\n        return cmd_output(exe, '-S', '-c', prog)[1].strip()\n    except CalledProcessError:\n        return f'<<error retrieving version from {exe}>>'\n\n\ndef _read_pyvenv_cfg(filename: str) -> Dict[str, str]:\n    ret = {}\n    with open(filename) as f:\n        for line in f:\n            try:\n                k, v = line.split('=')\n            except ValueError:  # blank line / comment / etc.\n                continue\n            else:\n                ret[k.strip()] = v.strip()\n    return ret\n\n\ndef bin_dir(venv: str) -> str:\n    \"\"\"On windows there's a different directory for the virtualenv\"\"\"\n    bin_part = 'Scripts' if os.name == 'nt' else 'bin'\n    return os.path.join(venv, bin_part)\n\n\ndef get_env_patch(venv: str) -> PatchesT:\n    return (\n        ('PIP_DISABLE_PIP_VERSION_CHECK', '1'),\n        ('PYTHONHOME', UNSET),\n        ('VIRTUAL_ENV', venv),\n        ('PATH', (bin_dir(venv), os.pathsep, Var('PATH'))),\n    )\n\n\ndef _find_by_py_launcher(\n        version: str,\n) -> Optional[str]:  # pragma: no cover (windows only)\n    if version.startswith('python'):\n        num = version[len('python'):]\n        try:\n            cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')\n            return cmd_output(*cmd)[1].strip()\n        except CalledProcessError:\n            pass\n    return None\n\n\ndef _find_by_sys_executable() -> Optional[str]:\n    def _norm(path: str) -> Optional[str]:\n        _, exe = os.path.split(path.lower())\n        exe, _, _ = exe.partition('.exe')\n        if exe not in {'python', 'pythonw'} and find_executable(exe):\n            return exe\n        return None\n\n    # On linux, I see these common sys.executables:\n    #\n    # system `python`: /usr/bin/python -> python2.7\n    # system `python2`: /usr/bin/python2 -> python2.7\n    # virtualenv v: v/bin/python (will not return from this loop)\n    # virtualenv v -ppython2: v/bin/python -> python2\n    # virtualenv v -ppython2.7: v/bin/python -> python2.7\n    # virtualenv v -ppypy: v/bin/python -> v/bin/pypy\n    for path in (sys.executable, os.path.realpath(sys.executable)):\n        exe = _norm(path)\n        if exe:\n            return exe\n    return None\n\n\n@functools.lru_cache(maxsize=1)\ndef get_default_version() -> str:  # pragma: no cover (platform dependent)\n    # First attempt from `sys.executable` (or the realpath)\n    exe = _find_by_sys_executable()\n    if exe:\n        return exe\n\n    # Next try the `pythonX.X` executable\n    exe = f'python{sys.version_info[0]}.{sys.version_info[1]}'\n    if find_executable(exe):\n        return exe\n\n    if _find_by_py_launcher(exe):\n        return exe\n\n    # Give a best-effort try for windows\n    default_folder_name = exe.replace('.', '')\n    if os.path.exists(fr'C:\\{default_folder_name}\\python.exe'):\n        return exe\n\n    # We tried!\n    return C.DEFAULT\n\n\ndef _sys_executable_matches(version: str) -> bool:\n    if version == 'python':\n        return True\n    elif not version.startswith('python'):\n        return False\n\n    try:\n        info = tuple(int(p) for p in version[len('python'):].split('.'))\n    except ValueError:\n        return False\n\n    return sys.version_info[:len(info)] == info\n\n\ndef norm_version(version: str) -> str:\n    if version == C.DEFAULT:\n        return os.path.realpath(sys.executable)\n\n    # first see if our current executable is appropriate\n    if _sys_executable_matches(version):\n        return sys.executable\n\n    if os.name == 'nt':  # pragma: no cover (windows)\n        version_exec = _find_by_py_launcher(version)\n        if version_exec:\n            return version_exec\n\n        # Try looking up by name\n        version_exec = find_executable(version)\n        if version_exec and version_exec != version:\n            return version_exec\n\n        # If it is in the form pythonx.x search in the default\n        # place on windows\n        if version.startswith('python'):\n            default_folder_name = version.replace('.', '')\n            return fr'C:\\{default_folder_name}\\python.exe'\n\n    # Otherwise assume it is a path\n    return os.path.expanduser(version)\n\n\n@contextlib.contextmanager\ndef in_env(\n        prefix: Prefix,\n        language_version: str,\n) -> Generator[None, None, None]:\n    directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)\n    envdir = prefix.path(directory)\n    with envcontext(get_env_patch(envdir)):\n        yield\n\n\ndef healthy(prefix: Prefix, language_version: str) -> bool:\n    directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)\n    envdir = prefix.path(directory)\n    pyvenv_cfg = os.path.join(envdir, 'pyvenv.cfg')\n\n    # created with \"old\" virtualenv\n    if not os.path.exists(pyvenv_cfg):\n        return False\n\n    exe_name = 'python.exe' if sys.platform == 'win32' else 'python'\n    py_exe = prefix.path(bin_dir(envdir), exe_name)\n    cfg = _read_pyvenv_cfg(pyvenv_cfg)\n\n    return (\n        'version_info' in cfg and\n        _version_info(py_exe) == cfg['version_info'] and (\n            'base-executable' not in cfg or\n            _version_info(cfg['base-executable']) == cfg['version_info']\n        )\n    )\n\n\ndef install_environment(\n        prefix: Prefix,\n        version: str,\n        additional_dependencies: Sequence[str],\n) -> None:\n    envdir = prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))\n    python = norm_version(version)\n    venv_cmd = (sys.executable, '-mvirtualenv', envdir, '-p', python)\n    install_cmd = ('python', '-mpip', 'install', '.', *additional_dependencies)\n\n    with clean_path_on_failure(envdir):\n        cmd_output_b(*venv_cmd, cwd='/')\n        with in_env(prefix, version):\n            helpers.run_setup_cmd(prefix, install_cmd)\n\n\ndef run_hook(\n        hook: Hook,\n        file_args: Sequence[str],\n        color: bool,\n) -> Tuple[int, bytes]:\n    with in_env(hook.prefix, hook.language_version):\n        return helpers.run_xargs(hook, hook.cmd, file_args, color=color)\n", "path": "pre_commit/languages/python.py"}], "after_files": [{"content": "import contextlib\nimport functools\nimport os\nimport sys\nfrom typing import Dict\nfrom typing import Generator\nfrom typing import Optional\nfrom typing import Sequence\nfrom typing import Tuple\n\nimport pre_commit.constants as C\nfrom pre_commit.envcontext import envcontext\nfrom pre_commit.envcontext import PatchesT\nfrom pre_commit.envcontext import UNSET\nfrom pre_commit.envcontext import Var\nfrom pre_commit.hook import Hook\nfrom pre_commit.languages import helpers\nfrom pre_commit.parse_shebang import find_executable\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\nfrom pre_commit.util import cmd_output_b\n\nENVIRONMENT_DIR = 'py_env'\n\n\n@functools.lru_cache(maxsize=None)\ndef _version_info(exe: str) -> str:\n    prog = 'import sys;print(\".\".join(str(p) for p in sys.version_info))'\n    try:\n        return cmd_output(exe, '-S', '-c', prog)[1].strip()\n    except CalledProcessError:\n        return f'<<error retrieving version from {exe}>>'\n\n\ndef _read_pyvenv_cfg(filename: str) -> Dict[str, str]:\n    ret = {}\n    with open(filename) as f:\n        for line in f:\n            try:\n                k, v = line.split('=')\n            except ValueError:  # blank line / comment / etc.\n                continue\n            else:\n                ret[k.strip()] = v.strip()\n    return ret\n\n\ndef bin_dir(venv: str) -> str:\n    \"\"\"On windows there's a different directory for the virtualenv\"\"\"\n    bin_part = 'Scripts' if os.name == 'nt' else 'bin'\n    return os.path.join(venv, bin_part)\n\n\ndef get_env_patch(venv: str) -> PatchesT:\n    return (\n        ('PIP_DISABLE_PIP_VERSION_CHECK', '1'),\n        ('PYTHONHOME', UNSET),\n        ('VIRTUAL_ENV', venv),\n        ('PATH', (bin_dir(venv), os.pathsep, Var('PATH'))),\n    )\n\n\ndef _find_by_py_launcher(\n        version: str,\n) -> Optional[str]:  # pragma: no cover (windows only)\n    if version.startswith('python'):\n        num = version[len('python'):]\n        cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')\n        env = dict(os.environ, PYTHONIOENCODING='UTF-8')\n        try:\n            return cmd_output(*cmd, env=env)[1].strip()\n        except CalledProcessError:\n            pass\n    return None\n\n\ndef _find_by_sys_executable() -> Optional[str]:\n    def _norm(path: str) -> Optional[str]:\n        _, exe = os.path.split(path.lower())\n        exe, _, _ = exe.partition('.exe')\n        if exe not in {'python', 'pythonw'} and find_executable(exe):\n            return exe\n        return None\n\n    # On linux, I see these common sys.executables:\n    #\n    # system `python`: /usr/bin/python -> python2.7\n    # system `python2`: /usr/bin/python2 -> python2.7\n    # virtualenv v: v/bin/python (will not return from this loop)\n    # virtualenv v -ppython2: v/bin/python -> python2\n    # virtualenv v -ppython2.7: v/bin/python -> python2.7\n    # virtualenv v -ppypy: v/bin/python -> v/bin/pypy\n    for path in (sys.executable, os.path.realpath(sys.executable)):\n        exe = _norm(path)\n        if exe:\n            return exe\n    return None\n\n\n@functools.lru_cache(maxsize=1)\ndef get_default_version() -> str:  # pragma: no cover (platform dependent)\n    # First attempt from `sys.executable` (or the realpath)\n    exe = _find_by_sys_executable()\n    if exe:\n        return exe\n\n    # Next try the `pythonX.X` executable\n    exe = f'python{sys.version_info[0]}.{sys.version_info[1]}'\n    if find_executable(exe):\n        return exe\n\n    if _find_by_py_launcher(exe):\n        return exe\n\n    # Give a best-effort try for windows\n    default_folder_name = exe.replace('.', '')\n    if os.path.exists(fr'C:\\{default_folder_name}\\python.exe'):\n        return exe\n\n    # We tried!\n    return C.DEFAULT\n\n\ndef _sys_executable_matches(version: str) -> bool:\n    if version == 'python':\n        return True\n    elif not version.startswith('python'):\n        return False\n\n    try:\n        info = tuple(int(p) for p in version[len('python'):].split('.'))\n    except ValueError:\n        return False\n\n    return sys.version_info[:len(info)] == info\n\n\ndef norm_version(version: str) -> str:\n    if version == C.DEFAULT:\n        return os.path.realpath(sys.executable)\n\n    # first see if our current executable is appropriate\n    if _sys_executable_matches(version):\n        return sys.executable\n\n    if os.name == 'nt':  # pragma: no cover (windows)\n        version_exec = _find_by_py_launcher(version)\n        if version_exec:\n            return version_exec\n\n        # Try looking up by name\n        version_exec = find_executable(version)\n        if version_exec and version_exec != version:\n            return version_exec\n\n        # If it is in the form pythonx.x search in the default\n        # place on windows\n        if version.startswith('python'):\n            default_folder_name = version.replace('.', '')\n            return fr'C:\\{default_folder_name}\\python.exe'\n\n    # Otherwise assume it is a path\n    return os.path.expanduser(version)\n\n\n@contextlib.contextmanager\ndef in_env(\n        prefix: Prefix,\n        language_version: str,\n) -> Generator[None, None, None]:\n    directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)\n    envdir = prefix.path(directory)\n    with envcontext(get_env_patch(envdir)):\n        yield\n\n\ndef healthy(prefix: Prefix, language_version: str) -> bool:\n    directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)\n    envdir = prefix.path(directory)\n    pyvenv_cfg = os.path.join(envdir, 'pyvenv.cfg')\n\n    # created with \"old\" virtualenv\n    if not os.path.exists(pyvenv_cfg):\n        return False\n\n    exe_name = 'python.exe' if sys.platform == 'win32' else 'python'\n    py_exe = prefix.path(bin_dir(envdir), exe_name)\n    cfg = _read_pyvenv_cfg(pyvenv_cfg)\n\n    return (\n        'version_info' in cfg and\n        _version_info(py_exe) == cfg['version_info'] and (\n            'base-executable' not in cfg or\n            _version_info(cfg['base-executable']) == cfg['version_info']\n        )\n    )\n\n\ndef install_environment(\n        prefix: Prefix,\n        version: str,\n        additional_dependencies: Sequence[str],\n) -> None:\n    envdir = prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))\n    python = norm_version(version)\n    venv_cmd = (sys.executable, '-mvirtualenv', envdir, '-p', python)\n    install_cmd = ('python', '-mpip', 'install', '.', *additional_dependencies)\n\n    with clean_path_on_failure(envdir):\n        cmd_output_b(*venv_cmd, cwd='/')\n        with in_env(prefix, version):\n            helpers.run_setup_cmd(prefix, install_cmd)\n\n\ndef run_hook(\n        hook: Hook,\n        file_args: Sequence[str],\n        color: bool,\n) -> Tuple[int, bytes]:\n    with in_env(hook.prefix, hook.language_version):\n        return helpers.run_xargs(hook, hook.cmd, file_args, color=color)\n", "path": "pre_commit/languages/python.py"}]}