-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix #17997, don't load packages in Main
#23579
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -288,6 +288,7 @@ function reload(name::AbstractString) | |
error("use `include` instead of `reload` to load source files") | ||
else | ||
# reload("Package") is ok | ||
unreference_module(Symbol(name)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be unnecessary |
||
require(Symbol(name)) | ||
end | ||
end | ||
|
@@ -315,21 +316,78 @@ all platforms, including those with case-insensitive filesystems like macOS and | |
Windows. | ||
""" | ||
function require(mod::Symbol) | ||
_require(mod) | ||
# After successfully loading, notify downstream consumers | ||
if toplevel_load[] && myid() == 1 && nprocs() > 1 | ||
# broadcast top-level import/using from node 1 (only) | ||
@sync for p in procs() | ||
p == 1 && continue | ||
@async remotecall_wait(p) do | ||
if !isbindingresolved(Main, mod) || !isdefined(Main, mod) | ||
_require(mod) | ||
if !root_module_exists(mod) | ||
_require(mod) | ||
# After successfully loading, notify downstream consumers | ||
if toplevel_load[] && myid() == 1 && nprocs() > 1 | ||
# broadcast top-level import/using from node 1 (only) | ||
@sync for p in procs() | ||
p == 1 && continue | ||
@async remotecall_wait(p) do | ||
require(mod) | ||
nothing | ||
end | ||
end | ||
end | ||
for callback in package_callbacks | ||
invokelatest(callback, mod) | ||
end | ||
end | ||
for callback in package_callbacks | ||
invokelatest(callback, mod) | ||
return root_module(mod) | ||
end | ||
|
||
const loaded_modules = ObjectIdDict() | ||
const module_keys = ObjectIdDict() | ||
|
||
function register_root_module(key, m::Module) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like a bad idea. Maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is really only meant for internal use. Nobody but the loading code should call it. Does that address your concern, or is there something else? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This design appears to assume there will be a 1-to-1 correlation between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we'll be able to handle it either way. If loading something defines 0 modules, we can store |
||
if haskey(loaded_modules, key) | ||
oldm = loaded_modules[key] | ||
if oldm !== m | ||
name = module_name(oldm) | ||
warn("replacing module $name.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we doing Uppercase messages? I don't precisely remember what style we've started to settle on. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't seem to be totally consistent yet. On a quick search it's close to 50/50. There are some other occurrences of this same message that are lowercase. |
||
end | ||
end | ||
loaded_modules[key] = m | ||
module_keys[m] = key | ||
nothing | ||
end | ||
|
||
register_root_module(:Core, Core) | ||
register_root_module(:Base, Base) | ||
register_root_module(:Main, Main) | ||
|
||
is_root_module(m::Module) = haskey(module_keys, m) | ||
|
||
root_module_key(m::Module) = module_keys[m] | ||
|
||
# This is used as the current module when loading top-level modules. | ||
# It has the special behavior that modules evaluated in it get added | ||
# to the loaded_modules table instead of getting bindings. | ||
baremodule __toplevel__ | ||
using Base | ||
end | ||
|
||
# get a top-level Module from the given key | ||
# for now keys can only be Symbols, but that will change | ||
root_module(key::Symbol) = loaded_modules[key] | ||
|
||
root_module_exists(key::Symbol) = haskey(loaded_modules, key) | ||
|
||
loaded_modules_array() = collect(values(loaded_modules)) | ||
|
||
function unreference_module(key) | ||
if haskey(loaded_modules, key) | ||
m = pop!(loaded_modules, key) | ||
# need to ensure all modules are GC rooted; will still be referenced | ||
# in module_keys | ||
end | ||
end | ||
|
||
function register_all(a) | ||
for m in a | ||
if module_parent(m) === m | ||
register_root_module(module_name(m), m) | ||
end | ||
end | ||
end | ||
|
||
|
@@ -364,7 +422,8 @@ function _require(mod::Symbol) | |
if JLOptions().use_compiled_modules != 0 | ||
doneprecompile = _require_search_from_serialized(mod, path) | ||
if !isa(doneprecompile, Bool) | ||
return # success | ||
register_all(doneprecompile) | ||
return | ||
end | ||
end | ||
|
||
|
@@ -391,14 +450,16 @@ function _require(mod::Symbol) | |
warn(m, prefix="WARNING: ") | ||
# fall-through, TODO: disable __precompile__(true) error so that the normal include will succeed | ||
else | ||
return # success | ||
register_all(m) | ||
return | ||
end | ||
end | ||
|
||
# just load the file normally via include | ||
# for unknown dependencies | ||
try | ||
Base.include_relative(Main, path) | ||
Base.include_relative(__toplevel__, path) | ||
return | ||
catch ex | ||
if doneprecompile === true || JLOptions().use_compiled_modules == 0 || !precompilableerror(ex, true) | ||
rethrow() # rethrow non-precompilable=true errors | ||
|
@@ -411,6 +472,7 @@ function _require(mod::Symbol) | |
# TODO: disable __precompile__(true) error and do normal include instead of error | ||
error("Module $mod declares __precompile__(true) but require failed to create a usable precompiled cache file.") | ||
end | ||
register_all(m) | ||
end | ||
finally | ||
toplevel_load[] = last | ||
|
@@ -532,7 +594,7 @@ function create_expr_cache(input::String, output::String, concrete_deps::Vector{ | |
task_local_storage()[:SOURCE_PATH] = $(source) | ||
end) | ||
end | ||
serialize(in, :(Base.include(Main, $(abspath(input))))) | ||
serialize(in, :(Base.include(Base.__toplevel__, $(abspath(input))))) | ||
if source !== nothing | ||
serialize(in, :(delete!(task_local_storage(), :SOURCE_PATH))) | ||
end | ||
|
@@ -570,15 +632,9 @@ function compilecache(name::String) | |
cachefile::String = abspath(cachepath, name*".ji") | ||
# build up the list of modules that we want the precompile process to preserve | ||
concrete_deps = copy(_concrete_dependencies) | ||
for existing in names(Main) | ||
if isdefined(Main, existing) | ||
mod = getfield(Main, existing) | ||
if isa(mod, Module) && !(mod === Main || mod === Core || mod === Base) | ||
mod = mod::Module | ||
if module_parent(mod) === Main && module_name(mod) === existing | ||
push!(concrete_deps, (existing, module_uuid(mod))) | ||
end | ||
end | ||
for (key,mod) in loaded_modules | ||
if !(mod === Main || mod === Core || mod === Base) | ||
push!(concrete_deps, (key, module_uuid(mod))) | ||
end | ||
end | ||
# run the expression and cache the result | ||
|
@@ -675,7 +731,7 @@ function stale_cachefile(modpath::String, cachefile::String) | |
if mod == :Main || mod == :Core || mod == :Base | ||
continue | ||
# Module is already loaded | ||
elseif isbindingresolved(Main, mod) | ||
elseif root_module_exists(mod) | ||
continue | ||
end | ||
name = string(mod) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does one do this now? / What does
workspace()
do? / Should we addempty!(Base. loaded_modules)
, and (re-)register the new modules withregister_root_module
? / Should we add a new function to do that (Base.reset_require()
or argument toworkspace
) instead?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll update the doc string. I think
workspace()
should just give you a new Main module. Sousing X
in the new Main module will point to the already-loaded copy. To re-load packages there isreload
.