Skip to content

Commit 4ff23b4

Browse files
xhochyasottile
authored andcommitted
Support for conda as a language
1 parent 6850c27 commit 4ff23b4

File tree

8 files changed

+137
-1
lines changed

8 files changed

+137
-1
lines changed

azure-pipelines.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ jobs:
2222
COVERAGE_IGNORE_WINDOWS: '# pragma: windows no cover'
2323
TOX_TESTENV_PASSENV: COVERAGE_IGNORE_WINDOWS
2424
TEMP: C:\Temp # remove when dropping python2
25+
pre_test:
26+
- powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts"
27+
displayName: Add conda to PATH
2528
- template: job--python-tox.yml@asottile
2629
parameters:
2730
toxenvs: [py37]

pre_commit/languages/all.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import unicode_literals
22

3+
from pre_commit.languages import conda
34
from pre_commit.languages import docker
45
from pre_commit.languages import docker_image
56
from pre_commit.languages import fail
@@ -52,6 +53,7 @@
5253
# """
5354

5455
languages = {
56+
'conda': conda,
5557
'docker': docker,
5658
'docker_image': docker_image,
5759
'fail': fail,

pre_commit/languages/conda.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import contextlib
2+
import os
3+
4+
from pre_commit.envcontext import envcontext
5+
from pre_commit.envcontext import UNSET
6+
from pre_commit.envcontext import Var
7+
from pre_commit.languages import helpers
8+
from pre_commit.util import clean_path_on_failure
9+
from pre_commit.util import cmd_output_b
10+
11+
ENVIRONMENT_DIR = 'conda'
12+
get_default_version = helpers.basic_get_default_version
13+
healthy = helpers.basic_healthy
14+
15+
16+
def get_env_patch(env):
17+
# On non-windows systems executable live in $CONDA_PREFIX/bin, on Windows
18+
# they can be in $CONDA_PREFIX/bin, $CONDA_PREFIX/Library/bin,
19+
# $CONDA_PREFIX/Scripts and $CONDA_PREFIX. Whereas the latter only
20+
# seems to be used for python.exe.
21+
path = (os.path.join(env, 'bin'), os.pathsep, Var('PATH'))
22+
if os.name == 'nt': # pragma: no cover (platform specific)
23+
path = (env, os.pathsep) + path
24+
path = (os.path.join(env, 'Scripts'), os.pathsep) + path
25+
path = (os.path.join(env, 'Library', 'bin'), os.pathsep) + path
26+
27+
return (
28+
('PYTHONHOME', UNSET),
29+
('VIRTUAL_ENV', UNSET),
30+
('CONDA_PREFIX', env),
31+
('PATH', path),
32+
)
33+
34+
35+
@contextlib.contextmanager
36+
def in_env(prefix, language_version):
37+
directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)
38+
envdir = prefix.path(directory)
39+
with envcontext(get_env_patch(envdir)):
40+
yield
41+
42+
43+
def install_environment(prefix, version, additional_dependencies):
44+
helpers.assert_version_default('conda', version)
45+
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
46+
47+
env_dir = prefix.path(directory)
48+
with clean_path_on_failure(env_dir):
49+
cmd_output_b(
50+
'conda', 'env', 'create', '-p', env_dir, '--file',
51+
'environment.yml', cwd=prefix.prefix_dir,
52+
)
53+
if additional_dependencies:
54+
cmd_output_b(
55+
'conda', 'install', '-p', env_dir, *additional_dependencies,
56+
cwd=prefix.prefix_dir
57+
)
58+
59+
60+
def run_hook(hook, file_args, color):
61+
# TODO: Some rare commands need to be run using `conda run` but mostly we
62+
# can run them withot which is much quicker and produces a better
63+
# output.
64+
# cmd = ('conda', 'run', '-p', env_dir) + hook.cmd
65+
with in_env(hook.prefix, hook.language_version):
66+
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
channels:
2+
- conda-forge
3+
- defaults
4+
dependencies:
5+
# This cannot be empty as otherwise no environment will be created.
6+
# We're using openssl here as it is available on all system and will
7+
# most likely be always installed anyways.
8+
# See https://github.com/conda/conda/issues/9487
9+
- openssl

pre_commit/store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def _git_cmd(*args):
173173

174174
LOCAL_RESOURCES = (
175175
'Cargo.toml', 'main.go', 'main.rs', '.npmignore', 'package.json',
176-
'pre_commit_dummy_package.gemspec', 'setup.py',
176+
'pre_commit_dummy_package.gemspec', 'setup.py', 'environment.yml',
177177
)
178178

179179
def make_local(self, deps):
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
- id: sys-exec
2+
name: sys-exec
3+
entry: python -c 'import os; import sys; print(sys.executable.split(os.path.sep)[-2]) if os.name == "nt" else print(sys.executable.split(os.path.sep)[-3])'
4+
language: conda
5+
files: \.py$
6+
- id: additional-deps
7+
name: additional-deps
8+
entry: python
9+
language: conda
10+
files: \.py$
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
channels:
2+
- conda-forge
3+
- defaults
4+
dependencies:
5+
- python
6+
- pip

tests/repository_test.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,46 @@ def _test_hook_repo(
7979
assert _norm_out(out) == expected
8080

8181

82+
def test_conda_hook(tempdir_factory, store):
83+
_test_hook_repo(
84+
tempdir_factory, store, 'conda_hooks_repo',
85+
'sys-exec', [os.devnull],
86+
b'conda-default\n',
87+
)
88+
89+
90+
def test_conda_with_additional_dependencies_hook(tempdir_factory, store):
91+
_test_hook_repo(
92+
tempdir_factory, store, 'conda_hooks_repo',
93+
'additional-deps', [os.devnull],
94+
b'OK\n',
95+
config_kwargs={
96+
'hooks': [{
97+
'id': 'additional-deps',
98+
'args': ['-c', 'import mccabe; print("OK")'],
99+
'additional_dependencies': ['mccabe'],
100+
}],
101+
},
102+
)
103+
104+
105+
def test_local_conda_additional_dependencies(store):
106+
config = {
107+
'repo': 'local',
108+
'hooks': [{
109+
'id': 'local-conda',
110+
'name': 'local-conda',
111+
'entry': 'python',
112+
'language': 'conda',
113+
'args': ['-c', 'import mccabe; print("OK")'],
114+
'additional_dependencies': ['mccabe'],
115+
}],
116+
}
117+
ret, out = _get_hook(config, store, 'local-conda').run((), color=False)
118+
assert ret == 0
119+
assert _norm_out(out) == b'OK\n'
120+
121+
82122
def test_python_hook(tempdir_factory, store):
83123
_test_hook_repo(
84124
tempdir_factory, store, 'python_hooks_repo',

0 commit comments

Comments
 (0)