a2km 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3df1210c6b58fc739fde387f9280174885f01e58
4
- data.tar.gz: 9413d30be46d71bf2a5155169f4295543be6dc70
3
+ metadata.gz: 1bd0be9d8cdd24b99dfca16dad1fdaca90ae3eb2
4
+ data.tar.gz: 3e45923484920d0c0849d9a408d0ba7e644257d9
5
5
  SHA512:
6
- metadata.gz: 2feac328e487b78a2dcb4b06001e5371ba71f4f412ef7c025d6a7f4be880002e65fb53f0b2d2e06815749f0361fc8eea057e3edf1b7628ef0e81a77da1cbb88c
7
- data.tar.gz: fc39bc254f3056a4afc6ffa6e84114deaba7c96e06640cafd6af9d126ae48aebc07a3bd878dced14c22825cecc67204327a30d7d4517c7d4e8deb5ddbd3f9566
6
+ metadata.gz: 94d3adad6abb635bc980075c362bf875af5b8a78a9ed8f8d9fefa53b37e8eddcff9f1b0ee996a8dc756836a2e8b88d3b4966ac35bd7757e0ffb402e8a72d9a23
7
+ data.tar.gz: 5194abffa5630161a00d9f938d9ee29319f83bfe668d30cfb0689d3286b2edf6a34d08b7995df704f7277322a78f84125504dcd077f441eafae2adedfa30ba4b
data/.gitignore ADDED
@@ -0,0 +1,35 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /vendor/bundle
26
+ /lib/bundler/man/
27
+
28
+ # for a library or gem, you might want to ignore these files since the code is
29
+ # intended to run in multiple environments; otherwise, check them in:
30
+ # Gemfile.lock
31
+ # .ruby-version
32
+ # .ruby-gemset
33
+
34
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
35
+ .rvmrc
data/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2015, Min RK
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ * Neither the name of a2km nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # Assistant to the Kernel Manager
2
+
3
+ Utility for working with [Jupyter](https://jupyter.org) kernelspecs.
4
+
5
+
6
+ ## Install
7
+
8
+ gem install a2km
9
+
10
+
11
+ ## Examples
12
+
13
+ a2km clone python3 myenvpy3
14
+ a2km set python3-copy display_name "Super cool Python Kernel"
15
+ a2km add-env python3-copy SPARK_HOME=/path/to/spark
16
+ a2km add-argv python3-copy -- --debug
17
+ # <debug some stuff>
18
+ a2km rm-argv python3-copy -- debug
19
+
20
+ ## Kernelspecs for environments
21
+
22
+ a2km has an `env-kernel` subcommand for creating kernelspecs for your conda or virtual environments.
23
+ Just pass a2km the name of the env, and you should be set:
24
+
25
+ conda create -n myenv ipykernel
26
+ a2km env-kernel myenv
27
+
28
+
29
+ ## Commands
30
+
31
+ add-argv Add argument(s) to a kernelspec launch command
32
+ add-env Add environment variables to a kernelspec
33
+ clone Clone a kernelspec
34
+ env-kernel Create a kernel from an env (conda or virtualenv)
35
+ help Display global or [command] help documentation
36
+ locate Print the path of a kernelspec
37
+ rename Rename a kernelspec
38
+ rm Remove a kernelspec
39
+ rm-argv Remove arguments from a kernelspec launch command
40
+ rm-env Remove environment variables from a kernelspec
41
+ set Set a value in the kernelspec
42
+ show Show info about a kernelspec
43
+
44
+
45
+ ![Assistant TO the Kernel Manager](http://i.imgur.com/F0WLaYR.jpg)
data/a2km.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ require File.dirname(__FILE__) + '/lib/a2km/version'
2
+ require 'date'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.require_paths = ["lib"]
6
+ s.name = 'a2km'
7
+ s.version = A2KM::VERSION
8
+ s.date = Date.today.to_s
9
+ s.summary = 'Assistant to the Kernel Manager'
10
+ s.description = 'Working with Juptyer kernels'
11
+ s.authors = ['Min RK']
12
+ s.email = 'benjaminrk@gmail.com'
13
+ s.homepage = 'http://github.com/minrk/a2km'
14
+ s.license = 'BSD'
15
+ s.executables = ['a2km']
16
+ s.files = `git ls-files`.split($/)
17
+ s.require_paths = %w(lib)
18
+ s.add_runtime_dependency 'commander'
19
+ s.add_runtime_dependency 'highline'
20
+ s.add_runtime_dependency 'liquid'
21
+ end
data/bin/a2km CHANGED
@@ -1,286 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'json'
3
+ require File.expand_path('../lib/a2km', __dir__)
4
4
 
5
- require 'rubygems'
6
- require 'commander'
7
- require 'highline'
8
-
9
-
10
- class AssitantToTheKernelManager
11
- include Commander::Methods
12
-
13
- def initialize
14
- @kernels = nil
15
- end
16
-
17
- def kernels
18
- if @kernels.nil?
19
- js = JSON.parse `jupyter kernelspec list --json`
20
- @kernels = js['kernelspecs']
21
- end
22
- @kernels
23
- end
24
-
25
- def kernel_dirs
26
- paths = JSON.parse `jupyter --paths --json`
27
- paths['data'].map do |p|
28
- p + '/' + 'kernels'
29
- end
30
- end
31
-
32
- def user_kernel_dir
33
- kernel_dirs.first
34
- end
35
-
36
- def get_kernel(name)
37
- "Get a kernel by name. Die with message if not found."
38
- if not kernels.has_key? name
39
- STDERR.puts "No such kernel: #{name}"
40
- STDERR.puts "Found kernels: #{kernels.keys.sort.join(' ')}"
41
- exit(-1)
42
- end
43
- kernels[name]
44
- end
45
-
46
- def kernel_json_path(name)
47
- "Return path to a kernel's kernel.json"
48
- kernel = get_kernel(name)
49
- File.join(kernel['resources_dir'], 'kernel.json')
50
- end
51
-
52
- def get_kernel_json(name)
53
- "Return kernel.json contents for a given kernelspec"
54
- File.open(kernel_json_path(name)) do |f|
55
- return JSON.parse f.read
56
- end
57
- end
58
-
59
- def save_kernel_json(name, data)
60
- "save new kernel JSON"
61
- File.open(kernel_json_path(name), 'w') do |f|
62
- f.write JSON.pretty_generate(data)
63
- end
64
- end
65
-
66
- def run
67
- program :name, 'Assistant to the KernelManager'
68
- program :version, '0.0.1'
69
- program :description, 'Work with Jupyter kernelspecs'
70
-
71
- command :rename do |c|
72
- c.syntax = 'a2km rename <from> <to>'
73
- c.summary = 'Rename a kernelspec'
74
- c.description = 'Rename kernelspec FROM to TO'
75
- c.action do |args, options|
76
- from = args.shift
77
- to = args.shift
78
- kernel = get_kernel(from)
79
- src = kernel['resources_dir']
80
-
81
- dst = File.join File.dirname(src), to
82
- if File.exists? dst
83
- STDERR.puts "Destination already exists: #{dst}"
84
- exit(-1)
85
- end
86
-
87
- puts "Moving #{src} → #{dst}"
88
- FileUtils.mv src, dst
89
- end
90
- end
91
-
92
- command :show do |c|
93
- c.syntax = 'a2km show <spec>'
94
- c.description = 'Show info about a kernelspec'
95
- c.action do |args, options|
96
- name = args.first
97
- kernel = get_kernel(name)
98
- spec = kernel['spec']
99
- puts "Kernel: #{name} (#{spec['display_name']})"
100
- puts " path: #{kernel['resources_dir']}"
101
- puts " argv: #{spec['argv'].join(' ')}"
102
- end
103
- end
104
-
105
- command :locate do |c|
106
- c.syntax = 'a2km locate <spec>'
107
- c.description = 'Print the path of a kernelspec'
108
- c.action do |args, options|
109
- puts get_kernel(args.first)['resources_dir']
110
- end
111
- end
112
-
113
- command :"add-argv" do |c|
114
- c.syntax = 'a2km add-argv <spec> <arg1> [arg2] ...'
115
- c.description = 'Add argument(s) to a kernelspec launch command'
116
- c.action do |args, options|
117
- name = args.shift
118
- kernelspec = get_kernel_json(name)
119
- kernelspec['argv'] += args
120
- save_kernel_json(name, kernelspec)
121
- puts "New argv: #{kernelspec['argv'].join(' ')}"
122
- end
123
- end
124
-
125
- command :"rm-argv" do |c|
126
- c.syntax = 'a2km rm-argv <spec> <arg1> [arg2] ...'
127
- c.summary = 'Remove arguments from a kernelspec launch command'
128
- c.description = c.summary + ". To remove args starting with '-'," \
129
- " use '--'."
130
- c.examples = [
131
- "a2km rm-argv myspec x",
132
- "a2km rm-argv myspec -- --debug",
133
- ]
134
- c.action do |args, options|
135
- name = args.shift
136
- to_remove = args
137
- kernelspec = get_kernel_json(name)
138
- argv = kernelspec['argv']
139
- argv.reject! { |arg| to_remove.include? arg }
140
- save_kernel_json(name, kernelspec)
141
- puts "New argv: #{argv.join(' ')}"
142
- end
143
- end
144
-
145
- command :"add-env" do |c|
146
- c.syntax = 'a2km add-env <spec> <key=value> [key=value] ...'
147
- c.summary = 'Add environment variables to a kernelspec'
148
- c.description = 'Add environment variables to a kernelspec.' \
149
- ' If no value is given, the value from the current env is used.'
150
- c.action do |args, options|
151
- name = args.shift
152
- spec = get_kernel_json(name)
153
- if not spec.has_key? 'env'
154
- spec['env'] = {}
155
- end
156
- env = spec['env']
157
- args.each do |arg|
158
- key_value = arg.split('=', 2)
159
- key = key_value.first
160
- if key_value.length == 2
161
- value = key_value[1]
162
- else
163
- value = ENV[key]
164
- end
165
- env[key] = value
166
- end
167
- save_kernel_json(name, spec)
168
- puts "New env: #{env}"
169
- end
170
- end
171
-
172
- command :"rm-env" do |c|
173
- c.syntax = 'a2km rm-env <spec> <key> [key] ...'
174
- c.description = 'Remove environment variables from a kernelspec'
175
- c.action do |args, options|
176
- name = args.shift
177
- spec = get_kernel_json(name)
178
- if not spec.has_key? 'env'
179
- spec['env'] = {}
180
- end
181
- env = spec['env']
182
- args.each do |arg|
183
- env.delete(arg)
184
- end
185
- save_kernel_json(name, spec)
186
- puts "New env: #{env}"
187
- end
188
- end
189
-
190
- command :rm do |c|
191
- c.syntax = 'a2km rm <spec>'
192
- c.description = 'Remove a kernelspec'
193
- c.option '-f', "Force removal (skip confirmation)"
194
- c.action do |args, options|
195
- name = args.shift
196
- if not kernels.has_key? name
197
- STDERR.puts "No such kernel: #{name}"
198
- STDERR.puts "Found kernels: #{kernels.keys.sort.join(' ')}"
199
- exit(-1)
200
- end
201
- path = kernels[name]['resources_dir']
202
- if options.f or not HighLine.agree("Permanently delete #{path}? (yes/no)")
203
- STDERR.puts "Aborting."
204
- exit(-1)
205
- end
206
- puts "Removing #{path}"
207
- FileUtils.rm_r path
208
- end
209
- end
210
-
211
- command :set do |c|
212
- c.syntax = 'a2km set <name> <key> <value>'
213
- c.description = 'Set a value in the kernelspec'
214
- c.examples = [
215
- 'a2km set python3 display_name "My Python 3"'
216
- ]
217
- c.action do |args, options|
218
- name = args.shift
219
- key = args.shift
220
- value = args.shift
221
- if value.nil?
222
- STDERR.puts c.syntax
223
- exit(-1)
224
- end
225
- puts "Setting #{name}.#{key} = '#{value}'"
226
- spec = get_kernel_json(name)
227
- spec[key] = value
228
- save_kernel_json(name, spec)
229
- end
230
- end
231
-
232
- command :clone do |c|
233
- c.syntax = 'a2km clone <from> <to> [display_name]'
234
- c.summary = 'Clone a kernelspec'
235
- c.description = 'Clones kernelspec FROM to TO'
236
- c.option '--user', 'Force clone to be in the user directory' \
237
- ' (default is to use the same directory as FROM)'
238
- c.action do |args, options|
239
- options.default :user => false
240
-
241
- if args.length < 2 or args.length > 3
242
- STDERR.puts "Must specify FROM and TO"
243
- exit(-1)
244
- end
245
- from_name = args.shift
246
- to_name = args.shift
247
- if args.length > 0
248
- display_name = args.shift
249
- else
250
- display_name = to_name
251
- end
252
-
253
- from = get_kernel(from_name)
254
- from = kernels[from_name]
255
- src = from['resources_dir']
256
-
257
- if options.user?
258
- dst_dir = user_kernel_dir
259
- makedirs(dst_dir)
260
- else
261
- dst_dir = File.dirname src
262
- end
263
- dst = File.join dst_dir, to_name
264
- if File.exists? dst
265
- STDERR.puts "Destination already exists: #{dst}"
266
- exit(-1)
267
- end
268
-
269
- puts "Cloning #{src} → #{dst}"
270
- FileUtils.cp_r src, dst
271
- kernel_json = File.join(dst, 'kernel.json')
272
- kernelspec = File.open(kernel_json) do |f|
273
- JSON.parse f.read
274
- end
275
- kernelspec['display_name'] = display_name
276
- File.open(kernel_json, 'w') do |f|
277
- f.write JSON.pretty_generate(kernelspec)
278
- end
279
- end
280
- end
281
-
282
- run!
283
- end
284
- end
285
-
286
- AssitantToTheKernelManager.new.run
5
+ A2KM::run
data/lib/a2km.rb ADDED
@@ -0,0 +1,4 @@
1
+ require __dir__ + '/a2km/version'
2
+ require __dir__ + '/a2km/api'
3
+ require __dir__ + '/a2km/env'
4
+ require __dir__ + '/a2km/command'
data/lib/a2km/api.rb ADDED
@@ -0,0 +1,59 @@
1
+ # Copyright Min RK, License: BSD 3-clause
2
+
3
+ require 'json'
4
+
5
+ module A2KM
6
+
7
+ def initialize
8
+ @kernels = nil
9
+ end
10
+
11
+ def A2KM.kernels
12
+ if @kernels.nil?
13
+ js = JSON.parse `jupyter kernelspec list --json`
14
+ @kernels = js['kernelspecs']
15
+ end
16
+ @kernels
17
+ end
18
+
19
+ def A2KM.kernel_dirs
20
+ paths = JSON.parse `jupyter --paths --json`
21
+ paths['data'].map do |p|
22
+ p + '/' + 'kernels'
23
+ end
24
+ end
25
+
26
+ def A2KM.user_kernel_dir
27
+ kernel_dirs.first
28
+ end
29
+
30
+ def A2KM.get_kernel(name)
31
+ "Get a kernel by name. Die with message if not found."
32
+ if not kernels.has_key? name
33
+ STDERR.puts "No such kernel: #{name}"
34
+ STDERR.puts "Found kernels: #{kernels.keys.sort.join(' ')}"
35
+ exit(-1)
36
+ end
37
+ kernels[name]
38
+ end
39
+
40
+ def A2KM.kernel_json_path(name)
41
+ "Return path to a kernel's kernel.json"
42
+ kernel = get_kernel(name)
43
+ File.join(kernel['resources_dir'], 'kernel.json')
44
+ end
45
+
46
+ def A2KM.get_kernel_json(name)
47
+ "Return kernel.json contents for a given kernelspec"
48
+ File.open(kernel_json_path(name)) do |f|
49
+ return JSON.parse f.read
50
+ end
51
+ end
52
+
53
+ def A2KM.save_kernel_json(name, data)
54
+ "save new kernel JSON"
55
+ File.open(kernel_json_path(name), 'w') do |f|
56
+ f.write JSON.pretty_generate(data)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,263 @@
1
+ # Copyright Min RK, License: BSD 3-clause
2
+
3
+ require 'json'
4
+
5
+ require 'rubygems'
6
+ require 'commander'
7
+ require 'highline'
8
+
9
+ module A2KM
10
+
11
+ # Start the a2km entrypoint
12
+ def A2KM.run
13
+ CLI.new.run
14
+ end
15
+
16
+ # The A2KM CLI entrypoint
17
+ class CLI
18
+ include Commander::Methods
19
+
20
+ def run
21
+ program :name, 'Assistant to the KernelManager'
22
+ program :version, VERSION
23
+ program :description, 'Work with Jupyter kernelspecs'
24
+
25
+ command :rename do |c|
26
+ c.syntax = 'a2km rename <from> <to>'
27
+ c.summary = 'Rename a kernelspec'
28
+ c.description = 'Rename kernelspec FROM to TO'
29
+ c.action do |args, options|
30
+ from = args.shift
31
+ to = args.shift
32
+ kernel = A2KM.get_kernel(from)
33
+ src = kernel['resources_dir']
34
+
35
+ dst = File.join File.dirname(src), to
36
+ if File.exists? dst
37
+ STDERR.puts "Destination already exists: #{dst}"
38
+ exit(-1)
39
+ end
40
+
41
+ puts "Moving #{src} → #{dst}"
42
+ FileUtils.mv src, dst
43
+ end
44
+ end
45
+
46
+ command :show do |c|
47
+ c.syntax = 'a2km show <spec>'
48
+ c.description = 'Show info about a kernelspec'
49
+ c.action do |args, options|
50
+ name = args.first
51
+ kernel = A2KM.get_kernel(name)
52
+ spec = kernel['spec']
53
+ puts "Kernel: #{name} (#{spec['display_name']})"
54
+ puts " path: #{kernel['resources_dir']}"
55
+ puts " argv: #{spec['argv'].join(' ')}"
56
+ end
57
+ end
58
+
59
+ command :locate do |c|
60
+ c.syntax = 'a2km locate <spec>'
61
+ c.description = 'Print the path of a kernelspec'
62
+ c.action do |args, options|
63
+ puts A2KM.get_kernel(args.first)['resources_dir']
64
+ end
65
+ end
66
+
67
+ command :"add-argv" do |c|
68
+ c.syntax = 'a2km add-argv <spec> <arg1> [arg2] ...'
69
+ c.description = 'Add argument(s) to a kernelspec launch command'
70
+ c.action do |args, options|
71
+ name = args.shift
72
+ kernelspec = A2KM.get_kernel_json(name)
73
+ kernelspec['argv'] += args
74
+ A2KM.save_kernel_json(name, kernelspec)
75
+ puts "New argv: #{kernelspec['argv'].join(' ')}"
76
+ end
77
+ end
78
+
79
+ command :"rm-argv" do |c|
80
+ c.syntax = 'a2km rm-argv <spec> <arg1> [arg2] ...'
81
+ c.summary = 'Remove arguments from a kernelspec launch command'
82
+ c.description = c.summary + ". To remove args starting with '-'," \
83
+ " use '--'."
84
+ c.examples = [
85
+ "a2km rm-argv myspec x",
86
+ "a2km rm-argv myspec -- --debug",
87
+ ]
88
+ c.action do |args, options|
89
+ name = args.shift
90
+ to_remove = args
91
+ kernelspec = A2KM.get_kernel_json(name)
92
+ argv = kernelspec['argv']
93
+ argv.reject! { |arg| to_remove.include? arg }
94
+ A2KM.save_kernel_json(name, kernelspec)
95
+ puts "New argv: #{argv.join(' ')}"
96
+ end
97
+ end
98
+
99
+ command :"add-env" do |c|
100
+ c.syntax = 'a2km add-env <spec> <key=value> [key=value] ...'
101
+ c.summary = 'Add environment variables to a kernelspec'
102
+ c.description = 'Add environment variables to a kernelspec.' \
103
+ ' If no value is given, the value from the current env is used.'
104
+ c.action do |args, options|
105
+ name = args.shift
106
+ spec = A2KM.get_kernel_json(name)
107
+ if not spec.has_key? 'env'
108
+ spec['env'] = {}
109
+ end
110
+ env = spec['env']
111
+ args.each do |arg|
112
+ key_value = arg.split('=', 2)
113
+ key = key_value.first
114
+ if key_value.length == 2
115
+ value = key_value[1]
116
+ else
117
+ value = ENV[key]
118
+ end
119
+ env[key] = value
120
+ end
121
+ A2KM.save_kernel_json(name, spec)
122
+ puts "New env: #{env}"
123
+ end
124
+ end
125
+
126
+ command :"rm-env" do |c|
127
+ c.syntax = 'a2km rm-env <spec> <key> [key] ...'
128
+ c.description = 'Remove environment variables from a kernelspec'
129
+ c.action do |args, options|
130
+ name = args.shift
131
+ spec = A2KM.get_kernel_json(name)
132
+ if not spec.has_key? 'env'
133
+ spec['env'] = {}
134
+ end
135
+ env = spec['env']
136
+ args.each do |arg|
137
+ env.delete(arg)
138
+ end
139
+ A2KM.save_kernel_json(name, spec)
140
+ puts "New env: #{env}"
141
+ end
142
+ end
143
+
144
+ command :rm do |c|
145
+ c.syntax = 'a2km rm <spec>'
146
+ c.description = 'Remove a kernelspec'
147
+ c.option '-f', "Force removal (skip confirmation)"
148
+ c.action do |args, options|
149
+ name = args.shift
150
+ if not A2KM.kernels.has_key? name
151
+ STDERR.puts "No such kernel: #{name}"
152
+ STDERR.puts "Found kernels: #{kernels.keys.sort.join(' ')}"
153
+ exit(-1)
154
+ end
155
+ path = A2KM.kernels[name]['resources_dir']
156
+ if options.f or not HighLine.agree("Permanently delete #{path}? (yes/no)")
157
+ STDERR.puts "Aborting."
158
+ exit(-1)
159
+ end
160
+ puts "Removing #{path}"
161
+ FileUtils.rm_r path
162
+ end
163
+ end
164
+
165
+ command :"env-kernel" do |c|
166
+ c.syntax = 'a2km env-kernel [--venv|--conda] <name> [-e env-name] [--prefix PREFIX] [--user]'
167
+ c.description = 'Create a kernel from an env (conda or virtualenv)'
168
+
169
+ c.option '--conda', "use conda env (default)"
170
+ c.option '--venv', "use virtualenv"
171
+ c.option '-e ENV', "specify the environment name to use (if different from <name>)"
172
+ c.option '--prefix PREFIX', 'specify the prefix for the executable to be installed'
173
+ c.action do |args, options|
174
+ name = args.shift
175
+ options.default :user => false
176
+ options.default :e => name
177
+ options.default :prefix => '~'
178
+ env = options.e
179
+
180
+ kind = 'conda'
181
+ if options.venv
182
+ kind = 'venv'
183
+ end
184
+ puts "Making kernel '#{name}' for #{kind}:#{env}"
185
+ spec = A2KM::make_env_kernel(name, env, kind: kind, prefix: options.prefix)
186
+ end
187
+ end
188
+
189
+ command :set do |c|
190
+ c.syntax = 'a2km set <name> <key> <value>'
191
+ c.description = 'Set a value in the kernelspec'
192
+ c.examples = [
193
+ 'a2km set python3 display_name "My Python 3"'
194
+ ]
195
+ c.action do |args, options|
196
+ name = args.shift
197
+ key = args.shift
198
+ value = args.shift
199
+ if value.nil?
200
+ STDERR.puts c.syntax
201
+ exit(-1)
202
+ end
203
+ puts "Setting #{name}.#{key} = '#{value}'"
204
+ spec = A2KM.get_kernel_json(name)
205
+ spec[key] = value
206
+ A2KM.save_kernel_json(name, spec)
207
+ end
208
+ end
209
+
210
+ command :clone do |c|
211
+ c.syntax = 'a2km clone <from> <to> [display_name]'
212
+ c.summary = 'Clone a kernelspec'
213
+ c.description = 'Clones kernelspec FROM to TO'
214
+ c.option '--user', 'Force clone to be in the user directory' \
215
+ ' (default is to use the same directory as FROM)'
216
+ c.action do |args, options|
217
+ options.default :user => false
218
+
219
+ if args.length < 2 or args.length > 3
220
+ STDERR.puts "Must specify FROM and TO"
221
+ exit(-1)
222
+ end
223
+ from_name = args.shift
224
+ to_name = args.shift
225
+ if args.length > 0
226
+ display_name = args.shift
227
+ else
228
+ display_name = to_name
229
+ end
230
+
231
+ from = A2KM.get_kernel(from_name)
232
+ from = A2KM.kernels[from_name]
233
+ src = from['resources_dir']
234
+
235
+ if options.user?
236
+ dst_dir = A2KM.user_kernel_dir
237
+ makedirs(dst_dir)
238
+ else
239
+ dst_dir = File.dirname src
240
+ end
241
+ dst = File.join dst_dir, to_name
242
+ if File.exists? dst
243
+ STDERR.puts "Destination already exists: #{dst}"
244
+ exit(-1)
245
+ end
246
+
247
+ puts "Cloning #{src} → #{dst}"
248
+ FileUtils.cp_r src, dst
249
+ kernel_json = File.join(dst, 'kernel.json')
250
+ kernelspec = File.open(kernel_json) do |f|
251
+ JSON.parse f.read
252
+ end
253
+ kernelspec['display_name'] = display_name
254
+ File.open(kernel_json, 'w') do |f|
255
+ f.write JSON.pretty_generate(kernelspec)
256
+ end
257
+ end
258
+ end
259
+
260
+ run!
261
+ end
262
+ end
263
+ end
data/lib/a2km/env.rb ADDED
@@ -0,0 +1,104 @@
1
+ # Copyright Min RK, License: BSD 3-clause
2
+
3
+ require 'Open3'
4
+ require 'liquid'
5
+
6
+ class ENVError < StandardError
7
+ end
8
+
9
+ module A2KM
10
+
11
+ module ENV_UTILS
12
+
13
+ ACTIVATE_TPL_S = "{{activate}} {{name}}"
14
+ ACTIVATE_TPL = Liquid::Template.parse ACTIVATE_TPL_S
15
+ ENV_CMD_TPL = Liquid::Template.parse(<<-END
16
+ #!/usr/bin/env bash
17
+ set -e
18
+ #{ACTIVATE_TPL_S}
19
+ exec python -m ipykernel $@
20
+ END
21
+ )
22
+
23
+ CONDA_ACTIVATE = 'source activate'
24
+ VIRTUALENV_ACTIVATE = 'workon'
25
+
26
+ module_function
27
+
28
+ def activate_cmd(kind)
29
+ case kind
30
+ when 'conda'
31
+ return CONDA_ACTIVATE
32
+ when 'venv'
33
+ return VIRTUALENV_ACTIVATE
34
+ else
35
+ throw ArgumentError, "kind must be 'conda' or 'venv', not '#{kind}'"
36
+ end
37
+ end
38
+
39
+ def in_env(env, cmd, kind: 'conda')
40
+ Open3::popen2('bash') do |stdin, stdout, wait_thr|
41
+ stdin.puts 'set -e'
42
+ stdin.puts ACTIVATE_TPL.render('name' => env, 'activate' => activate_cmd(kind))
43
+ stdin.puts cmd
44
+ stdin.close
45
+ status = wait_thr.value
46
+ if status != 0
47
+ raise ENVError.new("Failed to run #{cmd} in #{kind} env: #{env}")
48
+ end
49
+ return stdout.read
50
+ end
51
+ end
52
+
53
+ def kernel_script(name, kind: 'conda')
54
+ # return kernel script as a string
55
+ ENV_CMD_TPL.render('name' => name, 'activate' => activate_cmd(kind))
56
+ end
57
+
58
+ def ipykernel_version(env, kind: 'conda')
59
+ in_env(env, "python -c 'import ipykernel; print(ipykernel.__version__)'", kind: kind)
60
+ end
61
+
62
+ def make_kernel_exe(kernel_name, env_name, kind: 'conda', prefix: '/usr/local')
63
+ cmd = kernel_script(env_name, kind: kind)
64
+ bin = File.join(prefix, 'bin')
65
+ if not File.directory? bin
66
+ FileUtils.mkdir bin
67
+ end
68
+ kernel_exe = File.join(bin, "jupyter-kernel-#{kernel_name}")
69
+ puts "Making executable '#{kernel_exe}'"
70
+
71
+ File.open(kernel_exe, 'w') do |f|
72
+ f.write(cmd)
73
+ end
74
+ File.chmod(0o755, kernel_exe)
75
+ return kernel_exe
76
+ end
77
+
78
+ def get_prefix(name, kind)
79
+ in_env(name, 'python -c "import sys; print(sys.prefix)"', kind: kind)
80
+ end
81
+ end
82
+
83
+ def self.make_env_kernel(kernel_name, env_name, kind: 'conda', prefix: '/usr/local', user: true)
84
+ prefix = File.expand_path(prefix)
85
+ v = ENV_UTILS.ipykernel_version(env_name, kind: kind)
86
+
87
+ puts "Found ipykernel-#{v}"
88
+
89
+ exe = ENV_UTILS.make_kernel_exe(kernel_name, env_name, kind: kind, prefix: prefix)
90
+ if user
91
+ user_arg = '--user'
92
+ else
93
+ user_arg = ''
94
+ end
95
+
96
+ ENV_UTILS.in_env(env_name, "python -m ipykernel.kernelspec --name #{kernel_name} #{user_arg}", kind: kind)
97
+ spec = A2KM.get_kernel_json(kernel_name)
98
+ spec['argv'] = [exe, '-f', '{connection_file}']
99
+ spec['display_name'] = "#{spec['display_name']} (#{kind}:#{env_name})"
100
+ A2KM.save_kernel_json(kernel_name, spec)
101
+
102
+ end
103
+
104
+ end
@@ -0,0 +1,5 @@
1
+ # Copyright Min RK, License: BSD 3-clause
2
+
3
+ module A2KM
4
+ VERSION = '0.0.2'
5
+ end
metadata CHANGED
@@ -1,15 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: a2km
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Min RK
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-18 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2015-09-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: commander
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: highline
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: liquid
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
13
55
  description: Working with Juptyer kernels
14
56
  email: benjaminrk@gmail.com
15
57
  executables:
@@ -17,7 +59,16 @@ executables:
17
59
  extensions: []
18
60
  extra_rdoc_files: []
19
61
  files:
62
+ - ".gitignore"
63
+ - LICENSE
64
+ - README.md
65
+ - a2km.gemspec
20
66
  - bin/a2km
67
+ - lib/a2km.rb
68
+ - lib/a2km/api.rb
69
+ - lib/a2km/command.rb
70
+ - lib/a2km/env.rb
71
+ - lib/a2km/version.rb
21
72
  homepage: http://github.com/minrk/a2km
22
73
  licenses:
23
74
  - BSD
@@ -36,11 +87,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
36
87
  - - ">="
37
88
  - !ruby/object:Gem::Version
38
89
  version: '0'
39
- requirements:
40
- - commander
41
- - highline
90
+ requirements: []
42
91
  rubyforge_project:
43
- rubygems_version: 2.4.5
92
+ rubygems_version: 2.4.5.1
44
93
  signing_key:
45
94
  specification_version: 4
46
95
  summary: Assistant to the Kernel Manager