iruby 0.2.7 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ubuntu.yml +62 -0
  3. data/CHANGES +62 -0
  4. data/Gemfile +3 -1
  5. data/LICENSE +1 -1
  6. data/README.md +148 -27
  7. data/Rakefile +36 -10
  8. data/ci/Dockerfile.base.erb +41 -0
  9. data/ci/Dockerfile.main.erb +7 -0
  10. data/ci/requirements.txt +1 -0
  11. data/docker/setup.sh +15 -0
  12. data/docker/test.sh +7 -0
  13. data/iruby.gemspec +14 -18
  14. data/lib/iruby.rb +19 -3
  15. data/lib/iruby/backend.rb +22 -2
  16. data/lib/iruby/command.rb +76 -13
  17. data/lib/iruby/display.rb +69 -39
  18. data/lib/iruby/formatter.rb +5 -4
  19. data/lib/iruby/input.rb +41 -0
  20. data/lib/iruby/input/README.ipynb +502 -0
  21. data/lib/iruby/input/README.md +299 -0
  22. data/lib/iruby/input/autoload.rb +25 -0
  23. data/lib/iruby/input/builder.rb +67 -0
  24. data/lib/iruby/input/button.rb +47 -0
  25. data/lib/iruby/input/cancel.rb +32 -0
  26. data/lib/iruby/input/checkbox.rb +74 -0
  27. data/lib/iruby/input/date.rb +37 -0
  28. data/lib/iruby/input/field.rb +31 -0
  29. data/lib/iruby/input/file.rb +57 -0
  30. data/lib/iruby/input/form.rb +77 -0
  31. data/lib/iruby/input/label.rb +27 -0
  32. data/lib/iruby/input/multiple.rb +76 -0
  33. data/lib/iruby/input/popup.rb +41 -0
  34. data/lib/iruby/input/radio.rb +59 -0
  35. data/lib/iruby/input/select.rb +59 -0
  36. data/lib/iruby/input/textarea.rb +23 -0
  37. data/lib/iruby/input/widget.rb +34 -0
  38. data/lib/iruby/jupyter.rb +77 -0
  39. data/lib/iruby/kernel.rb +67 -22
  40. data/lib/iruby/ostream.rb +24 -8
  41. data/lib/iruby/session.rb +85 -67
  42. data/lib/iruby/session/cztop.rb +70 -0
  43. data/lib/iruby/session/ffi_rzmq.rb +87 -0
  44. data/lib/iruby/session/mixin.rb +47 -0
  45. data/lib/iruby/session_adapter.rb +66 -0
  46. data/lib/iruby/session_adapter/cztop_adapter.rb +45 -0
  47. data/lib/iruby/session_adapter/ffirzmq_adapter.rb +55 -0
  48. data/lib/iruby/session_adapter/pyzmq_adapter.rb +77 -0
  49. data/lib/iruby/utils.rb +5 -2
  50. data/lib/iruby/version.rb +1 -1
  51. data/run-test.sh +12 -0
  52. data/tasks/ci.rake +65 -0
  53. data/test/helper.rb +90 -0
  54. data/test/integration_test.rb +22 -11
  55. data/test/iruby/backend_test.rb +37 -0
  56. data/test/iruby/command_test.rb +207 -0
  57. data/test/iruby/jupyter_test.rb +27 -0
  58. data/test/iruby/mime_test.rb +32 -0
  59. data/test/iruby/multi_logger_test.rb +1 -2
  60. data/test/iruby/session_adapter/cztop_adapter_test.rb +20 -0
  61. data/test/iruby/session_adapter/ffirzmq_adapter_test.rb +20 -0
  62. data/test/iruby/session_adapter/session_adapter_test_base.rb +27 -0
  63. data/test/iruby/session_adapter_test.rb +91 -0
  64. data/test/iruby/session_test.rb +47 -0
  65. data/test/run-test.rb +18 -0
  66. metadata +130 -46
  67. data/.travis.yml +0 -16
  68. data/CONTRIBUTORS +0 -19
  69. data/test/test_helper.rb +0 -5
data/lib/iruby/utils.rb CHANGED
@@ -7,8 +7,11 @@ module IRuby
7
7
  def display(obj, options = {})
8
8
  Kernel.instance.session.send(:publish, :display_data,
9
9
  data: Display.display(obj, options),
10
- metadata: {},
11
- source: 'ruby') unless obj.nil?
10
+ metadata: {}) unless obj.nil?
11
+ end
12
+
13
+ def clear_output(wait=false)
14
+ Display.clear_output(wait)
12
15
  end
13
16
 
14
17
  def table(s, **options)
data/lib/iruby/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module IRuby
2
- VERSION = '0.2.7'
2
+ VERSION = '0.5.0'
3
3
  end
data/run-test.sh ADDED
@@ -0,0 +1,12 @@
1
+ #! /bin/bash
2
+
3
+ set -ex
4
+
5
+ export PYTHON=python3
6
+
7
+ ADAPTERS="cztop ffi-rzmq pyzmq"
8
+
9
+ for adapter in $ADAPTERS; do
10
+ export IRUBY_TEST_SESSION_ADAPTER_NAME=$adapter
11
+ bundle exec rake test TESTOPTS=-v
12
+ done
data/tasks/ci.rake ADDED
@@ -0,0 +1,65 @@
1
+ namespace :ci do
2
+ namespace :docker do
3
+ def ruby_version
4
+ @ruby_version ||= ENV['ruby_version']
5
+ end
6
+
7
+ def ruby_image_name
8
+ @ruby_image_name ||= "rubylang/ruby:#{ruby_version}-bionic"
9
+ end
10
+
11
+ def iruby_test_base_image_name
12
+ @iruby_test_base_image_name ||= "iruby-test-base:ruby-#{ruby_version}"
13
+ end
14
+
15
+ def iruby_test_image_name
16
+ @docker_image_name ||= begin
17
+ "sciruby/iruby-test:ruby-#{ruby_version}"
18
+ end
19
+ end
20
+
21
+ def docker_image_found?(image_name)
22
+ image_id = `docker images -q #{image_name}`.chomp
23
+ image_id.length > 0
24
+ end
25
+
26
+ directory 'tmp'
27
+
28
+ desc "Build iruby-test-base docker image"
29
+ task :build_test_base_image => 'tmp' do
30
+ unless docker_image_found?(iruby_test_base_image_name)
31
+ require 'erb'
32
+ dockerfile_content = ERB.new(File.read('ci/Dockerfile.base.erb')).result(binding)
33
+ File.write('tmp/Dockerfile', dockerfile_content)
34
+ sh 'docker', 'build', '-t', iruby_test_base_image_name, '-f', 'tmp/Dockerfile', '.'
35
+ end
36
+ end
37
+
38
+ desc "Pull docker image of ruby"
39
+ task :pull_ruby_image do
40
+ sh 'docker', 'pull', ruby_image_name
41
+ end
42
+
43
+ desc "Build iruby-test docker image"
44
+ task :build_test_image => 'tmp' do
45
+ require 'erb'
46
+ dockerfile_content = ERB.new(File.read('ci/Dockerfile.main.erb')).result(binding)
47
+ File.write('tmp/Dockerfile', dockerfile_content)
48
+ sh 'docker', 'build', '-t', iruby_test_image_name, '-f', 'tmp/Dockerfile', '.'
49
+ end
50
+
51
+ desc 'before_install script for CI with Docker'
52
+ task :before_install => :pull_ruby_image
53
+ task :before_install => :build_test_base_image
54
+
55
+ desc 'install script for CI with Docker'
56
+ task :install => :build_test_image
57
+
58
+ desc 'main script for CI with Docker'
59
+ task :script do
60
+ volumes = ['-v', "#{Dir.pwd}:/iruby"] if ENV['attach_pwd']
61
+ sh 'docker', 'run', '--rm', '-it', *volumes,
62
+ iruby_test_image_name, 'bash', 'run-test.sh'
63
+ end
64
+ end
65
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,90 @@
1
+ require "iruby"
2
+ require "test/unit"
3
+ require "test/unit/rr"
4
+ require "tmpdir"
5
+
6
+ module IRubyTest
7
+ class TestBase < Test::Unit::TestCase
8
+ def assert_output(stdout=nil, stderr=nil)
9
+ flunk "assert_output requires a block to capture output." unless block_given?
10
+
11
+ out, err = capture_io do
12
+ yield
13
+ end
14
+
15
+ y = check_assert_output_result(stderr, err, "stderr")
16
+ x = check_assert_output_result(stdout, out, "stdout")
17
+
18
+ (!stdout || x) && (!stderr || y)
19
+ end
20
+
21
+ private
22
+
23
+ def capture_io
24
+ captured_stdout = StringIO.new
25
+ captured_stderr = StringIO.new
26
+
27
+ orig_stdout, $stdout = $stdout, captured_stdout
28
+ orig_stderr, $stderr = $stderr, captured_stderr
29
+
30
+ yield
31
+
32
+ return captured_stdout.string, captured_stderr.string
33
+ ensure
34
+ $stdout = orig_stdout
35
+ $stderr = orig_stderr
36
+ end
37
+
38
+ def check_assert_output_result(expected, actual, name)
39
+ if expected
40
+ message = "In #{name}"
41
+ case expected
42
+ when Regexp
43
+ assert_match(expected, actual, message)
44
+ else
45
+ assert_equal(expected, actual, message)
46
+ end
47
+ end
48
+ end
49
+
50
+ def ignore_warning
51
+ saved, $VERBOSE = $VERBOSE , nil
52
+ yield
53
+ ensure
54
+ $VERBOSE = saved
55
+ end
56
+
57
+ def with_env(env)
58
+ keys = env.keys
59
+ saved_values = ENV.values_at(*keys)
60
+ ENV.update(env)
61
+ yield
62
+ ensure
63
+ if keys && saved_values
64
+ keys.zip(saved_values) do |k, v|
65
+ ENV[k] = v
66
+ end
67
+ end
68
+ end
69
+
70
+ def windows_only
71
+ omit('windows only test') unless windows?
72
+ end
73
+
74
+ def apple_only
75
+ omit('apple only test') unless windows?
76
+ end
77
+
78
+ def unix_only
79
+ omit('unix only test') if windows? || apple?
80
+ end
81
+
82
+ def windows?
83
+ /mingw|mswin/ =~ RUBY_PLATFORM
84
+ end
85
+
86
+ def apple?
87
+ /darwin/ =~ RUBY_PLATFORM
88
+ end
89
+ end
90
+ end
@@ -1,40 +1,51 @@
1
- require 'test_helper'
2
- require 'open3'
1
+ require 'pty'
3
2
  require 'expect'
4
3
 
5
- class IntegrationTest < IRubyTest
4
+ class IRubyTest::IntegrationTest < IRubyTest::TestBase
6
5
  def setup
7
- @stdin, @stdout, @stderr, @process = Open3.popen3('bin/iruby')
6
+ $expect_verbose = false # make true if you want to dump the output of iruby console
7
+
8
+ @in, @out, pid = PTY.spawn('bin/iruby --simple-prompt')
9
+ @waiter = Thread.start { Process.waitpid(pid) }
8
10
  expect 'In [', 30
9
11
  expect '1'
10
12
  expect ']:'
11
13
  end
12
14
 
13
15
  def teardown
14
- @stdin.close
15
- @stdout.close
16
- @stderr.close
17
- @process.kill
16
+ @in.close
17
+ @out.close
18
+ @waiter.join
18
19
  end
19
20
 
20
21
  def write(input)
21
- @stdin.puts input
22
+ @out.puts input
22
23
  end
23
24
 
24
- def expect(pattern, timeout = 1)
25
- assert @stdout.expect(pattern, timeout), "#{pattern} expected"
25
+ def expect(pattern, timeout = 10)
26
+ assert @in.expect(pattern, timeout), "#{pattern} expected, but timeout"
27
+ end
28
+
29
+ def wait_prompt
30
+ expect 'In ['
31
+ expect ']:'
26
32
  end
27
33
 
28
34
  def test_interaction
35
+ omit("This test too much unstable")
36
+
29
37
  write '"Hello, world!"'
30
38
  expect '"Hello, world!"'
31
39
 
40
+ wait_prompt
32
41
  write 'puts "Hello!"'
33
42
  expect 'Hello!'
34
43
 
44
+ wait_prompt
35
45
  write '12 + 12'
36
46
  expect '24'
37
47
 
48
+ wait_prompt
38
49
  write 'ls'
39
50
  expect 'self.methods'
40
51
  end
@@ -0,0 +1,37 @@
1
+ module IRubyTest
2
+ class PlainBackendTest < IRubyTest::TestBase
3
+ def setup
4
+ @plainbackend = IRuby::PlainBackend.new
5
+ end
6
+
7
+ def test_eval_one_plus_two
8
+ assert_equal 3, @plainbackend.eval('1+2', false)
9
+ end
10
+
11
+ def test_complete_req
12
+ assert_includes @plainbackend.complete('req'), 'require'
13
+ end
14
+
15
+ def test_complete_2_dot
16
+ assert_includes @plainbackend.complete('2.'), '2.even?'
17
+ end
18
+ end
19
+
20
+ class PryBackendTest < IRubyTest::TestBase
21
+ def setup
22
+ @prybackend = IRuby::PryBackend.new
23
+ end
24
+
25
+ def test_eval_one_plus_two
26
+ assert_equal 3, @prybackend.eval('1+2', false)
27
+ end
28
+
29
+ def test_complete_req
30
+ assert_includes @prybackend.complete('req'), 'require'
31
+ end
32
+
33
+ def test_complete_2_dot
34
+ assert_includes @prybackend.complete('2.'), '2.even?'
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,207 @@
1
+ require 'iruby/command'
2
+
3
+ module IRubyTest
4
+ class CommandTest < TestBase
5
+ def test_with_empty_argv
6
+ with_env('JUPYTER_DATA_DIR' => nil,
7
+ 'IPYTHONDIR' => nil) do
8
+ assert_output(nil, /\A\z/) do
9
+ @command = IRuby::Command.new([])
10
+ kernel_dir = File.join(IRuby::Jupyter.kernelspec_dir, 'ruby')
11
+ assert_equal(kernel_dir, @command.kernel_dir)
12
+ assert_equal(File.join(kernel_dir, 'kernel.json'), @command.kernel_file)
13
+ end
14
+ end
15
+ end
16
+
17
+ def test_with_JUPYTER_DATA_DIR
18
+ Dir.mktmpdir do |tmpdir|
19
+ with_env('JUPYTER_DATA_DIR' => tmpdir,
20
+ 'IPYTHONDIR' => nil) do
21
+ assert_output(nil, /\A\z/) do
22
+ @command = IRuby::Command.new([])
23
+ kernel_dir = File.join(tmpdir, 'kernels', 'ruby')
24
+ assert_equal(kernel_dir, @command.kernel_dir)
25
+ assert_equal(File.join(kernel_dir, 'kernel.json'), @command.kernel_file)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ def test_with_IPYTHONDIR
32
+ Dir.mktmpdir do |tmpdir|
33
+ with_env('JUPYTER_DATA_DIR' => nil,
34
+ 'IPYTHONDIR' => tmpdir) do
35
+ assert_output(nil, /IPYTHONDIR is deprecated\. Use JUPYTER_DATA_DIR instead\./) do
36
+ @command = IRuby::Command.new([])
37
+ kernel_dir = File.join(tmpdir, 'kernels', 'ruby')
38
+ assert_equal(kernel_dir, @command.kernel_dir)
39
+ assert_equal(File.join(kernel_dir, 'kernel.json'), @command.kernel_file)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def test_with_JUPYTER_DATA_DIR_and_IPYTHONDIR
46
+ Dir.mktmpdir do |tmpdir|
47
+ Dir.mktmpdir do |tmpdir2|
48
+ with_env('JUPYTER_DATA_DIR' => tmpdir,
49
+ 'IPYTHONDIR' => tmpdir2) do
50
+ assert_output(nil, /both JUPYTER_DATA_DIR and IPYTHONDIR are supplied; IPYTHONDIR is ignored\./) do
51
+ @command = IRuby::Command.new([])
52
+ kernel_dir = File.join(tmpdir, 'kernels', 'ruby')
53
+ assert_equal(kernel_dir, @command.kernel_dir)
54
+ assert_equal(File.join(kernel_dir, 'kernel.json'), @command.kernel_file)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ def test_with_ipython_dir_option
62
+ Dir.mktmpdir do |tmpdir|
63
+ with_env('JUPYTER_DATA_DIR' => nil,
64
+ 'IPYTHONDIR' => nil) do
65
+ assert_output(nil, /--ipython-dir is deprecated\. Use JUPYTER_DATA_DIR environment variable instead\./) do
66
+ @command = IRuby::Command.new(%W[--ipython-dir=#{tmpdir}])
67
+ kernel_dir = File.join(tmpdir, 'kernels', 'ruby')
68
+ assert_equal(kernel_dir, @command.kernel_dir)
69
+ assert_equal(File.join(kernel_dir, 'kernel.json'), @command.kernel_file)
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def test_with_JUPYTER_DATA_DIR_and_ipython_dir_option
76
+ Dir.mktmpdir do |tmpdir|
77
+ Dir.mktmpdir do |tmpdir2|
78
+ with_env('JUPYTER_DATA_DIR' => tmpdir,
79
+ 'IPYTHONDIR' => nil) do
80
+ assert_output(nil, /both JUPYTER_DATA_DIR and --ipython-dir are supplied; --ipython-dir is ignored\./) do
81
+ @command = IRuby::Command.new(%W[--ipython-dir=#{tmpdir2}])
82
+ kernel_dir = File.join(tmpdir, 'kernels', 'ruby')
83
+ assert_equal(kernel_dir, @command.kernel_dir)
84
+ assert_equal(File.join(kernel_dir, 'kernel.json'), @command.kernel_file)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ def test_register_when_there_is_kernel_in_ipython_dir
92
+ Dir.mktmpdir do |tmpdir|
93
+ Dir.mktmpdir do |tmpdir2|
94
+ with_env('JUPYTER_DATA_DIR' => nil,
95
+ 'IPYTHONDIR' => nil,
96
+ 'HOME' => tmpdir2) do
97
+ ignore_warning do
98
+ @command = IRuby::Command.new(["register", "--ipython-dir=~/.ipython"])
99
+ assert_equal("#{tmpdir2}/.ipython/kernels/ruby/kernel.json", @command.kernel_file)
100
+ @command.run
101
+ assert(File.file?("#{tmpdir2}/.ipython/kernels/ruby/kernel.json"))
102
+ end
103
+ end
104
+
105
+ with_env('JUPYTER_DATA_DIR' => nil,
106
+ 'IPYTHONDIR' => nil,
107
+ 'HOME' => tmpdir2) do
108
+ @command = IRuby::Command.new(["register"])
109
+ assert_output(nil, /IRuby kernel file already exists in the deprecated IPython's data directory\.\nUsing --force, you can replace the old kernel file with the new one in Jupyter's data directory\./) do
110
+ @command.run
111
+ end
112
+ assert(File.file?(File.join(@command.ipython_kernel_dir, 'kernel.json')))
113
+ refute(File.file?(@command.kernel_file))
114
+
115
+ @command = IRuby::Command.new(["register", "--force"])
116
+ assert_output(nil, nil) do
117
+ @command.run
118
+ end
119
+ refute(File.exist?(@command.ipython_kernel_dir))
120
+ assert(File.file?(@command.kernel_file))
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ def test_register_and_unregister_with_JUPYTER_DATA_DIR
127
+ Dir.mktmpdir do |tmpdir|
128
+ Dir.mktmpdir do |tmpdir2|
129
+ with_env('JUPYTER_DATA_DIR' => tmpdir,
130
+ 'IPYTHONDIR' => nil,
131
+ 'HOME' => tmpdir2) do
132
+ assert_output(nil, nil) do
133
+ @command = IRuby::Command.new(['register'])
134
+ kernel_dir = File.join(tmpdir, 'kernels', 'ruby')
135
+ kernel_file = File.join(kernel_dir, 'kernel.json')
136
+ assert(!File.file?(kernel_file))
137
+
138
+ @command.run
139
+ assert(File.file?(kernel_file))
140
+
141
+ @command = IRuby::Command.new(['unregister'])
142
+ @command.run
143
+ assert(!File.file?(kernel_file))
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ def test_register_and_unregister_with_JUPYTER_DATA_DIR_when_there_is_kernel_in_ipython_dir
151
+ Dir.mktmpdir do |tmpdir|
152
+ Dir.mktmpdir do |tmpdir2|
153
+ with_env('HOME' => tmpdir2) do
154
+ ignore_warning do
155
+ @command = IRuby::Command.new(["register", "--ipython-dir=~/.ipython"])
156
+ assert_equal("#{tmpdir2}/.ipython/kernels/ruby/kernel.json", @command.kernel_file)
157
+ @command.run
158
+ assert(File.file?("#{tmpdir2}/.ipython/kernels/ruby/kernel.json"))
159
+ end
160
+ end
161
+
162
+ with_env('JUPYTER_DATA_DIR' => tmpdir,
163
+ 'IPYTHONDIR' => nil,
164
+ 'HOME' => tmpdir2) do
165
+ @command = IRuby::Command.new(["register"])
166
+ assert_output(nil, /IRuby kernel file already exists in the deprecated IPython's data directory\.\nUsing --force, you can replace the old kernel file with the new one in Jupyter's data directory\./) do
167
+ @command.run
168
+ end
169
+ assert(File.file?(File.join(@command.ipython_kernel_dir, 'kernel.json')))
170
+ refute(File.file?(@command.kernel_file))
171
+
172
+ @command = IRuby::Command.new(["register", "--force"])
173
+ assert_output(nil, nil) do
174
+ @command.run
175
+ end
176
+ refute(File.file?(File.join(@command.ipython_kernel_dir, 'kernel.json')))
177
+ assert(File.file?(@command.kernel_file))
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ def test_register_and_unregister_with_IPYTHONDIR
184
+ Dir.mktmpdir do |tmpdir|
185
+ Dir.mktmpdir do |tmpdir2|
186
+ with_env('JUPYTER_DATA_DIR' => nil,
187
+ 'IPYTHONDIR' => tmpdir,
188
+ 'HOME' => tmpdir2) do
189
+ ignore_warning do
190
+ @command = IRuby::Command.new(['register'])
191
+ kernel_dir = File.join(tmpdir, 'kernels', 'ruby')
192
+ kernel_file = File.join(kernel_dir, 'kernel.json')
193
+ assert(!File.file?(kernel_file))
194
+
195
+ @command.run
196
+ assert(File.file?(kernel_file))
197
+
198
+ @command = IRuby::Command.new(['unregister'])
199
+ @command.run
200
+ assert(!File.file?(kernel_file))
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end