Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/iruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

require 'iruby/version'
require 'iruby/jupyter'
require 'iruby/event_manager'
require 'iruby/kernel'
require 'iruby/backend'
require 'iruby/ostream'
Expand Down
40 changes: 40 additions & 0 deletions lib/iruby/event_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module IRuby
class EventManager
def initialize(available_events)
@available_events = available_events.dup.freeze
@callbacks = available_events.map {|n| [n, []] }.to_h
end

attr_reader :available_events

def register(event, &block)
check_available_event(event)
@callbacks[event] << block unless block.nil?
block
end

def unregister(event, callback)
check_available_event(event)
val = @callbacks[event].delete(callback)
unless val
raise ArgumentError,
"Given callable object #{callback} is not registered as a #{event} callback"
end
val
end

def trigger(event, *args, **kwargs)
check_available_event(event)
@callbacks[event].each do |fn|
fn.call(*args, **kwargs)
end
end

private

def check_available_event(event)
return if @callbacks.key?(event)
raise ArgumentError, "Unknown event name: #{event}", caller
end
end
end
39 changes: 34 additions & 5 deletions lib/iruby/kernel.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
module IRuby
ExecutionInfo = Struct.new(:raw_cell, :store_history, :silent)

class Kernel
RED = "\e[31m"
RESET = "\e[0m"
Expand All @@ -9,22 +11,32 @@ class Kernel

attr_reader :session

def initialize(config_file)
EVENTS = [
:pre_execute,
:pre_run_cell,
:post_run_cell,
:post_execute
].freeze

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 = 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 = create_backend
@running = true
end

attr_reader :events

def create_backend
PryBackend.new
rescue Exception => e
Expand Down Expand Up @@ -83,18 +95,31 @@ def send_status(status)

def execute_request(msg)
code = msg[:content]['code']
@execution_count += 1 if msg[:content]['store_history']
@session.send(:publish, :execute_input, code: code, execution_count: @execution_count)
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, msg[:content]['store_history'])
result = @backend.eval(code, store_history)
rescue SystemExit
content[:payload] << { source: :ask_exit }
rescue Exception => e
Expand All @@ -103,6 +128,10 @@ def execute_request(msg)
content[:status] = :error
content[:execution_count] = @execution_count
end

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

@session.send(:reply, :execute_reply, content)
@session.send(:publish, :execute_result,
data: Display.display(result),
Expand Down
6 changes: 6 additions & 0 deletions lib/iruby/session_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ def self.available?
false
end

def self.load_requirements
# Do nothing
end

def initialize(config)
@config = config
end
Expand Down Expand Up @@ -37,12 +41,14 @@ def make_rep_socket(protocol, host, port)
require_relative 'session_adapter/ffirzmq_adapter'
require_relative 'session_adapter/cztop_adapter'
require_relative 'session_adapter/pyzmq_adapter'
require_relative 'session_adapter/test_adapter'

def self.select_adapter_class(name=nil)
classes = {
'ffi-rzmq' => SessionAdapter::FfirzmqAdapter,
'cztop' => SessionAdapter::CztopAdapter,
# 'pyzmq' => SessionAdapter::PyzmqAdapter
'test' => SessionAdapter::TestAdapter,
}
if (name ||= ENV.fetch('IRUBY_SESSION_ADAPTER', nil))
cls = classes[name]
Expand Down
49 changes: 49 additions & 0 deletions lib/iruby/session_adapter/test_adapter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require 'iruby/session/mixin'

module IRuby
module SessionAdapter
class TestAdapter < BaseAdapter
include IRuby::SessionSerialize

DummySocket = Struct.new(:type, :protocol, :host, :port)

def initialize(config)
super

unless config['key'].empty? || config['signature_scheme'].empty?
unless config['signature_scheme'] =~ /\Ahmac-/
raise "Unknown signature_scheme: #{config['signature_scheme']}"
end
digest_algorithm = config['signature_scheme'][/\Ahmac-(.*)\Z/, 1]
@hmac = OpenSSL::HMAC.new(config['key'], OpenSSL::Digest.new(digest_algorithm))
end

@send_callback = nil
@recv_callback = nil
end

attr_accessor :send_callback, :recv_callback

def send(sock, data)
unless @send_callback.nil?
@send_callback.call(sock, unserialize(data))
end
end

def recv(sock)
unless @recv_callback.nil?
serialize(@recv_callback.call(sock))
end
end

def heartbeat_loop(sock)
end

private

def make_socket(type, protocol, host, port)
DummySocket.new(type, protocol, host, port)
end
end
end
end
43 changes: 43 additions & 0 deletions test/helper.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,53 @@
require "iruby"
require "iruby/logger"
require "json"
require 'multi_json'
require "pathname"
require "test/unit"
require "test/unit/rr"
require "tmpdir"


IRuby.logger = IRuby::MultiLogger.new(*Logger.new(STDERR, level: Logger::Severity::INFO))

module IRubyTest
class TestBase < Test::Unit::TestCase
def self.startup
@__config_dir = Dir.mktmpdir("iruby-test")
@__config_path = Pathname.new(@__config_dir) + "config.json"
File.write(@__config_path, {
control_port: 50160,
shell_port: 57503,
transport: "tcp",
signature_scheme: "hmac-sha256",
stdin_port: 52597,
hb_port: 42540,
ip: "127.0.0.1",
iopub_port: 40885,
key: "a0436f6c-1916-498b-8eb9-e81ab9368e84"
}.to_json)

@__original_kernel_instance = IRuby::Kernel.instance
end

def self.shutdown
FileUtils.remove_entry_secure(@__config_dir)
end

def self.test_config_filename
@__config_path.to_s
end

def teardown
IRuby::Kernel.instance = @__original_kernel_instance
end

def with_session_adapter(session_adapter_name)
IRuby::Kernel.new(self.class.test_config_filename, session_adapter_name)
$stdout = STDOUT
$stderr = STDERR
end

def assert_output(stdout=nil, stderr=nil)
flunk "assert_output requires a block to capture output." unless block_given?

Expand Down
92 changes: 92 additions & 0 deletions test/iruby/event_manager_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
module IRubyTest
class EventManagerTest < TestBase
def setup
@man = IRuby::EventManager.new([:foo, :bar])
end

def test_available_events
assert_equal([:foo, :bar],
@man.available_events)
end

sub_test_case("#register") do
sub_test_case("known event name") do
def test_register
fn = ->() {}
assert_equal(fn,
@man.register(:foo, &fn))
end
end

sub_test_case("unknown event name") do
def test_register
assert_raise_message("Unknown event name: baz") do
@man.register(:baz) {}
end
end
end
end

sub_test_case("#unregister") do
sub_test_case("no event is registered") do
def test_unregister
fn = ->() {}
assert_raise_message("Given callable object #{fn} is not registered as a foo callback") do
@man.unregister(:foo, fn)
end
end
end

sub_test_case("the registered callable is given") do
def test_unregister
results = { values: [] }
fn = ->(a) { values << a }

@man.register(:foo, &fn)

results[:retval] = @man.unregister(:foo, fn)

@man.trigger(:foo, 42)

assert_equal({
values: [],
retval: fn
},
results)
end
end
end

sub_test_case("#trigger") do
sub_test_case("no event is registered") do
def test_trigger
assert_nothing_raised do
@man.trigger(:foo)
end
end
end

sub_test_case("some events are registered") do
def test_trigger
values = []
@man.register(:foo) {|a| values << a }
@man.register(:foo) {|a| values << 10*a }
@man.register(:foo) {|a| values << 100+a }

@man.trigger(:foo, 5)

assert_equal([5, 50, 105],
values)
end
end

sub_test_case("unknown event name") do
def test_trigger
assert_raise_message("Unknown event name: baz") do
@man.trigger(:baz, 100)
end
end
end
end
end
end
Loading