I am trying to get an error message of a caught exception for use in a warning.
For example, while following
julia> a = 3+b
ERROR: UndefVarError: b not defined
Stacktrace:
[1] top-level scope
# REPL[1]:1
says that the problem is that b is not defined, the following method of handling exception has much less helpful message:
julia> try
a = 3+b
catch e
#warn "Bad thing happend: $e"
end
┌ Warning: Bad thing happend: UndefVarError(:b)
└ # Main REPL[2]:4
How do I get the error message and stacktrace from the exception as String?
While a bit wordy, the answer seems to be by using the Base.showerror(io::IO, err::MyException) function defined for builtin exceptions:
julia> try
a = 3+b
catch e
io = IOBuffer();
showerror(io, e)
error_msg = String(take!(io))
#warn "Trouble doing things:\n$(error_msg)"
end
┌ Warning: Trouble doing things:
│ UndefVarError: b not defined
└ # Main REPL[3]:7
To also get the stacktrace:
use stacktrace(catch_backtrace()) to get the stacktrace vector from the place exception was thrown (see docs)
use show(io, mime, x) to format the vector nicely (see this SO answer)
Combining the two with the sprint that handles the IOBuffer for us, we get to:
julia> try
a = 3+b
catch e
error_msg = sprint(showerror, e)
st = sprint((io,v) -> show(io, "text/plain", v), stacktrace(catch_backtrace()))
#warn "Trouble doing things:\n$(error_msg)\n$(st)"
end
┌ Warning: Trouble doing things:
│ UndefVarError: b not defined
│ 13-element Vector{Base.StackTraces.StackFrame}:
│ top-level scope at REPL[4]:2
│ eval at boot.jl:373 [inlined]
│ ...
└ # Main REPL[4]:6
I am trying to understand process communication in erlang. Here I have a master process and five friends process. If a friend sends a message to any of the other 5 they have to reply back. But the master should be aware of all this. I am pasting the code below.
-module(prog).
-import(lists,[append/2,concat/1]).
-import(maps,[from_lists/1,find/2,get/2,update/3]).
-import(string,[equal/2]).
-import(file,[consult/1]).
-export([create_process/1,friends/4, master/1, main/0,prnt/1]).
%% CREATE PROCESS
create_process([])->ok;
create_process([H|T])->
{A,B} = H,
Pid = spawn(prog,friends,[B,self(),0,A]),
register(A,Pid),
create_process(T).
%% FRIENDS PROCESS
friends(Msg, M_pid, State, Self_name)->
S = lists:concat([Self_name," state =",State,"\n"]),
io:fwrite(S),
if
State == 0 ->
timer:sleep(500),
io:fwrite("~p~n",[Self_name]),
lists:foreach(fun(X) -> whereis(X)!{Self_name,"intro",self()} end, Msg),
friends(Msg, M_pid, State + 1, Self_name);
State > 0 ->
receive
{Process_name, Process_msg, Process_id} ->
I = equal(Process_msg,"intro"),
R = equal(Process_msg,"reply"),
XxX = lists:concat([Self_name," recieved ",Process_msg," from ",Process_name,"\n"]),
io:fwrite(XxX),
if
I == true ->
io:fwrite("~p~n",[whereis(Process_name)]),
M_pid!{lists:concat([Self_name," received intro message from ", Process_name , "[",Process_id,"]"]), self()},
io:fwrite(I),
whereis(Process_name)!{Self_name, "reply",self()},
friends(Msg, M_pid, State + 1, Self_name);
R == true ->
M_pid!{lists:concat([Self_name," received reply message from ", Process_name , "[",Process_id,"]"]), self()},
io:fwrite(R),
friends(Msg, M_pid, State + 1, Self_name)
end
after
1000->
io:fwrite(lists:concat([Self_name," has received no calls for 1 second, ending..."]))
end
end.
master(State)->
receive
{Process_message, Process_id} ->
io:fwrite(Process_message),
master(State+1)
after
2000->
ok
end.
main() ->
B = [{john, [jill,joe,bob]},
{jill, [bob,joe,bob]},
{sue, [jill,jill,jill,bob,jill]},
{bob, [john]},
{joe, [sue]}],
create_process(B),
io:fwrite("~p~n",[whereis(sue)]),
master(0).
I think the line in friends() function,
M_pid!{lists:concat([Self_name," received intro message from ", Process_name , "[",Process_id,"]"]), self()}
is the cause of error but I cannot understand why. M_pid is known and I am concatenating all the info and sending it to master but I am confused why it isnt working.
The error I am getting is as follows:
Error in process <0.55.0> with exit value: {function_clause,[{lists,thing_to_list,
[<0.54.0>],
[{file,"lists.erl"},{line,603}]},
{lists,flatmap,2,[{file,"lists.erl"},{line,1250}]},
{lists,flatmap,2,[{file,"lists.erl"},{line,1250}]},
{prog,friends,4,[{file,"prog.erl"},{line,45}]}]}
I dont know what is causing the error. Sorry for asking noob questions and thanks for your help.
An example of what Dogbert discovered:
-module(my).
-compile(export_all).
go() ->
Pid = spawn(my, nothing, []),
lists:concat(["hello", Pid]).
nothing() -> nothing.
In the shell:
2> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
3> my:go().
** exception error: no function clause matching
lists:thing_to_list(<0.75.0>) (lists.erl, line 603)
in function lists:flatmap/2 (lists.erl, line 1250)
in call from lists:flatmap/2 (lists.erl, line 1250)
4>
But:
-module(my).
-compile(export_all).
go() ->
Pid = spawn(my, nothing, []),
lists:concat(["hello", pid_to_list(Pid)]).
nothing() -> nothing.
In the shell:
4> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
5> my:go().
"hello<0.83.0>"
From the erl docs:
concat(Things) -> string()
Things = [Thing]
Thing = atom() | integer() | float() | string()
The list that you feed concat() must contain either atoms, integers, floats, or strings. A pid is neither an atom, integer, float, nor string, so a pid cannot be used with concat(). However, pid_to_list() returns a string:
pid_to_list(Pid) -> string()
Pid = pid()
As you can see, a pid has its own type: pid().
I ran your code.
Where you went wrong was to pass Process_id(which is of type pid()) to lists:concat/1.
Let us try to understand this error:
{function_clause,[{lists,thing_to_list,
[<0.84.0>],
[{file,"lists.erl"},{line,603}]},
{lists,flatmap,2,[{file,"lists.erl"},{line,1250}]},
{lists,flatmap,2,[{file,"lists.erl"},{line,1250}]},
{prog,friends,4,[{file,"prog.erl"},{line,39}]}]}
It states the function lists:thing_to_list/1 has no definition(see the word function_clause in the error log) which accepts an argument of type pid() as denoted here by [<0.84.0>].
Strings are represented as lists in erlang, which is why we use lists:concat/1.
As #7stud pointed out these are the valid types which can be passed to lists:concat/1 as per the documentation:
atom() | integer() | float() | string()
There are 2 occurrences of the following line. Fix them and you are good to go:
Incorrect Code:
M_pid!{lists:concat([Self_name," received intro message from ", Process_name , "[",Process_id,"]"]), self()},
Corrected Code
M_pid!{lists:concat([Self_name," received intro message from ", Process_name , "[",pid_to_list(Process_id),"]"]), self()},
Notice the use of the function erlang:pid_to_list/1. As per the documentation the function accepts type pid() and returns it as string().
I have a helper function to use in python repl to move variables to global for easy debugging. But there is a mypy error:
class stepin(object): # pylint: disable=R0903
def __init__(self, func: Callable) -> None:
self.func = func
self.args = func.__code__.co_varnames
if hasattr(func, "__defaults__") and func.__defaults__:
self.defaults = dict(zip(reversed(self.args), reversed(func.__defaults__)))
else:
self.defaults = None
def __call__(self, *args, **kwargs):
result_dict = {x: None for x in self.args}
if self.defaults:
result_dict.update(self.defaults)
result_dict.update(dict(zip(self.args, args)))
result_dict.update(kwargs)
for x in result_dict.keys():
if result_dict[x] is None:
raise ValueError('Missing args: ', self.func.__qualname__, x)
globals().update(result_dict)
Now, the line
if hasattr(func, "__defaults__") and func.__defaults__:
self.defaults = dict(zip(reversed(self.args), reversed(func.__defaults__)))
raises a mypy error that says func has no __defaults__
Now I understand that the BDFL has said he despises the "hasattr" check so it's probably not gonna be solved inside mypy; then my question is, is there a way to change the __init__ typing signature to get rid of the error?
What have I tried: Callable doesn't work, understandable: not all Callables have __defaults__.
But where is the type "function"? If I type() a function it says "function" but "function" is not in preamble or "typing". I see that some people mention "FunctionType" but it's not in "typing" either.
The type of a function is types.FunctionType (in the types module).
If you modify the annotation for func from Callable to types.FunctionType, mypy no longer complains about __defaults__.
I'm trying to make a recursive call to handle_call/3 before replying.
But it seems to not be possible, because a timeout exit exception is thrown.
Below you can see the code and the error.
The code:
-module(test).
-behavior(gen_server).
%% API
-export([start_link/0,init/1,handle_info/2,first_call/1,second_call/1, handle_call/3, terminate/2]).
-record(state, {whatever}).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init(_Args) ->
{ok, #state{}}.
handle_info(Data, State) ->
{noreply, State}.
% synchronous messages
handle_call(_Request, _From, State) ->
case _Request of
first_call ->
{reply, data1, State};
second_call ->
{reply, {data2, first_call(self())}, State}
end.
first_call(Pid) ->
gen_server:call(Pid, first_call).
second_call(Pid) ->
gen_server:call(Pid, second_call).
terminate(_Reason, _State) ->
ok.
The error:
2> {_, PID} = test:start_link().
{ok,<0.64.0>}
3> test:second_call(PID).
** exception exit: {timeout,{gen_server,call,[<0.64.0>,second_call]}}
in function gen_server:call/2 (gen_server.erl, line 204)
A possible reason for this behaviour is that gen_server is not able to handle recursive calls until the first one is done (creating a deadlock). Is this the right reason, and if yes why?
Thanks.
Yes, that is the reason. The gen_server is a single process, and while handle_call is executing it doesn't pick up any messages nor respond to any gen_server:call. Therefore first_call(self()) times out.