mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-12-17 13:43:05 +08:00
Remove todos related to Elixir v1.15 (#2354)
Co-authored-by: Jonatan Kłosko <jonatanklosko@gmail.com>
This commit is contained in:
parent
04eaab448b
commit
82b633d595
8 changed files with 20 additions and 233 deletions
|
|
@ -38,7 +38,6 @@ defmodule Livebook.Runtime.ErlDist do
|
||||||
Livebook.Runtime.ErlDist.RuntimeServer,
|
Livebook.Runtime.ErlDist.RuntimeServer,
|
||||||
Livebook.Runtime.ErlDist.EvaluatorSupervisor,
|
Livebook.Runtime.ErlDist.EvaluatorSupervisor,
|
||||||
Livebook.Runtime.ErlDist.IOForwardGL,
|
Livebook.Runtime.ErlDist.IOForwardGL,
|
||||||
Livebook.Runtime.ErlDist.LoggerGLBackend,
|
|
||||||
Livebook.Runtime.ErlDist.LoggerGLHandler,
|
Livebook.Runtime.ErlDist.LoggerGLHandler,
|
||||||
Livebook.Runtime.ErlDist.Sink,
|
Livebook.Runtime.ErlDist.Sink,
|
||||||
Livebook.Runtime.ErlDist.SmartCellGL
|
Livebook.Runtime.ErlDist.SmartCellGL
|
||||||
|
|
|
||||||
|
|
@ -1,135 +0,0 @@
|
||||||
defmodule Livebook.Runtime.ErlDist.LoggerGLBackend do
|
|
||||||
# A logger backend used to forward logs to Livebook,
|
|
||||||
# as with regular output.
|
|
||||||
#
|
|
||||||
# The backend is based on `Logger.Backends.Console`,
|
|
||||||
# but instead of logging to the console, it sends
|
|
||||||
# log output to the group leader of the source process,
|
|
||||||
# provided the group leader is an instance of
|
|
||||||
# `Livebook.Runtime.Evaluator.IOProxy`.
|
|
||||||
#
|
|
||||||
# Basic configuration is taken from the console
|
|
||||||
# logger configuration to match its formatting.
|
|
||||||
|
|
||||||
@behaviour :gen_event
|
|
||||||
|
|
||||||
@type state :: %{
|
|
||||||
colors: keyword(),
|
|
||||||
format: binary(),
|
|
||||||
level: atom(),
|
|
||||||
metadata: list(atom())
|
|
||||||
}
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def init(__MODULE__) do
|
|
||||||
config = Application.get_env(:logger, :console, [])
|
|
||||||
{:ok, init_state(config)}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event({level, gl, {Logger, msg, ts, md}}, state) do
|
|
||||||
%{level: log_level} = state
|
|
||||||
|
|
||||||
if meet_level?(level, log_level) do
|
|
||||||
log_event(level, msg, ts, md, gl, state)
|
|
||||||
end
|
|
||||||
|
|
||||||
{:ok, state}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_event(_, state) do
|
|
||||||
{:ok, state}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def code_change(_old_vsn, state, _extra) do
|
|
||||||
{:ok, state}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_call(_message, state) do
|
|
||||||
{:ok, :ok, state}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_info(_message, state) do
|
|
||||||
{:ok, state}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def terminate(_reason, _state) do
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
defp init_state(config) do
|
|
||||||
level = Keyword.get(config, :level)
|
|
||||||
format = Logger.Formatter.compile(Keyword.get(config, :format))
|
|
||||||
colors = configure_colors(config)
|
|
||||||
metadata = Keyword.get(config, :metadata, []) |> configure_metadata()
|
|
||||||
|
|
||||||
%{format: format, metadata: metadata, level: level, colors: colors}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp configure_metadata(:all), do: :all
|
|
||||||
defp configure_metadata(metadata), do: Enum.reverse(metadata)
|
|
||||||
|
|
||||||
defp configure_colors(config) do
|
|
||||||
colors = Keyword.get(config, :colors, [])
|
|
||||||
|
|
||||||
%{
|
|
||||||
debug: Keyword.get(colors, :debug, :cyan),
|
|
||||||
info: Keyword.get(colors, :info, :normal),
|
|
||||||
warn: Keyword.get(colors, :warn, :yellow),
|
|
||||||
error: Keyword.get(colors, :error, :red),
|
|
||||||
enabled: Keyword.get(colors, :enabled, IO.ANSI.enabled?())
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp meet_level?(_lvl, nil), do: true
|
|
||||||
|
|
||||||
defp meet_level?(lvl, min) do
|
|
||||||
Logger.compare_levels(lvl, min) != :lt
|
|
||||||
end
|
|
||||||
|
|
||||||
defp log_event(level, msg, ts, md, gl, state) do
|
|
||||||
output = format_event(level, msg, ts, md, state)
|
|
||||||
|
|
||||||
if Livebook.Runtime.ErlDist.NodeManager.known_io_proxy?(gl) do
|
|
||||||
async_io(gl, output)
|
|
||||||
else
|
|
||||||
send(Livebook.Runtime.ErlDist.NodeManager, {:orphan_log, output})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def async_io(device, output) when is_pid(device) do
|
|
||||||
send(device, {:io_request, self(), make_ref(), {:put_chars, :unicode, output}})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp format_event(level, msg, ts, md, state) do
|
|
||||||
%{format: format, metadata: keys, colors: colors} = state
|
|
||||||
|
|
||||||
format
|
|
||||||
|> Logger.Formatter.format(level, msg, ts, take_metadata(md, keys))
|
|
||||||
|> color_event(level, colors, md)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp take_metadata(metadata, :all) do
|
|
||||||
metadata
|
|
||||||
end
|
|
||||||
|
|
||||||
defp take_metadata(metadata, keys) do
|
|
||||||
Enum.reduce(keys, [], fn key, acc ->
|
|
||||||
case Keyword.fetch(metadata, key) do
|
|
||||||
{:ok, val} -> [{key, val} | acc]
|
|
||||||
:error -> acc
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp color_event(data, _level, %{enabled: false}, _md), do: data
|
|
||||||
|
|
||||||
defp color_event(data, level, %{enabled: true} = colors, md) do
|
|
||||||
color = md[:ansi_color] || Map.fetch!(colors, level)
|
|
||||||
IO.ANSI.format([color, data])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -91,20 +91,15 @@ defmodule Livebook.Runtime.ErlDist.NodeManager do
|
||||||
Process.unregister(:standard_error)
|
Process.unregister(:standard_error)
|
||||||
Process.register(io_forward_gl_pid, :standard_error)
|
Process.register(io_forward_gl_pid, :standard_error)
|
||||||
|
|
||||||
# TODO: remove logger backend once we require Elixir v1.15
|
{:ok, _pid} = Livebook.Runtime.ErlDist.Sink.start_link()
|
||||||
if Code.ensure_loaded?(Logger) and function_exported?(Logger, :add_handlers, 1) do
|
|
||||||
{:ok, _pid} = Livebook.Runtime.ErlDist.Sink.start_link()
|
|
||||||
|
|
||||||
:logger.add_handler(:livebook_gl_handler, Livebook.Runtime.ErlDist.LoggerGLHandler, %{
|
:logger.add_handler(:livebook_gl_handler, Livebook.Runtime.ErlDist.LoggerGLHandler, %{
|
||||||
formatter: Logger.Formatter.new(),
|
formatter: Logger.Formatter.new(),
|
||||||
filters: [
|
filters: [
|
||||||
code_server_logs:
|
code_server_logs:
|
||||||
{&Livebook.Runtime.ErlDist.LoggerGLHandler.filter_code_server_logs/2, nil}
|
{&Livebook.Runtime.ErlDist.LoggerGLHandler.filter_code_server_logs/2, nil}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
else
|
|
||||||
Logger.add_backend(Livebook.Runtime.ErlDist.LoggerGLBackend)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Set `ignore_module_conflict` only for the NodeManager lifetime.
|
# Set `ignore_module_conflict` only for the NodeManager lifetime.
|
||||||
initial_ignore_module_conflict = Code.compiler_options()[:ignore_module_conflict]
|
initial_ignore_module_conflict = Code.compiler_options()[:ignore_module_conflict]
|
||||||
|
|
@ -154,12 +149,7 @@ defmodule Livebook.Runtime.ErlDist.NodeManager do
|
||||||
Process.unregister(:standard_error)
|
Process.unregister(:standard_error)
|
||||||
Process.register(state.original_standard_error, :standard_error)
|
Process.register(state.original_standard_error, :standard_error)
|
||||||
|
|
||||||
# TODO: remove logger backend once we require Elixir v1.15
|
:logger.remove_handler(:livebook_gl_handler)
|
||||||
if Code.ensure_loaded?(Logger) and function_exported?(Logger, :add_handlers, 1) do
|
|
||||||
:logger.remove_handler(:livebook_gl_handler)
|
|
||||||
else
|
|
||||||
Logger.remove_backend(Livebook.Runtime.ErlDist.LoggerGLBackend)
|
|
||||||
end
|
|
||||||
|
|
||||||
if state.unload_modules_on_termination do
|
if state.unload_modules_on_termination do
|
||||||
ErlDist.unload_required_modules()
|
ErlDist.unload_required_modules()
|
||||||
|
|
|
||||||
|
|
@ -387,7 +387,7 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
|
||||||
def handle_info({:orphan_log, output}, state) do
|
def handle_info({:orphan_log, output}, state) do
|
||||||
with %{} = evaluator <- state.last_evaluator,
|
with %{} = evaluator <- state.last_evaluator,
|
||||||
{:group_leader, io_proxy} <- Process.info(evaluator.pid, :group_leader) do
|
{:group_leader, io_proxy} <- Process.info(evaluator.pid, :group_leader) do
|
||||||
ErlDist.LoggerGLBackend.async_io(io_proxy, output)
|
ErlDist.LoggerGLHandler.async_io(io_proxy, output)
|
||||||
end
|
end
|
||||||
|
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
|
|
|
||||||
|
|
@ -613,7 +613,7 @@ defmodule Livebook.Runtime.Evaluator do
|
||||||
|
|
||||||
defp eval(:elixir, code, binding, env) do
|
defp eval(:elixir, code, binding, env) do
|
||||||
{{result, extra_diagnostics}, diagnostics} =
|
{{result, extra_diagnostics}, diagnostics} =
|
||||||
with_diagnostics([log: true], fn ->
|
Code.with_diagnostics([log: true], fn ->
|
||||||
try do
|
try do
|
||||||
quoted = Code.string_to_quoted!(code, file: env.file)
|
quoted = Code.string_to_quoted!(code, file: env.file)
|
||||||
|
|
||||||
|
|
@ -816,17 +816,6 @@ defmodule Livebook.Runtime.Evaluator do
|
||||||
Enum.reject(code_markers, &(&1.line == 0))
|
Enum.reject(code_markers, &(&1.line == 0))
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: remove once we require Elixir v1.15
|
|
||||||
if Code.ensure_loaded?(Code) and function_exported?(Code, :with_diagnostics, 2) do
|
|
||||||
defp with_diagnostics(opts, fun) do
|
|
||||||
Code.with_diagnostics(opts, fun)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
defp with_diagnostics(_opts, fun) do
|
|
||||||
{fun.(), []}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp extra_diagnostic?(%SyntaxError{}), do: true
|
defp extra_diagnostic?(%SyntaxError{}), do: true
|
||||||
defp extra_diagnostic?(%TokenMissingError{}), do: true
|
defp extra_diagnostic?(%TokenMissingError{}), do: true
|
||||||
|
|
||||||
|
|
@ -847,11 +836,6 @@ defmodule Livebook.Runtime.Evaluator do
|
||||||
do: {:variable, var},
|
do: {:variable, var},
|
||||||
into: identifiers_used
|
into: identifiers_used
|
||||||
|
|
||||||
identifiers_used =
|
|
||||||
for var <- tracer_info.undefined_vars,
|
|
||||||
do: {:variable, var},
|
|
||||||
into: identifiers_used
|
|
||||||
|
|
||||||
identifiers_defined =
|
identifiers_defined =
|
||||||
for var <- vars_defined(context, prev_context),
|
for var <- vars_defined(context, prev_context),
|
||||||
do: {{:variable, var}, context.id},
|
do: {{:variable, var}, context.id},
|
||||||
|
|
|
||||||
|
|
@ -92,25 +92,11 @@ defmodule Livebook.Runtime.Evaluator.Doctests do
|
||||||
defp report_doctest_result(%{state: {:failed, failure}} = test, lines) do
|
defp report_doctest_result(%{state: {:failed, failure}} = test, lines) do
|
||||||
doctest_line = test.tags.doctest_line
|
doctest_line = test.tags.doctest_line
|
||||||
[prompt_line | _] = lines = Enum.drop(lines, doctest_line - 1)
|
[prompt_line | _] = lines = Enum.drop(lines, doctest_line - 1)
|
||||||
|
end_line = test.tags[:doctest_data][:end_line]
|
||||||
end_line =
|
|
||||||
if end_line = test.tags[:doctest_data][:end_line] do
|
|
||||||
end_line
|
|
||||||
else
|
|
||||||
# TODO: Remove this branch once we require Elixir v1.15+
|
|
||||||
interval =
|
|
||||||
lines
|
|
||||||
|> Enum.take_while(&(not end_of_doctest?(&1)))
|
|
||||||
|> length()
|
|
||||||
|
|
||||||
interval + doctest_line - 1
|
|
||||||
end
|
|
||||||
|
|
||||||
end_line =
|
end_line =
|
||||||
with {:error, %ExUnit.AssertionError{}, [{_, _, _, location} | _]} <- failure,
|
with {:error, %ExUnit.AssertionError{}, [{_, _, _, location} | _]} <- failure,
|
||||||
assertion_line = location[:line],
|
assertion_line = location[:line],
|
||||||
# TODO: Remove this check once we require Elixir v1.15+
|
|
||||||
true <- is_integer(test.tags[:doctest_data][:end_line]),
|
|
||||||
true <- assertion_line in doctest_line..end_line do
|
true <- assertion_line in doctest_line..end_line do
|
||||||
end_line -
|
end_line -
|
||||||
length(
|
length(
|
||||||
|
|
@ -148,17 +134,6 @@ defmodule Livebook.Runtime.Evaluator.Doctests do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp end_of_doctest?(line) do
|
|
||||||
case String.trim_leading(line) do
|
|
||||||
"" -> true
|
|
||||||
"```" <> _ -> true
|
|
||||||
"~~~" <> _ -> true
|
|
||||||
"'''" <> _ -> true
|
|
||||||
"\"\"\"" <> _ -> true
|
|
||||||
_ -> false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp define_test_module(doctests_specs) do
|
defp define_test_module(doctests_specs) do
|
||||||
id =
|
id =
|
||||||
doctests_specs
|
doctests_specs
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@ defmodule Livebook.Runtime.Evaluator.Tracer do
|
||||||
requires_used: MapSet.new(),
|
requires_used: MapSet.new(),
|
||||||
requires_defined: MapSet.new(),
|
requires_defined: MapSet.new(),
|
||||||
imports_used?: false,
|
imports_used?: false,
|
||||||
imports_defined?: false,
|
imports_defined?: false
|
||||||
undefined_vars: MapSet.new()
|
|
||||||
|
|
||||||
@type t :: %__MODULE__{
|
@type t :: %__MODULE__{
|
||||||
modules_used: MapSet.t(),
|
modules_used: MapSet.t(),
|
||||||
|
|
@ -25,8 +24,7 @@ defmodule Livebook.Runtime.Evaluator.Tracer do
|
||||||
requires_used: MapSet.t(),
|
requires_used: MapSet.t(),
|
||||||
requires_defined: MapSet.t(),
|
requires_defined: MapSet.t(),
|
||||||
imports_used?: boolean(),
|
imports_used?: boolean(),
|
||||||
imports_defined?: boolean(),
|
imports_defined?: boolean()
|
||||||
undefined_vars: MapSet.t()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
|
|
@ -62,13 +60,11 @@ defmodule Livebook.Runtime.Evaluator.Tracer do
|
||||||
if(env.module, do: [], else: [:import_defined]) ++
|
if(env.module, do: [], else: [:import_defined]) ++
|
||||||
[{:module_used, module}, {:alias_used, module}]
|
[{:module_used, module}, {:alias_used, module}]
|
||||||
|
|
||||||
{:imported_function, meta, module, name, _arity} ->
|
{:imported_function, _meta, module, _name, _arity} ->
|
||||||
var? = Keyword.has_key?(meta, :if_undefined)
|
[{:module_used, module}, :import_used]
|
||||||
[{:module_used, module}, {:import_used, name, var?}]
|
|
||||||
|
|
||||||
{:imported_macro, meta, module, name, _arity} ->
|
{:imported_macro, _meta, module, _name, _arity} ->
|
||||||
var? = Keyword.has_key?(meta, :if_undefined)
|
[{:module_used, module}, :import_used]
|
||||||
[{:module_used, module}, {:import_used, name, var?}]
|
|
||||||
|
|
||||||
{:alias, _meta, alias, as, _opts} ->
|
{:alias, _meta, alias, as, _opts} ->
|
||||||
if(env.module, do: [], else: [{:alias_defined, as, alias}]) ++
|
if(env.module, do: [], else: [{:alias_defined, as, alias}]) ++
|
||||||
|
|
@ -136,14 +132,8 @@ defmodule Livebook.Runtime.Evaluator.Tracer do
|
||||||
update_in(info.requires_defined, &MapSet.put(&1, module))
|
update_in(info.requires_defined, &MapSet.put(&1, module))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp apply_update(info, {:import_used, name, var?}) do
|
defp apply_update(info, :import_used) do
|
||||||
info = put_in(info.imports_used?, true)
|
put_in(info.imports_used?, true)
|
||||||
|
|
||||||
if var? do
|
|
||||||
update_in(info.undefined_vars, &MapSet.put(&1, {name, nil}))
|
|
||||||
else
|
|
||||||
info
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp apply_update(info, :import_defined) do
|
defp apply_update(info, :import_defined) do
|
||||||
|
|
|
||||||
|
|
@ -834,22 +834,6 @@ defmodule Livebook.Runtime.EvaluatorTest do
|
||||||
assert {:variable, {:z, nil}} not in identifiers.used
|
assert {:variable, {:z, nil}} not in identifiers.used
|
||||||
end
|
end
|
||||||
|
|
||||||
test "reports parentheses-less arity-0 import as a used variable", %{evaluator: evaluator} do
|
|
||||||
# TODO: remove all logic around undefined unused vars once we require Elixir v1.15
|
|
||||||
Code.put_compiler_option(:on_undefined_variable, :warn)
|
|
||||||
|
|
||||||
identifiers =
|
|
||||||
"""
|
|
||||||
self
|
|
||||||
"""
|
|
||||||
|> eval(evaluator, 0)
|
|
||||||
|
|
||||||
Code.put_compiler_option(:on_undefined_variable, :raise)
|
|
||||||
|
|
||||||
assert {:variable, {:self, nil}} in identifiers.used
|
|
||||||
assert :imports in identifiers.used
|
|
||||||
end
|
|
||||||
|
|
||||||
test "module definition", %{evaluator: evaluator} do
|
test "module definition", %{evaluator: evaluator} do
|
||||||
identifiers =
|
identifiers =
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue