Nothing
#' @include options.r class_unions.r
NULL
# Create an empty named list
#' @importFrom stats setNames
namedlist <- function() setNames(list(), character(0))
# Converts something to a string no matter what
#' @importFrom utils str
resilient_to_str <- function(v) tryCatch(toString(v), error = function(e) capture.output(str(v)))
plot_builds_upon <- function(prev, current) {
if (is.null(prev)) {
return(TRUE)
}
lprev <- length(prev[[1]])
lcurrent <- length(current[[1]])
lcurrent >= lprev && identical(current[[1]][1:lprev], prev[[1]][1:lprev])
}
ask <- function(prompt = '') {
answer <- NA
while (is.na(answer)) {
answer <- switch(readline(prompt),
y = , Y = TRUE,
n = , N = FALSE,
c = NULL,
NA)
}
answer
}
format_stack <- function(calls) {
line_refs <- rep('', length(calls))
tb <- lapply(seq_along(calls), function(cl) {
call <- calls[[cl]]
# first_line, first_byte, last_line, last_byte, first_column, last_column, first_parsed, last_parsed
ref <- attr(call, 'srcref')
filename <- attr(ref, 'srcfile')$filename
if (!is.null(ref)) {
f <- ref[[1]]
l <- ref[[3]]
lines <- if (f == l) f else paste0(f, '-', l)
line_refs[[cl]] <<- paste0(' # at line ', lines, ' of file ', filename)
}
white <- paste(rep(' ', nchar(format(cl))), collapse = '')
f.call <- format(call)
line.prefix <- c(cl, rep(white, length(f.call) - 1))
paste(paste0(line.prefix, '. ', f.call), collapse = '\n')
})
paste0(tb, line_refs)
}
#' @importFrom utils capture.output
Executor <- setRefClass(
Class = 'Executor',
fields = list(
send_response = 'function',
handle_stdin = 'function',
abort_queued_messages = 'function',
execution_count = 'integer',
payload = 'list',
err = 'list',
interrupted = 'logical',
last_recorded_plot = 'recordedplotOrNULL',
current_request = 'listOrNULL',
nframe = 'integer'),
methods = list(
is_silent = function() {
current_request$content$silent
},
should_store_history = function() {
sh <- current_request$content$store_history
!is.null(sh) && sh
},
send_error_msg = function(msg) {
if (is_silent()) return()
send_response('stream', current_request, 'iopub',
list(name = 'stderr', text = msg))
},
display_data = function(data, metadata = NULL) {
if (is.null(metadata)) {
metadata <- namedlist()
}
send_response('display_data', current_request, 'iopub', list(
data = data,
metadata = metadata))
invisible(TRUE)
},
clear_output = function(wait = TRUE) {
send_response('clear_output', current_request, 'iopub', list(wait = wait))
},
page = function(mimebundle) {
payload <<- c(payload, list(c(source = 'page', mimebundle)))
},
# .Last doesn’t seem to work, so replicating behavior
quit = function(save = 'default', status = 0, runLast = TRUE) {
save <- switch(save,
default = , yes = TRUE,
no = FALSE,
ask = ask('Save workspace image? [y/n/c]: '),
stop('unknown `save` value'))
if (is.null(save)) return() # cancel
if (runLast) {
if (!is.null(.GlobalEnv$.Last)) .GlobalEnv$.Last()
if (!is.null(.GlobalEnv$.Last.sys)) .GlobalEnv$.Last.sys()
}
if (save) NULL # TODO: actually save history
payload <<- c(.self$payload, list(list(source = 'ask_exit', keepkernel = FALSE)))
},
# noninteractive
readline = function(prompt = '') {
log_debug('entering custom readline')
send_response('input_request', current_request, 'stdin',
list(prompt = prompt, password = FALSE))
# wait for 'input_reply' response message
input <- handle_stdin()
},
# noninteractive 5.0 protocol:
get_pass = function(prompt = '') {
log_debug('entering custom get_pass')
send_response('input_request', current_request, 'stdin',
list(prompt = prompt, password = TRUE))
# wait for 'input_reply' response message
log_debug('exiting custom get_pass')
input <- handle_stdin()
},
handle_error = function(e) {
estr <- resilient_to_str(e)
tryCatch({
log_debug('Error output: %s', estr)
calls <- head(sys.calls()[-seq_len(nframe + 1L)], -3)
calls <- skip_repeated(calls)
msg <- paste0(estr, 'Traceback:\n')
stack_info <- format_stack(calls)
err <<- list(ename = 'ERROR', evalue = estr, traceback = as.list(c(msg, stack_info)))
if (!is_silent()) {
send_response('error', current_request, 'iopub', err)
}
}, error = function(e2) {
log_error('Error in handle_error! %s', resilient_to_str(e2))
log_error('Caused when handling %s', estr)
})
},
send_plot = function(plotobj) {
log_debug('Sending plot...')
formats <- namedlist()
metadata <- namedlist()
for (mime in getOption('jupyter.plot_mimetypes')) {
w <- attr(plotobj, '.irkernel_width')
h <- attr(plotobj, '.irkernel_height')
ppi <- attr(plotobj, '.irkernel_ppi')
tryCatch({
formats[[mime]] <- mime2repr[[mime]](plotobj, w, h)
}, error = handle_error)
if (!identical(mime, 'text/plain')) {
metadata[[mime]] <- list(
width = w * ppi,
height = h * ppi
)
}
# Isolating SVGs (putting them in an iframe) avoids strange
# interactions with CSS on the page.
if (identical(mime, 'image/svg+xml')) {
metadata[[mime]]$isolated <- TRUE
}
}
publish_mimebundle(formats, metadata)
},
handle_display_error = function(e) {
# This is used with withCallingHandler and only has two additional
# calls at the end instead of the 3 for tryCatch... (-2 at the end)
# we also remove the tryCatch and mime2repr stuff at the head of the callstack (+7)
calls <- head(sys.calls()[-seq_len(nframe + 7L)], -2)
stack_info <- format_stack(calls)
msg <- sprintf('ERROR while rich displaying an object: %s\nTraceback:\n%s\n',
toString(e),
paste(stack_info, collapse = '\n'))
log_debug(msg)
send_error_msg(msg)
},
handle_value = function(obj, visible) {
log_debug('Value output...')
set_last_value(obj)
if (!visible) return()
mimebundle <- prepare_mimebundle_kernel(obj, .self$handle_display_error)
if (is.null(mimebundle$data)) return()
if (length(intersect(class(obj), getOption('jupyter.pager_classes'))) > 0) {
log_debug('Showing pager: %s', paste(capture.output(str(mimebundle$data)), collapse = '\n'))
page(mimebundle)
} else {
log_debug('Sending display_data: %s', paste(capture.output(str(mimebundle$data)), collapse = '\n'))
send_response('display_data', current_request, 'iopub', mimebundle)
}
},
stream = function(output, streamname) {
log_debug('Stream output: %s', output)
send_response('stream', current_request, 'iopub', list(
name = streamname,
text = paste(output, collapse = '\n')))
},
handle_graphics = function(plotobj) {
log_debug('Graphics output...')
if (!plot_builds_upon(last_recorded_plot, plotobj)) {
send_plot(last_recorded_plot)
}
# need to be set here to capture the size and have it available when the plot is sent
attr(plotobj, '.irkernel_width') <- getOption('repr.plot.width', repr_option_defaults$repr.plot.width)
attr(plotobj, '.irkernel_height') <- getOption('repr.plot.height', repr_option_defaults$repr.plot.height)
attr(plotobj, '.irkernel_ppi') <-
getOption('repr.plot.res', repr_option_defaults$repr.plot.res) /
getOption('jupyter.plot_scale', jupyter_option_defaults$jupyter.plot_scale)
last_recorded_plot <<- plotobj
},
handle_message = function(o) {
log_debug('Message output: %s', o)
stream(paste(c(conditionMessage(o), '\n'), collapse = ''), 'stderr')
},
handle_warning = function(o) {
call <- conditionCall(o)
call <- if (is.null(call)) '' else sprintf(' in %s', deparse(call)[[1]])
msg <- sprintf('Warning message%s:\n%s\n', call, dQuote(conditionMessage(o)))
log_debug('Warning output: %s', msg)
stream(msg, 'stderr')
},
execute = function(request) {
send_response('execute_input', request, 'iopub', list(
code = request$content$code,
execution_count = execution_count))
# Make the current request available to other functions
current_request <<- request
# reset ...
payload <<- list()
err <<- list()
# shade base::readline
replace_in_package('base', 'readline', .self$readline)
# shade getPass::getPass
add_to_user_searchpath('getPass', .self$get_pass, 'getPass')
# shade base::quit
replace_in_package('base', 'quit', .self$quit)
replace_in_package('base', 'q', .self$quit)
# find out stack depth in notebook cell
# TODO: maybe replace with a single call on first execute and rest reuse the value?
tryCatch(evaluate(
'stop()',
stop_on_error = 1L,
output_handler = new_output_handler(error = function(e) nframe <<- sys.nframe())))
oh <- if (is_silent()) {
new_output_handler(
text = identity,
graphics = identity,
message = identity,
warning = identity,
error = identity,
value = identity)
} else {
new_output_handler(
text = function(o) stream(o, 'stdout'),
graphics = .self$handle_graphics,
message = .self$handle_message,
warning = .self$handle_warning,
error = .self$handle_error,
value = .self$handle_value)
}
interrupted <<- FALSE
last_recorded_plot <<- NULL
log_debug('Executing code: %s', request$content$code)
warn_unicode_on_windows(request$content$code, .self$send_error_msg)
tryCatch(
evaluate(
request$content$code,
envir = .GlobalEnv,
output_handler = oh,
stop_on_error = 1L),
interrupt = function(cond) {
log_debug('Interrupt during execution')
interrupted <<- TRUE
},
error = .self$handle_error) # evaluate does not catch errors in parsing
if (!is_silent() && !is.null(last_recorded_plot)) {
send_plot(last_recorded_plot)
}
status <-
if (interrupted) 'abort'
else if (!is.null(err$ename)) 'error'
else 'ok'
log_debug('Code execution result: %s', status)
reply_content <- c(
list(
status = status,
execution_count = execution_count),
switch(status,
ok = list(payload = payload, user_expressions = namedlist()),
error = err))
send_response('execute_reply', request, 'shell', reply_content)
if (interrupted || !is.null(err$ename)) {
# errors or interrupts should interrupt all currently queued messages,
# not only the currently running one...
abort_queued_messages()
}
if (!is_silent() && should_store_history()) {
execution_count <<- execution_count + 1L
}
},
initialize = function(...) {
execution_count <<- 1L
err <<- list()
options(
pager = function(files, header, title, delete.file) {
text <- title
for (path in files) {
text <- c(text, header, readLines(path))
}
if (delete.file) file.remove(files)
data <- list('text/plain' = paste(text, collapse = '\n'))
page(list(data=data, metadata=namedlist()))
},
jupyter.base_display_func = .self$display_data,
jupyter.clear_output_func = .self$clear_output
)
# Create the shadow env here and detach it finalize
# so it's available for the whole lifetime of the kernel.
.BaseNamespaceEnv$attach(NULL, name = 'jupyter:irkernel')
# Add stuff to the user environment and configure a few options
# in the current session
init_shadowenv()
init_session()
init_null_device()
callSuper(...)
},
finalize = function() {
detach('jupyter:irkernel')
})
)
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.