Tarantool development patches archive
 help / color / mirror / Atom feed
From: Konstantin Osipov <kostja@tarantool.org>
To: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: tml <tarantool-patches@freelists.org>,
	Alexander Turenko <alexander.turenko@tarantool.org>
Subject: [tarantool-patches] Re: [RFC 2/2] box/lua/console: Add support for lua output format
Date: Sat, 6 Jul 2019 01:59:08 +0300	[thread overview]
Message-ID: <20190705225908.GC30966@atlas> (raw)
In-Reply-To: <20190705210959.8527-3-gorcunov@gmail.com>

* Cyrill Gorcunov <gorcunov@gmail.com> [19/07/06 00:13]:

LGTM. @totktonada, please provide a more thorough review.

One question is what happens to the output on client reconnect.
Perhaps we should cache it on the client as well (this is the same
issue with console language).

> Historically we use YAML format to print results of operation to
> a console. Moreover our test engine is aiming YAML as a primary format
> to compare results of test runs. Still we need an ability to print
> results in a different fasion, in particular one may need to use
> the console in a REPL way so that the results would be copied and
> pased back to further processing.
> 
> For this sake we introduce that named "output" command which allows
> to specify which exactly output format to use. Currently only yaml
> and lua formats are supported.
> 
> To specify lua output format type
> 
>  | tarantool> \set output lua
> 
> in the console. lua mode supports line oriented output (default) or
> block mode.
> 
> For example
> 
>  | tarantool> a={1,2,3}
>  | tarantool> a
>  | ---
>  | - - 1
>  |   - 2
>  |   - 3
>  | ...
>  | tarantool> \set output lua
>  | true
>  | tarantool> a
>  | {1, 2, 3}
>  | tarantool> \set output lua,block
>  | true
>  | tarantool> a
>  | {
>  |   1,
>  |   2,
>  |   3
>  | }
> 
> By default YAML output format is kept for now, simply to not
> break the test engine. The output is bound to a session, thus every
> new session should setup own conversion if needed.
> 
> Since serializing lua data is not a trivial task we use "serpent"
> third party module to convert data.
> ---
>  src/box/CMakeLists.txt  |  1 +
>  src/box/lua/console.c   | 26 ++++++++++++
>  src/box/lua/console.lua | 90 ++++++++++++++++++++++++++++++++++++++++-
>  src/lua/help_en_US.lua  |  1 +
>  4 files changed, 117 insertions(+), 1 deletion(-)
> 
> diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
> index 481842a39..4b037094a 100644
> --- a/src/box/CMakeLists.txt
> +++ b/src/box/CMakeLists.txt
> @@ -11,6 +11,7 @@ lua_source(lua_sources lua/feedback_daemon.lua)
>  lua_source(lua_sources lua/net_box.lua)
>  lua_source(lua_sources lua/upgrade.lua)
>  lua_source(lua_sources lua/console.lua)
> +lua_source(lua_sources ../../third_party/serpent/src/serpent.lua)
>  lua_source(lua_sources lua/xlog.lua)
>  lua_source(lua_sources lua/key_def.lua)
>  lua_source(lua_sources lua/merger.lua)
> diff --git a/src/box/lua/console.c b/src/box/lua/console.c
> index cefe5d863..57e7e7f4f 100644
> --- a/src/box/lua/console.c
> +++ b/src/box/lua/console.c
> @@ -46,6 +46,8 @@
>  #include <stdlib.h>
>  #include <ctype.h>
>  
> +extern char serpent_lua[];
> +
>  static struct luaL_serializer *luaL_yaml_default = NULL;
>  
>  /*
> @@ -431,6 +433,24 @@ console_session_push(struct session *session, uint64_t sync, struct port *port)
>  				     TIMEOUT_INFINITY);
>  }
>  
> +static void
> +lua_serpent_init(struct lua_State *L)
> +{
> +	static const char modname[] = "serpent";
> +	const char *modfile;
> +
> +	lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
> +	modfile = lua_pushfstring(L, "@builtin/%s.lua", modname);
> +	if (luaL_loadbuffer(L, serpent_lua, strlen(serpent_lua), modfile)) {
> +		panic("Error loading Lua module %s...: %s",
> +		      modname, lua_tostring(L, -1));
> +	}
> +
> +	lua_call(L, 0, 1);
> +	lua_setfield(L, -3, modname);  /* _LOADED[modname] = new table */
> +	lua_pop(L, 2);
> +}
> +
>  void
>  tarantool_lua_console_init(struct lua_State *L)
>  {
> @@ -471,6 +491,12 @@ tarantool_lua_console_init(struct lua_State *L)
>  	};
>  	session_vtab_registry[SESSION_TYPE_CONSOLE] = console_session_vtab;
>  	session_vtab_registry[SESSION_TYPE_REPL] = console_session_vtab;
> +
> +	/*
> +	 * Register serpent serializer as we
> +	 * need it inside console REPL mode.
> +	 */
> +	lua_serpent_init(L);
>  }
>  
>  /*
> diff --git a/src/box/lua/console.lua b/src/box/lua/console.lua
> index f922f0320..7c3b608cb 100644
> --- a/src/box/lua/console.lua
> +++ b/src/box/lua/console.lua
> @@ -1,5 +1,6 @@
>  -- console.lua -- internal file
>  
> +local serpent = require('serpent')
>  local internal = require('console')
>  local session_internal = require('box.internal.session')
>  local fiber = require('fiber')
> @@ -13,7 +14,9 @@ local net_box = require('net.box')
>  local YAML_TERM = '\n...\n'
>  local PUSH_TAG_HANDLE = '!push!'
>  
> -local function format(status, ...)
> +local output_handlers = { }
> +
> +output_handlers["yaml"] = function(status, opts, ...)
>      local err
>      if status then
>          -- serializer can raise an exception
> @@ -33,6 +36,71 @@ local function format(status, ...)
>      return internal.format({ error = err })
>  end
>  
> +output_handlers["lua"] = function(status, opts, ...)
> +    local data = ...
> +    --
> +    -- Don't print nil if there is no data
> +    if data == nil then
> +        return ""
> +    end
> +    --
> +    -- Map internals symbols which serpent don't know
> +    -- about into known representation.
> +    local map_symbols = function(tag, head, body, tail, level)
> +        local symbols = {
> +            ['"cdata<void %*>: NULL"']  = 'box.NULL'
> +        }
> +        for k,v in pairs(symbols) do
> +            body = body:gsub(k, v)
> +        end
> +        return tag..head..body..tail
> +    end
> +    local serpent_opts = {
> +        custom  = map_symbols,
> +        comment = false,
> +    }
> +    if opts == "block" then
> +        return serpent.block(..., serpent_opts)
> +    end
> +    return serpent.line(..., serpent_opts)
> +end
> +
> +local function output_verify_opts(fmt, opts)
> +    if opts == nil then
> +        return
> +    end
> +    if fmt == "lua" then
> +        if opts ~= "line" and opts ~= "block" then
> +            local msg = 'Wrong option "%s", expecting: line or block.'
> +            error(msg:format(opts))
> +        end
> +    end
> +end
> +
> +local function output_save(fmt, opts)
> +    --
> +    -- Output format descriptors are saved per
> +    -- session thus each console may specify own
> +    -- specifics.
> +    box.session.storage.console_output = {
> +        ["fmt"] = fmt, ["opts"] = opts
> +    }
> +end
> +
> +local function format(status, ...)
> +    local d = box.session.storage.console_output
> +    --
> +    -- If there was no assignment yet provide
> +    -- a default value; for now it is YAML
> +    -- for backward compatibility sake (don't
> +    -- forget about test results which are in
> +    -- YAML format).
> +    if d == nil then
> +        d = { ["fmt"] = "yaml", ["opts"] = nil }
> +    end
> +    return output_handlers[d["fmt"]](status, d["opts"], ...)
> +end
> +
>  --
>  -- Set delimiter
>  --
> @@ -71,11 +139,31 @@ local function set_language(storage, value)
>      return true
>  end
>  
> +local function set_output(storage, value)
> +    local fmt, opts
> +    if value:match("([^,]+),([^,]+)") ~= nil then
> +        fmt, opts = value:match("([^,]+),([^,]+)")
> +    else
> +        fmt = value
> +    end
> +    for k, _ in pairs(output_handlers) do
> +        if k == fmt then
> +            output_verify_opts(fmt, opts)
> +            output_save(fmt, opts)
> +            return true
> +        end
> +    end
> +    local msg = 'Invalid format "%s", supported languages: lua and yaml.'
> +    return error(msg:format(value))
> +end
> +
>  local function set_param(storage, func, param, value)
>      local params = {
>          language = set_language,
>          lang = set_language,
>          l = set_language,
> +        output = set_output,
> +        o = set_output,
>          delimiter = set_delimiter,
>          delim = set_delimiter,
>          d = set_delimiter
> diff --git a/src/lua/help_en_US.lua b/src/lua/help_en_US.lua
> index d37b49bf7..4f0f7d65b 100644
> --- a/src/lua/help_en_US.lua
> +++ b/src/lua/help_en_US.lua
> @@ -8,6 +8,7 @@ To start the interactive Tarantool tutorial, type 'tutorial()' here.
>  Available backslash commands:
>  
>    \set language <language>   -- set language (lua or sql)
> +  \set output <format>       -- set output format (lua[,line|block] or yaml)
>    \set delimiter <delimiter> -- set expression delimiter
>    \help                      -- show this screen
>    \quit                      -- quit interactive console
> -- 
> 2.20.1
> 

-- 
Konstantin Osipov, Moscow, Russia

  reply	other threads:[~2019-07-05 22:59 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-07-05 21:09 [tarantool-patches] [RFC v2 0/2] " Cyrill Gorcunov
2019-07-05 21:09 ` [tarantool-patches] [RFC 1/2] third_party/serpent: Add serpent repo Cyrill Gorcunov
2019-07-05 21:09 ` [tarantool-patches] [RFC 2/2] box/lua/console: Add support for lua output format Cyrill Gorcunov
2019-07-05 22:59   ` Konstantin Osipov [this message]
2019-07-05 23:04     ` [tarantool-patches] " Konstantin Osipov
2019-07-06  6:50     ` Cyrill Gorcunov
2019-07-06 16:02       ` Konstantin Osipov
2019-07-06 20:35         ` Cyrill Gorcunov
2019-07-09  3:46         ` Alexander Turenko
2019-07-09 18:06           ` Konstantin Osipov
2019-07-09  3:19   ` Alexander Turenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190705225908.GC30966@atlas \
    --to=kostja@tarantool.org \
    --cc=alexander.turenko@tarantool.org \
    --cc=gorcunov@gmail.com \
    --cc=tarantool-patches@freelists.org \
    --subject='[tarantool-patches] Re: [RFC 2/2] box/lua/console: Add support for lua output format' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox