Class: IRuby::Kernel

Inherits:
Object
  • Object
show all
Defined in:
lib/iruby/kernel.rb

Constant Summary collapse

RED =
"\e[31m"
RESET =
"\e[0m"
EVENTS =
[
  :pre_execute,
  :pre_run_cell,
  :post_run_cell,
  :post_execute
].freeze

Class Attribute Summary collapse

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_file, session_adapter_name = nil) ⇒ Kernel

Returns a new instance of Kernel.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/iruby/kernel.rb', line 40

def initialize(config_file, session_adapter_name=nil)
  @config = MultiJson.load(File.read(config_file))
  IRuby.logger.debug("IRuby kernel start with config #{@config}")
  Kernel.instance = self

  @session = Session.new(@config, session_adapter_name)
  $stdout = OStream.new(@session, :stdout)
  $stderr = OStream.new(@session, :stderr)

  init_parent_process_poller

  @events = EventManager.new(EVENTS)
  @execution_count = 0
  @backend = PlainBackend.new
  @running = true

  self.class.events.trigger(:initialized, self)
end

Class Attribute Details

.eventsObject (readonly)

Return the event manager defined in the ‘IRuby::Kernel` class. This event manager can handle the following event:

  • ‘initialized`: The event occurred after the initialization of a `IRuby::Kernel` instance is finished

Examples:

Registering initialized event

IRuby::Kernel.events.register(:initialized) do |result|
  STDERR.puts "IRuby kernel has been initialized"
end

See Also:



24
25
26
# File 'lib/iruby/kernel.rb', line 24

def events
  @events
end

.instanceObject

Returns the singleton kernel instance



27
28
29
# File 'lib/iruby/kernel.rb', line 27

def instance
  @instance
end

Instance Attribute Details

#eventsObject (readonly)

Returns the event manager defined in a ‘IRuby::Kernel` instance. This event manager can handle the following events:

  • ‘pre_execute`: The event occurred before running the code

  • ‘pre_run_cell`: The event occurred before running the code and if the code execution is not silent

  • ‘post_execute`: The event occurred after running the code

  • ‘post_run_cell`: The event occurred after running the code and if the code execution is not silent

The callback functions of ‘pre_run_cell` event must take one argument to get an `ExecutionInfo` object. The callback functions of `post_run_cell` event must take one argument to get the result of the code execution.

Examples:

Registering post_run_cell event

IRuby::Kernel.instance.events.register(:post_run_cell) do |result|
  STDERR.puts "The result of the last execution: %p" % result
end

See Also:



85
86
87
# File 'lib/iruby/kernel.rb', line 85

def events
  @events
end

#sessionObject (readonly)

Returns a session object



31
32
33
# File 'lib/iruby/kernel.rb', line 31

def session
  @session
end

Instance Method Details

#comm_close(msg) ⇒ Object



295
296
297
298
299
# File 'lib/iruby/kernel.rb', line 295

def comm_close(msg)
  comm_id = msg[:content]['comm_id']
  Comm.comm[comm_id].handle_close(msg[:content]['data'])
  Comm.comm.delete(comm_id)
end

#comm_msg(msg) ⇒ Object



290
291
292
# File 'lib/iruby/kernel.rb', line 290

def comm_msg(msg)
  Comm.comm[msg[:content]['comm_id']].handle_msg(msg[:content]['data'])
end

#comm_open(msg) ⇒ Object



283
284
285
286
287
# File 'lib/iruby/kernel.rb', line 283

def comm_open(msg)
  comm_id = msg[:content]['comm_id']
  target_name = msg[:content]['target_name']
  Comm.comm[comm_id] = Comm.target[target_name].new(target_name, comm_id)
end

#complete_request(msg) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/iruby/kernel.rb', line 243

def complete_request(msg)
  # HACK for #26, only complete last line
  code = msg[:content]['code']
  if start = code.rindex(/\s|\R/)
    code = code[start+1..-1]
    start += 1
  end
  @session.send(:reply, :complete_reply,
                matches: @backend.complete(code),
                cursor_start: start.to_i,
                cursor_end: msg[:content]['cursor_pos'],
                metadata: {},
                status: :ok)
end

#connect_request(msg) ⇒ Object



259
260
261
# File 'lib/iruby/kernel.rb', line 259

def connect_request(msg)
  @session.send(:reply, :connect_reply, Hash[%w(shell_port iopub_port stdin_port hb_port).map {|k| [k, @config[k]] }])
end

#dispatchObject



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/iruby/kernel.rb', line 132

def dispatch
  msg = @session.recv(:reply)
  IRuby.logger.debug "Kernel#dispatch: msg = #{msg}"
  type = msg[:header]['msg_type']
  raise "Unknown message type: #{msg.inspect}" unless type =~ /comm_|_request\Z/ && respond_to?(type)
  begin
    send_status :busy
    send(type, msg)
  ensure
    send_status :idle
  end
rescue Exception => e
  IRuby.logger.debug "Kernel error: #{e.message}\n#{e.backtrace.join("\n")}"
  @session.send(:publish, :error, error_content(e))
end

#error_content(e) ⇒ Object



227
228
229
230
231
232
233
# File 'lib/iruby/kernel.rb', line 227

def error_content(e)
  rindex = e.backtrace.rindex{|line| line.start_with?(@backend.eval_path)} || -1
  backtrace = SyntaxError === e  && rindex == -1 ? [] : e.backtrace[0..rindex]
  { ename: e.class.to_s,
    evalue: e.message,
    traceback: ["#{RED}#{e.class}#{RESET}: #{e.message}", *backtrace] }
end

#execute_request(msg) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/iruby/kernel.rb', line 177

def execute_request(msg)
  code = msg[:content]['code']
  store_history = msg[:content]['store_history']
  silent = msg[:content]['silent']

  @execution_count += 1 if store_history

  unless silent
    @session.send(:publish, :execute_input, code: code, execution_count: @execution_count)
  end

  events.trigger(:pre_execute)
  unless silent
    exec_info = ExecutionInfo.new(code, store_history, silent)
    events.trigger(:pre_run_cell, exec_info)
  end

  content = {
    status: :ok,
    payload: [],
    user_expressions: {},
    execution_count: @execution_count
  }

  result = nil
  begin
    result = @backend.eval(code, store_history)
  rescue SystemExit
    content[:payload] << { source: :ask_exit }
  rescue Exception => e
    content = error_content(e)
    @session.send(:publish, :error, content)
    content[:status] = :error
    content[:execution_count] = @execution_count
  end

  unless result.nil? || silent
    @session.send(:publish, :execute_result,
                  data: Display.display(result),
                  metadata: {},
                  execution_count: @execution_count)
  end

  events.trigger(:post_execute)
  events.trigger(:post_run_cell, result) unless silent

  @session.send(:reply, :execute_reply, content)
end

#history_request(msg) ⇒ Object



270
271
272
273
274
# File 'lib/iruby/kernel.rb', line 270

def history_request(msg)
  # we will just send back empty history for now, pending clarification
  # as requested in ipython/ipython#3806
  @session.send(:reply, :history_reply, history: [])
end

#inspect_request(msg) ⇒ Object



277
278
279
280
# File 'lib/iruby/kernel.rb', line 277

def inspect_request(msg)
  # not yet implemented. See (#119).
  @session.send(:reply, :inspect_reply, status: :ok, found: false, data: {}, metadata: {})
end

#is_complete_request(msg) ⇒ Object



236
237
238
239
240
# File 'lib/iruby/kernel.rb', line 236

def is_complete_request(msg)
  # FIXME: the code completeness should be judged by using ripper or other Ruby parser
  @session.send(:reply, :is_complete_reply,
                status: :unknown)
end

#kernel_info_request(msg) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/iruby/kernel.rb', line 149

def kernel_info_request(msg)
  @session.send(:reply, :kernel_info_reply,
                protocol_version: '5.0',
                implementation: 'iruby',
                implementation_version: IRuby::VERSION,
                language_info: {
                  name: 'ruby',
                  version: RUBY_VERSION,
                  mimetype: 'application/x-ruby',
                  file_extension: '.rb'
                },
                banner: "IRuby #{IRuby::VERSION} (with #{@session.description})",
                help_links: [
                  {
                    text: "Ruby Documentation",
                    url:  "https://ruby-doc.org/"
                  }
                ],
                status: :ok)
end

#runObject



124
125
126
127
128
129
# File 'lib/iruby/kernel.rb', line 124

def run
  send_status :starting
  while @running
    dispatch
  end
end

#send_status(status) ⇒ Object



171
172
173
174
# File 'lib/iruby/kernel.rb', line 171

def send_status(status)
  IRuby.logger.debug "Send status: #{status}"
  @session.send(:publish, :status, execution_state: status)
end

#shutdown_request(msg) ⇒ Object



264
265
266
267
# File 'lib/iruby/kernel.rb', line 264

def shutdown_request(msg)
  @session.send(:reply, :shutdown_reply, msg[:content])
  @running = false
end

#switch_backend!(backend) ⇒ true, false

Switch the backend (interactive shell) system

Parameters:

  • backend (:irb, :plain, :pry)

    Specify the backend name switched to

Returns:

  • (true, false)

    true if the switching succeeds, otherwise false



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/iruby/kernel.rb', line 92

def switch_backend!(backend)
  name = case backend
         when String, Symbol
           name = backend.downcase
         else
           name = backend
         end

  backend_class = case name
                  when :irb, :plain
                    PlainBackend
                  when :pry
                    PryBackend
                  else
                    raise ArgumentError,
                      "Unknown backend name: %p" % backend
                  end

  begin
    new_backend = backend_class.new
    @backend = new_backend
    true
  rescue Exception => e
    unless LoadError === e
      IRuby.logger.warn "Could not load #{backend_class}: " +
                        "#{e.message}\n#{e.backtrace.join("\n")}"
    end
    return false
  end
end