iruby 0.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ubuntu.yml +62 -0
  3. data/CHANGES.md +203 -0
  4. data/Gemfile +3 -1
  5. data/LICENSE +1 -1
  6. data/README.md +137 -87
  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 +14 -8
  15. data/lib/iruby/backend.rb +38 -10
  16. data/lib/iruby/command.rb +67 -15
  17. data/lib/iruby/display.rb +77 -41
  18. data/lib/iruby/event_manager.rb +40 -0
  19. data/lib/iruby/formatter.rb +3 -3
  20. data/lib/iruby/input.rb +6 -6
  21. data/lib/iruby/input/README.md +299 -0
  22. data/lib/iruby/input/autoload.rb +1 -1
  23. data/lib/iruby/input/builder.rb +4 -4
  24. data/lib/iruby/input/button.rb +2 -2
  25. data/lib/iruby/input/cancel.rb +1 -1
  26. data/lib/iruby/input/checkbox.rb +3 -3
  27. data/lib/iruby/input/date.rb +3 -3
  28. data/lib/iruby/input/field.rb +2 -2
  29. data/lib/iruby/input/file.rb +3 -3
  30. data/lib/iruby/input/form.rb +6 -6
  31. data/lib/iruby/input/label.rb +4 -4
  32. data/lib/iruby/input/multiple.rb +10 -10
  33. data/lib/iruby/input/popup.rb +2 -2
  34. data/lib/iruby/input/radio.rb +6 -6
  35. data/lib/iruby/input/select.rb +8 -8
  36. data/lib/iruby/input/textarea.rb +1 -1
  37. data/lib/iruby/input/widget.rb +2 -2
  38. data/lib/iruby/jupyter.rb +77 -0
  39. data/lib/iruby/kernel.rb +204 -36
  40. data/lib/iruby/ostream.rb +29 -8
  41. data/lib/iruby/session.rb +117 -0
  42. data/lib/iruby/session/cztop.rb +4 -0
  43. data/lib/iruby/session_adapter.rb +72 -0
  44. data/lib/iruby/session_adapter/cztop_adapter.rb +45 -0
  45. data/lib/iruby/session_adapter/ffirzmq_adapter.rb +55 -0
  46. data/lib/iruby/session_adapter/pyzmq_adapter.rb +77 -0
  47. data/lib/iruby/session_adapter/test_adapter.rb +49 -0
  48. data/lib/iruby/utils.rb +13 -2
  49. data/lib/iruby/version.rb +1 -1
  50. data/run-test.sh +12 -0
  51. data/tasks/ci.rake +65 -0
  52. data/test/helper.rb +136 -0
  53. data/test/integration_test.rb +22 -11
  54. data/test/iruby/backend_test.rb +37 -0
  55. data/test/iruby/command_test.rb +207 -0
  56. data/test/iruby/event_manager_test.rb +92 -0
  57. data/test/iruby/jupyter_test.rb +27 -0
  58. data/test/iruby/kernel_test.rb +185 -0
  59. data/test/iruby/mime_test.rb +50 -0
  60. data/test/iruby/multi_logger_test.rb +1 -5
  61. data/test/iruby/session_adapter/cztop_adapter_test.rb +20 -0
  62. data/test/iruby/session_adapter/ffirzmq_adapter_test.rb +20 -0
  63. data/test/iruby/session_adapter/session_adapter_test_base.rb +27 -0
  64. data/test/iruby/session_adapter_test.rb +91 -0
  65. data/test/iruby/session_test.rb +48 -0
  66. data/test/run-test.rb +19 -0
  67. metadata +120 -50
  68. data/.travis.yml +0 -16
  69. data/CHANGES +0 -143
  70. data/CONTRIBUTORS +0 -19
  71. data/lib/iruby/session/rbczmq.rb +0 -68
  72. data/test/test_helper.rb +0 -5
data/Rakefile CHANGED
@@ -1,15 +1,41 @@
1
- require 'rake/testtask'
1
+ require "bundler/gem_helper"
2
2
 
3
- begin
4
- require 'bundler/gem_tasks'
5
- rescue Exception
6
- end
3
+ base_dir = File.join(File.dirname(__FILE__))
4
+
5
+ helper = Bundler::GemHelper.new(base_dir)
6
+ helper.install
7
7
 
8
- Rake::TestTask.new('test') do |t|
9
- t.libs << 'lib'
10
- t.libs << 'test'
11
- t.test_files = FileList['test/**/*_test.rb']
12
- t.verbose = true
8
+ FileList['tasks/**.rake'].each {|f| load f }
9
+
10
+ desc "Run tests"
11
+ task :test do
12
+ cd(base_dir) do
13
+ ruby("test/run-test.rb")
14
+ end
13
15
  end
14
16
 
15
17
  task default: 'test'
18
+
19
+ namespace :docker do
20
+ def root_dir
21
+ @root_dir ||= File.expand_path("..", __FILE__)
22
+ end
23
+
24
+ task :build do
25
+ container_name = "iruby_build"
26
+ image_name = "mrkn/iruby"
27
+ sh "docker", "run",
28
+ "--name", container_name,
29
+ "-v", "#{root_dir}:/tmp/iruby",
30
+ "rubylang/ruby", "/bin/bash", "/tmp/iruby/docker/setup.sh"
31
+ sh "docker", "commit", container_name, image_name
32
+ sh "docker", "rm", container_name
33
+ end
34
+
35
+ task :test do
36
+ root_dir = File.expand_path("..", __FILE__)
37
+ sh "docker", "run", "-it", "--rm",
38
+ "-v", "#{root_dir}:/tmp/iruby",
39
+ "mrkn/iruby", "/bin/bash", "/tmp/iruby/docker/test.sh"
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ FROM rubylang/ruby:<%= ruby_version %>-bionic
2
+
3
+ ADD ci/requirements.txt /tmp
4
+
5
+ RUN apt-get update \
6
+ && apt-get install -y --no-install-recommends \
7
+ libczmq-dev \
8
+ python3 \
9
+ python3-pip \
10
+ python3-setuptools \
11
+ libpython3.6 \
12
+ && pip3 install wheel \
13
+ && pip3 install -r /tmp/requirements.txt \
14
+ && rm -f /tmp/requirements.txt
15
+
16
+ # ZeroMQ version 4.1.6 and CZMQ version 3.0.2 for rbczmq
17
+ RUN apt-get update \
18
+ && apt-get install -y --no-install-recommends \
19
+ build-essential \
20
+ file \
21
+ wget \
22
+ && cd /tmp \
23
+ && wget https://github.com/zeromq/zeromq4-1/releases/download/v4.1.6/zeromq-4.1.6.tar.gz \
24
+ && wget https://archive.org/download/zeromq_czmq_3.0.2/czmq-3.0.2.tar.gz \
25
+ && tar xf zeromq-4.1.6.tar.gz \
26
+ && tar xf czmq-3.0.2.tar.gz \
27
+ && \
28
+ ( \
29
+ cd zeromq-4.1.6 \
30
+ && ./configure \
31
+ && make install \
32
+ ) \
33
+ && \
34
+ ( \
35
+ cd czmq-3.0.2 \
36
+ && wget -O 1.patch https://github.com/zeromq/czmq/commit/2594d406d8ec6f54e54d7570d7febba10a6906b2.diff \
37
+ && wget -O 2.patch https://github.com/zeromq/czmq/commit/b651cb479235751b22b8f9a822a2fc6bc1be01ab.diff \
38
+ && cat *.patch | patch -p1 \
39
+ && ./configure \
40
+ && make install \
41
+ )
@@ -0,0 +1,7 @@
1
+ FROM iruby-test-base:ruby-<%= ruby_version %>
2
+
3
+ RUN gem install cztop
4
+ RUN mkdir -p /iruby
5
+ ADD . /iruby
6
+ WORKDIR /iruby
7
+ RUN bundle install
@@ -0,0 +1 @@
1
+ jupyter-console>=6.0.0
data/docker/setup.sh ADDED
@@ -0,0 +1,15 @@
1
+ #!/bin/bash
2
+
3
+ set -ex
4
+
5
+ apt-get update
6
+ apt-get install -y --no-install-recommends \
7
+ libczmq-dev \
8
+ python3 \
9
+ python3-pip \
10
+ python3-setuptools \
11
+ python3-wheel
12
+
13
+ cd /tmp/iruby
14
+ bundle install --with test --without plot
15
+ pip3 install jupyter
data/docker/test.sh ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ set -ex
4
+
5
+ cd /tmp/iruby
6
+ bundle install --with test --without plot
7
+ bundle exec rake test
data/iruby.gemspec CHANGED
@@ -1,34 +1,30 @@
1
- # coding: utf-8
2
1
  require_relative 'lib/iruby/version'
3
- require 'date'
4
2
 
5
3
  Gem::Specification.new do |s|
6
4
  s.name = 'iruby'
7
- s.date = Date.today.to_s
8
5
  s.version = IRuby::VERSION
9
6
  s.authors = ['Daniel Mendler', 'The SciRuby developers']
10
7
  s.email = ['mail@daniel-mendler.de']
11
- s.summary = 'Ruby Kernel for Jupyter/IPython'
12
- s.description = 'A Ruby kernel for Jupyter/IPython frontends (e.g. notebook). Try it at try.jupyter.org.'
8
+ s.summary = 'Ruby Kernel for Jupyter'
9
+ s.description = 'A Ruby kernel for Jupyter environment. Try it at try.jupyter.org.'
13
10
  s.homepage = 'https://github.com/SciRuby/iruby'
14
11
  s.license = 'MIT'
15
12
 
16
- s.files = `git ls-files`.split($/)
13
+ s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
14
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
15
  s.test_files = s.files.grep(%r{^test/})
19
- s.require_paths = %w(lib)
16
+ s.require_paths = %w[lib]
20
17
 
21
- m = "Consider installing the optional dependencies to get additional functionality:\n"
22
- File.read('Gemfile').scan(/gem\s+'(.*?)'/) { m << " * #{$1}\n" }
23
- s.post_install_message = m << "\n"
18
+ s.required_ruby_version = '>= 2.3.0'
24
19
 
25
- s.required_ruby_version = '>= 2.1.0'
20
+ s.add_dependency 'data_uri', '~> 0.1'
21
+ s.add_dependency 'ffi-rzmq'
22
+ s.add_dependency 'irb'
23
+ s.add_dependency 'mime-types', '>= 3.3.1'
24
+ s.add_dependency 'multi_json', '~> 1.11'
26
25
 
27
- s.add_development_dependency 'rake', '~> 10.4'
28
- s.add_development_dependency 'minitest', '~> 5.6'
29
-
30
- s.add_runtime_dependency 'bond', '~> 0.5'
31
- s.add_runtime_dependency 'multi_json', '~> 1.11'
32
- s.add_runtime_dependency 'mimemagic', '~> 0.3'
33
- s.add_runtime_dependency 'data_uri', '~> 0.1'
26
+ s.add_development_dependency 'pycall', '>= 1.2.1'
27
+ s.add_development_dependency 'rake'
28
+ s.add_development_dependency 'test-unit'
29
+ s.add_development_dependency 'test-unit-rr'
34
30
  end
data/lib/iruby.rb CHANGED
@@ -1,10 +1,16 @@
1
- require 'mimemagic'
1
+ require 'fileutils'
2
+ require 'mime/types'
2
3
  require 'multi_json'
3
4
  require 'securerandom'
4
5
  require 'openssl'
5
6
  require 'tempfile'
6
7
  require 'set'
8
+ require 'stringio'
9
+
7
10
  require 'iruby/version'
11
+ require 'iruby/jupyter'
12
+ require 'iruby/event_manager'
13
+ require 'iruby/logger'
8
14
  require 'iruby/kernel'
9
15
  require 'iruby/backend'
10
16
  require 'iruby/ostream'
@@ -13,18 +19,18 @@ require 'iruby/formatter'
13
19
  require 'iruby/utils'
14
20
  require 'iruby/display'
15
21
  require 'iruby/comm'
16
- require 'iruby/session/mixin'
17
22
 
18
- begin
19
- require 'iruby/session/cztop'
20
- rescue LoadError
23
+ if ENV.fetch('IRUBY_OLD_SESSION', false)
24
+ require 'iruby/session/mixin'
21
25
  begin
22
- require 'iruby/session/rbczmq'
26
+ require 'iruby/session/ffi_rzmq'
23
27
  rescue LoadError
24
28
  begin
25
- require 'iruby/session/ffi_rzmq'
29
+ require 'iruby/session/cztop'
26
30
  rescue LoadError
27
- STDERR.puts "You should install cztop, rbczmq or ffi_rzmq before running iruby notebook. See README."
31
+ STDERR.puts "Please install ffi-rzmq or cztop before running iruby. See README."
28
32
  end
29
33
  end
34
+ else
35
+ require 'iruby/session'
30
36
  end
data/lib/iruby/backend.rb CHANGED
@@ -1,10 +1,9 @@
1
1
  module IRuby
2
2
  In, Out = [nil], [nil]
3
- ::In, ::Out = In, Out
4
3
 
5
4
  module History
6
5
  def eval(code, store_history)
7
- b = TOPLEVEL_BINDING
6
+ b = eval_binding
8
7
 
9
8
  b.local_variable_set(:_ih, In) unless b.local_variable_defined?(:_ih)
10
9
  b.local_variable_set(:_oh, Out) unless b.local_variable_defined?(:_oh)
@@ -33,45 +32,74 @@ module IRuby
33
32
  end
34
33
 
35
34
  class PlainBackend
35
+ attr_reader :eval_path
36
36
  prepend History
37
37
 
38
38
  def initialize
39
- require 'bond'
40
- Bond.start(debug: true)
39
+ require 'irb'
40
+ require 'irb/completion'
41
+ IRB.setup(nil)
42
+ @main = TOPLEVEL_BINDING.eval("self").dup
43
+ @workspace = IRB::WorkSpace.new(@main)
44
+ @irb = IRB::Irb.new(@workspace)
45
+ @eval_path = @irb.context.irb_path
46
+ IRB.conf[:MAIN_CONTEXT] = @irb.context
47
+ end
48
+
49
+ def eval_binding
50
+ @workspace.binding
41
51
  end
42
52
 
43
53
  def eval(code, store_history)
44
- TOPLEVEL_BINDING.eval(code)
54
+ @irb.context.evaluate(code, 0)
55
+ @irb.context.last_value
45
56
  end
46
57
 
47
58
  def complete(code)
48
- Bond.agent.call(code, code)
59
+ IRB::InputCompletor::CompletionProc.call(code)
49
60
  end
50
61
  end
51
62
 
52
63
  class PryBackend
64
+ attr_reader :eval_path
53
65
  prepend History
54
66
 
55
67
  def initialize
56
68
  require 'pry'
57
- Pry.memory_size = 3
69
+ Pry.memory_size = 3
58
70
  Pry.pager = false # Don't use the pager
59
71
  Pry.print = proc {|output, value|} # No result printing
60
72
  Pry.exception_handler = proc {|output, exception, _| }
73
+ @eval_path = Pry.eval_path
61
74
  reset
62
75
  end
63
76
 
77
+ def eval_binding
78
+ TOPLEVEL_BINDING
79
+ end
80
+
64
81
  def eval(code, store_history)
82
+ Pry.current_line = 1
65
83
  @pry.last_result = nil
66
84
  unless @pry.eval(code)
67
85
  reset
68
86
  raise SystemExit
69
87
  end
70
- unless @pry.eval_string.empty?
88
+
89
+ # Pry::Code.complete_expression? return false
90
+ if !@pry.eval_string.empty?
71
91
  syntax_error = @pry.eval_string
72
92
  @pry.reset_eval_string
73
- @pry.evaluate_ruby syntax_error
93
+ @pry.evaluate_ruby(syntax_error)
94
+
95
+ # Pry::Code.complete_expression? raise SyntaxError
96
+ # evaluate again for current line number
97
+ elsif @pry.last_result_is_exception? &&
98
+ @pry.last_exception.is_a?(SyntaxError) &&
99
+ @pry.last_exception.is_a?(Pry::UserError)
100
+ @pry.evaluate_ruby(code)
74
101
  end
102
+
75
103
  raise @pry.last_exception if @pry.last_result_is_exception?
76
104
  @pry.push_initial_binding unless @pry.current_binding # ensure that we have a binding
77
105
  @pry.last_result
@@ -82,7 +110,7 @@ module IRuby
82
110
  end
83
111
 
84
112
  def reset
85
- @pry = Pry.new(output: $stdout, target: TOPLEVEL_BINDING)
113
+ @pry = Pry.new(output: $stdout, target: eval_binding)
86
114
  end
87
115
  end
88
116
  end
data/lib/iruby/command.rb CHANGED
@@ -1,20 +1,22 @@
1
- require 'fileutils'
2
- require 'multi_json'
1
+ require 'iruby'
3
2
 
4
3
  module IRuby
5
4
  class Command
6
5
  def initialize(args)
7
6
  @args = args
8
7
 
9
- ipython_dir = ENV['IPYTHONDIR'] || '~/.ipython'
10
- @args.each do |arg|
11
- ipython_dir = $1 if arg =~ /\A--ipython-dir=(.*)\Z/
12
- end
13
- @kernel_dir = File.join(File.expand_path(ipython_dir), 'kernels', 'ruby')
14
- @kernel_file = File.join(@kernel_dir, 'kernel.json')
8
+ @ipython_dir = File.expand_path("~/.ipython")
9
+ @kernel_dir = resolve_kernelspec_dir.freeze
10
+ @kernel_file = File.join(@kernel_dir, 'kernel.json').freeze
15
11
  @iruby_path = File.expand_path $0
16
12
  end
17
13
 
14
+ attr_reader :ipython_dir, :kernel_dir, :kernel_file
15
+
16
+ def ipython_kernel_dir
17
+ File.join(File.expand_path(@ipython_dir), 'kernels', 'ruby')
18
+ end
19
+
18
20
  def run
19
21
  case @args.first
20
22
  when 'version', '-v', '--version'
@@ -23,11 +25,12 @@ module IRuby
23
25
  when 'help', '-h', '--help'
24
26
  print_help
25
27
  when 'register'
26
- if registered_iruby_path && !@args.include?('--force')
28
+ force_p = @args.include?('--force')
29
+ if registered_iruby_path && !force_p
27
30
  STDERR.puts "#{@kernel_file} already exists!\nUse --force to force a register."
28
31
  exit 1
29
32
  end
30
- register_kernel
33
+ register_kernel(force_p)
31
34
  when 'unregister'
32
35
  unregister_kernel
33
36
  when 'kernel'
@@ -39,6 +42,38 @@ module IRuby
39
42
 
40
43
  private
41
44
 
45
+ def resolve_kernelspec_dir
46
+ if ENV.has_key?('JUPYTER_DATA_DIR')
47
+ if ENV.has_key?('IPYTHONDIR')
48
+ warn 'both JUPYTER_DATA_DIR and IPYTHONDIR are supplied; IPYTHONDIR is ignored.'
49
+ end
50
+ if @args.find {|x| /\A--ipython-dir=/ =~ x }
51
+ warn 'both JUPYTER_DATA_DIR and --ipython-dir are supplied; --ipython-dir is ignored.'
52
+ end
53
+ jupyter_data_dir = ENV['JUPYTER_DATA_DIR']
54
+ return File.join(jupyter_data_dir, 'kernels', 'ruby')
55
+ end
56
+
57
+ if ENV.has_key?('IPYTHONDIR')
58
+ warn 'IPYTHONDIR is deprecated. Use JUPYTER_DATA_DIR instead.'
59
+ ipython_dir = ENV['IPYTHONDIR']
60
+ end
61
+
62
+ @args.each do |arg|
63
+ next unless /\A--ipython-dir=(.*)\Z/ =~ arg
64
+ ipython_dir = Regexp.last_match[1]
65
+ warn '--ipython-dir is deprecated. Use JUPYTER_DATA_DIR environment variable instead.'
66
+ break
67
+ end
68
+
69
+ if ipython_dir
70
+ @ipython_dir = ipython_dir
71
+ ipython_kernel_dir
72
+ else
73
+ File.join(Jupyter.kernelspec_dir, 'ruby')
74
+ end
75
+ end
76
+
42
77
  def print_help
43
78
  puts %{
44
79
  Usage:
@@ -54,8 +89,9 @@ Try `ipython help` for more information.
54
89
  end
55
90
 
56
91
  def run_kernel
57
- require 'iruby/logger'
58
92
  IRuby.logger = MultiLogger.new(*Logger.new(STDOUT))
93
+ STDOUT.sync = true # FIXME: This can make the integration test.
94
+
59
95
  @args.reject! {|arg| arg =~ /\A--log=(.*)\Z/ && IRuby.logger.loggers << Logger.new($1) }
60
96
  IRuby.logger.level = @args.delete('--debug') ? Logger::DEBUG : Logger::INFO
61
97
 
@@ -64,7 +100,7 @@ Try `ipython help` for more information.
64
100
  Dir.chdir(working_dir) if working_dir
65
101
 
66
102
  require boot_file if boot_file
67
- check_bundler {|e| IRuby.logger.warn "Could not load bundler: #{e.message}\n#{e.backtrace.join("\n")}" }
103
+ check_bundler {|e| IRuby.logger.warn "Could not load bundler: #{e.message}" }
68
104
 
69
105
  require 'iruby'
70
106
  Kernel.new(config_file).run
@@ -91,11 +127,11 @@ Try `ipython help` for more information.
91
127
  check_registered_kernel
92
128
  check_bundler {|e| STDERR.puts "Could not load bundler: #{e.message}" }
93
129
 
94
- Kernel.exec('ipython', *@args)
130
+ Process.exec('ipython', *@args)
95
131
  end
96
132
 
97
133
  def check_registered_kernel
98
- if kernel = registered_iruby_path
134
+ if (kernel = registered_iruby_path)
99
135
  STDERR.puts "#{@iruby_path} differs from registered path #{registered_iruby_path}.
100
136
  This might not work. Run 'iruby register --force' to fix it." if @iruby_path != kernel
101
137
  else
@@ -113,7 +149,12 @@ Add `gem 'iruby'` to your Gemfile to fix it.} unless Bundler.definition.specs.an
113
149
  yield(e)
114
150
  end
115
151
 
116
- def register_kernel
152
+ def register_kernel(force_p=false)
153
+ if force_p
154
+ unregister_kernel_in_ipython_dir
155
+ else
156
+ return unless check_existing_kernel_in_ipython_dir
157
+ end
117
158
  FileUtils.mkpath(@kernel_dir)
118
159
  unless RUBY_PLATFORM =~ /mswin(?!ce)|mingw|cygwin/
119
160
  File.write(@kernel_file, MultiJson.dump(argv: [ @iruby_path, 'kernel', '{connection_file}' ],
@@ -127,6 +168,13 @@ Add `gem 'iruby'` to your Gemfile to fix it.} unless Bundler.definition.specs.an
127
168
  FileUtils.copy(Dir[File.join(__dir__, 'assets', '*')], @kernel_dir) rescue nil
128
169
  end
129
170
 
171
+ def check_existing_kernel_in_ipython_dir
172
+ return true unless File.file?(File.join(ipython_kernel_dir, 'kernel.json'))
173
+ warn "IRuby kernel file already exists in the deprecated IPython's data directory."
174
+ warn "Using --force, you can replace the old kernel file with the new one in Jupyter's data directory."
175
+ false
176
+ end
177
+
130
178
  def registered_iruby_path
131
179
  File.exist?(@kernel_file) && MultiJson.load(File.read(@kernel_file))['argv'].first
132
180
  end
@@ -134,5 +182,9 @@ Add `gem 'iruby'` to your Gemfile to fix it.} unless Bundler.definition.specs.an
134
182
  def unregister_kernel
135
183
  FileUtils.rm_rf(@kernel_dir)
136
184
  end
185
+
186
+ def unregister_kernel_in_ipython_dir
187
+ FileUtils.rm_rf(ipython_kernel_dir)
188
+ end
137
189
  end
138
190
  end