pre-commit-ex 4.5.1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. pre_commit/__init__.py +0 -0
  2. pre_commit/__main__.py +7 -0
  3. pre_commit/all_languages.py +50 -0
  4. pre_commit/clientlib.py +551 -0
  5. pre_commit/color.py +109 -0
  6. pre_commit/commands/__init__.py +0 -0
  7. pre_commit/commands/autoupdate.py +215 -0
  8. pre_commit/commands/clean.py +16 -0
  9. pre_commit/commands/gc.py +98 -0
  10. pre_commit/commands/hazmat.py +95 -0
  11. pre_commit/commands/hook_impl.py +272 -0
  12. pre_commit/commands/init_templatedir.py +39 -0
  13. pre_commit/commands/install_uninstall.py +167 -0
  14. pre_commit/commands/migrate_config.py +135 -0
  15. pre_commit/commands/run.py +474 -0
  16. pre_commit/commands/sample_config.py +18 -0
  17. pre_commit/commands/try_repo.py +77 -0
  18. pre_commit/commands/validate_config.py +18 -0
  19. pre_commit/commands/validate_manifest.py +18 -0
  20. pre_commit/constants.py +13 -0
  21. pre_commit/envcontext.py +62 -0
  22. pre_commit/error_handler.py +81 -0
  23. pre_commit/errors.py +5 -0
  24. pre_commit/file_lock.py +75 -0
  25. pre_commit/git.py +245 -0
  26. pre_commit/hook.py +60 -0
  27. pre_commit/lang_base.py +196 -0
  28. pre_commit/languages/__init__.py +0 -0
  29. pre_commit/languages/conda.py +77 -0
  30. pre_commit/languages/coursier.py +76 -0
  31. pre_commit/languages/dart.py +97 -0
  32. pre_commit/languages/docker.py +181 -0
  33. pre_commit/languages/docker_image.py +32 -0
  34. pre_commit/languages/dotnet.py +111 -0
  35. pre_commit/languages/fail.py +27 -0
  36. pre_commit/languages/golang.py +161 -0
  37. pre_commit/languages/haskell.py +56 -0
  38. pre_commit/languages/julia.py +133 -0
  39. pre_commit/languages/lua.py +75 -0
  40. pre_commit/languages/node.py +110 -0
  41. pre_commit/languages/perl.py +50 -0
  42. pre_commit/languages/pygrep.py +133 -0
  43. pre_commit/languages/python.py +228 -0
  44. pre_commit/languages/r.py +278 -0
  45. pre_commit/languages/ruby.py +145 -0
  46. pre_commit/languages/rust.py +160 -0
  47. pre_commit/languages/swift.py +50 -0
  48. pre_commit/languages/unsupported.py +10 -0
  49. pre_commit/languages/unsupported_script.py +32 -0
  50. pre_commit/logging_handler.py +42 -0
  51. pre_commit/main.py +472 -0
  52. pre_commit/meta_hooks/__init__.py +0 -0
  53. pre_commit/meta_hooks/check_hooks_apply.py +43 -0
  54. pre_commit/meta_hooks/check_useless_excludes.py +83 -0
  55. pre_commit/meta_hooks/identity.py +17 -0
  56. pre_commit/output.py +33 -0
  57. pre_commit/parse_shebang.py +85 -0
  58. pre_commit/prefix.py +18 -0
  59. pre_commit/repository.py +237 -0
  60. pre_commit/resources/__init__.py +0 -0
  61. pre_commit/resources/empty_template_.npmignore +1 -0
  62. pre_commit/resources/empty_template_Cargo.toml +7 -0
  63. pre_commit/resources/empty_template_LICENSE.renv +7 -0
  64. pre_commit/resources/empty_template_Makefile.PL +6 -0
  65. pre_commit/resources/empty_template_activate.R +440 -0
  66. pre_commit/resources/empty_template_environment.yml +9 -0
  67. pre_commit/resources/empty_template_go.mod +1 -0
  68. pre_commit/resources/empty_template_main.go +3 -0
  69. pre_commit/resources/empty_template_main.rs +1 -0
  70. pre_commit/resources/empty_template_package.json +4 -0
  71. pre_commit/resources/empty_template_pre-commit-package-dev-1.rockspec +12 -0
  72. pre_commit/resources/empty_template_pre_commit_placeholder_package.gemspec +6 -0
  73. pre_commit/resources/empty_template_pubspec.yaml +4 -0
  74. pre_commit/resources/empty_template_renv.lock +20 -0
  75. pre_commit/resources/empty_template_setup.py +4 -0
  76. pre_commit/resources/hook-tmpl +20 -0
  77. pre_commit/resources/rbenv.tar.gz +0 -0
  78. pre_commit/resources/ruby-build.tar.gz +0 -0
  79. pre_commit/resources/ruby-download.tar.gz +0 -0
  80. pre_commit/staged_files_only.py +113 -0
  81. pre_commit/store.py +235 -0
  82. pre_commit/util.py +239 -0
  83. pre_commit/xargs.py +184 -0
  84. pre_commit/yaml.py +19 -0
  85. pre_commit/yaml_rewrite.py +52 -0
  86. pre_commit_ex-4.5.1.dist-info/METADATA +63 -0
  87. pre_commit_ex-4.5.1.dist-info/RECORD +91 -0
  88. pre_commit_ex-4.5.1.dist-info/WHEEL +6 -0
  89. pre_commit_ex-4.5.1.dist-info/entry_points.txt +2 -0
  90. pre_commit_ex-4.5.1.dist-info/licenses/LICENSE +19 -0
  91. pre_commit_ex-4.5.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,161 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import functools
5
+ import json
6
+ import os.path
7
+ import platform
8
+ import shutil
9
+ import sys
10
+ import tarfile
11
+ import tempfile
12
+ import urllib.error
13
+ import urllib.request
14
+ import zipfile
15
+ from collections.abc import Generator
16
+ from collections.abc import Sequence
17
+ from typing import ContextManager
18
+ from typing import IO
19
+ from typing import Protocol
20
+
21
+ import pre_commit.constants as C
22
+ from pre_commit import lang_base
23
+ from pre_commit.envcontext import envcontext
24
+ from pre_commit.envcontext import PatchesT
25
+ from pre_commit.envcontext import Var
26
+ from pre_commit.git import no_git_env
27
+ from pre_commit.prefix import Prefix
28
+ from pre_commit.util import cmd_output
29
+ from pre_commit.util import rmtree
30
+
31
+ ENVIRONMENT_DIR = 'golangenv'
32
+ health_check = lang_base.basic_health_check
33
+ run_hook = lang_base.basic_run_hook
34
+
35
+ _ARCH_ALIASES = {
36
+ 'x86_64': 'amd64',
37
+ 'i386': '386',
38
+ 'aarch64': 'arm64',
39
+ 'armv8': 'arm64',
40
+ 'armv7l': 'armv6l',
41
+ }
42
+ _ARCH = platform.machine().lower()
43
+ _ARCH = _ARCH_ALIASES.get(_ARCH, _ARCH)
44
+
45
+
46
+ class ExtractAll(Protocol):
47
+ def extractall(self, path: str) -> None: ...
48
+
49
+
50
+ if sys.platform == 'win32': # pragma: win32 cover
51
+ _EXT = 'zip'
52
+
53
+ def _open_archive(bio: IO[bytes]) -> ContextManager[ExtractAll]:
54
+ return zipfile.ZipFile(bio)
55
+ else: # pragma: win32 no cover
56
+ _EXT = 'tar.gz'
57
+
58
+ def _open_archive(bio: IO[bytes]) -> ContextManager[ExtractAll]:
59
+ return tarfile.open(fileobj=bio)
60
+
61
+
62
+ @functools.lru_cache(maxsize=1)
63
+ def get_default_version() -> str:
64
+ if lang_base.exe_exists('go'):
65
+ return 'system'
66
+ else:
67
+ return C.DEFAULT
68
+
69
+
70
+ def get_env_patch(venv: str, version: str) -> PatchesT:
71
+ if version == 'system':
72
+ return (
73
+ ('PATH', (os.path.join(venv, 'bin'), os.pathsep, Var('PATH'))),
74
+ )
75
+
76
+ return (
77
+ ('GOROOT', os.path.join(venv, '.go')),
78
+ ('GOTOOLCHAIN', 'local'),
79
+ (
80
+ 'PATH', (
81
+ os.path.join(venv, 'bin'), os.pathsep,
82
+ os.path.join(venv, '.go', 'bin'), os.pathsep, Var('PATH'),
83
+ ),
84
+ ),
85
+ )
86
+
87
+
88
+ @functools.lru_cache
89
+ def _infer_go_version(version: str) -> str:
90
+ if version != C.DEFAULT:
91
+ return version
92
+ resp = urllib.request.urlopen('https://go.dev/dl/?mode=json')
93
+ return json.load(resp)[0]['version'].removeprefix('go')
94
+
95
+
96
+ def _get_url(version: str) -> str:
97
+ os_name = platform.system().lower()
98
+ version = _infer_go_version(version)
99
+ return f'https://dl.google.com/go/go{version}.{os_name}-{_ARCH}.{_EXT}'
100
+
101
+
102
+ def _install_go(version: str, dest: str) -> None:
103
+ try:
104
+ resp = urllib.request.urlopen(_get_url(version))
105
+ except urllib.error.HTTPError as e: # pragma: no cover
106
+ if e.code == 404:
107
+ raise ValueError(
108
+ f'Could not find a version matching your system requirements '
109
+ f'(os={platform.system().lower()}; arch={_ARCH})',
110
+ ) from e
111
+ else:
112
+ raise
113
+ else:
114
+ with tempfile.TemporaryFile() as f:
115
+ shutil.copyfileobj(resp, f)
116
+ f.seek(0)
117
+
118
+ with _open_archive(f) as archive:
119
+ archive.extractall(dest)
120
+ shutil.move(os.path.join(dest, 'go'), os.path.join(dest, '.go'))
121
+
122
+
123
+ @contextlib.contextmanager
124
+ def in_env(prefix: Prefix, version: str) -> Generator[None]:
125
+ envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
126
+ with envcontext(get_env_patch(envdir, version)):
127
+ yield
128
+
129
+
130
+ def install_environment(
131
+ prefix: Prefix,
132
+ version: str,
133
+ additional_dependencies: Sequence[str],
134
+ ) -> None:
135
+ env_dir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
136
+
137
+ if version != 'system':
138
+ _install_go(version, env_dir)
139
+
140
+ if sys.platform == 'cygwin': # pragma: no cover
141
+ gopath = cmd_output('cygpath', '-w', env_dir)[1].strip()
142
+ else:
143
+ gopath = env_dir
144
+
145
+ env = no_git_env(dict(os.environ, GOPATH=gopath))
146
+ env.pop('GOBIN', None)
147
+ if version != 'system':
148
+ env['GOTOOLCHAIN'] = 'local'
149
+ env['GOROOT'] = os.path.join(env_dir, '.go')
150
+ env['PATH'] = os.pathsep.join((
151
+ os.path.join(env_dir, '.go', 'bin'), os.environ['PATH'],
152
+ ))
153
+
154
+ lang_base.setup_cmd(prefix, ('go', 'install', './...'), env=env)
155
+ for dependency in additional_dependencies:
156
+ lang_base.setup_cmd(prefix, ('go', 'install', dependency), env=env)
157
+
158
+ # save some disk space -- we don't need this after installation
159
+ pkgdir = os.path.join(env_dir, 'pkg')
160
+ if os.path.exists(pkgdir): # pragma: no branch (always true on windows?)
161
+ rmtree(pkgdir)
@@ -0,0 +1,56 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import os.path
5
+ from collections.abc import Generator
6
+ from collections.abc import Sequence
7
+
8
+ from pre_commit import lang_base
9
+ from pre_commit.envcontext import envcontext
10
+ from pre_commit.envcontext import PatchesT
11
+ from pre_commit.envcontext import Var
12
+ from pre_commit.errors import FatalError
13
+ from pre_commit.prefix import Prefix
14
+
15
+ ENVIRONMENT_DIR = 'hs_env'
16
+ get_default_version = lang_base.basic_get_default_version
17
+ health_check = lang_base.basic_health_check
18
+ run_hook = lang_base.basic_run_hook
19
+
20
+
21
+ def get_env_patch(target_dir: str) -> PatchesT:
22
+ bin_path = os.path.join(target_dir, 'bin')
23
+ return (('PATH', (bin_path, os.pathsep, Var('PATH'))),)
24
+
25
+
26
+ @contextlib.contextmanager
27
+ def in_env(prefix: Prefix, version: str) -> Generator[None]:
28
+ envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
29
+ with envcontext(get_env_patch(envdir)):
30
+ yield
31
+
32
+
33
+ def install_environment(
34
+ prefix: Prefix,
35
+ version: str,
36
+ additional_dependencies: Sequence[str],
37
+ ) -> None:
38
+ lang_base.assert_version_default('haskell', version)
39
+ envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
40
+
41
+ pkgs = [*prefix.star('.cabal'), *additional_dependencies]
42
+ if not pkgs:
43
+ raise FatalError('Expected .cabal files or additional_dependencies')
44
+
45
+ bindir = os.path.join(envdir, 'bin')
46
+ os.makedirs(bindir, exist_ok=True)
47
+ lang_base.setup_cmd(prefix, ('cabal', 'update'))
48
+ lang_base.setup_cmd(
49
+ prefix,
50
+ (
51
+ 'cabal', 'install',
52
+ '--install-method', 'copy',
53
+ '--installdir', bindir,
54
+ *pkgs,
55
+ ),
56
+ )
@@ -0,0 +1,133 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import os
5
+ import shutil
6
+ from collections.abc import Generator
7
+ from collections.abc import Sequence
8
+
9
+ from pre_commit import lang_base
10
+ from pre_commit.envcontext import envcontext
11
+ from pre_commit.envcontext import PatchesT
12
+ from pre_commit.envcontext import UNSET
13
+ from pre_commit.prefix import Prefix
14
+ from pre_commit.util import cmd_output_b
15
+
16
+ ENVIRONMENT_DIR = 'juliaenv'
17
+ health_check = lang_base.basic_health_check
18
+ get_default_version = lang_base.basic_get_default_version
19
+
20
+
21
+ def run_hook(
22
+ prefix: Prefix,
23
+ entry: str,
24
+ args: Sequence[str],
25
+ file_args: Sequence[str],
26
+ *,
27
+ is_local: bool,
28
+ require_serial: bool,
29
+ color: bool,
30
+ ) -> tuple[int, bytes]:
31
+ # `entry` is a (hook-repo relative) file followed by (optional) args, e.g.
32
+ # `bin/id.jl` or `bin/hook.jl --arg1 --arg2` so we
33
+ # 1) shell parse it and join with args with hook_cmd
34
+ # 2) prepend the hooks prefix path to the first argument (the file), unless
35
+ # it is a local script
36
+ # 3) prepend `julia` as the interpreter
37
+
38
+ cmd = lang_base.hook_cmd(entry, args)
39
+ script = cmd[0] if is_local else prefix.path(cmd[0])
40
+ cmd = ('julia', '--startup-file=no', script, *cmd[1:])
41
+ return lang_base.run_xargs(
42
+ cmd,
43
+ file_args,
44
+ require_serial=require_serial,
45
+ color=color,
46
+ )
47
+
48
+
49
+ def get_env_patch(target_dir: str, version: str) -> PatchesT:
50
+ return (
51
+ ('JULIA_LOAD_PATH', target_dir),
52
+ # May be set, remove it to not interfer with LOAD_PATH
53
+ ('JULIA_PROJECT', UNSET),
54
+ )
55
+
56
+
57
+ @contextlib.contextmanager
58
+ def in_env(prefix: Prefix, version: str) -> Generator[None]:
59
+ envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
60
+ with envcontext(get_env_patch(envdir, version)):
61
+ yield
62
+
63
+
64
+ def install_environment(
65
+ prefix: Prefix,
66
+ version: str,
67
+ additional_dependencies: Sequence[str],
68
+ ) -> None:
69
+ envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
70
+ with in_env(prefix, version):
71
+ # TODO: Support language_version with juliaup similar to rust via
72
+ # rustup
73
+ # if version != 'system':
74
+ # ...
75
+
76
+ # Copy Project.toml to hook env if it exist
77
+ os.makedirs(envdir, exist_ok=True)
78
+ project_names = ('JuliaProject.toml', 'Project.toml')
79
+ project_found = False
80
+ for project_name in project_names:
81
+ project_file = prefix.path(project_name)
82
+ if not os.path.isfile(project_file):
83
+ continue
84
+ shutil.copy(project_file, envdir)
85
+ project_found = True
86
+ break
87
+
88
+ # If no project file was found we create an empty one so that the
89
+ # package manager doesn't error
90
+ if not project_found:
91
+ open(os.path.join(envdir, 'Project.toml'), 'a').close()
92
+
93
+ # Copy Manifest.toml to hook env if it exists
94
+ manifest_names = ('JuliaManifest.toml', 'Manifest.toml')
95
+ for manifest_name in manifest_names:
96
+ manifest_file = prefix.path(manifest_name)
97
+ if not os.path.isfile(manifest_file):
98
+ continue
99
+ shutil.copy(manifest_file, envdir)
100
+ break
101
+
102
+ # Julia code to instantiate the hook environment
103
+ julia_code = """
104
+ @assert length(ARGS) > 0
105
+ hook_env = ARGS[1]
106
+ deps = join(ARGS[2:end], " ")
107
+
108
+ # We prepend @stdlib here so that we can load the package manager even
109
+ # though `get_env_patch` limits `JULIA_LOAD_PATH` to just the hook env.
110
+ pushfirst!(LOAD_PATH, "@stdlib")
111
+ using Pkg
112
+ popfirst!(LOAD_PATH)
113
+
114
+ # Instantiate the environment shipped with the hook repo. If we have
115
+ # additional dependencies we disable precompilation in this step to
116
+ # avoid double work.
117
+ precompile = isempty(deps) ? "1" : "0"
118
+ withenv("JULIA_PKG_PRECOMPILE_AUTO" => precompile) do
119
+ Pkg.instantiate()
120
+ end
121
+
122
+ # Add additional dependencies (with precompilation)
123
+ if !isempty(deps)
124
+ withenv("JULIA_PKG_PRECOMPILE_AUTO" => "1") do
125
+ Pkg.REPLMode.pkgstr("add " * deps)
126
+ end
127
+ end
128
+ """
129
+ cmd_output_b(
130
+ 'julia', '--startup-file=no', '-e', julia_code, '--', envdir,
131
+ *additional_dependencies,
132
+ cwd=prefix.prefix_dir,
133
+ )
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import os
5
+ import sys
6
+ from collections.abc import Generator
7
+ from collections.abc import Sequence
8
+
9
+ from pre_commit import lang_base
10
+ from pre_commit.envcontext import envcontext
11
+ from pre_commit.envcontext import PatchesT
12
+ from pre_commit.envcontext import Var
13
+ from pre_commit.prefix import Prefix
14
+ from pre_commit.util import cmd_output
15
+
16
+ ENVIRONMENT_DIR = 'lua_env'
17
+ get_default_version = lang_base.basic_get_default_version
18
+ health_check = lang_base.basic_health_check
19
+ run_hook = lang_base.basic_run_hook
20
+
21
+
22
+ def _get_lua_version() -> str: # pragma: win32 no cover
23
+ """Get the Lua version used in file paths."""
24
+ _, stdout, _ = cmd_output('luarocks', 'config', '--lua-ver')
25
+ return stdout.strip()
26
+
27
+
28
+ def get_env_patch(d: str) -> PatchesT: # pragma: win32 no cover
29
+ version = _get_lua_version()
30
+ so_ext = 'dll' if sys.platform == 'win32' else 'so'
31
+ return (
32
+ ('PATH', (os.path.join(d, 'bin'), os.pathsep, Var('PATH'))),
33
+ (
34
+ 'LUA_PATH', (
35
+ os.path.join(d, 'share', 'lua', version, '?.lua;'),
36
+ os.path.join(d, 'share', 'lua', version, '?', 'init.lua;;'),
37
+ ),
38
+ ),
39
+ (
40
+ 'LUA_CPATH',
41
+ (os.path.join(d, 'lib', 'lua', version, f'?.{so_ext};;'),),
42
+ ),
43
+ )
44
+
45
+
46
+ @contextlib.contextmanager # pragma: win32 no cover
47
+ def in_env(prefix: Prefix, version: str) -> Generator[None]:
48
+ envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
49
+ with envcontext(get_env_patch(envdir)):
50
+ yield
51
+
52
+
53
+ def install_environment(
54
+ prefix: Prefix,
55
+ version: str,
56
+ additional_dependencies: Sequence[str],
57
+ ) -> None: # pragma: win32 no cover
58
+ lang_base.assert_version_default('lua', version)
59
+
60
+ envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
61
+ with in_env(prefix, version):
62
+ # luarocks doesn't bootstrap a tree prior to installing
63
+ # so ensure the directory exists.
64
+ os.makedirs(envdir, exist_ok=True)
65
+
66
+ # Older luarocks (e.g., 2.4.2) expect the rockspec as an arg
67
+ for rockspec in prefix.star('.rockspec'):
68
+ make_cmd = ('luarocks', '--tree', envdir, 'make', rockspec)
69
+ lang_base.setup_cmd(prefix, make_cmd)
70
+
71
+ # luarocks can't install multiple packages at once
72
+ # so install them individually.
73
+ for dependency in additional_dependencies:
74
+ cmd = ('luarocks', '--tree', envdir, 'install', dependency)
75
+ lang_base.setup_cmd(prefix, cmd)
@@ -0,0 +1,110 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import functools
5
+ import os
6
+ import sys
7
+ from collections.abc import Generator
8
+ from collections.abc import Sequence
9
+
10
+ import pre_commit.constants as C
11
+ from pre_commit import lang_base
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.languages.python import bin_dir
17
+ from pre_commit.prefix import Prefix
18
+ from pre_commit.util import cmd_output
19
+ from pre_commit.util import cmd_output_b
20
+ from pre_commit.util import rmtree
21
+
22
+ ENVIRONMENT_DIR = 'node_env'
23
+ run_hook = lang_base.basic_run_hook
24
+
25
+
26
+ @functools.lru_cache(maxsize=1)
27
+ def get_default_version() -> str:
28
+ # nodeenv does not yet support `-n system` on windows
29
+ if sys.platform == 'win32':
30
+ return C.DEFAULT
31
+ # if node is already installed, we can save a bunch of setup time by
32
+ # using the installed version
33
+ elif all(lang_base.exe_exists(exe) for exe in ('node', 'npm')):
34
+ return 'system'
35
+ else:
36
+ return C.DEFAULT
37
+
38
+
39
+ def get_env_patch(venv: str) -> PatchesT:
40
+ if sys.platform == 'cygwin': # pragma: no cover
41
+ _, win_venv, _ = cmd_output('cygpath', '-w', venv)
42
+ install_prefix = fr'{win_venv.strip()}\bin'
43
+ lib_dir = 'lib'
44
+ elif sys.platform == 'win32': # pragma: no cover
45
+ install_prefix = bin_dir(venv)
46
+ lib_dir = 'Scripts'
47
+ else: # pragma: win32 no cover
48
+ install_prefix = venv
49
+ lib_dir = 'lib'
50
+ return (
51
+ ('NODE_VIRTUAL_ENV', venv),
52
+ ('NPM_CONFIG_PREFIX', install_prefix),
53
+ ('npm_config_prefix', install_prefix),
54
+ ('NPM_CONFIG_USERCONFIG', UNSET),
55
+ ('npm_config_userconfig', UNSET),
56
+ ('NODE_PATH', os.path.join(venv, lib_dir, 'node_modules')),
57
+ ('PATH', (bin_dir(venv), os.pathsep, Var('PATH'))),
58
+ )
59
+
60
+
61
+ @contextlib.contextmanager
62
+ def in_env(prefix: Prefix, version: str) -> Generator[None]:
63
+ envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
64
+ with envcontext(get_env_patch(envdir)):
65
+ yield
66
+
67
+
68
+ def health_check(prefix: Prefix, version: str) -> str | None:
69
+ with in_env(prefix, version):
70
+ retcode, _, _ = cmd_output_b('node', '--version', check=False)
71
+ if retcode != 0: # pragma: win32 no cover
72
+ return f'`node --version` returned {retcode}'
73
+ else:
74
+ return None
75
+
76
+
77
+ def install_environment(
78
+ prefix: Prefix, version: str, additional_dependencies: Sequence[str],
79
+ ) -> None:
80
+ assert prefix.exists('package.json')
81
+ envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
82
+
83
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx?f=255&MSPPError=-2147217396#maxpath
84
+ if sys.platform == 'win32': # pragma: no cover
85
+ envdir = fr'\\?\{os.path.normpath(envdir)}'
86
+ cmd = [sys.executable, '-mnodeenv', '--prebuilt', '--clean-src', envdir]
87
+ if version != C.DEFAULT:
88
+ cmd.extend(['-n', version])
89
+ cmd_output_b(*cmd)
90
+
91
+ with in_env(prefix, version):
92
+ # https://npm.community/t/npm-install-g-git-vs-git-clone-cd-npm-install-g/5449
93
+ # install as if we installed from git
94
+
95
+ local_install_cmd = (
96
+ 'npm', 'install', '--include=dev', '--include=prod',
97
+ '--ignore-prepublish', '--no-progress', '--no-save',
98
+ )
99
+ lang_base.setup_cmd(prefix, local_install_cmd)
100
+
101
+ _, pkg, _ = cmd_output('npm', 'pack', cwd=prefix.prefix_dir)
102
+ pkg = prefix.path(pkg.strip())
103
+
104
+ install = ('npm', 'install', '-g', pkg, *additional_dependencies)
105
+ lang_base.setup_cmd(prefix, install)
106
+
107
+ # clean these up after installation
108
+ if prefix.exists('node_modules'): # pragma: win32 no cover
109
+ rmtree(prefix.path('node_modules'))
110
+ os.remove(pkg)
@@ -0,0 +1,50 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import os
5
+ import shlex
6
+ from collections.abc import Generator
7
+ from collections.abc import Sequence
8
+
9
+ from pre_commit import lang_base
10
+ from pre_commit.envcontext import envcontext
11
+ from pre_commit.envcontext import PatchesT
12
+ from pre_commit.envcontext import Var
13
+ from pre_commit.prefix import Prefix
14
+
15
+ ENVIRONMENT_DIR = 'perl_env'
16
+ get_default_version = lang_base.basic_get_default_version
17
+ health_check = lang_base.basic_health_check
18
+ run_hook = lang_base.basic_run_hook
19
+
20
+
21
+ def get_env_patch(venv: str) -> PatchesT:
22
+ return (
23
+ ('PATH', (os.path.join(venv, 'bin'), os.pathsep, Var('PATH'))),
24
+ ('PERL5LIB', os.path.join(venv, 'lib', 'perl5')),
25
+ ('PERL_MB_OPT', f'--install_base {shlex.quote(venv)}'),
26
+ (
27
+ 'PERL_MM_OPT', (
28
+ f'INSTALL_BASE={shlex.quote(venv)} '
29
+ f'INSTALLSITEMAN1DIR=none INSTALLSITEMAN3DIR=none'
30
+ ),
31
+ ),
32
+ )
33
+
34
+
35
+ @contextlib.contextmanager
36
+ def in_env(prefix: Prefix, version: str) -> Generator[None]:
37
+ envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
38
+ with envcontext(get_env_patch(envdir)):
39
+ yield
40
+
41
+
42
+ def install_environment(
43
+ prefix: Prefix, version: str, additional_dependencies: Sequence[str],
44
+ ) -> None:
45
+ lang_base.assert_version_default('perl', version)
46
+
47
+ with in_env(prefix, version):
48
+ lang_base.setup_cmd(
49
+ prefix, ('cpan', '-T', '.', *additional_dependencies),
50
+ )