From ba26a3295cf3f12cfa52fbed8850cc898ee09824 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Fri, 15 Nov 2019 04:34:59 -0600 Subject: [PATCH 1/2] `methodswith`: support non-exported functions and submodule traversal See #33866 --- .../InteractiveUtils/src/InteractiveUtils.jl | 36 ++++++++++++------- stdlib/InteractiveUtils/test/runtests.jl | 24 +++++++++++++ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/stdlib/InteractiveUtils/src/InteractiveUtils.jl b/stdlib/InteractiveUtils/src/InteractiveUtils.jl index 239e138c7fa03..367843b1b409f 100644 --- a/stdlib/InteractiveUtils/src/InteractiveUtils.jl +++ b/stdlib/InteractiveUtils/src/InteractiveUtils.jl @@ -122,15 +122,19 @@ end # `methodswith` -- shows a list of methods using the type given """ - methodswith(typ[, module or function]; supertypes::Bool=false]) + methodswith(typ[, module or function]; supertypes::Bool=false, all::Bool=false, submodules::Bool=false) Return an array of methods with an argument of type `typ`. -The optional second argument restricts the search to a particular module or function -(the default is all top-level modules). +The optional second argument restricts the search to a particular module or function. +By default, only top-level modules and modules loaded into Main are examined. +Set `submodules=true` if you want to include inner modules. If keyword `supertypes` is `true`, also return arguments with a parent type of `typ`, excluding type `Any`. + +By default, the results include only exported functions. +Set `all=true` if you want to include non-exported functions. """ function methodswith(t::Type, f::Function, meths = Method[]; supertypes::Bool=false) for d in methods(f) @@ -149,27 +153,35 @@ function methodswith(t::Type, f::Function, meths = Method[]; supertypes::Bool=fa return meths end -function _methodswith(t::Type, m::Module, supertypes::Bool) - meths = Method[] - for nm in names(m) +function methodswith!(meths, checking, t::Type, m::Module; + supertypes::Bool=false, all::Bool=false, submodules::Bool=false) + push!(checking, m) # block recursion with submodules (nameof(m) ∈ names(m)) + for nm in names(m; all = all) if isdefined(m, nm) f = getfield(m, nm) if isa(f, Function) methodswith(t, f, meths; supertypes = supertypes) + elseif submodules && isa(f, Module) && f ∉ checking + methodswith!(meths, checking, t, f; supertypes = supertypes, + all = all, submodules = true) end end end - return unique(meths) + return meths end -methodswith(t::Type, m::Module; supertypes::Bool=false) = _methodswith(t, m, supertypes) +function methodswith(t::Type, m::Module; kwargs...) + meths, checking = Set{Method}(), Set{Module}() + methodswith!(meths, checking, t, m; kwargs...) + return collect(meths) +end -function methodswith(t::Type; supertypes::Bool=false) - meths = Method[] +function methodswith(t::Type; kwargs...) + meths, checking = Set{Method}(), Set{Module}() for mod in Base.loaded_modules_array() - append!(meths, _methodswith(t, mod, supertypes)) + methodswith!(meths, checking, t, mod; kwargs...) end - return unique(meths) + return collect(meths) end # subtypes diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index afe63cd0d5949..90cfcf0ab4ba1 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -12,6 +12,30 @@ struct Type4Union end func4union(::Union{Type4Union,Int}) = () @test !isempty(methodswith(Type4Union, @__MODULE__)) +module MWM +export f, SubModule +struct C end +f(::C) = 1 +g(::C) = 1 + +module SubModule +using ..MWM: C +export h +h(::C) = 1 +end + +end + +@test methodswith(MWM.C, MWM) == [which(MWM.f, Tuple{MWM.C})] +@test Set(methodswith(MWM.C, MWM; all=true)) == Set([which(MWM.f, Tuple{MWM.C}), + which(MWM.g, Tuple{MWM.C})]) +@test Set(methodswith(MWM.C, MWM; submodules=true)) == Set([which(MWM.f, Tuple{MWM.C}), + which(MWM.SubModule.h, Tuple{MWM.C})]) +@test Set(methodswith(MWM.C, MWM; all=true, submodules=true)) == + Set([which(MWM.f, Tuple{MWM.C}), + which(MWM.g, Tuple{MWM.C}), + which(MWM.SubModule.h, Tuple{MWM.C})]) + # PR #19964 @test isempty(subtypes(Float64)) From 8a49b8b9624615d2f963d7068d4db7499789dd64 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 16 Nov 2019 09:37:37 -0600 Subject: [PATCH 2/2] `methodswith`: add `subtypes` keyword to match functionality of `methods` See #33866 --- .../InteractiveUtils/src/InteractiveUtils.jl | 23 +++++++++++----- stdlib/InteractiveUtils/test/runtests.jl | 27 +++++++++++++++++++ 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/stdlib/InteractiveUtils/src/InteractiveUtils.jl b/stdlib/InteractiveUtils/src/InteractiveUtils.jl index 367843b1b409f..e4a1dcc032a07 100644 --- a/stdlib/InteractiveUtils/src/InteractiveUtils.jl +++ b/stdlib/InteractiveUtils/src/InteractiveUtils.jl @@ -122,7 +122,7 @@ end # `methodswith` -- shows a list of methods using the type given """ - methodswith(typ[, module or function]; supertypes::Bool=false, all::Bool=false, submodules::Bool=false) + methodswith(typ[, module or function]; supertypes::Bool=false, subtypes::Bool=false, all::Bool=false, submodules::Bool=false) Return an array of methods with an argument of type `typ`. @@ -132,18 +132,28 @@ Set `submodules=true` if you want to include inner modules. If keyword `supertypes` is `true`, also return arguments with a parent type of `typ`, excluding type `Any`. +If keyword `subtypes` is `true`, include methods with arguments that are subtypes of `typ`. +You may not set both to be true. By default, the results include only exported functions. Set `all=true` if you want to include non-exported functions. """ -function methodswith(t::Type, f::Function, meths = Method[]; supertypes::Bool=false) +function methodswith(t::Type, f::Function, meths = Method[]; supertypes::Bool=false, subtypes::Bool=false) + supertypes && subtypes && throw(ArgumentError("supertypes and subtypes cannot both be true")) for d in methods(f) if any(function (x) + if subtypes + let x = unwrap_unionall(x) + isa(x, DataType) && return x <: t + return false + end + end let x = rewrap_unionall(x, d.sig) (type_close_enough(x, t) || + (subtypes ? x <: t : (supertypes ? (t <: x && (!isa(x,TypeVar) || x.ub != Any)) : (isa(x,TypeVar) && x.ub != Any && t == x.ub)) && - x != Any) + x != Any)) end end, unwrap_unionall(d.sig).parameters) @@ -154,16 +164,17 @@ function methodswith(t::Type, f::Function, meths = Method[]; supertypes::Bool=fa end function methodswith!(meths, checking, t::Type, m::Module; - supertypes::Bool=false, all::Bool=false, submodules::Bool=false) + supertypes::Bool=false, subtypes::Bool=false, + all::Bool=false, submodules::Bool=false) push!(checking, m) # block recursion with submodules (nameof(m) ∈ names(m)) for nm in names(m; all = all) if isdefined(m, nm) f = getfield(m, nm) if isa(f, Function) - methodswith(t, f, meths; supertypes = supertypes) + methodswith(t, f, meths; supertypes = supertypes, subtypes = subtypes) elseif submodules && isa(f, Module) && f ∉ checking methodswith!(meths, checking, t, f; supertypes = supertypes, - all = all, submodules = true) + subtypes = subtypes, all = all, submodules = true) end end end diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index 90cfcf0ab4ba1..516f12fea444d 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -15,7 +15,22 @@ func4union(::Union{Type4Union,Int}) = () module MWM export f, SubModule struct C end +abstract type Super end +struct Sub <: Super end +abstract type SuperP{A<:AbstractArray} end +abstract type SubP{A<:AbstractVector} <: SuperP{A} end f(::C) = 1 +f(::Super) = 2 +f(::Sub) = 3 +f(::SuperP) = 4 +f(::SubP) = 5 +f(::SubP{Vector{Int}}) = 6 + +f1(::Vector) = 1 +f2(::Vector{Int}) = 1 +f3(::Vector{I}) where I = 1 +f4(::Vector{<:Integer}) = 1 + g(::C) = 1 module SubModule @@ -36,6 +51,18 @@ end which(MWM.g, Tuple{MWM.C}), which(MWM.SubModule.h, Tuple{MWM.C})]) +@test methodswith(MWM.Super, MWM) == [which(MWM.f, Tuple{MWM.Super})] +@test Set(methodswith(MWM.Super, MWM; subtypes=true)) == Set(methods(MWM.f, Tuple{MWM.Super})) + +@test Set(methodswith(MWM.SuperP, MWM; subtypes=true)) == Set(methods(MWM.f, Tuple{MWM.SuperP})) +@test Set(methodswith(MWM.SubP, MWM; subtypes=true)) == Set(methods(MWM.f, Tuple{MWM.SubP})) +@test Set(methodswith(MWM.SubP{Vector{Int}}, MWM; subtypes=true)) == Set(methods(MWM.f, Tuple{MWM.SubP{Vector{Int}}})) + +for f in (MWM.f1, MWM.f2, MWM.f3, MWM.f4) + @test Set(methodswith(Vector, f; subtypes=true)) == Set(methods(f, Tuple{Vector})) +end + + # PR #19964 @test isempty(subtypes(Float64))