Add Infiltrator.jl to Base?

I know that folks often ask to add things to Base here on Discourse, and that usually the correct answer is “no”, but I think there’s a very good reason to add Infiltrator.jl to Base Julia. Let me explain.

Infiltrator.jl is a very useful and performant tool for debugging. However, it has one main drawback. If you want to debug code in a package, you have to add Infiltrator as a project dependency. So whenever I want to use @infiltrate, I have to do ] add Infiltrator and then add using Infiltrator to my package code. And then when I’m done debugging, I have to remember to undo those by deleting using Infiltrator and running ] rm Infiltrator.

If Infiltrator.jl was in Base, then we wouldn’t need to do those things. We would always have a good debugging tool close at hand. Also, Infiltrator.jl is only 511 source lines of code and has no dependencies other than base + stdlib Julia, so it would not be a very large addition to the Julia code base.

2 Likes

I share this concern, but it’s not too hard to write Main.@infiltrate instead of just @infiltrate.

3 Likes

Ohhh, does that work? I was wondering if there was a better way to do this. I guess I should have asked on Discourse instead of Stack Overflow, which I did 22 days ago:

Hmm, I tried using Main.@infiltrate in my code and I’m getting the error below. Any advice?

julia> using Infiltrator

julia> using PackageA
[ Info: Precompiling PackageA [b7d5e62f-924e-4906-b2b5-430ba4531d91]
ERROR: LoadError: UndefVarError: @infiltrate not defined
Stacktrace:
  [1] #macroexpand#51
    @ ./expr.jl:115 [inlined]
  [2] macroexpand
    @ ./expr.jl:114 [inlined]
  [3] docm(source::LineNumberNode, mod::Module, meta::Any, ex::Any, define::Bool) (repeats 2 times)
    @ Base.Docs ./docs/Docs.jl:537
  [4] (::DocStringExtensions.var"#32#33"{typeof(DocStringExtensions.template_hook)})(::LineNumberNode, ::Vararg{Any})
    @ DocStringExtensions ~/.julia/packages/DocStringExtensions/iscC8/src/templates.jl:11
  [5] var"@doc"(::LineNumberNode, ::Module, ::String, ::Vararg{Any})
    @ Core ./boot.jl:517
  [6] include(mod::Module, _path::String)
    @ Base ./Base.jl:418
  [7] include(x::String)
    @ PackageA ~/projects/PackageA/src/PackageA.jl:1
  [8] top-level scope
    @ ~/projects/PackageA/src/PackageA.jl:12
  [9] include
    @ ./Base.jl:418 [inlined]
 [10] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt64}}, source::Nothing)
    @ Base ./loading.jl:1318
 [11] top-level scope
    @ none:1
 [12] eval
    @ ./boot.jl:373 [inlined]
 [13] eval(x::Expr)
    @ Base.MainInclude ./client.jl:453
 [14] top-level scope
    @ none:1

That shouldn’t be fatal, it just means your package wasn’t fully precompiled

Did you try adding Infiltrator in your default Julia environment? I think that makes it implicitly available in other environments as part of the environment stack in LOAD_PATH?

Then you should be able to do using Infiltrator within your package — it’ll emit some warnings about undeclared dependencies but otherwise work.

That said, I do kinda wish Infiltrator or something like it was part of the REPL stdlib. If there was a macro @REPL to spawn a REPL inside an arbitrary scope that would be pretty great.

13 Likes

Just wrote some (minimal) documentation for this:
https://juliadebug.github.io/Infiltrator.jl/stable/API/#Infiltration-and-exfiltration

There’s also a precompile-safe function form now, which you can use like this:

if isdefined(Main, :Infiltrator)
  Main.infiltrate(@__MODULE__, Base.@locals, @__FILE__, @__LINE__)
end
5 Likes

An older thread has additional details / tips that might be helpful for keeping Infiltrator out of your dependencies:

1 Like