iruby 0.6.0 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|