Is there a way to get the file/name/line info for the caller of a Julia function?
I found this way to get some stacktrace info, and if the caller is another function (but not the main context) I get the file:line info:
module pd
global g_bTraceOn = true
export callerOfTraceIt
function callerOfTraceIt()
traceit( "hi" ) ;
end
function traceit( msg )
global g_bTraceOn
if ( g_bTraceOn )
bt = backtrace() ;
s = sprint(io->Base.show_backtrace(io, bt))
println( "debug: $s: $msg" )
end
end
end
using pd
callerOfTraceIt( )
This shows:
$ julia bt.jl
debug:
in traceit at C:\cygwin64\home\Peeter\julia\HarmonicBalance\bt.jl:15
in callerOfTraceIt at C:\cygwin64\home\Peeter\julia\HarmonicBalance\bt.jl:8
in include at boot.jl:245
in include_from_node1 at loading.jl:128
in process_options at client.jl:285
in _start at client.jl:354: hi
I'd really like just the second frame (caller of traceit()), and would also like the function name if it's available.
If you do #show bt inside traceit, you'll discover it's just a list of pointers, each corresponding to a single stack frame. Those stack frames that come from julia code (rather than C) are displayed by show_backtrace.
You can call Profile.lookup(uint(bt[1])) to extract file/function/line information from each element:
julia> Profile.lookup(uint(bt[1]))
LineInfo("rec_backtrace","/home/tim/src/julia-old/usr/bin/../lib/libjulia.so",-1,true,140293228378757)
julia> names(Profile.LineInfo)
5-element Array{Symbol,1}:
:func
:file
:line
:fromC
:ip
You likely want to ignore all elements with fromC == true.
Related
I want to import a function present in a julia file somewhere during runtime
just like in python we have importlib.import_module to import module is there something present in julia
I'm new to julia and I'm not sure how to do that.
I have to import a function main from another julia file and want to run it but I have to also check a condition before that if the condition is true then I want to import the function.
EDIT
I have a file
main.jl
function myMain()
s1 = "Hello"
s2 = "World!"
include("functions/hello.jl")
say(s1, s2)
end
myMain()
hello.jl
function say(s1, s2)
print(s1, s2)
end
Error
ERROR: LoadError: MethodError: no method matching say(::String, ::String)
The applicable method may be too new: running in world age 32378, while current world is 32379.
Closest candidates are:
say(::Any, ::Any) at ~/Desktop/julia_including/functions/hello.jl:1 (method too new to be called from this world context.)
Stacktrace:
[1] myMain()
# Main ~/Desktop/julia_including/main.jl:5
[2] top-level scope
# ~/Desktop/julia_including/main.jl:8
in expression starting at /home/shivansh/Desktop/julia_including/main.jl:8
It works fine when I don't use include inside the myMain() function in main.jl
Julia contrary to Python is a compiled language.
Hence for maximum performance all functions should be known at the compilation time so efficient assembly code can be generated.
Hence generally you want to avoid syntax like the one you are proposing and have the inlude outside of the function.
However, if you really know what you are doing, why you are doing this and need such functionality then comes the world of Julia metaprogramming.
The code is the following:
function myMain()
s1 = "Hello"
s2 = "World!"
include("say.jl")
Base.invokelatest(say, s1, s2)
end
Now you can do:
julia> myMain()
HelloWorld!
Honestly, it just sounds like you wanna do this
if some_condition
include("/path/to/some/file/that/you/need.jl")
else
include("/path/to/some/OTHER/file/that/you/need.jl")
end
EDIT
I think though, after seeing the error that you added, what you wanna do is define your function multiple times instead, and with different argument types:
function say(x::Any, y::Any)
println("Default")
end
function say(x::String, y::Any)
println("String in arg1: $x")
end
function say(x::Any, y::String)
println("String in arg2: $y")
end
function say(x::String, y::String)
println("String in args 1 and 2: $x $y")
end
That way using say differs based on the datatype of the arguments:
julia> say("Foo", "Bar")
String in args 1 and 2: Foo Bar
julia> say("Foo", 2)
String in arg1: Foo
julia> say(0, "Bar")
String in arg2: Bar
and if you have two different functions that act on 2 strings you can do this instead:
struct MySayFunctionType{Symbol}
end
function say(x::String, y::String, ::MySayFunctionType{:Type1})
println("Type1 function is being called: $x $y")
end
function say(x::String, y::String, ::MySayFunctionType{:Type2})
println("Type2 function is being called: $x $y")
end
and then use these like this
if condition
say("Foo", "Bar", MySayFunctionType{Symbol("Type1")}() )
else
say("Foo", "Bar", MySayFunctionType{Symbol("Type2")}() )
end
and if you want a default version of this function to be called -- say Type2 -- you can just add a default value for the third argument like this:
function say(x::String, y::String, z::MySayFunctionType{:Type2}=MySayFunctionType{Symbol("Type2")}())
println("Type2 function is being called: $x $y")
end
The reason this works is because "MySayFunctionType{:Type1}" is considered to be a different type than "MySayFunctionType{:Type2}" so multiple dispatch would consider that as well.
I need to attach an action to the row in the list. The code below displays a window with a list, but the activation of a row (double click on it) leads to an error:
ls = GtkListStore(String, Int)
push!(ls,("Peter",20))
push!(ls,("Paul",30))
push!(ls,("Mary",25))
tv = GtkTreeView(GtkTreeModel(ls))
rTxt = GtkCellRendererText()
c1 = GtkTreeViewColumn("Name", rTxt, Dict([("text",0)]))
c2 = GtkTreeViewColumn("Age", rTxt, Dict([("text",1)]))
push!(tv, c1, c2)
signal_connect(tv_row_activated, tv, "row-activated")
win = GtkWindow(tv, "List View")
showall(win)
function tv_row_activated(w)
println("Works")
end
As the error message says, it is expecting the callback method to be tv_row_activated(::GtkTreeViewLeaf, ::Gtk.GLib.GBoxedUnknown, GtkTreeViewColumnLeaf) i.e. the callback method should accept three arguments. Currently yours accepts a single argument w (mentioned under "Closest candidates" as tv_row_activated(::Any)).
Looking at the signature of "row-activated" in the underlying C Gtk library,
void
row_activated (
GtkTreeView* self,
GtkTreePath* path,
GtkTreeViewColumn* column,
gpointer user_data
)
it seems that the arguments passed to the callback in Julia might be the first three arguments mentioned here (though I'm not familiar enough with the library to say that for sure).
Gtk is attempting to call your tv_row_activated function with three arguments:
An argument of type GtkTreeViewLeaf
An argument of type Gtk.Glib.BoxedUnknown
An argument of type GtkTreeViewColumnLeaf
As defined your function tv_row_activated takes a single argument, w.
Since you appear to be trying to debug or explore, I suggest redefining tv_row_activated to take any number of arguments as follows:
function tv_row_activated(w...)
println("Works")
end
Let's test this in the REPL:
julia> function tv_row_activated(w...)
println(w)
println(typeof.(w))
println("Works")
end
tv_row_activated (generic function with 1 method)
julia> tv_row_activated(1,2,3)
(1, 2, 3)
(Int64, Int64, Int64)
Works
julia> tv_row_activated(nothing)
(nothing,)
(Nothing,)
Works
julia> tv_row_activated(5.0)
(5.0,)
(Float64,)
Works
This is the method that processes an input string
def process(input) do
list=String.split(input, "\n")
f3=fn(a) ->
String.split(a," ")
end
list=Enum.map(list, f3)
func3=fn(n) ->
length(n)==3
end
func2=fn(n) ->
length(n)<=2
end
system=for x <-list, func3.(x), do: x
input=for y <- list, func2.(y), do: y
input=Enum.slice(input,0..length(input)-2)
output=""
output(input,output, system)
end
This is the output function that uses recursion to edit a string and eventually return its value
def output(input, output, system) do
cond do
length(input)==0 ->
output
true ->
[thing|tail]=input
if length(thing)==2 do
output=output<>"From "<>Enum.at(thing, 0)<>" to "<>Enum.at(thing,1)<>" is "<>Integer.to_string(calculate(thing, system))<>"km\n"
output(tail, output, system)
end
if length(thing)==1 do
if Enum.at(thing,0)=="Sun" do
output=output<>"Sun orbits"
output(tail, output, system)
else
output=output<>orbits(thing, system)<>" Sun"
output(tail, output, system)
end
end
output(tail, output, system)
end
end
As you can see when the input is an empty list it should return the output string. Using inspect shows that the output string does indeed have the correct value. Yet when the function is called in process(), it only returns the empty string, or nil.
Any help is appreciated, I am new to elixir so apologies if my code is a bit messy.
This could be a case where using pattern matching in the function head will let you avoid essentially all of the conditionals. You could break this down as:
def output([], message, _) do
message
end
def output([[from, to] | tail], message, system) do
distance = Integer.to_string(calculate(thing, system))
new_message = "#{message}From #{from} to #{to} is #{distance} km\n"
output(tail, new_message, system)
end
def output([["Sun"] | tail], message, system) do
output(tail, "Sun orbits #{message}", system)
end
def output([[thing] | tail], message, system) do
new_message = "#{message}#{orbits([thing], system)} Sun"
output(tail, new_message, system)
end
This gets around some of the difficulties highlighted in the comments: reassigning output inside a block doesn't have an effect, and there aren't non-local returns so after an if ... end block completes and goes on to the next conditional, its result is lost. This will also trap some incorrect inputs, and your process will exit with a pattern-match error if an empty or 3-element list winds up in the input list.
I've renamed the output parameter to the output function to message. This isn't required – the code will work fine whether or not you change it – but I found it a little confusing reading through the function whether output is a function call or a variable.
Some of the parameters to a simulation I am writing are functions. When the output is generated, I want to put the definition of these functional parameters in the output. I have in mind a macro that somehow saves the definition as a string, and then defines it. For example, here's what I do now:
borda_score_fn(p) = exp(1/p)
global g_borda_score_fn_string = "exp(1/p)"
And then I write g_borda_score_fn_string to my output. But this is really ugly!
What I would like to do is something like this:
#paramfn borda_score_fn(p) = exp(1/p)
And later be able to both call borda_score_fn(p), and have the form (i.e., "exp(1/p)") available for writing to my output log. (The string form might get stashed in a global dict, actually, they both could.)
I have tried many version of this, but can't get the right set of parses and calls to get it to work. Any help would be appreciated.
This may be a bit different than what you have in mind, but one perhaps "Julian" approach might be to have the function itself return the form string via multiple dispatch, rather than defining a whole new global variable just for that. For example, say we have a type
struct Form end
that we can use for dispatch, then we can write
borda_score_fn(p) = exp(1/p)
borda_score_fn(::Form) = "exp(1/p)"
which can then be retrieved just by calling the function with our type
julia> borda_score_fn(2)
1.6487212707001282
julia> borda_score_fn(Form())
"exp(1/p)"
That might actually be not bad on its own. But, if you want a macro to do both parts at once, then something along the lines of
macro paramfn(e)
name = esc(e.args[1].args[1])
str = string(e.args[2].args[2])
f = esc(e)
quote
$name(::Form) = $str
$f
end
end
would let you write
julia> #paramfn borda_score_fn(p) = exp(1/p)
borda_score_fn (generic function with 2 methods)
julia> borda_score_fn(1)
2.718281828459045
julia> borda_score_fn(Form())
"exp(1 / p)"
For completeness, here's how you can do it in a way more similar to your original approach, but more idiomatically than with a global variable:
julia> module FormOf
export formof, #paramfn
function formof end
macro paramfn(expr)
name = esc(expr.args[1].args[1])
form_str = string(expr.args[2].args[2])
quote
$(esc(expr))
$FormOf.formof(::typeof($name)) = $form_str
$name
end
end
end
Main.FormOf
julia> FormOf.#paramfn borda_score_fn(p) = exp(1/p)
borda_score_fn (generic function with 1 method)
julia> FormOf.formof(borda_score_fn)
"exp(1 / p)"
However, since it defines a new method of FormOf.formof, this only works in global scope:
julia> function bla()
FormOf.#paramfn fn(p) = exp(1/p)
fn(10) + 1
end
ERROR: syntax: Global method definition around REPL[45]:10 needs to be placed at the top level, or use "eval".
Stacktrace:
[1] top-level scope
# REPL[50]:1
#cbk's solution does not have this limitation.
walk is a recursive function that walks the given tree and if walked over a file do something with it.
The "do something with it" should be changed.
I could use coroutine.yield(f) in walk but I wanted to know my mistake first.
As you see the argument lootfunc is given by a reference and should be called within walk.
But it gives me the error seen below. So why is the parameter lootfunc nil?
local KEYWORDS = {
"%.db[x]?",
"%.ojsn",
}
local function loot(d)
if MATCH == "path" then -- only look to the path not to the content
for i,keyword in pairs(KEYWORDS) do
if string.find(d,keyword) then
--coroutine.yield(d)
print(d)
end
end
end
end
local function walk (path,lootfunc)
for file in lfs.dir(path) do
if file ~= "." and file ~= ".." then
local f = path..'/'..file
local attr = lfs.attributes (f)
if(type(attr) == "table") then
if attr.mode == "directory" then
walk (f) -- next round
elseif attr.mode == "file" then
lootfunc(f)
end
end
end
end
end
walk("/path/",loot)
shadowed.lua:73: attempt to call local 'lootfunc' (a nil value)
stack traceback:
(command line):1: in function 'lootfunc'
shadowed.lua:73: in function 'walk'
shadowed.lua:71: in function 'walk'
shadowed.lua:71: in function 'walk'
shadowed.lua:88: in main chunk
[C]: in function 'dofile'
(command line):1: in function <(command line):1>
[C]: in function 'xpcall'
(command line):1: in main chunk
[C]: ?
You are calling walk(f) in the function walk, there's only one argument, the second argument is filled with nil, so change:
if attr.mode == "directory" then
walk(f) -- next round
to
if attr.mode == "directory" then
walk(f, lootfunc) -- next round