From 193e1b839973eb6d1013de58861b5ec07cf9a380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Tue, 29 Oct 2024 13:56:54 +0100 Subject: [PATCH] Some enhacements to `is_loaded_directly` (#1880) --- src/utils.jl | 42 ++++++++++++++++++++++++----- test/utils/Banners/ModA/src/ModA.jl | 1 + test/utils/Banners/ModB/src/ModB.jl | 1 + test/utils/Banners/banners.jl | 32 +++++++++++----------- 4 files changed, 54 insertions(+), 22 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index 50cc4ce0ae..11445083ee 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,6 +1,7 @@ function is_loaded_directly() try @static if VERSION < v"1.11.0-" + @debug "is_loaded_directly: VERSION < 1.11.0-" # Check if were loaded from another package # if VERSION < 1.7.*, only the "other" package will have the # _tryrequire_from_serialized in the backtrace. @@ -12,22 +13,51 @@ function is_loaded_directly() # or one with four arguments (hence five as the function name is the first argument) # 'using Package' serialized will have a version with less arguments bt = Base.process_backtrace(Base.backtrace()) + @debug "is_loaded_directly: full backtrace:\n$(sprint(show, "text/plain", bt))" Base.filter!(sf -> sf[1].func === :_tryrequire_from_serialized, bt) - return length(bt) == 0 || - (length(bt) == 1 && length(only(bt)[1].linfo.specTypes.parameters) < 4) + length_bt = length(bt) + @debug "is_loaded_directly: `_tryrequire_from_serialized` appears $(length_bt) times in backtrace" + length_bt == 0 && return true + length_bt != 1 && return false + params = only(bt)[1].linfo.specTypes.parameters + @debug "is_loaded_directly: `_tryrequire_from_serialized` gets called with parameters of type $(params)" + return length(params) < 4 else + @debug "is_loaded_directly: VERSION >= 1.11.0-" # Starting with julia 1.11, the package loading was completely revamped. # The only difference in the callstack is the line number of the call to _include_from_serialized # inside of the _require_search_from_serialized function. # To make it a bit more robust, we check the difference between the line number of the beginning # of _require_search_from_serialized and the call to _include_from_serialized. - # For `using OtherPackage`, the difference is 61, while for `using Package`, the difference is 75 or 78 - # (on all 1.11 pre-releases up to 1.11.0-rc1 and 1.12.0-DEV.896, which are the newest at the time of writing this). - # for 1.12.0-DEV.1322 the differences are 72 and 88 bt = Base.process_backtrace(Base.backtrace()) + @debug "is_loaded_directly: full backtrace:\n$(sprint(show, "text/plain", bt))" Base.filter!(sf -> contains(string(sf[1].func), "_require_search_from_serialized"), bt) + length_bt = length(bt) + @debug "is_loaded_directly: `_require_search_from_serialized` appears $(length_bt) times in backtrace; expected 1" bt_entry = only(bt)[1] - return bt_entry.line - bt_entry.linfo.def.line >= 73 + line_call = bt_entry.line + line_funcbegin = bt_entry.linfo.def.line + line_difference = line_call - line_funcbegin + @debug "is_loaded_directly: `_require_search_from_serialized` called at line $line_call, function begins at line $line_funcbegin, difference $(line_difference)" + # difference for `using Package` / `using OtherPackage` + # 1.11.0-alpha1: 75 / 61 + # 1.11.0-alpha2: 75 / 61 + # 1.11.0-beta1: 75 / 61 + # 1.11.0-beta2: 75 / 61 + # 1.11.0-rc1: 75 / 61 + # 1.11.0-rc2: 78 / 61 + # 1.11.0-rc3: 78 / 61 + # 1.11.0-rc4: 77 / 61 + # 1.11.0: 77 / 61 + # 1.11.1: 77 / 61 + # 1.12.0-DEV.896: 78 / 61 # ignored + # 1.12.0-DEV.1322: 88 / 72 # ignored + # 1.12.0-DEV.1506: 106 / 93 + @static if v"1.11.0-" < VERSION < v"1.12.0-DEV.1506" + return line_difference >= 73 + else # v"1.12.0-DEV.1506" <= VERSION + return line_difference >= 100 + end end catch e @debug "Error while checking if loaded directly" exception=(e, Base.catch_backtrace()) diff --git a/test/utils/Banners/ModA/src/ModA.jl b/test/utils/Banners/ModA/src/ModA.jl index 090a23af96..014a917a7d 100644 --- a/test/utils/Banners/ModA/src/ModA.jl +++ b/test/utils/Banners/ModA/src/ModA.jl @@ -3,6 +3,7 @@ module ModA import AbstractAlgebra: should_show_banner function __init__() + @debug "__init__ of ModA" if should_show_banner() println("Banner of ModA") end diff --git a/test/utils/Banners/ModB/src/ModB.jl b/test/utils/Banners/ModB/src/ModB.jl index 782558158d..ee9a108378 100644 --- a/test/utils/Banners/ModB/src/ModB.jl +++ b/test/utils/Banners/ModB/src/ModB.jl @@ -5,6 +5,7 @@ import AbstractAlgebra: should_show_banner using ModA function __init__() + @debug "__init__ of ModB" if should_show_banner() println("Banner of ModB") end diff --git a/test/utils/Banners/banners.jl b/test/utils/Banners/banners.jl index 83b60f6a32..df811fd0b6 100644 --- a/test/utils/Banners/banners.jl +++ b/test/utils/Banners/banners.jl @@ -1,17 +1,17 @@ -using Pkg +using Pkg, Test @testset "Banners" begin function run_repl_code(code::String, proj::String) bin = Base.julia_cmd() - opts = ["--project=$proj", "-i", "-e", "$code; exit();"] + opts = ["--startup-file=no", "--project=$proj", "-i", "-e", "$code; exit();"] cmd = Cmd(`$bin $opts`, ignorestatus=true) outs = IOBuffer() errs = IOBuffer() - proc = run(pipeline(`$cmd`, stderr=errs, stdout=outs)) - result = String(take!(outs)) + proc = run(pipeline(addenv(`$cmd`, "JULIA_DEBUG" => "AbstractAlgebra,ModA,ModB"), stderr=errs, stdout=outs)) + out = String(take!(outs)) err = String(take!(errs)) - return result, err, proc.exitcode + return out, err, proc.exitcode end # Set up a separate temporary project for some modules that depend on each @@ -33,27 +33,27 @@ using Pkg Pkg.develop(path=raw"$modcdir"); Pkg.precompile(); """ - out,err,exitcode = run_repl_code(code, td) + out, err, exitcode = run_repl_code(code, td) res = @test exitcode == 0 if res isa Test.Fail - println("out\n$out") - println("err\n$err") + println("OUT:\n$out") + println("ERR:\n$err") end # Banner of ModA shows out, err = run_repl_code("using ModA;", td) res = @test strip(out) == "Banner of ModA" if res isa Test.Fail - println("out\n$out") - println("err\n$err") + println("OUT:\n$out") + println("ERR:\n$err") end # Banner of ModB shows, but ModA is supressed out, err = run_repl_code("using ModB;", td) res = @test strip(out) == "Banner of ModB" if res isa Test.Fail - println("out\n$out") - println("err\n$err") + println("OUT:\n$out") + println("ERR:\n$err") end # Banner of ModB shows, but ModA is supressed, even if ModA is specifically @@ -61,15 +61,15 @@ using Pkg out, err = run_repl_code("using ModB; using ModA;", td) res = @test strip(out) == "Banner of ModB" if res isa Test.Fail - println("out\n$out") - println("err\n$err") + println("OUT:\n$out") + println("ERR:\n$err") end # Banner does not show when our module is a dependency out, err = run_repl_code("using ModC;", td) res = @test strip(out) == "" if res isa Test.Fail - println("out\n$out") - println("err\n$err") + println("OUT:\n$out") + println("ERR:\n$err") end end