Skip to content

Commit

Permalink
Allow watchers for the active project (#42731)
Browse files Browse the repository at this point in the history
This allows packages (with Revise as the intended target) to register
themselves to be notified when the active project changes.
This will allow Revise to keep track of the appropriate source for
non-default environments.

For practical effect, Pkg (and perhaps packages like Pluto) will have
to switch from setting `Base.ACTIVE_PROJECT[]` directly to calling
`Base.set_active_project`.

Co-authored by: Jameson Nash <jameson@juliacomputing.com>
  • Loading branch information
timholy committed Dec 4, 2021
1 parent ca3fbf1 commit 83d7eba
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 9 deletions.
28 changes: 25 additions & 3 deletions base/initdefs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,11 @@ See also
const LOAD_PATH = copy(DEFAULT_LOAD_PATH)
# HOME_PROJECT is no longer used, here just to avoid breaking things
const HOME_PROJECT = Ref{Union{String,Nothing}}(nothing)
const ACTIVE_PROJECT = Ref{Union{String,Nothing}}(nothing)
const ACTIVE_PROJECT = Ref{Union{String,Nothing}}(nothing) # Modify this only via `Base.set_active_project(proj)`
## Watchers for when the active project changes (e.g., Revise)
# Each should be a thunk, i.e., `f()`. To determine the current active project,
# the thunk can query `Base.active_project()`.
const active_project_callbacks = []

function current_project(dir::AbstractString)
# look for project file in current dir and parents
Expand Down Expand Up @@ -231,10 +235,11 @@ function init_active_project()
project = (JLOptions().project != C_NULL ?
unsafe_string(Base.JLOptions().project) :
get(ENV, "JULIA_PROJECT", nothing))
ACTIVE_PROJECT[] =
set_active_project(
project === nothing ? nothing :
project == "" ? nothing :
startswith(project, "@") ? load_path_expand(project) : abspath(expanduser(project))
)
end

## load path expansion: turn LOAD_PATH entries into concrete paths ##
Expand Down Expand Up @@ -280,7 +285,7 @@ load_path_expand(::Nothing) = nothing
"""
active_project()
Return the path of the active `Project.toml` file.
Return the path of the active `Project.toml` file. See also [`Base.set_active_project`](@ref).
"""
function active_project(search_load_path::Bool=true)
for project in (ACTIVE_PROJECT[],)
Expand All @@ -306,6 +311,23 @@ function active_project(search_load_path::Bool=true)
end
end

"""
set_active_project(projfile::Union{AbstractString,Nothing})
Set the active `Project.toml` file to `projfile`. See also [`Base.active_project`](@ref).
"""
function set_active_project(projfile::Union{AbstractString,Nothing})
ACTIVE_PROJECT[] = projfile
for f in active_project_callbacks
try
Base.invokelatest(f)
catch
@error "active project callback $f failed" maxlog=1
end
end
end


"""
load_path()
Expand Down
4 changes: 2 additions & 2 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1345,7 +1345,7 @@ function load_path_setup_code(load_path::Bool=true)
code *= """
append!(empty!(Base.LOAD_PATH), $(repr(load_path)))
ENV["JULIA_LOAD_PATH"] = $(repr(join(load_path, Sys.iswindows() ? ';' : ':')))
Base.ACTIVE_PROJECT[] = nothing
Base.set_active_project(nothing)
"""
end
return code
Expand All @@ -1358,7 +1358,7 @@ function include_package_for_output(pkg::PkgId, input::String, depot_path::Vecto
append!(empty!(Base.DL_LOAD_PATH), dl_load_path)
append!(empty!(Base.LOAD_PATH), load_path)
ENV["JULIA_LOAD_PATH"] = join(load_path, Sys.iswindows() ? ';' : ':')
Base.ACTIVE_PROJECT[] = nothing
set_active_project(nothing)
Base._track_dependencies[] = true
get!(Base.PkgOrigin, Base.pkgorigins, pkg).path = input
append!(empty!(Base._concrete_dependencies), concrete_deps)
Expand Down
1 change: 1 addition & 0 deletions doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Base.methods
Base.@show
ans
Base.active_project
Base.set_active_project
```

## [Keywords](@id Keywords)
Expand Down
15 changes: 11 additions & 4 deletions test/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,15 @@ end
saved_load_path = copy(LOAD_PATH)
saved_depot_path = copy(DEPOT_PATH)
saved_active_project = Base.ACTIVE_PROJECT[]
watcher_counter = Ref(0)
push!(Base.active_project_callbacks, () -> watcher_counter[] += 1)
push!(Base.active_project_callbacks, () -> error("broken"))

push!(empty!(LOAD_PATH), joinpath(@__DIR__, "project"))
append!(empty!(DEPOT_PATH), [mktempdir(), joinpath(@__DIR__, "depot")])
Base.ACTIVE_PROJECT[] = nothing
@test watcher_counter[] == 0
@test_logs (:error, r"active project callback .* failed") Base.set_active_project(nothing)
@test watcher_counter[] == 1

@test load_path() == [joinpath(@__DIR__, "project", "Project.toml")]

Expand Down Expand Up @@ -598,10 +603,10 @@ end == "opening file $(repr(joinpath(@__DIR__, "notarealfile.jl")))"
old_act_proj = Base.ACTIVE_PROJECT[]
pushfirst!(LOAD_PATH, "@")
try
Base.ACTIVE_PROJECT[] = joinpath(@__DIR__, "TestPkg")
Base.set_active_project(joinpath(@__DIR__, "TestPkg"))
@eval using TestPkg
finally
Base.ACTIVE_PROJECT[] = old_act_proj
Base.set_active_project(old_act_proj)
popfirst!(LOAD_PATH)
end

Expand Down Expand Up @@ -684,7 +689,9 @@ end

append!(empty!(LOAD_PATH), saved_load_path)
append!(empty!(DEPOT_PATH), saved_depot_path)
Base.ACTIVE_PROJECT[] = saved_active_project
for _ = 1:2 pop!(Base.active_project_callbacks) end
Base.set_active_project(saved_active_project)
@test watcher_counter[] == 3

# issue #28190
module Foo; import Libdl; end
Expand Down

4 comments on commit 83d7eba

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executing the daily package evaluation, I will reply here when finished:

@nanosoldier runtests(ALL, isdaily = true)

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something went wrong when running your job:

NanosoldierError: failed to run tests: TaskFailedException

    nested task error: IOError: mktempdir("/tmp"): no space left on device (ENOSPC)
    Stacktrace:
     [1] uv_error
       @ ./libuv.jl:97 [inlined]
     [2] #mktempdir#20
       @ ./file.jl:682
     [3] mktempdir
       @ ./file.jl:668 [inlined]
     [4] build_executor_command
       @ /storage/pkgeval/depot/packages/Sandbox/aUsDe/src/UserNamespaces.jl:145
     [5] #run#64
       @ /storage/pkgeval/depot/packages/Sandbox/aUsDe/src/Sandbox.jl:123
     [6] #run_sandboxed_julia#37
       @ /storage/pkgeval/depot/packages/PkgEval/a8wK1/src/run.jl:61
     [7] #run_sandboxed_test#45
       @ /storage/pkgeval/depot/packages/PkgEval/a8wK1/src/run.jl:254
     [8] macro expansion
       @ /storage/pkgeval/depot/packages/PkgEval/a8wK1/src/run.jl:585 [inlined]
     [9] #61
       @ ./task.jl:411

Logs and partial data can be found here
cc @maleadt

@DilumAluthge
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maleadt @vtjnash @staticfloat ^

This is the second day that I've seen this error.

@maleadt
Copy link
Member

@maleadt maleadt commented on 83d7eba Dec 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm cleaning up /tmp, it should work again. Will look into the actual problem later.

Please sign in to comment.