Is there a way to get all method definitions in a Module? I tried names(mod;all=true,imported=true) but going this way still doesn’t catch methods extending existing functions using this style: function Base.push!(c::MyContainer,x) etc.
Disclaimer: I do some analysis of Julia code, so I hope this category is appropriate for this question
@algunion thank you! I think it doesn’t quite cut it.
@info module_methods[push!]
This assumes you know which function you’re interested in (push! in this example). I don’t know that. I only have a Module object. And using only it as the input, I want to get all method definitions inside it.
Generally, you approach is similar to what I’ve been doing so far, and, as I said above, I don’t see a way to catch method definitions for functions created elsewhere (e.g. Base.push!) if you don’t know in advance which functions were extended in this module.
My code doesn’t assume anything about the push! - you can replace Base with your Module and the module_methods will result in a Dict mapping function => methods for all the functions in your Module.
I explained above why I wanted to show you the push! methods.
I get it now - you want to get all the methods from all the modules that are accessible from inside your Module.
You just need to run it in a recursive way - get the names from your module (Base will be included) and then call all_methods on all modules that show up in your names(Module).
No, I don’t want all methods of all accessible modules. Only methods defined in this module. The example module M above defines exactly one method. And I want a way to get exactly that (I don’t mind couple extra special methods like eval and include but not “all methods of all accessible modules”!) .
Also, it’s not clear to me how you mean to get modules on which the given module depend. The names function doesn’t give you that info.
You can at least inspect method definitions given the names and ignore the names with no definitions. But since :identity is missing, we don’t see that definition.
julia> [methods(getproperty(Example, name), Example) for name in names(Example; all = true, imported = true)]
9-element Vector{Base.MethodList}:
# 0 methods for type constructor
# 0 methods for type constructor
# 0 methods for type constructor
# 1 method for generic function "+":
[1] +(::typeof(Main.Example.f), ::typeof(Main.Example.f)) in Main.Example at REPL[1]:6
# 0 methods for generic function "-"
# 0 methods for callable object
# 1 method for generic function "eval":
[1] eval(x) in Main.Example at REPL[1]:1
# 1 method for generic function "f":
[1] f() in Main.Example at REPL[1]:3
# 2 methods for generic function "include":
[1] include(mapexpr::Function, x) in Main.Example at REPL[1]:1
[2] include(x) in Main.Example at REPL[1]:1
Some additional context: it was the same approach as I initially proposed. But there is a field module available for Method. So I just used that to test if the method was defined in your module (parent in this context).
This is a big chunk of code - I would suggest breaking it into multiple functions to clarify things.
which can identify the method definition as occurring in Example:
julia> parentmodule(identity)
Base
julia> @which identity(1)
identity(x) in Base at operators.jl:526
julia> @which identity(Example.f)
identity(::typeof(Main.Example.f)) in Main.Example at REPL[1]:4
I believe ulysses wants to find this information from Example alone.
julia> all_methods(Example,Example)
ERROR: UndefVarError: f not defined
Stacktrace:
[1] top-level scope
@ :0
[2] eval
@ ./boot.jl:368 [inlined]
[3] eval
@ ./client.jl:478 [inlined]
[4] #4
@ ./REPL[2]:3 [inlined]
[5] filter(f::var"#4#8", a::Vector{Symbol})
@ Base ./array.jl:2539
[6] all_methods(parent::Module, target::Module; rec::Bool)
@ Main ./REPL[2]:3
[7] all_methods(parent::Module, target::Module)
@ Main ./REPL[2]:1
[8] top-level scope
I believe this is the issue:
julia> names(Example, all=false)
2-element Vector{Symbol}:
:Example
:f
julia> eval(:f)
ERROR: UndefVarError: f not defined
Maybe try getproperty(target, x) instead of eval(x)? You could narrow it down to the module with eval(target, x), but I’d rather access something I know is already there instead of evaluating an expression.
I don’t want to do names(Base) – it defines a lot of stuff that I have no interest in.
Your recursive solution doesn’t work either: it traverses only submodules of a given module; e.g. when you do module M; module N; ... end; #= N =# end; # M) it will find N, but it will not find modules M makes use of (like Base).
For now - the only feasible way I can see it working is this:
get all the defined functions (even from Base - because when you do Base.push!(...) = ... you only associate the new method with your caller module, but the function name is still defined in Base - and to get the methods for your function, you actually need that function name - and that is part of Base).
get the methods for each function
filter the methods to check which one meets a certain condition (in this case method.module indicating in which module the method was defined)
However, since you clearly stated that you want to avoid something like names(Base), I don’t think there is value in fixing the function above to work from outside modules.
If you still want to give it a try - you can execute it from inside the module of interest. It should work.
I wish you luck in finding a solution that satisfies the restriction related to avoiding names(Base).
An alternative way to perform your analysis is to parse the code and inspect the Expr objects.
This will not cover cases where you had some dynamically generated functions at runtime - but if you are interested only in the source code, the parsing method + Expr analysis will do the job.