-
Notifications
You must be signed in to change notification settings - Fork 29
/
install-cl-jupyter.py
277 lines (216 loc) · 9.81 KB
/
install-cl-jupyter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
#!/usr/bin/env python3
## cl-Jupyter installation script
## Note: since the kernel of Jupyter is written itself in Python,
## it is much simpler to have a Python-based installation script.
import subprocess
import sys
import shutil
import os
import json
class RequirementException(BaseException):
pass
def halt(msg):
print(msg, file=sys.stderr)
print("Abort.", file=sys.stderr)
sys.exit(1)
CL_JUPYTER_HEADER = """
cl-Jupyter -- an enhanced interactive Common Lisp shell
(C) 2014-2015 Frederic Peschanski (cf. LICENSE)
----"""
print(CL_JUPYTER_HEADER)
# check that we run as a script
if __name__ != "__main__":
halt("Error: cl-Jupyter startup must be run as a script")
# check the python version, needs at least 3.2
if sys.version_info.major < 3 \
or sys.version_info.minor < 3:
halt("Error: cl-Jupyter requires Python v3.3 or above")
# check if ipython is available
try:
import IPython
except ImportError:
halt("Error: IPython not available (check your Python Path)")
# check Ipython version
ipython_version_major, ipython_version_minor, ipython_version_patch, ipython_version_tag = IPython.version_info
if ipython_version_major < 3:
halt("Error: IPython v3.x required (found v{}.{})".format(ipython_version_major, ipython_version_minor))
print("... Frontend: using IPython v{}.{}".format(ipython_version_major, ipython_version_minor))
###################################
## (Ad-hoc) command-line parsing ##
###################################
class Config:
def __init__(self):
self.ipython_dir = IPython.paths.get_ipython_dir()
self.ipython_profile_dir = self.ipython_dir + "/profile_cl_jupyter"
self.lisp_implementation = "sbcl" # TODO: ccl support (others ? requires threading)
self.lisp_executable = None # we'll derive executable from implementation later if need be
self.ipython_executable = shutil.which("ipython3")
self.ipython_command = "console"
self.lisp_preload = None
def process_command_line(argv):
config = Config()
import inspect
import os.path
config.cl_jupyter_startup_def_dir = os.path.dirname(os.path.realpath(inspect.getsourcefile(Config)))
#print("cl-Jupyter startup def dir = {}".format(config.cl_jupyter_startup_def_dir))
config.cl_jupyter_startup_run_dir = os.path.realpath(os.getcwd())
#print("cl-Jupyter startup run dir = {}".format(config.cl_jupyter_startup_run_dir))
config.cl_jupyter_startup_script = os.path.realpath(argv[0])
#print("cl-Jupyter startup script = {}".format(config.cl_jupyter_startup_script))
i = 1
if len(argv) > 1 and not (argv[i].startswith('-')): # first argument should be the ipython command
config.ipython_command = argv[i]
i += 1
# print("IPython command = {}".format(config.ipython_command))
# default is "console"
if config.ipython_command not in { "console", "notebook" }:
halt("Error: command '{}' not available\n ==> choose 'console' (default) or 'notebook'".format(config.ipython_command))
profile_dir_set = False
profile_set = False
lisp_set = False
lisp_exec_set = False
ipython_exec_set = False
while i < len(argv):
#print("cmd line option #{}: {}".format(i, argv[i]))
if argv[i].startswith("--profile-dir="):
if profile_dir_set or profile_set:
halt("Error: unexpected '--profile-dir' option, profile already set")
config.ipython_profile_dir = argv[i][14:]
profile_dir_set = True
elif argv[i].startswith("--profile="):
if profile_set or profile_dir_set:
halt("Error: unexpected '--profile' option, profile already set")
config.ipython_profile_dir = config.ipython_dir + "/profile_" + argv[i][10:]
profile_set = True
elif argv[i].startswith("--lisp="):
if lisp_set:
halt("Error: --lisp option set twice")
config.lisp_implementation = argv[i][7:]
lisp_set = True
elif argv[i].startswith("--lisp-exec="):
if lisp_exec_set:
halt("Error: --lisp-exec option set twice")
config.lisp_executable = argv[i][12:]
lisp_exec_set = True
elif argv[i].startswith("--ipython-exec="):
if ipython_exec_set:
halt("Error: --ipython-exec option set twice")
config.ipython_executable = shutil.which(argv[i][15:])
ipython_exec_set = True
elif argv[i].startswith("--lisp-preload="):
config.lisp_preload = argv[i][15:]
else:
halt("Error: unexpected option '{}'".format(argv[i]))
i += 1
#print("IPython profile directory = {}".format(config.ipython_profile_dir))
#print("Lisp implementation = {}".format(config.lisp_implementation))
#print("IPython executable = {}".format(config.ipython_executable))
return config
config = process_command_line(sys.argv)
###################################
## Check Ipython executable ##
###################################
if not config.ipython_executable:
halt("Error: Ipython executable not found")
else:
try:
ipython_version_string = subprocess.check_output([config.ipython_executable, "--version"]).decode()
except FileNotFoundError:
halt("Error: cannot find ipython executable")
except subprocess.CalledProcessError as e:
halt("Error: {}".format(e))
#print("ipython version string = {}".format(ipython_version_string))
import re
# cut off a hyphen and anything following, e.g. "2.4.2-maint" --> "2.4.2"
foo = re.sub ("-.*$", "", ipython_version_string)
ipython_version = tuple([int(d) for d in foo.split(".")])
#print("ipython version = {}".format(ipython_version))
if (ipython_version[0] != ipython_version_major) \
or (ipython_version[1] != ipython_version_minor):
halt("Error: mismatch ipython version ({}.{} vs {}.{})".format(ipython_version[0], ipython_version[1],
ipython_version_major, ipython_version_minor))
###################################
## Check the lisp implementation ##
###################################
if config.lisp_implementation == "sbcl":
if not config.lisp_executable:
config.lisp_executable = 'sbcl'
try:
sbcl_version_string = subprocess.check_output([config.lisp_executable, "--version"]).decode()
except FileNotFoundError:
halt("Error: Lisp executable '{0}' not in PATH".format (config.lisp_executable))
except subprocess.CalledProcessError as e:
halt("Error: {} from SBCL".format(e))
print("sbcl reports version {}".format(sbcl_version_string))
import re
try:
regexp = re.compile(r'(\d+)\.(\d+)\.(\d+)')
version = regexp.findall(sbcl_version_string)[0]
except IndexError:
halt("Error: issue with sbcl version string (please report)")
config.sbcl_version = map(int, version)
sbcl_min_version = (1, 2, 0)
for have, need in zip(config.sbcl_version, sbcl_min_version):
if have < need:
message = "found {}; required: sbcl >= {}"
message = message.format(config.sbcl_version, sbcl_min_version)
raise RequirementException(message)
print("... Kernel: using {}".format(sbcl_version_string))
elif config.lisp_implementation == "ccl":
if not config.lisp_executable:
config.lisp_executable = 'ccl'
try:
ccl_version_string = subprocess.check_output([config.lisp_executable, "-V"]).decode()
except FileNotFoundError:
halt("Error: Lisp executable '{0}' not in PATH".format (config.lisp_executable))
except subprocess.CalledProcessError as e:
halt("Error: {} from CCL".format(e))
#print("ccl version string = {}".format(ccl_version_string))
import re
m = re.match(r".*([0-9]+\.[0-9]+)", ccl_version_string)
if not m:
halt("Error: issue with ccl version string (please report)")
config.ccl_version = tuple([int(d) for d in m.group(1).split(".")])
#print("ccl version = {}".format(config.ccl_version))
if config.ccl_version[0] < 1 or config.ccl_version[1] < 10:
halt("Error: require CCL v1.10 or above")
print("... Kernel: using {}".format(ccl_version_string))
elif config.lisp_implementation == "ecl":
halt("Error: ECL not (yet) supported")
elif config.lisp_implementation == "cmucl":
halt("Error: CMUCL not (yet) supported")
elif config.lisp_implementation == "clisp":
halt("Error: CLisp not (yet) supported")
else:
halt("Error: Common Lisp implementation '{}' not supported".format(config.lisp_implementation))
##############################
## Installation of kernel ##
##############################
os.makedirs(config.ipython_dir + "/kernels/lisp", exist_ok=True)
if config.lisp_implementation == "sbcl":
KERNEL_SPEC = {
"argv": [
config.lisp_executable,'--non-interactive', '--load',
"{0}/cl-jupyter.lisp".format(config.cl_jupyter_startup_def_dir),
"{0}/src".format(config.cl_jupyter_startup_def_dir),
config.cl_jupyter_startup_def_dir,
'{connection_file}'],
"display_name": "SBCL Lisp",
"language": "lisp"
}
elif config.lisp_implementation == "ccl":
KERNEL_SPEC = {
"argv": [
config.lisp_executable,'--batch', '--load',
"{0}/cl-jupyter.lisp".format(config.cl_jupyter_startup_def_dir), "--",
"{0}/src".format(config.cl_jupyter_startup_def_dir),
config.cl_jupyter_startup_def_dir,
'{connection_file}'],
"display_name": "CCL Lisp",
"language": "lisp"
}
else:
halt("Error: unsupported lisp implementation '{}'".format(lisp_implementation))
with open(config.ipython_dir + "/kernels/lisp/kernel.json", "w") as kernel_spec_file:
json.dump(KERNEL_SPEC, kernel_spec_file)
print("Installation Complete.")