Skip to content

Commit 77a5491

Browse files
committed
adds --conda-activate option when installing kernel
1 parent 9049a84 commit 77a5491

File tree

3 files changed

+93
-5
lines changed

3 files changed

+93
-5
lines changed

bash_kernel/assets/bashrc-conda

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Different platforms have different names for the systemwide bashrc
2+
if [[ -f /etc/bashrc ]]; then
3+
source /etc/bashrc
4+
fi
5+
if [[ -f /etc/bash.bashrc ]]; then
6+
source /etc/bash.bashrc
7+
fi
8+
if [[ -f ~/.bashrc ]]; then
9+
source ~/.bashrc
10+
fi
11+
12+
# Reset PS1 so pexpect can find it
13+
PS1="$"
14+
15+
# Unset PROMPT_COMMAND, so that it can't change PS1 to something unexpected.
16+
unset PROMPT_COMMAND
17+
18+
19+
eval "$(conda shell.bash hook)"
20+
21+
conda activate {CONDA_DEFAULT_ENV}

bash_kernel/install.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,28 @@
1313
"env":{"PS1": "$"}
1414
}
1515

16-
def install_my_kernel_spec(user=True, prefix=None):
16+
def install_my_kernel_spec(user=True, prefix=None, conda_activate=False):
17+
if conda_activate:
18+
CONDA_DEFAULT_ENV = os.environ.get("CONDA_DEFAULT_ENV")
19+
20+
if not CONDA_DEFAULT_ENV:
21+
raise RuntimeError(
22+
"This script must be run from a conda environment "
23+
"when using the --conda-activate flag"
24+
)
25+
26+
settings = {"conda_default_env": CONDA_DEFAULT_ENV}
27+
else:
28+
settings = {"conda_default_env": None}
29+
1730
with TemporaryDirectory() as td:
1831
os.chmod(td, 0o755) # Starts off as 700, not user readable
1932
with open(os.path.join(td, 'kernel.json'), 'w') as f:
2033
json.dump(kernel_json, f, sort_keys=True)
34+
35+
with open(os.path.join(td, 'settings.json'), 'w') as f:
36+
json.dump(settings, f, sort_keys=True)
37+
2138
# TODO: Copy resources once they're specified
2239

2340
print('Installing IPython kernel spec')
@@ -51,6 +68,12 @@ def main(argv=None):
5168
help='Install KernelSpec in this prefix',
5269
default=None
5370
)
71+
parser.add_argument(
72+
'--conda-activate',
73+
help='Initialize the bash kernel with the current conda environment',
74+
action='store_true',
75+
dest='conda_activate'
76+
)
5477

5578
args = parser.parse_args(argv)
5679

@@ -63,7 +86,7 @@ def main(argv=None):
6386
elif args.user or not _is_root():
6487
user = True
6588

66-
install_my_kernel_spec(user=user, prefix=prefix)
89+
install_my_kernel_spec(user=user, prefix=prefix, conda_activate=args.conda_activate)
6790

6891
if __name__ == '__main__':
6992
main()

bash_kernel/kernel.py

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1+
import json
2+
import tempfile
13
from ipykernel.kernelbase import Kernel
4+
from jupyter_client.kernelspec import find_kernel_specs
25
from pexpect import replwrap, EOF
36
import pexpect
7+
from pathlib import Path
48

59
from subprocess import check_output
610
import os.path
711
import uuid
812
import random
913
import string
1014

15+
try:
16+
import bash_kernel
17+
# installation reads __version__. since the package isn't ready yet
18+
# this will fail, so we add the except clause
19+
except ModuleNotFoundError:
20+
bash_kernel = None
21+
22+
1123
import re
1224
import signal
1325

@@ -101,9 +113,42 @@ def __init__(self, **kwargs):
101113
rand = ''.join(random.choices(string.ascii_uppercase, k=12))
102114
self.unique_prompt = "PROMPT_" + rand
103115
Kernel.__init__(self, **kwargs)
104-
self._start_bash()
116+
117+
# determine which bashrc to use
118+
kernelspecs = find_kernel_specs()
119+
kernelspec_bash = kernelspecs['bash']
120+
121+
if not kernelspec_bash:
122+
raise RuntimeError("Can't find bash kernelspec")
123+
124+
settings = json.loads(Path(kernelspec_bash, 'settings.json').read_text())
125+
CONDA_DEFAULT_ENV = settings.get("conda_default_env")
126+
127+
if CONDA_DEFAULT_ENV:
128+
print("Generating temporary bashrc...")
129+
template = os.path.join(os.path.dirname(bash_kernel.__file__), 'assets',
130+
'bashrc-conda')
131+
template_content = (Path(template).read_text()
132+
.format(CONDA_DEFAULT_ENV=CONDA_DEFAULT_ENV))
133+
134+
self._bashrc_tmp = tempfile.NamedTemporaryFile(mode='w')
135+
self._bashrc_tmp.write(template_content)
136+
self._bashrc_tmp.flush()
137+
self._path_to_bashrc = self._bashrc_tmp.name
138+
self._start_bash()
139+
else:
140+
bashrc = os.path.join(os.path.dirname(pexpect.__file__), 'bashrc.sh')
141+
self._bashrc_tmp = None
142+
self._path_to_bashrc = bashrc
143+
self._start_bash()
144+
145+
105146
self._known_display_ids = set()
106147

148+
def __del__(self):
149+
if self._bashrc_tmp:
150+
self._bashrc_tmp.close()
151+
107152
def _start_bash(self):
108153
# Signal handlers are inherited by forked processes, and we can't easily
109154
# reset it from the subprocess. Since kernelapp ignores SIGINT except in
@@ -115,8 +160,7 @@ def _start_bash(self):
115160
# bash() function of pexpect/replwrap.py. Look at the
116161
# source code there for comments and context for
117162
# understanding the code here.
118-
bashrc = os.path.join(os.path.dirname(pexpect.__file__), 'bashrc.sh')
119-
child = pexpect.spawn("bash", ['--rcfile', bashrc], echo=False,
163+
child = pexpect.spawn("bash", ['--rcfile', self._path_to_bashrc], echo=False,
120164
encoding='utf-8', codec_errors='replace')
121165
# Following comment stolen from upstream's REPLWrap:
122166
# If the user runs 'env', the value of PS1 will be in the output. To avoid

0 commit comments

Comments
 (0)