From a01ee5800be8282ceb9d67e147a539463e113cfa Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Fri, 2 Oct 2015 16:56:45 +0200 Subject: [PATCH 01/94] Making work this stuff :-) stills not finished --- README.md | 5 +- ipython_kernel.lua | 1024 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 855 insertions(+), 174 deletions(-) diff --git a/README.md b/README.md index 7c16eba..da4e0de 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,9 @@ The installation process is janky right now. ipython profile create lua ``` - * Modify the profile's `ipython_config.py` to use lua_ipython_kernel. This will be at either - `~/.config/ipython/lua/ipython_config.py` or `~/.ipython/lua/ipython_config.py`: + * Modify the profile's `ipython_config.py` to use lua_ipython_kernel. This + will be at either `~/.config/ipython/profile_lua/ipython_config.py` or + `~/.ipython/profile_lua/ipython_config.py`: ``` # Configuration file for ipython. diff --git a/ipython_kernel.lua b/ipython_kernel.lua index 9ce1c44..a05708a 100644 --- a/ipython_kernel.lua +++ b/ipython_kernel.lua @@ -1,58 +1,767 @@ --[[ -lua_ipython_kernel - -Copyright (c) 2013 Evan Wies. All rights reserved. + lua_ipython_kernel + + Copyright (c) 2013 Evan Wies. All rights reserved. + Copyright (c) 2015 Francisco Zamora-Martinez. Simplified, less deps. -Released under the MIT License, see the LICENSE file. + Released under the MIT License, see the LICENSE file. -https://github.com/neomantra/lua_ipython_kernel - -usage: lua ipython_kernel.lua `connection_file` -]] + https://github.com/neomantra/lua_ipython_kernel + usage: lua ipython_kernel.lua `connection_file` +--]] if #arg ~= 1 then - io.stderr:write('usage: ipython_kernel.lua `connection_filename`\n') - os.exit(-1) + io.stderr:write('usage: ipython_kernel.lua CONNECTION_FILENAME\n') + os.exit(-1) +end + +--------------------------------------------------------------------------- +-- DKJSON library embedded here for simplicity: no external dependencies -- +--------------------------------------------------------------------------- +local json +do + -- Module options: + local always_try_using_lpeg = true + local register_global_module_table = false + local global_module_name = 'json' + + --[[ + + David Kolf's JSON module for Lua 5.1/5.2 + + Version 2.5 + + + For the documentation see the corresponding readme.txt or visit + . + + You can contact the author by sending an e-mail to 'david' at the + domain 'dkolf.de'. + + + Copyright (C) 2010-2013 David Heiko Kolf + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + --]] + + -- global dependencies: + local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset = + pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset + local error, require, pcall, select = error, require, pcall, select + local floor, huge = math.floor, math.huge + local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = + string.rep, string.gsub, string.sub, string.byte, string.char, + string.find, string.len, string.format + local strmatch = string.match + local concat = table.concat + + json = { version = "dkjson 2.5" } + + if register_global_module_table then + _G[global_module_name] = json + end + + local _ENV = nil -- blocking globals in Lua 5.2 + + pcall (function() + -- Enable access to blocked metatables. + -- Don't worry, this module doesn't change anything in them. + local debmeta = require "debug".getmetatable + if debmeta then getmetatable = debmeta end + end) + + json.null = setmetatable ({}, { + __tojson = function () return "null" end + }) + + local function isarray (tbl) + local max, n, arraylen = 0, 0, 0 + for k,v in pairs (tbl) do + if k == 'n' and type(v) == 'number' then + arraylen = v + if v > max then + max = v + end + else + if type(k) ~= 'number' or k < 1 or floor(k) ~= k then + return false + end + if k > max then + max = k + end + n = n + 1 + end + end + if max > 10 and max > arraylen and max > n * 2 then + return false -- don't create an array with too many holes + end + return true, max + end + + local escapecodes = { + ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", + ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" + } + + local function escapeutf8 (uchar) + local value = escapecodes[uchar] + if value then + return value + end + local a, b, c, d = strbyte (uchar, 1, 4) + a, b, c, d = a or 0, b or 0, c or 0, d or 0 + if a <= 0x7f then + value = a + elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then + value = (a - 0xc0) * 0x40 + b - 0x80 + elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then + value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80 + elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then + value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80 + else + return "" + end + if value <= 0xffff then + return strformat ("\\u%.4x", value) + elseif value <= 0x10ffff then + -- encode as UTF-16 surrogate pair + value = value - 0x10000 + local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400) + return strformat ("\\u%.4x\\u%.4x", highsur, lowsur) + else + return "" + end + end + + local function fsub (str, pattern, repl) + -- gsub always builds a new string in a buffer, even when no match + -- exists. First using find should be more efficient when most strings + -- don't contain the pattern. + if strfind (str, pattern) then + return gsub (str, pattern, repl) + else + return str + end + end + + local function quotestring (value) + -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js + value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8) + if strfind (value, "[\194\216\220\225\226\239]") then + value = fsub (value, "\194[\128-\159\173]", escapeutf8) + value = fsub (value, "\216[\128-\132]", escapeutf8) + value = fsub (value, "\220\143", escapeutf8) + value = fsub (value, "\225\158[\180\181]", escapeutf8) + value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8) + value = fsub (value, "\226\129[\160-\175]", escapeutf8) + value = fsub (value, "\239\187\191", escapeutf8) + value = fsub (value, "\239\191[\176-\191]", escapeutf8) + end + return "\"" .. value .. "\"" + end + json.quotestring = quotestring + + local function replace(str, o, n) + local i, j = strfind (str, o, 1, true) + if i then + return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1) + else + return str + end + end + + -- locale independent num2str and str2num functions + local decpoint, numfilter + + local function updatedecpoint () + decpoint = strmatch(tostring(0.5), "([^05+])") + -- build a filter that can be used to remove group separators + numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+" + end + + updatedecpoint() + + local function num2str (num) + return replace(fsub(tostring(num), numfilter, ""), decpoint, ".") + end + + local function str2num (str) + local num = tonumber(replace(str, ".", decpoint)) + if not num then + updatedecpoint() + num = tonumber(replace(str, ".", decpoint)) + end + return num + end + + local function addnewline2 (level, buffer, buflen) + buffer[buflen+1] = "\n" + buffer[buflen+2] = strrep (" ", level) + buflen = buflen + 2 + return buflen + end + + function json.addnewline (state) + if state.indent then + state.bufferlen = addnewline2 (state.level or 0, + state.buffer, state.bufferlen or #(state.buffer)) + end + end + + local encode2 -- forward declaration + + local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state) + local kt = type (key) + if kt ~= 'string' and kt ~= 'number' then + return nil, "type '" .. kt .. "' is not supported as a key by JSON." + end + if prev then + buflen = buflen + 1 + buffer[buflen] = "," + end + if indent then + buflen = addnewline2 (level, buffer, buflen) + end + buffer[buflen+1] = quotestring (key) + buffer[buflen+2] = ":" + return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state) + end + + local function appendcustom(res, buffer, state) + local buflen = state.bufferlen + if type (res) == 'string' then + buflen = buflen + 1 + buffer[buflen] = res + end + return buflen + end + + local function exception(reason, value, state, buffer, buflen, defaultmessage) + defaultmessage = defaultmessage or reason + local handler = state.exception + if not handler then + return nil, defaultmessage + else + state.bufferlen = buflen + local ret, msg = handler (reason, value, state, defaultmessage) + if not ret then return nil, msg or defaultmessage end + return appendcustom(ret, buffer, state) + end + end + + function json.encodeexception(reason, value, state, defaultmessage) + return quotestring("<" .. defaultmessage .. ">") + end + + encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state) + local valtype = type (value) + local valmeta = getmetatable (value) + valmeta = type (valmeta) == 'table' and valmeta -- only tables + local valtojson = valmeta and valmeta.__tojson + if valtojson then + if tables[value] then + return exception('reference cycle', value, state, buffer, buflen) + end + tables[value] = true + state.bufferlen = buflen + local ret, msg = valtojson (value, state) + if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end + tables[value] = nil + buflen = appendcustom(ret, buffer, state) + elseif value == nil then + buflen = buflen + 1 + buffer[buflen] = "null" + elseif valtype == 'number' then + local s + if value ~= value or value >= huge or -value >= huge then + -- This is the behaviour of the original JSON implementation. + s = "null" + else + s = num2str (value) + end + buflen = buflen + 1 + buffer[buflen] = s + elseif valtype == 'boolean' then + buflen = buflen + 1 + buffer[buflen] = value and "true" or "false" + elseif valtype == 'string' then + buflen = buflen + 1 + buffer[buflen] = quotestring (value) + elseif valtype == 'table' then + if tables[value] then + return exception('reference cycle', value, state, buffer, buflen) + end + tables[value] = true + level = level + 1 + local isa, n = isarray (value) + if n == 0 and valmeta and valmeta.__jsontype == 'object' then + isa = false + end + local msg + if isa then -- JSON array + buflen = buflen + 1 + buffer[buflen] = "[" + for i = 1, n do + buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + if i < n then + buflen = buflen + 1 + buffer[buflen] = "," + end + end + buflen = buflen + 1 + buffer[buflen] = "]" + else -- JSON object + local prev = false + buflen = buflen + 1 + buffer[buflen] = "{" + local order = valmeta and valmeta.__jsonorder or globalorder + if order then + local used = {} + n = #order + for i = 1, n do + local k = order[i] + local v = value[k] + if v then + used[k] = true + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + prev = true -- add a seperator before the next element + end + end + for k,v in pairs (value) do + if not used[k] then + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + else -- unordered + for k,v in pairs (value) do + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + if indent then + buflen = addnewline2 (level - 1, buffer, buflen) + end + buflen = buflen + 1 + buffer[buflen] = "}" + end + tables[value] = nil + else + return exception ('unsupported type', value, state, buffer, buflen, + "type '" .. valtype .. "' is not supported by JSON.") + end + return buflen + end + + function json.encode (value, state) + state = state or {} + local oldbuffer = state.buffer + local buffer = oldbuffer or {} + state.buffer = buffer + updatedecpoint() + local ret, msg = encode2 (value, state.indent, state.level or 0, + buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state) + if not ret then + error (msg, 2) + elseif oldbuffer == buffer then + state.bufferlen = ret + return true + else + state.bufferlen = nil + state.buffer = nil + return concat (buffer) + end + end + + local function loc (str, where) + local line, pos, linepos = 1, 1, 0 + while true do + pos = strfind (str, "\n", pos, true) + if pos and pos < where then + line = line + 1 + linepos = pos + pos = pos + 1 + else + break + end + end + return "line " .. line .. ", column " .. (where - linepos) + end + + local function unterminated (str, what, where) + return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where) + end + + local function scanwhite (str, pos) + while true do + pos = strfind (str, "%S", pos) + if not pos then return nil end + local sub2 = strsub (str, pos, pos + 1) + if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then + -- UTF-8 Byte Order Mark + pos = pos + 3 + elseif sub2 == "//" then + pos = strfind (str, "[\n\r]", pos + 2) + if not pos then return nil end + elseif sub2 == "/*" then + pos = strfind (str, "*/", pos + 2) + if not pos then return nil end + pos = pos + 2 + else + return pos + end + end + end + + local escapechars = { + ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f", + ["n"] = "\n", ["r"] = "\r", ["t"] = "\t" + } + + local function unichar (value) + if value < 0 then + return nil + elseif value <= 0x007f then + return strchar (value) + elseif value <= 0x07ff then + return strchar (0xc0 + floor(value/0x40), + 0x80 + (floor(value) % 0x40)) + elseif value <= 0xffff then + return strchar (0xe0 + floor(value/0x1000), + 0x80 + (floor(value/0x40) % 0x40), + 0x80 + (floor(value) % 0x40)) + elseif value <= 0x10ffff then + return strchar (0xf0 + floor(value/0x40000), + 0x80 + (floor(value/0x1000) % 0x40), + 0x80 + (floor(value/0x40) % 0x40), + 0x80 + (floor(value) % 0x40)) + else + return nil + end + end + + local function scanstring (str, pos) + local lastpos = pos + 1 + local buffer, n = {}, 0 + while true do + local nextpos = strfind (str, "[\"\\]", lastpos) + if not nextpos then + return unterminated (str, "string", pos) + end + if nextpos > lastpos then + n = n + 1 + buffer[n] = strsub (str, lastpos, nextpos - 1) + end + if strsub (str, nextpos, nextpos) == "\"" then + lastpos = nextpos + 1 + break + else + local escchar = strsub (str, nextpos + 1, nextpos + 1) + local value + if escchar == "u" then + value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16) + if value then + local value2 + if 0xD800 <= value and value <= 0xDBff then + -- we have the high surrogate of UTF-16. Check if there is a + -- low surrogate escaped nearby to combine them. + if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then + value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16) + if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then + value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000 + else + value2 = nil -- in case it was out of range for a low surrogate + end + end + end + value = value and unichar (value) + if value then + if value2 then + lastpos = nextpos + 12 + else + lastpos = nextpos + 6 + end + end + end + end + if not value then + value = escapechars[escchar] or escchar + lastpos = nextpos + 2 + end + n = n + 1 + buffer[n] = value + end + end + if n == 1 then + return buffer[1], lastpos + elseif n > 1 then + return concat (buffer), lastpos + else + return "", lastpos + end + end + + local scanvalue -- forward declaration + + local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta) + local len = strlen (str) + local tbl, n = {}, 0 + local pos = startpos + 1 + if what == 'object' then + setmetatable (tbl, objectmeta) + else + setmetatable (tbl, arraymeta) + end + while true do + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + local char = strsub (str, pos, pos) + if char == closechar then + return tbl, pos + 1 + end + local val1, err + val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) + if err then return nil, pos, err end + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + char = strsub (str, pos, pos) + if char == ":" then + if val1 == nil then + return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")" + end + pos = scanwhite (str, pos + 1) + if not pos then return unterminated (str, what, startpos) end + local val2 + val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) + if err then return nil, pos, err end + tbl[val1] = val2 + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + char = strsub (str, pos, pos) + else + n = n + 1 + tbl[n] = val1 + end + if char == "," then + pos = pos + 1 + end + end + end + + scanvalue = function (str, pos, nullval, objectmeta, arraymeta) + pos = pos or 1 + pos = scanwhite (str, pos) + if not pos then + return nil, strlen (str) + 1, "no valid JSON value (reached the end)" + end + local char = strsub (str, pos, pos) + if char == "{" then + return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta) + elseif char == "[" then + return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta) + elseif char == "\"" then + return scanstring (str, pos) + else + local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos) + if pstart then + local number = str2num (strsub (str, pstart, pend)) + if number then + return number, pend + 1 + end + end + pstart, pend = strfind (str, "^%a%w*", pos) + if pstart then + local name = strsub (str, pstart, pend) + if name == "true" then + return true, pend + 1 + elseif name == "false" then + return false, pend + 1 + elseif name == "null" then + return nullval, pend + 1 + end + end + return nil, pos, "no valid JSON value at " .. loc (str, pos) + end + end + + local function optionalmetatables(...) + if select("#", ...) > 0 then + return ... + else + return {__jsontype = 'object'}, {__jsontype = 'array'} + end + end + + function json.decode (str, pos, nullval, ...) + local objectmeta, arraymeta = optionalmetatables(...) + return scanvalue (str, pos, nullval, objectmeta, arraymeta) + end + + function json.use_lpeg () + local g = require ("lpeg") + + if g.version() == "0.11" then + error "due to a bug in LPeg 0.11, it cannot be used for JSON matching" + end + + local pegmatch = g.match + local P, S, R = g.P, g.S, g.R + + local function ErrorCall (str, pos, msg, state) + if not state.msg then + state.msg = msg .. " at " .. loc (str, pos) + state.pos = pos + end + return false + end + + local function Err (msg) + return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall) + end + + local SingleLineComment = P"//" * (1 - S"\n\r")^0 + local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/" + local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0 + + local PlainChar = 1 - S"\"\\\n\r" + local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars + local HexDigit = R("09", "af", "AF") + local function UTF16Surrogate (match, pos, high, low) + high, low = tonumber (high, 16), tonumber (low, 16) + if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then + return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000) + else + return false + end + end + local function UTF16BMP (hex) + return unichar (tonumber (hex, 16)) + end + local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit)) + local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP + local Char = UnicodeEscape + EscapeSequence + PlainChar + local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string") + local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0)) + local Fractal = P"." * R"09"^0 + local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1 + local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num + local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1) + local SimpleValue = Number + String + Constant + local ArrayContent, ObjectContent + + -- The functions parsearray and parseobject parse only a single value/pair + -- at a time and store them directly to avoid hitting the LPeg limits. + local function parsearray (str, pos, nullval, state) + local obj, cont + local npos + local t, nt = {}, 0 + repeat + obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state) + if not npos then break end + pos = npos + nt = nt + 1 + t[nt] = obj + until cont == 'last' + return pos, setmetatable (t, state.arraymeta) + end + + local function parseobject (str, pos, nullval, state) + local obj, key, cont + local npos + local t = {} + repeat + key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state) + if not npos then break end + pos = npos + t[key] = obj + until cont == 'last' + return pos, setmetatable (t, state.objectmeta) + end + + local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected") + local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected") + local Value = Space * (Array + Object + SimpleValue) + local ExpectedValue = Value + Space * Err "value expected" + ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() + local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue) + ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() + local DecodeValue = ExpectedValue * g.Cp () + + function json.decode (str, pos, nullval, ...) + local state = {} + state.objectmeta, state.arraymeta = optionalmetatables(...) + local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state) + if state.msg then + return nil, state.pos, state.msg + else + return obj, retpos + end + end + + -- use this function only once: + json.use_lpeg = function () return json end + + json.using_lpeg = true + + return json -- so you can get the module using json = require "dkjson".use_lpeg() + end + + if always_try_using_lpeg then + pcall (json.use_lpeg) + end end -local json = require 'dkjson' +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- -- our kernel's state local kernel = {} -- load connection object info from JSON file do - local connection_file = io.open(arg[1]) - if not connection_file then - io.stderr:write('couldn not open connection file "', arg[1], '")": ', err, '\n') - os.exit(-1) - end - - local connection_json, err = connection_file:read('*a') - if not connection_json then - io.stderr:write('couldn not read connection file "', arg[1], '")": ', err, '\n') - os.exit(-1) - end - - kernel.connection_obj = json.decode(connection_json) - if not kernel.connection_obj then - io.stderr:write('connection file is missing connection object\n') - os.exit(-1) - end - connection_file:close() + local connection_file = assert( io.open(arg[1]) ) + + local connection_json = assert( connection_file:read('*a') ) + + kernel.connection_obj = assert( json.decode(connection_json) ) + connection_file:close() end -local zmq = require 'zmq' -local zmq_poller = require 'zmq/poller' +local zmq = require 'lzmq' +local zmq_poller = require 'lzmq.poller' local z_NOBLOCK, z_POLLIN = zmq.NOBLOCK, zmq.POLL_IN local z_RCVMORE, z_SNDMORE = zmq.RCVMORE, zmq.SNDMORE -local uuid = require 'uuid' +-- local uuid = require 'uuid' -- TODO: randomseed or luasocket or something else -local username = os.getenv('USER') +local MSG_DELIM = '' +local username = os.getenv('USER') or "unknown" @@ -60,80 +769,69 @@ local username = os.getenv('USER') -- IPython Message ("ipmsg") functions local function ipmsg_to_table(parts) - local ipmsg = { ids = {}, blobs = {} } - - local i = 1 - while i <= #parts and parts[i] ~= '' do - ipmsg.ids[#ipmsg.ids + 1] = parts[i] - i = i + 1 - end - i = i + 1 - ipmsg.hmac = parts[i] ; i = i + 1 - ipmsg.header = parts[i] ; i = i + 1 - ipmsg.parent_header = parts[i] ; i = i + 1 - ipmsg.metadata = parts[i] ; i = i + 1 - ipmsg.content = parts[i] ; i = i + 1 - - while i <= #parts do - ipmsg.blobs[#ipmsg.blobs + 1] = parts[i] - i = i + 1 - end - - return ipmsg + local ipmsg = { ids = {}, blobs = {} } + + local i = 1 + while i <= #parts and parts[i] ~= MSG_DELIM do + ipmsg.ids[#ipmsg.ids + 1] = parts[i] + i = i + 1 + end + i = i + 1 + ipmsg.hmac = parts[i] ; i = i + 1 + ipmsg.header = parts[i] ; i = i + 1 + ipmsg.parent_header = parts[i] ; i = i + 1 + ipmsg.metadata = parts[i] ; i = i + 1 + ipmsg.content = parts[i] ; i = i + 1 + + while i <= #parts do + ipmsg.blobs[#ipmsg.blobs + 1] = parts[i] + i = i + 1 + end + + return ipmsg end local function ipmsg_header(session, msg_type) - return json.encode({ - msg_id = uuid.new(), -- TODO: randomness warning - username = username, - date = os.date('%Y-%m-%dT%h:%M:%s.000000'), -- TODO milliseconds - session = session, - msg_type = msg_type, - }) -end - - -local function ipmsg_recv(sock) - -- TODO: error handling - local parts, err = {} - parts[1], err = sock:recv() - while sock:getopt(z_RCVMORE) == 1 do - parts[#parts + 1], err = sock:recv() - end - return parts, err + return json.encode({ + msg_id = io.popen("uuidgen"):read("*l"), -- TODO: randomness warning: uuid.new() + username = username, + session = session, + msg_type = msg_type, + version = '5.0', + }) end local function ipmsg_send(sock, ids, hmac, hdr, p_hdr, meta, content, blobs) - if type(ids) == 'table' then - for _, v in ipairs(ids) do - sock:send(v, z_SNDMORE) - end - else - sock:send(ids, z_SNDMORE) - end - sock:send('', z_SNDMORE) - sock:send(hmac, z_SNDMORE) - sock:send(hdr, z_SNDMORE) - sock:send(p_hdr, z_SNDMORE) - sock:send(meta, z_SNDMORE) - if blobs then - sock:send(content, z_SNDMORE) - if type(blobs) == 'table' then - for i, v in ipairs(blobs) do - if i == #blobs then - sock:send(v) - else - sock:send(v, z_SNDMORE) - end - end - else - sock:send(blobs) - end - else - sock:send(content) - end + if type(ids) == 'table' then + for _, v in ipairs(ids) do + sock:send(v, z_SNDMORE) + end + else + sock:send(ids, z_SNDMORE) + end + sock:send(MSG_DELIM, z_SNDMORE) + sock:send(hmac, z_SNDMORE) + sock:send(hdr, z_SNDMORE) + sock:send(p_hdr, z_SNDMORE) + sock:send(meta, z_SNDMORE) + if blobs then + sock:send(content, z_SNDMORE) + if type(blobs) == 'table' then + for i, v in ipairs(blobs) do + if i == #blobs then + sock:send(v) + else + sock:send(v, z_SNDMORE) + end + end + else + sock:send(blobs) + end + else + sock:send(content) + end end @@ -142,83 +840,66 @@ end -- ZMQ Read Handlers local function on_hb_read( sock ) - -- read the data and send a pong - local data, err = sock:recv(zmq.NOBLOCK) - if not data then - assert(err == 'timeout', 'Bad error on zmq socket.') - -- TODO return sock_blocked(on_sock_recv, z_POLLIN) - assert(false, "bad hb_read_data") - end - sock:send('pong') + -- read the data and send a pong + local data = assert( sock:recv(zmq.NOBLOCK) ) + -- TODO: handle 'timeout' error + sock:send('pong') end local function on_control_read( sock ) - -- read the data and send a pong - local data, err = sock:recv(zmq.NOBLOCK) - if not data then - assert(err == 'timeout', 'Bad error on zmq socket.') - -- TODO return sock_blocked(on_sock_recv, z_POLLIN) - assert(false, "bad hb_read_data") - end --- print('control', data) + local data = assert( sock:recv(zmq.NOBLOCK) ) + -- TODO: handle 'timeout' error + print("CTRL", data) end local function on_stdin_read( sock ) - -- read the data and send a pong - local data, err = sock:recv(zmq.NOBLOCK) - if not data then - assert(err == 'timeout', 'Bad error on zmq socket.') - -- TODO return sock_blocked(on_sock_recv, z_POLLIN) - assert(false, "bad hb_read_data") - end --- print('stdin', data) + local data = assert( sock:recv(zmq.NOBLOCK) ) + -- TODO: handle 'timeout' error + print("STDIN", data) end local function on_shell_read( sock ) + -- TODO: error handling + local ipmsg = assert( sock:recv_all() ) + + local msg = ipmsg_to_table(ipmsg) - local ipmsg, err = ipmsg_recv(sock) - local msg = ipmsg_to_table(ipmsg) - - for k, v in pairs(msg) do print(k,v) end + for k, v in pairs(msg) do print(k,v) end - local header_obj = json.decode(msg.header) - if header_obj.msg_type == 'kernel_info_request' then - local header = ipmsg_header( header_obj.session, 'kernel_info_reply' ) - local content = json.encode({ - protocol_version = {4, 0}, - language_version = {5, 1}, - language = 'lua', - }) + local header_obj = json.decode(msg.header) + if header_obj.msg_type == 'kernel_info_request' then + local header = ipmsg_header( header_obj.session, 'kernel_info_reply' ) + local major,minor = _VERSION:match("(%d+)%.(%d+)") + local content = json.encode({ + protocol_version = {4, 0}, + language_version = {tonumber(major), tonumber(minor)}, + language = 'lua', + }) - ipmsg_send(sock, header_obj.session, '', header, msg.header, '{}', content) + ipmsg_send(sock, header_obj.session, '', header, msg.header, '{}', content) - elseif header_obj.msg_type == 'execute_request' then + elseif header_obj.msg_type == 'execute_request' then - local header = ipmsg_header( header_obj.session, 'execute_reply' ) - kernel.execution_count = kernel.execution_count + 1 - local content = json.encode({ - status = 'ok', - execution_count = kernel.execution_count, - }) + local header = ipmsg_header( header_obj.session, 'execute_reply' ) + kernel.execution_count = kernel.execution_count + 1 + local content = json.encode({ + status = 'ok', + execution_count = kernel.execution_count, + }) - ipmsg_send(sock, header_obj.session, '', header, msg.header, '{}', content) + ipmsg_send(sock, header_obj.session, '', header, msg.header, '{}', content) - end + end end local function on_iopub_read( sock ) - -- read the data and send a pong - local data, err = sock:recv(zmq.NOBLOCK) - if not data then - assert(err == 'timeout', 'Bad error on zmq socket.') - -- TODO return sock_blocked(on_sock_recv, z_POLLIN) - assert(false, "bad hb_read_data") - end --- print('iopub', data) - + -- read the data and send a pong + local data = assert( sock:recv(zmq.NOBLOCK) ) + -- TODO: handle timeout error + print("PUB", data) end @@ -226,31 +907,30 @@ end -- SETUP local kernel_sockets = { - { name = 'heartbeat_sock', sock_type = zmq.REP, port = 'hb', handler = on_hb_read }, - { name = 'control_sock', sock_type = zmq.ROUTER, port = 'control', handler = on_control_read }, - { name = 'stdin_sock', sock_type = zmq.ROUTER, port = 'stdin', handler = on_stdin_read }, - { name = 'shell_sock', sock_type = zmq.ROUTER, port = 'shell', handler = on_shell_read }, - { name = 'iopub_sock', sock_type = zmq.PUB, port = 'iopub', handler = on_iopub_read }, + { name = 'heartbeat_sock', sock_type = zmq.REP, port = 'hb', handler = on_hb_read }, + { name = 'control_sock', sock_type = zmq.ROUTER, port = 'control', handler = on_control_read }, + { name = 'stdin_sock', sock_type = zmq.ROUTER, port = 'stdin', handler = on_stdin_read }, + { name = 'shell_sock', sock_type = zmq.ROUTER, port = 'shell', handler = on_shell_read }, + { name = 'iopub_sock', sock_type = zmq.PUB, port = 'iopub', handler = on_iopub_read }, } -local z_ctx = zmq.init() +local z_ctx = zmq.context() local z_poller = zmq_poller(#kernel_sockets) for _, v in ipairs(kernel_sockets) do - -- TODO: error handling in here - local sock = z_ctx:socket(v.sock_type) + -- TODO: error handling in here + local sock = assert( z_ctx:socket(v.sock_type) ) - local conn_obj = kernel.connection_obj - local addr = string.format('%s://%s:%s', - conn_obj.transport, - conn_obj.ip, - conn_obj[v.port..'_port']) + local conn_obj = kernel.connection_obj + local addr = string.format('%s://%s:%s', + conn_obj.transport, + conn_obj.ip, + conn_obj[v.port..'_port']) --- io.stderr:write(string.format('binding %s to %s\n', v.name, addr)) - sock:bind(addr) + assert( sock:bind(addr) ) - z_poller:add(sock, zmq.POLLIN, v.handler) + z_poller:add(sock, zmq.POLLIN, v.handler) - kernel[v.name] = sock + kernel[v.name] = sock end kernel.execution_count = 0 @@ -263,6 +943,6 @@ kernel.execution_count = 0 z_poller:start() for _, v in ipairs(kernel_sockets) do - kernel[v.name]:close() + kernel[v.name]:close() end z_ctx:term() From 91d3eaf6b35d4e461beb93190eb4f498f5c39900 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Fri, 2 Oct 2015 19:35:10 +0200 Subject: [PATCH 02/94] More... --- ipython_kernel.lua | 59 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/ipython_kernel.lua b/ipython_kernel.lua index a05708a..2772fca 100644 --- a/ipython_kernel.lua +++ b/ipython_kernel.lua @@ -763,8 +763,6 @@ local z_RCVMORE, z_SNDMORE = zmq.RCVMORE, zmq.SNDMORE local MSG_DELIM = '' local username = os.getenv('USER') or "unknown" - - ------------------------------------------------------------------------------- -- IPython Message ("ipmsg") functions @@ -836,6 +834,39 @@ end +------------------------------------------------------------------------------- + +-- environment where all code is executed +local env_session +local env_header +local env_source +local function pubstr(str) + print("PUBPUB", str) + local header = ipmsg_header( env_session, 'display_data' ) + local content = json.encode{ + source = env_source, + data = { ['text/plain'] = str }, + -- metadata = { ['text/plain'] = {} }, + } + print("CONTENT", content) + ipmsg_send(kernel.iopub_sock, env_session, '', header, env_header, '{}', content) +end +local env = {} +for k,v in pairs(_G) do env[k] = v end +env.args = nil +env.print = function(...) + local str = table.concat(table.pack(...),"\t") + pubstr(str) +end +env.io.write = function(...) + local str = table.concat(table.pack(...)) + pubstring(str) +end + +local function add_return(code) + return code +end + ------------------------------------------------------------------------------- -- ZMQ Read Handlers @@ -858,8 +889,6 @@ local function on_stdin_read( sock ) print("STDIN", data) end - - local function on_shell_read( sock ) -- TODO: error handling local ipmsg = assert( sock:recv_all() ) @@ -887,10 +916,30 @@ local function on_shell_read( sock ) local content = json.encode({ status = 'ok', execution_count = kernel.execution_count, + payload = {}, + user_expresions = {}, }) ipmsg_send(sock, header_obj.session, '', header, msg.header, '{}', content) - + + local header = ipmsg_header( header_obj.session, 'status' ) + local content = json.encode{ execution_state='busy' } + ipmsg_send(kernel.iopub_sock, header_obj.session, '', + header, '{}', '{}', content) + local msg_content = json.decode(msg.content) + local code = msg_content.code + env_header = msg.header + env_session = header_obj.session + env_source = code + local f = load(add_return(code), nil, nil, env) + local out = f() + if out then + -- TODO: show output of the function + end + + local content = json.encode{ execution_state='idle' } + ipmsg_send(kernel.iopub_sock, header_obj.session, '', + header, '{}', '{}', content) end end From 2c9c0da450de0acd747e4c087aa8c919848cf9e9 Mon Sep 17 00:00:00 2001 From: Francisco Zamora-Martinez Date: Sat, 3 Oct 2015 00:50:32 +0200 Subject: [PATCH 03/94] Update README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index da4e0de..6641ad3 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,11 @@ This is a kernel to support Lua with [IPython](http://ipython.org). It is pure The following Lua libraries are required: - * [zeromq](http://zeromq.org/bindings:lua) - * [dkjson](http://dkolf.de/src/dkjson-lua.fsl/home) - * [uuid](https://github.com/Tieske/uuid) + * [lzmq](https://github.com/zeromq/lzmq) zeromq for Lua + +The code for Lua JSON of dkjs habeen embedded into the +Kenel source. Uuid's are generated using SO commands via +io.popen. Here's how to install via [LuaRocks](http://luarocks.org/): From 04fafa224e4dcb873223cc4e17a625facb1bb201 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 12:18:50 +0200 Subject: [PATCH 04/94] Code refactoring --- LICENSE | 24 +- README.md | 81 ++-- iPyLua/dkjson.lua | 596 +++++++++++++++++++++++++++ iPyLua/uuid.lua | 196 +++++++++ iPyLuaKernel.lua | 277 +++++++++++++ ipython_kernel.lua | 997 --------------------------------------------- 6 files changed, 1131 insertions(+), 1040 deletions(-) create mode 100644 iPyLua/dkjson.lua create mode 100644 iPyLua/uuid.lua create mode 100644 iPyLuaKernel.lua delete mode 100644 ipython_kernel.lua diff --git a/LICENSE b/LICENSE index b8a3c46..ca8d8da 100644 --- a/LICENSE +++ b/LICENSE @@ -1,9 +1,23 @@ -lua_ipython_kernel - +iPyLua +Copyright (c) 2015 Francisco Zamora-Martinez. Simplified, less deps and making +it work. + +Original name and copyright: lua_ipython_kernel Copyright (c) 2013 Evan Wies. All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 6641ad3..01cd0d2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -# Lua IPython Kernel +# iPyLua: Lua IPython Kernel -This is a kernel to support Lua with [IPython](http://ipython.org). It is pure Lua and should work with both Lua and LuaJIT. +This is a kernel to support Lua with [IPython](http://ipython.org). It is pure +Lua and should work with both Lua and LuaJIT. ## Requirements @@ -9,51 +10,35 @@ The following Lua libraries are required: * [lzmq](https://github.com/zeromq/lzmq) zeromq for Lua -The code for Lua JSON of dkjs habeen embedded into the -Kenel source. Uuid's are generated using SO commands via -io.popen. - -Here's how to install via [LuaRocks](http://luarocks.org/): - -``` -# You need zeromq... on OSX I use Homebrew: -# brew install zeromq -# On Ubuntu: -# sudo apt-get install libzmq-dev - -# The LuaRocks -sudo luarocks install https://raw.github.com/Neopallium/lua-zmq/master/rockspecs/lua-zmq-scm-1.rockspec -sudo luarocks install dkjson -sudo luarocks install uuid -``` - -Of course you also need to [install IPython](http://ipython.org/install.html)... - +The code for Lua JSON of [dkjson](http://dkolf.de/src/dkjson-lua.fsl/home) and +[uuid](https://github.com/Tieske/uuid/blob/master/src/uuid.lua) has been copied +into this module to avoid external dependencies. ## Installation The installation process is janky right now. - * Install the Requirements above + * Install the Requirements: [zeromq](http://zeromq.org/) and + [lzmq](https://github.com/zeromq/lzmq). - * Create a profile with IPython + * Create a profile with IPython: ``` -ipython profile create lua +$ ipython profile create lua ``` - * Modify the profile's `ipython_config.py` to use lua_ipython_kernel. This + * Modify the profile's `ipython_config.py` to use iPyLua. This will be at either `~/.config/ipython/profile_lua/ipython_config.py` or `~/.ipython/profile_lua/ipython_config.py`: -``` +```Python # Configuration file for ipython. c = get_config() c.KernelManager.kernel_cmd = [ - "luajit", # select your Lua interpreter here - "ipython_kernel.lua", # probably need full path + "luajit", # select your Lua interpreter here (lua5.2, lua5.1, luajit) + "iPyLua/iPyLuaKernel.lua", # probably need full path "{connection_file}" ] @@ -65,9 +50,9 @@ c.Session.keyfile = b'' * Invoke IPython with this Lua kernel: ``` -ipython console --profile lua +$ ipython console --profile lua # or -ipython notebook --profile lua +$ ipython notebook --profile lua ``` ## TODO @@ -81,23 +66,43 @@ ipython notebook --profile lua ## Acknowledgements -Thanks to Andrew Gibiansky for his [IHaskell article](http://andrew.gibiansky.com/blog/ipython/ipython-kernels/) that inspired this. +Thanks to Evan Wies who has written the original code +[lua_ipython_kernel](https://github.com/neomantra/lua_ipython_kernel). -Thanks to the makers of the dependencies of this library, who made this pretty easy to create: [Robert Jakabosky](https://github.com/Neopallium), [David Kolf](http://dkolf.de/src/dkjson-lua.fsl/home), and [Thijs Schreijer](https://github.com/Tieske). +Thanks to Andrew Gibiansky for his +[IHaskell article](http://andrew.gibiansky.com/blog/ipython/ipython-kernels/) +that inspired this. -And of course thanks to the [IPython folks ](http://ipython.org/citing.html). +Thanks to the makers of the dependencies of this library, who made this pretty +easy to create: [Robert Jakabosky](https://github.com/Neopallium), +[David Kolf](http://dkolf.de/src/dkjson-lua.fsl/home), and +[Thijs Schreijer](https://github.com/Tieske). +And of course thanks to the [IPython folks ](http://ipython.org/citing.html). ## LICENSE -**lua_ipython_kernel** is distributed under the [MIT License](http://opensource.org/licenses/mit-license.php). +**lua_ipython_kernel** is distributed under the + [MIT License](http://opensource.org/licenses/mit-license.php). > lua_ipython_kernel > > Copyright (c) 2013 Evan Wies. All rights reserved. > -> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: > -> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. > -> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. diff --git a/iPyLua/dkjson.lua b/iPyLua/dkjson.lua new file mode 100644 index 0000000..b394b2c --- /dev/null +++ b/iPyLua/dkjson.lua @@ -0,0 +1,596 @@ +--[[ + + David Kolf's JSON module for Lua 5.1/5.2 + + Version 2.5 + + + For the documentation see the corresponding readme.txt or visit + . + + You can contact the author by sending an e-mail to 'david' at the + domain 'dkolf.de'. + + + Copyright (C) 2010-2013 David Heiko Kolf + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +--]] + +-- Use original module in case it has been loaded with a previous require or it +-- has been preload by the user +local json = package.loaded["dkjson"] or package.preload["dkjson"] +if json then return json end + +-- global dependencies: +local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset = + pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset +local error, require, pcall, select = error, require, pcall, select +local floor, huge = math.floor, math.huge +local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = + string.rep, string.gsub, string.sub, string.byte, string.char, +string.find, string.len, string.format +local strmatch = string.match +local concat = table.concat + +local json = { version = "dkjson 2.5" } + +local _ENV = nil -- blocking globals in Lua 5.2 + +pcall (function() + -- Enable access to blocked metatables. + -- Don't worry, this module doesn't change anything in them. + local debmeta = require "debug".getmetatable + if debmeta then getmetatable = debmeta end +end) + +json.null = setmetatable ({}, { + __tojson = function () return "null" end +}) + +local function isarray (tbl) + local max, n, arraylen = 0, 0, 0 + for k,v in pairs (tbl) do + if k == 'n' and type(v) == 'number' then + arraylen = v + if v > max then + max = v + end + else + if type(k) ~= 'number' or k < 1 or floor(k) ~= k then + return false + end + if k > max then + max = k + end + n = n + 1 + end + end + if max > 10 and max > arraylen and max > n * 2 then + return false -- don't create an array with too many holes + end + return true, max +end + +local escapecodes = { + ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", + ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" +} + +local function escapeutf8 (uchar) + local value = escapecodes[uchar] + if value then + return value + end + local a, b, c, d = strbyte (uchar, 1, 4) + a, b, c, d = a or 0, b or 0, c or 0, d or 0 + if a <= 0x7f then + value = a + elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then + value = (a - 0xc0) * 0x40 + b - 0x80 + elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then + value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80 + elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then + value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80 + else + return "" + end + if value <= 0xffff then + return strformat ("\\u%.4x", value) + elseif value <= 0x10ffff then + -- encode as UTF-16 surrogate pair + value = value - 0x10000 + local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400) + return strformat ("\\u%.4x\\u%.4x", highsur, lowsur) + else + return "" + end +end + +local function fsub (str, pattern, repl) + -- gsub always builds a new string in a buffer, even when no match + -- exists. First using find should be more efficient when most strings + -- don't contain the pattern. + if strfind (str, pattern) then + return gsub (str, pattern, repl) + else + return str + end +end + +local function quotestring (value) + -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js + value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8) + if strfind (value, "[\194\216\220\225\226\239]") then + value = fsub (value, "\194[\128-\159\173]", escapeutf8) + value = fsub (value, "\216[\128-\132]", escapeutf8) + value = fsub (value, "\220\143", escapeutf8) + value = fsub (value, "\225\158[\180\181]", escapeutf8) + value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8) + value = fsub (value, "\226\129[\160-\175]", escapeutf8) + value = fsub (value, "\239\187\191", escapeutf8) + value = fsub (value, "\239\191[\176-\191]", escapeutf8) + end + return "\"" .. value .. "\"" +end +json.quotestring = quotestring + +local function replace(str, o, n) + local i, j = strfind (str, o, 1, true) + if i then + return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1) + else + return str + end +end + +-- locale independent num2str and str2num functions +local decpoint, numfilter + +local function updatedecpoint () + decpoint = strmatch(tostring(0.5), "([^05+])") + -- build a filter that can be used to remove group separators + numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+" +end + +updatedecpoint() + +local function num2str (num) + return replace(fsub(tostring(num), numfilter, ""), decpoint, ".") +end + +local function str2num (str) + local num = tonumber(replace(str, ".", decpoint)) + if not num then + updatedecpoint() + num = tonumber(replace(str, ".", decpoint)) + end + return num +end + +local function addnewline2 (level, buffer, buflen) + buffer[buflen+1] = "\n" + buffer[buflen+2] = strrep (" ", level) + buflen = buflen + 2 + return buflen +end + +function json.addnewline (state) + if state.indent then + state.bufferlen = addnewline2 (state.level or 0, + state.buffer, state.bufferlen or #(state.buffer)) + end +end + +local encode2 -- forward declaration + +local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state) + local kt = type (key) + if kt ~= 'string' and kt ~= 'number' then + return nil, "type '" .. kt .. "' is not supported as a key by JSON." + end + if prev then + buflen = buflen + 1 + buffer[buflen] = "," + end + if indent then + buflen = addnewline2 (level, buffer, buflen) + end + buffer[buflen+1] = quotestring (key) + buffer[buflen+2] = ":" + return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state) +end + +local function appendcustom(res, buffer, state) + local buflen = state.bufferlen + if type (res) == 'string' then + buflen = buflen + 1 + buffer[buflen] = res + end + return buflen +end + +local function exception(reason, value, state, buffer, buflen, defaultmessage) + defaultmessage = defaultmessage or reason + local handler = state.exception + if not handler then + return nil, defaultmessage + else + state.bufferlen = buflen + local ret, msg = handler (reason, value, state, defaultmessage) + if not ret then return nil, msg or defaultmessage end + return appendcustom(ret, buffer, state) + end +end + +function json.encodeexception(reason, value, state, defaultmessage) + return quotestring("<" .. defaultmessage .. ">") +end + +encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state) + local valtype = type (value) + local valmeta = getmetatable (value) + valmeta = type (valmeta) == 'table' and valmeta -- only tables + local valtojson = valmeta and valmeta.__tojson + if valtojson then + if tables[value] then + return exception('reference cycle', value, state, buffer, buflen) + end + tables[value] = true + state.bufferlen = buflen + local ret, msg = valtojson (value, state) + if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end + tables[value] = nil + buflen = appendcustom(ret, buffer, state) + elseif value == nil then + buflen = buflen + 1 + buffer[buflen] = "null" + elseif valtype == 'number' then + local s + if value ~= value or value >= huge or -value >= huge then + -- This is the behaviour of the original JSON implementation. + s = "null" + else + s = num2str (value) + end + buflen = buflen + 1 + buffer[buflen] = s + elseif valtype == 'boolean' then + buflen = buflen + 1 + buffer[buflen] = value and "true" or "false" + elseif valtype == 'string' then + buflen = buflen + 1 + buffer[buflen] = quotestring (value) + elseif valtype == 'table' then + if tables[value] then + return exception('reference cycle', value, state, buffer, buflen) + end + tables[value] = true + level = level + 1 + local isa, n = isarray (value) + if n == 0 and valmeta and valmeta.__jsontype == 'object' then + isa = false + end + local msg + if isa then -- JSON array + buflen = buflen + 1 + buffer[buflen] = "[" + for i = 1, n do + buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + if i < n then + buflen = buflen + 1 + buffer[buflen] = "," + end + end + buflen = buflen + 1 + buffer[buflen] = "]" + else -- JSON object + local prev = false + buflen = buflen + 1 + buffer[buflen] = "{" + local order = valmeta and valmeta.__jsonorder or globalorder + if order then + local used = {} + n = #order + for i = 1, n do + local k = order[i] + local v = value[k] + if v then + used[k] = true + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + prev = true -- add a seperator before the next element + end + end + for k,v in pairs (value) do + if not used[k] then + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + else -- unordered + for k,v in pairs (value) do + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + if indent then + buflen = addnewline2 (level - 1, buffer, buflen) + end + buflen = buflen + 1 + buffer[buflen] = "}" + end + tables[value] = nil + else + return exception ('unsupported type', value, state, buffer, buflen, + "type '" .. valtype .. "' is not supported by JSON.") + end + return buflen +end + +function json.encode (value, state) + state = state or {} + local oldbuffer = state.buffer + local buffer = oldbuffer or {} + state.buffer = buffer + updatedecpoint() + local ret, msg = encode2 (value, state.indent, state.level or 0, + buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state) + if not ret then + error (msg, 2) + elseif oldbuffer == buffer then + state.bufferlen = ret + return true + else + state.bufferlen = nil + state.buffer = nil + return concat (buffer) + end +end + +local function loc (str, where) + local line, pos, linepos = 1, 1, 0 + while true do + pos = strfind (str, "\n", pos, true) + if pos and pos < where then + line = line + 1 + linepos = pos + pos = pos + 1 + else + break + end + end + return "line " .. line .. ", column " .. (where - linepos) +end + +local function unterminated (str, what, where) + return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where) +end + +local function scanwhite (str, pos) + while true do + pos = strfind (str, "%S", pos) + if not pos then return nil end + local sub2 = strsub (str, pos, pos + 1) + if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then + -- UTF-8 Byte Order Mark + pos = pos + 3 + elseif sub2 == "//" then + pos = strfind (str, "[\n\r]", pos + 2) + if not pos then return nil end + elseif sub2 == "/*" then + pos = strfind (str, "*/", pos + 2) + if not pos then return nil end + pos = pos + 2 + else + return pos + end + end +end + +local escapechars = { + ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f", + ["n"] = "\n", ["r"] = "\r", ["t"] = "\t" +} + +local function unichar (value) + if value < 0 then + return nil + elseif value <= 0x007f then + return strchar (value) + elseif value <= 0x07ff then + return strchar (0xc0 + floor(value/0x40), + 0x80 + (floor(value) % 0x40)) + elseif value <= 0xffff then + return strchar (0xe0 + floor(value/0x1000), + 0x80 + (floor(value/0x40) % 0x40), + 0x80 + (floor(value) % 0x40)) + elseif value <= 0x10ffff then + return strchar (0xf0 + floor(value/0x40000), + 0x80 + (floor(value/0x1000) % 0x40), + 0x80 + (floor(value/0x40) % 0x40), + 0x80 + (floor(value) % 0x40)) + else + return nil + end +end + +local function scanstring (str, pos) + local lastpos = pos + 1 + local buffer, n = {}, 0 + while true do + local nextpos = strfind (str, "[\"\\]", lastpos) + if not nextpos then + return unterminated (str, "string", pos) + end + if nextpos > lastpos then + n = n + 1 + buffer[n] = strsub (str, lastpos, nextpos - 1) + end + if strsub (str, nextpos, nextpos) == "\"" then + lastpos = nextpos + 1 + break + else + local escchar = strsub (str, nextpos + 1, nextpos + 1) + local value + if escchar == "u" then + value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16) + if value then + local value2 + if 0xD800 <= value and value <= 0xDBff then + -- we have the high surrogate of UTF-16. Check if there is a + -- low surrogate escaped nearby to combine them. + if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then + value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16) + if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then + value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000 + else + value2 = nil -- in case it was out of range for a low surrogate + end + end + end + value = value and unichar (value) + if value then + if value2 then + lastpos = nextpos + 12 + else + lastpos = nextpos + 6 + end + end + end + end + if not value then + value = escapechars[escchar] or escchar + lastpos = nextpos + 2 + end + n = n + 1 + buffer[n] = value + end + end + if n == 1 then + return buffer[1], lastpos + elseif n > 1 then + return concat (buffer), lastpos + else + return "", lastpos + end +end + +local scanvalue -- forward declaration + +local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta) + local len = strlen (str) + local tbl, n = {}, 0 + local pos = startpos + 1 + if what == 'object' then + setmetatable (tbl, objectmeta) + else + setmetatable (tbl, arraymeta) + end + while true do + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + local char = strsub (str, pos, pos) + if char == closechar then + return tbl, pos + 1 + end + local val1, err + val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) + if err then return nil, pos, err end + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + char = strsub (str, pos, pos) + if char == ":" then + if val1 == nil then + return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")" + end + pos = scanwhite (str, pos + 1) + if not pos then return unterminated (str, what, startpos) end + local val2 + val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) + if err then return nil, pos, err end + tbl[val1] = val2 + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + char = strsub (str, pos, pos) + else + n = n + 1 + tbl[n] = val1 + end + if char == "," then + pos = pos + 1 + end + end +end + +scanvalue = function (str, pos, nullval, objectmeta, arraymeta) + pos = pos or 1 + pos = scanwhite (str, pos) + if not pos then + return nil, strlen (str) + 1, "no valid JSON value (reached the end)" + end + local char = strsub (str, pos, pos) + if char == "{" then + return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta) + elseif char == "[" then + return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta) + elseif char == "\"" then + return scanstring (str, pos) + else + local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos) + if pstart then + local number = str2num (strsub (str, pstart, pend)) + if number then + return number, pend + 1 + end + end + pstart, pend = strfind (str, "^%a%w*", pos) + if pstart then + local name = strsub (str, pstart, pend) + if name == "true" then + return true, pend + 1 + elseif name == "false" then + return false, pend + 1 + elseif name == "null" then + return nullval, pend + 1 + end + end + return nil, pos, "no valid JSON value at " .. loc (str, pos) + end +end + +local function optionalmetatables(...) + if select("#", ...) > 0 then + return ... + else + return {__jsontype = 'object'}, {__jsontype = 'array'} + end +end + +function json.decode (str, pos, nullval, ...) + local objectmeta, arraymeta = optionalmetatables(...) + return scanvalue (str, pos, nullval, objectmeta, arraymeta) +end + +return json diff --git a/iPyLua/uuid.lua b/iPyLua/uuid.lua new file mode 100644 index 0000000..49951f1 --- /dev/null +++ b/iPyLua/uuid.lua @@ -0,0 +1,196 @@ +--------------------------------------------------------------------------------------- +-- Copyright 2012 Rackspace (original), 2013 Thijs Schreijer (modifications) +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS-IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- see http://www.ietf.org/rfc/rfc4122.txt +-- +-- Note that this is not a true version 4 (random) UUID. Since `os.time()` precision is only 1 second, it would be hard +-- to guarantee spacial uniqueness when two hosts generate a uuid after being seeded during the same second. This +-- is solved by using the node field from a version 1 UUID. It represents the mac address. +-- +-- 28-apr-2013 modified by Thijs Schreijer from the original [Rackspace code](https://github.com/kans/zirgo/blob/807250b1af6725bad4776c931c89a784c1e34db2/util/uuid.lua) as a generic Lua module. +-- Regarding the above mention on `os.time()`; the modifications use the `socket.gettime()` function from LuaSocket +-- if available and hence reduce that problem (provided LuaSocket has been loaded before uuid). + +-- Use original module in case it has been loaded with a previous require or it +-- has been preload by the user +local M = package.loaded["uuid"] or package.preload["uuid"] +if M then return M end + +local M = {} +local math = require('math') +local os = require('os') +local string = require('string') + +local bitsize = 32 -- bitsize assumed for Lua VM. See randomseed function below. +local lua_version = tonumber(_VERSION:match("%d%.*%d*")) -- grab Lua version used + +local MATRIX_AND = {{0,0},{0,1} } +local MATRIX_OR = {{0,1},{1,1}} +local HEXES = '0123456789abcdef' + +-- performs the bitwise operation specified by truth matrix on two numbers. +local function BITWISE(x, y, matrix) + local z = 0 + local pow = 1 + while x > 0 or y > 0 do + z = z + (matrix[x%2+1][y%2+1] * pow) + pow = pow * 2 + x = math.floor(x/2) + y = math.floor(y/2) + end + return z +end + +local function INT2HEX(x) + local s,base = '',16 + local d + while x > 0 do + d = x % base + 1 + x = math.floor(x/base) + s = string.sub(HEXES, d, d)..s + end + while #s < 2 do s = "0" .. s end + return s +end + +---------------------------------------------------------------------------- +-- Creates a new uuid. Either provide a unique hex string, or make sure the +-- random seed is properly set. The module table itself is a shortcut to this +-- function, so `my_uuid = uuid.new()` equals `my_uuid = uuid()`. +-- +-- For proper use there are 3 options; +-- +-- 1. first require `luasocket`, then call `uuid.seed()`, and request a uuid using no +-- parameter, eg. `my_uuid = uuid()` +-- 2. use `uuid` without `luasocket`, set a random seed using `uuid.randomseed(some_good_seed)`, +-- and request a uuid using no parameter, eg. `my_uuid = uuid()` +-- 3. use `uuid` without `luasocket`, and request a uuid using an unique hex string, +-- eg. `my_uuid = uuid(my_networkcard_macaddress)` +-- +-- @return a properly formatted uuid string +-- @param hwaddr (optional) string containing a unique hex value (e.g.: `00:0c:29:69:41:c6`), to be used to compensate for the lesser `math.random()` function. Use a mac address for solid results. If omitted, a fully randomized uuid will be generated, but then you must ensure that the random seed is set properly! +-- @usage +-- local uuid = require("uuid") +-- print("here's a new uuid: ",uuid()) +function M.new(hwaddr) + -- bytes are treated as 8bit unsigned bytes. + local bytes = { + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + } + + if hwaddr then + assert(type(hwaddr)=="string", "Expected hex string, got "..type(hwaddr)) + -- Cleanup provided string, assume mac address, so start from back and cleanup until we've got 12 characters + local i,str, hwaddr = #hwaddr, hwaddr, "" + while i>0 and #hwaddr<12 do + local c = str:sub(i,i):lower() + if HEXES:find(c, 1, true) then + -- valid HEX character, so append it + hwaddr = c..hwaddr + end + i = i - 1 + end + assert(#hwaddr == 12, "Provided string did not contain at least 12 hex characters, retrieved '"..hwaddr.."' from '"..str.."'") + + -- no split() in lua. :( + bytes[11] = tonumber(hwaddr:sub(1, 2), 16) + bytes[12] = tonumber(hwaddr:sub(3, 4), 16) + bytes[13] = tonumber(hwaddr:sub(5, 6), 16) + bytes[14] = tonumber(hwaddr:sub(7, 8), 16) + bytes[15] = tonumber(hwaddr:sub(9, 10), 16) + bytes[16] = tonumber(hwaddr:sub(11, 12), 16) + end + + -- set the version + bytes[7] = BITWISE(bytes[7], 0x0f, MATRIX_AND) + bytes[7] = BITWISE(bytes[7], 0x40, MATRIX_OR) + -- set the variant + bytes[9] = BITWISE(bytes[7], 0x3f, MATRIX_AND) + bytes[9] = BITWISE(bytes[7], 0x80, MATRIX_OR) + return INT2HEX(bytes[1])..INT2HEX(bytes[2])..INT2HEX(bytes[3])..INT2HEX(bytes[4]).."-".. + INT2HEX(bytes[5])..INT2HEX(bytes[6]).."-".. + INT2HEX(bytes[7])..INT2HEX(bytes[8]).."-".. + INT2HEX(bytes[9])..INT2HEX(bytes[10]).."-".. + INT2HEX(bytes[11])..INT2HEX(bytes[12])..INT2HEX(bytes[13])..INT2HEX(bytes[14])..INT2HEX(bytes[15])..INT2HEX(bytes[16]) +end + +---------------------------------------------------------------------------- +-- Improved randomseed function. +-- Lua 5.1 and 5.2 both truncate the seed given if it exceeds the integer +-- range. If this happens, the seed will be 0 or 1 and all randomness will +-- be gone (each application run will generate the same sequence of random +-- numbers in that case). This improved version drops the most significant +-- bits in those cases to get the seed within the proper range again. +-- @param seed the random seed to set (integer from 0 - 2^32, negative values will be made positive) +-- @return the (potentially modified) seed used +-- @usage +-- local socket = require("socket") -- gettime() has higher precision than os.time() +-- local uuid = require("uuid") +-- -- see also example at uuid.seed() +-- uuid.randomseed(socket.gettime()*10000) +-- print("here's a new uuid: ",uuid()) +function M.randomseed(seed) + seed = math.floor(math.abs(seed)) + if seed >= (2^bitsize) then + -- integer overflow, so reduce to prevent a bad seed + seed = seed - math.floor(seed / 2^bitsize) * (2^bitsize) + end + if lua_version < 5.2 then + -- 5.1 uses (incorrect) signed int + math.randomseed(seed - 2^(bitsize-1)) + else + -- 5.2 uses (correct) unsigned int + math.randomseed(seed) + end + return seed +end + +---------------------------------------------------------------------------- +-- Seeds the random generator. +-- It does so in 2 possible ways; +-- +-- 1. use `os.time()`: this only offers resolution to one second (used when +-- LuaSocket hasn't been loaded yet +-- 2. use luasocket `gettime()` function, but it only does so when LuaSocket +-- has been required already. +-- @usage +-- local socket = require("socket") -- gettime() has higher precision than os.time() +-- -- LuaSocket loaded, so below line does the same as the example from randomseed() +-- uuid.seed() +-- print("here's a new uuid: ",uuid()) +function M.seed() + if package.loaded["socket"] and package.loaded["socket"].gettime then + return M.randomseed(package.loaded["socket"].gettime()*10000) + else + return M.randomseed(os.time()) + end +end + +return setmetatable( M, { __call = function(self, hwaddr) return self.new(hwaddr) end} ) diff --git a/iPyLuaKernel.lua b/iPyLuaKernel.lua new file mode 100644 index 0000000..3f9cf4a --- /dev/null +++ b/iPyLuaKernel.lua @@ -0,0 +1,277 @@ +--[[ + iPyLua + + Copyright (c) 2015 Francisco Zamora-Martinez. Simplified, less deps and making + it work. + + Original name and copyright: lua_ipython_kernel + Copyright (c) 2013 Evan Wies. All rights reserved. + + + Released under the MIT License, see the LICENSE file. + + https://github.com/neomantra/lua_ipython_kernel + + usage: lua ipython_kernel.lua `connection_file` +--]] + +if #arg ~= 1 then + io.stderr:write(('usage: %s CONNECTION_FILENAME\n'):format(arg[0])) + os.exit(-1) +end + +local json = require "iPyLua.dkjson" + +-- our kernel's state +local kernel = {} + +-- load connection object info from JSON file +do + local connection_file = assert( io.open(arg[1]) ) + local connection_json = assert( connection_file:read('*a') ) + kernel.connection_obj = assert( json.decode(connection_json) ) + connection_file:close() +end + +local zmq = require 'lzmq' +local zmq_poller = require 'lzmq.poller' +local z_NOBLOCK, z_POLLIN = zmq.NOBLOCK, zmq.POLL_IN +local z_RCVMORE, z_SNDMORE = zmq.RCVMORE, zmq.SNDMORE + +local uuid = require 'iPyLua.uuid' -- TODO: randomseed or luasocket or something else + +local MSG_DELIM = '' +local username = os.getenv('USER') or "unknown" + +------------------------------------------------------------------------------- +-- IPython Message ("ipmsg") functions + +local function ipmsg_to_table(parts) + local ipmsg = { ids = {}, blobs = {} } + + local i = 1 + while i <= #parts and parts[i] ~= MSG_DELIM do + ipmsg.ids[#ipmsg.ids + 1] = parts[i] + i = i + 1 + end + i = i + 1 + ipmsg.hmac = parts[i] ; i = i + 1 + ipmsg.header = parts[i] ; i = i + 1 + ipmsg.parent_header = parts[i] ; i = i + 1 + ipmsg.metadata = parts[i] ; i = i + 1 + ipmsg.content = parts[i] ; i = i + 1 + + while i <= #parts do + ipmsg.blobs[#ipmsg.blobs + 1] = parts[i] + i = i + 1 + end + + return ipmsg +end + + +local function ipmsg_header(session, msg_type) + return json.encode({ + msg_id = uuid.new(), -- TODO: randomness warning: uuid.new() + username = username, + session = session, + msg_type = msg_type, + version = '5.0', + }) +end + + +local function ipmsg_send(sock, ids, hmac, hdr, p_hdr, meta, content, blobs) + if type(ids) == 'table' then + for _, v in ipairs(ids) do + sock:send(v, z_SNDMORE) + end + else + sock:send(ids, z_SNDMORE) + end + sock:send(MSG_DELIM, z_SNDMORE) + sock:send(hmac, z_SNDMORE) + sock:send(hdr, z_SNDMORE) + sock:send(p_hdr, z_SNDMORE) + sock:send(meta, z_SNDMORE) + if blobs then + sock:send(content, z_SNDMORE) + if type(blobs) == 'table' then + for i, v in ipairs(blobs) do + if i == #blobs then + sock:send(v) + else + sock:send(v, z_SNDMORE) + end + end + else + sock:send(blobs) + end + else + sock:send(content) + end +end + + + +------------------------------------------------------------------------------- + +-- environment where all code is executed +local env_session +local env_header +local env_source +local function pubstr(str) + print("PUBPUB", str) + local header = ipmsg_header( env_session, 'display_data' ) + local content = json.encode{ + source = env_source, + data = { ['text/plain'] = str }, + -- metadata = { ['text/plain'] = {} }, + } + print("CONTENT", content) + ipmsg_send(kernel.iopub_sock, env_session, '', header, env_header, '{}', content) +end +local env = {} +for k,v in pairs(_G) do env[k] = v end +env.args = nil +env.print = function(...) + local str = table.concat(table.pack(...),"\t") + pubstr(str) +end +env.io.write = function(...) + local str = table.concat(table.pack(...)) + pubstring(str) +end + +local function add_return(code) + return code +end + +------------------------------------------------------------------------------- +-- ZMQ Read Handlers + +local function on_hb_read( sock ) + -- read the data and send a pong + local data = assert( sock:recv(zmq.NOBLOCK) ) + -- TODO: handle 'timeout' error + sock:send('pong') +end + +local function on_control_read( sock ) + local data = assert( sock:recv(zmq.NOBLOCK) ) + -- TODO: handle 'timeout' error + print("CTRL", data) +end + +local function on_stdin_read( sock ) + local data = assert( sock:recv(zmq.NOBLOCK) ) + -- TODO: handle 'timeout' error + print("STDIN", data) +end + +local function on_shell_read( sock ) + -- TODO: error handling + local ipmsg = assert( sock:recv_all() ) + + local msg = ipmsg_to_table(ipmsg) + + for k, v in pairs(msg) do print(k,v) end + + local header_obj = json.decode(msg.header) + if header_obj.msg_type == 'kernel_info_request' then + local header = ipmsg_header( header_obj.session, 'kernel_info_reply' ) + local major,minor = _VERSION:match("(%d+)%.(%d+)") + local content = json.encode({ + protocol_version = {4, 0}, + language_version = {tonumber(major), tonumber(minor)}, + language = 'lua', + }) + + ipmsg_send(sock, header_obj.session, '', header, msg.header, '{}', content) + + elseif header_obj.msg_type == 'execute_request' then + + local header = ipmsg_header( header_obj.session, 'execute_reply' ) + kernel.execution_count = kernel.execution_count + 1 + local content = json.encode({ + status = 'ok', + execution_count = kernel.execution_count, + payload = {}, + user_expresions = {}, + }) + + ipmsg_send(sock, header_obj.session, '', header, msg.header, '{}', content) + + local header = ipmsg_header( header_obj.session, 'status' ) + local content = json.encode{ execution_state='busy' } + ipmsg_send(kernel.iopub_sock, header_obj.session, '', + header, '{}', '{}', content) + local msg_content = json.decode(msg.content) + local code = msg_content.code + env_header = msg.header + env_session = header_obj.session + env_source = code + local f = load(add_return(code), nil, nil, env) + local out = f() + if out then + -- TODO: show output of the function + end + + local content = json.encode{ execution_state='idle' } + ipmsg_send(kernel.iopub_sock, header_obj.session, '', + header, '{}', '{}', content) + end + +end + +local function on_iopub_read( sock ) + -- read the data and send a pong + local data = assert( sock:recv(zmq.NOBLOCK) ) + -- TODO: handle timeout error + print("PUB", data) +end + + +------------------------------------------------------------------------------- +-- SETUP + +local kernel_sockets = { + { name = 'heartbeat_sock', sock_type = zmq.REP, port = 'hb', handler = on_hb_read }, + { name = 'control_sock', sock_type = zmq.ROUTER, port = 'control', handler = on_control_read }, + { name = 'stdin_sock', sock_type = zmq.ROUTER, port = 'stdin', handler = on_stdin_read }, + { name = 'shell_sock', sock_type = zmq.ROUTER, port = 'shell', handler = on_shell_read }, + { name = 'iopub_sock', sock_type = zmq.PUB, port = 'iopub', handler = on_iopub_read }, +} + +local z_ctx = zmq.context() +local z_poller = zmq_poller(#kernel_sockets) +for _, v in ipairs(kernel_sockets) do + -- TODO: error handling in here + local sock = assert( z_ctx:socket(v.sock_type) ) + + local conn_obj = kernel.connection_obj + local addr = string.format('%s://%s:%s', + conn_obj.transport, + conn_obj.ip, + conn_obj[v.port..'_port']) + + assert( sock:bind(addr) ) + + z_poller:add(sock, zmq.POLLIN, v.handler) + + kernel[v.name] = sock +end + +kernel.execution_count = 0 + + +------------------------------------------------------------------------------- +-- POLL then SHUTDOWN + +--print("Starting poll") +z_poller:start() + +for _, v in ipairs(kernel_sockets) do + kernel[v.name]:close() +end +z_ctx:term() diff --git a/ipython_kernel.lua b/ipython_kernel.lua deleted file mode 100644 index 2772fca..0000000 --- a/ipython_kernel.lua +++ /dev/null @@ -1,997 +0,0 @@ ---[[ - lua_ipython_kernel - - Copyright (c) 2013 Evan Wies. All rights reserved. - Copyright (c) 2015 Francisco Zamora-Martinez. Simplified, less deps. - - Released under the MIT License, see the LICENSE file. - - https://github.com/neomantra/lua_ipython_kernel - - usage: lua ipython_kernel.lua `connection_file` ---]] - -if #arg ~= 1 then - io.stderr:write('usage: ipython_kernel.lua CONNECTION_FILENAME\n') - os.exit(-1) -end - ---------------------------------------------------------------------------- --- DKJSON library embedded here for simplicity: no external dependencies -- ---------------------------------------------------------------------------- -local json -do - -- Module options: - local always_try_using_lpeg = true - local register_global_module_table = false - local global_module_name = 'json' - - --[[ - - David Kolf's JSON module for Lua 5.1/5.2 - - Version 2.5 - - - For the documentation see the corresponding readme.txt or visit - . - - You can contact the author by sending an e-mail to 'david' at the - domain 'dkolf.de'. - - - Copyright (C) 2010-2013 David Heiko Kolf - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - --]] - - -- global dependencies: - local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset = - pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset - local error, require, pcall, select = error, require, pcall, select - local floor, huge = math.floor, math.huge - local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = - string.rep, string.gsub, string.sub, string.byte, string.char, - string.find, string.len, string.format - local strmatch = string.match - local concat = table.concat - - json = { version = "dkjson 2.5" } - - if register_global_module_table then - _G[global_module_name] = json - end - - local _ENV = nil -- blocking globals in Lua 5.2 - - pcall (function() - -- Enable access to blocked metatables. - -- Don't worry, this module doesn't change anything in them. - local debmeta = require "debug".getmetatable - if debmeta then getmetatable = debmeta end - end) - - json.null = setmetatable ({}, { - __tojson = function () return "null" end - }) - - local function isarray (tbl) - local max, n, arraylen = 0, 0, 0 - for k,v in pairs (tbl) do - if k == 'n' and type(v) == 'number' then - arraylen = v - if v > max then - max = v - end - else - if type(k) ~= 'number' or k < 1 or floor(k) ~= k then - return false - end - if k > max then - max = k - end - n = n + 1 - end - end - if max > 10 and max > arraylen and max > n * 2 then - return false -- don't create an array with too many holes - end - return true, max - end - - local escapecodes = { - ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", - ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" - } - - local function escapeutf8 (uchar) - local value = escapecodes[uchar] - if value then - return value - end - local a, b, c, d = strbyte (uchar, 1, 4) - a, b, c, d = a or 0, b or 0, c or 0, d or 0 - if a <= 0x7f then - value = a - elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then - value = (a - 0xc0) * 0x40 + b - 0x80 - elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then - value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80 - elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then - value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80 - else - return "" - end - if value <= 0xffff then - return strformat ("\\u%.4x", value) - elseif value <= 0x10ffff then - -- encode as UTF-16 surrogate pair - value = value - 0x10000 - local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400) - return strformat ("\\u%.4x\\u%.4x", highsur, lowsur) - else - return "" - end - end - - local function fsub (str, pattern, repl) - -- gsub always builds a new string in a buffer, even when no match - -- exists. First using find should be more efficient when most strings - -- don't contain the pattern. - if strfind (str, pattern) then - return gsub (str, pattern, repl) - else - return str - end - end - - local function quotestring (value) - -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js - value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8) - if strfind (value, "[\194\216\220\225\226\239]") then - value = fsub (value, "\194[\128-\159\173]", escapeutf8) - value = fsub (value, "\216[\128-\132]", escapeutf8) - value = fsub (value, "\220\143", escapeutf8) - value = fsub (value, "\225\158[\180\181]", escapeutf8) - value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8) - value = fsub (value, "\226\129[\160-\175]", escapeutf8) - value = fsub (value, "\239\187\191", escapeutf8) - value = fsub (value, "\239\191[\176-\191]", escapeutf8) - end - return "\"" .. value .. "\"" - end - json.quotestring = quotestring - - local function replace(str, o, n) - local i, j = strfind (str, o, 1, true) - if i then - return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1) - else - return str - end - end - - -- locale independent num2str and str2num functions - local decpoint, numfilter - - local function updatedecpoint () - decpoint = strmatch(tostring(0.5), "([^05+])") - -- build a filter that can be used to remove group separators - numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+" - end - - updatedecpoint() - - local function num2str (num) - return replace(fsub(tostring(num), numfilter, ""), decpoint, ".") - end - - local function str2num (str) - local num = tonumber(replace(str, ".", decpoint)) - if not num then - updatedecpoint() - num = tonumber(replace(str, ".", decpoint)) - end - return num - end - - local function addnewline2 (level, buffer, buflen) - buffer[buflen+1] = "\n" - buffer[buflen+2] = strrep (" ", level) - buflen = buflen + 2 - return buflen - end - - function json.addnewline (state) - if state.indent then - state.bufferlen = addnewline2 (state.level or 0, - state.buffer, state.bufferlen or #(state.buffer)) - end - end - - local encode2 -- forward declaration - - local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state) - local kt = type (key) - if kt ~= 'string' and kt ~= 'number' then - return nil, "type '" .. kt .. "' is not supported as a key by JSON." - end - if prev then - buflen = buflen + 1 - buffer[buflen] = "," - end - if indent then - buflen = addnewline2 (level, buffer, buflen) - end - buffer[buflen+1] = quotestring (key) - buffer[buflen+2] = ":" - return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state) - end - - local function appendcustom(res, buffer, state) - local buflen = state.bufferlen - if type (res) == 'string' then - buflen = buflen + 1 - buffer[buflen] = res - end - return buflen - end - - local function exception(reason, value, state, buffer, buflen, defaultmessage) - defaultmessage = defaultmessage or reason - local handler = state.exception - if not handler then - return nil, defaultmessage - else - state.bufferlen = buflen - local ret, msg = handler (reason, value, state, defaultmessage) - if not ret then return nil, msg or defaultmessage end - return appendcustom(ret, buffer, state) - end - end - - function json.encodeexception(reason, value, state, defaultmessage) - return quotestring("<" .. defaultmessage .. ">") - end - - encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state) - local valtype = type (value) - local valmeta = getmetatable (value) - valmeta = type (valmeta) == 'table' and valmeta -- only tables - local valtojson = valmeta and valmeta.__tojson - if valtojson then - if tables[value] then - return exception('reference cycle', value, state, buffer, buflen) - end - tables[value] = true - state.bufferlen = buflen - local ret, msg = valtojson (value, state) - if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end - tables[value] = nil - buflen = appendcustom(ret, buffer, state) - elseif value == nil then - buflen = buflen + 1 - buffer[buflen] = "null" - elseif valtype == 'number' then - local s - if value ~= value or value >= huge or -value >= huge then - -- This is the behaviour of the original JSON implementation. - s = "null" - else - s = num2str (value) - end - buflen = buflen + 1 - buffer[buflen] = s - elseif valtype == 'boolean' then - buflen = buflen + 1 - buffer[buflen] = value and "true" or "false" - elseif valtype == 'string' then - buflen = buflen + 1 - buffer[buflen] = quotestring (value) - elseif valtype == 'table' then - if tables[value] then - return exception('reference cycle', value, state, buffer, buflen) - end - tables[value] = true - level = level + 1 - local isa, n = isarray (value) - if n == 0 and valmeta and valmeta.__jsontype == 'object' then - isa = false - end - local msg - if isa then -- JSON array - buflen = buflen + 1 - buffer[buflen] = "[" - for i = 1, n do - buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state) - if not buflen then return nil, msg end - if i < n then - buflen = buflen + 1 - buffer[buflen] = "," - end - end - buflen = buflen + 1 - buffer[buflen] = "]" - else -- JSON object - local prev = false - buflen = buflen + 1 - buffer[buflen] = "{" - local order = valmeta and valmeta.__jsonorder or globalorder - if order then - local used = {} - n = #order - for i = 1, n do - local k = order[i] - local v = value[k] - if v then - used[k] = true - buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) - prev = true -- add a seperator before the next element - end - end - for k,v in pairs (value) do - if not used[k] then - buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) - if not buflen then return nil, msg end - prev = true -- add a seperator before the next element - end - end - else -- unordered - for k,v in pairs (value) do - buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) - if not buflen then return nil, msg end - prev = true -- add a seperator before the next element - end - end - if indent then - buflen = addnewline2 (level - 1, buffer, buflen) - end - buflen = buflen + 1 - buffer[buflen] = "}" - end - tables[value] = nil - else - return exception ('unsupported type', value, state, buffer, buflen, - "type '" .. valtype .. "' is not supported by JSON.") - end - return buflen - end - - function json.encode (value, state) - state = state or {} - local oldbuffer = state.buffer - local buffer = oldbuffer or {} - state.buffer = buffer - updatedecpoint() - local ret, msg = encode2 (value, state.indent, state.level or 0, - buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state) - if not ret then - error (msg, 2) - elseif oldbuffer == buffer then - state.bufferlen = ret - return true - else - state.bufferlen = nil - state.buffer = nil - return concat (buffer) - end - end - - local function loc (str, where) - local line, pos, linepos = 1, 1, 0 - while true do - pos = strfind (str, "\n", pos, true) - if pos and pos < where then - line = line + 1 - linepos = pos - pos = pos + 1 - else - break - end - end - return "line " .. line .. ", column " .. (where - linepos) - end - - local function unterminated (str, what, where) - return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where) - end - - local function scanwhite (str, pos) - while true do - pos = strfind (str, "%S", pos) - if not pos then return nil end - local sub2 = strsub (str, pos, pos + 1) - if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then - -- UTF-8 Byte Order Mark - pos = pos + 3 - elseif sub2 == "//" then - pos = strfind (str, "[\n\r]", pos + 2) - if not pos then return nil end - elseif sub2 == "/*" then - pos = strfind (str, "*/", pos + 2) - if not pos then return nil end - pos = pos + 2 - else - return pos - end - end - end - - local escapechars = { - ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f", - ["n"] = "\n", ["r"] = "\r", ["t"] = "\t" - } - - local function unichar (value) - if value < 0 then - return nil - elseif value <= 0x007f then - return strchar (value) - elseif value <= 0x07ff then - return strchar (0xc0 + floor(value/0x40), - 0x80 + (floor(value) % 0x40)) - elseif value <= 0xffff then - return strchar (0xe0 + floor(value/0x1000), - 0x80 + (floor(value/0x40) % 0x40), - 0x80 + (floor(value) % 0x40)) - elseif value <= 0x10ffff then - return strchar (0xf0 + floor(value/0x40000), - 0x80 + (floor(value/0x1000) % 0x40), - 0x80 + (floor(value/0x40) % 0x40), - 0x80 + (floor(value) % 0x40)) - else - return nil - end - end - - local function scanstring (str, pos) - local lastpos = pos + 1 - local buffer, n = {}, 0 - while true do - local nextpos = strfind (str, "[\"\\]", lastpos) - if not nextpos then - return unterminated (str, "string", pos) - end - if nextpos > lastpos then - n = n + 1 - buffer[n] = strsub (str, lastpos, nextpos - 1) - end - if strsub (str, nextpos, nextpos) == "\"" then - lastpos = nextpos + 1 - break - else - local escchar = strsub (str, nextpos + 1, nextpos + 1) - local value - if escchar == "u" then - value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16) - if value then - local value2 - if 0xD800 <= value and value <= 0xDBff then - -- we have the high surrogate of UTF-16. Check if there is a - -- low surrogate escaped nearby to combine them. - if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then - value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16) - if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then - value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000 - else - value2 = nil -- in case it was out of range for a low surrogate - end - end - end - value = value and unichar (value) - if value then - if value2 then - lastpos = nextpos + 12 - else - lastpos = nextpos + 6 - end - end - end - end - if not value then - value = escapechars[escchar] or escchar - lastpos = nextpos + 2 - end - n = n + 1 - buffer[n] = value - end - end - if n == 1 then - return buffer[1], lastpos - elseif n > 1 then - return concat (buffer), lastpos - else - return "", lastpos - end - end - - local scanvalue -- forward declaration - - local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta) - local len = strlen (str) - local tbl, n = {}, 0 - local pos = startpos + 1 - if what == 'object' then - setmetatable (tbl, objectmeta) - else - setmetatable (tbl, arraymeta) - end - while true do - pos = scanwhite (str, pos) - if not pos then return unterminated (str, what, startpos) end - local char = strsub (str, pos, pos) - if char == closechar then - return tbl, pos + 1 - end - local val1, err - val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) - if err then return nil, pos, err end - pos = scanwhite (str, pos) - if not pos then return unterminated (str, what, startpos) end - char = strsub (str, pos, pos) - if char == ":" then - if val1 == nil then - return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")" - end - pos = scanwhite (str, pos + 1) - if not pos then return unterminated (str, what, startpos) end - local val2 - val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) - if err then return nil, pos, err end - tbl[val1] = val2 - pos = scanwhite (str, pos) - if not pos then return unterminated (str, what, startpos) end - char = strsub (str, pos, pos) - else - n = n + 1 - tbl[n] = val1 - end - if char == "," then - pos = pos + 1 - end - end - end - - scanvalue = function (str, pos, nullval, objectmeta, arraymeta) - pos = pos or 1 - pos = scanwhite (str, pos) - if not pos then - return nil, strlen (str) + 1, "no valid JSON value (reached the end)" - end - local char = strsub (str, pos, pos) - if char == "{" then - return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta) - elseif char == "[" then - return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta) - elseif char == "\"" then - return scanstring (str, pos) - else - local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos) - if pstart then - local number = str2num (strsub (str, pstart, pend)) - if number then - return number, pend + 1 - end - end - pstart, pend = strfind (str, "^%a%w*", pos) - if pstart then - local name = strsub (str, pstart, pend) - if name == "true" then - return true, pend + 1 - elseif name == "false" then - return false, pend + 1 - elseif name == "null" then - return nullval, pend + 1 - end - end - return nil, pos, "no valid JSON value at " .. loc (str, pos) - end - end - - local function optionalmetatables(...) - if select("#", ...) > 0 then - return ... - else - return {__jsontype = 'object'}, {__jsontype = 'array'} - end - end - - function json.decode (str, pos, nullval, ...) - local objectmeta, arraymeta = optionalmetatables(...) - return scanvalue (str, pos, nullval, objectmeta, arraymeta) - end - - function json.use_lpeg () - local g = require ("lpeg") - - if g.version() == "0.11" then - error "due to a bug in LPeg 0.11, it cannot be used for JSON matching" - end - - local pegmatch = g.match - local P, S, R = g.P, g.S, g.R - - local function ErrorCall (str, pos, msg, state) - if not state.msg then - state.msg = msg .. " at " .. loc (str, pos) - state.pos = pos - end - return false - end - - local function Err (msg) - return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall) - end - - local SingleLineComment = P"//" * (1 - S"\n\r")^0 - local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/" - local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0 - - local PlainChar = 1 - S"\"\\\n\r" - local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars - local HexDigit = R("09", "af", "AF") - local function UTF16Surrogate (match, pos, high, low) - high, low = tonumber (high, 16), tonumber (low, 16) - if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then - return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000) - else - return false - end - end - local function UTF16BMP (hex) - return unichar (tonumber (hex, 16)) - end - local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit)) - local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP - local Char = UnicodeEscape + EscapeSequence + PlainChar - local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string") - local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0)) - local Fractal = P"." * R"09"^0 - local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1 - local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num - local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1) - local SimpleValue = Number + String + Constant - local ArrayContent, ObjectContent - - -- The functions parsearray and parseobject parse only a single value/pair - -- at a time and store them directly to avoid hitting the LPeg limits. - local function parsearray (str, pos, nullval, state) - local obj, cont - local npos - local t, nt = {}, 0 - repeat - obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state) - if not npos then break end - pos = npos - nt = nt + 1 - t[nt] = obj - until cont == 'last' - return pos, setmetatable (t, state.arraymeta) - end - - local function parseobject (str, pos, nullval, state) - local obj, key, cont - local npos - local t = {} - repeat - key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state) - if not npos then break end - pos = npos - t[key] = obj - until cont == 'last' - return pos, setmetatable (t, state.objectmeta) - end - - local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected") - local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected") - local Value = Space * (Array + Object + SimpleValue) - local ExpectedValue = Value + Space * Err "value expected" - ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() - local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue) - ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() - local DecodeValue = ExpectedValue * g.Cp () - - function json.decode (str, pos, nullval, ...) - local state = {} - state.objectmeta, state.arraymeta = optionalmetatables(...) - local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state) - if state.msg then - return nil, state.pos, state.msg - else - return obj, retpos - end - end - - -- use this function only once: - json.use_lpeg = function () return json end - - json.using_lpeg = true - - return json -- so you can get the module using json = require "dkjson".use_lpeg() - end - - if always_try_using_lpeg then - pcall (json.use_lpeg) - end -end - -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- - --- our kernel's state -local kernel = {} - --- load connection object info from JSON file -do - local connection_file = assert( io.open(arg[1]) ) - - local connection_json = assert( connection_file:read('*a') ) - - kernel.connection_obj = assert( json.decode(connection_json) ) - connection_file:close() -end - - -local zmq = require 'lzmq' -local zmq_poller = require 'lzmq.poller' -local z_NOBLOCK, z_POLLIN = zmq.NOBLOCK, zmq.POLL_IN -local z_RCVMORE, z_SNDMORE = zmq.RCVMORE, zmq.SNDMORE - --- local uuid = require 'uuid' --- TODO: randomseed or luasocket or something else - -local MSG_DELIM = '' -local username = os.getenv('USER') or "unknown" - -------------------------------------------------------------------------------- --- IPython Message ("ipmsg") functions - -local function ipmsg_to_table(parts) - local ipmsg = { ids = {}, blobs = {} } - - local i = 1 - while i <= #parts and parts[i] ~= MSG_DELIM do - ipmsg.ids[#ipmsg.ids + 1] = parts[i] - i = i + 1 - end - i = i + 1 - ipmsg.hmac = parts[i] ; i = i + 1 - ipmsg.header = parts[i] ; i = i + 1 - ipmsg.parent_header = parts[i] ; i = i + 1 - ipmsg.metadata = parts[i] ; i = i + 1 - ipmsg.content = parts[i] ; i = i + 1 - - while i <= #parts do - ipmsg.blobs[#ipmsg.blobs + 1] = parts[i] - i = i + 1 - end - - return ipmsg -end - - -local function ipmsg_header(session, msg_type) - return json.encode({ - msg_id = io.popen("uuidgen"):read("*l"), -- TODO: randomness warning: uuid.new() - username = username, - session = session, - msg_type = msg_type, - version = '5.0', - }) -end - - -local function ipmsg_send(sock, ids, hmac, hdr, p_hdr, meta, content, blobs) - if type(ids) == 'table' then - for _, v in ipairs(ids) do - sock:send(v, z_SNDMORE) - end - else - sock:send(ids, z_SNDMORE) - end - sock:send(MSG_DELIM, z_SNDMORE) - sock:send(hmac, z_SNDMORE) - sock:send(hdr, z_SNDMORE) - sock:send(p_hdr, z_SNDMORE) - sock:send(meta, z_SNDMORE) - if blobs then - sock:send(content, z_SNDMORE) - if type(blobs) == 'table' then - for i, v in ipairs(blobs) do - if i == #blobs then - sock:send(v) - else - sock:send(v, z_SNDMORE) - end - end - else - sock:send(blobs) - end - else - sock:send(content) - end -end - - - -------------------------------------------------------------------------------- - --- environment where all code is executed -local env_session -local env_header -local env_source -local function pubstr(str) - print("PUBPUB", str) - local header = ipmsg_header( env_session, 'display_data' ) - local content = json.encode{ - source = env_source, - data = { ['text/plain'] = str }, - -- metadata = { ['text/plain'] = {} }, - } - print("CONTENT", content) - ipmsg_send(kernel.iopub_sock, env_session, '', header, env_header, '{}', content) -end -local env = {} -for k,v in pairs(_G) do env[k] = v end -env.args = nil -env.print = function(...) - local str = table.concat(table.pack(...),"\t") - pubstr(str) -end -env.io.write = function(...) - local str = table.concat(table.pack(...)) - pubstring(str) -end - -local function add_return(code) - return code -end - -------------------------------------------------------------------------------- --- ZMQ Read Handlers - -local function on_hb_read( sock ) - -- read the data and send a pong - local data = assert( sock:recv(zmq.NOBLOCK) ) - -- TODO: handle 'timeout' error - sock:send('pong') -end - -local function on_control_read( sock ) - local data = assert( sock:recv(zmq.NOBLOCK) ) - -- TODO: handle 'timeout' error - print("CTRL", data) -end - -local function on_stdin_read( sock ) - local data = assert( sock:recv(zmq.NOBLOCK) ) - -- TODO: handle 'timeout' error - print("STDIN", data) -end - -local function on_shell_read( sock ) - -- TODO: error handling - local ipmsg = assert( sock:recv_all() ) - - local msg = ipmsg_to_table(ipmsg) - - for k, v in pairs(msg) do print(k,v) end - - local header_obj = json.decode(msg.header) - if header_obj.msg_type == 'kernel_info_request' then - local header = ipmsg_header( header_obj.session, 'kernel_info_reply' ) - local major,minor = _VERSION:match("(%d+)%.(%d+)") - local content = json.encode({ - protocol_version = {4, 0}, - language_version = {tonumber(major), tonumber(minor)}, - language = 'lua', - }) - - ipmsg_send(sock, header_obj.session, '', header, msg.header, '{}', content) - - elseif header_obj.msg_type == 'execute_request' then - - local header = ipmsg_header( header_obj.session, 'execute_reply' ) - kernel.execution_count = kernel.execution_count + 1 - local content = json.encode({ - status = 'ok', - execution_count = kernel.execution_count, - payload = {}, - user_expresions = {}, - }) - - ipmsg_send(sock, header_obj.session, '', header, msg.header, '{}', content) - - local header = ipmsg_header( header_obj.session, 'status' ) - local content = json.encode{ execution_state='busy' } - ipmsg_send(kernel.iopub_sock, header_obj.session, '', - header, '{}', '{}', content) - local msg_content = json.decode(msg.content) - local code = msg_content.code - env_header = msg.header - env_session = header_obj.session - env_source = code - local f = load(add_return(code), nil, nil, env) - local out = f() - if out then - -- TODO: show output of the function - end - - local content = json.encode{ execution_state='idle' } - ipmsg_send(kernel.iopub_sock, header_obj.session, '', - header, '{}', '{}', content) - end - -end - -local function on_iopub_read( sock ) - -- read the data and send a pong - local data = assert( sock:recv(zmq.NOBLOCK) ) - -- TODO: handle timeout error - print("PUB", data) -end - - -------------------------------------------------------------------------------- --- SETUP - -local kernel_sockets = { - { name = 'heartbeat_sock', sock_type = zmq.REP, port = 'hb', handler = on_hb_read }, - { name = 'control_sock', sock_type = zmq.ROUTER, port = 'control', handler = on_control_read }, - { name = 'stdin_sock', sock_type = zmq.ROUTER, port = 'stdin', handler = on_stdin_read }, - { name = 'shell_sock', sock_type = zmq.ROUTER, port = 'shell', handler = on_shell_read }, - { name = 'iopub_sock', sock_type = zmq.PUB, port = 'iopub', handler = on_iopub_read }, -} - -local z_ctx = zmq.context() -local z_poller = zmq_poller(#kernel_sockets) -for _, v in ipairs(kernel_sockets) do - -- TODO: error handling in here - local sock = assert( z_ctx:socket(v.sock_type) ) - - local conn_obj = kernel.connection_obj - local addr = string.format('%s://%s:%s', - conn_obj.transport, - conn_obj.ip, - conn_obj[v.port..'_port']) - - assert( sock:bind(addr) ) - - z_poller:add(sock, zmq.POLLIN, v.handler) - - kernel[v.name] = sock -end - -kernel.execution_count = 0 - - -------------------------------------------------------------------------------- --- POLL then SHUTDOWN - ---print("Starting poll") -z_poller:start() - -for _, v in ipairs(kernel_sockets) do - kernel[v.name]:close() -end -z_ctx:term() From d625f3ff14b750e8b27e73f4b99c6558c10fd1cf Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 12:20:37 +0200 Subject: [PATCH 05/94] Updated readme --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 01cd0d2..d8746c7 100644 --- a/README.md +++ b/README.md @@ -85,8 +85,11 @@ And of course thanks to the [IPython folks ](http://ipython.org/citing.html). **lua_ipython_kernel** is distributed under the [MIT License](http://opensource.org/licenses/mit-license.php). -> lua_ipython_kernel -> +> iPyLua +> Copyright (c) 2015 Francisco Zamora-Martinez. Simplified, less deps and making +> it work. +> +> Original name and copyright: lua_ipython_kernel > Copyright (c) 2013 Evan Wies. All rights reserved. > > Permission is hereby granted, free of charge, to any person obtaining a copy From d40d2f6fbb177681d14bc2f3d2869890ead0634d Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 12:21:36 +0200 Subject: [PATCH 06/94] Added gitignore and dir-locals --- .dir-locals.el | 9 +++++++++ .gitignore | 29 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 .dir-locals.el create mode 100644 .gitignore diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..d701b72 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,9 @@ +( + (nil . + ( + (c-basic-offset . 2) + (tab-width . 8) + (indent-tabs-mode . nil) + ) + ) + ) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0791fb5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# Emacs backups # +################# +*~ +\#*\# +.\#* + +# Vim swap # +*.swp +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +Icon? +ehthumbs.db +Thumbs.db + +# Emacs Clang autocompletion # +############################## +.clang-completion-error + +# Global tags # +############### +GPATH +GRTAGS +GSYMS +GTAGS From 28b25af8833b60658bd52002240064221867236c Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 12:22:09 +0200 Subject: [PATCH 07/94] Updated readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8746c7..eb8616b 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ And of course thanks to the [IPython folks ](http://ipython.org/citing.html). ## LICENSE -**lua_ipython_kernel** is distributed under the +**iPyLua** is distributed under the [MIT License](http://opensource.org/licenses/mit-license.php). > iPyLua From 88ec2beb61ad077cbc6d69f43e4ca946919d4a4e Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 14:34:03 +0200 Subject: [PATCH 08/94] The kernel is execution one-line code. Multi-line stills in development --- README.md | 12 +- iPyLuaKernel.lua | 346 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 241 insertions(+), 117 deletions(-) diff --git a/README.md b/README.md index eb8616b..4df2155 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# iPyLua: Lua IPython Kernel +1;3803;0c1# iPyLua: Lua IPython Kernel This is a kernel to support Lua with [IPython](http://ipython.org). It is pure Lua and should work with both Lua and LuaJIT. @@ -24,12 +24,12 @@ The installation process is janky right now. * Create a profile with IPython: ``` -$ ipython profile create lua +$ ipython profile create iPyLua ``` * Modify the profile's `ipython_config.py` to use iPyLua. This - will be at either `~/.config/ipython/profile_lua/ipython_config.py` or - `~/.ipython/profile_lua/ipython_config.py`: + will be at either `~/.config/ipython/profile_iPyLua/ipython_config.py` or + `~/.ipython/profile_iPyLua/ipython_config.py`: ```Python # Configuration file for ipython. @@ -50,9 +50,9 @@ c.Session.keyfile = b'' * Invoke IPython with this Lua kernel: ``` -$ ipython console --profile lua +$ ipython console --profile iPyLua # or -$ ipython notebook --profile lua +$ ipython notebook --profile iPyLua ``` ## TODO diff --git a/iPyLuaKernel.lua b/iPyLuaKernel.lua index 3f9cf4a..d823235 100644 --- a/iPyLuaKernel.lua +++ b/iPyLuaKernel.lua @@ -20,10 +20,28 @@ if #arg ~= 1 then os.exit(-1) end +do + -- Setting iPyLua in the registry allow to extend this implementation with + -- specifications due to other Lua modules. For instance, APRIL-ANN uses this + -- variable to configure how to encode images, plots, matrices, ... As well + -- as introducing a sort of inline documentation. + local reg = debug.getregistry() + reg.iPyLua = true +end + local json = require "iPyLua.dkjson" -- our kernel's state -local kernel = {} +local kernel = { execution_count=0 } + +local function next_execution_count() + kernel.execution_count = kernel.execution_count + 1 + return kernel.execution_count +end + +local function current_execution_count() + return kernel.execution_count +end -- load connection object info from JSON file do @@ -33,10 +51,13 @@ do connection_file:close() end +local HMAC = '' + local zmq = require 'lzmq' local zmq_poller = require 'lzmq.poller' local z_NOBLOCK, z_POLLIN = zmq.NOBLOCK, zmq.POLL_IN local z_RCVMORE, z_SNDMORE = zmq.RCVMORE, zmq.SNDMORE +local zassert = zmq.assert local uuid = require 'iPyLua.uuid' -- TODO: randomseed or luasocket or something else @@ -47,41 +68,55 @@ local username = os.getenv('USER') or "unknown" -- IPython Message ("ipmsg") functions local function ipmsg_to_table(parts) - local ipmsg = { ids = {}, blobs = {} } + local msg = { ids = {}, blobs = {} } local i = 1 while i <= #parts and parts[i] ~= MSG_DELIM do - ipmsg.ids[#ipmsg.ids + 1] = parts[i] + msg.ids[#msg.ids + 1] = parts[i] i = i + 1 end i = i + 1 - ipmsg.hmac = parts[i] ; i = i + 1 - ipmsg.header = parts[i] ; i = i + 1 - ipmsg.parent_header = parts[i] ; i = i + 1 - ipmsg.metadata = parts[i] ; i = i + 1 - ipmsg.content = parts[i] ; i = i + 1 + msg.hmac = parts[i] ; i = i + 1 + msg.header = parts[i] ; i = i + 1 + msg.parent_header = parts[i] ; i = i + 1 + msg.metadata = parts[i] ; i = i + 1 + msg.content = parts[i] ; i = i + 1 while i <= #parts do - ipmsg.blobs[#ipmsg.blobs + 1] = parts[i] + msg.blobs[#msg.blobs + 1] = parts[i] i = i + 1 end - return ipmsg + return msg end - -local function ipmsg_header(session, msg_type) - return json.encode({ - msg_id = uuid.new(), -- TODO: randomness warning: uuid.new() - username = username, - session = session, - msg_type = msg_type, - version = '5.0', - }) +local session_id = uuid.new() +local function ipmsg_header(msg_type) + return { + msg_id = uuid.new(), -- TODO: randomness warning: uuid.new() + msg_type = msg_type, + username = username, + session = session_id, + version = '5.0', + date = os.date("%Y-%m-%dT%H:%M:%S"), + } end -local function ipmsg_send(sock, ids, hmac, hdr, p_hdr, meta, content, blobs) +local function ipmsg_send(sock, params) + local parent = params.parent or {header={}} + local ids = parent.ids or {} + local hdr = json.encode(assert( params.header )) + local meta = json.encode(params.meta or {}) + local content = json.encode(params.content or {}) + local blobs = params.blob + -- print("IPMSG_SEND") + -- print("\tHEADER", hdr) + -- print("\tCONTENT", content) + -- + local hmac = HMAC + local p_hdr = json.encode(parent.header) + -- if type(ids) == 'table' then for _, v in ipairs(ids) do sock:send(v, z_SNDMORE) @@ -118,33 +153,178 @@ end -- environment where all code is executed local env_session -local env_header +local env_parent local env_source -local function pubstr(str) - print("PUBPUB", str) - local header = ipmsg_header( env_session, 'display_data' ) - local content = json.encode{ - source = env_source, - data = { ['text/plain'] = str }, - -- metadata = { ['text/plain'] = {} }, +local function new_environment() + local function pubstr(str) + local header = ipmsg_header( 'pyout' ) + local content = { + data = { ['text/plain'] = str }, + execution_count = current_execution_count(), + metadata = {}, + } + ipmsg_send(kernel.iopub_sock, { + session = env_session, + parent = env_parent, + header = header, + content = content + }) + end + + local env_G = {} for k,v in pairs(_G) do env_G[k] = v end + env_G.args = nil + env_G._G = nil + local env = setmetatable({}, { __index = env_G }) + env._G = env + env._ENV = env + + env_G.print = function(...) + local str = table.concat(table.pack(...),"\t") + pubstr(str) + end + + env_G.io.write = function(...) + local str = table.concat(table.pack(...)) + pubstr(str) + end + return env +end +local env = new_environment() + +local function add_return(code) + return code +end + +------------------------------------------------------------------------------- + +local function send_execute_reply(sock, parent, count) + local session = parent.header.session + local header = ipmsg_header( 'execute_reply' ) + local content = { + status = 'ok', + execution_count = count, + payload = {}, + user_expresions = {}, } - print("CONTENT", content) - ipmsg_send(kernel.iopub_sock, env_session, '', header, env_header, '{}', content) + ipmsg_send(sock, { + session = session, + parent = parent, + header = header, + content = content, + }) +end + +local function send_busy_message(sock, parent) + local session = parent.header.session + local header = ipmsg_header( 'status' ) + local content = { execution_state='busy' } + ipmsg_send(sock, { + session = session, + parent = parent, + header = header, + content = content, + }) end -local env = {} -for k,v in pairs(_G) do env[k] = v end -env.args = nil -env.print = function(...) - local str = table.concat(table.pack(...),"\t") - pubstr(str) + +local function send_idle_message(sock, parent) + local session = parent.header.session + local header = ipmsg_header( 'status' ) + local content = { execution_state='idle' } + ipmsg_send(sock, { + session = session, + parent = parent, + header = header, + content = content, + }) end -env.io.write = function(...) - local str = table.concat(table.pack(...)) - pubstring(str) + +local function execute_code(parent) + local session = parent.header.session + local code = parent.content.code + env_parent = parent + env_session = session + env_source = code + local f,msg = load(add_return(code), nil, nil, env) + local out = f() + if out then + -- TODO: show output of the function + end end -local function add_return(code) - return code +local function send_pyin_message(sock, parent, count) + local session = parent.header.session + local header = ipmsg_header( 'pyin' ) + local content = { code=parent.content.code, + execution_count = count, } + ipmsg_send(sock, { + session = session, + parent = parent, + header = header, + content = content, + }) +end + +-- implemented routes +local shell_routes = { + kernel_info_request = function(sock, parent) + local session = parent.header.session + local header = ipmsg_header( 'kernel_info_reply' ) + local major,minor = _VERSION:match("(%d+)%.(%d+)") + local content = { + protocol_version = {4, 0}, + language_version = {tonumber(major), tonumber(minor)}, + language = 'iPyLua', + } + ipmsg_send(sock, { + session=session, + parent=parent, + header=header, + content=content, + }) + end, + + execute_request = function(sock, parent) + local count = next_execution_count() + parent.content = json.decode(parent.content) + -- + send_busy_message(kernel.iopub_sock, parent) + + local out = execute_code(parent) + send_pyin_message(kernel.iopub_sock, parent, count) + + send_execute_reply(sock, parent, count) + send_idle_message(kernel.iopub_sock, parent) + end, + + shutdown_request = function(sock, parent) + print("SHUTDOWN") + parent.content = json.decode(parent.content) + -- + send_busy_message(sock, parent) + local session = parent.header.session + local header = ipmsg_header( 'shutdown_reply' ) + local content = parent.content + ipmsg_send(sock, { + session=session, + parent=parent, + header=header, + content=content, + }) + send_idle_message(kernel.iopub_sock, parent) + for _, v in ipairs(kernel_sockets) do + kernel[v.name]:close() + end + os.exit() + end, +} + +do + local function dummy_function() end + setmetatable(shell_routes, { + __index = function(self, key) + return rawget(self,key) or dummy_function + end, + }) end ------------------------------------------------------------------------------- @@ -152,86 +332,31 @@ end local function on_hb_read( sock ) -- read the data and send a pong - local data = assert( sock:recv(zmq.NOBLOCK) ) + local data = zassert( sock:recv(zmq.NOBLOCK) ) -- TODO: handle 'timeout' error sock:send('pong') end local function on_control_read( sock ) - local data = assert( sock:recv(zmq.NOBLOCK) ) + local data = zassert( sock:recv(zmq.NOBLOCK) ) -- TODO: handle 'timeout' error - print("CTRL", data) end local function on_stdin_read( sock ) - local data = assert( sock:recv(zmq.NOBLOCK) ) + local data = zassert( sock:recv(zmq.NOBLOCK) ) -- TODO: handle 'timeout' error - print("STDIN", data) end local function on_shell_read( sock ) -- TODO: error handling - local ipmsg = assert( sock:recv_all() ) - + local ipmsg = zassert( sock:recv_all() ) local msg = ipmsg_to_table(ipmsg) - - for k, v in pairs(msg) do print(k,v) end - - local header_obj = json.decode(msg.header) - if header_obj.msg_type == 'kernel_info_request' then - local header = ipmsg_header( header_obj.session, 'kernel_info_reply' ) - local major,minor = _VERSION:match("(%d+)%.(%d+)") - local content = json.encode({ - protocol_version = {4, 0}, - language_version = {tonumber(major), tonumber(minor)}, - language = 'lua', - }) - - ipmsg_send(sock, header_obj.session, '', header, msg.header, '{}', content) - - elseif header_obj.msg_type == 'execute_request' then - - local header = ipmsg_header( header_obj.session, 'execute_reply' ) - kernel.execution_count = kernel.execution_count + 1 - local content = json.encode({ - status = 'ok', - execution_count = kernel.execution_count, - payload = {}, - user_expresions = {}, - }) - - ipmsg_send(sock, header_obj.session, '', header, msg.header, '{}', content) - - local header = ipmsg_header( header_obj.session, 'status' ) - local content = json.encode{ execution_state='busy' } - ipmsg_send(kernel.iopub_sock, header_obj.session, '', - header, '{}', '{}', content) - local msg_content = json.decode(msg.content) - local code = msg_content.code - env_header = msg.header - env_session = header_obj.session - env_source = code - local f = load(add_return(code), nil, nil, env) - local out = f() - if out then - -- TODO: show output of the function - end - - local content = json.encode{ execution_state='idle' } - ipmsg_send(kernel.iopub_sock, header_obj.session, '', - header, '{}', '{}', content) - end - + -- for k, v in pairs(msg) do print(k,v) end + msg.header = json.decode(msg.header) + -- print("REQUEST FOR KEY", msg.header.msg_type) + shell_routes[msg.header.msg_type](sock, msg) end -local function on_iopub_read( sock ) - -- read the data and send a pong - local data = assert( sock:recv(zmq.NOBLOCK) ) - -- TODO: handle timeout error - print("PUB", data) -end - - ------------------------------------------------------------------------------- -- SETUP @@ -247,7 +372,7 @@ local z_ctx = zmq.context() local z_poller = zmq_poller(#kernel_sockets) for _, v in ipairs(kernel_sockets) do -- TODO: error handling in here - local sock = assert( z_ctx:socket(v.sock_type) ) + local sock = zassert( z_ctx:socket(v.sock_type) ) local conn_obj = kernel.connection_obj local addr = string.format('%s://%s:%s', @@ -255,16 +380,15 @@ for _, v in ipairs(kernel_sockets) do conn_obj.ip, conn_obj[v.port..'_port']) - assert( sock:bind(addr) ) - - z_poller:add(sock, zmq.POLLIN, v.handler) + zassert( sock:bind(addr) ) + + if v.name ~= 'iopub_sock' then -- avoid polling from iopub + z_poller:add(sock, zmq.POLLIN, v.handler) + end kernel[v.name] = sock end -kernel.execution_count = 0 - - ------------------------------------------------------------------------------- -- POLL then SHUTDOWN From 578d95cc7b5234acaced2b03bf318c59fac42781 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 14:34:24 +0200 Subject: [PATCH 09/94] Fix problem in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4df2155..fb9dfd2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -1;3803;0c1# iPyLua: Lua IPython Kernel +# iPyLua: Lua IPython Kernel This is a kernel to support Lua with [IPython](http://ipython.org). It is pure Lua and should work with both Lua and LuaJIT. From 64a1d929357aff6eb0d63b1700f87cdeb3cc5885 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 19:12:21 +0200 Subject: [PATCH 10/94] It is working, in its most simple way :-) --- iPyLua.sh | 6 ++ iPyLuaKernel.lua | 175 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 152 insertions(+), 29 deletions(-) create mode 100755 iPyLua.sh diff --git a/iPyLua.sh b/iPyLua.sh new file mode 100755 index 0000000..cc89715 --- /dev/null +++ b/iPyLua.sh @@ -0,0 +1,6 @@ +#!/bin/bash +how=$1 +if [[ -z $how ]]; then how="console"; fi +ipython $how --profile=iPyLua --colors=Linux +pid=$(pgrep -f "lua5.2 .*iPyLua/iPyLuaKernel") +if [[ ! -z $pid ]]; then kill -9 $pid; fi diff --git a/iPyLuaKernel.lua b/iPyLuaKernel.lua index d823235..9d8564e 100644 --- a/iPyLuaKernel.lua +++ b/iPyLuaKernel.lua @@ -20,13 +20,24 @@ if #arg ~= 1 then os.exit(-1) end +local output_filters = {} +local help_functions = {} do -- Setting iPyLua in the registry allow to extend this implementation with -- specifications due to other Lua modules. For instance, APRIL-ANN uses this -- variable to configure how to encode images, plots, matrices, ... As well -- as introducing a sort of inline documentation. + -- + -- To extend iPyLua output you need to stack into registry + -- iPyLua.output_filters new functions which receive an object and return a + -- data table as expected by IPython, that is, a data table with pairs of { + -- [mime_type] = representation, ... } followed by the metadata table + -- (optional). local reg = debug.getregistry() - reg.iPyLua = true + reg.iPyLua = { + output_filters = output_filters, + help_functions = help_functions, + } end local json = require "iPyLua.dkjson" @@ -152,16 +163,17 @@ end ------------------------------------------------------------------------------- -- environment where all code is executed +local new_environment local env_session local env_parent local env_source -local function new_environment() - local function pubstr(str) +do + local pyout = function(data, metadata) local header = ipmsg_header( 'pyout' ) local content = { - data = { ['text/plain'] = str }, + data = assert( data ), execution_count = current_execution_count(), - metadata = {}, + metadata = metadata or {}, } ipmsg_send(kernel.iopub_sock, { session = env_session, @@ -170,26 +182,90 @@ local function new_environment() content = content }) end - - local env_G = {} for k,v in pairs(_G) do env_G[k] = v end - env_G.args = nil - env_G._G = nil - local env = setmetatable({}, { __index = env_G }) - env._G = env - env._ENV = env - env_G.print = function(...) - local str = table.concat(table.pack(...),"\t") - pubstr(str) + local stringfy = function(v) + local v_str = tostring(v) + return not v_str:find("\n") and v_str or type(v) end + + local MAX = 10 + table.insert(output_filters, + function(obj, MAX) + local tt,footer = type(obj) + if tt == "table" then + local tbl = {} + do + local max = false + for k,v in ipairs(obj) do + table.insert(tbl, ("\t[%d] = %s,"):format(k,stringfy(v))) + if k >= MAX then max=true break end + end + if max then table.insert(tbl, "\t...") end + end + do + local max = false + local keys = {} + for k,v in pairs(obj) do + if type(k) ~= "number" then keys[#keys+1] = k end + end + table.sort(keys, function(a,b) return tostring(a) < tostring(b) end) + for i,k in ipairs(keys) do + table.insert(tbl, ("\t[%q] = %s,"):format(stringfy(k), + stringfy(obj[k]))) + if i >= MAX then max=true break end + end + if max then table.insert(tbl, "\t...") end + footer = ("# %s with %d array part, %d hash part"):format(tostring(obj), #obj, #keys) + end + table.insert(tbl, footer) + return { ["text/plain"]=table.concat(tbl, "\n") } + else + return { ["text/plain"]=tostring(obj) } + end + end) + + local function print_obj(obj, MAX) + for i=#output_filters,1,-1 do + local data,metadata = output_filters[i](obj, MAX) + if data then pyout(data,metadata) return true end + end + return false + end + + function new_environment() + local env_G,env = {},{} + for k,v in pairs(_G) do env_G[k] = v end + env_G.args = nil + env_G._G = env + env_G._ENV = env + local env_G = setmetatable(env_G, { __index = _G }) + local env = setmetatable(env, { __index = env_G }) + + env_G.print = function(...) + if select('#',...) == 1 then + if print_obj(..., MAX) then return end + end + local args = table.pack(...) + for i=1,#args do args[i]=stringfy(args[i]) end + local str = table.concat(args,"\t") + pyout({ ["text/plain"] = str }) + end - env_G.io.write = function(...) - local str = table.concat(table.pack(...)) - pubstr(str) + env_G.io.write = function(...) + local args = table.pack(...) + for i=1,#args do args[i]=stringfy(args[i]) end + local str = table.concat(table.pack(...)) + pyout({ ["text/plain"] = str }) + end + + env_G.vars = function() + print_obj(env, math.huge) + end + + return env,env_G end - return env end -local env = new_environment() +local env,env_G = new_environment() local function add_return(code) return code @@ -197,15 +273,20 @@ end ------------------------------------------------------------------------------- -local function send_execute_reply(sock, parent, count) +local function send_execute_reply(sock, parent, count, status, err) local session = parent.header.session local header = ipmsg_header( 'execute_reply' ) local content = { - status = 'ok', + status = status or 'ok', execution_count = count, payload = {}, user_expresions = {}, } + if status=="error" then + content.ename = err + content.evalue = '' + content.traceback = {err} + end ipmsg_send(sock, { session = session, parent = parent, @@ -241,13 +322,31 @@ end local function execute_code(parent) local session = parent.header.session local code = parent.content.code + if not code or #code==0 then return end env_parent = parent env_session = session env_source = code - local f,msg = load(add_return(code), nil, nil, env) - local out = f() - if out then - -- TODO: show output of the function + if code:find("^%s-%?") then + for i=#help_functions,1,-1 do + local data,metadata = help_functions[i](code:gsub("^%s-%?", "")) + if data then pyout(data,metadata) return true end + end + return nil,"Documentation not found" + else + if code:sub(1,1) == "=" then code = "return " .. code:sub(2) end + local ok,err = true,nil + local f,msg = load(code, nil, nil, env) + if f then + local out = table.pack(xpcall(f, debug.traceback)) + if not out[1] then + ok,err = nil,out[2] + elseif #out > 1 then + env.print(table.unpack(out, 2)) + end + else + ok,err = nil,msg + end + return ok,err end end @@ -264,6 +363,19 @@ local function send_pyin_message(sock, parent, count) }) end +local function send_pyerr_message(sock, parent, count, err) + local session = parent.header.session + local header = ipmsg_header( 'pyerr' ) + local content = { ename = err, evalue = '', traceback = {err}, + execution_count = count, } + ipmsg_send(sock, { + session = session, + parent = parent, + header = header, + content = content, + }) +end + -- implemented routes local shell_routes = { kernel_info_request = function(sock, parent) @@ -289,10 +401,15 @@ local shell_routes = { -- send_busy_message(kernel.iopub_sock, parent) - local out = execute_code(parent) + local ok,err = execute_code(parent) send_pyin_message(kernel.iopub_sock, parent, count) - - send_execute_reply(sock, parent, count) + if ok then + send_execute_reply(sock, parent, count) + else + err = err or "Unknown error" + send_pyerr_message(kernel.iopub_sock, parent, count, err) + send_execute_reply(sock, parent, count, "error", err) + end send_idle_message(kernel.iopub_sock, parent) end, From 8e8c789e984f82f0d02a81cdf5544e1e7313aa68 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 19:52:24 +0200 Subject: [PATCH 11/94] more changes --- .gitignore | 1 + iPyLua.sh | 2 +- iPyLuaKernel.lua | 76 ++++++++++++++++++++++++++++++++++-------------- 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index 0791fb5..fb28614 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.ipynb # Emacs backups # ################# *~ diff --git a/iPyLua.sh b/iPyLua.sh index cc89715..7d2ad28 100755 --- a/iPyLua.sh +++ b/iPyLua.sh @@ -1,6 +1,6 @@ #!/bin/bash how=$1 if [[ -z $how ]]; then how="console"; fi -ipython $how --profile=iPyLua --colors=Linux +ipython $how --profile=iPyLua --colors=Linux --ConsoleWidget.font_size=12 pid=$(pgrep -f "lua5.2 .*iPyLua/iPyLuaKernel") if [[ ! -z $pid ]]; then kill -9 $pid; fi diff --git a/iPyLuaKernel.lua b/iPyLuaKernel.lua index 9d8564e..96e0ec1 100644 --- a/iPyLuaKernel.lua +++ b/iPyLuaKernel.lua @@ -41,10 +41,24 @@ do end local json = require "iPyLua.dkjson" +local zmq = require 'lzmq' +local zmq_poller = require 'lzmq.poller' +local z_NOBLOCK, z_POLLIN = zmq.NOBLOCK, zmq.POLL_IN +local z_RCVMORE, z_SNDMORE = zmq.RCVMORE, zmq.SNDMORE +local zassert = zmq.assert + +local uuid = require 'iPyLua.uuid' -- TODO: randomseed or luasocket or something else + +local HMAC = '' +local MSG_DELIM = '' +local username = os.getenv('USER') or "unknown" -- our kernel's state local kernel = { execution_count=0 } +-- sockets description +local kernel_sockets + local function next_execution_count() kernel.execution_count = kernel.execution_count + 1 return kernel.execution_count @@ -62,19 +76,6 @@ do connection_file:close() end -local HMAC = '' - -local zmq = require 'lzmq' -local zmq_poller = require 'lzmq.poller' -local z_NOBLOCK, z_POLLIN = zmq.NOBLOCK, zmq.POLL_IN -local z_RCVMORE, z_SNDMORE = zmq.RCVMORE, zmq.SNDMORE -local zassert = zmq.assert - -local uuid = require 'iPyLua.uuid' -- TODO: randomseed or luasocket or something else - -local MSG_DELIM = '' -local username = os.getenv('USER') or "unknown" - ------------------------------------------------------------------------------- -- IPython Message ("ipmsg") functions @@ -162,6 +163,14 @@ end ------------------------------------------------------------------------------- +local function help(str) + for i=#help_functions,1,-1 do + local data,metadata = help_functions[i](str) + if data then pyout(data,metadata) return true end + end + return nil,"Documentation not found" +end + -- environment where all code is executed local new_environment local env_session @@ -236,7 +245,7 @@ do local env_G,env = {},{} for k,v in pairs(_G) do env_G[k] = v end env_G.args = nil - env_G._G = env + env_G._G = env_G env_G._ENV = env local env_G = setmetatable(env_G, { __index = _G }) local env = setmetatable(env, { __index = env_G }) @@ -261,6 +270,31 @@ do env_G.vars = function() print_obj(env, math.huge) end + + env_G.help = function(str) help(str) end + + env_G["%quickref"] = function() + local tbl = { + "? -> Introduction and overview.", + "%quickref -> This guide.", + "help(object) -> Help about a given object.", + "object? -> Help about a given object.", + "print(...) -> Print a list of objects using \\t as separator.", + " If only one object is given, it would be printed", + " in a fancy way when possible. If more than one", + " object is given, the fancy version will be used", + " only if it fits in one line, otherwise the type", + " of the object will be shown.", + "vars() -> Shows all global variables declared by the user.", + } + print_obj(table.concat(tbl,"\n")) + end + + env_G["%guiref"] = function() + local tbl = { + "GUI reference not written.", + } + end return env,env_G end @@ -326,13 +360,12 @@ local function execute_code(parent) env_parent = parent env_session = session env_source = code - if code:find("^%s-%?") then - for i=#help_functions,1,-1 do - local data,metadata = help_functions[i](code:gsub("^%s-%?", "")) - if data then pyout(data,metadata) return true end - end - return nil,"Documentation not found" + if code:find("%?+\n?$") then + return help(code:gsub("%?+\n?$", "")) else + if code:sub(1,1) == "%" then + code = ("_G[%q]()"):format(code:gsub("\n","")) + end if code:sub(1,1) == "=" then code = "return " .. code:sub(2) end local ok,err = true,nil local f,msg = load(code, nil, nil, env) @@ -414,7 +447,6 @@ local shell_routes = { end, shutdown_request = function(sock, parent) - print("SHUTDOWN") parent.content = json.decode(parent.content) -- send_busy_message(sock, parent) @@ -477,7 +509,7 @@ end ------------------------------------------------------------------------------- -- SETUP -local kernel_sockets = { +kernel_sockets = { { name = 'heartbeat_sock', sock_type = zmq.REP, port = 'hb', handler = on_hb_read }, { name = 'control_sock', sock_type = zmq.ROUTER, port = 'control', handler = on_control_read }, { name = 'stdin_sock', sock_type = zmq.ROUTER, port = 'stdin', handler = on_stdin_read }, From cf8ce9a7d1b79ca92eb03dcb8a7d81600837997f Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 20:00:20 +0200 Subject: [PATCH 12/94] Replaced iPy by IPy --- {iPyLua => IPyLua}/dkjson.lua | 0 {iPyLua => IPyLua}/uuid.lua | 0 iPyLuaKernel.lua => IPyLuaKernel.lua | 16 ++++++++-------- LICENSE | 2 +- README.md | 20 ++++++++++---------- iPyLua.sh => runIPyLua.sh | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) rename {iPyLua => IPyLua}/dkjson.lua (100%) rename {iPyLua => IPyLua}/uuid.lua (100%) rename iPyLuaKernel.lua => IPyLuaKernel.lua (97%) rename iPyLua.sh => runIPyLua.sh (50%) diff --git a/iPyLua/dkjson.lua b/IPyLua/dkjson.lua similarity index 100% rename from iPyLua/dkjson.lua rename to IPyLua/dkjson.lua diff --git a/iPyLua/uuid.lua b/IPyLua/uuid.lua similarity index 100% rename from iPyLua/uuid.lua rename to IPyLua/uuid.lua diff --git a/iPyLuaKernel.lua b/IPyLuaKernel.lua similarity index 97% rename from iPyLuaKernel.lua rename to IPyLuaKernel.lua index 96e0ec1..96ec67b 100644 --- a/iPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -1,5 +1,5 @@ --[[ - iPyLua + IPyLua Copyright (c) 2015 Francisco Zamora-Martinez. Simplified, less deps and making it work. @@ -23,31 +23,31 @@ end local output_filters = {} local help_functions = {} do - -- Setting iPyLua in the registry allow to extend this implementation with + -- Setting IPyLua in the registry allow to extend this implementation with -- specifications due to other Lua modules. For instance, APRIL-ANN uses this -- variable to configure how to encode images, plots, matrices, ... As well -- as introducing a sort of inline documentation. -- - -- To extend iPyLua output you need to stack into registry - -- iPyLua.output_filters new functions which receive an object and return a + -- To extend IPyLua output you need to stack into registry + -- IPyLua.output_filters new functions which receive an object and return a -- data table as expected by IPython, that is, a data table with pairs of { -- [mime_type] = representation, ... } followed by the metadata table -- (optional). local reg = debug.getregistry() - reg.iPyLua = { + reg.IPyLua = { output_filters = output_filters, help_functions = help_functions, } end -local json = require "iPyLua.dkjson" +local json = require "IPyLua.dkjson" local zmq = require 'lzmq' local zmq_poller = require 'lzmq.poller' local z_NOBLOCK, z_POLLIN = zmq.NOBLOCK, zmq.POLL_IN local z_RCVMORE, z_SNDMORE = zmq.RCVMORE, zmq.SNDMORE local zassert = zmq.assert -local uuid = require 'iPyLua.uuid' -- TODO: randomseed or luasocket or something else +local uuid = require 'IPyLua.uuid' -- TODO: randomseed or luasocket or something else local HMAC = '' local MSG_DELIM = '' @@ -418,7 +418,7 @@ local shell_routes = { local content = { protocol_version = {4, 0}, language_version = {tonumber(major), tonumber(minor)}, - language = 'iPyLua', + language = 'IPyLua', } ipmsg_send(sock, { session=session, diff --git a/LICENSE b/LICENSE index ca8d8da..166c8d4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -iPyLua +IPyLua Copyright (c) 2015 Francisco Zamora-Martinez. Simplified, less deps and making it work. diff --git a/README.md b/README.md index fb9dfd2..f3b22b3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# iPyLua: Lua IPython Kernel +# IPyLua: Lua IPython Kernel This is a kernel to support Lua with [IPython](http://ipython.org). It is pure Lua and should work with both Lua and LuaJIT. @@ -24,12 +24,12 @@ The installation process is janky right now. * Create a profile with IPython: ``` -$ ipython profile create iPyLua +$ ipython profile create IPyLua ``` - * Modify the profile's `ipython_config.py` to use iPyLua. This - will be at either `~/.config/ipython/profile_iPyLua/ipython_config.py` or - `~/.ipython/profile_iPyLua/ipython_config.py`: + * Modify the profile's `ipython_config.py` to use IPyLua. This + will be at either `~/.config/ipython/profile_IPyLua/ipython_config.py` or + `~/.ipython/profile_IPyLua/ipython_config.py`: ```Python # Configuration file for ipython. @@ -38,7 +38,7 @@ c = get_config() c.KernelManager.kernel_cmd = [ "luajit", # select your Lua interpreter here (lua5.2, lua5.1, luajit) - "iPyLua/iPyLuaKernel.lua", # probably need full path + "IPyLua/IPyLuaKernel.lua", # probably need full path "{connection_file}" ] @@ -50,9 +50,9 @@ c.Session.keyfile = b'' * Invoke IPython with this Lua kernel: ``` -$ ipython console --profile iPyLua +$ ipython console --profile IPyLua # or -$ ipython notebook --profile iPyLua +$ ipython notebook --profile IPyLua ``` ## TODO @@ -82,10 +82,10 @@ And of course thanks to the [IPython folks ](http://ipython.org/citing.html). ## LICENSE -**iPyLua** is distributed under the +**IPyLua** is distributed under the [MIT License](http://opensource.org/licenses/mit-license.php). -> iPyLua +> IPyLua > Copyright (c) 2015 Francisco Zamora-Martinez. Simplified, less deps and making > it work. > diff --git a/iPyLua.sh b/runIPyLua.sh similarity index 50% rename from iPyLua.sh rename to runIPyLua.sh index 7d2ad28..4d9b972 100755 --- a/iPyLua.sh +++ b/runIPyLua.sh @@ -1,6 +1,6 @@ #!/bin/bash how=$1 if [[ -z $how ]]; then how="console"; fi -ipython $how --profile=iPyLua --colors=Linux --ConsoleWidget.font_size=12 -pid=$(pgrep -f "lua5.2 .*iPyLua/iPyLuaKernel") +ipython $how --profile=IPyLua --colors=Linux --ConsoleWidget.font_size=12 +pid=$(pgrep -f "lua5.2 .*IPyLua/IPyLuaKernel") if [[ ! -z $pid ]]; then kill -9 $pid; fi From 1ba351a9f0f523fa0a0fac110f9a8835efdce178 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 20:04:13 +0200 Subject: [PATCH 13/94] Allowing ? at start or end of the line --- IPyLuaKernel.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 96ec67b..94b2f1d 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -360,8 +360,8 @@ local function execute_code(parent) env_parent = parent env_session = session env_source = code - if code:find("%?+\n?$") then - return help(code:gsub("%?+\n?$", "")) + if code:find("%?+\n?$") or code:find("^%?+") then + return help(code:match("%?*([^?]*)%?*\n?$")) else if code:sub(1,1) == "%" then code = ("_G[%q]()"):format(code:gsub("\n","")) From 93848da2eae52b3b74431eac1e0f853fbf82eaef Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 20:09:17 +0200 Subject: [PATCH 14/94] Minor change in doc --- IPyLuaKernel.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 94b2f1d..36dc846 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -164,11 +164,12 @@ end ------------------------------------------------------------------------------- local function help(str) + if #str == 0 then str = nil end for i=#help_functions,1,-1 do local data,metadata = help_functions[i](str) if data then pyout(data,metadata) return true end end - return nil,"Documentation not found" + return nil,("Documentation not found%s"):format(str and " for object: "..str or "") end -- environment where all code is executed @@ -361,7 +362,7 @@ local function execute_code(parent) env_session = session env_source = code if code:find("%?+\n?$") or code:find("^%?+") then - return help(code:match("%?*([^?]*)%?*\n?$")) + return help(code:match("^%?*([^?\n]*)%?*\n?$")) else if code:sub(1,1) == "%" then code = ("_G[%q]()"):format(code:gsub("\n","")) From 149c5839a1f8d9ef82021d51c5bce96ce4871e54 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 20:10:45 +0200 Subject: [PATCH 15/94] Changed executable name --- runIPyLua.sh => ipylua | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename runIPyLua.sh => ipylua (100%) diff --git a/runIPyLua.sh b/ipylua similarity index 100% rename from runIPyLua.sh rename to ipylua From a6c6d67fa81f31409e84306916c8ba40cc1af3e4 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sat, 3 Oct 2015 20:14:10 +0200 Subject: [PATCH 16/94] Added \n after printing --- IPyLuaKernel.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 36dc846..e4b096f 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -228,9 +228,9 @@ do footer = ("# %s with %d array part, %d hash part"):format(tostring(obj), #obj, #keys) end table.insert(tbl, footer) - return { ["text/plain"]=table.concat(tbl, "\n") } + return { ["text/plain"]=table.concat(tbl, "\n").."\n" } else - return { ["text/plain"]=tostring(obj) } + return { ["text/plain"]=tostring(obj).."\n" } end end) @@ -258,14 +258,14 @@ do local args = table.pack(...) for i=1,#args do args[i]=stringfy(args[i]) end local str = table.concat(args,"\t") - pyout({ ["text/plain"] = str }) + pyout({ ["text/plain"] = str.."\n" }) end env_G.io.write = function(...) local args = table.pack(...) for i=1,#args do args[i]=stringfy(args[i]) end local str = table.concat(table.pack(...)) - pyout({ ["text/plain"] = str }) + pyout({ ["text/plain"] = str.."\n" }) end env_G.vars = function() From 91622947da24ad341e0e4303dd9c552b5ab3b92d Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sun, 4 Oct 2015 00:28:05 +0200 Subject: [PATCH 17/94] Added demo.ipynb --- IPyLuaKernel.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index e4b096f..4b1110b 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -193,8 +193,9 @@ do }) end - local stringfy = function(v) + local stringfy = function(v,use_quotes) local v_str = tostring(v) + if type(v) == "string" and use_quotes then v_str = ("%q"):format(v) end return not v_str:find("\n") and v_str or type(v) end @@ -207,7 +208,7 @@ do do local max = false for k,v in ipairs(obj) do - table.insert(tbl, ("\t[%d] = %s,"):format(k,stringfy(v))) + table.insert(tbl, ("\t[%d] = %s,"):format(k,stringfy(v,true))) if k >= MAX then max=true break end end if max then table.insert(tbl, "\t...") end @@ -220,8 +221,8 @@ do end table.sort(keys, function(a,b) return tostring(a) < tostring(b) end) for i,k in ipairs(keys) do - table.insert(tbl, ("\t[%q] = %s,"):format(stringfy(k), - stringfy(obj[k]))) + table.insert(tbl, ("\t[%s] = %s,"):format(stringfy(k,true), + stringfy(obj[k],true))) if i >= MAX then max=true break end end if max then table.insert(tbl, "\t...") end From f8e3e7065ca5b08ac42d41447e6ab3412dcf1efb Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sun, 4 Oct 2015 12:06:18 +0200 Subject: [PATCH 18/94] Heartbeat is executed in an independent thread, out cells can be highlighted when its html element has id=ipylua_static_code --- IPyLuaKernel.lua | 85 +++++++++++++++++++++++++++++++++++------------ README.md | 3 ++ config/custom.css | 10 ++++++ config/custom.js | 47 ++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 22 deletions(-) create mode 100644 config/custom.css create mode 100644 config/custom.js diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 4b1110b..6191239 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -43,6 +43,7 @@ end local json = require "IPyLua.dkjson" local zmq = require 'lzmq' local zmq_poller = require 'lzmq.poller' +local zthreads = require "lzmq.threads" local z_NOBLOCK, z_POLLIN = zmq.NOBLOCK, zmq.POLL_IN local z_RCVMORE, z_SNDMORE = zmq.RCVMORE, zmq.SNDMORE local zassert = zmq.assert @@ -204,7 +205,7 @@ do function(obj, MAX) local tt,footer = type(obj) if tt == "table" then - local tbl = {} + local tbl = { "{" } do local max = false for k,v in ipairs(obj) do @@ -226,12 +227,21 @@ do if i >= MAX then max=true break end end if max then table.insert(tbl, "\t...") end - footer = ("# %s with %d array part, %d hash part"):format(tostring(obj), #obj, #keys) + footer = ("-- %s with %d array part, %d hash part"):format(tostring(obj), #obj, #keys) end + table.insert(tbl, "}") table.insert(tbl, footer) - return { ["text/plain"]=table.concat(tbl, "\n").."\n" } + local str = table.concat(tbl, "\n") + return { + ["text/plain"]=str.."\n", + ["text/html"]=('%s'):format(str), + } else - return { ["text/plain"]=tostring(obj).."\n" } + local str = tostring(obj) + return { + ["text/plain"]=str.."\n", + ["text/html"]=('
%s
'):format(str), + } end end) @@ -482,10 +492,7 @@ end -- ZMQ Read Handlers local function on_hb_read( sock ) - -- read the data and send a pong - local data = zassert( sock:recv(zmq.NOBLOCK) ) - -- TODO: handle 'timeout' error - sock:send('pong') + end local function on_control_read( sock ) @@ -512,7 +519,7 @@ end -- SETUP kernel_sockets = { - { name = 'heartbeat_sock', sock_type = zmq.REP, port = 'hb', handler = on_hb_read }, + -- { name = 'heartbeat_sock', sock_type = zmq.REP, port = 'hb', handler = on_hb_read }, { name = 'control_sock', sock_type = zmq.ROUTER, port = 'control', handler = on_control_read }, { name = 'stdin_sock', sock_type = zmq.ROUTER, port = 'stdin', handler = on_stdin_read }, { name = 'shell_sock', sock_type = zmq.ROUTER, port = 'shell', handler = on_shell_read }, @@ -522,24 +529,57 @@ kernel_sockets = { local z_ctx = zmq.context() local z_poller = zmq_poller(#kernel_sockets) for _, v in ipairs(kernel_sockets) do - -- TODO: error handling in here - local sock = zassert( z_ctx:socket(v.sock_type) ) + if v.name ~= "heartbeat_sock" then + -- TODO: error handling in here + local sock = zassert( z_ctx:socket(v.sock_type) ) - local conn_obj = kernel.connection_obj - local addr = string.format('%s://%s:%s', - conn_obj.transport, - conn_obj.ip, - conn_obj[v.port..'_port']) + local conn_obj = kernel.connection_obj + local addr = string.format('%s://%s:%s', + conn_obj.transport, + conn_obj.ip, + conn_obj[v.port..'_port']) - zassert( sock:bind(addr) ) - - if v.name ~= 'iopub_sock' then -- avoid polling from iopub - z_poller:add(sock, zmq.POLLIN, v.handler) - end + zassert( sock:bind(addr) ) + + if v.name ~= 'iopub_sock' then -- avoid polling from iopub + z_poller:add(sock, zmq.POLLIN, v.handler) + end - kernel[v.name] = sock + kernel[v.name] = sock + end end +-- heartbeat is controlled through an independent thread, allowing the main +-- thread to manage interactive commands given by the IPython +local thread = zthreads.run(z_ctx, + function(conn_obj, require, string, print) + local zmq = require "lzmq" + local z_ctx = require"lzmq.threads".get_parent_ctx() + local zassert = zmq.assert + local v = { + name = 'heartbeat_sock', + sock_type = zmq.REP, + port = 'hb', + } + local sock = zassert( z_ctx:socket(v.sock_type) ) + local addr = string.format('%s://%s:%s', + conn_obj.transport, + conn_obj.ip, + conn_obj[v.port..'_port']) + zassert( sock:bind(addr) ) + while true do + -- read the data and send a pong + local data,msg = sock:recv() + if msg ~= "timeout" then + if not data then break end + -- TODO: handle 'timeout' error + sock:send('pong') + end + end + end, + kernel.connection_obj, require, string, print) +thread:start(true,true) + ------------------------------------------------------------------------------- -- POLL then SHUTDOWN @@ -550,3 +590,4 @@ for _, v in ipairs(kernel_sockets) do kernel[v.name]:close() end z_ctx:term() +thread:join() diff --git a/README.md b/README.md index f3b22b3..1f15dc2 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,9 @@ c.Session.key = b'' c.Session.keyfile = b'' ``` + * Copy the content of `config` folder into your + `~/.ipython/profile_IPyLua/static/custom/` folder. + * Invoke IPython with this Lua kernel: ``` diff --git a/config/custom.css b/config/custom.css new file mode 100644 index 0000000..218aae4 --- /dev/null +++ b/config/custom.css @@ -0,0 +1,10 @@ +/* disable smoothing on images (needed to visualize filters) */ +img { + image-rendering: optimizeSpeed; + image-rendering: -moz-crisp-edges; /* Firefox */ + image-rendering: -o-crisp-edges; /* Opera */ + image-rendering: -webkit-optimize-contrast; /* Chrome (and eventually Safari) */ + image-rendering: optimize-contrast; /* CSS3 Proposed */ + -ms-interpolation-mode: nearest-neighbor; /* IE8+ */ + +} diff --git a/config/custom.js b/config/custom.js new file mode 100644 index 0000000..079f424 --- /dev/null +++ b/config/custom.js @@ -0,0 +1,47 @@ +function checkDOMChange() { + $("#ipylua_static_code").each( + function() { + var $this = $(this), + $code = $this.html(); + $this.empty(); + var myCodeMirror = CodeMirror(this, { + value: $code, + mode: 'lua', + lineNumbers: false, + readOnly: true + }); + }).attr("id", "highlighted_ipylua_static_code"); + setTimeout( checkDOMChange, 500 ); +} +checkDOMChange(); + +$([IPython.events]).on('notebook_loaded.Notebook', function(){ + // add here logic that should be run once per **notebook load** + // (!= page load), like restarting a checkpoint + var md = IPython.notebook.metadata + if(md.language){ + console.log('language already defined and is :', md.language); + } else { + md.language = 'lua' ; + console.log('add metadata hint that language is lua'); + } +}); + +// logic per page-refresh +$([IPython.events]).on("app_initialized.NotebookApp", function () { + $('head').append(''); + + + IPython.CodeCell.options_default['cm_config']['mode'] = 'lua'; + + CodeMirror.requireMode('lua', function() { + IPython.OutputArea.prototype._should_scroll = function(){return false} + cells = IPython.notebook.get_cells(); + for(var i in cells){ + c = cells[i]; + if (c.cell_type === 'code') { + c.auto_highlight() + } + } + }); +}); From 75b346f7ff66811416e8413fa80ebe74eb037ea2 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sun, 4 Oct 2015 18:45:36 +0200 Subject: [PATCH 19/94] Simplified codemirror in output cells --- config/custom.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/config/custom.js b/config/custom.js index 079f424..a32ba92 100644 --- a/config/custom.js +++ b/config/custom.js @@ -1,16 +1,6 @@ function checkDOMChange() { - $("#ipylua_static_code").each( - function() { - var $this = $(this), - $code = $this.html(); - $this.empty(); - var myCodeMirror = CodeMirror(this, { - value: $code, - mode: 'lua', - lineNumbers: false, - readOnly: true - }); - }).attr("id", "highlighted_ipylua_static_code"); + $("#ipylua_static_code").each(CodeMirror.fromTextArea) + .attr("id", "highlighted_ipylua_static_code"); setTimeout( checkDOMChange, 500 ); } checkDOMChange(); @@ -31,7 +21,6 @@ $([IPython.events]).on('notebook_loaded.Notebook', function(){ $([IPython.events]).on("app_initialized.NotebookApp", function () { $('head').append(''); - IPython.CodeCell.options_default['cm_config']['mode'] = 'lua'; CodeMirror.requireMode('lua', function() { From 647a090598297a3a50445ed9f6e709cf6762aecc Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sun, 4 Oct 2015 20:14:29 +0200 Subject: [PATCH 20/94] Added auto-completion :-) --- IPyLua/rlcompleter.lua | 195 +++++++++++++++++++++++++++++++++++++++++ IPyLuaKernel.lua | 31 ++++++- 2 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 IPyLua/rlcompleter.lua diff --git a/IPyLua/rlcompleter.lua b/IPyLua/rlcompleter.lua new file mode 100644 index 0000000..90da3ce --- /dev/null +++ b/IPyLua/rlcompleter.lua @@ -0,0 +1,195 @@ +--[[ + IPyLua + + Copyright (c) 2015 Francisco Zamora-Martinez. + + Released under the MIT License, see the LICENSE file. + + https://github.com/neomantra/lua_ipython_kernel + + usage: lua IPyLuaKernel.lua CONNECTION_FILENAME +--]] + +-- This file is based on the work of Patrick Rapin, adapted by Reuben Thomas: +-- https://github.com/rrthomas/lua-rlcompleter/blob/master/rlcompleter.lua + +local ok,lfs = pcall(require, "lfs") if not ok then lfs = nil end + +local type = type + +-- The list of Lua keywords +local keywords = { + 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', + 'function', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', + 'return', 'then', 'true', 'until', 'while' +} + +local word_break_chars_pattern = " \t\n\"\\'><=%;%:%+%-%*%/%%%^%~%#%{%}%(%)%[%]%.%," +local last_word_pattern = ("[%s]?([^%s]*)$"):format(word_break_chars_pattern, + word_break_chars_pattern) +local endpos_pattern = ("(.*)[%s]?"):format(word_break_chars_pattern) + +-- Returns index_table field from a metatable, which is a copy of __index table +-- in APRIL-ANN +local function get_index(mt) + if type(mt.__index) == "table" then return mt.__index end + return mt.index_table -- only for APRIL-ANN +end + +-- This function needs to be called to complete current input line +local function do_completion(line, cursor_pos, env_G, env) + local line = line:match("([^\n]*)[\n]?$") + -- Extract the last word. + local word=line:match( last_word_pattern ) or "" + local startpos,endpos=1,#(line:match(endpos_pattern) or "") + + -- Helper function registering possible completion words, verifying matches. + local matches = {} + local function add(value) + value = tostring(value) + if value:match("^" .. word) then + matches[#matches + 1] = value + end + end + + -- This function does the same job as the default completion of readline, + -- completing paths and filenames. + local function filename_list(str) + if lfs then + local path, name = str:match("(.*)[\\/]+(.*)") + path = (path or ".") .. "/" + name = name or str + for f in lfs.dir(path) do + if (lfs.attributes(path .. f) or {}).mode == 'directory' then + add(f .. "/") + else + add(f) + end + end + end + end + + -- This function is called in a context where a keyword or a global + -- variable can be inserted. Local variables cannot be listed! + local function add_globals() + for _, k in ipairs(keywords) do + add(k) + end + for k in pairs(env_G) do + add(k) + end + for k in pairs(env) do + add(k) + end + end + + -- Main completion function. It evaluates the current sub-expression + -- to determine its type. Currently supports tables fields, global + -- variables and function prototype completion. + local function contextual_list(expr, sep, str) + if str then + return filename_list(str) + end + if expr and expr ~= "" then + local v = load("return " .. expr, nil, nil, env) + if v then + v = v() + local t = type(v) + if sep == '.' or sep == ':' then + if t == 'table' then + for k, v in pairs(v) do + if type(k) == 'string' and (sep ~= ':' or type(v) == "function") then + add(k) + end + end + end + if (t == 'string' or t == 'table' or t == 'userdata') and getmetatable(v) then + local aux = v + repeat + local mt = getmetatable(aux) + local idx = get_index(mt) + if idx and type(idx) == 'table' then + for k,v in pairs(idx) do + add(k) + end + end + if rawequal(aux,idx) then break end -- avoid infinite loops + aux = idx + until not aux or not getmetatable(aux) + end + elseif sep == '[' then + if t == 'table' then + for k in pairs(v) do + if type(k) == 'number' then + add(k .. "]") + end + end + if word ~= "" then add_globals() end + end + end + end + end + if #matches == 0 then + add_globals() + end + end + + -- This complex function tries to simplify the input line, by removing + -- literal strings, full table constructors and balanced groups of + -- parentheses. Returns the sub-expression preceding the word, the + -- separator item ( '.', ':', '[', '(' ) and the current string in case + -- of an unfinished string literal. + local function simplify_expression(expr) + -- Replace annoying sequences \' and \" inside literal strings + expr = expr:gsub("\\(['\"])", function (c) + return string.format("\\%03d", string.byte(c)) + end) + local curstring + -- Remove (finished and unfinished) literal strings + while true do + local idx1, _, equals = expr:find("%[(=*)%[") + local idx2, _, sign = expr:find("(['\"])") + if idx1 == nil and idx2 == nil then + break + end + local idx, startpat, endpat + if (idx1 or math.huge) < (idx2 or math.huge) then + idx, startpat, endpat = idx1, "%[" .. equals .. "%[", "%]" .. equals .. "%]" + else + idx, startpat, endpat = idx2, sign, sign + end + if expr:sub(idx):find("^" .. startpat .. ".-" .. endpat) then + expr = expr:gsub(startpat .. "(.-)" .. endpat, " STRING ") + else + expr = expr:gsub(startpat .. "(.*)", function (str) + curstring = str + return "(CURSTRING " + end) + end + end + expr = expr:gsub("%b()"," PAREN ") -- Remove groups of parentheses + expr = expr:gsub("%b{}"," TABLE ") -- Remove table constructors + -- Avoid two consecutive words without operator + expr = expr:gsub("(%w)%s+(%w)","%1|%2") + expr = expr:gsub("%s+", "") -- Remove now useless spaces + -- This main regular expression looks for table indexes and function calls. + return curstring, expr:match("([%.%w%[%]_]-)([:%.%[%(])" .. word .. "$") + end + + -- Now call the processing functions and return the list of results. + local str, expr, sep = simplify_expression(line:sub(1, endpos)) + contextual_list(expr, sep, str) + table.sort(matches) + return { + status = "ok", + matches = matches, + cursor_start = 1, + cursor_end = cursor_pos, + matched_text = word, + metadata = {}, + } +end + +return { + do_completion = do_completion, +} diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 6191239..613ab36 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -7,12 +7,11 @@ Original name and copyright: lua_ipython_kernel Copyright (c) 2013 Evan Wies. All rights reserved. - Released under the MIT License, see the LICENSE file. https://github.com/neomantra/lua_ipython_kernel - usage: lua ipython_kernel.lua `connection_file` + usage: lua IPyLuaKernel.lua CONNECTION_FILENAME --]] if #arg ~= 1 then @@ -40,6 +39,7 @@ do } end +local do_completion = require "IPyLua.rlcompleter".do_completion local json = require "IPyLua.dkjson" local zmq = require 'lzmq' local zmq_poller = require 'lzmq.poller' @@ -234,7 +234,7 @@ do local str = table.concat(tbl, "\n") return { ["text/plain"]=str.."\n", - ["text/html"]=('%s'):format(str), + ["text/html"]=('
%s
'):format(str), } else local str = tostring(obj) @@ -380,6 +380,7 @@ local function execute_code(parent) end if code:sub(1,1) == "=" then code = "return " .. code:sub(2) end local ok,err = true,nil + -- TODO: reimplement it to be Lua 5.1 compatible local f,msg = load(code, nil, nil, env) if f then local out = table.pack(xpcall(f, debug.traceback)) @@ -477,12 +478,36 @@ local shell_routes = { end os.exit() end, + + complete_request = function(sock, parent) + parent.content = json.decode(parent.content) + local session = parent.header.session + local header = ipmsg_header( 'complete_reply' ) + local content = do_completion(parent.content.code or parent.content.line, + parent.content.cursor_pos, + env_G, env) + ipmsg_send(sock, { + session=session, + parent=parent, + header=header, + content=content, + }) + end, + + history_request = function(sock, parent) + print("history_requested but not implemented") + end, + + comm_open = function(sock, parent) + print("comm_open but not implemented") + end, } do local function dummy_function() end setmetatable(shell_routes, { __index = function(self, key) + print(key) return rawget(self,key) or dummy_function end, }) From c970c40ac28eabc718673d1051c93442cc05f231 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sun, 4 Oct 2015 20:29:33 +0200 Subject: [PATCH 21/94] Solved problem with globals loaded from other modules --- IPyLua/rlcompleter.lua | 21 ++++++++++----------- IPyLuaKernel.lua | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/IPyLua/rlcompleter.lua b/IPyLua/rlcompleter.lua index 90da3ce..667624c 100644 --- a/IPyLua/rlcompleter.lua +++ b/IPyLua/rlcompleter.lua @@ -37,18 +37,22 @@ local function get_index(mt) end -- This function needs to be called to complete current input line -local function do_completion(line, cursor_pos, env_G, env) +local function do_completion(line, cursor_pos, env_G, env, _G) local line = line:match("([^\n]*)[\n]?$") -- Extract the last word. local word=line:match( last_word_pattern ) or "" local startpos,endpos=1,#(line:match(endpos_pattern) or "") -- Helper function registering possible completion words, verifying matches. + local matches_set = {} local matches = {} local function add(value) value = tostring(value) - if value:match("^" .. word) then - matches[#matches + 1] = value + if not matches_set[value] then + if value:match("^" .. word) then + matches_set[value] = true + matches[#matches + 1] = value + end end end @@ -72,14 +76,9 @@ local function do_completion(line, cursor_pos, env_G, env) -- This function is called in a context where a keyword or a global -- variable can be inserted. Local variables cannot be listed! local function add_globals() - for _, k in ipairs(keywords) do - add(k) - end - for k in pairs(env_G) do - add(k) - end - for k in pairs(env) do - add(k) + for _, k in ipairs(keywords) do add(k) end + for i,tbl in ipairs{env, env_G, _G} do + for k in pairs(tbl) do add(k) end end end diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 613ab36..48d8e35 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -485,7 +485,7 @@ local shell_routes = { local header = ipmsg_header( 'complete_reply' ) local content = do_completion(parent.content.code or parent.content.line, parent.content.cursor_pos, - env_G, env) + env_G, env, _ENV) ipmsg_send(sock, { session=session, parent=parent, From be2bbf674f63456d628a636e6b67e87f995fa0c4 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sun, 4 Oct 2015 22:41:24 +0200 Subject: [PATCH 22/94] Fixing problems in auto-completion --- IPyLua/rlcompleter.lua | 19 +++++++++++++------ IPyLuaKernel.lua | 1 + 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/IPyLua/rlcompleter.lua b/IPyLua/rlcompleter.lua index 667624c..c9a8cc2 100644 --- a/IPyLua/rlcompleter.lua +++ b/IPyLua/rlcompleter.lua @@ -37,7 +37,7 @@ local function get_index(mt) end -- This function needs to be called to complete current input line -local function do_completion(line, cursor_pos, env_G, env, _G) +local function do_completion(line, text, cursor_pos, env_G, env, _G) local line = line:match("([^\n]*)[\n]?$") -- Extract the last word. local word=line:match( last_word_pattern ) or "" @@ -62,7 +62,9 @@ local function do_completion(line, cursor_pos, env_G, env, _G) if lfs then local path, name = str:match("(.*)[\\/]+(.*)") path = (path or ".") .. "/" + if str:sub(1,1) == "/" then path = "/" .. path end name = name or str + if not lfs.attributes(path) then return end for f in lfs.dir(path) do if (lfs.attributes(path .. f) or {}).mode == 'directory' then add(f .. "/") @@ -128,9 +130,7 @@ local function do_completion(line, cursor_pos, env_G, env, _G) end end end - if #matches == 0 then - add_globals() - end + if #matches == 0 and #word == #line then add_globals() end end -- This complex function tries to simplify the input line, by removing @@ -179,12 +179,19 @@ local function do_completion(line, cursor_pos, env_G, env, _G) local str, expr, sep = simplify_expression(line:sub(1, endpos)) contextual_list(expr, sep, str) table.sort(matches) + -- rebuild the list of matches to prepend all required characters + if text ~= "" then + local prefix = line:match("(.*)"..word) + if prefix then + for i,v in ipairs(matches) do matches[i] = prefix .. v end + end + end return { status = "ok", matches = matches, - cursor_start = 1, - cursor_end = cursor_pos, matched_text = word, + -- cursor_start = 1, + -- cusor_end = cursor_pos, metadata = {}, } end diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 48d8e35..e5c2589 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -484,6 +484,7 @@ local shell_routes = { local session = parent.header.session local header = ipmsg_header( 'complete_reply' ) local content = do_completion(parent.content.code or parent.content.line, + parent.content.text, parent.content.cursor_pos, env_G, env, _ENV) ipmsg_send(sock, { From 80d16f890d2eeee204dc0123da838c6838c6327c Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Sun, 4 Oct 2015 22:55:53 +0200 Subject: [PATCH 23/94] Improving use of help function --- IPyLuaKernel.lua | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index e5c2589..3b509cd 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -164,16 +164,8 @@ end ------------------------------------------------------------------------------- -local function help(str) - if #str == 0 then str = nil end - for i=#help_functions,1,-1 do - local data,metadata = help_functions[i](str) - if data then pyout(data,metadata) return true end - end - return nil,("Documentation not found%s"):format(str and " for object: "..str or "") -end - -- environment where all code is executed +local help local new_environment local env_session local env_parent @@ -252,6 +244,14 @@ do end return false end + + function help(obj) + for i=#help_functions,1,-1 do + local data,metadata = help_functions[i](obj) + if data then pyout(data,metadata) return end + end + pyout({ ["text/plain"] = "No documentation found" }) + end function new_environment() local env_G,env = {},{} @@ -283,7 +283,7 @@ do print_obj(env, math.huge) end - env_G.help = function(str) help(str) end + env_G.help = help env_G["%quickref"] = function() local tbl = { @@ -373,7 +373,8 @@ local function execute_code(parent) env_session = session env_source = code if code:find("%?+\n?$") or code:find("^%?+") then - return help(code:match("^%?*([^?\n]*)%?*\n?$")) + help(env[code:match("^%?*([^?\n]*)%?*\n?$")]) + return true else if code:sub(1,1) == "%" then code = ("_G[%q]()"):format(code:gsub("\n","")) From 2213203bb2bde7709f5d9fbc003c09c6762aad9f Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Mon, 5 Oct 2015 09:30:44 +0200 Subject: [PATCH 24/94] Added dependencies at external folder --- .gitmodules | 7 +++++++ external/lua-llthreads2 | 1 + external/lzmq | 1 + 3 files changed, 9 insertions(+) create mode 100644 .gitmodules create mode 160000 external/lua-llthreads2 create mode 160000 external/lzmq diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..cd5b8b1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,7 @@ +[submodule "external/lua-llthreads2"] + path = external/lua-llthreads2 + url = git@github.com:moteus/lua-llthreads2.git +[submodule "external/lzmq"] + path = external/lzmq + url = https://github.com/zeromq/lzmq.git + branch = master diff --git a/external/lua-llthreads2 b/external/lua-llthreads2 new file mode 160000 index 0000000..07cda72 --- /dev/null +++ b/external/lua-llthreads2 @@ -0,0 +1 @@ +Subproject commit 07cda72f53d1c1bb176417797e1ea3d2c6272931 diff --git a/external/lzmq b/external/lzmq new file mode 160000 index 0000000..72dbca0 --- /dev/null +++ b/external/lzmq @@ -0,0 +1 @@ +Subproject commit 72dbca09652bcc8f27a6ae2662f05d8220e25a2f From 790a01b481112dec6130e19a48514d9c36e5e3e5 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Mon, 5 Oct 2015 10:46:03 +0200 Subject: [PATCH 25/94] Updated help function and added low-level pyout function --- IPyLuaKernel.lua | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 3b509cd..51284b0 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -165,7 +165,6 @@ end ------------------------------------------------------------------------------- -- environment where all code is executed -local help local new_environment local env_session local env_parent @@ -245,7 +244,7 @@ do return false end - function help(obj) + local function help(obj) for i=#help_functions,1,-1 do local data,metadata = help_functions[i](obj) if data then pyout(data,metadata) return end @@ -262,6 +261,19 @@ do local env_G = setmetatable(env_G, { __index = _G }) local env = setmetatable(env, { __index = env_G }) + env_G.pyout = function(data,metadata) + metadata = metadata or {} + assert(type(data) == "table", "Needs a table as first argument") + assert(type(metadata) == "table", "Needs nil or table as second argument") + for k,v in pairs(data) do + assert(type(v) == "string", "Expected dictionary of strings") + end + for k,v in pairs(metadata) do + assert(type(v) == "string", "Expected dictionary of strings") + end + pyout(data,metadata) + end + env_G.print = function(...) if select('#',...) == 1 then if print_obj(..., MAX) then return end @@ -297,6 +309,7 @@ do " object is given, the fancy version will be used", " only if it fits in one line, otherwise the type", " of the object will be shown.", + "pyout(data) -> Allow low-level print to IPython", "vars() -> Shows all global variables declared by the user.", } print_obj(table.concat(tbl,"\n")) @@ -373,7 +386,7 @@ local function execute_code(parent) env_session = session env_source = code if code:find("%?+\n?$") or code:find("^%?+") then - help(env[code:match("^%?*([^?\n]*)%?*\n?$")]) + env.help(env[code:match("^%?*([^?\n]*)%?*\n?$")]) return true else if code:sub(1,1) == "%" then From 80a00147a8f6ee66c6c84683e2cd60cda5fefbb4 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Mon, 5 Oct 2015 11:52:06 +0200 Subject: [PATCH 26/94] Minor change in globals --- IPyLua/rlcompleter.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPyLua/rlcompleter.lua b/IPyLua/rlcompleter.lua index c9a8cc2..fa2881e 100644 --- a/IPyLua/rlcompleter.lua +++ b/IPyLua/rlcompleter.lua @@ -130,7 +130,7 @@ local function do_completion(line, text, cursor_pos, env_G, env, _G) end end end - if #matches == 0 and #word == #line then add_globals() end + if #matches == 0 then add_globals() end end -- This complex function tries to simplify the input line, by removing From 48fa5c93a208f174519ed506c06852109d2f0cad Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Mon, 5 Oct 2015 22:20:06 +0200 Subject: [PATCH 27/94] Updated rlcompleter to match at cursor_pos --- IPyLua/rlcompleter.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPyLua/rlcompleter.lua b/IPyLua/rlcompleter.lua index fa2881e..0c99e4a 100644 --- a/IPyLua/rlcompleter.lua +++ b/IPyLua/rlcompleter.lua @@ -38,7 +38,7 @@ end -- This function needs to be called to complete current input line local function do_completion(line, text, cursor_pos, env_G, env, _G) - local line = line:match("([^\n]*)[\n]?$") + local line = line:sub(1,cursor_pos):match("([^\n]*)[\n]?$") -- Extract the last word. local word=line:match( last_word_pattern ) or "" local startpos,endpos=1,#(line:match(endpos_pattern) or "") From 2c9871612292599c837f27bf2c89d31d30a7b82d Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Tue, 6 Oct 2015 12:19:37 +0200 Subject: [PATCH 28/94] Minor changes --- IPyLua/rlcompleter.lua | 13 ++--- IPyLuaKernel.lua | 57 ++++++++++++++------- config/custom.css | 10 ---- custom/custom.css | 98 ++++++++++++++++++++++++++++++++++++ {config => custom}/custom.js | 0 ipylua | 2 +- 6 files changed, 144 insertions(+), 36 deletions(-) delete mode 100644 config/custom.css create mode 100644 custom/custom.css rename {config => custom}/custom.js (100%) diff --git a/IPyLua/rlcompleter.lua b/IPyLua/rlcompleter.lua index fa2881e..b4ca5e6 100644 --- a/IPyLua/rlcompleter.lua +++ b/IPyLua/rlcompleter.lua @@ -1,12 +1,13 @@ --[[ IPyLua - Copyright (c) 2015 Francisco Zamora-Martinez. - + Copyright (c) 2015 Francisco Zamora-Martinez. Simplified, less deps and making + it work. + + https://github.com/pakozm/IPyLua + Released under the MIT License, see the LICENSE file. - - https://github.com/neomantra/lua_ipython_kernel - + usage: lua IPyLuaKernel.lua CONNECTION_FILENAME --]] @@ -15,7 +16,7 @@ local ok,lfs = pcall(require, "lfs") if not ok then lfs = nil end -local type = type +local type = luatype or type -- The list of Lua keywords local keywords = { diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 51284b0..1325d9a 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -3,14 +3,15 @@ Copyright (c) 2015 Francisco Zamora-Martinez. Simplified, less deps and making it work. - - Original name and copyright: lua_ipython_kernel + + https://github.com/pakozm/IPyLua + + Original name and copyright: lua_ipython_kernel, Copyright (c) 2013 Evan Wies. All rights reserved. - - Released under the MIT License, see the LICENSE file. - https://github.com/neomantra/lua_ipython_kernel + Released under the MIT License, see the LICENSE file. + usage: lua IPyLuaKernel.lua CONNECTION_FILENAME --]] @@ -19,8 +20,9 @@ if #arg ~= 1 then os.exit(-1) end -local output_filters = {} +local output_functions = {} local help_functions = {} +local plot_functions = {} do -- Setting IPyLua in the registry allow to extend this implementation with -- specifications due to other Lua modules. For instance, APRIL-ANN uses this @@ -28,16 +30,23 @@ do -- as introducing a sort of inline documentation. -- -- To extend IPyLua output you need to stack into registry - -- IPyLua.output_filters new functions which receive an object and return a + -- IPyLua.output_functions new functions which receive an object and return a -- data table as expected by IPython, that is, a data table with pairs of { -- [mime_type] = representation, ... } followed by the metadata table -- (optional). local reg = debug.getregistry() reg.IPyLua = { - output_filters = output_filters, + output_functions = output_functions, help_functions = help_functions, + plot_functions = plot_functions, } end +local function lookup_function_for_object(obj, stack, ...) + for i=#stack,1,-1 do + local result = table.pack( output_functions[i](obj, ...) ) + if result[1] then return table.unpack(result) end + end +end local do_completion = require "IPyLua.rlcompleter".do_completion local json = require "IPyLua.dkjson" @@ -192,7 +201,7 @@ do end local MAX = 10 - table.insert(output_filters, + table.insert(output_functions, function(obj, MAX) local tt,footer = type(obj) if tt == "table" then @@ -235,20 +244,29 @@ do } end end) + + local function draw(...) + local result = {} + local args = table.pack(...) + for i=1,#args do + local v = args[i] + v.x = lookup_function_for_object( (assert(v.x, "Needs x field")), + plot_functions ) + v.y = lookup_function_for_object( (assert(v.y, "Needs y field")), + plot_functions ) + + end + end local function print_obj(obj, MAX) - for i=#output_filters,1,-1 do - local data,metadata = output_filters[i](obj, MAX) - if data then pyout(data,metadata) return true end - end + local data,metadata = lookup_function_for_object(obj, output_functions, MAX) + if data then pyout(data,metadata) return true end return false end local function help(obj) - for i=#help_functions,1,-1 do - local data,metadata = help_functions[i](obj) - if data then pyout(data,metadata) return end - end + local data,metadata = lookup_function_for_object(obj, help_functions) + if data then pyout(data,metadata) return end pyout({ ["text/plain"] = "No documentation found" }) end @@ -406,6 +424,7 @@ local function execute_code(parent) else ok,err = nil,msg end + collectgarbage("collect") return ok,err end end @@ -592,7 +611,7 @@ end -- heartbeat is controlled through an independent thread, allowing the main -- thread to manage interactive commands given by the IPython local thread = zthreads.run(z_ctx, - function(conn_obj, require, string, print) + function(conn_obj) local zmq = require "lzmq" local z_ctx = require"lzmq.threads".get_parent_ctx() local zassert = zmq.assert @@ -617,7 +636,7 @@ local thread = zthreads.run(z_ctx, end end end, - kernel.connection_obj, require, string, print) + kernel.connection_obj) thread:start(true,true) ------------------------------------------------------------------------------- diff --git a/config/custom.css b/config/custom.css deleted file mode 100644 index 218aae4..0000000 --- a/config/custom.css +++ /dev/null @@ -1,10 +0,0 @@ -/* disable smoothing on images (needed to visualize filters) */ -img { - image-rendering: optimizeSpeed; - image-rendering: -moz-crisp-edges; /* Firefox */ - image-rendering: -o-crisp-edges; /* Opera */ - image-rendering: -webkit-optimize-contrast; /* Chrome (and eventually Safari) */ - image-rendering: optimize-contrast; /* CSS3 Proposed */ - -ms-interpolation-mode: nearest-neighbor; /* IE8+ */ - -} diff --git a/custom/custom.css b/custom/custom.css new file mode 100644 index 0000000..9449d3f --- /dev/null +++ b/custom/custom.css @@ -0,0 +1,98 @@ +/* disable smoothing on images (needed to visualize filters) */ +img { + image-rendering: optimizeSpeed; + image-rendering: -moz-crisp-edges; /* Firefox */ + image-rendering: -o-crisp-edges; /* Opera */ + image-rendering: -webkit-optimize-contrast; /* Chrome (and eventually Safari) */ + image-rendering: optimize-contrast; /* CSS3 Proposed */ + -ms-interpolation-mode: nearest-neighbor; /* IE8+ */ + +} + +/* + + Name: Base16 Ocean Dark + Author: Chris Kempson (http://chriskempson.com) + + CodeMirror template adapted for IPython Notebook by Nikhil Sonnad (https://github.com/nsonnad/base16-ipython-notebook) + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-chrome-devtools) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +/* Uncomment to use a custom font +div#notebook, div.CodeMirror, div.output_area pre, div.output_wrapper, div.prompt { + font-family: 'Custom Font Name', monospace !important; +} +*/ + +/* GLOBALS */ +body {background-color: #2b303b;} +a {color: #8fa1b3;} + +/* INTRO PAGE */ +.toolbar_info, .list_container {color: #dfe1e8;} + +/* NOTEBOOK */ + +/* comment out this line to bring the toolbar back */ +div#maintoolbar, div#header {display: none !important;} + +div#notebook {border-top: none;} + +div.input_prompt {color: #ab7967;} +div.output_prompt {color: #b48ead;} +div.input_area { + border-radius: 0px; + border: 1px solid #4f5b66; +} +div.output_area pre {font-weight: normal; color: #c0c5ce;} +div.output_subarea {font-weight: normal; color: #c0c5ce;} + +.rendered_html table, .rendered_html th, .rendered_html tr, .rendered_html td { + border: 1px #c0c5ce solid; + color: #c0c5ce; +} +div.output_html { font-family: sans-serif; } +table.dataframe tr {border: 1px #c0c5ce;} + +div.cell.selected {border-radius: 0px;} +div.cell.edit_mode {border-radius: 0px; border: thin solid #b48ead;} +div.text_cell_render, div.output_html {color: #c0c5ce;} + +span.ansiblack {color: #343d46;} +span.ansiblue {color: #96b5b4;} +span.ansigray {color: #a7adba;} +span.ansigreen {color: #a3be8c;} +span.ansipurple {color: #b48ead;} +span.ansired {color: #bf616a;} +span.ansiyellow {color: #ebcb8b;} + +div.output_stderr {background-color: #bf616a;} +div.output_stderr pre {color: #dfe1e8;} + +.cm-s-ipython.CodeMirror {background: #2b303b; color: #dfe1e8;} +.cm-s-ipython div.CodeMirror-selected {background: #343d46 !important;} +.cm-s-ipython .CodeMirror-gutters {background: #2b303b; border-right: 0px;} +.cm-s-ipython .CodeMirror-linenumber {color: #65737e;} +.cm-s-ipython .CodeMirror-cursor {border-left: 1px solid #a7adba !important;} + +.cm-s-ipython span.cm-comment {color: #ab7967;} +.cm-s-ipython span.cm-atom {color: #b48ead;} +.cm-s-ipython span.cm-number {color: #b48ead;} + +.cm-s-ipython span.cm-property, .cm-s-ipython span.cm-attribute {color: #a3be8c;} +.cm-s-ipython span.cm-keyword {color: #bf616a;} +.cm-s-ipython span.cm-string {color: #ebcb8b;} +.cm-s-ipython span.cm-operator {color: #ab7967;} +.cm-s-ipython span.cm-builtin {color: #b48ead;} + +.cm-s-ipython span.cm-variable {color: #a3be8c;} +.cm-s-ipython span.cm-variable-2 {color: #8fa1b3;} +.cm-s-ipython span.cm-def {color: #d08770;} +.cm-s-ipython span.cm-error {background: #bf616a; color: #a7adba;} +.cm-s-ipython span.cm-bracket {color: #c0c5ce;} +.cm-s-ipython span.cm-tag {color: #bf616a;} +.cm-s-ipython span.cm-link {color: #b48ead;} + +.cm-s-ipython .CodeMirror-matchingbracket { text-decoration: underline; color: #dfe1e8 !important;} diff --git a/config/custom.js b/custom/custom.js similarity index 100% rename from config/custom.js rename to custom/custom.js diff --git a/ipylua b/ipylua index 4d9b972..db9f595 100755 --- a/ipylua +++ b/ipylua @@ -2,5 +2,5 @@ how=$1 if [[ -z $how ]]; then how="console"; fi ipython $how --profile=IPyLua --colors=Linux --ConsoleWidget.font_size=12 -pid=$(pgrep -f "lua5.2 .*IPyLua/IPyLuaKernel") +pid=$(pgrep -f "lua5.2.*profile_IPyLua.*") if [[ ! -z $pid ]]; then kill -9 $pid; fi From 4639a537282679e5e3a1b3c127e96895d37aba41 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Tue, 6 Oct 2015 12:28:06 +0200 Subject: [PATCH 29/94] Improving connection with external modules --- IPyLuaKernel.lua | 101 +++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 1325d9a..afbc96f 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -20,9 +20,9 @@ if #arg ~= 1 then os.exit(-1) end -local output_functions = {} -local help_functions = {} -local plot_functions = {} +local output_functions +local help_functions +local plot_functions do -- Setting IPyLua in the registry allow to extend this implementation with -- specifications due to other Lua modules. For instance, APRIL-ANN uses this @@ -35,12 +35,17 @@ do -- [mime_type] = representation, ... } followed by the metadata table -- (optional). local reg = debug.getregistry() + reg.IPyLua = reg.IPyLua or {} + output_functions = reg.IPyLua.output_functions or {} + help_functions = reg.IPyLua.help_functions or {} + plot_functions = reg.IPyLua.plot_functions or {} reg.IPyLua = { output_functions = output_functions, - help_functions = help_functions, - plot_functions = plot_functions, + help_functions = help_functions, + plot_functions = plot_functions, } end + local function lookup_function_for_object(obj, stack, ...) for i=#stack,1,-1 do local result = table.pack( output_functions[i](obj, ...) ) @@ -201,49 +206,49 @@ do end local MAX = 10 - table.insert(output_functions, - function(obj, MAX) - local tt,footer = type(obj) - if tt == "table" then - local tbl = { "{" } - do - local max = false - for k,v in ipairs(obj) do - table.insert(tbl, ("\t[%d] = %s,"):format(k,stringfy(v,true))) - if k >= MAX then max=true break end - end - if max then table.insert(tbl, "\t...") end - end - do - local max = false - local keys = {} - for k,v in pairs(obj) do - if type(k) ~= "number" then keys[#keys+1] = k end - end - table.sort(keys, function(a,b) return tostring(a) < tostring(b) end) - for i,k in ipairs(keys) do - table.insert(tbl, ("\t[%s] = %s,"):format(stringfy(k,true), - stringfy(obj[k],true))) - if i >= MAX then max=true break end - end - if max then table.insert(tbl, "\t...") end - footer = ("-- %s with %d array part, %d hash part"):format(tostring(obj), #obj, #keys) - end - table.insert(tbl, "}") - table.insert(tbl, footer) - local str = table.concat(tbl, "\n") - return { - ["text/plain"]=str.."\n", - ["text/html"]=('
%s
'):format(str), - } - else - local str = tostring(obj) - return { - ["text/plain"]=str.."\n", - ["text/html"]=('
%s
'):format(str), - } - end - end) + local basic_output_function = function(obj, MAX) + local tt,footer = type(obj) + if tt == "table" then + local tbl = { "{" } + do + local max = false + for k,v in ipairs(obj) do + table.insert(tbl, ("\t[%d] = %s,"):format(k,stringfy(v,true))) + if k >= MAX then max=true break end + end + if max then table.insert(tbl, "\t...") end + end + do + local max = false + local keys = {} + for k,v in pairs(obj) do + if type(k) ~= "number" then keys[#keys+1] = k end + end + table.sort(keys, function(a,b) return tostring(a) < tostring(b) end) + for i,k in ipairs(keys) do + table.insert(tbl, ("\t[%s] = %s,"):format(stringfy(k,true), + stringfy(obj[k],true))) + if i >= MAX then max=true break end + end + if max then table.insert(tbl, "\t...") end + footer = ("-- %s with %d array part, %d hash part"):format(tostring(obj), #obj, #keys) + end + table.insert(tbl, "}") + table.insert(tbl, footer) + local str = table.concat(tbl, "\n") + return { + ["text/plain"]=str.."\n", + ["text/html"]=('
%s
'):format(str), + } + else + local str = tostring(obj) + return { + ["text/plain"]=str.."\n", + ["text/html"]=('
%s
'):format(str), + } + end + end + table.insert(output_functions, 1, basic_output_function) local function draw(...) local result = {} From 42278cd417efc33279cb86c59a954381ab4a729e Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Tue, 6 Oct 2015 14:12:40 +0200 Subject: [PATCH 30/94] Added pcall at do_completion --- IPyLuaKernel.lua | 28 +++++++++++++++++----------- ipylua | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index afbc96f..82e2cfa 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -519,18 +519,24 @@ local shell_routes = { complete_request = function(sock, parent) parent.content = json.decode(parent.content) - local session = parent.header.session - local header = ipmsg_header( 'complete_reply' ) - local content = do_completion(parent.content.code or parent.content.line, - parent.content.text, - parent.content.cursor_pos, - env_G, env, _ENV) - ipmsg_send(sock, { - session=session, - parent=parent, - header=header, + local ok,content = pcall(do_completion, + parent.content.code or parent.content.line, + parent.content.text, + parent.content.cursor_pos, + env_G, env, _ENV) + if not ok then + print("Error at do_completion") + print(content) + else + local session = parent.header.session + local header = ipmsg_header( 'complete_reply' ) + ipmsg_send(sock, { + session=session, + parent=parent, + header=header, content=content, - }) + }) + end end, history_request = function(sock, parent) diff --git a/ipylua b/ipylua index db9f595..92cbd6a 100755 --- a/ipylua +++ b/ipylua @@ -1,6 +1,6 @@ #!/bin/bash how=$1 -if [[ -z $how ]]; then how="console"; fi +if [[ -z $how ]]; then how="qtconsole"; fi ipython $how --profile=IPyLua --colors=Linux --ConsoleWidget.font_size=12 pid=$(pgrep -f "lua5.2.*profile_IPyLua.*") if [[ ! -z $pid ]]; then kill -9 $pid; fi From 0dfbb81fc145446707583c553d27f0bc327921e8 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Tue, 6 Oct 2015 14:13:00 +0200 Subject: [PATCH 31/94] Added april demo From 449359917edd0592919439926160ff7c6d687748 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Tue, 6 Oct 2015 19:50:27 +0200 Subject: [PATCH 32/94] Adding documentation --- IPyLuaKernel.lua | 59 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 82e2cfa..2d52064 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -48,8 +48,10 @@ end local function lookup_function_for_object(obj, stack, ...) for i=#stack,1,-1 do - local result = table.pack( output_functions[i](obj, ...) ) - if result[1] then return table.unpack(result) end + local result = table.pack( pcall(stack[i], obj, ...) ) + if result[1] and result[2] then + return table.unpack(result, 2) + end end end @@ -249,6 +251,44 @@ do end end table.insert(output_functions, 1, basic_output_function) + + local basic_help_function = function(obj, verbosity) + local html = { + "

IPyLua help

", + "", + "", + (""):format(type(obj)), + } + + local plain = { + ("LuaType: %s\n"):format(type(obj)), + } + + if type(obj) == "function" then + local info = debug.getinfo(obj) + table.insert(html, (""):format(info.isvararg and "..." or info.nparams)) + table.insert(html, (""):format(info.what)) + table.insert(html, (""):format(info.nups)) + + table.insert(plain, ("NumParams: %d"):format(info.isvararg and "..." or info.nparams)) + table.insert(plain, ("What: %s"):format(info.what)) + table.insert(plain, ("Nups: %s"):format(info.nups)) + elseif type(obj) == "table" then + table.insert(html, (""):format(#obj)) + table.insert(plain, ("Length: %d"):format(#obj)) + else + table.insert(html, (""):format(tostring(obj))) + table.insert(plain, ("ToString: %s"):format(tostring(obj))) + end + + table.insert(html, "
KeyValue
LuaType%s
NumParams%d
What%s
Nups%s
Length%d
ToString%s
") + local data = { + ["text/html"] = table.concat(html), + ["text/plain"] = table.concat(plain), + } + return data + end + table.insert(help_functions, 1, basic_help_function) local function draw(...) local result = {} @@ -269,8 +309,8 @@ do return false end - local function help(obj) - local data,metadata = lookup_function_for_object(obj, help_functions) + local function help(obj, ...) + local data,metadata = lookup_function_for_object(obj, help_functions, ...) if data then pyout(data,metadata) return end pyout({ ["text/plain"] = "No documentation found" }) end @@ -297,6 +337,17 @@ do pyout(data,metadata) end + env_G.pyget = function(obj) + return lookup_function_for_object(obj, output_functions, MAX) + end + + env_G.pystr = function(...) + local args = table.pack(...) + for i=1,#args do args[i]=stringfy(args[i]) end + local str = table.concat(args,"\t") + pyout({ ["text/plain"] = str.."\n" }) + end + env_G.print = function(...) if select('#',...) == 1 then if print_obj(..., MAX) then return end From 7154948c83064209cf628707fadc6ea23a9b791a Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Tue, 6 Oct 2015 19:51:46 +0200 Subject: [PATCH 33/94] Minor change --- IPyLuaKernel.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 2d52064..3e71eaf 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -277,7 +277,7 @@ do table.insert(html, ("Length%d"):format(#obj)) table.insert(plain, ("Length: %d"):format(#obj)) else - table.insert(html, ("ToString%s"):format(tostring(obj))) + table.insert(html, ("ToString
%s
"):format(tostring(obj))) table.insert(plain, ("ToString: %s"):format(tostring(obj))) end From 465be2968aaca71923b1054516c880f46055eb2a Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Tue, 6 Oct 2015 22:31:09 +0200 Subject: [PATCH 34/94] Improving help --- IPyLuaKernel.lua | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 3e71eaf..b2457e9 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -289,7 +289,13 @@ do return data end table.insert(help_functions, 1, basic_help_function) - + + local function pcall_wrap(...) + local ok,msg = pcall(...) + if not ok then print(msg) return false end + return true + end + local function draw(...) local result = {} local args = table.pack(...) @@ -305,13 +311,15 @@ do local function print_obj(obj, MAX) local data,metadata = lookup_function_for_object(obj, output_functions, MAX) - if data then pyout(data,metadata) return true end + if data then return pcall_wrap(pyout,data,metadata) end return false end local function help(obj, ...) local data,metadata = lookup_function_for_object(obj, help_functions, ...) - if data then pyout(data,metadata) return end + if data then + if pcall_wrap(pyout,data,metadata) then return end + end pyout({ ["text/plain"] = "No documentation found" }) end @@ -328,12 +336,6 @@ do metadata = metadata or {} assert(type(data) == "table", "Needs a table as first argument") assert(type(metadata) == "table", "Needs nil or table as second argument") - for k,v in pairs(data) do - assert(type(v) == "string", "Expected dictionary of strings") - end - for k,v in pairs(metadata) do - assert(type(v) == "string", "Expected dictionary of strings") - end pyout(data,metadata) end @@ -460,7 +462,9 @@ local function execute_code(parent) env_session = session env_source = code if code:find("%?+\n?$") or code:find("^%?+") then - env.help(env[code:match("^%?*([^?\n]*)%?*\n?$")]) + local x = load("return "..code:match("^%?*([^?\n]*)%?*\n?$"), nil, nil, env) + if x then x = x() end + env.help(x) return true else if code:sub(1,1) == "%" then From a107c2fa771a72c7a0538935718034ac370b8e7a Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 06:37:00 +0200 Subject: [PATCH 35/94] Minor changes --- IPyLua/rlcompleter.lua | 5 ++-- IPyLuaKernel.lua | 56 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/IPyLua/rlcompleter.lua b/IPyLua/rlcompleter.lua index a0b3075..0dcbbd5 100644 --- a/IPyLua/rlcompleter.lua +++ b/IPyLua/rlcompleter.lua @@ -182,8 +182,9 @@ local function do_completion(line, text, cursor_pos, env_G, env, _G) table.sort(matches) -- rebuild the list of matches to prepend all required characters if text ~= "" then - local prefix = line:match("(.*)"..word) - if prefix then + if expr then + local prefix = expr .. (sep or "") + word = prefix .. word for i,v in ipairs(matches) do matches[i] = prefix .. v end end end diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index b2457e9..0b7a761 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -36,13 +36,13 @@ do -- (optional). local reg = debug.getregistry() reg.IPyLua = reg.IPyLua or {} - output_functions = reg.IPyLua.output_functions or {} - help_functions = reg.IPyLua.help_functions or {} - plot_functions = reg.IPyLua.plot_functions or {} + output_functions = reg.IPyLua.output_functions or {} + help_functions = reg.IPyLua.help_functions or {} + plot_functions = reg.IPyLua.plot_functions or {} reg.IPyLua = { - output_functions = output_functions, - help_functions = help_functions, - plot_functions = plot_functions, + output_functions = output_functions, + help_functions = help_functions, + plot_functions = plot_functions, } end @@ -524,7 +524,7 @@ local shell_routes = { local content = { protocol_version = {4, 0}, language_version = {tonumber(major), tonumber(minor)}, - language = 'IPyLua', + language = 'lua', } ipmsg_send(sock, { session=session, @@ -601,6 +601,48 @@ local shell_routes = { comm_open = function(sock, parent) print("comm_open but not implemented") end, + + object_info_request = function(sock, parent) + parent.content = json.decode(parent.content) + local session = parent.header.session + local header = ipmsg_header( 'object_info_reply' ) + local oname = parent.content.oname + -- TODO: handle detail_level + local len + local x = load("return "..oname, nil, nil, env) + if x then + ok,x = pcall(f) + ok,len = pcall(function() return #x end) + if not ok then len = nil end + end + local argspec + if type(x) == "function" then + local info = debug.getinfo(x) + argspec = { args = {} } + if info.isvararg then + argspec.args[1] = "..." + else + for i=1,info.nparams do argspec.args[i] = "arg"..i end + end + end + local content = { + oname = oname, + found = (x~=nil), + ismagic = false, + isalias = false, + namespace = 'global', + type_name = type(x), + string_form = stringfy(x), + length = len, + argspec = argspec, + } + ipmsg_send(sock, { + session=session, + parent=parent, + header=header, + content=content, + }) + end, } do From b501fa4193b2ffc593bd140bb07de20ad5c4093a Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 07:30:58 +0200 Subject: [PATCH 36/94] Added function definition in help string --- IPyLuaKernel.lua | 57 +++++++++++++++++++++++++++++++++++------------- ipylua | 2 +- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 0b7a761..1ab7f46 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -179,6 +179,11 @@ end ------------------------------------------------------------------------------- +local stringfy = function(v,use_quotes) + local v_str = tostring(v) + if type(v) == "string" and use_quotes then v_str = ("%q"):format(v) end + return not v_str:find("\n") and v_str or type(v) +end -- environment where all code is executed local new_environment @@ -201,12 +206,6 @@ do }) end - local stringfy = function(v,use_quotes) - local v_str = tostring(v) - if type(v) == "string" and use_quotes then v_str = ("%q"):format(v) end - return not v_str:find("\n") and v_str or type(v) - end - local MAX = 10 local basic_output_function = function(obj, MAX) local tt,footer = type(obj) @@ -266,21 +265,34 @@ do if type(obj) == "function" then local info = debug.getinfo(obj) - table.insert(html, ("NumParams%d"):format(info.isvararg and "..." or info.nparams)) + definition = {"... = funcname","("} + if info.isvararg then + table.insert(definition, "...") + else + local args = {} + for i=1,info.nparams do table.insert(args, "arg"..i) end + table.insert(definition, table.concat(args,",")) + end + table.insert(definition, ")") + definition = table.concat(definition) + + table.insert(html, ("Def.%s"):format(definition)) + table.insert(html, ("NumParams%s"):format(info.isvararg and "..." or info.nparams)) table.insert(html, ("What%s"):format(info.what)) table.insert(html, ("Nups%s"):format(info.nups)) - table.insert(plain, ("NumParams: %d"):format(info.isvararg and "..." or info.nparams)) - table.insert(plain, ("What: %s"):format(info.what)) - table.insert(plain, ("Nups: %s"):format(info.nups)) + table.insert(plain, ("Def.: %s\n"):format(definition)) + table.insert(plain, ("NumParams: %s\n"):format(info.isvararg and "..." or info.nparams)) + table.insert(plain, ("What: %s\n"):format(info.what)) + table.insert(plain, ("Nups: %s\n"):format(info.nups)) elseif type(obj) == "table" then table.insert(html, ("Length%d"):format(#obj)) - table.insert(plain, ("Length: %d"):format(#obj)) + table.insert(plain, ("Length: %d\n"):format(#obj)) else table.insert(html, ("ToString
%s
"):format(tostring(obj))) - table.insert(plain, ("ToString: %s"):format(tostring(obj))) + table.insert(plain, ("ToString: %s\n"):format(tostring(obj))) end - + table.insert(html, "") local data = { ["text/html"] = table.concat(html), @@ -603,6 +615,7 @@ local shell_routes = { end, object_info_request = function(sock, parent) + print("object_info_request") parent.content = json.decode(parent.content) local session = parent.header.session local header = ipmsg_header( 'object_info_reply' ) @@ -611,19 +624,28 @@ local shell_routes = { local len local x = load("return "..oname, nil, nil, env) if x then - ok,x = pcall(f) + ok,x = pcall(x) + if not ok then x = nil end ok,len = pcall(function() return #x end) if not ok then len = nil end end + local definition local argspec if type(x) == "function" then + definition = {"... = ",oname,"("} local info = debug.getinfo(x) argspec = { args = {} } if info.isvararg then argspec.args[1] = "..." + table.insert(definition, "...") else - for i=1,info.nparams do argspec.args[i] = "arg"..i end + for i=1,info.nparams do + local name = "arg"..i + argspec.args[i] = name + table.insert(definition, name) + end end + table.insert(definition, ")") end local content = { oname = oname, @@ -635,7 +657,12 @@ local shell_routes = { string_form = stringfy(x), length = len, argspec = argspec, + definition = definition and table.concat(definition) or nil, } + if x then + local data = lookup_function_for_object(x, help_functions) + if data then content.docstring = data["text/plain"] end + end ipmsg_send(sock, { session=session, parent=parent, diff --git a/ipylua b/ipylua index 92cbd6a..db9f595 100755 --- a/ipylua +++ b/ipylua @@ -1,6 +1,6 @@ #!/bin/bash how=$1 -if [[ -z $how ]]; then how="qtconsole"; fi +if [[ -z $how ]]; then how="console"; fi ipython $how --profile=IPyLua --colors=Linux --ConsoleWidget.font_size=12 pid=$(pgrep -f "lua5.2.*profile_IPyLua.*") if [[ ! -z $pid ]]; then kill -9 $pid; fi From 652331ac80c2e516a236ba5025e7527c0c46e569 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 07:32:25 +0200 Subject: [PATCH 37/94] Removed debug print --- IPyLuaKernel.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 1ab7f46..4657896 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -615,7 +615,6 @@ local shell_routes = { end, object_info_request = function(sock, parent) - print("object_info_request") parent.content = json.decode(parent.content) local session = parent.header.session local header = ipmsg_header( 'object_info_reply' ) From b81ad8d44207f751720a258e7b168b4912087e8c Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 07:40:14 +0200 Subject: [PATCH 38/94] Fixed problem with autocompletion in console --- IPyLua/rlcompleter.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/IPyLua/rlcompleter.lua b/IPyLua/rlcompleter.lua index 0dcbbd5..315cb2c 100644 --- a/IPyLua/rlcompleter.lua +++ b/IPyLua/rlcompleter.lua @@ -182,6 +182,10 @@ local function do_completion(line, text, cursor_pos, env_G, env, _G) table.sort(matches) -- rebuild the list of matches to prepend all required characters if text ~= "" then + if sep ~= "." then + expr = line:match("([^%(%)%[%]%,%:><=%+%-%*%^'\\\"])*"..word.."$") + sep = "" + end if expr then local prefix = expr .. (sep or "") word = prefix .. word From b5d29a65b3b2d6a649fcd3856f44b711240e4e29 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 08:00:56 +0200 Subject: [PATCH 39/94] Updated custom.js to fix indent=2 --- custom/custom.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/custom/custom.js b/custom/custom.js index a32ba92..352296e 100644 --- a/custom/custom.js +++ b/custom/custom.js @@ -22,7 +22,8 @@ $([IPython.events]).on("app_initialized.NotebookApp", function () { $('head').append(''); IPython.CodeCell.options_default['cm_config']['mode'] = 'lua'; - + IPython.CodeCell.options_default['cm_config']['indentUnit'] = 2; + CodeMirror.requireMode('lua', function() { IPython.OutputArea.prototype._should_scroll = function(){return false} cells = IPython.notebook.get_cells(); From 64f9fb88b91fd2378c9ecd4b53bf53310a4fb935 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 08:07:30 +0200 Subject: [PATCH 40/94] Updated CSS --- custom/custom.css | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/custom/custom.css b/custom/custom.css index 9449d3f..21de18a 100644 --- a/custom/custom.css +++ b/custom/custom.css @@ -1,12 +1,12 @@ /* disable smoothing on images (needed to visualize filters) */ -img { - image-rendering: optimizeSpeed; - image-rendering: -moz-crisp-edges; /* Firefox */ - image-rendering: -o-crisp-edges; /* Opera */ +img { + image-rendering: optimizeSpeed; /* STOP SMOOTHING, GIVE ME SPEED */ + image-rendering: -moz-crisp-edges; /* Firefox */ + image-rendering: -o-crisp-edges; /* Opera */ image-rendering: -webkit-optimize-contrast; /* Chrome (and eventually Safari) */ - image-rendering: optimize-contrast; /* CSS3 Proposed */ - -ms-interpolation-mode: nearest-neighbor; /* IE8+ */ - + image-rendering: pixelated; /* Chrome */ + image-rendering: optimize-contrast; /* CSS3 Proposed */ + -ms-interpolation-mode: nearest-neighbor; /* IE8+ */ } /* From 5145c3bab3c0bf2e3d3a92e82c28d74a151662fa Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 08:11:31 +0200 Subject: [PATCH 41/94] Updated CSS --- custom/custom.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom/custom.css b/custom/custom.css index 21de18a..101de7c 100644 --- a/custom/custom.css +++ b/custom/custom.css @@ -36,7 +36,7 @@ a {color: #8fa1b3;} /* NOTEBOOK */ /* comment out this line to bring the toolbar back */ -div#maintoolbar, div#header {display: none !important;} +/* div#maintoolbar, div#header {display: none !important;} */ div#notebook {border-top: none;} From 16361cc680e930b24c8aec69df4de85aced448f2 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 08:15:16 +0200 Subject: [PATCH 42/94] Updated navbar color --- custom/custom.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/custom/custom.css b/custom/custom.css index 101de7c..0d888ff 100644 --- a/custom/custom.css +++ b/custom/custom.css @@ -38,6 +38,8 @@ a {color: #8fa1b3;} /* comment out this line to bring the toolbar back */ /* div#maintoolbar, div#header {display: none !important;} */ +div.navbar {color:white;} + div#notebook {border-top: none;} div.input_prompt {color: #ab7967;} From 000d27e35cf7eb45cb3139c9927fe29c053fa5f2 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 08:51:37 +0200 Subject: [PATCH 43/94] Updated custom.js --- custom/custom.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/custom/custom.js b/custom/custom.js index 352296e..44897ee 100644 --- a/custom/custom.js +++ b/custom/custom.js @@ -1,9 +1,20 @@ -function checkDOMChange() { - $("#ipylua_static_code").each(CodeMirror.fromTextArea) - .attr("id", "highlighted_ipylua_static_code"); - setTimeout( checkDOMChange, 500 ); -} -checkDOMChange(); +// function checkDOMChange() { +// $("#ipylua_static_code").each(function() { +// var $this = $(this), +// $code = $this.html(); +// $this.empty(); +// var myCodeMirror = CodeMirror(this, { +// value: $code, +// mode: 'lua', +// lineNumbers: false, +// readOnly: true +// }); +// /*CodeMirror.fromTextArea*/ +// }) +// .attr("id", "highlighted_ipylua_static_code"); +// setTimeout( checkDOMChange, 500 ); +// } +// checkDOMChange(); $([IPython.events]).on('notebook_loaded.Notebook', function(){ // add here logic that should be run once per **notebook load** From c6c60b5d12bb538691e39fb27f01503083b1cd7b Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 09:00:11 +0200 Subject: [PATCH 44/94] Solved problems with paths in console --- IPyLua/rlcompleter.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/IPyLua/rlcompleter.lua b/IPyLua/rlcompleter.lua index 315cb2c..5ae719b 100644 --- a/IPyLua/rlcompleter.lua +++ b/IPyLua/rlcompleter.lua @@ -56,13 +56,15 @@ local function do_completion(line, text, cursor_pos, env_G, env, _G) end end end - + + local filenames_path -- This function does the same job as the default completion of readline, -- completing paths and filenames. local function filename_list(str) if lfs then local path, name = str:match("(.*)[\\/]+(.*)") path = (path or ".") .. "/" + filenames_path = path if str:sub(1,1) == "/" then path = "/" .. path end name = name or str if not lfs.attributes(path) then return end @@ -131,7 +133,9 @@ local function do_completion(line, text, cursor_pos, env_G, env, _G) end end end - if #matches == 0 then add_globals() end + if #matches == 0 then + if not sep or sep~="." then add_globals() end + end end -- This complex function tries to simplify the input line, by removing @@ -182,7 +186,10 @@ local function do_completion(line, text, cursor_pos, env_G, env, _G) table.sort(matches) -- rebuild the list of matches to prepend all required characters if text ~= "" then - if sep ~= "." then + if filenames_path then + expr = filenames_path + sep = "" + elseif sep ~= "." then expr = line:match("([^%(%)%[%]%,%:><=%+%-%*%^'\\\"])*"..word.."$") sep = "" end From ee4c05b65d99da3a7c51705e66c7a5c77be38838 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 10:54:36 +0200 Subject: [PATCH 45/94] Updated execution_count only when store_history=true --- IPyLuaKernel.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 4657896..65f880d 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -547,8 +547,13 @@ local shell_routes = { end, execute_request = function(sock, parent) - local count = next_execution_count() parent.content = json.decode(parent.content) + local count + if parent.content.store_history then + count = next_execution_count() + else + count = current_execution_count() + end -- send_busy_message(kernel.iopub_sock, parent) From ac50b1967586423abd4c137b0758185f59c33e1b Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 10:55:17 +0200 Subject: [PATCH 46/94] Added .* to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index fb28614..1aaa013 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.* *.ipynb # Emacs backups # ################# From 729fd6302ba2a1a2776aad348e97c7a52c8c42bb Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 11:59:50 +0200 Subject: [PATCH 47/94] Added lua def in case its source can be retrieved --- IPyLuaKernel.lua | 83 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 65f880d..806c40b 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -51,6 +51,8 @@ local function lookup_function_for_object(obj, stack, ...) local result = table.pack( pcall(stack[i], obj, ...) ) if result[1] and result[2] then return table.unpack(result, 2) + else + print(result[2]) end end end @@ -264,17 +266,37 @@ do } if type(obj) == "function" then + local definition = {} local info = debug.getinfo(obj) - definition = {"... = funcname","("} - if info.isvararg then - table.insert(definition, "...") - else - local args = {} - for i=1,info.nparams do table.insert(args, "arg"..i) end - table.insert(definition, table.concat(args,",")) + if info.what == "Lua" and info.source and info.linedefined then + local source = info.source + local first = info.linedefined + local last = info.lastlinedefined + local iterator + if source:sub(1,1) == "@" then + iterator = table.pack( io.lines(source:sub(2)) ) + else + iterator = table.pack( source:gmatch("([^\r\n]+)") ) + end + local k=1 + for line in table.unpack( iterator ) do + if first == k then definition = line break end + k=k+1 + end + end + + if #definition == 0 then + definition = {"... = funcname","("} + if info.isvararg then + table.insert(definition, "...") + else + local args = {} + for i=1,info.nparams do table.insert(args, "arg"..i) end + table.insert(definition, table.concat(args,",")) + end + table.insert(definition, ")") + definition = table.concat(definition) end - table.insert(definition, ")") - definition = table.concat(definition) table.insert(html, ("Def.%s"):format(definition)) table.insert(html, ("NumParams%s"):format(info.isvararg and "..." or info.nparams)) @@ -285,9 +307,11 @@ do table.insert(plain, ("NumParams: %s\n"):format(info.isvararg and "..." or info.nparams)) table.insert(plain, ("What: %s\n"):format(info.what)) table.insert(plain, ("Nups: %s\n"):format(info.nups)) + elseif type(obj) == "table" then table.insert(html, ("Length%d"):format(#obj)) table.insert(plain, ("Length: %d\n"):format(#obj)) + else table.insert(html, ("ToString
%s
"):format(tostring(obj))) table.insert(plain, ("ToString: %s\n"):format(tostring(obj))) @@ -302,8 +326,8 @@ do end table.insert(help_functions, 1, basic_help_function) - local function pcall_wrap(...) - local ok,msg = pcall(...) + local function pcall_wrap(func,...) + local ok,msg = xpcall(func,debug.traceback,...) if not ok then print(msg) return false end return true end @@ -636,20 +660,37 @@ local shell_routes = { local definition local argspec if type(x) == "function" then - definition = {"... = ",oname,"("} local info = debug.getinfo(x) - argspec = { args = {} } - if info.isvararg then - argspec.args[1] = "..." - table.insert(definition, "...") + if info.what == "Lua" and info.source and info.linedefined then + local source = info.source + local first = info.linedefined + local iterator + if source:sub(1,1) == "@" then + iterator = table.pack( io.lines(source:sub(2)) ) + else + iterator = table.pack( source:gmatch("([^\r\n]+)") ) + end + local k=1 + for line in table.unpack( iterator ) do + if first == k then definition = {line} break end + k=k+1 + end else - for i=1,info.nparams do - local name = "arg"..i - argspec.args[i] = name - table.insert(definition, name) + definition = {"... = ",oname,"("} + + argspec = { args = {} } + if info.isvararg then + argspec.args[1] = "..." + table.insert(definition, "...") + else + local args = {} + for i=1,info.nparams do + args[i] = "arg"..i + end + table.insert(definition, table.concat(args,",")) end + table.insert(definition, ")") end - table.insert(definition, ")") end local content = { oname = oname, From 93fee39d380447f1265e8a78753393467d14fb38 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 12:02:57 +0200 Subject: [PATCH 48/94] Removed debug print --- IPyLuaKernel.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 806c40b..27bb5cd 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -51,8 +51,6 @@ local function lookup_function_for_object(obj, stack, ...) local result = table.pack( pcall(stack[i], obj, ...) ) if result[1] and result[2] then return table.unpack(result, 2) - else - print(result[2]) end end end From 4a0fbfe31d6f7331b189c2e77455f9417cf7b876 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 7 Oct 2015 14:37:54 +0200 Subject: [PATCH 49/94] Updated module url --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index cd5b8b1..422f6a7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "external/lua-llthreads2"] path = external/lua-llthreads2 - url = git@github.com:moteus/lua-llthreads2.git + url = https://github.com/moteus/lua-llthreads2.git [submodule "external/lzmq"] path = external/lzmq url = https://github.com/zeromq/lzmq.git From e914470abfb31ede1cc42d5dcb7cc19346de7aed Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Thu, 8 Oct 2015 11:03:55 +0200 Subject: [PATCH 50/94] Changed print by show --- IPyLuaKernel.lua | 55 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 27bb5cd..a782de1 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -343,7 +343,7 @@ do end end - local function print_obj(obj, MAX) + local function show_obj(obj, MAX) local data,metadata = lookup_function_for_object(obj, output_functions, MAX) if data then return pcall_wrap(pyout,data,metadata) end return false @@ -384,25 +384,55 @@ do pyout({ ["text/plain"] = str.."\n" }) end - env_G.print = function(...) + env_G.show = function(...) if select('#',...) == 1 then - if print_obj(..., MAX) then return end + if show_obj(..., MAX) then return end end local args = table.pack(...) - for i=1,#args do args[i]=stringfy(args[i]) end + local html = { "
" } + for i=1,#args do + if args[i] == "\n" then + table.insert(html, '
') + else + local component + local data = env_G.pyget(args[i]) + -- TODO: add more mime-types + if data["text/html"] then + component = data["text/html"] + elseif data["image/png"] then + component = (''):format(data["image/png"]) + else + component = ("
%s
"):format(data["text/plain"]) + end + table.insert(html, ('
%s
'):format(component)) + end + -- for text/plain output + args[i]=stringfy(args[i]) + end + table.insert(html, "
") + local str = table.concat(args,"\t") + pyout({ + ["text/plain"] = str.."\n", + ["text/html"] = table.concat(html) + }) + end + + env_G.print = function(...) + local args = table.pack(...) + for i=1,#args do args[i]=tostring(args[i]) end local str = table.concat(args,"\t") pyout({ ["text/plain"] = str.."\n" }) end - + env_G.io.write = function(...) local args = table.pack(...) - for i=1,#args do args[i]=stringfy(args[i]) end + for i=1,#args do args[i]=tostring(args[i]) end local str = table.concat(table.pack(...)) pyout({ ["text/plain"] = str.."\n" }) end env_G.vars = function() - print_obj(env, math.huge) + show_obj(env, math.huge) end env_G.help = help @@ -413,16 +443,11 @@ do "%quickref -> This guide.", "help(object) -> Help about a given object.", "object? -> Help about a given object.", - "print(...) -> Print a list of objects using \\t as separator.", - " If only one object is given, it would be printed", - " in a fancy way when possible. If more than one", - " object is given, the fancy version will be used", - " only if it fits in one line, otherwise the type", - " of the object will be shown.", + "show(...) -> Show using a list of objects by columns (fancy output).", "pyout(data) -> Allow low-level print to IPython", "vars() -> Shows all global variables declared by the user.", } - print_obj(table.concat(tbl,"\n")) + show_obj(table.concat(tbl,"\n")) end env_G["%guiref"] = function() @@ -513,7 +538,7 @@ local function execute_code(parent) if not out[1] then ok,err = nil,out[2] elseif #out > 1 then - env.print(table.unpack(out, 2)) + env.show(table.unpack(out, 2)) end else ok,err = nil,msg From c1e135f9518725eb2cdfaadee59329cef419d54e Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Fri, 9 Oct 2015 01:20:12 +0200 Subject: [PATCH 51/94] Updated to use ipylua_show() metamethod at show() function --- IPyLuaKernel.lua | 111 ++++++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 58 deletions(-) diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index a782de1..261eab8 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -20,6 +20,8 @@ if #arg ~= 1 then os.exit(-1) end +local type = luatype or type + local output_functions local help_functions local plot_functions @@ -209,38 +211,52 @@ do local MAX = 10 local basic_output_function = function(obj, MAX) local tt,footer = type(obj) - if tt == "table" then - local tbl = { "{" } - do - local max = false - for k,v in ipairs(obj) do - table.insert(tbl, ("\t[%d] = %s,"):format(k,stringfy(v,true))) - if k >= MAX then max=true break end - end - if max then table.insert(tbl, "\t...") end - end - do - local max = false - local keys = {} - for k,v in pairs(obj) do - if type(k) ~= "number" then keys[#keys+1] = k end + if (tt == "table" or tt == "userdata") then + local mt = getmetatable(obj) + if mt and mt.ipylua_show then + return mt.ipylua_show(obj) + + elseif mt and mt.__tostring then + local str = tostring(obj) + return { + ["text/plain"]=str.."\n", + ["text/html"]=('
%s
'):format(str), + } + + elseif tt == "table" then + local tbl = { "{" } + do + local max = false + for k,v in ipairs(obj) do + table.insert(tbl, ("\t[%d] = %s,"):format(k,stringfy(v,true))) + if k >= MAX then max=true break end + end + if max then table.insert(tbl, "\t...") end end - table.sort(keys, function(a,b) return tostring(a) < tostring(b) end) - for i,k in ipairs(keys) do - table.insert(tbl, ("\t[%s] = %s,"):format(stringfy(k,true), - stringfy(obj[k],true))) - if i >= MAX then max=true break end + do + local max = false + local keys = {} + for k,v in pairs(obj) do + if type(k) ~= "number" then keys[#keys+1] = k end + end + table.sort(keys, function(a,b) return tostring(a) < tostring(b) end) + for i,k in ipairs(keys) do + table.insert(tbl, ("\t[%s] = %s,"):format(stringfy(k,true), + stringfy(obj[k],true))) + if i >= MAX then max=true break end + end + if max then table.insert(tbl, "\t...") end + footer = ("-- %s with %d array part, %d hash part"):format(tostring(obj), #obj, #keys) end - if max then table.insert(tbl, "\t...") end - footer = ("-- %s with %d array part, %d hash part"):format(tostring(obj), #obj, #keys) + table.insert(tbl, "}") + table.insert(tbl, footer) + local str = table.concat(tbl, "\n") + return { + ["text/plain"]=str.."\n", + ["text/html"]=('
%s
'):format(str), + } end - table.insert(tbl, "}") - table.insert(tbl, footer) - local str = table.concat(tbl, "\n") - return { - ["text/plain"]=str.."\n", - ["text/html"]=('
%s
'):format(str), - } + else local str = tostring(obj) return { @@ -278,7 +294,7 @@ do end local k=1 for line in table.unpack( iterator ) do - if first == k then definition = line break end + if first == k then definition = line:match("^%s*(.*)%s*$") break end k=k+1 end end @@ -330,19 +346,6 @@ do return true end - local function draw(...) - local result = {} - local args = table.pack(...) - for i=1,#args do - local v = args[i] - v.x = lookup_function_for_object( (assert(v.x, "Needs x field")), - plot_functions ) - v.y = lookup_function_for_object( (assert(v.y, "Needs y field")), - plot_functions ) - - end - end - local function show_obj(obj, MAX) local data,metadata = lookup_function_for_object(obj, output_functions, MAX) if data then return pcall_wrap(pyout,data,metadata) end @@ -377,13 +380,6 @@ do return lookup_function_for_object(obj, output_functions, MAX) end - env_G.pystr = function(...) - local args = table.pack(...) - for i=1,#args do args[i]=stringfy(args[i]) end - local str = table.concat(args,"\t") - pyout({ ["text/plain"] = str.."\n" }) - end - env_G.show = function(...) if select('#',...) == 1 then if show_obj(..., MAX) then return end @@ -441,10 +437,13 @@ do local tbl = { "? -> Introduction and overview.", "%quickref -> This guide.", + "%guiref -> Not implemented.", "help(object) -> Help about a given object.", "object? -> Help about a given object.", + "print(...) -> Standard Lua print function.", "show(...) -> Show using a list of objects by columns (fancy output).", - "pyout(data) -> Allow low-level print to IPython", + "pyget(obj) -> Returns low-level data table for pyout() function.", + "pyout(data) -> Allow printing low-level data to IPython", "vars() -> Shows all global variables declared by the user.", } show_obj(table.concat(tbl,"\n")) @@ -452,7 +451,7 @@ do env_G["%guiref"] = function() local tbl = { - "GUI reference not written.", + "GUI reference not implemented.", } end @@ -461,10 +460,6 @@ do end local env,env_G = new_environment() -local function add_return(code) - return code -end - ------------------------------------------------------------------------------- local function send_execute_reply(sock, parent, count, status, err) @@ -521,7 +516,7 @@ local function execute_code(parent) env_session = session env_source = code if code:find("%?+\n?$") or code:find("^%?+") then - local x = load("return "..code:match("^%?*([^?\n]*)%?*\n?$"), nil, nil, env) + local x = load("return "..(code:match("^%?*([^?\n]*)%?*\n?$") or "nil"), nil, nil, env) if x then x = x() end env.help(x) return true @@ -695,7 +690,7 @@ local shell_routes = { end local k=1 for line in table.unpack( iterator ) do - if first == k then definition = {line} break end + if first == k then definition = {line:match("^%s*(.*)%s*$")} break end k=k+1 end else From d2056ba467f9ebb93eb67d08c7a99299e0c7da2a Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Fri, 9 Oct 2015 12:26:29 +0200 Subject: [PATCH 52/94] Added bokeh.lua for plotting --- IPyLua/bokeh.lua | 474 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 474 insertions(+) create mode 100644 IPyLua/bokeh.lua diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua new file mode 100644 index 0000000..ebd0f22 --- /dev/null +++ b/IPyLua/bokeh.lua @@ -0,0 +1,474 @@ +local json = require "IPyLua.dkjson" +local uuid = require "IPyLua.uuid" +local type = luatype or type + +local figure = {} +local figure_methods = {} + +-- error checking + +local function extend(param, n) + local tt = type(param) + if tt == "string" or tt == "number" then + local value = param + param = {} + for i=1,n do param[i] = value end + end + return param +end + +local function serie_totable(s) + if s==nil then return nil end + local tt = type(s) + if tt == "table" or tt == "userdata" then + local mt = getmetatable(s) + if mt and mt.ipylua_totable then + return mt.ipylua_totable(s) + elseif tt == "table" then + return s + end + end + error("serie data type cannot be handled") +end + +local function invert(t) + local r = {} + for k,v in pairs(t) do r[v] = k end + return r +end + +local function check_table(t, ...) + local inv_list = invert({...}) + for i,v in pairs(t) do + assert(inv_list[i], ("unknown field= %s"):format(i)) + end +end + +local function check_value(t, k, ...) + if t[k] then + local inv_list = invert({...}) + local v = t[k] + assert(inv_list[v], ("unknown value= %s at field= %s"):format(v,k)) + end +end + +local function check_type(t, k, ty) + local tt = type(t[k]) + assert(t[k] == nil or tt == ty, + ("type= %s is not valid for field= %s"):format(tt, k)) +end + +local function check_types(t, keys, types) + assert(#keys == #types) + for i=1,#keys do + local k,ty = keys[i],types[i] + check_type(t, k, ty) + end +end + +local function check_mandatory(t, key) + assert(t[key], ("field= %s is mandatory"):format(key)) +end + +local function check_mandatories(t, ...) + for _,key in ipairs(table.pack(...)) do + check_mandatory(t, key) + end +end + +-- private functions + +local function add_observer(self, id, tbl) + self._observers[id] = self._observers[id] or {} + table.insert(self._observers[id], tbl) +end + +local function update_observers(self, id, obj) + for _,ref in ipairs(self._observers[id] or {}) do + for k,v in pairs(ref) do + ref[k] = assert( obj[k] ) + end + end +end + +local function compile_glyph(self, i) + i = i or #self._list + self._list[i] = json.encode( self._list[i] ) +end + +local function append_renderer(self, ref) + table.insert( self._doc.attributes.renderers, ref ) +end + +local function add_simple_glyph(self, name, attributes) + local id = uuid.new() + local list = self._list + attributes.id = id + local glyph = { + attributes = attributes, + type = name, + id = id, + } + local ref = { type = name, id = id, } + add_observer(self, id, ref) + list[#list+1] = glyph + return ref,glyph +end + +local function add_tool(self, name, attributes) + attributes.plot = {} + attributes.plot.type = self._doc.type + attributes.plot.subtype = self._doc.subtype + local tools = self._doc.attributes.tools + tools[#tools+1] = add_simple_glyph(self, name, attributes) +end + +local function add_axis(self, key, params) + check_mandatories(params, "pos", "type") + if not self[key][params.pos] then + local axis_ref,axis = add_simple_glyph(self, params.type, + { tags={}, doc=nil, axis_label=params.label }) + local formatter_ref,formatter = add_simple_glyph(self, "BasicTickFomatter", + { tags={}, doc=nil }) + local ticker_ref,ticker = + add_simple_glyph(self, "BasicTicker", + { tags={}, doc=nil, mantissas={2, 5, 10} }) + + axis.formatter = formatter_ref + axis.ticker = ticker_ref + + append_renderer(self, add_simple_glyph(self, "Grid", + { tags={}, doc=nil, dimension=1, + ticker=axis.ticker })) + append_renderer(self, axis_ref) + + self._doc.attributes[params.pos][1] = axis_ref + self[key][params.pos] = axis + else + local axis = self[key][params.pos] + axis.attributes.axis_label = params.label + axis.type = params.type + update_observers(self, axis.id, axis) + end +end + +local function tool_events(self) + self._doc.attributes.tool_events = + add_simple_glyph( + self, "ToolEvents", + { + geometries = {}, + tags = {}, + doc = nil, + } + ) +end + +-- figure class implementation + +local figure_methods = { + + -- axis + + x_axis = function(self, params) + check_table(params, "type", "label", "pos", "log", "grid", "num_minor_ticks", + "visible", "number_formatter") + check_value(params, "type", "LinearAxis", "CategoricalAxis") + check_value(params, "pos", "below", "above") + check_types(params, + {"log","grid","num_minor_ticks","visible"}, + {"boolean","boolean","number","boolean"}) + + add_axis(self, "_x_axis", params) + return self + end, + + y_axis = function(self, params) + check_table(params, "type", "label", "pos", "log", "grid", "num_minor_ticks", + "visible", "number_formatter") + check_value(params, "type", "LinearAxis", "CategoricalAxis") + check_value(params, "pos", "left", "right") + check_types(params, + {"log","grid","num_minor_ticks","visible"}, + {"boolean","boolean","number","boolean"}) + + add_axis(self, "_y_axis", params) + return self + end, + + -- tools + + tool_box_select = function(self, select_every_mousemove) + select_every_mousemove = select_every_mousemove or true + add_tool(self, "BoxSelectTool", { select_every_mousemove=select_every_mousemove, tags={}, doc=nil }) + compile_glyph(self) + return self + end, + + tool_box_zoom = function(self, dimensions) + dimensions = dimensions or { "width", "height" } + add_tool(self, "BoxZoomTool", { dimensions=dimensions, tags={}, doc=nil }) + compile_glyph(self) + return self + end, + + tool_crosshair = function(self) + add_tool(self, "CrossHair", { tags={}, doc=nil }) + compile_glyph(self) + return self + end, + + tool_lasso_select = function(self, select_every_mousemove) + select_every_mousemove = select_every_mousemove or true + add_tool(self, "LassoSelectTool", { select_every_mousemove=select_every_mousemove, tags={}, doc=nil }) + compile_glyph(self) + return self + end, + + tool_pan = function(self, dimensions) + dimensions = dimensions or { "width", "height" } + add_tool(self, "PanTool", { dimensions=dimensions, tags={}, doc=nil }) + compile_glyph(self) + return self + end, + + tool_reset = function(self) + add_tool(self, "ResetTool", { tags={}, doc=nil }) + compile_glyph(self) + return self + end, + + tool_resize = function(self) + add_tool(self, "ResizeTool", { tags={}, doc=nil }) + compile_glyph(self) + return self + end, + + tool_save = function(self) + add_tool(self, "PreviewSaveTool", { tags={}, doc=nil }) + compile_glyph(self) + return self + end, + + tool_wheel_zoom = function(self, dimensions) + dimensions = dimensions or { "width", "height" } + add_tool(self, "WheelZoomTool", { dimensions=dimensions, tags={}, doc=nil }) + compile_glyph(self) + return self + end, + + -- layer functions + + points = function(self, params) + check_table(params, "x", "y", "glyph", "color", "alpha", "size", + "hover", "legend") + check_value(params, "glyph", "Circle", "Triangle") + check_mandatories(params, "x", "y") + local x = serie_totable(params.x) + local y = serie_totable(params.y) + local color = serie_totable( extend(params.color or "#f22c40", #x) ) + local alpha = serie_totable( extend(params.alpha or 0.8, #x) ) + local hover = serie_totable( params.hover ) + local data = { + x = x, + y = y, + color = color, + fill_alpha = alpha, + hover = hover, + } + local columns = { "x", "y", "fill_alpha", "color" } + if hover then table.insert(columns, "hover") end + + local attributes = { + tags = {}, + doc = nil, + selected = { + ["2d"] = { indices = {} }, + ["1d"] = { indices = {} }, + ["0d"] = { indices = {}, flag=false }, + }, + callback = nil, + data = data, + column_names = columns, + } + local source_ref = add_simple_glyph(self, "ColumnDataSource", attributes) + + local attributes = { + fill_color = { field = "color" }, + tags = {}, + doc = nil, + fill_alpha = { field = "fill_alpha" }, + x = { field = "x" }, + y = { field = "y" }, + } + local points_ref = add_simple_glyph(self, params.glyph or "Circle", attributes) + + local attributes = { + nonselection_glyph = nil, + data_source = source_ref, + tags = {}, + doc = nil, + selection_glyph = nil, + glyph = points_ref, + } + append_renderer(self, add_simple_glyph(self, "GlyphRenderer", attributes) ) + return self + end, + + -- conversion + + to_json = function(self) + local doc_json = json.encode(self._doc) + local tbl = { doc_json } + for i=2,#self._list do + local v = self._list[i] + if type(v) ~= "string" then v = json.encode(v) end + tbl[#tbl+1] = v + end + return ("[%s]"):format(table.concat(tbl, ",")) + end, +} + +local html_template = [[ +(function(global) { + if (typeof (window._bokeh_onload_callbacks) === "undefined"){ + window._bokeh_onload_callbacks = []; + } + function load_lib(url, callback){ + window._bokeh_onload_callbacks.push(callback); + if (window._bokeh_is_loading){ + console.log("Bokeh: BokehJS is being loaded, scheduling callback at", new Date()); + return null; + } + console.log("Bokeh: BokehJS not loaded, scheduling load and callback at", new Date()); + window._bokeh_is_loading = true; + var s = document.createElement('script'); + s.src = url; + s.async = true; + s.onreadystatechange = s.onload = function(){ + Bokeh.embed.inject_css("http://cdn.pydata.org/bokeh/release/bokeh-0.10.0.min.css"); + window._bokeh_onload_callbacks.forEach(function(callback){callback()}); + }; + s.onerror = function(){ + console.warn("failed to load library " + url); + }; + document.getElementsByTagName("head")[0].appendChild(s); + } + + bokehjs_url = "http://cdn.pydata.org/bokeh/release/bokeh-0.10.0.min.js" + + var elt = document.getElementById("$ID"); + if(elt==null) { + console.log("Bokeh: ERROR: autoload.js configured with elementid '$ID' but no matching script tag was found. ") + return false; + } + + // These will be set for the static case + var all_models = [$MODEL]; + + if(typeof(Bokeh) !== "undefined") { + console.log("Bokeh: BokehJS loaded, going straight to plotting"); + Bokeh.embed.inject_plot("$ID", all_models); + } else { + load_lib(bokehjs_url, function() { + console.log("Bokeh: BokehJS plotting callback run at", new Date()) + Bokeh.embed.inject_plot("$ID", all_models); + }); + } + +}(this)); +]] + +local figure_mt = { + __index = figure_methods, + + ipylua_show = function(self) + local html = html_template: + gsub("$ID", uuid.new()): + gsub("$MODEL", self:to_json()) + return { + ["text/html"] = html, + ["text/plain"] = "-- impossible to show ASCII art plots", + } + end, +} + +setmetatable( + figure, + { + __call = function(_,params) + params = params or {} + + local function default(name, value) + params[name] = params[name] or value + end + + default("tools", { "pan", "wheel_zoom", "box_zoom", "resize", "reset", "save" }) + default("width", 480) + default("height", 520) + default("title", nil) -- not necessary but useful to make it explicit + default("xlab", nil) + default("ylab", nil) + default("xlim", nil) + default("ylim", nil) + default("padding_factor", 0.07) + default("plot_width", nil) + default("plot_height", nil) + default("xgrid", true) + default("ygrid", true) + default("xaxes", "below") + default("yaxes", "left") + -- ??? default("theme", "bokeh_theme") ??? + + local self = { _list = {}, _y_axis = {}, _x_axis = {}, _observers={} } + setmetatable(self, figure_mt) + local plot_id = uuid.new() + self._doc = { + type = "Plot", + subtype = "Chart", + id = plot_id, + attributes = { + plot_width = params.plot_width, + plot_height = params.plot_height, + title = params.title, + -- + id = plot_id, + tags = {}, + title_text_font_style = "bold", + title_text_font_size = { value = "12pt" }, + tools = {}, + renderers = {}, + below = {}, + above = {}, + left = {}, + right = {}, + responsive = false, + }, + } + self._list[1] = self._doc + + tool_events(self) + for _,name in ipairs(params.tools) do self["tool_" .. name](self) end + if params.xaxes then + self:x_axis{ label=params.xlab, log=false, grid=params.xgrid, + num_minor_ticks=5, visible=true, number_formatter=tostring, + type="LinearAxis", pos=params.xaxes } + end + if params.yaxes then + self:y_axis{ label=params.ylab, log=false, grid=params.ygrid, + num_minor_ticks=5, visible=true, number_formatter=tostring, + type="LinearAxis", pos=params.yaxes } + end + + return self + end + } +) + +local x = figure():points{ x={1,2,3,4}, y={10,5,20,30} } + +print(x:to_json()) + +return { + figure = figure, +} From 4d4cfc82fdf9c69d2c98d3a6bb8c44b45eabaf45 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Fri, 9 Oct 2015 15:13:22 +0200 Subject: [PATCH 53/94] First plot ready, we can plot points :D --- IPyLua/bokeh.lua | 356 +++++++++++++++++++++++---------------- IPyLua/html_template.lua | 56 ++++++ IPyLuaKernel.lua | 5 +- custom/custom.css | 9 +- 4 files changed, 275 insertions(+), 151 deletions(-) create mode 100644 IPyLua/html_template.lua diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index ebd0f22..d58592b 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -1,11 +1,33 @@ local json = require "IPyLua.dkjson" local uuid = require "IPyLua.uuid" +local html_template = require "IPyLua.html_template" +local null = json.null local type = luatype or type local figure = {} local figure_methods = {} --- error checking +local function default_true(value) + if value == nil then return true else return value end +end + +local function reduce(t, func) + local out = t[1] + for i=2,#t do out = func(out, t[i]) end + return out +end + +local function min(t) return reduce(t, math.min) end +local function max(t) return reduce(t, math.max) end + +local function factors(t) + local out = {} for i=1,#t do out[i] = tostring(t[i]) end return out +end + +local function apply_gap(p, a, b) + local gap = p * (b-a) + return a-gap, b+gap +end local function extend(param, n) local tt = type(param) @@ -37,10 +59,12 @@ local function invert(t) return r end +-- error checking + local function check_table(t, ...) local inv_list = invert({...}) for i,v in pairs(t) do - assert(inv_list[i], ("unknown field= %s"):format(i)) + assert(inv_list[i], ("unknown field %s"):format(i)) end end @@ -48,14 +72,14 @@ local function check_value(t, k, ...) if t[k] then local inv_list = invert({...}) local v = t[k] - assert(inv_list[v], ("unknown value= %s at field= %s"):format(v,k)) + assert(inv_list[v], ("invalid value %s at field %s"):format(v,k)) end end local function check_type(t, k, ty) local tt = type(t[k]) assert(t[k] == nil or tt == ty, - ("type= %s is not valid for field= %s"):format(tt, k)) + ("type %s is not valid for field %s"):format(tt, k)) end local function check_types(t, keys, types) @@ -67,7 +91,7 @@ local function check_types(t, keys, types) end local function check_mandatory(t, key) - assert(t[key], ("field= %s is mandatory"):format(key)) + assert(t[key], ("field %s is mandatory"):format(key)) end local function check_mandatories(t, ...) @@ -78,13 +102,13 @@ end -- private functions -local function add_observer(self, id, tbl) - self._observers[id] = self._observers[id] or {} - table.insert(self._observers[id], tbl) +local function add_reference(self, id, tbl) + self._references[id] = self._references[id] or {} + table.insert(self._references[id], tbl) end -local function update_observers(self, id, obj) - for _,ref in ipairs(self._observers[id] or {}) do +local function update_references(self, id, obj) + for _,ref in ipairs(self._references[id] or {}) do for k,v in pairs(ref) do ref[k] = assert( obj[k] ) end @@ -100,7 +124,7 @@ local function append_renderer(self, ref) table.insert( self._doc.attributes.renderers, ref ) end -local function add_simple_glyph(self, name, attributes) +local function add_simple_glyph(self, name, attributes, subtype) local id = uuid.new() local list = self._list attributes.id = id @@ -109,47 +133,83 @@ local function add_simple_glyph(self, name, attributes) type = name, id = id, } - local ref = { type = name, id = id, } - add_observer(self, id, ref) + self._dict[id] = glyph + local ref = { type = name, subtype = subtype, id = id, } + add_reference(self, id, ref) list[#list+1] = glyph return ref,glyph end local function add_tool(self, name, attributes) - attributes.plot = {} - attributes.plot.type = self._doc.type - attributes.plot.subtype = self._doc.subtype + attributes.plot = self._docref local tools = self._doc.attributes.tools tools[#tools+1] = add_simple_glyph(self, name, attributes) end local function add_axis(self, key, params) check_mandatories(params, "pos", "type") - if not self[key][params.pos] then - local axis_ref,axis = add_simple_glyph(self, params.type, - { tags={}, doc=nil, axis_label=params.label }) - local formatter_ref,formatter = add_simple_glyph(self, "BasicTickFomatter", - { tags={}, doc=nil }) + local doc_axis = self._doc.attributes[params.pos] + if not doc_axis[1] then + local formatter_ref,formatter = add_simple_glyph(self, "BasicTickFormatter", + { tags={}, doc=null }) + local ticker_ref,ticker = add_simple_glyph(self, "BasicTicker", - { tags={}, doc=nil, mantissas={2, 5, 10} }) - - axis.formatter = formatter_ref - axis.ticker = ticker_ref + { tags={}, doc=null, mantissas={2, 5, 10} }) + local axis_ref,axis = add_simple_glyph(self, params.type, + { + tags={}, + doc=null, + axis_label=params.label, + plot = self._docref, + formatter = formatter_ref, + ticker = ticker_ref, + } + ) + + local dim = (key == "x") and 0 or 1 append_renderer(self, add_simple_glyph(self, "Grid", - { tags={}, doc=nil, dimension=1, - ticker=axis.ticker })) + { tags={}, doc=null, dimension=dim, + ticker=ticker_ref, + plot = self._docref, })) append_renderer(self, axis_ref) - self._doc.attributes[params.pos][1] = axis_ref - self[key][params.pos] = axis + doc_axis[1] = axis_ref else - local axis = self[key][params.pos] + local axis = self._dict[ doc_axis[1].id ] axis.attributes.axis_label = params.label axis.type = params.type - update_observers(self, axis.id, axis) + update_references(self, axis.id, axis) + end +end + +local function axis_range(self, a, b, key) + local range = key .. "_range" + local glyph + if not self._doc.attributes[range] then + local ref + ref,glyph = add_simple_glyph(self, "DUMMY", + { callback = null, doc = null, tags = {} }) + self._doc.attributes[range] = ref + else + glyph = self._dict[ self._doc.attributes[range].id ] end + if type(a) == "table" then + assert(not b, "expected one table of factors or two numbers (min, max)") + glyph.type = "FactorRange" + glyph.attributes.factors = a + glyph.attributes["start"] = nil + glyph.attributes["end"] = nil + else + assert(type(a) == "number" and type(b) == "number", + "expected one table of factors or two numbers (min, max)") + glyph.type = "Range1d" + glyph.attributes.factors = nil + glyph.attributes["start"] = a + glyph.attributes["end"] = b + end + update_references(self, glyph.id, glyph) end local function tool_events(self) @@ -159,7 +219,7 @@ local function tool_events(self) { geometries = {}, tags = {}, - doc = nil, + doc = null, } ) end @@ -171,6 +231,7 @@ local figure_methods = { -- axis x_axis = function(self, params) + params = params or {} check_table(params, "type", "label", "pos", "log", "grid", "num_minor_ticks", "visible", "number_formatter") check_value(params, "type", "LinearAxis", "CategoricalAxis") @@ -179,11 +240,12 @@ local figure_methods = { {"log","grid","num_minor_ticks","visible"}, {"boolean","boolean","number","boolean"}) - add_axis(self, "_x_axis", params) + add_axis(self, "x", params) return self end, y_axis = function(self, params) + params = params or {} check_table(params, "type", "label", "pos", "log", "grid", "num_minor_ticks", "visible", "number_formatter") check_value(params, "type", "LinearAxis", "CategoricalAxis") @@ -192,67 +254,93 @@ local figure_methods = { {"log","grid","num_minor_ticks","visible"}, {"boolean","boolean","number","boolean"}) - add_axis(self, "_y_axis", params) + add_axis(self, "y", params) + return self + end, + + x_range = function(self, a, b) + axis_range(self, a, b, "x") + return self + end, + + y_range = function(self, a, b) + axis_range(self, a, b, "y") return self end, -- tools tool_box_select = function(self, select_every_mousemove) - select_every_mousemove = select_every_mousemove or true - add_tool(self, "BoxSelectTool", { select_every_mousemove=select_every_mousemove, tags={}, doc=nil }) + select_every_mousemove = default_true( select_every_mousemove ) + add_tool(self, "BoxSelectTool", { select_every_mousemove=select_every_mousemove, tags={}, doc=null }) compile_glyph(self) return self end, tool_box_zoom = function(self, dimensions) dimensions = dimensions or { "width", "height" } - add_tool(self, "BoxZoomTool", { dimensions=dimensions, tags={}, doc=nil }) + add_tool(self, "BoxZoomTool", { dimensions=dimensions, tags={}, doc=null }) compile_glyph(self) return self end, tool_crosshair = function(self) - add_tool(self, "CrossHair", { tags={}, doc=nil }) + add_tool(self, "CrossHair", { tags={}, doc=null }) compile_glyph(self) return self end, + + -- tool_hover = function(self, params) + -- params = params or {} + -- check_table(params, "always_active", "tooltips") + -- local always_active = default_true( params.always_active ) + -- local tooltips = params.tooltips or "($x, $y)" + -- add_tool(self, "HoverTool", { tags={}, doc=null, callback=null, + -- always_active=always_active, + -- mode="mouse", line_policy="prev", + -- name=null, names={}, plot=self._docref, + -- point_policy="snap_to_data", + -- renderers={}, + -- tooltips=tooltips }) + -- compile_glyph(self) + -- return self + -- end, tool_lasso_select = function(self, select_every_mousemove) - select_every_mousemove = select_every_mousemove or true - add_tool(self, "LassoSelectTool", { select_every_mousemove=select_every_mousemove, tags={}, doc=nil }) + select_every_mousemove = default_true( select_every_mousemove ) + add_tool(self, "LassoSelectTool", { select_every_mousemove=select_every_mousemove, tags={}, doc=null }) compile_glyph(self) return self end, tool_pan = function(self, dimensions) dimensions = dimensions or { "width", "height" } - add_tool(self, "PanTool", { dimensions=dimensions, tags={}, doc=nil }) + add_tool(self, "PanTool", { dimensions=dimensions, tags={}, doc=null }) compile_glyph(self) return self end, tool_reset = function(self) - add_tool(self, "ResetTool", { tags={}, doc=nil }) + add_tool(self, "ResetTool", { tags={}, doc=null }) compile_glyph(self) return self end, tool_resize = function(self) - add_tool(self, "ResizeTool", { tags={}, doc=nil }) + add_tool(self, "ResizeTool", { tags={}, doc=null }) compile_glyph(self) return self end, tool_save = function(self) - add_tool(self, "PreviewSaveTool", { tags={}, doc=nil }) + add_tool(self, "PreviewSaveTool", { tags={}, doc=null }) compile_glyph(self) return self end, tool_wheel_zoom = function(self, dimensions) dimensions = dimensions or { "width", "height" } - add_tool(self, "WheelZoomTool", { dimensions=dimensions, tags={}, doc=nil }) + add_tool(self, "WheelZoomTool", { dimensions=dimensions, tags={}, doc=null }) compile_glyph(self) return self end, @@ -260,34 +348,35 @@ local figure_methods = { -- layer functions points = function(self, params) + params = params or {} check_table(params, "x", "y", "glyph", "color", "alpha", "size", - "hover", "legend") + "legend") check_value(params, "glyph", "Circle", "Triangle") check_mandatories(params, "x", "y") local x = serie_totable(params.x) local y = serie_totable(params.y) local color = serie_totable( extend(params.color or "#f22c40", #x) ) local alpha = serie_totable( extend(params.alpha or 0.8, #x) ) - local hover = serie_totable( params.hover ) + -- local hover = serie_totable( params.hover ) local data = { x = x, y = y, color = color, fill_alpha = alpha, - hover = hover, + -- hover = hover, } local columns = { "x", "y", "fill_alpha", "color" } - if hover then table.insert(columns, "hover") end + -- if hover then table.insert(columns, "hover") end local attributes = { tags = {}, - doc = nil, + doc = null, selected = { ["2d"] = { indices = {} }, ["1d"] = { indices = {} }, ["0d"] = { indices = {}, flag=false }, }, - callback = nil, + callback = null, data = data, column_names = columns, } @@ -296,7 +385,7 @@ local figure_methods = { local attributes = { fill_color = { field = "color" }, tags = {}, - doc = nil, + doc = null, fill_alpha = { field = "fill_alpha" }, x = { field = "x" }, y = { field = "y" }, @@ -304,14 +393,33 @@ local figure_methods = { local points_ref = add_simple_glyph(self, params.glyph or "Circle", attributes) local attributes = { - nonselection_glyph = nil, + nonselection_glyph = null, data_source = source_ref, tags = {}, - doc = nil, - selection_glyph = nil, + doc = null, + selection_glyph = null, glyph = points_ref, } append_renderer(self, add_simple_glyph(self, "GlyphRenderer", attributes) ) + + if not self._doc.attributes.x_range then + local axis = self._doc.attributes.below[1] or self._doc.attributes.above[1] + if axis.type == "LinearAxis" then + self:x_range( apply_gap(0.05, min(x), max(x)) ) + else + self:x_range(factors(x)) + end + end + + if not self._doc.attributes.y_range then + local axis = self._doc.attributes.left[1] or self._doc.attributes.right[1] + if axis.type == "LinearAxis" then + self:y_range( apply_gap(0.05, min(y), max(y)) ) + else + self:y_range(factors(y)) + end + end + return self end, @@ -327,65 +435,21 @@ local figure_methods = { end return ("[%s]"):format(table.concat(tbl, ",")) end, + } -local html_template = [[ -(function(global) { - if (typeof (window._bokeh_onload_callbacks) === "undefined"){ - window._bokeh_onload_callbacks = []; - } - function load_lib(url, callback){ - window._bokeh_onload_callbacks.push(callback); - if (window._bokeh_is_loading){ - console.log("Bokeh: BokehJS is being loaded, scheduling callback at", new Date()); - return null; - } - console.log("Bokeh: BokehJS not loaded, scheduling load and callback at", new Date()); - window._bokeh_is_loading = true; - var s = document.createElement('script'); - s.src = url; - s.async = true; - s.onreadystatechange = s.onload = function(){ - Bokeh.embed.inject_css("http://cdn.pydata.org/bokeh/release/bokeh-0.10.0.min.css"); - window._bokeh_onload_callbacks.forEach(function(callback){callback()}); - }; - s.onerror = function(){ - console.warn("failed to load library " + url); - }; - document.getElementsByTagName("head")[0].appendChild(s); - } - - bokehjs_url = "http://cdn.pydata.org/bokeh/release/bokeh-0.10.0.min.js" - - var elt = document.getElementById("$ID"); - if(elt==null) { - console.log("Bokeh: ERROR: autoload.js configured with elementid '$ID' but no matching script tag was found. ") - return false; - } - - // These will be set for the static case - var all_models = [$MODEL]; - - if(typeof(Bokeh) !== "undefined") { - console.log("Bokeh: BokehJS loaded, going straight to plotting"); - Bokeh.embed.inject_plot("$ID", all_models); - } else { - load_lib(bokehjs_url, function() { - console.log("Bokeh: BokehJS plotting callback run at", new Date()) - Bokeh.embed.inject_plot("$ID", all_models); - }); - } - -}(this)); -]] - local figure_mt = { __index = figure_methods, ipylua_show = function(self) - local html = html_template: - gsub("$ID", uuid.new()): - gsub("$MODEL", self:to_json()) + local html = html_template:gsub("$([A-Z]+)", + { + SCRIPTID = uuid.new(), + MODELTYPE = self._doc.type, + MODELID = self._doc.id, + MODEL = self:to_json(), + } + ) return { ["text/html"] = html, ["text/plain"] = "-- impossible to show ASCII art plots", @@ -404,60 +468,62 @@ setmetatable( end default("tools", { "pan", "wheel_zoom", "box_zoom", "resize", "reset", "save" }) - default("width", 480) - default("height", 520) + default("width", 500) + default("height", 400) default("title", nil) -- not necessary but useful to make it explicit default("xlab", nil) default("ylab", nil) default("xlim", nil) default("ylim", nil) default("padding_factor", 0.07) - default("plot_width", nil) - default("plot_height", nil) default("xgrid", true) default("ygrid", true) - default("xaxes", "below") - default("yaxes", "left") + default("xaxes", {"below"}) + default("yaxes", {"left"}) -- ??? default("theme", "bokeh_theme") ??? - local self = { _list = {}, _y_axis = {}, _x_axis = {}, _observers={} } + local self = { _list = {}, _references={}, _dict = {} } setmetatable(self, figure_mt) - local plot_id = uuid.new() - self._doc = { - type = "Plot", - subtype = "Chart", - id = plot_id, - attributes = { - plot_width = params.plot_width, - plot_height = params.plot_height, - title = params.title, - -- - id = plot_id, - tags = {}, - title_text_font_style = "bold", - title_text_font_size = { value = "12pt" }, - tools = {}, - renderers = {}, - below = {}, - above = {}, - left = {}, - right = {}, - responsive = false, - }, - } - self._list[1] = self._doc + + self._docref,self._doc = + add_simple_glyph(self, "Plot", + { + plot_width = params.width, + plot_height = params.height, + title = params.title, + -- + x_range = nil, + y_range = nil, + extra_x_ranges = {}, + extra_y_ranges = {}, + id = plot_id, + tags = {}, + title_text_font_style = "bold", + title_text_font_size = { value = "12pt" }, + tools = {}, + renderers = {}, + below = {}, + above = {}, + left = {}, + right = {}, + responsive = false, + }, + "Chart") tool_events(self) + for _,name in ipairs(params.tools) do self["tool_" .. name](self) end - if params.xaxes then + + for _,pos in ipairs(params.xaxes) do self:x_axis{ label=params.xlab, log=false, grid=params.xgrid, num_minor_ticks=5, visible=true, number_formatter=tostring, - type="LinearAxis", pos=params.xaxes } + type="LinearAxis", pos=pos } end - if params.yaxes then + + for _,pos in ipairs(params.yaxes) do self:y_axis{ label=params.ylab, log=false, grid=params.ygrid, num_minor_ticks=5, visible=true, number_formatter=tostring, - type="LinearAxis", pos=params.yaxes } + type="LinearAxis", pos=pos } end return self @@ -465,10 +531,6 @@ setmetatable( } ) -local x = figure():points{ x={1,2,3,4}, y={10,5,20,30} } - -print(x:to_json()) - return { figure = figure, } diff --git a/IPyLua/html_template.lua b/IPyLua/html_template.lua new file mode 100644 index 0000000..39d32b4 --- /dev/null +++ b/IPyLua/html_template.lua @@ -0,0 +1,56 @@ +return [[ +]] diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index 261eab8..db1eb01 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -57,6 +57,7 @@ local function lookup_function_for_object(obj, stack, ...) end end +local bokeh = require "IPyLua.bokeh" local do_completion = require "IPyLua.rlcompleter".do_completion local json = require "IPyLua.dkjson" local zmq = require 'lzmq' @@ -368,6 +369,8 @@ do env_G._ENV = env local env_G = setmetatable(env_G, { __index = _G }) local env = setmetatable(env, { __index = env_G }) + + env_G.bokeh = bokeh env_G.pyout = function(data,metadata) metadata = metadata or {} @@ -430,7 +433,7 @@ do env_G.vars = function() show_obj(env, math.huge) end - + env_G.help = help env_G["%quickref"] = function() diff --git a/custom/custom.css b/custom/custom.css index 0d888ff..3533a57 100644 --- a/custom/custom.css +++ b/custom/custom.css @@ -20,11 +20,14 @@ img { */ -/* Uncomment to use a custom font +/* Uncomment to use a custom font */ div#notebook, div.CodeMirror, div.output_area pre, div.output_wrapper, div.prompt { - font-family: 'Custom Font Name', monospace !important; + font-family: 'Monaco', monospace !important; + font-size: 12pt; } -*/ + +/* Change the width of the container */ +.container { width:95% !important; } /* GLOBALS */ body {background-color: #2b303b;} From 2b298863aabc4941abc156ca210c4ca78e64e330 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Fri, 9 Oct 2015 19:51:07 +0200 Subject: [PATCH 54/94] Added first plot abilities: points, bars, lines, hist2d. --- IPyLua/bokeh.lua | 474 +++++++++++++++++++++++++++++++++++++++------- custom/custom.css | 2 +- 2 files changed, 410 insertions(+), 66 deletions(-) diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index d58592b..5d2584f 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -4,9 +4,35 @@ local html_template = require "IPyLua.html_template" local null = json.null local type = luatype or type +local DEF_XGRID = 100 +local DEF_YGRID = 100 +local DEF_SIZE = 6 +local DEF_WIDTH = 6 +local DEF_HEIGTH = 6 +local DEF_ALPHA = 0.8 +local DEF_X = 0 +local DEF_Y = 0 +local COLORS = { + "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", "#00ffff", "#000000", + "#800000", "#008000", "#000080", "#808000", "#800080", "#008080", "#808080", + "#c00000", "#00c000", "#0000c0", "#c0c000", "#c000c0", "#00c0c0", "#c0c0c0", + "#400000", "#004000", "#000040", "#404000", "#400040", "#004040", "#404040", + "#200000", "#002000", "#000020", "#202000", "#200020", "#002020", "#202020", + "#600000", "#006000", "#000060", "#606000", "#600060", "#006060", "#606060", + "#a00000", "#00a000", "#0000a0", "#a0a000", "#a000a0", "#00a0a0", "#a0a0a0", + "#e00000", "#00e000", "#0000e0", "#e0e000", "#e000e0", "#00e0e0", "#e0e0e0", +} + local figure = {} local figure_methods = {} +local function round(val) + if val > 0 then + return math.floor(val + 0.5) + end + return -math.floor(-val + 0.5) +end + local function default_true(value) if value == nil then return true else return value end end @@ -17,11 +43,30 @@ local function reduce(t, func) return out end -local function min(t) return reduce(t, math.min) end -local function max(t) return reduce(t, math.max) end +local math_min = math.min +local math_max = math.max + +local function min(t) return reduce(t, math_min) end +local function max(t) return reduce(t, math_max) end -local function factors(t) - local out = {} for i=1,#t do out[i] = tostring(t[i]) end return out +local function minmax(t) + local min,max = t[1],t[1] + for i=2,#t do + min=math_min(min,t[i]) + max=math_max(max,t[i]) + end + return min,max +end + +local function factors(t, out, dict) + local out,dict = out or {},dict or {} + for i=1,#t do + local s = tostring(t[i]) + if not dict[s] then + out[i],dict[s] = s,true + end + end + return out,dict end local function apply_gap(p, a, b) @@ -39,18 +84,19 @@ local function extend(param, n) return param end -local function serie_totable(s) +local function toseries(s) + collectgarbage("collect") if s==nil then return nil end local tt = type(s) if tt == "table" or tt == "userdata" then local mt = getmetatable(s) - if mt and mt.ipylua_totable then - return mt.ipylua_totable(s) + if mt and mt.ipylua_toseries then + return mt.ipylua_toseries(s) elseif tt == "table" then return s end end - error("serie data type cannot be handled") + error(("series data type %s cannot be handled: " ):format(tt)) end local function invert(t) @@ -61,6 +107,14 @@ end -- error checking +local function check_equal_sizes(...) + local arg = table.pack(...) + local N = #arg[1] + for i=2,#arg do + assert(N == #arg[i], "all the given seires shoud be equal in size") + end +end + local function check_table(t, ...) local inv_list = invert({...}) for i,v in pairs(t) do @@ -79,7 +133,7 @@ end local function check_type(t, k, ty) local tt = type(t[k]) assert(t[k] == nil or tt == ty, - ("type %s is not valid for field %s"):format(tt, k)) + ("type %s is not valid for field %s, expected %s"):format(tt, k, ty)) end local function check_types(t, keys, types) @@ -102,6 +156,56 @@ end -- private functions +local function next_color(self) + self._color_number = (self._color_number + 1) % #self._colors + local color = self._colors[ self._color_number + 1] + return color +end + +local function check_axis_range(self) + if not self._doc.attributes.x_range then + local axis = self._doc.attributes.below[1] or self._doc.attributes.above[1] + if axis.type == "LinearAxis" then + local x_min,x_max = math.huge,-math.huge + for _,source in ipairs(self._sources) do + local s_min,s_max = minmax(source.attributes.data.x) + x_min = math.min(x_min,s_min) + x_max = math.max(x_max,s_max) + end + self:x_range( apply_gap(0.05, x_min, x_max) ) + else + local list,dict + for _,source in ipairs(self._sources) do + list,dict = factors(x, list, dict) + end + self:x_range(list) + end + end + + if not self._doc.attributes.y_range then + local axis = self._doc.attributes.below[1] or self._doc.attributes.above[1] + if axis.type == "LinearAxis" then + local y_min,y_max = math.huge,-math.huge + for _,source in ipairs(self._sources) do + local s_min,s_max = minmax(source.attributes.data.y) + y_min = math.min(y_min, s_min) + y_max = math.max(y_max, s_max) + end + self:y_range( apply_gap(0.05, y_min, y_max) ) + else + local list,dict + for _,source in ipairs(self._sources) do + list,dict = factors(x, list, dict) + end + self:y_range(list) + end + end +end + +local function add_source(self, source) + table.insert(self._sources, source) +end + local function add_reference(self, id, tbl) self._references[id] = self._references[id] or {} table.insert(self._references[id], tbl) @@ -122,6 +226,7 @@ end local function append_renderer(self, ref) table.insert( self._doc.attributes.renderers, ref ) + return ref end local function add_simple_glyph(self, name, attributes, subtype) @@ -140,6 +245,49 @@ local function add_simple_glyph(self, name, attributes, subtype) return ref,glyph end + +local function add_column_data_source(self, data, columns) + local attributes = { + tags = {}, + doc = null, + selected = { + ["2d"] = { indices = {} }, + ["1d"] = { indices = {} }, + ["0d"] = { indices = {}, flag=false }, + }, + callback = null, + data = data, + column_names = columns, + } + local source_ref,source = add_simple_glyph(self, "ColumnDataSource", + attributes) + add_source(self, source) + return source_ref +end + +local function append_source_renderer(self, source_ref, glyph_ref) + local attributes = { + nonselection_glyph = null, + data_source = source_ref, + tags = {}, + doc = null, + selection_glyph = null, + glyph = glyph_ref, + } + return append_renderer(self, add_simple_glyph(self, "GlyphRenderer", + attributes) ) +end + +local function add_legend(self, legend, renderer_ref) + local attributes = { + tags = {}, + doc = null, + plot = self._docref, + legends = { { legend, { renderer_ref } } }, + } + return append_renderer(self, add_simple_glyph(self, "Legend", attributes)) +end + local function add_tool(self, name, attributes) attributes.plot = self._docref local tools = self._doc.attributes.tools @@ -230,7 +378,7 @@ local figure_methods = { -- axis - x_axis = function(self, params) + x_axis = function(self, params) -- type, label, pos, log, grid, num_minor_ticks, visible, number_formatter params = params or {} check_table(params, "type", "label", "pos", "log", "grid", "num_minor_ticks", "visible", "number_formatter") @@ -244,7 +392,7 @@ local figure_methods = { return self end, - y_axis = function(self, params) + y_axis = function(self, params) -- type, label, pos, log, grid, num_minor_ticks, visible, number_formatter params = params or {} check_table(params, "type", "label", "pos", "log", "grid", "num_minor_ticks", "visible", "number_formatter") @@ -267,7 +415,42 @@ local figure_methods = { axis_range(self, a, b, "y") return self end, - + + drop_points = function(self, xmin, xmax, ymin, ymax) + for _,source in ipairs(self._sources) do + local data = source.attributes.data + local new_data = { + x = {}, + y = {}, + } + local other_than_xy = {} + for k,v in pairs(data) do + if k~="x" and k~="y" then + if type(v) == "table" then + new_data[k] = {} + table.insert(other_than_xy, k) + else + new_data[k] = v + end + end + end + local j=1 + for i=1,#data.x do + local x,y = data.x[i],data.y[i] + if xmin<=x and x<=xmax and ymin<=y and y<=ymax then + new_data.x[j] = x + new_data.y[j] = y + for _,k in ipairs(other_than_xy) do + new_data[k][j] = data[k][i] + end + j=j+1 + end + end + source.attributes.data = new_data + end + return self + end, + -- tools tool_box_select = function(self, select_every_mousemove) @@ -277,7 +460,7 @@ local figure_methods = { return self end, - tool_box_zoom = function(self, dimensions) + tool_box_zoom = function(self, dimensions) -- { "width", "height" } dimensions = dimensions or { "width", "height" } add_tool(self, "BoxZoomTool", { dimensions=dimensions, tags={}, doc=null }) compile_glyph(self) @@ -313,7 +496,7 @@ local figure_methods = { return self end, - tool_pan = function(self, dimensions) + tool_pan = function(self, dimensions) -- { "width", "height" } dimensions = dimensions or { "width", "height" } add_tool(self, "PanTool", { dimensions=dimensions, tags={}, doc=null }) compile_glyph(self) @@ -338,94 +521,198 @@ local figure_methods = { return self end, - tool_wheel_zoom = function(self, dimensions) + tool_wheel_zoom = function(self, dimensions) -- { "width", "height" } dimensions = dimensions or { "width", "height" } add_tool(self, "WheelZoomTool", { dimensions=dimensions, tags={}, doc=null }) compile_glyph(self) return self end, + + -- color functions + + color_last = function(self) + return self._colors[ self._color_number + 1 ] + end, + + color_num = function(self, i) + assert(i>0 and i<=#self._colors, + ("color number out-of-bounds [1,%d]"):format(#self._colors)) + return self._colors[i] + end, -- layer functions - - points = function(self, params) + + lines = function(self, params) -- x, y, color, alpha, width, legend + params = params or {} + check_table(params, "x", "y", "color", "alpha", "width", "legend") + check_mandatories(params, "x", "y") + local x = toseries(params.x) + local y = toseries(params.y) + local color = params.color or next_color(self) + local alpha = params.alpha or DEF_ALPHA + local width = params.width or DEF_WIDTH + check_equal_sizes(x, y) + -- local hover = toseries( params.hover ) + local data = { x=x, y=y, } + local columns = { "x", "y" } + + local source_ref = add_column_data_source(self, data, columns) + + local attributes = { + tags = {}, + doc = null, + line_color = { value = color }, + line_alpha = { value = alpha }, + line_width = { value = width }, + x = { field = "x" }, + y = { field = "y" }, + } + local lines_ref = add_simple_glyph(self, "Line", attributes) + + local renderer_ref = append_source_renderer(self, source_ref, lines_ref) + + if params.legend then add_legend(self, params.legend, renderer_ref) end + + return self + end, + + bars = function(self, params) -- x, y, width, height, color, alpha, legend + params = params or {} + check_table(params, "x", "height", "width", "y", "color", "alpha", "legend") + check_mandatories(params, "x") + local x = toseries( extend(params.x or DEF_X, 1) ) + local y = toseries( extend(params.y or DEF_Y, #x) ) + local width = toseries( extend(params.width or DEF_WIDTH, #x) ) + local height = toseries( extend(params.height or DEF_HEIGTH, #x) ) + local color = toseries( extend(params.color or next_color(self), #x) ) + local alpha = toseries( extend(params.alpha or DEF_ALPHA, #x) ) + check_equal_sizes(x, y, width, height, color, alpha) + -- local hover = toseries( params.hover ) + local data = { x=x, y=y, width=width, height=height, fill_alpha=alpha, color=color } + local columns = { "x", "y", "width", "height", "fill_alpha", "color" } + + local source_ref = add_column_data_source(self, data, columns) + + local attributes = { + tags = {}, + doc = null, + fill_color = { field = "color" }, + fill_alpha = { field = "fill_alpha" }, + height = { units = "data", field = "height" }, + width = { units = "data", field = "width" }, + x = { field = "x" }, + y = { field = "y" }, + } + local lines_ref = add_simple_glyph(self, "Rect", attributes) + + local renderer_ref = append_source_renderer(self, source_ref, lines_ref) + + if params.legend then add_legend(self, params.legend, renderer_ref) end + + return self + end, + + points = function(self, params) -- x, y, glyph, color, alpha, size, legend params = params or {} check_table(params, "x", "y", "glyph", "color", "alpha", "size", "legend") check_value(params, "glyph", "Circle", "Triangle") check_mandatories(params, "x", "y") - local x = serie_totable(params.x) - local y = serie_totable(params.y) - local color = serie_totable( extend(params.color or "#f22c40", #x) ) - local alpha = serie_totable( extend(params.alpha or 0.8, #x) ) - -- local hover = serie_totable( params.hover ) + local x = toseries(params.x) + local y = toseries(params.y) + local color = toseries( extend(params.color or next_color(self), #x) ) + local alpha = toseries( extend(params.alpha or DEF_ALPHA, #x) ) + local size = toseries( extend(params.size or DEF_SIZE, #x) ) + check_equal_sizes(x, y, color, alpha, size) + -- local hover = toseries( params.hover ) local data = { x = x, y = y, color = color, fill_alpha = alpha, + size = size, -- hover = hover, } - local columns = { "x", "y", "fill_alpha", "color" } + local columns = { "x", "y", "fill_alpha", "color", "size" } -- if hover then table.insert(columns, "hover") end + + local source_ref = add_column_data_source(self, data, columns) local attributes = { tags = {}, doc = null, - selected = { - ["2d"] = { indices = {} }, - ["1d"] = { indices = {} }, - ["0d"] = { indices = {}, flag=false }, - }, - callback = null, - data = data, - column_names = columns, - } - local source_ref = add_simple_glyph(self, "ColumnDataSource", attributes) - - local attributes = { fill_color = { field = "color" }, - tags = {}, - doc = null, fill_alpha = { field = "fill_alpha" }, + size = { field = "size" }, x = { field = "x" }, y = { field = "y" }, } - local points_ref = add_simple_glyph(self, params.glyph or "Circle", attributes) + local points_ref = add_simple_glyph(self, params.glyph or "Circle", + attributes) + + local renderer_ref = append_source_renderer(self, source_ref, points_ref) + + if params.legend then add_legend(self, params.legend, renderer_ref) end - local attributes = { - nonselection_glyph = null, - data_source = source_ref, - tags = {}, - doc = null, - selection_glyph = null, - glyph = points_ref, - } - append_renderer(self, add_simple_glyph(self, "GlyphRenderer", attributes) ) - - if not self._doc.attributes.x_range then - local axis = self._doc.attributes.below[1] or self._doc.attributes.above[1] - if axis.type == "LinearAxis" then - self:x_range( apply_gap(0.05, min(x), max(x)) ) - else - self:x_range(factors(x)) - end + return self + end, + + hist2d = function(self, params) -- x, y, glyph, color, alpha, size, legend, xgrid, ygrid + params = params or {} + check_table(params, "x", "y", "glyph", "color", "alpha", "size", "legend", + "xgrid", "ygrid") + check_value(params, "glyph", "Circle") + check_mandatories(params, "x", "y") + check_types(params, + { "color", "alpha", "size" }, + { "string", "number", "number" }) + local x = toseries(params.x) + local y = toseries(params.y) + local color = params.color or next_color(self) + local alpha = params.alpha or DEF_ALPHA + local size = params.size or 2*DEF_SIZE + local xgrid = params.xgrid or DEF_XGRID + local ygrid = params.ygrid or DEF_YGRID + check_equal_sizes(x, y) + local x_min,x_max = minmax(x) + local y_min,y_max = minmax(y) + local x_width = (x_max - x_min) / (xgrid-1) + local y_width = (y_max - y_min) / (ygrid-1) + local cols = {} + for i=1,xgrid*ygrid do cols[i] = 0 end + local max_count = 0 + for i=1,#x do + local ix = math.floor((x[i]-x_min)/x_width) + local iy = math.floor((y[i]-y_min)/y_width) + local k = iy*xgrid + ix + 1 + cols[k] = cols[k] + 1 + if cols[k] > max_count then max_count= cols[k] end end - - if not self._doc.attributes.y_range then - local axis = self._doc.attributes.left[1] or self._doc.attributes.right[1] - if axis.type == "LinearAxis" then - self:y_range( apply_gap(0.05, min(y), max(y)) ) - else - self:y_range(factors(y)) + local x_off = x_width*0.5 + local y_off = y_width*0.5 + local new_x,new_y,new_sizes = {},{},{} + local l=1 + for i=0,xgrid-1 do + for j=0,ygrid-2 do + local k = j*xgrid + i + 1 + if cols[k] > 0 then + new_x[l] = i*x_width + x_min + x_off + new_y[l] = j*y_width + y_min + y_off + new_sizes[l] = size * cols[k]/max_count + l = l + 1 + end end end + x,y,size = new_x,new_y,new_sizes - return self + return self:points{ x=x, y=y, size=size, alpha=alpha, color=color, + glyph=params.glyph, legend=params.legend } end, -- conversion - + to_json = function(self) + check_axis_range(self) local doc_json = json.encode(self._doc) local tbl = { doc_json } for i=2,#self._list do @@ -433,6 +720,7 @@ local figure_methods = { if type(v) ~= "string" then v = json.encode(v) end tbl[#tbl+1] = v end + collectgarbage("collect") return ("[%s]"):format(table.concat(tbl, ",")) end, @@ -482,7 +770,9 @@ setmetatable( default("yaxes", {"left"}) -- ??? default("theme", "bokeh_theme") ??? - local self = { _list = {}, _references={}, _dict = {} } + local self = { _list = {}, _references={}, _dict = {}, + _color_number = #COLORS - 1, _colors = COLORS, + _sources = {} } setmetatable(self, figure_mt) self._docref,self._doc = @@ -531,6 +821,60 @@ setmetatable( } ) +-- color transformers + +-- http://www.andrewnoske.com/wiki/Code_-_heatmaps_and_color_gradients +local function linear_color_transformer(x) + local x = toseries(x) + assert(type(x) == "table", "needs a series as input") + local min,max = minmax(x) + local diff = max-min + local color = {} + -- 4 colors: blue, green, yellow, red + local COLORS = { {0,0,255}, {0,255,0}, {255,255,0}, {255,0,0} } + for i=1,#x do + local idx1,idx2 -- our desired color will be between these two indexes in COLORS + local fract = 0 -- fraction between idx1 and idx2 + local v = (x[i] - min) / diff + if v <= 0 then + idx1,idx2 = 1,1 + elseif v >= 1 then + idx1,idx2 = #COLORS,#COLORS + else + v = v * (#COLORS-1) + idx1 = math.floor(v) + 1 + idx2 = idx1+1 + fract = v - (idx1 - 1) + end + local r = round( (COLORS[idx2][1] - COLORS[idx1][1])*fract + COLORS[idx1][1] ) + local g = round( (COLORS[idx2][2] - COLORS[idx1][2])*fract + COLORS[idx1][2] ) + local b = round( (COLORS[idx2][3] - COLORS[idx1][3])*fract + COLORS[idx1][3] ) + color[i] = ("#%02x%02x%02x"):format(r, g, b) + end + return color +end + +local function linear_size_transformer(x, smin, smax) + local x = toseries(x) + assert(type(x) == "table", "needs a series as 1st argument") + assert(smin and smax, "needs two numbers as 2nd and 3rd arguments") + local sdiff = smax - smin + local min,max = minmax(x) + local diff = max-min + local size = {} + for i=1,#x do + local v = (x[i] - min) / diff + size[i] = v*sdiff + smin + end + return size +end + return { figure = figure, + colors = { + linear = linear_color_transformer, + }, + sizes = { + linear = linear_size_transformer, + }, } diff --git a/custom/custom.css b/custom/custom.css index 3533a57..f3b5812 100644 --- a/custom/custom.css +++ b/custom/custom.css @@ -39,7 +39,7 @@ a {color: #8fa1b3;} /* NOTEBOOK */ /* comment out this line to bring the toolbar back */ -/* div#maintoolbar, div#header {display: none !important;} */ +div#maintoolbar, div#header {display: none !important;} div.navbar {color:white;} From 14fe1c21fb533782eed38307a032328a5b9ffe11 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Fri, 9 Oct 2015 22:39:00 +0200 Subject: [PATCH 55/94] Changed http by https in bokeh dependencies --- IPyLua/html_template.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IPyLua/html_template.lua b/IPyLua/html_template.lua index 39d32b4..23638c7 100644 --- a/IPyLua/html_template.lua +++ b/IPyLua/html_template.lua @@ -21,7 +21,7 @@ return [[\n" + ], + "metadata": {}, + "output_type": "pyout", + "prompt_number": 8, + "text": [ + "-- impossible to show ASCII art plots" + ] + } + ], + "prompt_number": 8 + } + ], + "metadata": {} + } + ] +} \ No newline at end of file From c82e8f7e0578a0b8e98fe79343e2641debd026ad Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Mon, 12 Oct 2015 10:55:07 +0200 Subject: [PATCH 64/94] Added cors plot --- IPyLua/bokeh.lua | 151 ++++++++++++++++++++++++++++++++++++++++------- IPyLuaKernel.lua | 7 ++- 2 files changed, 135 insertions(+), 23 deletions(-) diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index cc190c2..77f5681 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -26,7 +26,11 @@ local COLORS = { local figure = {} local figure_methods = {} +-- forward declarations local hist2d_transformation +local linear_color_transformer +local linear_size_transformer +------------------------------ local function round(val) if val > 0 then @@ -45,10 +49,12 @@ local function reduce(t, func) return out end +local math_abs = math.abs local math_min = math.min local math_max = math.max local function min(t) + local t = t or 0.0 local tt = type(t) if tt == "table" or tt == "userdata" then return reduce(t, math_min) @@ -58,6 +64,7 @@ local function min(t) end local function max(t) + local t = t or 0.0 local tt = type(t) if tt == "table" or tt == "userdata" then return reduce(t, math_max) @@ -67,19 +74,21 @@ local function max(t) end local function minmax(t, size) + local t = t or 0.0 local size = size or {} local tmin,tmax local tt = type(t) if tt == "table" or tt == "userdata" then - tmin = t[1] - (size[1] or 0)*0.5 - tmax = t[1] + (size[1] or 0)*0.5 + local s = (size[1] or 0)*0.5 + tmin = t[1] - s + tmax = t[1] + s for i=2,#t do local s = (size[i] or 0)*0.5 - tmin=math_min(tmin,t[i] - s) - tmax=math_max(tmax,t[i] + s) + tmin = math_min(tmin, t[i] - s) + tmax = math_max(tmax, t[i] + s) end else - local s = (max(size) or 0.0)*0.5 + local s = max(size) * 0.5 tmin = t - s tmax = t + s end @@ -91,7 +100,7 @@ local function factors(t, out, dict) for i=1,#t do local s = tostring(t[i]) if not dict[s] then - out[i],dict[s] = s,true + out[#out+1],dict[s] = s,true end end return out,dict @@ -102,6 +111,16 @@ local function apply_gap(p, a, b) return a-gap, b+gap end +local function tomatrix(m) + local tt = type(m) + if tt == "table" then return m end + if tt == "userdata" then + mt = getmetatable(m) + if mt.__index and mt.__len then return m end + end + error("Improper data type") +end + local function toseries(s) collectgarbage("collect") if s==nil then return nil end @@ -126,6 +145,26 @@ local function invert(t) return r end +local function compute_optim(x, DEF) + local optim + local x = toseries(x) + if type(x) == "number" then + optim = DEF + elseif type(x) == "string" then + optim = 1.0 + else + if type(x[1]) == "string" then + optim = 1.0 + else + optim = DEF + local t = {} + for i=1,#x do t[i] = x[i] end table.sort(t) + for i=2,#t do optim = math_min( optim, math_abs( t[i-1] - t[i] ) ) end + end + end + return optim +end + -- error checking local function check_equal_sizes(...) @@ -240,16 +279,16 @@ local function check_axis_range(self) end self:x_range( apply_gap(0.05, x_min, x_max) ) else - local list,dict + local list,dict = {},{} for _,source in ipairs(self._sources) do - list,dict = factors(x, list, dict) + list,dict = factors(source.attributes.data.x, list, dict) end self:x_range(list) end end if not self._doc.attributes.y_range then - local axis = self._doc.attributes.below[1] or self._doc.attributes.above[1] + local axis = self._doc.attributes.left[1] or self._doc.attributes.right[1] if axis.type == "LinearAxis" then local y_min,y_max = math.huge,-math.huge for _,source in ipairs(self._sources) do @@ -265,7 +304,7 @@ local function check_axis_range(self) else local list,dict for _,source in ipairs(self._sources) do - list,dict = factors(x, list, dict) + list,dict = factors(source.attributes.data.y, list, dict) end self:y_range(list) end @@ -333,6 +372,7 @@ local function add_column_data_source(self, data, columns) local source_ref,source = add_simple_glyph(self, "ColumnDataSource", attributes) add_source(self, source) + return source_ref end @@ -367,14 +407,25 @@ end local function add_axis(self, key, params) check_mandatories(params, "pos", "type") + + local formatter_type = "BasicTickFormatter" + local ticker_type = "BasicTicker" + + if params.type == "CategoricalAxis" then + formatter_type = "CategoricalTickFormatter" + ticker_type = "CategoricalTicker" + end + local doc_axis = self._doc.attributes[params.pos] if not doc_axis[1] then - local formatter_ref,formatter = add_simple_glyph(self, "BasicTickFormatter", + + local formatter_ref,formatter = add_simple_glyph(self, formatter_type, { tags={}, doc=null }) local ticker_ref,ticker = - add_simple_glyph(self, "BasicTicker", - { tags={}, doc=null, mantissas={2, 5, 10} }) + add_simple_glyph(self, ticker_type, + { tags={}, doc=null, mantissas={2, 5, 10}, + num_minor_ticks=params.num_minor_ticks }) local axis_ref,axis = add_simple_glyph(self, params.type, { @@ -397,8 +448,18 @@ local function add_axis(self, key, params) doc_axis[1] = axis_ref else local axis = self._dict[ doc_axis[1].id ] - axis.attributes.axis_label = params.label - axis.type = params.type + if params.label then axis.attributes.axis_label = params.label end + if params.type then + axis.type = params.type + local formatter_id = axis.attributes.formatter.id + local ticker_id = axis.attributes.ticker.id + local formatter = self._dict[ formatter_id ] + local ticker = self._dict[ ticker_id ] + formatter.type = formatter_type + ticker.type = ticker_type + update_references(self, formatter_id, formatter) + update_references(self, ticker_id, ticker) + end update_references(self, axis.id, axis) end end @@ -659,12 +720,15 @@ local figure_methods = { check_mandatories(params, "x") local x = params.x or DEF_X local y = params.y or DEF_Y - local width = params.width or DEF_WIDTH - local height = params.height or DEF_HEIGTH + local width = params.width or "auto" + local height = params.height or "auto" local color = params.color or next_color(self) local alpha = params.alpha or DEF_ALPHA local hover = params.hover + if width == "auto" then width = compute_optim(x, DEF_WIDTH) end + if height == "auto" then height = compute_optim(y, DEF_HEIGTH) end + local data = { x=x, y=y, @@ -692,6 +756,50 @@ local figure_methods = { return self end, + + cors = function(self, params) -- data, alpha, legend, min, max + params = params or {} + check_table(params, "xy", "xnames", "ynames", "alpha", + "legend", "min", "max") + check_mandatories(params, "xy") + local alpha = params.alpha or DEF_ALPHA + local hover = params.hover + + local xnames = params.xnames or {} + local ynames = params.ynames or {} + local xy = tomatrix( params.xy ) + local x = {} + local y = {} + local cor = {} + + local N = #xy + for i=1,N do + local row = toseries( xy[i] ) + for j=1,#row do + local c = row[j] + table.insert(x, xnames[j] or j) + table.insert(y, ynames[i] or i) + table.insert(cor, c) + end + end + + local data = { + x=x, + y=y, + width=1.0, + height=1.0, + alpha=alpha, + color=linear_color_transformer(cor, params.min or -1.0, params.max or 1.0), + hover=cor, + } + + self:bars( data ) + + if xnames then self:x_axis{ type="CategoricalAxis", pos="below" } end + if ynames then self:y_axis{ type="CategoricalAxis", pos="left" } end + + return self + end, points = function(self, params) -- x, y, glyph, color, alpha, size, legend, hover, more_data params = params or {} @@ -946,10 +1054,13 @@ end -- color transformers -- http://www.andrewnoske.com/wiki/Code_-_heatmaps_and_color_gradients -local function linear_color_transformer(x) +function linear_color_transformer(x, xmin, xmax) local x = toseries(x) assert(type(x) == "table", "needs a series as input") - local min,max = minmax(x) + if not xmin and xmax then xmin = min(x) end + if not xmax and xmin then xmax = max(x) end + local min,max = xmin,xmax + if not min and not max then min,max = minmax(x) end local diff = max-min local color = {} -- 4 colors: blue, green, yellow, red @@ -976,7 +1087,7 @@ local function linear_color_transformer(x) return color end -local function linear_size_transformer(x, smin, smax) +function linear_size_transformer(x, smin, smax) local x = toseries(x) assert(type(x) == "table", "needs a series as 1st argument") assert(smin and smax, "needs two numbers as 2nd and 3rd arguments") diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index db1eb01..d6ef78e 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -349,16 +349,17 @@ do local function show_obj(obj, MAX) local data,metadata = lookup_function_for_object(obj, output_functions, MAX) - if data then return pcall_wrap(pyout,data,metadata) end + if data then pyout(data,metadata) return true end return false end local function help(obj, ...) local data,metadata = lookup_function_for_object(obj, help_functions, ...) if data then - if pcall_wrap(pyout,data,metadata) then return end + pyout(data,metadata) + else + pyout({ ["text/plain"] = "No documentation found" }) end - pyout({ ["text/plain"] = "No documentation found" }) end function new_environment() From 4e33ade883b66d48790651fa11d579f37e0d9473 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Mon, 12 Oct 2015 18:52:01 +0200 Subject: [PATCH 65/94] added box-kind plots --- IPyLua/bokeh.lua | 209 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 196 insertions(+), 13 deletions(-) diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index 77f5681..4fb09a4 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -4,6 +4,7 @@ local html_template = require "IPyLua.html_template" local null = json.null local type = luatype or type +local DEF_BREAKS = 20 local DEF_XGRID = 100 local DEF_YGRID = 100 local DEF_SIZE = 6 @@ -32,6 +33,31 @@ local linear_color_transformer local linear_size_transformer ------------------------------ +local function quantile(tbl, q) + local N = #tbl + local pos = (N+1)*q + local pos_floor,pos_ceil,result = math.floor(pos),math.ceil(pos) + local result + if pos_floor == 0 then + return tbl[1] + elseif pos_floor >= N then + return tbl[N] + else + local dec = pos - pos_floor + local a,b = tbl[pos_floor],tbl[pos_ceil] + return a + dec * (b - a) + end +end + +local function take_outliers(tbl, upper, lower) + local result = {} + for i=1,#tbl do + local x = tbl[i] + if x < lower or x > upper then result[#result+1] = x end + end + return result +end + local function round(val) if val > 0 then return math.floor(val + 0.5) @@ -281,7 +307,9 @@ local function check_axis_range(self) else local list,dict = {},{} for _,source in ipairs(self._sources) do - list,dict = factors(source.attributes.data.x, list, dict) + list,dict = factors(source.attributes.data.x or + (glyphs[source.id].attributes.x or {}).value, + list, dict) end self:x_range(list) end @@ -304,7 +332,9 @@ local function check_axis_range(self) else local list,dict for _,source in ipairs(self._sources) do - list,dict = factors(source.attributes.data.y, list, dict) + list,dict = factors(source.attributes.data.y or + (glyphs[source.id].attributes.y or {}).value, + list, dict) end self:y_range(list) end @@ -680,7 +710,147 @@ local figure_methods = { end, -- layer functions + + boxes = function(self, params) + + check_mandatories(params, "min", "max", "q1", "q2", "q3", "x") + + local x = params.x + local min = params.min + local max = params.max + local q1 = params.q1 + local q2 = params.q2 + local q3 = params.q3 + local outliers = params.outliers + local width = params.width + + local box_height = {} + local box_mid = {} + local line_height = {} + local line_mid = {} + local max_height = 0 + for i=1,#x do + box_mid[i] = (q1[i] + q3[i])/2.0 + box_height[i] = q3[i] - q1[i] + line_mid[i] = (max[i] + min[i])/2.0 + line_height[i] = max[i] - min[i] + if box_height[i] > max_height then max_height = box_height[i] end + end + local color = params.color or next_color(self) + + self:bars{ x = x, + y = box_mid, + height = box_height, + width = width, + color = color, } + + self:bars{ x = x, + y = q2, + height = 0.01 * max_height, + width = width, + color = color, } + + self:bars{ x = x, + y = min, + height = 0.01 * max_height, + width = width * 0.3, + color = color, } + + self:bars{ x = x, + y = max, + height = 0.01 * max_height, + width = width * 0.3, + color = color, } + + self:bars{ x = x, + y = line_mid, + height = line_height, + width = width * 0.001, + color = color, } + + -- FIXME: check sizes + if outliers then + for i=1,#x do + if outliers[i] and #outliers[i] > 0 then + local list = {} + for j=1,#outliers[i] do list[j] = x[i] end + self:hist2d{ x = list, + y = outliers[i], + color = color, + xgrid = 1, } + end + end + end + + self:x_axis{ type="CategoricalAxis", pos="below" } + + end, + + boxplot = function(self, params) + local x = toseries( params.x ) + local y = toseries( params.y ) + + assert(type(y) == "table" or type(y) == "userdata") + + local boxes = { + min = {}, + max = {}, + q1 = {}, + q2 = {}, + q3 = {}, + outliers = {}, + tag = {}, + width = {}, + x = {}, + } + + local levels + if type(x) == "table" or type(x) == "userdata" then + levels = factors(x) + else + levels = { x or "1" } + end + + for i,factor in ipairs(levels) do + + local tbl = {} + for i=1,#y do + if not x or not x[i] or x[i] == factor then tbl[#tbl + 1] = y[i] end + end + table.sort(tbl) + + local q1 = quantile(tbl, 0.25) + local q2 = quantile(tbl, 0.50) + local q3 = quantile(tbl, 0.75) + local IQ = q3 - q1 + + local min = params.min or quantile(tbl, 0.0) + local max = params.max or quantile(tbl, 1.0) + + local upper = math.min(q3 + 1.5 * IQ, max) + local lower = math.max(q1 - 1.5 * IQ, min) + + local outliers + if not params.ignore_outliers then + outliers = take_outliers(tbl, upper, lower) + end + + boxes.min[i] = upper + boxes.max[i] = lower + boxes.q1[i] = q1 + boxes.q2[i] = q2 + boxes.q3[i] = q3 + boxes.outliers[i] = outliers + boxes.width = params.width or 0.9 + boxes.x[i] = tostring( factor ) + + end + + self:boxes( boxes ) + + end, + lines = function(self, params) -- x, y, color, alpha, width, legend, more_data params = params or {} check_table(params, "x", "y", "color", "alpha", "width", "legend", "more_data") @@ -850,12 +1020,12 @@ local figure_methods = { minsize = params.minsize, maxsize = params.maxsize, xgrid = params.xgrid, - ygrid = params.ygrid + ygrid = params.ygrid, }, { - glyph = params.glyph, - color = params.color, - alpha = params.alpha, + glyph = params.glyph, + color = params.color, + alpha = params.alpha, legend = params.legend, } ) @@ -1000,10 +1170,25 @@ function hist2d_transformation(params, more_params) -- x, y, minsize, maxsize, x local ygrid = params.ygrid or DEF_YGRID assert(minsize <= maxsize, "failure of predicate minsize < maxsize") check_equal_sizes(x, y) - local x_min,x_max = minmax(x) - local y_min,y_max = minmax(y) - local x_width = (x_max - x_min) / (xgrid-1) - local y_width = (y_max - y_min) / (ygrid-1) + local x_min,x_max = assert( minmax(x) ) + local y_min,y_max = assert( minmax(y) ) + local x_width,y_width,x_off,y_off + if xgrid == 1 then + x_width = math.max(1.0, x_max - x_min) + y_width = (y_max - y_min) / (ygrid-1) + x_off = 0.0 + y_off = y_width*0.5 + elseif ygrid == 1 then + x_width = (x_max - x_min) / (xgrid-1) + y_width = math.max(1.0, y_max - y_min) + x_off = x_width*0.5 + y_off = 0.0 + else + x_width = (x_max - x_min) / (xgrid-1) + y_width = (y_max - y_min) / (ygrid-1) + x_off = x_width*0.5 + y_off = y_width*0.5 + end local grid = {} for i=1,xgrid*ygrid do grid[i] = 0 end local max_count = 0 @@ -1014,8 +1199,6 @@ function hist2d_transformation(params, more_params) -- x, y, minsize, maxsize, x grid[k] = grid[k] + 1 if grid[k] > max_count then max_count= grid[k] end end - local x_off = x_width*0.5 - local y_off = y_width*0.5 local size_diff = maxsize - minsize local new_x,new_y,new_sizes,counts,ratios = {},{},{},{},{} local l=1 @@ -1043,7 +1226,7 @@ function hist2d_transformation(params, more_params) -- x, y, minsize, maxsize, x ratio = ratios, }, } - for k,v in ipairs(more_params or {}) do + for k,v in pairs(more_params or {}) do assert(k ~= "more_data", "Unable to handle more_data argument") result[k]=v end From 390ed58386c8287c423fd4360ae6eba927b74a34 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Mon, 12 Oct 2015 19:11:55 +0200 Subject: [PATCH 66/94] Fixed problems with boxplot and lines plots --- IPyLua/bokeh.lua | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index 4fb09a4..1686edf 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -9,6 +9,7 @@ local DEF_XGRID = 100 local DEF_YGRID = 100 local DEF_SIZE = 6 local DEF_WIDTH = 6 +local DEF_LINE_WIDTH = 2 local DEF_HEIGTH = 6 local DEF_ALPHA = 0.8 local DEF_X = 0 @@ -174,7 +175,7 @@ end local function compute_optim(x, DEF) local optim local x = toseries(x) - if type(x) == "number" then + if not x or type(x) == "number" then optim = DEF elseif type(x) == "string" then optim = 1.0 @@ -859,9 +860,9 @@ local figure_methods = { local y = params.y local color = params.color or next_color(self) local alpha = params.alpha or DEF_ALPHA - local width = params.width or DEF_WIDTH + local width = params.width or DEF_LINE_WIDTH - local data = { x=x, y=y } + local data = { x=x, y=y, width=width, alpha=alpha, color=color } local more_data = params.more_data or {} local s_data,s_columns,s_more_data = create_data_columns(data, more_data) @@ -1173,20 +1174,18 @@ function hist2d_transformation(params, more_params) -- x, y, minsize, maxsize, x local x_min,x_max = assert( minmax(x) ) local y_min,y_max = assert( minmax(y) ) local x_width,y_width,x_off,y_off - if xgrid == 1 then + if xgrid == 1 or x_max == x_min then x_width = math.max(1.0, x_max - x_min) - y_width = (y_max - y_min) / (ygrid-1) x_off = 0.0 - y_off = y_width*0.5 - elseif ygrid == 1 then + else x_width = (x_max - x_min) / (xgrid-1) - y_width = math.max(1.0, y_max - y_min) x_off = x_width*0.5 + end + if ygrid == 1 or y_max == y_min then + y_width = math.max(1.0, y_max - y_min) y_off = 0.0 else - x_width = (x_max - x_min) / (xgrid-1) y_width = (y_max - y_min) / (ygrid-1) - x_off = x_width*0.5 y_off = y_width*0.5 end local grid = {} From c97e5a687f1ac0adbf7da9454b7219c3dd859725 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Mon, 12 Oct 2015 23:17:05 +0200 Subject: [PATCH 67/94] Improved boxplot --- IPyLua/bokeh.lua | 82 +++++++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index 1686edf..c47ef8b 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -4,6 +4,8 @@ local html_template = require "IPyLua.html_template" local null = json.null local type = luatype or type +local DEF_LEVEL = "__nil__" +local DEF_BOX_WIDTH = 0.5 local DEF_BREAKS = 20 local DEF_XGRID = 100 local DEF_YGRID = 100 @@ -713,7 +715,9 @@ local figure_methods = { -- layer functions boxes = function(self, params) - + check_table(params, "min", "max", "q1", "q2", "q3", "x", + "outliers", "width", "alpha") + check_mandatories(params, "min", "max", "q1", "q2", "q3", "x") local x = params.x @@ -723,7 +727,8 @@ local figure_methods = { local q2 = params.q2 local q3 = params.q3 local outliers = params.outliers - local width = params.width + local width = params.width or DEF_BOX_WIDTH + local alpha = params.alpha or DEF_ALPHA local box_height = {} local box_mid = {} @@ -739,47 +744,54 @@ local figure_methods = { end local color = params.color or next_color(self) + + self:bars{ x = x, + y = line_mid, + height = line_height, + width = width * 0.005, + alpha = alpha, + color = color, } self:bars{ x = x, y = box_mid, height = box_height, width = width, - color = color, } + color = color, + alpha = alpha, + legend = params.legend, } self:bars{ x = x, y = q2, - height = 0.01 * max_height, + height = 0.005 * max_height, width = width, + alpha = alpha, color = color, } self:bars{ x = x, y = min, - height = 0.01 * max_height, + height = 0.005 * max_height, width = width * 0.3, + alpha = alpha, color = color, } self:bars{ x = x, y = max, - height = 0.01 * max_height, + height = 0.005 * max_height, width = width * 0.3, + alpha = alpha, color = color, } - self:bars{ x = x, - y = line_mid, - height = line_height, - width = width * 0.001, - color = color, } - -- FIXME: check sizes if outliers then for i=1,#x do if outliers[i] and #outliers[i] > 0 then local list = {} for j=1,#outliers[i] do list[j] = x[i] end - self:hist2d{ x = list, + self:points{ x = list, y = outliers[i], color = color, - xgrid = 1, } + alpha = alpha, } + --xgrid = 1, } end end end @@ -801,7 +813,6 @@ local figure_methods = { q2 = {}, q3 = {}, outliers = {}, - tag = {}, width = {}, x = {}, } @@ -810,15 +821,21 @@ local figure_methods = { if type(x) == "table" or type(x) == "userdata" then levels = factors(x) else - levels = { x or "1" } + levels = { x or DEF_LEVEL } + end + + + local tbl = {} + for i,factor in ipairs(levels) do tbl[factor] = {} end + + for i=1,#y do + local key = x and x[i] and tostring(x[i]) or DEF_LEVEL + table.insert( tbl[key], y[i] ) end for i,factor in ipairs(levels) do - local tbl = {} - for i=1,#y do - if not x or not x[i] or x[i] == factor then tbl[#tbl + 1] = y[i] end - end + local tbl = tbl[factor] table.sort(tbl) local q1 = quantile(tbl, 0.25) @@ -843,7 +860,7 @@ local figure_methods = { boxes.q2[i] = q2 boxes.q3[i] = q3 boxes.outliers[i] = outliers - boxes.width = params.width or 0.9 + boxes.width = params.width or DEF_BOX_WIDTH boxes.x[i] = tostring( factor ) end @@ -928,28 +945,26 @@ local figure_methods = { return self end, - cors = function(self, params) -- data, alpha, legend, min, max + corplot = function(self, params) -- xy, names, alpha, legend, min, max params = params or {} - check_table(params, "xy", "xnames", "ynames", "alpha", - "legend", "min", "max") + check_table(params, "xy", "names", "alpha", "legend", "min", "max") check_mandatories(params, "xy") local alpha = params.alpha or DEF_ALPHA local hover = params.hover - local xnames = params.xnames or {} - local ynames = params.ynames or {} + local names = params.names or {} local xy = tomatrix( params.xy ) local x = {} local y = {} local cor = {} local N = #xy - for i=1,N do + for i=N,1,-1 do local row = toseries( xy[i] ) for j=1,#row do local c = row[j] - table.insert(x, xnames[j] or j) - table.insert(y, ynames[i] or i) + table.insert(x, names[j] or j) + table.insert(y, names[i] or i) table.insert(cor, c) end end @@ -962,12 +977,15 @@ local figure_methods = { alpha=alpha, color=linear_color_transformer(cor, params.min or -1.0, params.max or 1.0), hover=cor, + legend=params.legend, } self:bars( data ) - if xnames then self:x_axis{ type="CategoricalAxis", pos="below" } end - if ynames then self:y_axis{ type="CategoricalAxis", pos="left" } end + if names then + self:x_axis{ type="CategoricalAxis", pos="below" } + self:y_axis{ type="CategoricalAxis", pos="left" } + end return self end, @@ -1272,7 +1290,7 @@ end function linear_size_transformer(x, smin, smax) local x = toseries(x) assert(type(x) == "table", "needs a series as 1st argument") - assert(smin and smax, "needs two numbers as 2nd and 3rd arguments") + local smin,smax = smin or DEF_SIZE*0.5, smax or DEF_SIZE*2.0 local sdiff = smax - smin local min,max = minmax(x) local diff = max-min From f1557d0acb6921b70fd96dc44c2c4e1fad7261ca Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Mon, 12 Oct 2015 23:20:27 +0200 Subject: [PATCH 68/94] Improved boxplot --- IPyLua/bokeh.lua | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index c47ef8b..63230b9 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -818,40 +818,45 @@ local figure_methods = { } local levels - if type(x) == "table" or type(x) == "userdata" then - levels = factors(x) + if not params.factors then + if type(x) == "table" or type(x) == "userdata" then + levels = factors(x) + else + levels = { x or DEF_LEVEL } + end else - levels = { x or DEF_LEVEL } + local aux = params.factors + levels = {} + for i=1,#aux do levels[i] = tostring(aux[i]) end end - - - local tbl = {} - for i,factor in ipairs(levels) do tbl[factor] = {} end + + local plt = {} + for i,factor in ipairs(levels) do plt[factor] = {} end for i=1,#y do local key = x and x[i] and tostring(x[i]) or DEF_LEVEL - table.insert( tbl[key], y[i] ) + table.insert( plt[key], y[i] ) end for i,factor in ipairs(levels) do - local tbl = tbl[factor] - table.sort(tbl) + local cur = plt[factor] + table.sort(cur) - local q1 = quantile(tbl, 0.25) - local q2 = quantile(tbl, 0.50) - local q3 = quantile(tbl, 0.75) + local q1 = quantile(cur, 0.25) + local q2 = quantile(cur, 0.50) + local q3 = quantile(cur, 0.75) local IQ = q3 - q1 - local min = params.min or quantile(tbl, 0.0) - local max = params.max or quantile(tbl, 1.0) + local min = params.min or quantile(cur, 0.0) + local max = params.max or quantile(cur, 1.0) local upper = math.min(q3 + 1.5 * IQ, max) local lower = math.max(q1 - 1.5 * IQ, min) local outliers if not params.ignore_outliers then - outliers = take_outliers(tbl, upper, lower) + outliers = take_outliers(cur, upper, lower) end boxes.min[i] = upper From ac401c9f1a35ff60a656c117b7f9fca226956dd7 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Mon, 12 Oct 2015 23:22:31 +0200 Subject: [PATCH 69/94] Improved boxplot --- IPyLua/bokeh.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index 63230b9..a09d56b 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -835,6 +835,7 @@ local figure_methods = { for i=1,#y do local key = x and x[i] and tostring(x[i]) or DEF_LEVEL + assert( plt[key], "found unknown factor level " .. key ) table.insert( plt[key], y[i] ) end From 949ac6ffe04d72f4382b81422532119e65089996 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Tue, 13 Oct 2015 13:12:40 +0200 Subject: [PATCH 70/94] Added first version of vioplot --- IPyLua/bokeh.lua | 344 +++++++++++++++++++++++++++++++++-------------- IPyLuaKernel.lua | 26 +++- demo.ipynb | 81 +++++++---- 3 files changed, 321 insertions(+), 130 deletions(-) diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index a09d56b..5559399 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -4,18 +4,20 @@ local html_template = require "IPyLua.html_template" local null = json.null local type = luatype or type -local DEF_LEVEL = "__nil__" +local DEF_ALPHA = 0.8 local DEF_BOX_WIDTH = 0.5 local DEF_BREAKS = 20 -local DEF_XGRID = 100 -local DEF_YGRID = 100 +local DEF_HEIGTH = 6 +local DEF_LEVEL = "__nil__" +local DEF_LINE_WIDTH = 2 local DEF_SIZE = 6 +local DEF_VIOLIN_WIDTH=0.95 local DEF_WIDTH = 6 -local DEF_LINE_WIDTH = 2 -local DEF_HEIGTH = 6 -local DEF_ALPHA = 0.8 local DEF_X = 0 +local DEF_XGRID = 100 local DEF_Y = 0 +local DEF_YGRID = 100 + local COLORS = { "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", "#00ffff", "#000000", "#800000", "#008000", "#000080", "#808000", "#800080", "#008080", "#808080", @@ -31,6 +33,7 @@ local figure = {} local figure_methods = {} -- forward declarations +local box_transformation local hist2d_transformation local linear_color_transformer local linear_size_transformer @@ -79,6 +82,7 @@ local function reduce(t, func) end local math_abs = math.abs +local math_floor = math.floor local math_min = math.min local math_max = math.max @@ -104,21 +108,33 @@ end local function minmax(t, size) local t = t or 0.0 - local size = size or {} + local size = size or 0.0 local tmin,tmax local tt = type(t) if tt == "table" or tt == "userdata" then - local s = (size[1] or 0)*0.5 - tmin = t[1] - s - tmax = t[1] + s - for i=2,#t do - local s = (size[i] or 0)*0.5 - tmin = math_min(tmin, t[i] - s) - tmax = math_max(tmax, t[i] + s) + if type(size) == "table" or type(size) == "userdata" then + local s = size[1] * 0.5 + tmin = t[1] - s + tmax = t[1] + s + for i=2,#t do + local s = size[i] * 0.5 + tmin = math_min(tmin, t[i] - s) + tmax = math_max(tmax, t[i] + s) + end + else + assert(type(size) == "number", + "Needs a series or number as second parameter") + local s = size * 0.5 + tmin = t[1] - s + tmax = t[1] + s + for i=2,#t do + tmin = math_min(tmin, t[i] - s) + tmax = math_max(tmax, t[i] + s) + end end else local s = max(size) * 0.5 - tmin = t - s + tmin = t - s tmax = t + s end return tmin,tmax @@ -126,8 +142,15 @@ end local function factors(t, out, dict) local out,dict = out or {},dict or {} - for i=1,#t do - local s = tostring(t[i]) + if type(t) == "table" or type(t) == "userdata" then + for i=1,#t do + local s = tostring(t[i]) + if not dict[s] then + out[#out+1],dict[s] = s,true + end + end + else + local s = tostring(t) if not dict[s] then out[#out+1],dict[s] = s,true end @@ -300,11 +323,11 @@ local function check_axis_range(self) for _,source in ipairs(self._sources) do local s_min,s_max = minmax(source.attributes.data.x or (glyphs[source.id].attributes.x or {}).value, - source.attributes.data.width) - local offset = (glyphs[source.id].attributes.width or {}).value or 0.0 - - x_min = math.min(x_min,s_min) - offset*0.5 - x_max = math.max(x_max,s_max) + offset*0.5 + source.attributes.data.width or + (glyphs[source.id].attributes.width or {}).value) + + x_min = math.min(x_min,s_min) + x_max = math.max(x_max,s_max) end self:x_range( apply_gap(0.05, x_min, x_max) ) else @@ -325,11 +348,11 @@ local function check_axis_range(self) for _,source in ipairs(self._sources) do local s_min,s_max = minmax(source.attributes.data.y or (glyphs[source.id].attributes.y or {}).value, - source.attributes.data.height) - local offset = (glyphs[source.id].attributes.height or {}).value or 0.0 + source.attributes.data.height or + (glyphs[source.id].attributes.height or {}).value) - y_min = math.min(y_min, s_min) - offset*0.5 - y_max = math.max(y_max, s_max) + offset*0.5 + y_min = math.min(y_min, s_min) + y_max = math.max(y_max, s_max) end self:y_range( apply_gap(0.05, y_min, y_max) ) else @@ -714,9 +737,9 @@ local figure_methods = { -- layer functions - boxes = function(self, params) + boxes = function(self, params) -- min, max, q1, q2, q3, x, outliers, width, alpha, legend, color check_table(params, "min", "max", "q1", "q2", "q3", "x", - "outliers", "width", "alpha") + "outliers", "width", "alpha", "legend", "color") check_mandatories(params, "min", "max", "q1", "q2", "q3", "x") @@ -798,80 +821,21 @@ local figure_methods = { self:x_axis{ type="CategoricalAxis", pos="below" } + return self end, - boxplot = function(self, params) - local x = toseries( params.x ) - local y = toseries( params.y ) - - assert(type(y) == "table" or type(y) == "userdata") - - local boxes = { - min = {}, - max = {}, - q1 = {}, - q2 = {}, - q3 = {}, - outliers = {}, - width = {}, - x = {}, - } + boxplot = function(self, params) -- x, y, legend, alpha, color, factors, width, ignore_outliers - local levels - if not params.factors then - if type(x) == "table" or type(x) == "userdata" then - levels = factors(x) - else - levels = { x or DEF_LEVEL } - end - else - local aux = params.factors - levels = {} - for i=1,#aux do levels[i] = tostring(aux[i]) end - end - - local plt = {} - for i,factor in ipairs(levels) do plt[factor] = {} end - - for i=1,#y do - local key = x and x[i] and tostring(x[i]) or DEF_LEVEL - assert( plt[key], "found unknown factor level " .. key ) - table.insert( plt[key], y[i] ) - end - - for i,factor in ipairs(levels) do - - local cur = plt[factor] - table.sort(cur) - - local q1 = quantile(cur, 0.25) - local q2 = quantile(cur, 0.50) - local q3 = quantile(cur, 0.75) - local IQ = q3 - q1 - - local min = params.min or quantile(cur, 0.0) - local max = params.max or quantile(cur, 1.0) - - local upper = math.min(q3 + 1.5 * IQ, max) - local lower = math.max(q1 - 1.5 * IQ, min) - - local outliers - if not params.ignore_outliers then - outliers = take_outliers(cur, upper, lower) - end - - boxes.min[i] = upper - boxes.max[i] = lower - boxes.q1[i] = q1 - boxes.q2[i] = q2 - boxes.q3[i] = q3 - boxes.outliers[i] = outliers - boxes.width = params.width or DEF_BOX_WIDTH - boxes.x[i] = tostring( factor ) - - end - - self:boxes( boxes ) + local boxes = + box_transformation( params, + { + legend=params.legend, + alpha=params.alpha, + color=params.color, + } + ) + + return self:boxes( boxes ) end, @@ -1056,6 +1020,26 @@ local figure_methods = { ) return self:points( hist2d ) end, + + vioplot = function(self, params) -- x, y, legend, alpha, color, factors, width + + local violins = + violin_transformation(params, + { + alpha = params.alpha, + legend = params.legend, + color = params.color, + } + ) + + for i=1,#violins.bars do + self:bars(violins.bars[i]) + end + + self:x_axis{ type="CategoricalAxis", pos="below" } + + return self + end, -- conversion @@ -1179,6 +1163,115 @@ setmetatable( -- data transformers +local function hist(x, breaks, output_type, scale) + local result = {} + local min = x[1] + local max = x[#x] + local diff = max - min + assert(diff > 0, "Unable to compute histogram for given data") + local inc = diff / (breaks+1) + local half = inc * 0.5 + local bins = {} + local y = {} + for i=1,breaks do + bins[i] = 0.0 + y[i] = (i - 1.0) * inc + half + min + end + for i=1,#x do + local b = math_floor( (x[i] - min)/diff * breaks ) + 1.0 + b = math_max(0.0, math_min(breaks, b)) + bins[b] = bins[b] + 1.0 + end + if output_type == "ratio" then + local scale = scale or 1.0 + local N = #x for i=1,#bins do bins[i] = (bins[i]/N)*scale end + elseif scale then + for i=1,#bins do bins[i] = bins[i] * scale end + end + return { y=y, width=bins, height=inc } +end + +-- +function box_transformation(params, more_params) -- x, y, factors, width, ignore_outliers + local x = toseries( params.x ) + local y = toseries( params.y ) + + assert(type(y) == "table" or type(y) == "userdata") + + local boxes = { + min = {}, + max = {}, + q1 = {}, + q2 = {}, + q3 = {}, + outliers = {}, + width = {}, + x = {}, + } + + local levels + if not params.factors then + if type(x) == "table" or type(x) == "userdata" then + levels = factors(x) + else + levels = { x or DEF_LEVEL } + end + else + local aux = params.factors + levels = {} + for i=1,#aux do levels[i] = tostring(aux[i]) end + end + + local plt = {} + for i,factor in ipairs(levels) do plt[factor] = {} end + + for i=1,#y do + local key = x and x[i] and tostring(x[i]) or DEF_LEVEL + assert( plt[key], "found unknown factor level " .. key ) + table.insert( plt[key], y[i] ) + end + + for i,factor in ipairs(levels) do + + local cur = plt[factor] + table.sort(cur) + + local q1 = quantile(cur, 0.25) + local q2 = quantile(cur, 0.50) + local q3 = quantile(cur, 0.75) + local IQ = q3 - q1 + + local min = params.min or quantile(cur, 0.0) + local max = params.max or quantile(cur, 1.0) + + local upper = math.min(q3 + 1.5 * IQ, max) + local lower = math.max(q1 - 1.5 * IQ, min) + + local outliers + if not params.ignore_outliers then + outliers = take_outliers(cur, upper, lower) + end + + boxes.min[i] = upper + boxes.max[i] = lower + boxes.q1[i] = q1 + boxes.q2[i] = q2 + boxes.q3[i] = q3 + boxes.outliers[i] = outliers + boxes.width = params.width or DEF_BOX_WIDTH + boxes.x[i] = tostring( factor ) + + end + + for k,v in pairs(more_params or {}) do + assert(k ~= "more_data", "Unable to handle more_data argument") + assert(not boxes[k], "Unable to redefine parameter " .. k) + boxes[k]=v + end + + return boxes +end + -- local hist2d_transformation is declared at the top of this file function hist2d_transformation(params, more_params) -- x, y, minsize, maxsize, xgrid, ygrid params = params or {} @@ -1257,6 +1350,58 @@ function hist2d_transformation(params, more_params) -- x, y, minsize, maxsize, x return result end +function violin_transformation(params, more_params) -- x, y, factors, width, breaks + local breaks = params.breaks or DEF_BREAKS + local x = toseries( params.x ) + local y = toseries( params.y ) + + assert(type(y) == "table" or type(y) == "userdata") + + local violins = { + bars = {}, + } + + local levels + if not params.factors then + if type(x) == "table" or type(x) == "userdata" then + levels = factors(x) + else + levels = { x or DEF_LEVEL } + end + else + local aux = params.factors + levels = {} + for i=1,#aux do levels[i] = tostring(aux[i]) end + end + + local plt = {} + for i,factor in ipairs(levels) do plt[factor] = {} end + + for i=1,#y do + local key = x and x[i] and tostring(x[i]) or DEF_LEVEL + assert( plt[key], "found unknown factor level " .. key ) + table.insert( plt[key], y[i] ) + end + + for i,factor in ipairs(levels) do + local cur = plt[factor] + table.sort(cur) + + local bars = violins.bars + bars[i] = hist(cur, breaks, "ratio", width) + bars[i].x = tostring( factor ) + bars[i].hover = bars[i].width + + for k,v in pairs(more_params or {}) do + assert(k ~= "more_data", "Unable to handle more_data argument") + assert(not bars[i][k], "Unable to redefine parameter " .. k) + bars[i][k]=v + end + end + + return violins +end + -- color transformers -- http://www.andrewnoske.com/wiki/Code_-_heatmaps_and_color_gradients @@ -1317,6 +1462,7 @@ return { linear = linear_size_transformer, }, transformations = { + box = box_transformation, hist2d = hist2d_transformation, }, } diff --git a/IPyLuaKernel.lua b/IPyLuaKernel.lua index d6ef78e..948a9c0 100644 --- a/IPyLuaKernel.lua +++ b/IPyLuaKernel.lua @@ -390,8 +390,10 @@ do end local args = table.pack(...) local html = { "
" } - for i=1,#args do - if args[i] == "\n" then + for i=1,args.n do + if args[i] == nil then + table.insert(html, '
nil
') + elseif args[i] == "\n" then table.insert(html, '
') else local component @@ -419,16 +421,22 @@ do env_G.print = function(...) local args = table.pack(...) - for i=1,#args do args[i]=tostring(args[i]) end + for i=1,args.n do args[i]=tostring(args[i]) end local str = table.concat(args,"\t") - pyout({ ["text/plain"] = str.."\n" }) + pyout({ + ["text/plain"] = str.."\n", + ["text/html"] = ("
%s
"):format(str), + }) end env_G.io.write = function(...) local args = table.pack(...) - for i=1,#args do args[i]=tostring(args[i]) end + for i=1,args.n do args[i]=tostring(args[i]) end local str = table.concat(table.pack(...)) - pyout({ ["text/plain"] = str.."\n" }) + pyout({ + ["text/plain"] = str.."\n", + ["text/html"] = ("
%s
"):format(str), + }) end env_G.vars = function() @@ -570,7 +578,11 @@ local function send_pyerr_message(sock, parent, count, err) parent = parent, header = header, content = content, - }) + }) + -- env_G.pyout({ + -- ["text/plain"] = err.."\n", + -- ["text/html"] = ("
%s
"):format(err), + -- }) end -- implemented routes diff --git a/demo.ipynb b/demo.ipynb index cc3afea..63be608 100644 --- a/demo.ipynb +++ b/demo.ipynb @@ -2,7 +2,7 @@ "metadata": { "language": "lua", "name": "", - "signature": "sha256:03a88a2e988e168f3f1ead7aac71e1b539ca851fd6df197f5122856e3e2cb7be" + "signature": "sha256:f3ddbf4d6bccf8e356162a2248cf98b27c64b3105c5d37864c5f13c6e54fb769" }, "nbformat": 3, "nbformat_minor": 0, @@ -48,6 +48,9 @@ "metadata": {}, "outputs": [ { + "html": [ + "
fib(1) =\t1
" + ], "metadata": {}, "output_type": "pyout", "prompt_number": 2, @@ -56,6 +59,9 @@ ] }, { + "html": [ + "
fib(2) =\t1
" + ], "metadata": {}, "output_type": "pyout", "prompt_number": 2, @@ -64,6 +70,9 @@ ] }, { + "html": [ + "
fib(3) =\t2
" + ], "metadata": {}, "output_type": "pyout", "prompt_number": 2, @@ -72,6 +81,9 @@ ] }, { + "html": [ + "
fib(4) =\t3
" + ], "metadata": {}, "output_type": "pyout", "prompt_number": 2, @@ -80,6 +92,9 @@ ] }, { + "html": [ + "
fib(5) =\t5
" + ], "metadata": {}, "output_type": "pyout", "prompt_number": 2, @@ -88,6 +103,9 @@ ] }, { + "html": [ + "
fib(6) =\t8
" + ], "metadata": {}, "output_type": "pyout", "prompt_number": 2, @@ -96,6 +114,9 @@ ] }, { + "html": [ + "
fib(7) =\t13
" + ], "metadata": {}, "output_type": "pyout", "prompt_number": 2, @@ -104,6 +125,9 @@ ] }, { + "html": [ + "
fib(8) =\t21
" + ], "metadata": {}, "output_type": "pyout", "prompt_number": 2, @@ -112,6 +136,9 @@ ] }, { + "html": [ + "
fib(9) =\t34
" + ], "metadata": {}, "output_type": "pyout", "prompt_number": 2, @@ -120,6 +147,9 @@ ] }, { + "html": [ + "
fib(10) =\t55
" + ], "metadata": {}, "output_type": "pyout", "prompt_number": 2, @@ -170,11 +200,14 @@ "metadata": {}, "outputs": [ { + "html": [ + "
table: 0x1428010
" + ], "metadata": {}, "output_type": "pyout", "prompt_number": 4, "text": [ - "table: 0x8ce820\n" + "table: 0x1428010\n" ] }, { @@ -186,7 +219,7 @@ "\t[\"a\"] = 1,\n", "\t[\"b\"] = 2,\n", "}\n", - "-- table: 0x8ce820 with 3 array part, 2 hash part" + "-- table: 0x1428010 with 3 array part, 2 hash part" ], "metadata": {}, "output_type": "pyout", @@ -199,7 +232,7 @@ "\t[\"a\"] = 1,\n", "\t[\"b\"] = 2,\n", "}\n", - "-- table: 0x8ce820 with 3 array part, 2 hash part\n" + "-- table: 0x1428010 with 3 array part, 2 hash part\n" ] } ], @@ -230,19 +263,19 @@ "\t[\"a\"] = 1,\n", "\t[\"b\"] = 2,\n", "}\n", - "-- table: 0x8ce820 with 3 array part, 2 hash part
{\n",
+        "-- table: 0x1428010 with 3 array part, 2 hash part
{\n",
         "\t[1] = 1,\n",
         "\t[2] = 2,\n",
         "\t[3] = 3,\n",
         "\t[4] = 4,\n",
         "}\n",
-        "-- table: 0x8e66a0 with 4 array part, 0 hash part
" + "-- table: 0x13a3f40 with 4 array part, 0 hash part" ], "metadata": {}, "output_type": "pyout", "prompt_number": 5, "text": [ - "table: 0x8ce820\ttable: 0x8e66a0\n" + "table: 0x1428010\ttable: 0x13a3f40\n" ] } ], @@ -267,20 +300,20 @@ { "html": [ "
{\n",
-        "\t[\"fib\"] = function: 0x928df0,\n",
-        "\t[\"tbl\"] = table: 0x8ce820,\n",
+        "\t[\"fib\"] = function: 0x14286d0,\n",
+        "\t[\"tbl\"] = table: 0x1428010,\n",
         "}\n",
-        "-- table: 0x90d9b0 with 0 array part, 2 hash part
" + "-- table: 0x140d240 with 0 array part, 2 hash part" ], "metadata": {}, "output_type": "pyout", "prompt_number": 6, "text": [ "{\n", - "\t[\"fib\"] = function: 0x928df0,\n", - "\t[\"tbl\"] = table: 0x8ce820,\n", + "\t[\"fib\"] = function: 0x14286d0,\n", + "\t[\"tbl\"] = table: 0x1428010,\n", "}\n", - "-- table: 0x90d9b0 with 0 array part, 2 hash part\n" + "-- table: 0x140d240 with 0 array part, 2 hash part\n" ] } ], @@ -315,14 +348,14 @@ " 0.00668896 0.0083612 0.0100334 0.0117057 ... 0.506689 \n", "...\n", " 0.5 0.501672 0.503344 0.505017 ... 1 \n", - "# Matrix of size [300,300] stride [300,1] ref [0xc69810 data= 0xc698e0]\n", + "# Matrix of size [300,300] stride [300,1] ref [0x1647560 data= 0x1705a00]\n", "" ], "metadata": {}, "output_type": "pyout", "prompt_number": 7, "text": [ - "# Image 300x300+0+0 [0xbaf960 data= 0xb985e0]\tuserdata\n" + "# Image 300x300+0+0 [0x14e5dd0 data= 0x176bc40]\tuserdata\n" ] } ], @@ -358,9 +391,9 @@ { "html": [ "\n", @@ -391,22 +424,22 @@ "\n", " bokehjs_url = \"https://cdn.pydata.org/bokeh/release/bokeh-0.10.0.min.js\"\n", "\n", - " var elt = document.getElementById(\"c823b43b-9802-4179-c1ca-202ac6ec8e4a\");\n", + " var elt = document.getElementById(\"9165a560-bf4e-4433-c48c-504cacda79f4\");\n", " if(elt==null) {\n", - " console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'c823b43b-9802-4179-c1ca-202ac6ec8e4a' but no matching script tag was found. \")\n", + " console.log(\"Bokeh: ERROR: autoload.js configured with elementid '9165a560-bf4e-4433-c48c-504cacda79f4' but no matching script tag was found. \")\n", " return false;\n", " }\n", "\n", " // These will be set for the static case\n", - " var all_models = [{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"attributes\":{\"extra_x_ranges\":[],\"plot_width\":600,\"tools\":[{\"type\":\"PanTool\",\"id\":\"d1817714-307b-4c0b-cc3e-b69c17f6de2a\"},{\"type\":\"WheelZoomTool\",\"id\":\"79c1c701-94bc-4eec-cec9-f219463dcf18\"},{\"type\":\"BoxZoomTool\",\"id\":\"bf462cf0-c218-4bd8-cbb1-746fa7529921\"},{\"type\":\"ResizeTool\",\"id\":\"146023a8-1de1-4535-c587-4ff1c41f0983\"},{\"type\":\"ResetTool\",\"id\":\"66367329-4e6f-41a6-c176-15c7c9afe9de\"},{\"type\":\"PreviewSaveTool\",\"id\":\"100c862d-ed1c-4398-c3b3-8a68d293eb38\"},{\"type\":\"HoverTool\",\"id\":\"ca5f6118-ce63-4fef-cfd4-b6a2849f8094\"}],\"left\":[{\"type\":\"LinearAxis\",\"id\":\"35fe89aa-77d4-44b6-c431-d627ac525888\"}],\"tool_events\":{\"type\":\"ToolEvents\",\"id\":\"1283e39c-87d2-45bc-c55c-0b06017c4ab5\"},\"below\":[{\"type\":\"LinearAxis\",\"id\":\"aca1d5d0-c507-479e-c7c6-4813e12c2d5c\"}],\"title_text_font_size\":{\"value\":\"12pt\"},\"tags\":[],\"plot_height\":500,\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"above\":[],\"y_range\":{\"type\":\"Range1d\",\"id\":\"c8dc5261-de24-4a63-cafb-8eb5e81d0027\"},\"x_range\":{\"type\":\"Range1d\",\"id\":\"3f31081c-ba9d-46ad-c6c9-b8ba4ea52717\"},\"responsive\":false,\"right\":[],\"renderers\":[{\"type\":\"Grid\",\"id\":\"7160a519-53de-4b1a-cbbf-665d64459911\"},{\"type\":\"LinearAxis\",\"id\":\"aca1d5d0-c507-479e-c7c6-4813e12c2d5c\"},{\"type\":\"Grid\",\"id\":\"75a3b775-8a01-4431-c471-3ca73859836d\"},{\"type\":\"LinearAxis\",\"id\":\"35fe89aa-77d4-44b6-c431-d627ac525888\"},{\"type\":\"GlyphRenderer\",\"id\":\"050de5e6-0a6b-4f38-cf9f-127668759ad5\"},{\"type\":\"GlyphRenderer\",\"id\":\"8dacf59f-a36e-42fe-c2ec-1943cd4a2fba\"},{\"type\":\"GlyphRenderer\",\"id\":\"5d9d8460-80b1-44a6-c4b1-85e53dacb976\"},{\"type\":\"Legend\",\"id\":\"c92d5355-a32e-4e06-ce6a-3662c7210325\"},{\"type\":\"GlyphRenderer\",\"id\":\"8352a2bd-dd88-4519-c552-930bc984ec6d\"}],\"title_text_font_style\":\"bold\",\"extra_y_ranges\":[]}},{\"type\":\"ToolEvents\",\"id\":\"1283e39c-87d2-45bc-c55c-0b06017c4ab5\",\"attributes\":{\"doc\":null,\"geometries\":[],\"id\":\"1283e39c-87d2-45bc-c55c-0b06017c4ab5\",\"tags\":[]}},{\"type\":\"PanTool\",\"id\":\"d1817714-307b-4c0b-cc3e-b69c17f6de2a\",\"attributes\":{\"id\":\"d1817714-307b-4c0b-cc3e-b69c17f6de2a\",\"plot\":{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"subtype\":\"Chart\"},\"doc\":null,\"tags\":[],\"dimensions\":[\"width\",\"height\"]}},{\"type\":\"WheelZoomTool\",\"id\":\"79c1c701-94bc-4eec-cec9-f219463dcf18\",\"attributes\":{\"id\":\"79c1c701-94bc-4eec-cec9-f219463dcf18\",\"plot\":{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"subtype\":\"Chart\"},\"doc\":null,\"tags\":[],\"dimensions\":[\"width\",\"height\"]}},{\"type\":\"BoxZoomTool\",\"id\":\"bf462cf0-c218-4bd8-cbb1-746fa7529921\",\"attributes\":{\"id\":\"bf462cf0-c218-4bd8-cbb1-746fa7529921\",\"plot\":{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"subtype\":\"Chart\"},\"doc\":null,\"tags\":[],\"dimensions\":[\"width\",\"height\"]}},{\"type\":\"ResizeTool\",\"id\":\"146023a8-1de1-4535-c587-4ff1c41f0983\",\"attributes\":{\"doc\":null,\"tags\":[],\"id\":\"146023a8-1de1-4535-c587-4ff1c41f0983\",\"plot\":{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"subtype\":\"Chart\"}}},{\"type\":\"ResetTool\",\"id\":\"66367329-4e6f-41a6-c176-15c7c9afe9de\",\"attributes\":{\"doc\":null,\"tags\":[],\"id\":\"66367329-4e6f-41a6-c176-15c7c9afe9de\",\"plot\":{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"subtype\":\"Chart\"}}},{\"type\":\"PreviewSaveTool\",\"id\":\"100c862d-ed1c-4398-c3b3-8a68d293eb38\",\"attributes\":{\"doc\":null,\"tags\":[],\"id\":\"100c862d-ed1c-4398-c3b3-8a68d293eb38\",\"plot\":{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"subtype\":\"Chart\"}}},{\"type\":\"HoverTool\",\"id\":\"ca5f6118-ce63-4fef-cfd4-b6a2849f8094\",\"attributes\":{\"callback\":null,\"id\":\"ca5f6118-ce63-4fef-cfd4-b6a2849f8094\",\"names\":[],\"always_active\":true,\"name\":null,\"plot\":{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"subtype\":\"Chart\"},\"tooltips\":[[\"Tag\",\"@hover\"],[\"(x,y)\",\"($x, $y)\"]],\"renderers\":[],\"tags\":[],\"doc\":null,\"point_policy\":\"follow_mouse\"}},{\"type\":\"BasicTickFormatter\",\"id\":\"ab07c199-2324-42c7-c2bc-2faa501be31a\",\"attributes\":{\"tags\":[],\"id\":\"ab07c199-2324-42c7-c2bc-2faa501be31a\",\"doc\":null}},{\"type\":\"BasicTicker\",\"id\":\"7a453349-a8f2-4881-c8ef-244b8fa4df3b\",\"attributes\":{\"mantissas\":[2,5,10],\"tags\":[],\"id\":\"7a453349-a8f2-4881-c8ef-244b8fa4df3b\",\"doc\":null}},{\"type\":\"LinearAxis\",\"id\":\"aca1d5d0-c507-479e-c7c6-4813e12c2d5c\",\"attributes\":{\"ticker\":{\"type\":\"BasicTicker\",\"id\":\"7a453349-a8f2-4881-c8ef-244b8fa4df3b\"},\"id\":\"aca1d5d0-c507-479e-c7c6-4813e12c2d5c\",\"formatter\":{\"type\":\"BasicTickFormatter\",\"id\":\"ab07c199-2324-42c7-c2bc-2faa501be31a\"},\"tags\":[],\"doc\":null,\"plot\":{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"subtype\":\"Chart\"}}},{\"type\":\"Grid\",\"id\":\"7160a519-53de-4b1a-cbbf-665d64459911\",\"attributes\":{\"ticker\":{\"type\":\"BasicTicker\",\"id\":\"7a453349-a8f2-4881-c8ef-244b8fa4df3b\"},\"id\":\"7160a519-53de-4b1a-cbbf-665d64459911\",\"dimension\":0,\"tags\":[],\"doc\":null,\"plot\":{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"subtype\":\"Chart\"}}},{\"type\":\"BasicTickFormatter\",\"id\":\"e66ee1ac-7578-4a39-ca93-4d20bf7a7c31\",\"attributes\":{\"tags\":[],\"id\":\"e66ee1ac-7578-4a39-ca93-4d20bf7a7c31\",\"doc\":null}},{\"type\":\"BasicTicker\",\"id\":\"db224b2f-00e6-49cf-c9b0-2c0bf5c61cdc\",\"attributes\":{\"mantissas\":[2,5,10],\"tags\":[],\"id\":\"db224b2f-00e6-49cf-c9b0-2c0bf5c61cdc\",\"doc\":null}},{\"type\":\"LinearAxis\",\"id\":\"35fe89aa-77d4-44b6-c431-d627ac525888\",\"attributes\":{\"ticker\":{\"type\":\"BasicTicker\",\"id\":\"db224b2f-00e6-49cf-c9b0-2c0bf5c61cdc\"},\"id\":\"35fe89aa-77d4-44b6-c431-d627ac525888\",\"formatter\":{\"type\":\"BasicTickFormatter\",\"id\":\"e66ee1ac-7578-4a39-ca93-4d20bf7a7c31\"},\"tags\":[],\"doc\":null,\"plot\":{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"subtype\":\"Chart\"}}},{\"type\":\"Grid\",\"id\":\"75a3b775-8a01-4431-c471-3ca73859836d\",\"attributes\":{\"ticker\":{\"type\":\"BasicTicker\",\"id\":\"db224b2f-00e6-49cf-c9b0-2c0bf5c61cdc\"},\"id\":\"75a3b775-8a01-4431-c471-3ca73859836d\",\"dimension\":1,\"tags\":[],\"doc\":null,\"plot\":{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"subtype\":\"Chart\"}}},{\"type\":\"ColumnDataSource\",\"id\":\"570c18cf-e1fc-4548-c55b-70daaec96223\",\"attributes\":{\"callback\":null,\"data\":[],\"id\":\"570c18cf-e1fc-4548-c55b-70daaec96223\",\"selected\":{\"0d\":{\"flag\":false,\"indices\":[]},\"1d\":{\"indices\":[]},\"2d\":{\"indices\":[]}},\"column_names\":[],\"tags\":[],\"doc\":null}},{\"type\":\"Rect\",\"id\":\"6d1a99f7-1bde-48cd-c865-7488bef8f516\",\"attributes\":{\"height\":{\"units\":\"data\",\"value\":0.2},\"id\":\"6d1a99f7-1bde-48cd-c865-7488bef8f516\",\"width\":{\"units\":\"data\",\"value\":2},\"x\":{\"value\":0},\"y\":{\"value\":0.1},\"fill_color\":{\"value\":\"#ff0000\"},\"tags\":[],\"fill_alpha\":{\"value\":0.3},\"doc\":null}},{\"type\":\"GlyphRenderer\",\"id\":\"050de5e6-0a6b-4f38-cf9f-127668759ad5\",\"attributes\":{\"nonselection_glyph\":null,\"glyph\":{\"type\":\"Rect\",\"id\":\"6d1a99f7-1bde-48cd-c865-7488bef8f516\"},\"id\":\"050de5e6-0a6b-4f38-cf9f-127668759ad5\",\"selection_glyph\":null,\"data_source\":{\"type\":\"ColumnDataSource\",\"id\":\"570c18cf-e1fc-4548-c55b-70daaec96223\"},\"doc\":null,\"tags\":[]}},{\"type\":\"ColumnDataSource\",\"id\":\"9033cdac-12f6-4962-c9ee-ea1ae6e031eb\",\"attributes\":{\"callback\":null,\"data\":{\"height\":[3.9177151106573e-12,9.0140239022984e-12,2.0449048704352e-11,4.5739547566148e-11,1.0087341378862e-10,2.1934536131862e-10,4.7027071126138e-10,9.9410391030119e-10,2.0719614912679e-09,4.2579300085777e-09,8.6274782873375e-09,1.7235853633224e-08,3.3950691147311e-08,6.5937328486143e-08,1.26264282585e-07,2.3839400853376e-07,4.4378870711625e-07,8.1456255429657e-07,1.4741412996955e-06,2.6303830509278e-06,4.6277018554974e-06,8.0274767242372e-06,1.3729629245063e-05,2.3152837457019e-05,3.8496076740557e-05,6.3109509937931e-05,0.00010200936958427,0.0001625737932045,0.00025546317920089,0.00039579646545462,0.00060461956309155,0.00091066444292665,0.001352388295345,0.001980205764994,0.0028588138520718,0.0040693716146052,0.005711309146136,0.0079033244401217,0.010783275589347,0.014506331644952,0.019241157919168,0.025163479149342,0.032447185367346,0.04125240072608,0.051711603999138,0.063913553953171,0.077886909246445,0.093584075570107,0.1108680292964,0.12950205802917,0.14914655685425,0.16936209797859,0.18962070345879,0.20932511985302,0.22783651947975,0.24450719356537,0.25871786475182,0.26991522312164,0.27764809131622,0.28159722685814,0.28159722685814,0.27764809131622,0.26991522312164,0.25871786475182,0.24450719356537,0.22783651947975,0.20932511985302,0.18962070345879,0.16936209797859,0.14914655685425,0.12950205802917,0.1108680292964,0.093584075570107,0.077886909246445,0.063913598656654,0.051711566746235,0.04125240072608,0.032447185367346,0.025163503363729,0.019241139292717,0.014506331644952,0.010783275589347,0.0079033328220248,0.0057113035582006,0.0040693716146052,0.0028588138520718,0.0019802076276392,0.0013523870147765,0.00091066444292665,0.00060461956309155,0.0003957970184274,0.00025546291726641,0.0001625737932045,0.00010200936958427,6.3109575421549e-05,3.8496040360769e-05,2.3152770154411e-05,1.3729629245063e-05,8.027462172322e-06,4.6277195906441e-06,2.6303830509278e-06,1.4741356153536e-06,8.1456408906888e-07,4.4378870711625e-07,2.3839309903906e-07,1.26264282585e-07,6.5937200588451e-08,3.3950819045003e-08,1.7235853633224e-08,8.6274454247359e-09,4.2579459957892e-09,2.0719614912679e-09,9.9410013554291e-10,4.7027071126138e-10,2.1934536131862e-10,1.0087380236667e-10,4.5739547566148e-11,2.0448970641795e-11,9.0140759440027e-12,3.9177151106573e-12],\"x\":[-10,-9.8319330215454,-9.6638650894165,-9.4957981109619,-9.3277311325073,-9.1596641540527,-8.9915962219238,-8.8235292434692,-8.6554622650146,-8.4873952865601,-8.3193273544312,-8.1512603759766,-7.983193397522,-7.8151259422302,-7.6470584869385,-7.4789915084839,-7.3109245300293,-7.1428570747375,-6.9747896194458,-6.8067226409912,-6.6386556625366,-6.4705882072449,-6.3025207519531,-6.1344537734985,-5.9663863182068,-5.7983193397522,-5.6302518844604,-5.4621849060059,-5.2941174507141,-5.1260504722595,-4.9579830169678,-4.7899160385132,-4.6218485832214,-4.4537816047668,-4.2857141494751,-4.1176471710205,-3.9495797157288,-3.7815127372742,-3.6134452819824,-3.4453783035278,-3.2773108482361,-3.1092438697815,-2.9411764144897,-2.7731094360352,-2.6050419807434,-2.4369750022888,-2.2689075469971,-2.1008405685425,-1.9327726364136,-1.764705657959,-1.5966386795044,-1.4285717010498,-1.2605037689209,-1.0924367904663,-0.92436981201172,-0.75630283355713,-0.58823490142822,-0.42016792297363,-0.25210094451904,-0.084033966064453,0.084033966064453,0.25210094451904,0.42016792297363,0.58823490142822,0.75630283355713,0.92436981201172,1.0924367904663,1.2605037689209,1.4285717010498,1.5966386795044,1.764705657959,1.9327726364136,2.1008405685425,2.2689075469971,2.4369745254517,2.6050424575806,2.7731094360352,2.9411764144897,3.1092433929443,3.2773113250732,3.4453783035278,3.6134452819824,3.781512260437,3.9495801925659,4.1176471710205,4.2857141494751,4.4537811279297,4.6218490600586,4.7899160385132,4.9579830169678,5.1260499954224,5.2941179275513,5.4621849060059,5.6302518844604,5.798318862915,5.9663867950439,6.1344547271729,6.3025207519531,6.470588684082,6.6386547088623,6.8067226409912,6.9747905731201,7.1428565979004,7.3109245300293,7.4789924621582,7.6470584869385,7.8151264190674,7.9831924438477,8.1512603759766,8.3193283081055,8.4873943328857,8.6554622650146,8.8235301971436,8.9915962219238,9.1596641540527,9.327730178833,9.4957981109619,9.6638660430908,9.8319320678711,10],\"y\":[1.9588575553287e-12,4.5070119511492e-12,1.0224524352176e-11,2.2869773783074e-11,5.0436706894308e-11,1.0967268065931e-10,2.3513535563069e-10,4.970519551506e-10,1.0359807456339e-09,2.1289650042888e-09,4.3137391436687e-09,8.617926816612e-09,1.6975345573655e-08,3.2968664243072e-08,6.3132141292499e-08,1.1919700426688e-07,2.2189435355813e-07,4.0728127714829e-07,7.3707064984774e-07,1.3151915254639e-06,2.3138509277487e-06,4.0137383621186e-06,6.8648146225314e-06,1.1576418728509e-05,1.9248038370279e-05,3.1554754968965e-05,5.1004684792133e-05,8.1286896602251e-05,0.00012773158960044,0.00019789823272731,0.00030230978154577,0.00045533222146332,0.0006761941476725,0.00099010288249701,0.0014294069260359,0.0020346858073026,0.002855654573068,0.0039516622200608,0.0053916377946734,0.0072531658224761,0.0096205789595842,0.012581739574671,0.016223592683673,0.02062620036304,0.025855801999569,0.031956776976585,0.038943454623222,0.046792037785053,0.055434014648199,0.064751029014587,0.074573278427124,0.084681048989296,0.094810351729393,0.10466255992651,0.11391825973988,0.12225359678268,0.12935893237591,0.13495761156082,0.13882404565811,0.14079861342907,0.14079861342907,0.13882404565811,0.13495761156082,0.12935893237591,0.12225359678268,0.11391825973988,0.10466255992651,0.094810351729393,0.084681048989296,0.074573278427124,0.064751029014587,0.055434014648199,0.046792037785053,0.038943454623222,0.031956799328327,0.025855783373117,0.02062620036304,0.016223592683673,0.012581751681864,0.0096205696463585,0.0072531658224761,0.0053916377946734,0.0039516664110124,0.0028556517791003,0.0020346858073026,0.0014294069260359,0.00099010381381959,0.00067619350738823,0.00045533222146332,0.00030230978154577,0.0001978985092137,0.00012773145863321,8.1286896602251e-05,5.1004684792133e-05,3.1554787710775e-05,1.9248020180385e-05,1.1576385077205e-05,6.8648146225314e-06,4.013731086161e-06,2.3138597953221e-06,1.3151915254639e-06,7.3706780767679e-07,4.0728204453444e-07,2.2189435355813e-07,1.1919654951953e-07,6.3132141292499e-08,3.2968600294225e-08,1.6975409522502e-08,8.617926816612e-09,4.313722712368e-09,2.1289729978946e-09,1.0359807456339e-09,4.9705006777145e-10,2.3513535563069e-10,1.0967268065931e-10,5.0436901183337e-11,2.2869773783074e-11,1.0224485320898e-11,4.5070379720014e-12,1.9588575553287e-12]},\"id\":\"9033cdac-12f6-4962-c9ee-ea1ae6e031eb\",\"selected\":{\"0d\":{\"flag\":false,\"indices\":[]},\"1d\":{\"indices\":[]},\"2d\":{\"indices\":[]}},\"column_names\":[\"height\",\"x\",\"y\"],\"tags\":[],\"doc\":null}},{\"type\":\"Rect\",\"id\":\"ed16d2f7-8201-4049-c043-bf09b859df48\",\"attributes\":{\"height\":{\"units\":\"data\",\"field\":\"height\"},\"id\":\"ed16d2f7-8201-4049-c043-bf09b859df48\",\"width\":{\"units\":\"data\",\"value\":0.01},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"},\"fill_color\":{\"value\":\"#ff0000\"},\"tags\":[],\"fill_alpha\":{\"value\":0.3},\"doc\":null}},{\"type\":\"GlyphRenderer\",\"id\":\"8dacf59f-a36e-42fe-c2ec-1943cd4a2fba\",\"attributes\":{\"nonselection_glyph\":null,\"glyph\":{\"type\":\"Rect\",\"id\":\"ed16d2f7-8201-4049-c043-bf09b859df48\"},\"id\":\"8dacf59f-a36e-42fe-c2ec-1943cd4a2fba\",\"selection_glyph\":null,\"data_source\":{\"type\":\"ColumnDataSource\",\"id\":\"9033cdac-12f6-4962-c9ee-ea1ae6e031eb\"},\"doc\":null,\"tags\":[]}},{\"type\":\"ColumnDataSource\",\"id\":\"6101b2e3-02e2-4da3-cdec-acde468c27d4\",\"attributes\":{\"callback\":null,\"data\":{\"y\":[3.9177151106573e-12,9.0140239022984e-12,2.0449048704352e-11,4.5739547566148e-11,1.0087341378862e-10,2.1934536131862e-10,4.7027071126138e-10,9.9410391030119e-10,2.0719614912679e-09,4.2579300085777e-09,8.6274782873375e-09,1.7235853633224e-08,3.3950691147311e-08,6.5937328486143e-08,1.26264282585e-07,2.3839400853376e-07,4.4378870711625e-07,8.1456255429657e-07,1.4741412996955e-06,2.6303830509278e-06,4.6277018554974e-06,8.0274767242372e-06,1.3729629245063e-05,2.3152837457019e-05,3.8496076740557e-05,6.3109509937931e-05,0.00010200936958427,0.0001625737932045,0.00025546317920089,0.00039579646545462,0.00060461956309155,0.00091066444292665,0.001352388295345,0.001980205764994,0.0028588138520718,0.0040693716146052,0.005711309146136,0.0079033244401217,0.010783275589347,0.014506331644952,0.019241157919168,0.025163479149342,0.032447185367346,0.04125240072608,0.051711603999138,0.063913553953171,0.077886909246445,0.093584075570107,0.1108680292964,0.12950205802917,0.14914655685425,0.16936209797859,0.18962070345879,0.20932511985302,0.22783651947975,0.24450719356537,0.25871786475182,0.26991522312164,0.27764809131622,0.28159722685814,0.28159722685814,0.27764809131622,0.26991522312164,0.25871786475182,0.24450719356537,0.22783651947975,0.20932511985302,0.18962070345879,0.16936209797859,0.14914655685425,0.12950205802917,0.1108680292964,0.093584075570107,0.077886909246445,0.063913598656654,0.051711566746235,0.04125240072608,0.032447185367346,0.025163503363729,0.019241139292717,0.014506331644952,0.010783275589347,0.0079033328220248,0.0057113035582006,0.0040693716146052,0.0028588138520718,0.0019802076276392,0.0013523870147765,0.00091066444292665,0.00060461956309155,0.0003957970184274,0.00025546291726641,0.0001625737932045,0.00010200936958427,6.3109575421549e-05,3.8496040360769e-05,2.3152770154411e-05,1.3729629245063e-05,8.027462172322e-06,4.6277195906441e-06,2.6303830509278e-06,1.4741356153536e-06,8.1456408906888e-07,4.4378870711625e-07,2.3839309903906e-07,1.26264282585e-07,6.5937200588451e-08,3.3950819045003e-08,1.7235853633224e-08,8.6274454247359e-09,4.2579459957892e-09,2.0719614912679e-09,9.9410013554291e-10,4.7027071126138e-10,2.1934536131862e-10,1.0087380236667e-10,4.5739547566148e-11,2.0448970641795e-11,9.0140759440027e-12,3.9177151106573e-12],\"x\":[-10,-9.8319330215454,-9.6638650894165,-9.4957981109619,-9.3277311325073,-9.1596641540527,-8.9915962219238,-8.8235292434692,-8.6554622650146,-8.4873952865601,-8.3193273544312,-8.1512603759766,-7.983193397522,-7.8151259422302,-7.6470584869385,-7.4789915084839,-7.3109245300293,-7.1428570747375,-6.9747896194458,-6.8067226409912,-6.6386556625366,-6.4705882072449,-6.3025207519531,-6.1344537734985,-5.9663863182068,-5.7983193397522,-5.6302518844604,-5.4621849060059,-5.2941174507141,-5.1260504722595,-4.9579830169678,-4.7899160385132,-4.6218485832214,-4.4537816047668,-4.2857141494751,-4.1176471710205,-3.9495797157288,-3.7815127372742,-3.6134452819824,-3.4453783035278,-3.2773108482361,-3.1092438697815,-2.9411764144897,-2.7731094360352,-2.6050419807434,-2.4369750022888,-2.2689075469971,-2.1008405685425,-1.9327726364136,-1.764705657959,-1.5966386795044,-1.4285717010498,-1.2605037689209,-1.0924367904663,-0.92436981201172,-0.75630283355713,-0.58823490142822,-0.42016792297363,-0.25210094451904,-0.084033966064453,0.084033966064453,0.25210094451904,0.42016792297363,0.58823490142822,0.75630283355713,0.92436981201172,1.0924367904663,1.2605037689209,1.4285717010498,1.5966386795044,1.764705657959,1.9327726364136,2.1008405685425,2.2689075469971,2.4369745254517,2.6050424575806,2.7731094360352,2.9411764144897,3.1092433929443,3.2773113250732,3.4453783035278,3.6134452819824,3.781512260437,3.9495801925659,4.1176471710205,4.2857141494751,4.4537811279297,4.6218490600586,4.7899160385132,4.9579830169678,5.1260499954224,5.2941179275513,5.4621849060059,5.6302518844604,5.798318862915,5.9663867950439,6.1344547271729,6.3025207519531,6.470588684082,6.6386547088623,6.8067226409912,6.9747905731201,7.1428565979004,7.3109245300293,7.4789924621582,7.6470584869385,7.8151264190674,7.9831924438477,8.1512603759766,8.3193283081055,8.4873943328857,8.6554622650146,8.8235301971436,8.9915962219238,9.1596641540527,9.327730178833,9.4957981109619,9.6638660430908,9.8319320678711,10]},\"id\":\"6101b2e3-02e2-4da3-cdec-acde468c27d4\",\"selected\":{\"0d\":{\"flag\":false,\"indices\":[]},\"1d\":{\"indices\":[]},\"2d\":{\"indices\":[]}},\"column_names\":[\"y\",\"x\"],\"tags\":[],\"doc\":null}},{\"type\":\"Line\",\"id\":\"391c73dc-8b76-4be8-cbf4-2c303f5ceaa0\",\"attributes\":{\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"},\"doc\":null,\"id\":\"391c73dc-8b76-4be8-cbf4-2c303f5ceaa0\",\"tags\":[]}},{\"type\":\"GlyphRenderer\",\"id\":\"5d9d8460-80b1-44a6-c4b1-85e53dacb976\",\"attributes\":{\"nonselection_glyph\":null,\"glyph\":{\"type\":\"Line\",\"id\":\"391c73dc-8b76-4be8-cbf4-2c303f5ceaa0\"},\"id\":\"5d9d8460-80b1-44a6-c4b1-85e53dacb976\",\"selection_glyph\":null,\"data_source\":{\"type\":\"ColumnDataSource\",\"id\":\"6101b2e3-02e2-4da3-cdec-acde468c27d4\"},\"doc\":null,\"tags\":[]}},{\"type\":\"Legend\",\"id\":\"c92d5355-a32e-4e06-ce6a-3662c7210325\",\"attributes\":{\"id\":\"c92d5355-a32e-4e06-ce6a-3662c7210325\",\"legends\":[[\"Normal\",[{\"type\":\"GlyphRenderer\",\"id\":\"5d9d8460-80b1-44a6-c4b1-85e53dacb976\"}]]],\"doc\":null,\"tags\":[],\"plot\":{\"type\":\"Plot\",\"id\":\"b45536c2-42ae-44d8-c499-52aa86d94041\",\"subtype\":\"Chart\"}}},{\"type\":\"ColumnDataSource\",\"id\":\"be87863f-398a-45d8-c56a-bd7a1776f0e1\",\"attributes\":{\"callback\":null,\"data\":{\"y\":[3.9177151106573e-12,9.0140239022984e-12,2.0449048704352e-11,4.5739547566148e-11,1.0087341378862e-10,2.1934536131862e-10,4.7027071126138e-10,9.9410391030119e-10,2.0719614912679e-09,4.2579300085777e-09,8.6274782873375e-09,1.7235853633224e-08,3.3950691147311e-08,6.5937328486143e-08,1.26264282585e-07,2.3839400853376e-07,4.4378870711625e-07,8.1456255429657e-07,1.4741412996955e-06,2.6303830509278e-06,4.6277018554974e-06,8.0274767242372e-06,1.3729629245063e-05,2.3152837457019e-05,3.8496076740557e-05,6.3109509937931e-05,0.00010200936958427,0.0001625737932045,0.00025546317920089,0.00039579646545462,0.00060461956309155,0.00091066444292665,0.001352388295345,0.001980205764994,0.0028588138520718,0.0040693716146052,0.005711309146136,0.0079033244401217,0.010783275589347,0.014506331644952,0.019241157919168,0.025163479149342,0.032447185367346,0.04125240072608,0.051711603999138,0.063913553953171,0.077886909246445,0.093584075570107,0.1108680292964,0.12950205802917,0.14914655685425,0.16936209797859,0.18962070345879,0.20932511985302,0.22783651947975,0.24450719356537,0.25871786475182,0.26991522312164,0.27764809131622,0.28159722685814,0.28159722685814,0.27764809131622,0.26991522312164,0.25871786475182,0.24450719356537,0.22783651947975,0.20932511985302,0.18962070345879,0.16936209797859,0.14914655685425,0.12950205802917,0.1108680292964,0.093584075570107,0.077886909246445,0.063913598656654,0.051711566746235,0.04125240072608,0.032447185367346,0.025163503363729,0.019241139292717,0.014506331644952,0.010783275589347,0.0079033328220248,0.0057113035582006,0.0040693716146052,0.0028588138520718,0.0019802076276392,0.0013523870147765,0.00091066444292665,0.00060461956309155,0.0003957970184274,0.00025546291726641,0.0001625737932045,0.00010200936958427,6.3109575421549e-05,3.8496040360769e-05,2.3152770154411e-05,1.3729629245063e-05,8.027462172322e-06,4.6277195906441e-06,2.6303830509278e-06,1.4741356153536e-06,8.1456408906888e-07,4.4378870711625e-07,2.3839309903906e-07,1.26264282585e-07,6.5937200588451e-08,3.3950819045003e-08,1.7235853633224e-08,8.6274454247359e-09,4.2579459957892e-09,2.0719614912679e-09,9.9410013554291e-10,4.7027071126138e-10,2.1934536131862e-10,1.0087380236667e-10,4.5739547566148e-11,2.0448970641795e-11,9.0140759440027e-12,3.9177151106573e-12],\"x\":[-10,-9.8319330215454,-9.6638650894165,-9.4957981109619,-9.3277311325073,-9.1596641540527,-8.9915962219238,-8.8235292434692,-8.6554622650146,-8.4873952865601,-8.3193273544312,-8.1512603759766,-7.983193397522,-7.8151259422302,-7.6470584869385,-7.4789915084839,-7.3109245300293,-7.1428570747375,-6.9747896194458,-6.8067226409912,-6.6386556625366,-6.4705882072449,-6.3025207519531,-6.1344537734985,-5.9663863182068,-5.7983193397522,-5.6302518844604,-5.4621849060059,-5.2941174507141,-5.1260504722595,-4.9579830169678,-4.7899160385132,-4.6218485832214,-4.4537816047668,-4.2857141494751,-4.1176471710205,-3.9495797157288,-3.7815127372742,-3.6134452819824,-3.4453783035278,-3.2773108482361,-3.1092438697815,-2.9411764144897,-2.7731094360352,-2.6050419807434,-2.4369750022888,-2.2689075469971,-2.1008405685425,-1.9327726364136,-1.764705657959,-1.5966386795044,-1.4285717010498,-1.2605037689209,-1.0924367904663,-0.92436981201172,-0.75630283355713,-0.58823490142822,-0.42016792297363,-0.25210094451904,-0.084033966064453,0.084033966064453,0.25210094451904,0.42016792297363,0.58823490142822,0.75630283355713,0.92436981201172,1.0924367904663,1.2605037689209,1.4285717010498,1.5966386795044,1.764705657959,1.9327726364136,2.1008405685425,2.2689075469971,2.4369745254517,2.6050424575806,2.7731094360352,2.9411764144897,3.1092433929443,3.2773113250732,3.4453783035278,3.6134452819824,3.781512260437,3.9495801925659,4.1176471710205,4.2857141494751,4.4537811279297,4.6218490600586,4.7899160385132,4.9579830169678,5.1260499954224,5.2941179275513,5.4621849060059,5.6302518844604,5.798318862915,5.9663867950439,6.1344547271729,6.3025207519531,6.470588684082,6.6386547088623,6.8067226409912,6.9747905731201,7.1428565979004,7.3109245300293,7.4789924621582,7.6470584869385,7.8151264190674,7.9831924438477,8.1512603759766,8.3193283081055,8.4873943328857,8.6554622650146,8.8235301971436,8.9915962219238,9.1596641540527,9.327730178833,9.4957981109619,9.6638660430908,9.8319320678711,10]},\"id\":\"be87863f-398a-45d8-c56a-bd7a1776f0e1\",\"selected\":{\"0d\":{\"flag\":false,\"indices\":[]},\"1d\":{\"indices\":[]},\"2d\":{\"indices\":[]}},\"column_names\":[\"x\",\"y\"],\"tags\":[],\"doc\":null}},{\"type\":\"Circle\",\"id\":\"a3443647-7275-4d96-cd83-f8a7a4fccc63\",\"attributes\":{\"id\":\"a3443647-7275-4d96-cd83-f8a7a4fccc63\",\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"},\"fill_color\":{\"value\":\"#ff0000\"},\"tags\":[],\"doc\":null,\"fill_alpha\":{\"value\":0.8},\"size\":{\"value\":6}}},{\"type\":\"GlyphRenderer\",\"id\":\"8352a2bd-dd88-4519-c552-930bc984ec6d\",\"attributes\":{\"nonselection_glyph\":null,\"glyph\":{\"type\":\"Circle\",\"id\":\"a3443647-7275-4d96-cd83-f8a7a4fccc63\"},\"id\":\"8352a2bd-dd88-4519-c552-930bc984ec6d\",\"selection_glyph\":null,\"data_source\":{\"type\":\"ColumnDataSource\",\"id\":\"be87863f-398a-45d8-c56a-bd7a1776f0e1\"},\"doc\":null,\"tags\":[]}},{\"type\":\"Range1d\",\"id\":\"3f31081c-ba9d-46ad-c6c9-b8ba4ea52717\",\"attributes\":{\"callback\":null,\"id\":\"3f31081c-ba9d-46ad-c6c9-b8ba4ea52717\",\"start\":-11.0055,\"end\":11.0055,\"tags\":[],\"doc\":null}},{\"type\":\"Range1d\",\"id\":\"c8dc5261-de24-4a63-cafb-8eb5e81d0027\",\"attributes\":{\"callback\":null,\"id\":\"c8dc5261-de24-4a63-cafb-8eb5e81d0027\",\"start\":-0.014079861342907,\"end\":0.29567708820105,\"tags\":[],\"doc\":null}}];\n", + " var all_models = [{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"attributes\":{\"plot_width\":600,\"tool_events\":{\"id\":\"5e66c576-6502-4ff6-cf0c-ef6d971172a7\",\"type\":\"ToolEvents\"},\"y_range\":{\"id\":\"b6f10976-3f8d-499b-c9fa-e7c7d460bb5e\",\"type\":\"Range1d\"},\"extra_y_ranges\":[],\"x_range\":{\"id\":\"8a353caa-5def-4bf2-cb6d-6ca017514ca8\",\"type\":\"Range1d\"},\"below\":[{\"id\":\"51e271a7-53be-45eb-c5f7-7e484c1a4959\",\"type\":\"LinearAxis\"}],\"above\":[],\"plot_height\":500,\"title_text_font_size\":{\"value\":\"12pt\"},\"responsive\":false,\"left\":[{\"id\":\"23d12fe9-b07e-4887-c830-ab334229a223\",\"type\":\"LinearAxis\"}],\"title_text_font_style\":\"bold\",\"extra_x_ranges\":[],\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"tags\":[],\"right\":[],\"tools\":[{\"id\":\"e19ed225-c881-43ad-c30c-3ed9a8324b06\",\"type\":\"PanTool\"},{\"id\":\"98107dfe-121c-4540-c5e4-aebff62066d7\",\"type\":\"WheelZoomTool\"},{\"id\":\"be39fd87-bb30-44f7-c473-d0e5a51beb3d\",\"type\":\"BoxZoomTool\"},{\"id\":\"2c683c3f-8531-40ad-c02e-6d0c4ed3e30d\",\"type\":\"ResizeTool\"},{\"id\":\"0de094c8-10c9-4f4d-cf90-32e2ac1e1fd8\",\"type\":\"ResetTool\"},{\"id\":\"875b180c-8d98-49a3-c927-af15fa922208\",\"type\":\"PreviewSaveTool\"},{\"id\":\"73b7d084-8190-41be-c104-a0cd23c0a6aa\",\"type\":\"HoverTool\"}],\"renderers\":[{\"id\":\"86cfe783-2733-4283-c28e-03cc212d9572\",\"type\":\"Grid\"},{\"id\":\"51e271a7-53be-45eb-c5f7-7e484c1a4959\",\"type\":\"LinearAxis\"},{\"id\":\"8e942c28-29b8-414c-c168-91038447d0a8\",\"type\":\"Grid\"},{\"id\":\"23d12fe9-b07e-4887-c830-ab334229a223\",\"type\":\"LinearAxis\"},{\"id\":\"91e46610-deb8-4b08-cbda-75da7b058fb1\",\"type\":\"GlyphRenderer\"},{\"id\":\"c5d790f0-9383-428b-c240-087a91224fc7\",\"type\":\"GlyphRenderer\"},{\"id\":\"c3590fb9-52d8-49dd-c9f0-ff565f7a06a6\",\"type\":\"GlyphRenderer\"},{\"id\":\"85a78014-8ae6-4aae-ca19-64e4a9e2186c\",\"type\":\"Legend\"},{\"id\":\"47b0bc47-2028-4381-c374-41793ca9e5ab\",\"type\":\"GlyphRenderer\"}]},\"type\":\"Plot\"},{\"id\":\"5e66c576-6502-4ff6-cf0c-ef6d971172a7\",\"attributes\":{\"geometries\":[],\"id\":\"5e66c576-6502-4ff6-cf0c-ef6d971172a7\",\"doc\":null,\"tags\":[]},\"type\":\"ToolEvents\"},{\"id\":\"e19ed225-c881-43ad-c30c-3ed9a8324b06\",\"attributes\":{\"id\":\"e19ed225-c881-43ad-c30c-3ed9a8324b06\",\"dimensions\":[\"width\",\"height\"],\"doc\":null,\"plot\":{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"type\":\"Plot\",\"subtype\":\"Chart\"},\"tags\":[]},\"type\":\"PanTool\"},{\"id\":\"98107dfe-121c-4540-c5e4-aebff62066d7\",\"attributes\":{\"id\":\"98107dfe-121c-4540-c5e4-aebff62066d7\",\"dimensions\":[\"width\",\"height\"],\"doc\":null,\"plot\":{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"type\":\"Plot\",\"subtype\":\"Chart\"},\"tags\":[]},\"type\":\"WheelZoomTool\"},{\"id\":\"be39fd87-bb30-44f7-c473-d0e5a51beb3d\",\"attributes\":{\"id\":\"be39fd87-bb30-44f7-c473-d0e5a51beb3d\",\"dimensions\":[\"width\",\"height\"],\"doc\":null,\"plot\":{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"type\":\"Plot\",\"subtype\":\"Chart\"},\"tags\":[]},\"type\":\"BoxZoomTool\"},{\"id\":\"2c683c3f-8531-40ad-c02e-6d0c4ed3e30d\",\"attributes\":{\"doc\":null,\"id\":\"2c683c3f-8531-40ad-c02e-6d0c4ed3e30d\",\"plot\":{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"type\":\"Plot\",\"subtype\":\"Chart\"},\"tags\":[]},\"type\":\"ResizeTool\"},{\"id\":\"0de094c8-10c9-4f4d-cf90-32e2ac1e1fd8\",\"attributes\":{\"doc\":null,\"id\":\"0de094c8-10c9-4f4d-cf90-32e2ac1e1fd8\",\"plot\":{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"type\":\"Plot\",\"subtype\":\"Chart\"},\"tags\":[]},\"type\":\"ResetTool\"},{\"id\":\"875b180c-8d98-49a3-c927-af15fa922208\",\"attributes\":{\"doc\":null,\"id\":\"875b180c-8d98-49a3-c927-af15fa922208\",\"plot\":{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"type\":\"Plot\",\"subtype\":\"Chart\"},\"tags\":[]},\"type\":\"PreviewSaveTool\"},{\"id\":\"73b7d084-8190-41be-c104-a0cd23c0a6aa\",\"attributes\":{\"tooltips\":[[\"Tag\",\"@hover\"],[\"(x,y)\",\"($x, $y)\"]],\"always_active\":true,\"doc\":null,\"point_policy\":\"follow_mouse\",\"id\":\"73b7d084-8190-41be-c104-a0cd23c0a6aa\",\"renderers\":[],\"tags\":[],\"name\":null,\"plot\":{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"type\":\"Plot\",\"subtype\":\"Chart\"},\"names\":[],\"callback\":null},\"type\":\"HoverTool\"},{\"id\":\"1bbeb7a9-5671-4c1d-ccfb-32938e559b01\",\"attributes\":{\"doc\":null,\"id\":\"1bbeb7a9-5671-4c1d-ccfb-32938e559b01\",\"tags\":[]},\"type\":\"BasicTickFormatter\"},{\"id\":\"0c6c858e-fd57-4c1e-ccec-ec7fac922ac8\",\"attributes\":{\"mantissas\":[2,5,10],\"id\":\"0c6c858e-fd57-4c1e-ccec-ec7fac922ac8\",\"tags\":[],\"doc\":null,\"num_minor_ticks\":5},\"type\":\"BasicTicker\"},{\"id\":\"51e271a7-53be-45eb-c5f7-7e484c1a4959\",\"attributes\":{\"id\":\"51e271a7-53be-45eb-c5f7-7e484c1a4959\",\"ticker\":{\"id\":\"0c6c858e-fd57-4c1e-ccec-ec7fac922ac8\",\"type\":\"BasicTicker\"},\"tags\":[],\"doc\":null,\"formatter\":{\"id\":\"1bbeb7a9-5671-4c1d-ccfb-32938e559b01\",\"type\":\"BasicTickFormatter\"},\"plot\":{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"type\":\"Plot\",\"subtype\":\"Chart\"}},\"type\":\"LinearAxis\"},{\"id\":\"86cfe783-2733-4283-c28e-03cc212d9572\",\"attributes\":{\"id\":\"86cfe783-2733-4283-c28e-03cc212d9572\",\"tags\":[],\"doc\":null,\"plot\":{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"type\":\"Plot\",\"subtype\":\"Chart\"},\"ticker\":{\"id\":\"0c6c858e-fd57-4c1e-ccec-ec7fac922ac8\",\"type\":\"BasicTicker\"},\"dimension\":0},\"type\":\"Grid\"},{\"id\":\"0f071a63-c5df-4e7f-cecd-c724e7117d6e\",\"attributes\":{\"doc\":null,\"id\":\"0f071a63-c5df-4e7f-cecd-c724e7117d6e\",\"tags\":[]},\"type\":\"BasicTickFormatter\"},{\"id\":\"e165f208-9994-4bb9-cb8e-8644bc1cb6cc\",\"attributes\":{\"mantissas\":[2,5,10],\"id\":\"e165f208-9994-4bb9-cb8e-8644bc1cb6cc\",\"tags\":[],\"doc\":null,\"num_minor_ticks\":5},\"type\":\"BasicTicker\"},{\"id\":\"23d12fe9-b07e-4887-c830-ab334229a223\",\"attributes\":{\"id\":\"23d12fe9-b07e-4887-c830-ab334229a223\",\"ticker\":{\"id\":\"e165f208-9994-4bb9-cb8e-8644bc1cb6cc\",\"type\":\"BasicTicker\"},\"tags\":[],\"doc\":null,\"formatter\":{\"id\":\"0f071a63-c5df-4e7f-cecd-c724e7117d6e\",\"type\":\"BasicTickFormatter\"},\"plot\":{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"type\":\"Plot\",\"subtype\":\"Chart\"}},\"type\":\"LinearAxis\"},{\"id\":\"8e942c28-29b8-414c-c168-91038447d0a8\",\"attributes\":{\"id\":\"8e942c28-29b8-414c-c168-91038447d0a8\",\"tags\":[],\"doc\":null,\"plot\":{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"type\":\"Plot\",\"subtype\":\"Chart\"},\"ticker\":{\"id\":\"e165f208-9994-4bb9-cb8e-8644bc1cb6cc\",\"type\":\"BasicTicker\"},\"dimension\":1},\"type\":\"Grid\"},{\"id\":\"180091c9-7ff9-41cb-c1fc-fe6c26a190b4\",\"attributes\":{\"column_names\":[],\"id\":\"180091c9-7ff9-41cb-c1fc-fe6c26a190b4\",\"tags\":[],\"doc\":null,\"data\":[],\"callback\":null,\"selected\":{\"2d\":{\"indices\":[]},\"1d\":{\"indices\":[]},\"0d\":{\"indices\":[],\"flag\":false}}},\"type\":\"ColumnDataSource\"},{\"id\":\"35bcdc5e-74be-4bbc-cb3c-c0ab8490539d\",\"attributes\":{\"fill_color\":{\"value\":\"#ff0000\"},\"doc\":null,\"y\":{\"value\":0.1},\"height\":{\"units\":\"data\",\"value\":0.2},\"x\":{\"value\":0},\"tags\":[],\"fill_alpha\":{\"value\":0.3},\"id\":\"35bcdc5e-74be-4bbc-cb3c-c0ab8490539d\",\"width\":{\"units\":\"data\",\"value\":2}},\"type\":\"Rect\"},{\"id\":\"91e46610-deb8-4b08-cbda-75da7b058fb1\",\"attributes\":{\"data_source\":{\"id\":\"180091c9-7ff9-41cb-c1fc-fe6c26a190b4\",\"type\":\"ColumnDataSource\"},\"id\":\"91e46610-deb8-4b08-cbda-75da7b058fb1\",\"tags\":[],\"nonselection_glyph\":null,\"glyph\":{\"id\":\"35bcdc5e-74be-4bbc-cb3c-c0ab8490539d\",\"type\":\"Rect\"},\"doc\":null,\"selection_glyph\":null},\"type\":\"GlyphRenderer\"},{\"id\":\"c26c1037-2bbb-4352-c3b3-fd7c435119d5\",\"attributes\":{\"column_names\":[\"height\",\"x\",\"y\"],\"id\":\"c26c1037-2bbb-4352-c3b3-fd7c435119d5\",\"tags\":[],\"doc\":null,\"data\":{\"height\":[3.9177151106573e-12,9.0140239022984e-12,2.0449048704352e-11,4.5739547566148e-11,1.0087341378862e-10,2.1934536131862e-10,4.7027071126138e-10,9.9410391030119e-10,2.0719614912679e-09,4.2579300085777e-09,8.6274782873375e-09,1.7235853633224e-08,3.3950691147311e-08,6.5937328486143e-08,1.26264282585e-07,2.3839400853376e-07,4.4378870711625e-07,8.1456255429657e-07,1.4741412996955e-06,2.6303830509278e-06,4.6277018554974e-06,8.0274767242372e-06,1.3729629245063e-05,2.3152837457019e-05,3.8496076740557e-05,6.3109509937931e-05,0.00010200936958427,0.0001625737932045,0.00025546317920089,0.00039579646545462,0.00060461956309155,0.00091066444292665,0.001352388295345,0.001980205764994,0.0028588138520718,0.0040693716146052,0.005711309146136,0.0079033244401217,0.010783275589347,0.014506331644952,0.019241157919168,0.025163479149342,0.032447185367346,0.04125240072608,0.051711603999138,0.063913553953171,0.077886909246445,0.093584075570107,0.1108680292964,0.12950205802917,0.14914655685425,0.16936209797859,0.18962070345879,0.20932511985302,0.22783651947975,0.24450719356537,0.25871786475182,0.26991522312164,0.27764809131622,0.28159722685814,0.28159722685814,0.27764809131622,0.26991522312164,0.25871786475182,0.24450719356537,0.22783651947975,0.20932511985302,0.18962070345879,0.16936209797859,0.14914655685425,0.12950205802917,0.1108680292964,0.093584075570107,0.077886909246445,0.063913598656654,0.051711566746235,0.04125240072608,0.032447185367346,0.025163503363729,0.019241139292717,0.014506331644952,0.010783275589347,0.0079033328220248,0.0057113035582006,0.0040693716146052,0.0028588138520718,0.0019802076276392,0.0013523870147765,0.00091066444292665,0.00060461956309155,0.0003957970184274,0.00025546291726641,0.0001625737932045,0.00010200936958427,6.3109575421549e-05,3.8496040360769e-05,2.3152770154411e-05,1.3729629245063e-05,8.027462172322e-06,4.6277195906441e-06,2.6303830509278e-06,1.4741356153536e-06,8.1456408906888e-07,4.4378870711625e-07,2.3839309903906e-07,1.26264282585e-07,6.5937200588451e-08,3.3950819045003e-08,1.7235853633224e-08,8.6274454247359e-09,4.2579459957892e-09,2.0719614912679e-09,9.9410013554291e-10,4.7027071126138e-10,2.1934536131862e-10,1.0087380236667e-10,4.5739547566148e-11,2.0448970641795e-11,9.0140759440027e-12,3.9177151106573e-12],\"x\":[-10,-9.8319330215454,-9.6638650894165,-9.4957981109619,-9.3277311325073,-9.1596641540527,-8.9915962219238,-8.8235292434692,-8.6554622650146,-8.4873952865601,-8.3193273544312,-8.1512603759766,-7.983193397522,-7.8151259422302,-7.6470584869385,-7.4789915084839,-7.3109245300293,-7.1428570747375,-6.9747896194458,-6.8067226409912,-6.6386556625366,-6.4705882072449,-6.3025207519531,-6.1344537734985,-5.9663863182068,-5.7983193397522,-5.6302518844604,-5.4621849060059,-5.2941174507141,-5.1260504722595,-4.9579830169678,-4.7899160385132,-4.6218485832214,-4.4537816047668,-4.2857141494751,-4.1176471710205,-3.9495797157288,-3.7815127372742,-3.6134452819824,-3.4453783035278,-3.2773108482361,-3.1092438697815,-2.9411764144897,-2.7731094360352,-2.6050419807434,-2.4369750022888,-2.2689075469971,-2.1008405685425,-1.9327726364136,-1.764705657959,-1.5966386795044,-1.4285717010498,-1.2605037689209,-1.0924367904663,-0.92436981201172,-0.75630283355713,-0.58823490142822,-0.42016792297363,-0.25210094451904,-0.084033966064453,0.084033966064453,0.25210094451904,0.42016792297363,0.58823490142822,0.75630283355713,0.92436981201172,1.0924367904663,1.2605037689209,1.4285717010498,1.5966386795044,1.764705657959,1.9327726364136,2.1008405685425,2.2689075469971,2.4369745254517,2.6050424575806,2.7731094360352,2.9411764144897,3.1092433929443,3.2773113250732,3.4453783035278,3.6134452819824,3.781512260437,3.9495801925659,4.1176471710205,4.2857141494751,4.4537811279297,4.6218490600586,4.7899160385132,4.9579830169678,5.1260499954224,5.2941179275513,5.4621849060059,5.6302518844604,5.798318862915,5.9663867950439,6.1344547271729,6.3025207519531,6.470588684082,6.6386547088623,6.8067226409912,6.9747905731201,7.1428565979004,7.3109245300293,7.4789924621582,7.6470584869385,7.8151264190674,7.9831924438477,8.1512603759766,8.3193283081055,8.4873943328857,8.6554622650146,8.8235301971436,8.9915962219238,9.1596641540527,9.327730178833,9.4957981109619,9.6638660430908,9.8319320678711,10],\"y\":[1.9588575553287e-12,4.5070119511492e-12,1.0224524352176e-11,2.2869773783074e-11,5.0436706894308e-11,1.0967268065931e-10,2.3513535563069e-10,4.970519551506e-10,1.0359807456339e-09,2.1289650042888e-09,4.3137391436687e-09,8.617926816612e-09,1.6975345573655e-08,3.2968664243072e-08,6.3132141292499e-08,1.1919700426688e-07,2.2189435355813e-07,4.0728127714829e-07,7.3707064984774e-07,1.3151915254639e-06,2.3138509277487e-06,4.0137383621186e-06,6.8648146225314e-06,1.1576418728509e-05,1.9248038370279e-05,3.1554754968965e-05,5.1004684792133e-05,8.1286896602251e-05,0.00012773158960044,0.00019789823272731,0.00030230978154577,0.00045533222146332,0.0006761941476725,0.00099010288249701,0.0014294069260359,0.0020346858073026,0.002855654573068,0.0039516622200608,0.0053916377946734,0.0072531658224761,0.0096205789595842,0.012581739574671,0.016223592683673,0.02062620036304,0.025855801999569,0.031956776976585,0.038943454623222,0.046792037785053,0.055434014648199,0.064751029014587,0.074573278427124,0.084681048989296,0.094810351729393,0.10466255992651,0.11391825973988,0.12225359678268,0.12935893237591,0.13495761156082,0.13882404565811,0.14079861342907,0.14079861342907,0.13882404565811,0.13495761156082,0.12935893237591,0.12225359678268,0.11391825973988,0.10466255992651,0.094810351729393,0.084681048989296,0.074573278427124,0.064751029014587,0.055434014648199,0.046792037785053,0.038943454623222,0.031956799328327,0.025855783373117,0.02062620036304,0.016223592683673,0.012581751681864,0.0096205696463585,0.0072531658224761,0.0053916377946734,0.0039516664110124,0.0028556517791003,0.0020346858073026,0.0014294069260359,0.00099010381381959,0.00067619350738823,0.00045533222146332,0.00030230978154577,0.0001978985092137,0.00012773145863321,8.1286896602251e-05,5.1004684792133e-05,3.1554787710775e-05,1.9248020180385e-05,1.1576385077205e-05,6.8648146225314e-06,4.013731086161e-06,2.3138597953221e-06,1.3151915254639e-06,7.3706780767679e-07,4.0728204453444e-07,2.2189435355813e-07,1.1919654951953e-07,6.3132141292499e-08,3.2968600294225e-08,1.6975409522502e-08,8.617926816612e-09,4.313722712368e-09,2.1289729978946e-09,1.0359807456339e-09,4.9705006777145e-10,2.3513535563069e-10,1.0967268065931e-10,5.0436901183337e-11,2.2869773783074e-11,1.0224485320898e-11,4.5070379720014e-12,1.9588575553287e-12]},\"callback\":null,\"selected\":{\"2d\":{\"indices\":[]},\"1d\":{\"indices\":[]},\"0d\":{\"indices\":[],\"flag\":false}}},\"type\":\"ColumnDataSource\"},{\"id\":\"3580e514-38c0-4ced-cc91-c8169758c759\",\"attributes\":{\"fill_color\":{\"value\":\"#ff0000\"},\"doc\":null,\"y\":{\"field\":\"y\"},\"height\":{\"units\":\"data\",\"field\":\"height\"},\"x\":{\"field\":\"x\"},\"tags\":[],\"fill_alpha\":{\"value\":0.3},\"id\":\"3580e514-38c0-4ced-cc91-c8169758c759\",\"width\":{\"units\":\"data\",\"value\":0.01}},\"type\":\"Rect\"},{\"id\":\"c5d790f0-9383-428b-c240-087a91224fc7\",\"attributes\":{\"data_source\":{\"id\":\"c26c1037-2bbb-4352-c3b3-fd7c435119d5\",\"type\":\"ColumnDataSource\"},\"id\":\"c5d790f0-9383-428b-c240-087a91224fc7\",\"tags\":[],\"nonselection_glyph\":null,\"glyph\":{\"id\":\"3580e514-38c0-4ced-cc91-c8169758c759\",\"type\":\"Rect\"},\"doc\":null,\"selection_glyph\":null},\"type\":\"GlyphRenderer\"},{\"id\":\"a334dcdb-f5f8-4990-c992-a721ea6e7bb0\",\"attributes\":{\"column_names\":[\"x\",\"y\"],\"id\":\"a334dcdb-f5f8-4990-c992-a721ea6e7bb0\",\"tags\":[],\"doc\":null,\"data\":{\"x\":[-10,-9.8319330215454,-9.6638650894165,-9.4957981109619,-9.3277311325073,-9.1596641540527,-8.9915962219238,-8.8235292434692,-8.6554622650146,-8.4873952865601,-8.3193273544312,-8.1512603759766,-7.983193397522,-7.8151259422302,-7.6470584869385,-7.4789915084839,-7.3109245300293,-7.1428570747375,-6.9747896194458,-6.8067226409912,-6.6386556625366,-6.4705882072449,-6.3025207519531,-6.1344537734985,-5.9663863182068,-5.7983193397522,-5.6302518844604,-5.4621849060059,-5.2941174507141,-5.1260504722595,-4.9579830169678,-4.7899160385132,-4.6218485832214,-4.4537816047668,-4.2857141494751,-4.1176471710205,-3.9495797157288,-3.7815127372742,-3.6134452819824,-3.4453783035278,-3.2773108482361,-3.1092438697815,-2.9411764144897,-2.7731094360352,-2.6050419807434,-2.4369750022888,-2.2689075469971,-2.1008405685425,-1.9327726364136,-1.764705657959,-1.5966386795044,-1.4285717010498,-1.2605037689209,-1.0924367904663,-0.92436981201172,-0.75630283355713,-0.58823490142822,-0.42016792297363,-0.25210094451904,-0.084033966064453,0.084033966064453,0.25210094451904,0.42016792297363,0.58823490142822,0.75630283355713,0.92436981201172,1.0924367904663,1.2605037689209,1.4285717010498,1.5966386795044,1.764705657959,1.9327726364136,2.1008405685425,2.2689075469971,2.4369745254517,2.6050424575806,2.7731094360352,2.9411764144897,3.1092433929443,3.2773113250732,3.4453783035278,3.6134452819824,3.781512260437,3.9495801925659,4.1176471710205,4.2857141494751,4.4537811279297,4.6218490600586,4.7899160385132,4.9579830169678,5.1260499954224,5.2941179275513,5.4621849060059,5.6302518844604,5.798318862915,5.9663867950439,6.1344547271729,6.3025207519531,6.470588684082,6.6386547088623,6.8067226409912,6.9747905731201,7.1428565979004,7.3109245300293,7.4789924621582,7.6470584869385,7.8151264190674,7.9831924438477,8.1512603759766,8.3193283081055,8.4873943328857,8.6554622650146,8.8235301971436,8.9915962219238,9.1596641540527,9.327730178833,9.4957981109619,9.6638660430908,9.8319320678711,10],\"y\":[3.9177151106573e-12,9.0140239022984e-12,2.0449048704352e-11,4.5739547566148e-11,1.0087341378862e-10,2.1934536131862e-10,4.7027071126138e-10,9.9410391030119e-10,2.0719614912679e-09,4.2579300085777e-09,8.6274782873375e-09,1.7235853633224e-08,3.3950691147311e-08,6.5937328486143e-08,1.26264282585e-07,2.3839400853376e-07,4.4378870711625e-07,8.1456255429657e-07,1.4741412996955e-06,2.6303830509278e-06,4.6277018554974e-06,8.0274767242372e-06,1.3729629245063e-05,2.3152837457019e-05,3.8496076740557e-05,6.3109509937931e-05,0.00010200936958427,0.0001625737932045,0.00025546317920089,0.00039579646545462,0.00060461956309155,0.00091066444292665,0.001352388295345,0.001980205764994,0.0028588138520718,0.0040693716146052,0.005711309146136,0.0079033244401217,0.010783275589347,0.014506331644952,0.019241157919168,0.025163479149342,0.032447185367346,0.04125240072608,0.051711603999138,0.063913553953171,0.077886909246445,0.093584075570107,0.1108680292964,0.12950205802917,0.14914655685425,0.16936209797859,0.18962070345879,0.20932511985302,0.22783651947975,0.24450719356537,0.25871786475182,0.26991522312164,0.27764809131622,0.28159722685814,0.28159722685814,0.27764809131622,0.26991522312164,0.25871786475182,0.24450719356537,0.22783651947975,0.20932511985302,0.18962070345879,0.16936209797859,0.14914655685425,0.12950205802917,0.1108680292964,0.093584075570107,0.077886909246445,0.063913598656654,0.051711566746235,0.04125240072608,0.032447185367346,0.025163503363729,0.019241139292717,0.014506331644952,0.010783275589347,0.0079033328220248,0.0057113035582006,0.0040693716146052,0.0028588138520718,0.0019802076276392,0.0013523870147765,0.00091066444292665,0.00060461956309155,0.0003957970184274,0.00025546291726641,0.0001625737932045,0.00010200936958427,6.3109575421549e-05,3.8496040360769e-05,2.3152770154411e-05,1.3729629245063e-05,8.027462172322e-06,4.6277195906441e-06,2.6303830509278e-06,1.4741356153536e-06,8.1456408906888e-07,4.4378870711625e-07,2.3839309903906e-07,1.26264282585e-07,6.5937200588451e-08,3.3950819045003e-08,1.7235853633224e-08,8.6274454247359e-09,4.2579459957892e-09,2.0719614912679e-09,9.9410013554291e-10,4.7027071126138e-10,2.1934536131862e-10,1.0087380236667e-10,4.5739547566148e-11,2.0448970641795e-11,9.0140759440027e-12,3.9177151106573e-12]},\"callback\":null,\"selected\":{\"2d\":{\"indices\":[]},\"1d\":{\"indices\":[]},\"0d\":{\"indices\":[],\"flag\":false}}},\"type\":\"ColumnDataSource\"},{\"id\":\"460ba0da-8ee3-45c5-c56e-3fb5908f7d33\",\"attributes\":{\"id\":\"460ba0da-8ee3-45c5-c56e-3fb5908f7d33\",\"line_alpha\":{\"value\":0.3},\"x\":{\"field\":\"x\"},\"tags\":[],\"doc\":null,\"y\":{\"field\":\"y\"},\"line_color\":{\"value\":\"#ff0000\"},\"line_width\":{\"units\":\"data\",\"value\":3}},\"type\":\"Line\"},{\"id\":\"c3590fb9-52d8-49dd-c9f0-ff565f7a06a6\",\"attributes\":{\"data_source\":{\"id\":\"a334dcdb-f5f8-4990-c992-a721ea6e7bb0\",\"type\":\"ColumnDataSource\"},\"id\":\"c3590fb9-52d8-49dd-c9f0-ff565f7a06a6\",\"tags\":[],\"nonselection_glyph\":null,\"glyph\":{\"id\":\"460ba0da-8ee3-45c5-c56e-3fb5908f7d33\",\"type\":\"Line\"},\"doc\":null,\"selection_glyph\":null},\"type\":\"GlyphRenderer\"},{\"id\":\"85a78014-8ae6-4aae-ca19-64e4a9e2186c\",\"attributes\":{\"id\":\"85a78014-8ae6-4aae-ca19-64e4a9e2186c\",\"legends\":[[\"Normal\",[{\"id\":\"c3590fb9-52d8-49dd-c9f0-ff565f7a06a6\",\"type\":\"GlyphRenderer\"}]]],\"tags\":[],\"doc\":null,\"plot\":{\"id\":\"0fcf2c2b-4429-4f0d-cfb9-d9919d9bf371\",\"type\":\"Plot\",\"subtype\":\"Chart\"}},\"type\":\"Legend\"},{\"id\":\"3c27268f-0070-4c6b-cc6b-c1c0e5c7676b\",\"attributes\":{\"column_names\":[\"x\",\"y\"],\"id\":\"3c27268f-0070-4c6b-cc6b-c1c0e5c7676b\",\"tags\":[],\"doc\":null,\"data\":{\"x\":[-10,-9.8319330215454,-9.6638650894165,-9.4957981109619,-9.3277311325073,-9.1596641540527,-8.9915962219238,-8.8235292434692,-8.6554622650146,-8.4873952865601,-8.3193273544312,-8.1512603759766,-7.983193397522,-7.8151259422302,-7.6470584869385,-7.4789915084839,-7.3109245300293,-7.1428570747375,-6.9747896194458,-6.8067226409912,-6.6386556625366,-6.4705882072449,-6.3025207519531,-6.1344537734985,-5.9663863182068,-5.7983193397522,-5.6302518844604,-5.4621849060059,-5.2941174507141,-5.1260504722595,-4.9579830169678,-4.7899160385132,-4.6218485832214,-4.4537816047668,-4.2857141494751,-4.1176471710205,-3.9495797157288,-3.7815127372742,-3.6134452819824,-3.4453783035278,-3.2773108482361,-3.1092438697815,-2.9411764144897,-2.7731094360352,-2.6050419807434,-2.4369750022888,-2.2689075469971,-2.1008405685425,-1.9327726364136,-1.764705657959,-1.5966386795044,-1.4285717010498,-1.2605037689209,-1.0924367904663,-0.92436981201172,-0.75630283355713,-0.58823490142822,-0.42016792297363,-0.25210094451904,-0.084033966064453,0.084033966064453,0.25210094451904,0.42016792297363,0.58823490142822,0.75630283355713,0.92436981201172,1.0924367904663,1.2605037689209,1.4285717010498,1.5966386795044,1.764705657959,1.9327726364136,2.1008405685425,2.2689075469971,2.4369745254517,2.6050424575806,2.7731094360352,2.9411764144897,3.1092433929443,3.2773113250732,3.4453783035278,3.6134452819824,3.781512260437,3.9495801925659,4.1176471710205,4.2857141494751,4.4537811279297,4.6218490600586,4.7899160385132,4.9579830169678,5.1260499954224,5.2941179275513,5.4621849060059,5.6302518844604,5.798318862915,5.9663867950439,6.1344547271729,6.3025207519531,6.470588684082,6.6386547088623,6.8067226409912,6.9747905731201,7.1428565979004,7.3109245300293,7.4789924621582,7.6470584869385,7.8151264190674,7.9831924438477,8.1512603759766,8.3193283081055,8.4873943328857,8.6554622650146,8.8235301971436,8.9915962219238,9.1596641540527,9.327730178833,9.4957981109619,9.6638660430908,9.8319320678711,10],\"y\":[3.9177151106573e-12,9.0140239022984e-12,2.0449048704352e-11,4.5739547566148e-11,1.0087341378862e-10,2.1934536131862e-10,4.7027071126138e-10,9.9410391030119e-10,2.0719614912679e-09,4.2579300085777e-09,8.6274782873375e-09,1.7235853633224e-08,3.3950691147311e-08,6.5937328486143e-08,1.26264282585e-07,2.3839400853376e-07,4.4378870711625e-07,8.1456255429657e-07,1.4741412996955e-06,2.6303830509278e-06,4.6277018554974e-06,8.0274767242372e-06,1.3729629245063e-05,2.3152837457019e-05,3.8496076740557e-05,6.3109509937931e-05,0.00010200936958427,0.0001625737932045,0.00025546317920089,0.00039579646545462,0.00060461956309155,0.00091066444292665,0.001352388295345,0.001980205764994,0.0028588138520718,0.0040693716146052,0.005711309146136,0.0079033244401217,0.010783275589347,0.014506331644952,0.019241157919168,0.025163479149342,0.032447185367346,0.04125240072608,0.051711603999138,0.063913553953171,0.077886909246445,0.093584075570107,0.1108680292964,0.12950205802917,0.14914655685425,0.16936209797859,0.18962070345879,0.20932511985302,0.22783651947975,0.24450719356537,0.25871786475182,0.26991522312164,0.27764809131622,0.28159722685814,0.28159722685814,0.27764809131622,0.26991522312164,0.25871786475182,0.24450719356537,0.22783651947975,0.20932511985302,0.18962070345879,0.16936209797859,0.14914655685425,0.12950205802917,0.1108680292964,0.093584075570107,0.077886909246445,0.063913598656654,0.051711566746235,0.04125240072608,0.032447185367346,0.025163503363729,0.019241139292717,0.014506331644952,0.010783275589347,0.0079033328220248,0.0057113035582006,0.0040693716146052,0.0028588138520718,0.0019802076276392,0.0013523870147765,0.00091066444292665,0.00060461956309155,0.0003957970184274,0.00025546291726641,0.0001625737932045,0.00010200936958427,6.3109575421549e-05,3.8496040360769e-05,2.3152770154411e-05,1.3729629245063e-05,8.027462172322e-06,4.6277195906441e-06,2.6303830509278e-06,1.4741356153536e-06,8.1456408906888e-07,4.4378870711625e-07,2.3839309903906e-07,1.26264282585e-07,6.5937200588451e-08,3.3950819045003e-08,1.7235853633224e-08,8.6274454247359e-09,4.2579459957892e-09,2.0719614912679e-09,9.9410013554291e-10,4.7027071126138e-10,2.1934536131862e-10,1.0087380236667e-10,4.5739547566148e-11,2.0448970641795e-11,9.0140759440027e-12,3.9177151106573e-12]},\"callback\":null,\"selected\":{\"2d\":{\"indices\":[]},\"1d\":{\"indices\":[]},\"0d\":{\"indices\":[],\"flag\":false}}},\"type\":\"ColumnDataSource\"},{\"id\":\"6fe880f9-ce5a-4823-c80d-081cf020892c\",\"attributes\":{\"id\":\"6fe880f9-ce5a-4823-c80d-081cf020892c\",\"fill_color\":{\"value\":\"#ff0000\"},\"x\":{\"field\":\"x\"},\"tags\":[],\"doc\":null,\"y\":{\"field\":\"y\"},\"fill_alpha\":{\"value\":0.8},\"size\":{\"value\":6}},\"type\":\"Circle\"},{\"id\":\"47b0bc47-2028-4381-c374-41793ca9e5ab\",\"attributes\":{\"data_source\":{\"id\":\"3c27268f-0070-4c6b-cc6b-c1c0e5c7676b\",\"type\":\"ColumnDataSource\"},\"id\":\"47b0bc47-2028-4381-c374-41793ca9e5ab\",\"tags\":[],\"nonselection_glyph\":null,\"glyph\":{\"id\":\"6fe880f9-ce5a-4823-c80d-081cf020892c\",\"type\":\"Circle\"},\"doc\":null,\"selection_glyph\":null},\"type\":\"GlyphRenderer\"},{\"id\":\"8a353caa-5def-4bf2-cb6d-6ca017514ca8\",\"attributes\":{\"id\":\"8a353caa-5def-4bf2-cb6d-6ca017514ca8\",\"end\":11.011,\"tags\":[],\"doc\":null,\"start\":-11.011,\"callback\":null},\"type\":\"Range1d\"},{\"id\":\"b6f10976-3f8d-499b-c9fa-e7c7d460bb5e\",\"attributes\":{\"id\":\"b6f10976-3f8d-499b-c9fa-e7c7d460bb5e\",\"end\":0.32,\"tags\":[],\"doc\":null,\"start\":-0.12,\"callback\":null},\"type\":\"Range1d\"}];\n", "\n", " if(typeof(Bokeh) !== \"undefined\") {\n", " console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", - " Bokeh.embed.inject_plot(\"c823b43b-9802-4179-c1ca-202ac6ec8e4a\", all_models);\n", + " Bokeh.embed.inject_plot(\"9165a560-bf4e-4433-c48c-504cacda79f4\", all_models);\n", " } else {\n", " load_lib(bokehjs_url, function() {\n", " console.log(\"Bokeh: BokehJS plotting callback run at\", new Date())\n", - " Bokeh.embed.inject_plot(\"c823b43b-9802-4179-c1ca-202ac6ec8e4a\", all_models);\n", + " Bokeh.embed.inject_plot(\"9165a560-bf4e-4433-c48c-504cacda79f4\", all_models);\n", " });\n", " }\n", "\n", @@ -415,13 +448,13 @@ ], "metadata": {}, "output_type": "pyout", - "prompt_number": 8, + "prompt_number": 10, "text": [ "-- impossible to show ASCII art plots" ] } ], - "prompt_number": 8 + "prompt_number": 10 } ], "metadata": {} From cb8e05409b9f8d880ecd8ed3423c8ad5bb67c6e2 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Tue, 13 Oct 2015 15:17:10 +0200 Subject: [PATCH 71/94] Improved vioplot --- IPyLua/bokeh.lua | 74 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index 5559399..df7e01e 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -11,7 +11,7 @@ local DEF_HEIGTH = 6 local DEF_LEVEL = "__nil__" local DEF_LINE_WIDTH = 2 local DEF_SIZE = 6 -local DEF_VIOLIN_WIDTH=0.95 +local DEF_VIOLIN_WIDTH=0.90 local DEF_WIDTH = 6 local DEF_X = 0 local DEF_XGRID = 100 @@ -874,7 +874,7 @@ local figure_methods = { bars = function(self, params) -- x, y, width, height, color, alpha, legend, hover, more_data params = params or {} check_table(params, "x", "height", "width", "y", "color", "alpha", - "legend", "hover", "more_data") + "legend", "hover", "more_data", "line_color") check_mandatories(params, "x") local x = params.x or DEF_X local y = params.y or DEF_Y @@ -883,6 +883,7 @@ local figure_methods = { local color = params.color or next_color(self) local alpha = params.alpha or DEF_ALPHA local hover = params.hover + local line_color = params.line_color if width == "auto" then width = compute_optim(x, DEF_WIDTH) end if height == "auto" then height = compute_optim(y, DEF_HEIGTH) end @@ -895,6 +896,7 @@ local figure_methods = { fill_alpha=alpha, color=color, hover=hover, + line_color=line_color, } local more_data = params.more_data or {} @@ -1023,12 +1025,15 @@ local figure_methods = { vioplot = function(self, params) -- x, y, legend, alpha, color, factors, width + local color = params.color or next_color(self) + local violins = violin_transformation(params, { alpha = params.alpha, legend = params.legend, - color = params.color, + color = color, + line_color = color, } ) @@ -1036,6 +1041,11 @@ local figure_methods = { self:bars(violins.bars[i]) end + self:boxes(violins.boxes) + self:points{ x=violins.boxes.x, + y=violins.boxes.q2, + color="#ffffff", } + self:x_axis{ type="CategoricalAxis", pos="below" } return self @@ -1172,7 +1182,9 @@ local function hist(x, breaks, output_type, scale) local inc = diff / (breaks+1) local half = inc * 0.5 local bins = {} + local width = {} local y = {} + local max = 0.0 for i=1,breaks do bins[i] = 0.0 y[i] = (i - 1.0) * inc + half + min @@ -1181,14 +1193,17 @@ local function hist(x, breaks, output_type, scale) local b = math_floor( (x[i] - min)/diff * breaks ) + 1.0 b = math_max(0.0, math_min(breaks, b)) bins[b] = bins[b] + 1.0 + max = math_max(max, bins[b]) end + local scale = scale or 1.0 + for i=1,#bins do width[i] = (bins[i]/max)*scale end if output_type == "ratio" then local scale = scale or 1.0 - local N = #x for i=1,#bins do bins[i] = (bins[i]/N)*scale end + local N = #x for i=1,#bins do bins[i] = (bins[i]/N) end elseif scale then - for i=1,#bins do bins[i] = bins[i] * scale end + for i=1,#bins do bins[i] = bins[i] end end - return { y=y, width=bins, height=inc } + return { y=y, width=width, height=inc, bins=bins, } end -- @@ -1352,13 +1367,24 @@ end function violin_transformation(params, more_params) -- x, y, factors, width, breaks local breaks = params.breaks or DEF_BREAKS + local width = params.width or DEF_VIOLIN_WIDTH local x = toseries( params.x ) local y = toseries( params.y ) assert(type(y) == "table" or type(y) == "userdata") local violins = { - bars = {}, + bars = {}, + boxes = { + min = {}, + max = {}, + q1 = {}, + q2 = {}, + q3 = {}, + outliers = {}, + width = {}, + x = {}, + }, } local levels @@ -1388,9 +1414,37 @@ function violin_transformation(params, more_params) -- x, y, factors, width, bre table.sort(cur) local bars = violins.bars - bars[i] = hist(cur, breaks, "ratio", width) - bars[i].x = tostring( factor ) - bars[i].hover = bars[i].width + local h = hist(cur, breaks, "ratio", width) + + bars[i] = { + x = tostring( factor ), + y = h.y, + width = h.width, + height = h.height, + hover = h.bins, + } + + local boxes = violins.boxes + local q1 = quantile(cur, 0.25) + local q2 = quantile(cur, 0.50) + local q3 = quantile(cur, 0.75) + local IQ = q3 - q1 + + local min = quantile(cur, 0.0) + local max = quantile(cur, 1.0) + + local upper = math.min(q3 + 1.5 * IQ, max) + local lower = math.max(q1 - 1.5 * IQ, min) + + boxes.x[i] = tostring( factor ) + boxes.min[i] = upper + boxes.max[i] = lower + boxes.q1[i] = q1 + boxes.q2[i] = q2 + boxes.q3[i] = q3 + boxes.width = width * 0.05 + boxes.alpha = 1.0 + boxes.color = "#000000" for k,v in pairs(more_params or {}) do assert(k ~= "more_data", "Unable to handle more_data argument") From f665865de49d638e9de1276cfe6d5f00b257fd16 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Tue, 13 Oct 2015 15:28:21 +0200 Subject: [PATCH 72/94] Minor change in vioplot --- IPyLua/bokeh.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index df7e01e..c7c30b6 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -1042,9 +1042,6 @@ local figure_methods = { end self:boxes(violins.boxes) - self:points{ x=violins.boxes.x, - y=violins.boxes.q2, - color="#ffffff", } self:x_axis{ type="CategoricalAxis", pos="below" } @@ -1443,13 +1440,15 @@ function violin_transformation(params, more_params) -- x, y, factors, width, bre boxes.q2[i] = q2 boxes.q3[i] = q3 boxes.width = width * 0.05 - boxes.alpha = 1.0 - boxes.color = "#000000" for k,v in pairs(more_params or {}) do assert(k ~= "more_data", "Unable to handle more_data argument") assert(not bars[i][k], "Unable to redefine parameter " .. k) bars[i][k]=v + if k == "color" or k == "alpha" then + boxes[k]=boxes[k] or {} + boxes[k][i]=v + end end end From 93fb63aad01b8a41f6f802f776c249ceccdabfb2 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Tue, 13 Oct 2015 16:06:15 +0200 Subject: [PATCH 73/94] Minor cosmetic changes --- IPyLua/bokeh.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index c7c30b6..00cad7f 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -208,7 +208,6 @@ local function compute_optim(x, DEF) if type(x[1]) == "string" then optim = 1.0 else - optim = DEF local t = {} for i=1,#x do t[i] = x[i] end table.sort(t) for i=2,#t do optim = math_min( optim, math_abs( t[i-1] - t[i] ) ) end @@ -788,7 +787,7 @@ local figure_methods = { height = 0.005 * max_height, width = width, alpha = alpha, - color = color, } + color = "#000000", } self:bars{ x = x, y = min, From 281187d2b995cd5d87ef6f7ff95e41dce9f842d1 Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Tue, 13 Oct 2015 16:47:40 +0200 Subject: [PATCH 74/94] Minor cosmetic changes --- IPyLua/bokeh.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/IPyLua/bokeh.lua b/IPyLua/bokeh.lua index 00cad7f..17543db 100644 --- a/IPyLua/bokeh.lua +++ b/IPyLua/bokeh.lua @@ -1364,6 +1364,7 @@ end function violin_transformation(params, more_params) -- x, y, factors, width, breaks local breaks = params.breaks or DEF_BREAKS local width = params.width or DEF_VIOLIN_WIDTH + local more_params = more_params or {} local x = toseries( params.x ) local y = toseries( params.y ) @@ -1439,15 +1440,13 @@ function violin_transformation(params, more_params) -- x, y, factors, width, bre boxes.q2[i] = q2 boxes.q3[i] = q3 boxes.width = width * 0.05 + boxes.alpha = 1.0 + boxes.color = more_params.color - for k,v in pairs(more_params or {}) do + for k,v in pairs(more_params) do assert(k ~= "more_data", "Unable to handle more_data argument") assert(not bars[i][k], "Unable to redefine parameter " .. k) bars[i][k]=v - if k == "color" or k == "alpha" then - boxes[k]=boxes[k] or {} - boxes[k][i]=v - end end end From 89cee902b91abdf6badfb04335cc08bfefd3840d Mon Sep 17 00:00:00 2001 From: Paco Zamora Martinez Date: Wed, 14 Oct 2015 01:06:30 +0200 Subject: [PATCH 75/94] Changed https by http --- IPyLua/html_template.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IPyLua/html_template.lua b/IPyLua/html_template.lua index 23638c7..2ed903a 100644 --- a/IPyLua/html_template.lua +++ b/IPyLua/html_template.lua @@ -21,7 +21,7 @@ return [[