iruby 0.6.0 → 0.7.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 +4 -4
- data/.github/workflows/ubuntu.yml +9 -2
- data/CHANGES.md +232 -0
- data/README.md +17 -2
- data/iruby.gemspec +1 -0
- data/lib/iruby.rb +3 -0
- data/lib/iruby/backend.rb +11 -0
- data/lib/iruby/command.rb +2 -6
- data/lib/iruby/display.rb +178 -62
- data/lib/iruby/kernel.rb +99 -10
- data/lib/iruby/session.rb +1 -0
- data/lib/iruby/utils.rb +12 -1
- data/lib/iruby/version.rb +1 -1
- data/test/helper.rb +5 -2
- data/test/iruby/backend_test.rb +12 -0
- data/test/iruby/display_test.rb +185 -0
- data/test/iruby/kernel_test.rb +33 -1
- data/test/iruby/mime_test.rb +7 -0
- data/test/iruby/multi_logger_test.rb +0 -3
- data/test/iruby/utils_test.rb +25 -0
- metadata +22 -4
- data/CHANGES +0 -181
data/lib/iruby/kernel.rb
CHANGED
@@ -8,10 +8,26 @@ module IRuby
|
|
8
8
|
@events = EventManager.new([:initialized])
|
9
9
|
|
10
10
|
class << self
|
11
|
+
# Return the event manager defined in the `IRuby::Kernel` class.
|
12
|
+
# This event manager can handle the following event:
|
13
|
+
#
|
14
|
+
# - `initialized`: The event occurred after the initialization of
|
15
|
+
# a `IRuby::Kernel` instance is finished
|
16
|
+
#
|
17
|
+
# @example Registering initialized event
|
18
|
+
# IRuby::Kernel.events.register(:initialized) do |result|
|
19
|
+
# STDERR.puts "IRuby kernel has been initialized"
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# @see IRuby::EventManager
|
23
|
+
# @see IRuby::Kernel#events
|
11
24
|
attr_reader :events
|
25
|
+
|
26
|
+
# Returns the singleton kernel instance
|
12
27
|
attr_accessor :instance
|
13
28
|
end
|
14
29
|
|
30
|
+
# Returns a session object
|
15
31
|
attr_reader :session
|
16
32
|
|
17
33
|
EVENTS = [
|
@@ -34,21 +50,77 @@ module IRuby
|
|
34
50
|
|
35
51
|
@events = EventManager.new(EVENTS)
|
36
52
|
@execution_count = 0
|
37
|
-
@backend =
|
53
|
+
@backend = PlainBackend.new
|
38
54
|
@running = true
|
39
55
|
|
40
56
|
self.class.events.trigger(:initialized, self)
|
41
57
|
end
|
42
58
|
|
59
|
+
# Returns the event manager defined in a `IRuby::Kernel` instance.
|
60
|
+
# This event manager can handle the following events:
|
61
|
+
#
|
62
|
+
# - `pre_execute`: The event occurred before running the code
|
63
|
+
#
|
64
|
+
# - `pre_run_cell`: The event occurred before running the code and
|
65
|
+
# if the code execution is not silent
|
66
|
+
#
|
67
|
+
# - `post_execute`: The event occurred after running the code
|
68
|
+
#
|
69
|
+
# - `post_run_cell`: The event occurred after running the code and
|
70
|
+
# if the code execution is not silent
|
71
|
+
#
|
72
|
+
# The callback functions of `pre_run_cell` event must take one argument
|
73
|
+
# to get an `ExecutionInfo` object.
|
74
|
+
# The callback functions of `post_run_cell` event must take one argument
|
75
|
+
# to get the result of the code execution.
|
76
|
+
#
|
77
|
+
# @example Registering post_run_cell event
|
78
|
+
# IRuby::Kernel.instance.events.register(:post_run_cell) do |result|
|
79
|
+
# STDERR.puts "The result of the last execution: %p" % result
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# @see IRuby::EventManager
|
83
|
+
# @see IRuby::ExecutionInfo
|
84
|
+
# @see IRuby::Kernel.events
|
43
85
|
attr_reader :events
|
44
86
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
87
|
+
# Switch the backend (interactive shell) system
|
88
|
+
#
|
89
|
+
# @param backend [:irb,:plain,:pry] Specify the backend name switched to
|
90
|
+
#
|
91
|
+
# @return [true,false] true if the switching succeeds, otherwise false
|
92
|
+
def switch_backend!(backend)
|
93
|
+
name = case backend
|
94
|
+
when String, Symbol
|
95
|
+
name = backend.downcase
|
96
|
+
else
|
97
|
+
name = backend
|
98
|
+
end
|
99
|
+
|
100
|
+
backend_class = case name
|
101
|
+
when :irb, :plain
|
102
|
+
PlainBackend
|
103
|
+
when :pry
|
104
|
+
PryBackend
|
105
|
+
else
|
106
|
+
raise ArgumentError,
|
107
|
+
"Unknown backend name: %p" % backend
|
108
|
+
end
|
109
|
+
|
110
|
+
begin
|
111
|
+
new_backend = backend_class.new
|
112
|
+
@backend = new_backend
|
113
|
+
true
|
114
|
+
rescue Exception => e
|
115
|
+
unless LoadError === e
|
116
|
+
IRuby.logger.warn "Could not load #{backend_class}: " +
|
117
|
+
"#{e.message}\n#{e.backtrace.join("\n")}"
|
118
|
+
end
|
119
|
+
return false
|
120
|
+
end
|
50
121
|
end
|
51
122
|
|
123
|
+
# @private
|
52
124
|
def run
|
53
125
|
send_status :starting
|
54
126
|
while @running
|
@@ -56,6 +128,7 @@ module IRuby
|
|
56
128
|
end
|
57
129
|
end
|
58
130
|
|
131
|
+
# @private
|
59
132
|
def dispatch
|
60
133
|
msg = @session.recv(:reply)
|
61
134
|
IRuby.logger.debug "Kernel#dispatch: msg = #{msg}"
|
@@ -72,6 +145,7 @@ module IRuby
|
|
72
145
|
@session.send(:publish, :error, error_content(e))
|
73
146
|
end
|
74
147
|
|
148
|
+
# @private
|
75
149
|
def kernel_info_request(msg)
|
76
150
|
@session.send(:reply, :kernel_info_reply,
|
77
151
|
protocol_version: '5.0',
|
@@ -93,11 +167,13 @@ module IRuby
|
|
93
167
|
status: :ok)
|
94
168
|
end
|
95
169
|
|
170
|
+
# @private
|
96
171
|
def send_status(status)
|
97
172
|
IRuby.logger.debug "Send status: #{status}"
|
98
173
|
@session.send(:publish, :status, execution_state: status)
|
99
174
|
end
|
100
175
|
|
176
|
+
# @private
|
101
177
|
def execute_request(msg)
|
102
178
|
code = msg[:content]['code']
|
103
179
|
store_history = msg[:content]['store_history']
|
@@ -134,16 +210,20 @@ module IRuby
|
|
134
210
|
content[:execution_count] = @execution_count
|
135
211
|
end
|
136
212
|
|
213
|
+
unless result.nil? || silent
|
214
|
+
@session.send(:publish, :execute_result,
|
215
|
+
data: Display.display(result),
|
216
|
+
metadata: {},
|
217
|
+
execution_count: @execution_count)
|
218
|
+
end
|
219
|
+
|
137
220
|
events.trigger(:post_execute)
|
138
221
|
events.trigger(:post_run_cell, result) unless silent
|
139
222
|
|
140
223
|
@session.send(:reply, :execute_reply, content)
|
141
|
-
@session.send(:publish, :execute_result,
|
142
|
-
data: Display.display(result),
|
143
|
-
metadata: {},
|
144
|
-
execution_count: @execution_count) unless result.nil? || msg[:content]['silent']
|
145
224
|
end
|
146
225
|
|
226
|
+
# @private
|
147
227
|
def error_content(e)
|
148
228
|
rindex = e.backtrace.rindex{|line| line.start_with?(@backend.eval_path)} || -1
|
149
229
|
backtrace = SyntaxError === e && rindex == -1 ? [] : e.backtrace[0..rindex]
|
@@ -152,12 +232,14 @@ module IRuby
|
|
152
232
|
traceback: ["#{RED}#{e.class}#{RESET}: #{e.message}", *backtrace] }
|
153
233
|
end
|
154
234
|
|
235
|
+
# @private
|
155
236
|
def is_complete_request(msg)
|
156
237
|
# FIXME: the code completeness should be judged by using ripper or other Ruby parser
|
157
238
|
@session.send(:reply, :is_complete_reply,
|
158
239
|
status: :unknown)
|
159
240
|
end
|
160
241
|
|
242
|
+
# @private
|
161
243
|
def complete_request(msg)
|
162
244
|
# HACK for #26, only complete last line
|
163
245
|
code = msg[:content]['code']
|
@@ -173,36 +255,43 @@ module IRuby
|
|
173
255
|
status: :ok)
|
174
256
|
end
|
175
257
|
|
258
|
+
# @private
|
176
259
|
def connect_request(msg)
|
177
260
|
@session.send(:reply, :connect_reply, Hash[%w(shell_port iopub_port stdin_port hb_port).map {|k| [k, @config[k]] }])
|
178
261
|
end
|
179
262
|
|
263
|
+
# @private
|
180
264
|
def shutdown_request(msg)
|
181
265
|
@session.send(:reply, :shutdown_reply, msg[:content])
|
182
266
|
@running = false
|
183
267
|
end
|
184
268
|
|
269
|
+
# @private
|
185
270
|
def history_request(msg)
|
186
271
|
# we will just send back empty history for now, pending clarification
|
187
272
|
# as requested in ipython/ipython#3806
|
188
273
|
@session.send(:reply, :history_reply, history: [])
|
189
274
|
end
|
190
275
|
|
276
|
+
# @private
|
191
277
|
def inspect_request(msg)
|
192
278
|
# not yet implemented. See (#119).
|
193
279
|
@session.send(:reply, :inspect_reply, status: :ok, found: false, data: {}, metadata: {})
|
194
280
|
end
|
195
281
|
|
282
|
+
# @private
|
196
283
|
def comm_open(msg)
|
197
284
|
comm_id = msg[:content]['comm_id']
|
198
285
|
target_name = msg[:content]['target_name']
|
199
286
|
Comm.comm[comm_id] = Comm.target[target_name].new(target_name, comm_id)
|
200
287
|
end
|
201
288
|
|
289
|
+
# @private
|
202
290
|
def comm_msg(msg)
|
203
291
|
Comm.comm[msg[:content]['comm_id']].handle_msg(msg[:content]['data'])
|
204
292
|
end
|
205
293
|
|
294
|
+
# @private
|
206
295
|
def comm_close(msg)
|
207
296
|
comm_id = msg[:content]['comm_id']
|
208
297
|
Comm.comm[comm_id].handle_close(msg[:content]['data'])
|
data/lib/iruby/session.rb
CHANGED
data/lib/iruby/utils.rb
CHANGED
@@ -4,37 +4,48 @@ module IRuby
|
|
4
4
|
Display.convert(object, options)
|
5
5
|
end
|
6
6
|
|
7
|
+
# Display the object
|
7
8
|
def display(obj, options = {})
|
8
9
|
Kernel.instance.session.send(:publish, :display_data,
|
9
10
|
data: Display.display(obj, options),
|
10
11
|
metadata: {}) unless obj.nil?
|
12
|
+
# The next `nil` is necessary to prevent unintentional displaying
|
13
|
+
# the result of Session#send
|
14
|
+
nil
|
11
15
|
end
|
12
16
|
|
17
|
+
# Clear the output area
|
13
18
|
def clear_output(wait=false)
|
14
19
|
Display.clear_output(wait)
|
15
20
|
end
|
16
21
|
|
22
|
+
# Format the given object into HTML table
|
17
23
|
def table(s, **options)
|
18
|
-
html(HTML.table(s, options))
|
24
|
+
html(HTML.table(s, **options))
|
19
25
|
end
|
20
26
|
|
27
|
+
# Treat the given string as LaTeX text
|
21
28
|
def latex(s)
|
22
29
|
convert(s, mime: 'text/latex')
|
23
30
|
end
|
24
31
|
alias tex latex
|
25
32
|
|
33
|
+
# Format the given string of TeX equation into LaTeX text
|
26
34
|
def math(s)
|
27
35
|
convert("$$#{s}$$", mime: 'text/latex')
|
28
36
|
end
|
29
37
|
|
38
|
+
# Treat the given string as HTML
|
30
39
|
def html(s)
|
31
40
|
convert(s, mime: 'text/html')
|
32
41
|
end
|
33
42
|
|
43
|
+
# Treat the given string as JavaScript code
|
34
44
|
def javascript(s)
|
35
45
|
convert(s, mime: 'application/javascript')
|
36
46
|
end
|
37
47
|
|
48
|
+
# Treat the given string as SVG text
|
38
49
|
def svg(s)
|
39
50
|
convert(s, mime: 'image/svg+xml')
|
40
51
|
end
|
data/lib/iruby/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require "iruby"
|
2
|
-
require "iruby/logger"
|
3
2
|
require "json"
|
4
3
|
require 'multi_json'
|
5
4
|
require "pathname"
|
@@ -38,10 +37,14 @@ module IRubyTest
|
|
38
37
|
@__config_path.to_s
|
39
38
|
end
|
40
39
|
|
41
|
-
def
|
40
|
+
def self.restore_kernel
|
42
41
|
IRuby::Kernel.instance = @__original_kernel_instance
|
43
42
|
end
|
44
43
|
|
44
|
+
def teardown
|
45
|
+
self.class.restore_kernel
|
46
|
+
end
|
47
|
+
|
45
48
|
def with_session_adapter(session_adapter_name)
|
46
49
|
IRuby::Kernel.new(self.class.test_config_filename, session_adapter_name)
|
47
50
|
$stdout = STDOUT
|
data/test/iruby/backend_test.rb
CHANGED
@@ -8,6 +8,12 @@ module IRubyTest
|
|
8
8
|
assert_equal 3, @plainbackend.eval('1+2', false)
|
9
9
|
end
|
10
10
|
|
11
|
+
def test_include_module
|
12
|
+
assert_nothing_raised do
|
13
|
+
@plainbackend.eval("include Math, Comparable", false)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
11
17
|
def test_complete_req
|
12
18
|
assert_includes @plainbackend.complete('req'), 'require'
|
13
19
|
end
|
@@ -26,6 +32,12 @@ module IRubyTest
|
|
26
32
|
assert_equal 3, @prybackend.eval('1+2', false)
|
27
33
|
end
|
28
34
|
|
35
|
+
def test_include_module
|
36
|
+
assert_nothing_raised do
|
37
|
+
@prybackend.eval("include Math, Comparable", false)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
29
41
|
def test_complete_req
|
30
42
|
assert_includes @prybackend.complete('req'), 'require'
|
31
43
|
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
module IRubyTest
|
2
|
+
class DisplayTest < TestBase
|
3
|
+
def setup
|
4
|
+
@object = Object.new
|
5
|
+
@object.instance_variable_set(:@to_html_called, false)
|
6
|
+
@object.instance_variable_set(:@to_markdown_called, false)
|
7
|
+
@object.instance_variable_set(:@to_iruby_called, false)
|
8
|
+
@object.instance_variable_set(:@to_iruby_mimebundle_called, false)
|
9
|
+
|
10
|
+
class << @object
|
11
|
+
attr_reader :to_html_called
|
12
|
+
attr_reader :to_markdown_called
|
13
|
+
attr_reader :to_iruby_called
|
14
|
+
attr_reader :to_iruby_mimebundle_called
|
15
|
+
|
16
|
+
def html
|
17
|
+
"<b>html</b>"
|
18
|
+
end
|
19
|
+
|
20
|
+
def markdown
|
21
|
+
"*markdown*"
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
"!!! inspect !!!"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def define_to_html
|
31
|
+
class << @object
|
32
|
+
def to_html
|
33
|
+
@to_html_called = true
|
34
|
+
html
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_to_markdown
|
40
|
+
class << @object
|
41
|
+
def to_markdown
|
42
|
+
@to_markdown_called = true
|
43
|
+
markdown
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def define_to_iruby
|
49
|
+
class << @object
|
50
|
+
def to_iruby
|
51
|
+
@to_iruby_called = true
|
52
|
+
["text/html", "<b>to_iruby</b>"]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def define_to_iruby_mimebundle
|
58
|
+
class << @object
|
59
|
+
def to_iruby_mimebundle(include: [])
|
60
|
+
@to_iruby_mimebundle_called = true
|
61
|
+
mimes = if include.empty?
|
62
|
+
["text/markdown", "application/json"]
|
63
|
+
else
|
64
|
+
include
|
65
|
+
end
|
66
|
+
formats = mimes.map { |mime|
|
67
|
+
result = case mime
|
68
|
+
when "text/markdown"
|
69
|
+
"**markdown**"
|
70
|
+
when "application/json"
|
71
|
+
%Q[{"mimebundle": "json"}]
|
72
|
+
end
|
73
|
+
[mime, result]
|
74
|
+
}.to_h
|
75
|
+
metadata = {}
|
76
|
+
return formats, metadata
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def assert_iruby_display(expected)
|
82
|
+
assert_equal(expected,
|
83
|
+
{
|
84
|
+
result: IRuby::Display.display(@object),
|
85
|
+
to_html_called: @object.to_html_called,
|
86
|
+
to_markdown_called: @object.to_markdown_called,
|
87
|
+
to_iruby_called: @object.to_iruby_called,
|
88
|
+
to_iruby_mimebundle_called: @object.to_iruby_mimebundle_called
|
89
|
+
})
|
90
|
+
end
|
91
|
+
|
92
|
+
sub_test_case("the object cannot handle all the mime types") do
|
93
|
+
def test_display
|
94
|
+
assert_iruby_display({
|
95
|
+
result: {"text/plain" => "!!! inspect !!!"},
|
96
|
+
to_html_called: false,
|
97
|
+
to_markdown_called: false,
|
98
|
+
to_iruby_called: false,
|
99
|
+
to_iruby_mimebundle_called: false
|
100
|
+
})
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
sub_test_case("the object can respond to to_iruby") do
|
105
|
+
def setup
|
106
|
+
super
|
107
|
+
define_to_iruby
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_display
|
111
|
+
assert_iruby_display({
|
112
|
+
result: {
|
113
|
+
"text/html" => "<b>to_iruby</b>",
|
114
|
+
"text/plain" => "!!! inspect !!!"
|
115
|
+
},
|
116
|
+
to_html_called: false,
|
117
|
+
to_markdown_called: false,
|
118
|
+
to_iruby_called: true,
|
119
|
+
to_iruby_mimebundle_called: false
|
120
|
+
})
|
121
|
+
end
|
122
|
+
|
123
|
+
sub_test_case("the object can respond to to_markdown") do
|
124
|
+
def setup
|
125
|
+
super
|
126
|
+
define_to_markdown
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_display
|
130
|
+
assert_iruby_display({
|
131
|
+
result: {
|
132
|
+
"text/markdown" => "*markdown*",
|
133
|
+
"text/plain" => "!!! inspect !!!"
|
134
|
+
},
|
135
|
+
to_html_called: false,
|
136
|
+
to_markdown_called: true,
|
137
|
+
to_iruby_called: false,
|
138
|
+
to_iruby_mimebundle_called: false
|
139
|
+
})
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
sub_test_case("the object can respond to to_html") do
|
144
|
+
def setup
|
145
|
+
super
|
146
|
+
define_to_html
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_display
|
150
|
+
assert_iruby_display({
|
151
|
+
result: {
|
152
|
+
"text/html" => "<b>html</b>",
|
153
|
+
"text/plain" => "!!! inspect !!!"
|
154
|
+
},
|
155
|
+
to_html_called: true,
|
156
|
+
to_markdown_called: false,
|
157
|
+
to_iruby_called: false,
|
158
|
+
to_iruby_mimebundle_called: false
|
159
|
+
})
|
160
|
+
end
|
161
|
+
|
162
|
+
sub_test_case("the object can respond to to_iruby_mimebundle") do
|
163
|
+
def setup
|
164
|
+
super
|
165
|
+
define_to_iruby_mimebundle
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_display
|
169
|
+
assert_iruby_display({
|
170
|
+
result: {
|
171
|
+
"text/markdown" => "**markdown**",
|
172
|
+
"application/json" => %Q[{"mimebundle": "json"}],
|
173
|
+
"text/plain" => "!!! inspect !!!"
|
174
|
+
},
|
175
|
+
to_html_called: false,
|
176
|
+
to_markdown_called: false,
|
177
|
+
to_iruby_called: false,
|
178
|
+
to_iruby_mimebundle_called: true
|
179
|
+
})
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|