Skip to content

Commit dc27852

Browse files
authored
code loading fixes for cases where wrong version of package is loaded (#46068)
1 parent 7537cb3 commit dc27852

File tree

2 files changed

+114
-35
lines changed

2 files changed

+114
-35
lines changed

base/loading.jl

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -298,11 +298,41 @@ end
298298

299299
# Used by Pkg but not used in loading itself
300300
function find_package(arg)
301-
pkg = identify_package(arg)
302-
pkg === nothing && return nothing
303-
return locate_package(pkg)
301+
pkgenv = identify_package_env(arg)
302+
pkgenv === nothing && return nothing
303+
pkg, env = pkgenv
304+
return locate_package(pkg, env)
304305
end
305306

307+
"""
308+
Base.identify_package_env(name::String)::Union{Tuple{PkgId, String}, Nothing}
309+
Base.identify_package_env(where::Union{Module,PkgId}, name::String)::Union{Tuple{PkgId, String} Nothing}
310+
311+
Same as [`Base.identify_package`](@ref) except that the path to the environment where the package is identified
312+
is also returned.
313+
"""
314+
identify_package_env(where::Module, name::String) = identify_package_env(PkgId(where), name)
315+
function identify_package_env(where::PkgId, name::String)
316+
where.name === name && return where, nothing
317+
where.uuid === nothing && return identify_package_env(name) # ignore `where`
318+
for env in load_path()
319+
pkgid = manifest_deps_get(env, where, name)
320+
pkgid === nothing && continue # not found--keep looking
321+
pkgid.uuid === nothing || return pkgid, env # found in explicit environment--use it
322+
return nothing # found in implicit environment--return "not found"
323+
end
324+
return nothing
325+
end
326+
function identify_package_env(name::String)
327+
for env in load_path()
328+
uuid = project_deps_get(env, name)
329+
uuid === nothing || return uuid, env # found--return it
330+
end
331+
return nothing
332+
end
333+
334+
_nothing_or_first(x) = x === nothing ? nothing : first(x)
335+
306336
"""
307337
Base.identify_package(name::String)::Union{PkgId, Nothing}
308338
Base.identify_package(where::Union{Module,PkgId}, name::String)::Union{PkgId, Nothing}
@@ -329,25 +359,10 @@ julia> Base.identify_package(LinearAlgebra, "Pkg") # Pkg is not a dependency of
329359
330360
````
331361
"""
332-
identify_package(where::Module, name::String) = identify_package(PkgId(where), name)
333-
function identify_package(where::PkgId, name::String)::Union{Nothing,PkgId}
334-
where.name === name && return where
335-
where.uuid === nothing && return identify_package(name) # ignore `where`
336-
for env in load_path()
337-
pkgid = manifest_deps_get(env, where, name)
338-
pkgid === nothing && continue # not found--keep looking
339-
pkgid.uuid === nothing || return pkgid # found in explicit environment--use it
340-
return nothing # found in implicit environment--return "not found"
341-
end
342-
return nothing
343-
end
344-
function identify_package(name::String)::Union{Nothing,PkgId}
345-
for env in load_path()
346-
uuid = project_deps_get(env, name)
347-
uuid === nothing || return uuid # found--return it
348-
end
349-
return nothing
350-
end
362+
identify_package(where::Module, name::String) = _nothing_or_first(identify_package_env(where, name))
363+
identify_package(where::PkgId, name::String) = _nothing_or_first(identify_package_env(where, name))
364+
identify_package(name::String) = _nothing_or_first(identify_package_env(name))
365+
351366

352367
"""
353368
Base.locate_package(pkg::PkgId)::Union{String, Nothing}
@@ -363,7 +378,7 @@ julia> Base.locate_package(pkg)
363378
"/path/to/julia/stdlib/v$(VERSION.major).$(VERSION.minor)/Pkg/src/Pkg.jl"
364379
```
365380
"""
366-
function locate_package(pkg::PkgId)::Union{Nothing,String}
381+
function locate_package(pkg::PkgId, stopenv::Union{String, Nothing}=nothing)::Union{Nothing,String}
367382
if pkg.uuid === nothing
368383
for env in load_path()
369384
# look for the toplevel pkg `pkg.name` in this entry
@@ -377,16 +392,20 @@ function locate_package(pkg::PkgId)::Union{Nothing,String}
377392
return implicit_manifest_uuid_path(env, pkg)
378393
end
379394
end
395+
stopenv == env && return nothing
380396
end
381397
else
382398
for env in load_path()
383399
path = manifest_uuid_path(env, pkg)
400+
# missing is used as a sentinel to stop looking further down in envs
401+
path === missing && return nothing
384402
path === nothing || return entry_path(path, pkg.name)
403+
stopenv == env && break
385404
end
386405
# Allow loading of stdlibs if the name/uuid are given
387406
# e.g. if they have been explicitly added to the project/manifest
388407
path = manifest_uuid_path(Sys.STDLIB, pkg)
389-
path === nothing || return entry_path(path, pkg.name)
408+
path isa String && return entry_path(path, pkg.name)
390409
end
391410
return nothing
392411
end
@@ -539,7 +558,7 @@ function manifest_deps_get(env::String, where::PkgId, name::String)::Union{Nothi
539558
return nothing
540559
end
541560

542-
function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String}
561+
function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missing}
543562
project_file = env_project_file(env)
544563
if project_file isa String
545564
proj = project_file_name_uuid(project_file, pkg.name)
@@ -730,7 +749,7 @@ function explicit_manifest_deps_get(project_file::String, where::UUID, name::Str
730749
end
731750

732751
# find `uuid` stanza, return the corresponding path
733-
function explicit_manifest_uuid_path(project_file::String, pkg::PkgId)::Union{Nothing,String}
752+
function explicit_manifest_uuid_path(project_file::String, pkg::PkgId)::Union{Nothing,String,Missing}
734753
manifest_file = project_file_manifest_path(project_file)
735754
manifest_file === nothing && return nothing # no manifest, skip env
736755

@@ -765,7 +784,8 @@ function explicit_manifest_entry_path(manifest_file::String, pkg::PkgId, entry::
765784
ispath(path) && return abspath(path)
766785
end
767786
end
768-
return nothing
787+
# no depot contains the package, return missing to stop looking
788+
return missing
769789
end
770790

771791
## implicit project & manifest API ##
@@ -1204,9 +1224,9 @@ function require(into::Module, mod::Symbol)
12041224
@lock require_lock begin
12051225
LOADING_CACHE[] = LoadingCache()
12061226
try
1207-
uuidkey = identify_package(into, String(mod))
1208-
# Core.println("require($(PkgId(into)), $mod) -> $uuidkey")
1209-
if uuidkey === nothing
1227+
uuidkey_env = identify_package_env(into, String(mod))
1228+
# Core.println("require($(PkgId(into)), $mod) -> $uuidkey from env \"$env\"")
1229+
if uuidkey_env === nothing
12101230
where = PkgId(into)
12111231
if where.uuid === nothing
12121232
hint, dots = begin
@@ -1234,10 +1254,11 @@ function require(into::Module, mod::Symbol)
12341254
- Otherwise you may need to report an issue with $(where.name)"""))
12351255
end
12361256
end
1257+
uuidkey, env = uuidkey_env
12371258
if _track_dependencies[]
12381259
push!(_require_dependencies, (into, binpack(uuidkey), 0.0))
12391260
end
1240-
return _require_prelocked(uuidkey)
1261+
return _require_prelocked(uuidkey, env)
12411262
finally
12421263
LOADING_CACHE[] = nothing
12431264
end
@@ -1254,10 +1275,10 @@ const pkgorigins = Dict{PkgId,PkgOrigin}()
12541275

12551276
require(uuidkey::PkgId) = @lock require_lock _require_prelocked(uuidkey)
12561277

1257-
function _require_prelocked(uuidkey::PkgId)
1278+
function _require_prelocked(uuidkey::PkgId, env=nothing)
12581279
assert_havelock(require_lock)
12591280
if !root_module_exists(uuidkey)
1260-
newm = _require(uuidkey)
1281+
newm = _require(uuidkey, env)
12611282
if newm === nothing
12621283
error("package `$(uuidkey.name)` did not define the expected \
12631284
module `$(uuidkey.name)`, check for typos in package module name")
@@ -1349,7 +1370,7 @@ function set_pkgorigin_version_path(pkg::PkgId, path::Union{String,Nothing})
13491370
end
13501371

13511372
# Returns `nothing` or the new(ish) module
1352-
function _require(pkg::PkgId)
1373+
function _require(pkg::PkgId, env=nothing)
13531374
assert_havelock(require_lock)
13541375
# handle recursive calls to require
13551376
loading = get(package_locks, pkg, false)
@@ -1364,7 +1385,7 @@ function _require(pkg::PkgId)
13641385
try
13651386
toplevel_load[] = false
13661387
# perform the search operation to select the module file require intends to load
1367-
path = locate_package(pkg)
1388+
path = locate_package(pkg, env)
13681389
if path === nothing
13691390
throw(ArgumentError("""
13701391
Package $pkg is required but does not seem to be installed:

test/loading.jl

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,3 +930,61 @@ end
930930
end
931931
end
932932
end
933+
934+
935+
@testset "Loading with incomplete manifest/depot #45977" begin
936+
mktempdir() do tmp
937+
# Set up a stacked env.
938+
cp(joinpath(@__DIR__, "depot"), joinpath(tmp, "depot"))
939+
940+
mkdir(joinpath(tmp, "Env1"))
941+
mkdir(joinpath(tmp, "Global"))
942+
943+
for env in ["Env1", "Global"]
944+
write(joinpath(tmp, env, "Project.toml"), """
945+
[deps]
946+
Baz = "6801f525-dc68-44e8-a4e8-cabd286279e7"
947+
""")
948+
end
949+
950+
write(joinpath(tmp, "Global", "Manifest.toml"), """
951+
[[Baz]]
952+
uuid = "6801f525-dc68-44e8-a4e8-cabd286279e7"
953+
git-tree-sha1 = "efc7e24c53d6a328011975294a2c75fed2f9800a"
954+
""")
955+
956+
# This SHA does not exist in the depot.
957+
write(joinpath(tmp, "Env1", "Manifest.toml"), """
958+
[[Baz]]
959+
uuid = "6801f525-dc68-44e8-a4e8-cabd286279e7"
960+
git-tree-sha1 = "5f2f6e72d001b014b48b26ec462f3714c342e167"
961+
""")
962+
963+
964+
old_load_path = copy(LOAD_PATH)
965+
old_depot_path = copy(DEPOT_PATH)
966+
try
967+
empty!(LOAD_PATH)
968+
push!(empty!(DEPOT_PATH), joinpath(tmp, "depot"))
969+
970+
push!(LOAD_PATH, joinpath(tmp, "Global"))
971+
972+
pkg = Base.identify_package("Baz")
973+
# Package in manifest in current env not present in depot
974+
@test Base.locate_package(pkg) !== nothing
975+
976+
pushfirst!(LOAD_PATH, joinpath(tmp, "Env1"))
977+
978+
@test Base.locate_package(pkg) === nothing
979+
980+
write(joinpath(tmp, "Env1", "Manifest.toml"), """
981+
""")
982+
# Package in current env not present in manifest
983+
pkg, env = Base.identify_package_env("Baz")
984+
@test Base.locate_package(pkg, env) === nothing
985+
finally
986+
copy!(LOAD_PATH, old_load_path)
987+
copy!(DEPOT_PATH, old_load_path)
988+
end
989+
end
990+
end

0 commit comments

Comments
 (0)