iruby 0.8.2 → 0.8.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f389a45d763afc30ae093cb65b479b727bde7f1a4ecf04246329f56099ec280
4
- data.tar.gz: 16dc91aaba973d5b0e8cc4c2d9444266c9c8f479704a5394103a732f2f70ba01
3
+ metadata.gz: 43bde0525564da42d868ec8eded9727ba0398ad0ae30e7fbcaea3eb795068a24
4
+ data.tar.gz: 32bc31a371dcccc92c04ee85b557754d3336f88a9aec72efd6dbe9cfe096552a
5
5
  SHA512:
6
- metadata.gz: a2fcf2691c1f795abad87a5bfe174f17dd49980c11a6ffc1c3fc665e4345cf14caf3be273ff6123224864d261b9b6b730c9bd16907dcd535d2a168ce07f6763a
7
- data.tar.gz: 523123ce6314e0f5e5e49dc0156dd00e6d3aac45b49af8780009df1977c7edd3306388ed2b3375a315ed5267da0c2311a6da21bbd0fffae58ef8fe19a8079ce6
6
+ metadata.gz: a8c7e7275de0fb026784a66286a8e3474fe54a8d60b551e2103434d3d5a1ada05ad1d5dfd2e285f19aaa4ef4bff07d181b956cef23b2da788158e9c400cae871
7
+ data.tar.gz: 6f66cf0aab532b765d3760287fb0e1aab4578054484d7fa7b4cd5d724222154c943c2768597f36054bdddb6da6b9c8aa23040b1f278618d9917ead1f61ce3969
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
@@ -16,6 +16,7 @@ jobs:
16
16
  os:
17
17
  - ubuntu-latest
18
18
  ruby:
19
+ - "4.0"
19
20
  - "3.4"
20
21
  - "3.3"
21
22
  - "3.2"
@@ -27,7 +28,7 @@ jobs:
27
28
  - debug
28
29
 
29
30
  steps:
30
- - uses: actions/checkout@v4
31
+ - uses: actions/checkout@v7
31
32
  with:
32
33
  fetch-depth: 1
33
34
 
@@ -35,6 +36,15 @@ jobs:
35
36
  with:
36
37
  ruby-version: ${{ matrix.ruby }}
37
38
 
39
+ - uses: mamba-org/setup-micromamba@v3
40
+ with:
41
+ environment-name: iruby-ci
42
+ create-args: >-
43
+ python=3.12
44
+ jupyter_console
45
+ cache-environment: true
46
+ cache-downloads: true
47
+
38
48
  - run: rake build
39
49
 
40
50
  - name: Install ffi (if Ruby 2.7)
@@ -81,23 +91,17 @@ jobs:
81
91
  run: |
82
92
  sudo apt update
83
93
  sudo apt install -y --no-install-recommends \
84
- libczmq-dev \
85
- python3 \
86
- python3-pip \
87
- python3-setuptools
88
- sudo pip3 install wheel
89
- sudo pip3 install -r ci/requirements.txt
94
+ libczmq-dev
90
95
 
91
96
  - run: bundle install --jobs 4 --retry 3
92
97
 
93
98
  - name: Run tests
94
99
  env:
95
- PYTHON: python3
96
100
  ADAPTERS: cztop ffi-rzmq
97
101
  run: |
98
102
  for adapter in $ADAPTERS; do
99
103
  export IRUBY_TEST_SESSION_ADAPTER_NAME=$adapter
100
- bundle exec rake test TESTOPTS="-v"
104
+ micromamba run -n iruby-ci bundle exec rake test TESTOPTS="-v"
101
105
  done
102
106
 
103
107
  windows:
@@ -105,7 +109,7 @@ jobs:
105
109
  runs-on: windows-latest
106
110
 
107
111
  steps:
108
- - uses: actions/checkout@v4
112
+ - uses: actions/checkout@v7
109
113
  with:
110
114
  fetch-depth: 1
111
115
 
@@ -126,7 +130,7 @@ jobs:
126
130
  runs-on: macos-latest
127
131
 
128
132
  steps:
129
- - uses: actions/checkout@v4
133
+ - uses: actions/checkout@v7
130
134
  with:
131
135
  fetch-depth: 1
132
136
 
data/CHANGES.md CHANGED
@@ -1,3 +1,14 @@
1
+ # 0.8.3 (2026-06-30)
2
+
3
+ * Remove MultiJson dependency and replace with JSON by @kojix2 in https://github.com/SciRuby/iruby/pull/373
4
+ * Fix undefined var in FfirzmqAdapter by @kojix2 in https://github.com/SciRuby/iruby/pull/377
5
+ * Avoid conflict with Ruby's numbered parameters by @zalt50 in https://github.com/SciRuby/iruby/pull/380
6
+ * Remove legacy IRUBY_OLD_SESSION session implementations by @kojix2 in https://github.com/SciRuby/iruby/pull/384
7
+ * Add explicit session cleanup by @kojix2 in https://github.com/SciRuby/iruby/pull/388
8
+ * Use raw zmq_proxy for ffi-rzmq heartbeat by @kojix2 in https://github.com/SciRuby/iruby/pull/391
9
+ * Remove obsolete PyCall dependency by @kojix2 in https://github.com/SciRuby/iruby/pull/393
10
+ * Add Ruby 4.0 to CI workflow matrix by @kojix2 in https://github.com/SciRuby/iruby/pull/394
11
+
1
12
  # 0.8.2 (2025-04-10)
2
13
 
3
14
  * Update CI to refresh apt packages before installing IRuby gem by @kojix2 in https://github.com/SciRuby/iruby/pull/367
@@ -141,7 +152,7 @@
141
152
 
142
153
  * Improvements to IRuby dependency detection using `Bundler::Dependencies#specs` (@kou).
143
154
  * Use less memory forcing pry to store only the last 3 commands in memory (@kylekyle).
144
- * Use bigger z-index that is used accross all browsers (@kylekyle).
155
+ * Use bigger z-index that is used across all browsers (@kylekyle).
145
156
  * Ability to input date values as DateTime objects in IRuby/Input (@kylekyle).
146
157
  * Add option to have check boxes checked by default (@kylekyle).
147
158
  * Option for multi-select in drop down menus in the prompter (@kylekyle).
data/README.md CHANGED
@@ -6,15 +6,6 @@
6
6
 
7
7
  IRuby is a Ruby kernel for [Jupyter project](https://jupyter.org/).
8
8
 
9
- ## Try IRuby
10
-
11
- You can try IRuby with a sample notebook on Binder (the same link as the banner placed above):
12
-
13
- <https://mybinder.org/v2/gh/RubyData/binder/master?filepath=ruby-data.ipynb>
14
-
15
- The following URL launches JupyterLab directly on Binder.
16
-
17
- <https://mybinder.org/v2/gh/RubyData/binder/master?filepath=../lab>
18
9
 
19
10
  ## Installation
20
11
 
@@ -35,8 +26,8 @@ The following dependencies are optional.
35
26
 
36
27
  See the official document to know how to install Jupyter Notebook and/or JupyterLab.
37
28
 
38
- - <https://jupyter.readthedocs.io/en/latest/install/notebook-classic.html>
39
- - <https://jupyter.readthedocs.io/en/latest/install.html>
29
+ - <https://docs.jupyter.org/en/latest/install/notebook-classic.html>
30
+ - <https://docs.jupyter.org/en/latest/install.html>
40
31
 
41
32
  ### Ubuntu
42
33
 
@@ -167,6 +158,12 @@ Take a look at the [example notebook](https://nbviewer.jupyter.org/urls/raw.gith
167
158
  and the [collection of notebooks](https://github.com/SciRuby/sciruby-notebooks/) which includes a Dockerfile to create a containerized installation of iruby
168
159
  and other scientific gems.
169
160
 
161
+ ## Try IRuby
162
+
163
+ - [Try a sample notebook on Binder](https://mybinder.org/v2/gh/RubyData/binder/master?filepath=ruby-data.ipynb)
164
+
165
+ - [Launch JupyterLab on Binder](https://mybinder.org/v2/gh/RubyData/binder/master?filepath=../lab)
166
+
170
167
  ## Contributing
171
168
 
172
169
  Contributions to IRuby are very welcome.
data/Rakefile CHANGED
@@ -16,27 +16,3 @@ task :test do
16
16
  end
17
17
 
18
18
  task default: 'test'
19
-
20
- namespace :docker do
21
- def root_dir
22
- @root_dir ||= File.expand_path("..", __FILE__)
23
- end
24
-
25
- task :build do
26
- container_name = "iruby_build"
27
- image_name = "mrkn/iruby"
28
- sh "docker", "run",
29
- "--name", container_name,
30
- "-v", "#{root_dir}:/tmp/iruby",
31
- "rubylang/ruby", "/bin/bash", "/tmp/iruby/docker/setup.sh"
32
- sh "docker", "commit", container_name, image_name
33
- sh "docker", "rm", container_name
34
- end
35
-
36
- task :test do
37
- root_dir = File.expand_path("..", __FILE__)
38
- sh "docker", "run", "-it", "--rm",
39
- "-v", "#{root_dir}:/tmp/iruby",
40
- "mrkn/iruby", "/bin/bash", "/tmp/iruby/docker/test.sh"
41
- end
42
- end
data/iruby.gemspec CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
6
6
  s.authors = ['Daniel Mendler', 'The SciRuby developers']
7
7
  s.email = ['mail@daniel-mendler.de']
8
8
  s.summary = 'Ruby Kernel for Jupyter'
9
- s.description = 'A Ruby kernel for Jupyter environment. Try it at try.jupyter.org.'
9
+ s.description = 'A Ruby kernel for Jupyter environment.'
10
10
  s.homepage = 'https://github.com/SciRuby/iruby'
11
11
  s.license = 'MIT'
12
12
 
@@ -23,9 +23,7 @@ Gem::Specification.new do |s|
23
23
  s.add_dependency 'irb'
24
24
  s.add_dependency 'logger'
25
25
  s.add_dependency 'mime-types', '>= 3.3.1'
26
- s.add_dependency 'multi_json', '~> 1.11'
27
26
 
28
- s.add_development_dependency 'pycall', '>= 1.2.1'
29
27
  s.add_development_dependency 'rake'
30
28
  s.add_development_dependency 'test-unit'
31
29
  s.add_development_dependency 'test-unit-rr'
data/lib/iruby/backend.rb CHANGED
@@ -18,7 +18,7 @@ module IRuby
18
18
  # TODO Add IRuby.cache_size which controls the size of the Out array
19
19
  # and sets the oldest entries and _<n> variables to nil.
20
20
  if store_history
21
- b.local_variable_set("_#{Out.size}", out)
21
+ b.local_variable_set("_o#{Out.size}", out)
22
22
  b.local_variable_set("_i#{In.size}", code)
23
23
 
24
24
  Out << out
@@ -113,7 +113,7 @@
113
113
  "\n",
114
114
  "The enter key will submit an input or form and the escape key will cancel it. Canceled inputs are returned as `nil`. Inputs are automatically canceled if destroyed. An input can be destroyed by clearing its cell's output. The `cancel` button will cancel a form and all other buttons will submit it. \n",
115
115
  "\n",
116
- "After a form destroyed, the cell's output is cleared. Be careful not to prompt for input in a block that has previous output you would like to keep. Output is cleared to prevent forms from interferring with one another and to ensure that inputs are not inadvertently saved to the notebook. "
116
+ "After a form destroyed, the cell's output is cleared. Be careful not to prompt for input in a block that has previous output you would like to keep. Output is cleared to prevent forms from interfering with one another and to ensure that inputs are not inadvertently saved to the notebook. "
117
117
  ]
118
118
  },
119
119
  {
@@ -184,7 +184,7 @@
184
184
  "source": [
185
185
  "## Defaults\n",
186
186
  "\n",
187
- "Most inputs will accept a `default` parameter. If no default is given, the deault is `nil`. Since checkboxes can have multiple values selected, you can pass an array of values. To check everything, pass `true` as the default. "
187
+ "Most inputs will accept a `default` parameter. If no default is given, the default is `nil`. Since checkboxes can have multiple values selected, you can pass an array of values. To check everything, pass `true` as the default. "
188
188
  ]
189
189
  },
190
190
  {
@@ -67,7 +67,7 @@ end
67
67
 
68
68
  The enter key will submit an input or form and the escape key will cancel it. Canceled inputs are returned as `nil`. Inputs are automatically canceled if destroyed. An input can be destroyed by clearing its cell's output. The `cancel` button will cancel a form and all other buttons will submit it.
69
69
 
70
- After a form destroyed, the cell's output is cleared. Be careful not to prompt for input in a block that has previous output you would like to keep. Output is cleared to prevent forms from interferring with one another and to ensure that inputs are not inadvertently saved to the notebook.
70
+ After a form destroyed, the cell's output is cleared. Be careful not to prompt for input in a block that has previous output you would like to keep. Output is cleared to prevent forms from interfering with one another and to ensure that inputs are not inadvertently saved to the notebook.
71
71
 
72
72
 
73
73
  ```ruby
@@ -105,7 +105,7 @@ end
105
105
 
106
106
  ## Defaults
107
107
 
108
- Most inputs will accept a `default` parameter. If no default is given, the deault is `nil`. Since checkboxes can have multiple values selected, you can pass an array of values. To check everything, pass `true` as the default.
108
+ Most inputs will accept a `default` parameter. If no default is given, the default is `nil`. Since checkboxes can have multiple values selected, you can pass an array of values. To check everything, pass `true` as the default.
109
109
 
110
110
 
111
111
  ```ruby
@@ -63,7 +63,7 @@ module IRuby
63
63
  end
64
64
 
65
65
  def submit
66
- result = MultiJson.load(Kernel.instance.session.recv_input)
66
+ result = JSON.parse(Kernel.instance.session.recv_input)
67
67
 
68
68
  unless result.has_key? @id
69
69
  submit
@@ -74,4 +74,4 @@ module IRuby
74
74
  end
75
75
  end
76
76
  end
77
- end
77
+ end
data/lib/iruby/kernel.rb CHANGED
@@ -38,7 +38,7 @@ module IRuby
38
38
  ].freeze
39
39
 
40
40
  def initialize(config_file, session_adapter_name=nil)
41
- @config = MultiJson.load(File.read(config_file))
41
+ @config = JSON.parse(File.read(config_file))
42
42
  IRuby.logger.debug("IRuby kernel start with config #{@config}")
43
43
  Kernel.instance = self
44
44
 
@@ -126,6 +126,13 @@ module IRuby
126
126
  while @running
127
127
  dispatch
128
128
  end
129
+ ensure
130
+ close
131
+ end
132
+
133
+ def close
134
+ @session&.close
135
+ stop_parent_process_poller
129
136
  end
130
137
 
131
138
  # @private
@@ -314,6 +321,13 @@ module IRuby
314
321
  end
315
322
  end
316
323
 
324
+ def stop_parent_process_poller
325
+ return unless @parent_poller&.alive?
326
+
327
+ @parent_poller.kill
328
+ @parent_poller.join
329
+ end
330
+
317
331
  def start_parent_process_pollar_unix
318
332
  Thread.start do
319
333
  IRuby.logger.warn("parent process poller thread started.")
data/lib/iruby/session.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'iruby/session_adapter'
2
- require 'iruby/session/mixin'
2
+ require 'iruby/session_serializer'
3
3
 
4
4
  require 'securerandom'
5
5
  require 'time'
@@ -25,6 +25,26 @@ module IRuby
25
25
  "#{@adapter.name} session adapter"
26
26
  end
27
27
 
28
+ def close
29
+ return if @closed
30
+
31
+ @closed = true
32
+ begin
33
+ @adapter.shutdown_heartbeat(@hb_socket) if @hb_socket
34
+ ensure
35
+ begin
36
+ stop_heartbeat
37
+ ensure
38
+ begin
39
+ close_sockets
40
+ ensure
41
+ @adapter.close
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ # Optional setup hook.
28
48
  def setup
29
49
  end
30
50
 
@@ -51,10 +71,10 @@ module IRuby
51
71
  @hb_socket, @hb_port = @adapter.make_rep_socket(protocol, host, hb_port)
52
72
  @heartbeat_thread = Thread.start do
53
73
  begin
54
- # NOTE: this loop is copied from CZTop's old session code
74
+ # Adapters should return when cleanup closes the heartbeat socket/context.
55
75
  @adapter.heartbeat_loop(@hb_socket)
56
76
  rescue Exception => e
57
- IRuby.logger.fatal "Kernel heartbeat died: #{e.message}\n#{e.backtrace.join("\n")}"
77
+ IRuby.logger.fatal "Kernel heartbeat died: #{e.message}\n#{e.backtrace.join("\n")}" unless @closed
58
78
  end
59
79
  end
60
80
  end
@@ -102,6 +122,22 @@ module IRuby
102
122
 
103
123
  private
104
124
 
125
+ def close_sockets
126
+ @sockets&.values&.each do |socket|
127
+ @adapter.close_socket(socket)
128
+ end
129
+ end
130
+
131
+ def stop_heartbeat
132
+ return unless @heartbeat_thread&.alive?
133
+
134
+ @heartbeat_thread.join(1)
135
+ return unless @heartbeat_thread.alive?
136
+
137
+ @heartbeat_thread.kill
138
+ @heartbeat_thread.join
139
+ end
140
+
105
141
  def check_socket_type(socket_type)
106
142
  case socket_type
107
143
  when :publish, :reply, :stdin
@@ -26,23 +26,41 @@ module IRuby
26
26
  end
27
27
 
28
28
  def heartbeat_loop(sock)
29
- @heartbeat_device = ZMQ::Device.new(sock, sock)
29
+ # Avoid ZMQ::Device in #357; call libzmq's proxy directly.
30
+ rc = LibZMQ.zmq_proxy(sock.socket, sock.socket, nil)
31
+ errno = ZMQ::Util.errno
32
+ return if rc == -1 && (zmq_errno?(:ETERM, errno) || zmq_errno?(:EINTR, errno))
33
+
34
+ ZMQ::Util.error_check('zmq_proxy', rc)
35
+ end
36
+
37
+ def shutdown_heartbeat(sock)
38
+ if @zmq_context&.context && LibZMQ.respond_to?(:zmq_ctx_shutdown)
39
+ LibZMQ.zmq_ctx_shutdown(@zmq_context.context)
40
+ end
41
+ close_socket(sock)
42
+ end
43
+
44
+ def close
45
+ @zmq_context&.terminate
46
+ @zmq_context = nil
30
47
  end
31
48
 
32
49
  private
33
50
 
34
- def make_socket(type, protocol, host, port)
35
- case type
51
+ def make_socket(type_symbol, protocol, host, port)
52
+ case type_symbol
36
53
  when :ROUTER, :PUB, :REP
37
- type = ZMQ.const_get(type)
54
+ type = ZMQ.const_get(type_symbol)
38
55
  else
39
- if ZMQ.const_defined?(type)
56
+ if ZMQ.const_defined?(type_symbol)
40
57
  raise ArgumentError, "Unsupported ZMQ socket type: #{type_symbol}"
41
58
  else
42
59
  raise ArgumentError, "Invalid ZMQ socket type: #{type_symbol}"
43
60
  end
44
61
  end
45
62
  zmq_context.socket(type).tap do |sock|
63
+ sock.setsockopt(ZMQ::LINGER, 0) if type_symbol == :REP
46
64
  sock.bind("#{protocol}://#{host}:#{port}")
47
65
  end
48
66
  end
@@ -50,6 +68,10 @@ module IRuby
50
68
  def zmq_context
51
69
  @zmq_context ||= ZMQ::Context.new
52
70
  end
71
+
72
+ def zmq_errno?(name, errno)
73
+ ZMQ.const_defined?(name) && ZMQ.const_get(name) == errno
74
+ end
53
75
  end
54
76
  end
55
77
  end
@@ -1,4 +1,4 @@
1
- require 'iruby/session/mixin'
1
+ require 'iruby/session_serializer'
2
2
 
3
3
  module IRuby
4
4
  module SessionAdapter
@@ -20,9 +20,14 @@ module IRuby
20
20
 
21
21
  @send_callback = nil
22
22
  @recv_callback = nil
23
+ @closed_sockets = []
24
+ @closed = false
25
+ @heartbeat_started = false
26
+ @heartbeat_finished = false
23
27
  end
24
28
 
25
29
  attr_accessor :send_callback, :recv_callback
30
+ attr_reader :closed_sockets, :closed, :heartbeat_started, :heartbeat_finished
26
31
 
27
32
  def send(sock, data)
28
33
  unless @send_callback.nil?
@@ -37,6 +42,22 @@ module IRuby
37
42
  end
38
43
 
39
44
  def heartbeat_loop(sock)
45
+ @heartbeat_started = true
46
+ sleep 0.01 until @closed
47
+ @heartbeat_finished = true
48
+ end
49
+
50
+ def close_socket(sock)
51
+ @closed_sockets << sock
52
+ end
53
+
54
+ def shutdown_heartbeat(sock)
55
+ @closed = true
56
+ close_socket(sock)
57
+ end
58
+
59
+ def close
60
+ @closed = true
40
61
  end
41
62
 
42
63
  private
@@ -36,6 +36,18 @@ module IRuby
36
36
  socket, port = make_socket(:REP, protocol, host, port)
37
37
  [socket, port]
38
38
  end
39
+
40
+ def close_socket(socket)
41
+ socket.close if socket.respond_to?(:close)
42
+ end
43
+
44
+ def shutdown_heartbeat(socket)
45
+ close_socket(socket)
46
+ end
47
+
48
+ # Override in adapters that need cleanup.
49
+ def close
50
+ end
39
51
  end
40
52
 
41
53
  require_relative 'session_adapter/ffirzmq_adapter'
@@ -5,10 +5,10 @@ module IRuby
5
5
  private
6
6
 
7
7
  def serialize(idents, header, metadata = nil, content)
8
- msg = [MultiJson.dump(header),
9
- MultiJson.dump(@last_recvd_msg ? @last_recvd_msg[:header] : {}),
10
- MultiJson.dump(metadata || {}),
11
- MultiJson.dump(content || {})]
8
+ msg = [JSON.generate(header),
9
+ JSON.generate(@last_recvd_msg ? @last_recvd_msg[:header] : {}),
10
+ JSON.generate(metadata || {}),
11
+ JSON.generate(content || {})]
12
12
  frames = ([*idents].compact.map(&:to_s) << DELIM << sign(msg)) + msg
13
13
  IRuby.logger.debug "Sent #{frames.inspect}"
14
14
  frames
@@ -28,10 +28,10 @@ module IRuby
28
28
  raise 'Invalid signature' unless s == sign(msg_list[1..-1])
29
29
  {
30
30
  idents: idents,
31
- header: MultiJson.load(header),
32
- parent_header: MultiJson.load(parent_header),
33
- metadata: MultiJson.load(metadata),
34
- content: MultiJson.load(content),
31
+ header: JSON.parse(header),
32
+ parent_header: JSON.parse(parent_header),
33
+ metadata: JSON.parse(metadata),
34
+ content: JSON.parse(content),
35
35
  buffers: buffers
36
36
  }
37
37
  end
data/lib/iruby/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module IRuby
2
- VERSION = '0.8.2'
2
+ VERSION = '0.8.3'
3
3
  end
data/lib/iruby.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'fileutils'
2
2
  require 'mime/types'
3
- require 'multi_json'
3
+ require 'json'
4
4
  require 'securerandom'
5
5
  require 'openssl'
6
6
  require 'tempfile'
@@ -19,18 +19,4 @@ require 'iruby/formatter'
19
19
  require 'iruby/utils'
20
20
  require 'iruby/display'
21
21
  require 'iruby/comm'
22
-
23
- if ENV.fetch('IRUBY_OLD_SESSION', false)
24
- require 'iruby/session/mixin'
25
- begin
26
- require 'iruby/session/ffi_rzmq'
27
- rescue LoadError
28
- begin
29
- require 'iruby/session/cztop'
30
- rescue LoadError
31
- STDERR.puts "Please install ffi-rzmq or cztop before running iruby. See README."
32
- end
33
- end
34
- else
35
- require 'iruby/session'
36
- end
22
+ require 'iruby/session'
data/test/helper.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require "iruby"
2
2
  require "json"
3
- require 'multi_json'
4
3
  require "pathname"
5
4
  require "rbconfig"
6
5
  require "test/unit"
@@ -61,10 +60,12 @@ module IRubyTest
61
60
  end
62
61
 
63
62
  def teardown
63
+ IRuby::Kernel.instance&.close
64
64
  self.class.restore_kernel
65
65
  end
66
66
 
67
67
  def with_session_adapter(session_adapter_name)
68
+ IRuby::Kernel.instance&.close
68
69
  IRuby::Kernel.new(self.class.test_config_filename, session_adapter_name)
69
70
  $stdout = STDOUT
70
71
  $stderr = STDERR
@@ -137,7 +138,7 @@ module IRubyTest
137
138
  end
138
139
 
139
140
  def apple_only
140
- omit('apple only test') unless windows?
141
+ omit('apple only test') unless apple?
141
142
  end
142
143
 
143
144
  def unix_only
@@ -74,7 +74,7 @@ module IRubyTest::ApplicationTests
74
74
  kernel_name = "other-kernel-#{Process.pid}"
75
75
  out, status = Open3.capture2e(*iruby_command("console", "--kernel=#{kernel_name}"))
76
76
  refute status.success?
77
- assert_match(/\bNo such kernel named #{Regexp.escape(kernel_name)}\b/, out)
77
+ assert_match(/\b(?:No such kernel named|Could not find kernel) '?#{Regexp.escape(kernel_name)}'?(?=\W|\z)/, out)
78
78
  end
79
79
  end
80
80
 
@@ -17,6 +17,7 @@ module IRubyTest
17
17
 
18
18
  def teardown
19
19
  IRuby::Kernel.events.unregister(:initialized, @callback)
20
+ super
20
21
  end
21
22
 
22
23
  def test_iruby_initialized_event
@@ -15,7 +15,7 @@ module IRubyTest
15
15
  }
16
16
  end
17
17
 
18
- def test_new_with_session_adapter
18
+ def test_selects_correct_adapter_class
19
19
  adapter_name = ENV['IRUBY_TEST_SESSION_ADAPTER_NAME']
20
20
  adapter_class = case adapter_name
21
21
  when 'cztop'
@@ -26,8 +26,20 @@ module IRubyTest
26
26
  flunk "Unknown session adapter: #{adapter_name.inspect}"
27
27
  end
28
28
 
29
+ selected_class = IRuby::SessionAdapter.select_adapter_class(adapter_name)
30
+ assert_equal(adapter_class, selected_class)
31
+ end
32
+
33
+ def test_new_with_session_adapter_closes_heartbeat
34
+ adapter_name = ENV['IRUBY_TEST_SESSION_ADAPTER_NAME']
35
+ omit("ffi-rzmq only") unless adapter_name == 'ffi-rzmq'
36
+
29
37
  session = IRuby::Session.new(@session_config, adapter_name)
30
- assert_kind_of(adapter_class, session.adapter)
38
+
39
+ session.close
40
+ refute(session.instance_variable_get(:@heartbeat_thread).alive?)
41
+ ensure
42
+ session&.close
31
43
  end
32
44
 
33
45
  def test_without_any_session_adapter
@@ -40,5 +52,26 @@ module IRubyTest
40
52
  end
41
53
  end
42
54
  end
55
+
56
+ def test_close_releases_adapter_resources
57
+ session = IRuby::Session.new(@session_config, "test")
58
+ adapter = session.adapter
59
+
60
+ session.close
61
+
62
+ assert(adapter.closed)
63
+ assert(adapter.heartbeat_started)
64
+ assert(adapter.heartbeat_finished)
65
+ assert_equal([
66
+ :PUB,
67
+ :REP,
68
+ :ROUTER,
69
+ :ROUTER,
70
+ ],
71
+ adapter.closed_sockets.map(&:type).sort_by(&:to_s))
72
+ refute(session.instance_variable_get(:@heartbeat_thread).alive?)
73
+ ensure
74
+ session&.close
75
+ end
43
76
  end
44
77
  end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.8.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Mendler
8
8
  - The SciRuby developers
9
- autorequire:
10
9
  bindir: exe
11
10
  cert_chain: []
12
- date: 2025-04-10 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: data_uri
@@ -81,34 +80,6 @@ dependencies:
81
80
  - - ">="
82
81
  - !ruby/object:Gem::Version
83
82
  version: 3.3.1
84
- - !ruby/object:Gem::Dependency
85
- name: multi_json
86
- requirement: !ruby/object:Gem::Requirement
87
- requirements:
88
- - - "~>"
89
- - !ruby/object:Gem::Version
90
- version: '1.11'
91
- type: :runtime
92
- prerelease: false
93
- version_requirements: !ruby/object:Gem::Requirement
94
- requirements:
95
- - - "~>"
96
- - !ruby/object:Gem::Version
97
- version: '1.11'
98
- - !ruby/object:Gem::Dependency
99
- name: pycall
100
- requirement: !ruby/object:Gem::Requirement
101
- requirements:
102
- - - ">="
103
- - !ruby/object:Gem::Version
104
- version: 1.2.1
105
- type: :development
106
- prerelease: false
107
- version_requirements: !ruby/object:Gem::Requirement
108
- requirements:
109
- - - ">="
110
- - !ruby/object:Gem::Version
111
- version: 1.2.1
112
83
  - !ruby/object:Gem::Dependency
113
84
  name: rake
114
85
  requirement: !ruby/object:Gem::Requirement
@@ -151,7 +122,7 @@ dependencies:
151
122
  - - ">="
152
123
  - !ruby/object:Gem::Version
153
124
  version: '0'
154
- description: A Ruby kernel for Jupyter environment. Try it at try.jupyter.org.
125
+ description: A Ruby kernel for Jupyter environment.
155
126
  email:
156
127
  - mail@daniel-mendler.de
157
128
  executables:
@@ -159,6 +130,7 @@ executables:
159
130
  extensions: []
160
131
  extra_rdoc_files: []
161
132
  files:
133
+ - ".github/dependabot.yml"
162
134
  - ".github/workflows/ci.yml"
163
135
  - ".gitignore"
164
136
  - CHANGES.md
@@ -166,11 +138,6 @@ files:
166
138
  - LICENSE
167
139
  - README.md
168
140
  - Rakefile
169
- - ci/Dockerfile.base.erb
170
- - ci/Dockerfile.main.erb
171
- - ci/requirements.txt
172
- - docker/setup.sh
173
- - docker/test.sh
174
141
  - exe/iruby
175
142
  - iruby.gemspec
176
143
  - lib/iruby.rb
@@ -210,20 +177,16 @@ files:
210
177
  - lib/iruby/logger.rb
211
178
  - lib/iruby/ostream.rb
212
179
  - lib/iruby/session.rb
213
- - lib/iruby/session/cztop.rb
214
- - lib/iruby/session/ffi_rzmq.rb
215
- - lib/iruby/session/mixin.rb
216
180
  - lib/iruby/session_adapter.rb
217
181
  - lib/iruby/session_adapter/cztop_adapter.rb
218
182
  - lib/iruby/session_adapter/ffirzmq_adapter.rb
219
183
  - lib/iruby/session_adapter/test_adapter.rb
184
+ - lib/iruby/session_serializer.rb
220
185
  - lib/iruby/utils.rb
221
186
  - lib/iruby/version.rb
222
187
  - logo/logo-32x32.png
223
188
  - logo/logo-64x64.png
224
189
  - logo/ruby.svg
225
- - run-test.sh
226
- - tasks/ci.rake
227
190
  - test/helper.rb
228
191
  - test/integration_test.rb
229
192
  - test/iruby/application/application_test.rb
@@ -251,7 +214,6 @@ licenses:
251
214
  - MIT
252
215
  metadata:
253
216
  msys2_mingw_dependencies: zeromq
254
- post_install_message:
255
217
  rdoc_options: []
256
218
  require_paths:
257
219
  - lib
@@ -271,8 +233,7 @@ requirements:
271
233
  - 'system: libzmq: freebsd: libzmq4'
272
234
  - 'system: libzmq: homebrew: zmq'
273
235
  - 'system: libzmq: macports: zmq'
274
- rubygems_version: 3.5.22
275
- signing_key:
236
+ rubygems_version: 4.0.10
276
237
  specification_version: 4
277
238
  summary: Ruby Kernel for Jupyter
278
239
  test_files:
@@ -1,41 +0,0 @@
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
- )
@@ -1,7 +0,0 @@
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
data/ci/requirements.txt DELETED
@@ -1 +0,0 @@
1
- jupyter-console>=6.0.0
data/docker/setup.sh DELETED
@@ -1,15 +0,0 @@
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 DELETED
@@ -1,7 +0,0 @@
1
- #!/bin/bash
2
-
3
- set -ex
4
-
5
- cd /tmp/iruby
6
- bundle install --with test --without plot
7
- bundle exec rake test
@@ -1,70 +0,0 @@
1
- require 'cztop'
2
-
3
- module IRuby
4
- class Session
5
- include SessionSerialize
6
-
7
- def initialize(config)
8
- connection = "#{config['transport']}://#{config['ip']}:%d"
9
-
10
- reply_socket = CZTop::Socket::ROUTER.new(connection % config['shell_port'])
11
- pub_socket = CZTop::Socket::PUB.new(connection % config['iopub_port'])
12
- stdin_socket = CZTop::Socket::ROUTER.new(connection % config['stdin_port'])
13
-
14
- Thread.new do
15
- begin
16
- hb_socket = CZTop::Socket::REP.new(connection % config['hb_port'])
17
- loop do
18
- message = hb_socket.receive
19
- hb_socket << message
20
- end
21
- rescue Exception => e
22
- IRuby.logger.fatal "Kernel heartbeat died: #{e.message}\n#{e.backtrace.join("\n")}"
23
- end
24
- end
25
-
26
- @sockets = {
27
- publish: pub_socket,
28
- reply: reply_socket,
29
- stdin: stdin_socket,
30
- }
31
-
32
- @session = SecureRandom.uuid
33
- unless config['key'].to_s.empty? || config['signature_scheme'].to_s.empty?
34
- raise 'Unknown signature scheme' unless config['signature_scheme'] =~ /\Ahmac-(.*)\Z/
35
- @hmac = OpenSSL::HMAC.new(config['key'], OpenSSL::Digest.new($1))
36
- end
37
- end
38
-
39
- def description
40
- 'old-stle session using cztop'
41
- end
42
-
43
- # Build and send a message
44
- def send(socket, type, content)
45
- idents =
46
- if socket == :reply && @last_recvd_msg
47
- @last_recvd_msg[:idents]
48
- else
49
- type == :stream ? "stream.#{content[:name]}" : type
50
- end
51
- header = {
52
- msg_type: type,
53
- msg_id: SecureRandom.uuid,
54
- username: 'kernel',
55
- session: @session,
56
- version: '5.0'
57
- }
58
- @sockets[socket] << serialize(idents, header, content)
59
- end
60
-
61
- # Receive a message and decode it
62
- def recv(socket)
63
- @last_recvd_msg = unserialize(@sockets[socket].receive)
64
- end
65
-
66
- def recv_input
67
- unserialize(@sockets[:stdin].receive)[:content]["value"]
68
- end
69
- end
70
- end
@@ -1,87 +0,0 @@
1
- require 'ffi-rzmq'
2
-
3
- module IRuby
4
- class Session
5
- include SessionSerialize
6
-
7
- def initialize(config)
8
- c = ZMQ::Context.new
9
-
10
- connection = "#{config['transport']}://#{config['ip']}:%d"
11
- reply_socket = c.socket(ZMQ::XREP)
12
- reply_socket.bind(connection % config['shell_port'])
13
-
14
- pub_socket = c.socket(ZMQ::PUB)
15
- pub_socket.bind(connection % config['iopub_port'])
16
-
17
- stdin_socket = c.socket(ZMQ::XREP)
18
- stdin_socket.bind(connection % config['stdin_port'])
19
-
20
- Thread.new do
21
- begin
22
- hb_socket = c.socket(ZMQ::REP)
23
- hb_socket.bind(connection % config['hb_port'])
24
- ZMQ::Device.new(hb_socket, hb_socket)
25
- rescue Exception => e
26
- IRuby.logger.fatal "Kernel heartbeat died: #{e.message}\n#{e.backtrace.join("\n")}"
27
- end
28
- end
29
-
30
- @sockets = {
31
- publish: pub_socket, reply: reply_socket, stdin: stdin_socket
32
- }
33
-
34
- @session = SecureRandom.uuid
35
- unless config['key'].to_s.empty? || config['signature_scheme'].to_s.empty?
36
- raise 'Unknown signature scheme' unless config['signature_scheme'] =~ /\Ahmac-(.*)\Z/
37
- @hmac = OpenSSL::HMAC.new(config['key'], OpenSSL::Digest.new($1))
38
- end
39
- end
40
-
41
- # Build and send a message
42
- def send(socket, type, content)
43
- idents =
44
- if socket == :reply && @last_recvd_msg
45
- @last_recvd_msg[:idents]
46
- else
47
- type == :stream ? "stream.#{content[:name]}" : type
48
- end
49
- header = {
50
- msg_type: type,
51
- msg_id: SecureRandom.uuid,
52
- username: 'kernel',
53
- session: @session,
54
- version: '5.0'
55
- }
56
- socket = @sockets[socket]
57
- list = serialize(idents, header, content)
58
- list.each_with_index do |part, i|
59
- socket.send_string(part, i == list.size - 1 ? 0 : ZMQ::SNDMORE)
60
- end
61
- end
62
-
63
- # Receive a message and decode it
64
- def recv(socket)
65
- socket = @sockets[socket]
66
- msg = []
67
- while msg.empty? || socket.more_parts?
68
- begin
69
- frame = ''
70
- rc = socket.recv_string(frame)
71
- ZMQ::Util.error_check('zmq_msg_send', rc)
72
- msg << frame
73
- rescue
74
- end
75
- end
76
-
77
- @last_recvd_msg = unserialize(msg)
78
- end
79
-
80
- def recv_input
81
- last_recvd_msg = @last_recvd_msg
82
- input = recv(:stdin)[:content]["value"]
83
- @last_recvd_msg = last_recvd_msg
84
- input
85
- end
86
- end
87
- end
data/run-test.sh DELETED
@@ -1,12 +0,0 @@
1
- #! /bin/bash
2
-
3
- set -ex
4
-
5
- export PYTHON=python3
6
-
7
- ADAPTERS="cztop ffi-rzmq"
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 DELETED
@@ -1,65 +0,0 @@
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