Remove todos related to Elixir v1.15 (#2354)

Co-authored-by: Jonatan Kłosko <jonatanklosko@gmail.com>
This commit is contained in:
José Valim 2023-11-16 09:18:05 +01:00 committed by GitHub
parent 04eaab448b
commit 82b633d595
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 20 additions and 233 deletions

View file

@ -38,7 +38,6 @@ defmodule Livebook.Runtime.ErlDist do
Livebook.Runtime.ErlDist.RuntimeServer,
Livebook.Runtime.ErlDist.EvaluatorSupervisor,
Livebook.Runtime.ErlDist.IOForwardGL,
Livebook.Runtime.ErlDist.LoggerGLBackend,
Livebook.Runtime.ErlDist.LoggerGLHandler,
Livebook.Runtime.ErlDist.Sink,
Livebook.Runtime.ErlDist.SmartCellGL

View file

@ -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

View file

@ -91,20 +91,15 @@ defmodule Livebook.Runtime.ErlDist.NodeManager do
Process.unregister(:standard_error)
Process.register(io_forward_gl_pid, :standard_error)
# TODO: remove logger backend once we require Elixir v1.15
if Code.ensure_loaded?(Logger) and function_exported?(Logger, :add_handlers, 1) do
{:ok, _pid} = Livebook.Runtime.ErlDist.Sink.start_link()
{:ok, _pid} = Livebook.Runtime.ErlDist.Sink.start_link()
:logger.add_handler(:livebook_gl_handler, Livebook.Runtime.ErlDist.LoggerGLHandler, %{
formatter: Logger.Formatter.new(),
filters: [
code_server_logs:
{&Livebook.Runtime.ErlDist.LoggerGLHandler.filter_code_server_logs/2, nil}
]
})
else
Logger.add_backend(Livebook.Runtime.ErlDist.LoggerGLBackend)
end
:logger.add_handler(:livebook_gl_handler, Livebook.Runtime.ErlDist.LoggerGLHandler, %{
formatter: Logger.Formatter.new(),
filters: [
code_server_logs:
{&Livebook.Runtime.ErlDist.LoggerGLHandler.filter_code_server_logs/2, nil}
]
})
# Set `ignore_module_conflict` only for the NodeManager lifetime.
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.register(state.original_standard_error, :standard_error)
# TODO: remove logger backend once we require Elixir v1.15
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
:logger.remove_handler(:livebook_gl_handler)
if state.unload_modules_on_termination do
ErlDist.unload_required_modules()

View file

@ -387,7 +387,7 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do
def handle_info({:orphan_log, output}, state) do
with %{} = evaluator <- state.last_evaluator,
{: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
{:noreply, state}

View file

@ -613,7 +613,7 @@ defmodule Livebook.Runtime.Evaluator do
defp eval(:elixir, code, binding, env) do
{{result, extra_diagnostics}, diagnostics} =
with_diagnostics([log: true], fn ->
Code.with_diagnostics([log: true], fn ->
try do
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))
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?(%TokenMissingError{}), do: true
@ -847,11 +836,6 @@ defmodule Livebook.Runtime.Evaluator do
do: {:variable, var},
into: identifiers_used
identifiers_used =
for var <- tracer_info.undefined_vars,
do: {:variable, var},
into: identifiers_used
identifiers_defined =
for var <- vars_defined(context, prev_context),
do: {{:variable, var}, context.id},

View file

@ -92,25 +92,11 @@ defmodule Livebook.Runtime.Evaluator.Doctests do
defp report_doctest_result(%{state: {:failed, failure}} = test, lines) do
doctest_line = test.tags.doctest_line
[prompt_line | _] = lines = Enum.drop(lines, doctest_line - 1)
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 = test.tags[:doctest_data][:end_line]
end_line =
with {:error, %ExUnit.AssertionError{}, [{_, _, _, location} | _]} <- failure,
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
end_line -
length(
@ -148,17 +134,6 @@ defmodule Livebook.Runtime.Evaluator.Doctests do
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
id =
doctests_specs

View file

@ -14,8 +14,7 @@ defmodule Livebook.Runtime.Evaluator.Tracer do
requires_used: MapSet.new(),
requires_defined: MapSet.new(),
imports_used?: false,
imports_defined?: false,
undefined_vars: MapSet.new()
imports_defined?: false
@type t :: %__MODULE__{
modules_used: MapSet.t(),
@ -25,8 +24,7 @@ defmodule Livebook.Runtime.Evaluator.Tracer do
requires_used: MapSet.t(),
requires_defined: MapSet.t(),
imports_used?: boolean(),
imports_defined?: boolean(),
undefined_vars: MapSet.t()
imports_defined?: boolean()
}
@doc false
@ -62,13 +60,11 @@ defmodule Livebook.Runtime.Evaluator.Tracer do
if(env.module, do: [], else: [:import_defined]) ++
[{:module_used, module}, {:alias_used, module}]
{:imported_function, meta, module, name, _arity} ->
var? = Keyword.has_key?(meta, :if_undefined)
[{:module_used, module}, {:import_used, name, var?}]
{:imported_function, _meta, module, _name, _arity} ->
[{:module_used, module}, :import_used]
{:imported_macro, meta, module, name, _arity} ->
var? = Keyword.has_key?(meta, :if_undefined)
[{:module_used, module}, {:import_used, name, var?}]
{:imported_macro, _meta, module, _name, _arity} ->
[{:module_used, module}, :import_used]
{:alias, _meta, alias, as, _opts} ->
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))
end
defp apply_update(info, {:import_used, name, var?}) do
info = put_in(info.imports_used?, true)
if var? do
update_in(info.undefined_vars, &MapSet.put(&1, {name, nil}))
else
info
end
defp apply_update(info, :import_used) do
put_in(info.imports_used?, true)
end
defp apply_update(info, :import_defined) do

View file

@ -834,22 +834,6 @@ defmodule Livebook.Runtime.EvaluatorTest do
assert {:variable, {:z, nil}} not in identifiers.used
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
identifiers =
"""